1093 lines · 21.8 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <ctype.h>#include <fcall.h>#include <thread.h>#include <9p.h> #include "dat.h"#include "fns.h" #include <auth.h>#include <mp.h>#include <libsec.h> typedef struct Hconn Hconn;typedef struct Hpool Hpool;typedef struct Hauth Hauth; struct Hconn{	Hconn	*next;	long	time; 	int	fd;	int	ctl;	int	keep;	int	cancel;	int	tunnel;	int	len;	char	addr[128];	char	buf[IOUNIT+2];}; struct Hpool{	QLock; 	Hconn	*head;	int	active; 	int	limit;	int	peer;	int	idle;}; struct Hauth{	Hauth	*next;	Url	*url;	char	*auth;}; static Hpool hpool = {	.limit	= 16,	.peer	= 4,	.idle	= 5,	/* seconds */}; static QLock authlk;static Hauth *hauth; static void hclose(Hconn *h); static inttlstrace(char *fmt, ...){	int r;	va_list a;	va_start(a, fmt);	r = vfprint(2, fmt, a);	va_end(a);	return r;} static inttlswrap(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, int cached){	char addr[128];	Hconn *h, *p;	int fd, ctl; 	snprint(addr, sizeof(addr), "tcp!%s!%s", u->host, u->port ? u->port : u->scheme); 	qlock(&hpool);	if(cached){		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); 	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(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->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;} static voidhcloseall(Hconn *x){	Hconn *h; 	while(h = x){		x = h->next;		h->next = nil;		h->keep = 0;		hclose(h);	}} static voidhclose(Hconn *h){	Hconn *x, *t;	int i, n; 	if(h == nil)		return; 	qlock(&hpool);	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)					break;			if(++i < hpool.limit)				t = x;		}		if(x == nil){			/* return connection to pool */			h->time = time(0);			h->next = hpool.head;			hpool.head = h; 			/* cut off tail */			if(t){				x = t->next;				t->next = nil;			} 			i = h->next != nil;			qunlock(&hpool); 			/* free the tail */			hcloseall(x); 			/*			 * if h is first one in pool, spawn proc to close			 * idle connections.			 */			if(i == 0)			if(rfork(RFMEM|RFPROC|RFNOWAIT) == 0){				do {					Hconn **xx;					long now; 					sleep(1000); 					qlock(&hpool);					now = time(0); 					x = nil;					xx = &hpool.head;					while(h = *xx){						if((now - h->time) > hpool.idle){							*xx = h->next; 							/* link to tail */							h->next = x;							x = h;							continue;						}						xx = &h->next;					} 					i = hpool.head != nil;					qunlock(&hpool); 					/* free the tail */					hcloseall(x);				} while(i);				exits(nil);			}			return;		}	}	hpool.active--;	qunlock(&hpool); 	if(debug)		fprint(2, "hclose [%d] %s\n", hpool.active, h->addr); 	if(h->ctl >= 0)		close(h->ctl);	if(h->fd >= 0)		close(h->fd);	free(h);} static voidhhangup(Hconn *h){	if(debug)		fprint(2, "hangup pc=%p: %r\n", getcallerpc(&h));	h->keep = 0;	if(h->ctl >= 0)		hangup(h->ctl);} static inthread(Hconn *h, void *data, int len){	if(h->len > 0){		if(len > h->len)			len = h->len;		memmove(data, h->buf, len);		h->len -= len;		if(h->len > 0)			memmove(h->buf, h->buf + len, h->len);		return len;	}	if((len = read(h->fd, data, len)) == 0)		h->keep = 0;	if(len < 0)		hhangup(h);	return len;} static inthwrite(Hconn *h, void *data, int len){	if(write(h->fd, data, len) != len){		hhangup(h);		return -1;	}	return len;} static inthline(Hconn *h, char *data, int len, int cont){	char *x, *y, *e;	int n; 	data[0] = 0;	for(;;){		if(h->len > 0){			while(x = memchr(h->buf, '\n', h->len)){				n = x - h->buf;				if(n > 0 && x[-1] == '\r')					n--;				if(n > 0 && cont){					e = h->buf + h->len;					for(y = x+1; y < e; y++)						if(*y != ' ' && *y != '\t')							break;					if(y >= e || *y == 0)						break;					if(y > x+1){						if(x > h->buf && x[-1] == '\r')							x--;						memmove(x, y, e - y);						h->len -= y - x;						continue;					}				}							if(n < len)					len = n;				memmove(data, h->buf, len);				data[len] = 0;				h->len -= (++x - h->buf);				if(h->len > 0)					memmove(h->buf, x, h->len);				return len;			}		}		n = sizeof(h->buf) - h->len;		if(n <= 0)			return 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;		}		h->len += n;	}} static inthauthgetkey(char *params){	if(debug)		fprint(2, "hauthgetkey %s\n", params);	werrstr("needkey %s", params);	return -1;} intauthenticate(Url *u, Url *ru, char *method, char *s){	char oerr[ERRMAX], *user, *pass, *realm, *nonce, *qop, *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)){		UserPasswd *up; 		s += 6;		if(x = cistrstr(s, "realm="))			realm = unquote(x+6, &s);		if(realm == nil)			return -1;		up = nil;		if(user == nil || pass == nil){			fmtstrinit(&fmt);			fmtprint(&fmt, " realm=%q", realm);			if(user)				fmtprint(&fmt, " user=%q", user);			if((s = fmtstrflush(&fmt)) == nil)				return -1;			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;		}		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);		}		if((s = fmtstrflush(&fmt)) == nil)			return -1;		n = strlen(s);		fmtstrinit(&fmt);		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)){		char chal[1024], ouser[128], resp[2*MD5LEN+1];		uchar cnonce[16];		uint nc;		int nchal; 		s += 7;		if(x = cistrstr(s, "realm="))			realm = unquote(x+6, &s);		if(x = cistrstr(s, "nonce="))			nonce = unquote(x+6, &s);		if(x = cistrstr(s, "qop="))			qop = unquote(x+4, &s);		else			qop = "";		if(x = cistrstr(s, "opaque="))			opaque = unquote(x+7, &s);		if(realm == nil || nonce == nil)			return -1;		fmtstrinit(&fmt);		fmtprint(&fmt, " realm=%q", realm);		if(user)			fmtprint(&fmt, " user=%q", user);		if((s = fmtstrflush(&fmt)) == nil)			return -1; 		/* RFC2617 "quality of protection" (qop) extension */		if(cistrcmp(qop, "auth") == 0){			static uint counter = 0; 			nc = ++counter;			genrandom(cnonce, sizeof(cnonce));			nchal = snprint(chal, sizeof(chal), "%s:%8.8ud:%.*H:%s %s %U",				nonce, nc, sizeof(cnonce), cnonce, qop, method, ru);		} else {			nc = 0;			nchal = snprint(chal, sizeof(chal), "%s %s %U", nonce, method, ru);		}		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);		if(n < 0)			return -1;		fmtstrinit(&fmt);		fmtprint(&fmt, "Digest ");		fmtprint(&fmt, "username=\"%s\", ", ouser);		fmtprint(&fmt, "realm=\"%s\", ", realm);		fmtprint(&fmt, "host=\"%N\", ", u->host);		fmtprint(&fmt, "uri=\"%U\", ", ru);		fmtprint(&fmt, "nonce=\"%s\", ", nonce);		if(cistrcmp(qop, "auth") == 0){			fmtprint(&fmt, "qop=%s, nc=%8.8ud, cnonce=\"%.*H\", ", qop, nc, sizeof(cnonce), cnonce);			memset(cnonce, 0, sizeof(cnonce));		}		fmtprint(&fmt, "response=\"%.*s\"", n, resp);		if(opaque)			fmtprint(&fmt, ", opaque=\"%s\"", opaque);		u = saneurl(url("/", u));	/* BUG: should be the ones in domain= only */	} else		return -1;	if((s = fmtstrflush(&fmt)) == nil){		freeurl(u);		return -1;	}	if(u == nil){		free(s);		return -1;	} 	a = emalloc(sizeof(*a));	a->url = u;	a->auth = s;	qlock(&authlk);	a->next = hauth;	hauth = a;	qunlock(&authlk); 	errstr(oerr, sizeof(oerr));	return 0;} inthauthenticate(Url *u, Url *ru, char *method, char *key, Key *hdr){

Lines 1–500 of 1093.