]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/webfs/http.c
merge
[plan9front.git] / sys / src / cmd / webfs / http.c
index 3e023be4d56d2a591ce2a4c79afe1e800990d8f0..ef53e4e8ae414ffc8e676e5fc1850300272a76d0 100644 (file)
@@ -25,6 +25,7 @@ struct Hconn
        int     ctl;
        int     keep;
        int     cancel;
+       int     tunnel;
        int     len;
        char    addr[128];
        char    buf[8192+2];
@@ -60,61 +61,98 @@ static Hauth *hauth;
 
 static void hclose(Hconn *h);
 
+static int
+tlstrace(char *fmt, ...)
+{
+       int r;
+       va_list a;
+       va_start(a, fmt);
+       r = vfprint(2, fmt, a);
+       va_end(a);
+       return r;
+}
+
+static int
+tlswrap(int fd, char *servername)
+{
+       TLSconn conn;
+
+       memset(&conn, 0, sizeof(conn));
+       if(debug)
+               conn.trace = tlstrace;
+       if(servername != nil)
+               conn.serverName = smprint("%N", servername);
+       if((fd = tlsClient(fd, &conn)) < 0){
+               if(debug) fprint(2, "tlsClient: %r\n");
+       }
+       free(conn.cert);
+       free(conn.sessionID);
+       free(conn.serverName);
+       return fd;
+}
+
 static Hconn*
