shithub: riscv

Download patch

ref: 482694b72a4d720a6c4246b721b34195f02ac4b8
parent: e87bbc878ec35e0f26dd54e9dfcdf96fa1432323
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Mar 9 16:26:20 EDT 2015

webfs: implement CONNECT method for https connections over proxy

when using a http proxy, establish secure tls connection to the
other end with the CONNECT method so the proxy.

--- a/sys/src/cmd/webfs/http.c
+++ b/sys/src/cmd/webfs/http.c
@@ -25,6 +25,7 @@
 	int	ctl;
 	int	keep;
 	int	cancel;
+	int	tunnel;
 	int	len;
 	char	addr[128];
 	char	buf[8192+2];
@@ -60,12 +61,40 @@
 
 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)
+{
+	TLSconn conn;
+
+	memset(&conn, 0, sizeof(conn));
+	if(debug)
+		conn.trace = tlstrace;
+	if((fd = tlsClient(fd, &conn)) < 0){
+		if(debug) fprint(2, "tlsClient: %r\n");
+		return -1;
+	}
+	free(conn.cert);
+	free(conn.sessionID);
+	return fd;
+}
+
 static Hconn*
 hdial(Url *u)
 {
 	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);
 
@@ -83,38 +112,42 @@
 	}
 	hpool.active++;
 	qunlock(&hpool);
+
 	if(debug)
 		fprint(2, "hdial [%d] %s\n", hpool.active, addr);
 
-	if((fd = dial(addr, 0, 0, &ctl)) < 0)
-		return nil;
-	if(strcmp(u->scheme, "https") == 0){
-		char err[ERRMAX];
-		TLSconn conn;
+	if(proxy)
+		snprint(addr, sizeof(addr), "tcp!%s!%s",
+			proxy->host, proxy->port ? proxy->port : proxy->scheme);
 
-		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((fd = dial(addr, 0, 0, &ctl)) >= 0){
+		if(proxy){
+			if(strcmp(proxy->scheme, "https") == 0)
+				fd = tlswrap(fd);
+		} else {
+			if(strcmp(u->scheme, "https") == 0)
+				fd = tlswrap(fd);
 		}
 	}
+	if(fd < 0){
+		close(ctl);
+		return nil;
+	}
 
 	h = emalloc(sizeof(*h));
 	h->next = nil;
 	h->time = 0;
 	h->cancel = 0;
+	h->tunnel = 0;
 	h->keep = 1;
 	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 +176,7 @@
 		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)
@@ -299,9 +332,12 @@
 				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;
 		}
@@ -502,6 +538,7 @@
 		fd = -1;
 
 	h = nil;
+	cfd = -1;
 	pid = 0;
 	host = nil;
 	needlength = 0;
@@ -570,12 +607,12 @@
 		free(host);
 		host = smprint("%H", u->host);
 
-		if(proxy){
+		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;
 		}
@@ -587,11 +624,15 @@
 		}
 		if(h == nil){
 			alarm(timeout);
-			if((h = hdial(proxy ? proxy : u)) == nil)
+			if((h = hdial(u)) == 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;
@@ -617,8 +658,12 @@
 			}
 		}
 
-		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);
@@ -628,7 +673,7 @@
 			goto Retry;
 		}
 
-		if(qpost){
+		if(qpost && !h->tunnel){
 			h->cancel = 0;
 			if((pid = rfork(RFMEM|RFPROC)) <= 0){
 				int ifd;
@@ -832,6 +877,8 @@
 		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)
@@ -852,6 +899,19 @@
 		 */
 		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)) < 0)
+				break;
+
+			/* proceed to the original request */
+			h->tunnel = 0;
+			continue;
+		}
 
 		if(!chunked && length == NOLENGTH)
 			h->keep = 0;