#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. Load more
#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 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, 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 void hcloseall(Hconn *x) { Hconn *h; while(h = x){ x = h->next; h->next = nil; h->keep = 0; hclose(h); } } static void hclose(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 void hhangup(Hconn *h) { if(debug) fprint(2, "hangup pc=%p: %r\n", getcallerpc(&h)); h->keep = 0; if(h->ctl >= 0) hangup(h->ctl); } static int hread(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 int hwrite(Hconn *h, void *data, int len) { if(write(h->fd, data, len) != len){ hhangup(h); return -1; } return len; } static int hline(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 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 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; } int hauthenticate(Url *u, Url *ru, char *method, char *key, Key *hdr) {