-hdial(Url *u)
+hdial(Url *u, int cached)
 {
        char addr[128];
        Hconn *h, *p;
-       int fd, ofd, ctl;
+       int fd, ctl;
 
        snprint(addr, sizeof(addr), "tcp!%s!%s", u->host, u->port ? u->port : u->scheme);
 
-       qlock(&hpool);
-       for(p = nil, h = hpool.head; h; p = h, h = h->next){
-               if(strcmp(h->addr, addr) == 0){
-                       if(p)
-                               p->next = h->next;
-                       else
-                               hpool.head = h->next;
-                       h->next = nil;
-                       qunlock(&hpool);
-                       return h;
+       if(cached){
+               qlock(&hpool);
+               for(p = nil, h = hpool.head; h; p = h, h = h->next){
+                       if(strcmp(h->addr, addr) == 0){
+                               if(p)
+                                       p->next = h->next;
+                               else
+                                       hpool.head = h->next;
+                               h->next = nil;
+                               qunlock(&hpool);
+                               return h;
+                       }
                }
+               hpool.active++;
+               qunlock(&hpool);
        }
-       hpool.active++;
-       qunlock(&hpool);
+
        if(debug)
                fprint(2, "hdial [%d] %s\n", hpool.active, addr);
 
+       if(proxy)
+               snprint(addr, sizeof(addr), "tcp!%s!%s",
+                       proxy->host, proxy->port ? proxy->port : proxy->scheme);
+
        if((fd = dial(addr, 0, 0, &ctl)) < 0)
                return nil;
-       if(strcmp(u->scheme, "https") == 0){
-               char err[ERRMAX];
-               TLSconn conn;
-
-               strcpy(err, "tls error");
-               memset(&conn, 0, sizeof(conn));
-               if((fd = tlsClient(ofd = fd, &conn)) < 0)
-                       errstr(err, sizeof(err));
-               free(conn.cert);
-               free(conn.sessionID);
-               if(fd < 0){
-                       close(ofd);
-                       close(ctl);
-                       if(debug) fprint(2, "tlsClient: %s\n", err);
-                       errstr(err, sizeof(err));
-                       return nil;
-               }
+
+       if(proxy){
+               if(strcmp(proxy->scheme, "https") == 0)
+                       fd = tlswrap(fd, proxy->host);
+       } else {
+               if(strcmp(u->scheme, "https") == 0)
+                       fd = tlswrap(fd, u->host);
+       }
+       if(fd < 0){
+               close(ctl);
+               return nil;
        }
 
        h = emalloc(sizeof(*h));
        h->next = nil;
        h->time = 0;
        h->cancel = 0;
-       h->keep = 1;
+       h->tunnel = 0;
+       h->keep = cached;
        h->len = 0;
        h->fd = fd;
        h->ctl = ctl;
+
+       if(proxy){
+               h->tunnel = strcmp(u->scheme, "https") == 0;
+               snprint(addr, sizeof(addr), "tcp!%s!%s", u->host, u->port ? u->port : u->scheme);
+       }
        nstrcpy(h->addr, addr, sizeof(h->addr));
 
        return h;
@@ -143,7 +181,7 @@ hclose(Hconn *h)
                return;
 
        qlock(&hpool);
-       if(h->keep && h->fd >= 0){
+       if(!h->tunnel && h->keep && h->fd >= 0){
                for(n = 0, i = 0, t = nil, x = hpool.head; x; x = x->next){
                        if(strcmp(x->addr, h->addr) == 0)
                                if(++n > hpool.peer)
@@ -204,7 +242,7 @@ hclose(Hconn *h)
                                        /* free the tail */
                                        hcloseall(x);
                                } while(i);
-                               exits(0);
+                               exits(nil);
                        }
                        return;
                }
@@ -299,9 +337,12 @@ hline(Hconn *h, char *data, int len, int cont)
                                return len;
                        }
                }
-               if(h->len >= sizeof(h->buf))
+               n = sizeof(h->buf) - h->len;
+               if(n <= 0)
                        return 0;
-               if((n = read(h->fd, h->buf + h->len, sizeof(h->buf) - h->len)) <= 0){
+               if(h->tunnel)
+                       n = 1;  /* do not read beyond header */
+               if((n = read(h->fd, h->buf + h->len, n)) <= 0){
                        hhangup(h);
                        return -1;
                }
@@ -309,19 +350,30 @@ hline(Hconn *h, char *data, int len, int cont)
        }
 }
 
+static int
+hauthgetkey(char *params)
+{
+       if(debug)
+               fprint(2, "hauthgetkey %s\n", params);
+       werrstr("needkey %s", params);
+       return -1;
+}
+
 int
 authenticate(Url *u, Url *ru, char *method, char *s)
 {
-       char *user, *pass, *realm, *nonce, *opaque, *x;
+       char oerr[ERRMAX], *user, *pass, *realm, *nonce, *opaque, *x;
        Hauth *a;
        Fmt fmt;
        int n;
 
+       snprint(oerr, sizeof(oerr), "authentification failed");
+       errstr(oerr, sizeof(oerr));
+
        user = u->user;
        pass = u->pass;
        realm = nonce = opaque = nil;
        if(!cistrncmp(s, "Basic ", 6)){
-               char cred[128], plain[128];
                UserPasswd *up;
 
                s += 6;
@@ -337,26 +389,28 @@ authenticate(Url *u, Url *ru, char *method, char *s)
                                fmtprint(&fmt, " user=%q", user);
                        if((s = fmtstrflush(&fmt)) == nil)
                                return -1;
-                       up = auth_getuserpasswd(nil, "proto=pass service=http server=%q%s", u->host, s);
+                       up = auth_getuserpasswd(hauthgetkey,
+                               "proto=pass service=http server=%q%s", u->host, s);
                        free(s);
                        if(up == nil)
                                return -1;
                        user = up->user;
                        pass = up->passwd;
                }
-               n = snprint(plain, sizeof(plain), "%s:%s", user ? user : "", pass ? pass : "");
+               fmtstrinit(&fmt);
+               fmtprint(&fmt, "%s:%s", user ? user : "", pass ? pass : "");
                if(up){
                        memset(up->user, 0, strlen(up->user));
                        memset(up->passwd, 0, strlen(up->passwd));
                        free(up);
                }
-               n = enc64(cred, sizeof(cred), (uchar*)plain, n);
-               memset(plain, 0, sizeof(plain));
-               if(n == -1)
+               if((s = fmtstrflush(&fmt)) == nil)
                        return -1;
+               n = strlen(s);
                fmtstrinit(&fmt);
-               fmtprint(&fmt, "Basic %s", cred);
-               memset(cred, 0, sizeof(cred));
+               fmtprint(&fmt, "Basic %.*[", n, s);
+               memset(s, 0, n);
+               free(s);
                u = saneurl(url(".", u));       /* all uris below the requested one */
        }else
        if(!cistrncmp(s, "Digest ", 7)){
@@ -379,7 +433,7 @@ authenticate(Url *u, Url *ru, char *method, char *s)
                if((s = fmtstrflush(&fmt)) == nil)
                        return -1;
                nchal = snprint(chal, sizeof(chal), "%s %s %U", nonce, method, ru);
-               n = auth_respond(chal, nchal, ouser, sizeof ouser, resp, sizeof resp, nil,
+               n = auth_respond(chal, nchal, ouser, sizeof ouser, resp, sizeof resp, hauthgetkey,
                        "proto=httpdigest role=client server=%q%s", u->host, s);
                memset(chal, 0, sizeof(chal));
                free(s);
@@ -389,7 +443,7 @@ authenticate(Url *u, Url *ru, char *method, char *s)
                fmtprint(&fmt, "Digest ");
                fmtprint(&fmt, "username=\"%s\", ", ouser);
                fmtprint(&fmt, "realm=\"%s\", ", realm);
-               fmtprint(&fmt, "host=\"%s\", ", u->host);
+               fmtprint(&fmt, "host=\"%N\", ", u->host);
                fmtprint(&fmt, "uri=\"%U\", ", ru);
                fmtprint(&fmt, "nonce=\"%s\", ", nonce);
                fmtprint(&fmt, "response=\"%s\"", resp);
@@ -406,6 +460,7 @@ authenticate(Url *u, Url *ru, char *method, char *s)
                free(s);
                return -1;
        }
+
        a = emalloc(sizeof(*a));
        a->url = u;
        a->auth = s;
@@ -414,6 +469,7 @@ authenticate(Url *u, Url *ru, char *method, char *s)
        hauth = a;
        qunlock(&authlk);
 
+       errstr(oerr, sizeof(oerr));
        return 0;
 }
 
@@ -453,7 +509,7 @@ Again:
 static void
 catch(void *, char *msg)
 {
-       if(strstr("alarm", msg) || strstr("die", msg))
+       if(strstr("alarm", msg) != nil)
                noted(NCONT);
        else
                noted(NDFLT);
@@ -464,8 +520,8 @@ catch(void *, char *msg)
 void
 http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
 {
-       int i, l, n, try, pid, fd, cfd, needlength, chunked, retry, nobody;
-       char *s, *x, buf[8192+2], status[256], method[16];
+       int i, l, n, try, pid, fd, cfd, needlength, chunked, retry, nobody, badauth;
+       char *s, *x, buf[8192+2], status[256], method[16], *host;
        vlong length, offset;
        Url ru, tu, *nu;
        Key *k, *rhdr;
@@ -502,8 +558,11 @@ http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
                fd = -1;
 
        h = nil;
+       cfd = -1;
        pid = 0;
+       host = nil;
        needlength = 0;
+       badauth = 0;
        for(try = 0; try < 12; try++){
                strcpy(status, "0 No status");
                if(u == nil || (strcmp(u->scheme, "http") && strcmp(u->scheme, "https"))){
@@ -565,31 +624,40 @@ http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
                        qunlock(qpost);
                }
 
-               if(proxy){
+               /* http requires ascii encoding of host */
+               free(host);
+               host = smprint("%N", u->host);
+
+               if(proxy && strcmp(u->scheme, "https") != 0){
                        ru = *u;
+                       ru.host = host;
                        ru.fragment = nil;
                } else {
-                       memset(&ru, 0, sizeof(tu));
+                       memset(&ru, 0, sizeof(ru));
                        ru.path = Upath(u);
                        ru.query = u->query;
                }
-               n = snprint(buf, sizeof(buf), "%s %U HTTP/1.1\r\nHost: %H%s%s\r\n",
-                       method, &ru, u->host, u->port ? ":" : "", u->port ? u->port : "");
+               n = snprint(buf, sizeof(buf), "%s %U HTTP/1.1\r\nHost: %s%s%s\r\n",
+                       method, &ru, host, u->port ? ":" : "", u->port ? u->port : "");
                if(n >= sizeof(buf)-64){
                        werrstr("request too large");
                        break;
                }
                if(h == nil){
                        alarm(timeout);
-                       if((h = hdial(proxy ? proxy : u)) == nil)
+                       if((h = hdial(u, qpost!=nil)) == nil)
                                break;
                }
-
-               if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){
+               if(h->tunnel){
+                       n = snprint(buf, sizeof(buf), "CONNECT %s:%s HTTP/1.1\r\nHost: %s:%s\r\n",
+                               host, u->port ? u->port : "443",
+                               host, u->port ? u->port : "443");
+               }
+               else if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){
                        /* only scheme, host and path are relevant for cookies */
                        memset(&tu, 0, sizeof(tu));
                        tu.scheme = u->scheme;
-                       tu.host = u->host;
+                       tu.host = host;
                        tu.path = Upath(u);
                        fprint(cfd, "%U", &tu);
                        for(;;){
@@ -611,8 +679,12 @@ http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
                        }
                }
 
-               for(k = shdr; k; k = k->next)
+               for(k = shdr; k; k = k->next){
+                       /* only send proxy headers when establishing tunnel */
+                       if(h->tunnel && cistrncmp(k->key, "Proxy-", 6) != 0)
+                               continue;
                        n += snprint(buf+n, sizeof(buf)-2 - n, "%s: %s\r\n", k->key, k->val);
+               }
                n += snprint(buf+n, sizeof(buf)-n, "\r\n");
                if(debug)
                        fprint(2, "-> %.*s", n, buf);
@@ -622,7 +694,7 @@ http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
                        goto Retry;
                }
 
-               if(qpost){
+               if(qpost && !h->tunnel){
                        h->cancel = 0;
                        if((pid = rfork(RFMEM|RFPROC)) <= 0){
                                int ifd;
@@ -656,7 +728,7 @@ http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
                                }else
                                        h->keep = 0;
                                if(pid == 0)
-                                       exits(0);
+                                       exits(nil);
                        }
                        /* no timeout when posting */
                        alarm(0);
@@ -793,22 +865,34 @@ http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
                                goto Error;
                        freeurl(u);
                        u = nu;
-                       if(0){
+               if(0){
                case 401:       /* Unauthorized */
-                       if(x = lookkey(shdr, "Authorization"))
+                       if(x = lookkey(shdr, "Authorization")){
                                flushauth(nil, x);
-                       if(hauthenticate(u, &ru, method, "WWW-Authenticate", rhdr) < 0)
-                               goto Error;
+                               if(badauth++)
+                                       goto Error;
                        }
-                       if(0){
+                       if(hauthenticate(u, &ru, method, "WWW-Authenticate", rhdr) < 0){
+                       Autherror:
+                               h->cancel = 1;
+                               rerrstr(buf, sizeof(buf));
+                               buclose(qbody, buf);
+                               buclose(qpost, buf);
+                               break;
+                       }
+               }
+               if(0){
                case 407:       /* Proxy Auth */
                        if(proxy == nil)
                                goto Error;
-                       if(x = lookkey(shdr, "Proxy-Authorization"))
+                       if(x = lookkey(shdr, "Proxy-Authorization")){
                                flushauth(proxy, x);
-                       if(hauthenticate(u, &ru, method, "Proxy-Authenticate", rhdr) < 0)
-                               goto Error;
+                               if(badauth++)
+                                       goto Error;
                        }
+                       if(hauthenticate(proxy, proxy, method, "Proxy-Authenticate", rhdr) < 0)
+                               goto Autherror;
+               }
                case 0:         /* No status */
                        if(qpost && fd < 0){
                                if(i > 0)
@@ -826,6 +910,8 @@ http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
                case 202:       /* Accepted */
                case 203:       /* Non-Authoritative Information */
                case 206:       /* Partial Content */
+                       if(h->tunnel)
+                               break;
                        qbody->url = u; u = nil;
                        qbody->hdr = rhdr; rhdr = nil;
                        if(nobody)
@@ -847,6 +933,19 @@ http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
                shdr = delkey(shdr, "Proxy-Authorization");
                shdr = delkey(shdr, "Authorization");
 
+               /*
+                * when 2xx response is given for the CONNECT request
+                * then the proxy server has established the connection.
+                */
+               if(h->tunnel && !retry && (i/100) == 2){
+                       if((h->fd = tlswrap(h->fd, host)) < 0)
+                               break;
+
+                       /* proceed to the original request */
+                       h->tunnel = 0;
+                       continue;
+               }
+
                if(!chunked && length == NOLENGTH)
                        h->keep = 0;
 
@@ -925,10 +1024,11 @@ http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
 
        hclose(h);
        freeurl(u);
+       free(host);
 
        while(k = shdr){
                shdr = k->next;
                free(k);
        }
-       exits(0);
+       exits(nil);
 }