xref: /plan9/sys/src/cmd/ip/httpd/httpd.c (revision aeee3693c565baabd230992c0fa6413ddb05625a)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <auth.h>
49a747e4fSDavid du Colombier #include <mp.h>
59a747e4fSDavid du Colombier #include <libsec.h>
67dd7cddfSDavid du Colombier #include "httpd.h"
780ee5cbfSDavid du Colombier #include "httpsrv.h"
87dd7cddfSDavid du Colombier 
9*aeee3693SDavid du Colombier enum {
10*aeee3693SDavid du Colombier 	Nbuckets	= 256,
11*aeee3693SDavid du Colombier };
12*aeee3693SDavid du Colombier 
137dd7cddfSDavid du Colombier typedef struct Strings		Strings;
14*aeee3693SDavid du Colombier typedef struct System		System;
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier struct Strings
177dd7cddfSDavid du Colombier {
187dd7cddfSDavid du Colombier 	char	*s1;
197dd7cddfSDavid du Colombier 	char	*s2;
207dd7cddfSDavid du Colombier };
21*aeee3693SDavid du Colombier struct System {
22*aeee3693SDavid du Colombier 	char	*rsys;
23*aeee3693SDavid du Colombier 	ulong	reqs;
24*aeee3693SDavid du Colombier 	ulong	first;
25*aeee3693SDavid du Colombier 	ulong	last;
26*aeee3693SDavid du Colombier 	System	*next;			/* next in chain */
27*aeee3693SDavid du Colombier };
287dd7cddfSDavid du Colombier 
2980ee5cbfSDavid du Colombier char	*netdir;
3080ee5cbfSDavid du Colombier char	*HTTPLOG = "httpd/log";
317dd7cddfSDavid du Colombier 
3280ee5cbfSDavid du Colombier static	char		netdirb[256];
3380ee5cbfSDavid du Colombier static	char		*namespace;
34*aeee3693SDavid du Colombier static	System		syss[Nbuckets];
3580ee5cbfSDavid du Colombier 
367dd7cddfSDavid du Colombier static	void		becomenone(char*);
377dd7cddfSDavid du Colombier static	char		*csquery(char*, char*, char*);
387dd7cddfSDavid du Colombier static	void		dolisten(char*);
3980ee5cbfSDavid du Colombier static	int		doreq(HConnect*);
4080ee5cbfSDavid du Colombier static	int		send(HConnect*);
4180ee5cbfSDavid du Colombier static	Strings		stripmagic(HConnect*, char*);
427dd7cddfSDavid du Colombier static	char*		stripprefix(char*, char*);
437dd7cddfSDavid du Colombier static	char*		sysdom(void);
449a747e4fSDavid du Colombier static	int		notfound(HConnect *c, char *url);
459a747e4fSDavid du Colombier 
469a747e4fSDavid du Colombier uchar *certificate;
479a747e4fSDavid du Colombier int certlen;
485ede6b93SDavid du Colombier PEMChain *certchain;
497dd7cddfSDavid du Colombier 
507dd7cddfSDavid du Colombier void
usage(void)517dd7cddfSDavid du Colombier usage(void)
527dd7cddfSDavid du Colombier {
53431b537fSDavid du Colombier 	fprint(2, "usage: httpd [-c certificate] [-C CAchain] [-a srvaddress] "
54431b537fSDavid du Colombier 		"[-d domain] [-n namespace] [-w webroot]\n");
557dd7cddfSDavid du Colombier 	exits("usage");
567dd7cddfSDavid du Colombier }
577dd7cddfSDavid du Colombier 
587dd7cddfSDavid du Colombier void
main(int argc,char ** argv)597dd7cddfSDavid du Colombier main(int argc, char **argv)
607dd7cddfSDavid du Colombier {
617dd7cddfSDavid du Colombier 	char *address;
627dd7cddfSDavid du Colombier 
637dd7cddfSDavid du Colombier 	namespace = nil;
647dd7cddfSDavid du Colombier 	address = nil;
6580ee5cbfSDavid du Colombier 	hmydomain = nil;
6680ee5cbfSDavid du Colombier 	netdir = "/net";
679a747e4fSDavid du Colombier 	fmtinstall('D', hdatefmt);
689a747e4fSDavid du Colombier 	fmtinstall('H', httpfmt);
699a747e4fSDavid du Colombier 	fmtinstall('U', hurlfmt);
707dd7cddfSDavid du Colombier 	ARGBEGIN{
719a747e4fSDavid du Colombier 	case 'c':
72431b537fSDavid du Colombier 		certificate = readcert(EARGF(usage()), &certlen);
739a747e4fSDavid du Colombier 		if(certificate == nil)
74d9306527SDavid du Colombier 			sysfatal("reading certificate: %r");
759a747e4fSDavid du Colombier 		break;
765ede6b93SDavid du Colombier 	case 'C':
77431b537fSDavid du Colombier 		certchain = readcertchain(EARGF(usage()));
785ede6b93SDavid du Colombier 		if (certchain == nil)
795ede6b93SDavid du Colombier 			sysfatal("reading certificate chain: %r");
805ede6b93SDavid du Colombier 		break;
817dd7cddfSDavid du Colombier 	case 'n':
82431b537fSDavid du Colombier 		namespace = EARGF(usage());
837dd7cddfSDavid du Colombier 		break;
847dd7cddfSDavid du Colombier 	case 'a':
85431b537fSDavid du Colombier 		address = EARGF(usage());
867dd7cddfSDavid du Colombier 		break;
877dd7cddfSDavid du Colombier 	case 'd':
88431b537fSDavid du Colombier 		hmydomain = EARGF(usage());
897dd7cddfSDavid du Colombier 		break;
907dd7cddfSDavid du Colombier 	case 'w':
91431b537fSDavid du Colombier 		webroot = EARGF(usage());
927dd7cddfSDavid du Colombier 		break;
937dd7cddfSDavid du Colombier 	default:
947dd7cddfSDavid du Colombier 		usage();
957dd7cddfSDavid du Colombier 		break;
967dd7cddfSDavid du Colombier 	}ARGEND
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier 	if(argc)
997dd7cddfSDavid du Colombier 		usage();
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier 	if(namespace == nil)
1027dd7cddfSDavid du Colombier 		namespace = "/lib/namespace.httpd";
1037dd7cddfSDavid du Colombier 	if(address == nil)
1049a747e4fSDavid du Colombier 		address = "*";
1057dd7cddfSDavid du Colombier 	if(webroot == nil)
1067dd7cddfSDavid du Colombier 		webroot = "/usr/web";
1077dd7cddfSDavid du Colombier 	else{
1087dd7cddfSDavid du Colombier 		cleanname(webroot);
1097dd7cddfSDavid du Colombier 		if(webroot[0] != '/')
1107dd7cddfSDavid du Colombier 			webroot = "/usr/web";
1117dd7cddfSDavid du Colombier 	}
1127dd7cddfSDavid du Colombier 
1137dd7cddfSDavid du Colombier 	switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNAMEG)) {
1147dd7cddfSDavid du Colombier 	case -1:
1157dd7cddfSDavid du Colombier 		sysfatal("fork");
1167dd7cddfSDavid du Colombier 	case 0:
1177dd7cddfSDavid du Colombier 		break;
1187dd7cddfSDavid du Colombier 	default:
1197dd7cddfSDavid du Colombier 		exits(nil);
1207dd7cddfSDavid du Colombier 	}
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier 	/*
1237dd7cddfSDavid du Colombier 	 * open all files we might need before castrating namespace
1247dd7cddfSDavid du Colombier 	 */
1257dd7cddfSDavid du Colombier 	time(nil);
12680ee5cbfSDavid du Colombier 	if(hmydomain == nil)
12780ee5cbfSDavid du Colombier 		hmydomain = sysdom();
1287dd7cddfSDavid du Colombier 	syslog(0, HTTPLOG, nil);
1297dd7cddfSDavid du Colombier 	logall[0] = open("/sys/log/httpd/0", OWRITE);
1307dd7cddfSDavid du Colombier 	logall[1] = open("/sys/log/httpd/1", OWRITE);
131499069deSDavid du Colombier 	logall[2] = open("/sys/log/httpd/clf", OWRITE);
1327dd7cddfSDavid du Colombier 	redirectinit();
1337dd7cddfSDavid du Colombier 	contentinit();
1347dd7cddfSDavid du Colombier 	urlinit();
1357dd7cddfSDavid du Colombier 	statsinit();
1367dd7cddfSDavid du Colombier 
1377dd7cddfSDavid du Colombier 	becomenone(namespace);
1389a747e4fSDavid du Colombier 	dolisten(netmkaddr(address, "tcp", certificate == nil ? "http" : "https"));
1397dd7cddfSDavid du Colombier 	exits(nil);
1407dd7cddfSDavid du Colombier }
1417dd7cddfSDavid du Colombier 
1427dd7cddfSDavid du Colombier static void
becomenone(char * namespace)1437dd7cddfSDavid du Colombier becomenone(char *namespace)
1447dd7cddfSDavid du Colombier {
1457dd7cddfSDavid du Colombier 	int fd;
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier 	fd = open("#c/user", OWRITE);
1487dd7cddfSDavid du Colombier 	if(fd < 0 || write(fd, "none", strlen("none")) < 0)
1497dd7cddfSDavid du Colombier 		sysfatal("can't become none");
1507dd7cddfSDavid du Colombier 	close(fd);
1517dd7cddfSDavid du Colombier 	if(newns("none", nil) < 0)
1527dd7cddfSDavid du Colombier 		sysfatal("can't build normal namespace");
1537dd7cddfSDavid du Colombier 	if(addns("none", namespace) < 0)
1547dd7cddfSDavid du Colombier 		sysfatal("can't build httpd namespace");
1557dd7cddfSDavid du Colombier }
1567dd7cddfSDavid du Colombier 
15780ee5cbfSDavid du Colombier static HConnect*
mkconnect(char * scheme,char * port)158b39189fdSDavid du Colombier mkconnect(char *scheme, char *port)
1597dd7cddfSDavid du Colombier {
16080ee5cbfSDavid du Colombier 	HConnect *c;
1617dd7cddfSDavid du Colombier 
16280ee5cbfSDavid du Colombier 	c = ezalloc(sizeof(HConnect));
1637dd7cddfSDavid du Colombier 	c->hpos = c->header;
1647dd7cddfSDavid du Colombier 	c->hstop = c->header;
16580ee5cbfSDavid du Colombier 	c->replog = writelog;
166b39189fdSDavid du Colombier 	c->scheme = scheme;
167b39189fdSDavid du Colombier 	c->port = port;
1687dd7cddfSDavid du Colombier 	return c;
1697dd7cddfSDavid du Colombier }
1707dd7cddfSDavid du Colombier 
17180ee5cbfSDavid du Colombier static HSPriv*
mkhspriv(void)17280ee5cbfSDavid du Colombier mkhspriv(void)
17380ee5cbfSDavid du Colombier {
17480ee5cbfSDavid du Colombier 	HSPriv *p;
17580ee5cbfSDavid du Colombier 
17680ee5cbfSDavid du Colombier 	p = ezalloc(sizeof(HSPriv));
17780ee5cbfSDavid du Colombier 	return p;
17880ee5cbfSDavid du Colombier }
17980ee5cbfSDavid du Colombier 
180*aeee3693SDavid du Colombier static uint
hashstr(char * key)181*aeee3693SDavid du Colombier hashstr(char* key)
182*aeee3693SDavid du Colombier {
183*aeee3693SDavid du Colombier 	/* asu works better than pjw for urls */
184*aeee3693SDavid du Colombier 	uchar *k = (unsigned char*)key;
185*aeee3693SDavid du Colombier 	uint h = 0;
186*aeee3693SDavid du Colombier 
187*aeee3693SDavid du Colombier 	while(*k!=0)
188*aeee3693SDavid du Colombier 		h = 65599*h + *k++;
189*aeee3693SDavid du Colombier         return h;
190*aeee3693SDavid du Colombier }
191*aeee3693SDavid du Colombier 
192*aeee3693SDavid du Colombier static System *
hashsys(char * rsys)193*aeee3693SDavid du Colombier hashsys(char *rsys)
194*aeee3693SDavid du Colombier {
195*aeee3693SDavid du Colombier 	int notme;
196*aeee3693SDavid du Colombier 	System *sys;
197*aeee3693SDavid du Colombier 
198*aeee3693SDavid du Colombier 	sys = syss + hashstr(rsys) % nelem(syss);
199*aeee3693SDavid du Colombier 	/* if the bucket is empty, just use it, else find or allocate ours */
200*aeee3693SDavid du Colombier 	if(sys->rsys != nil) {
201*aeee3693SDavid du Colombier 		/* find match or chain end */
202*aeee3693SDavid du Colombier 		for(; notme = (strcmp(sys->rsys, rsys) != 0) &&
203*aeee3693SDavid du Colombier 		    sys->next != nil; sys = sys->next)
204*aeee3693SDavid du Colombier 			;
205*aeee3693SDavid du Colombier 		if(notme) {
206*aeee3693SDavid du Colombier 			sys->next = malloc(sizeof *sys);  /* extend chain */
207*aeee3693SDavid du Colombier 			sys = sys->next;
208*aeee3693SDavid du Colombier 		} else
209*aeee3693SDavid du Colombier 			return sys;
210*aeee3693SDavid du Colombier 	}
211*aeee3693SDavid du Colombier 	if(sys != nil) {
212*aeee3693SDavid du Colombier 		memset(sys, 0, sizeof *sys);
213*aeee3693SDavid du Colombier 		sys->rsys = strdup(rsys);
214*aeee3693SDavid du Colombier 	}
215*aeee3693SDavid du Colombier 	return sys;
216*aeee3693SDavid du Colombier }
217*aeee3693SDavid du Colombier 
218*aeee3693SDavid du Colombier /*
219*aeee3693SDavid du Colombier  * be sure to call this at least once per listen in the parent,
220*aeee3693SDavid du Colombier  * to update the hash chains.
221*aeee3693SDavid du Colombier  * it's okay to call it in the child too, but then sys->reqs only gets
222*aeee3693SDavid du Colombier  * updated in the child.
223*aeee3693SDavid du Colombier  */
224*aeee3693SDavid du Colombier static int
isswamped(char * rsys)225*aeee3693SDavid du Colombier isswamped(char *rsys)
226*aeee3693SDavid du Colombier {
227*aeee3693SDavid du Colombier 	ulong period;
228*aeee3693SDavid du Colombier 	System *sys = hashsys(rsys);
229*aeee3693SDavid du Colombier 
230*aeee3693SDavid du Colombier 	if(sys == nil)
231*aeee3693SDavid du Colombier 		return 0;
232*aeee3693SDavid du Colombier 	sys->last = time(nil);
233*aeee3693SDavid du Colombier 	if(sys->first == 0)
234*aeee3693SDavid du Colombier 		sys->first = sys->last;
235*aeee3693SDavid du Colombier 	period = sys->first - sys->last;
236*aeee3693SDavid du Colombier 	return ++sys->reqs > 30 && period > 30 && sys->reqs / period >= 2;
237*aeee3693SDavid du Colombier }
238*aeee3693SDavid du Colombier 
239*aeee3693SDavid du Colombier /* must only be called in child */
240*aeee3693SDavid du Colombier static void
throttle(int nctl,NetConnInfo * nci,int swamped)241*aeee3693SDavid du Colombier throttle(int nctl, NetConnInfo *nci, int swamped)
242*aeee3693SDavid du Colombier {
243*aeee3693SDavid du Colombier 	if(swamped || isswamped(nci->rsys)) {		/* shed load */
244*aeee3693SDavid du Colombier 		syslog(0, HTTPLOG, "overloaded by %s", nci->rsys);
245*aeee3693SDavid du Colombier 		sleep(30);
246*aeee3693SDavid du Colombier 		close(nctl);
247*aeee3693SDavid du Colombier 		exits(nil);
248*aeee3693SDavid du Colombier 	}
249*aeee3693SDavid du Colombier }
250*aeee3693SDavid du Colombier 
2517dd7cddfSDavid du Colombier static void
dolisten(char * address)2527dd7cddfSDavid du Colombier dolisten(char *address)
2537dd7cddfSDavid du Colombier {
25480ee5cbfSDavid du Colombier 	HSPriv *hp;
25580ee5cbfSDavid du Colombier 	HConnect *c;
2569a747e4fSDavid du Colombier 	NetConnInfo *nci;
257b39189fdSDavid du Colombier 	char ndir[NETPATHLEN], dir[NETPATHLEN], *p, *scheme;
258*aeee3693SDavid du Colombier 	int ctl, nctl, data, t, ok, spotchk, swamped;
2599a747e4fSDavid du Colombier 	TLSconn conn;
2607dd7cddfSDavid du Colombier 
26180ee5cbfSDavid du Colombier 	spotchk = 0;
2627dd7cddfSDavid du Colombier 	syslog(0, HTTPLOG, "httpd starting");
2637dd7cddfSDavid du Colombier 	ctl = announce(address, dir);
2647dd7cddfSDavid du Colombier 	if(ctl < 0){
2657dd7cddfSDavid du Colombier 		syslog(0, HTTPLOG, "can't announce on %s: %r", address);
2667dd7cddfSDavid du Colombier 		return;
2677dd7cddfSDavid du Colombier 	}
26880ee5cbfSDavid du Colombier 	strcpy(netdirb, dir);
2697dd7cddfSDavid du Colombier 	p = nil;
2707dd7cddfSDavid du Colombier 	if(netdir[0] == '/'){
27180ee5cbfSDavid du Colombier 		p = strchr(netdirb+1, '/');
2727dd7cddfSDavid du Colombier 		if(p != nil)
2737dd7cddfSDavid du Colombier 			*p = '\0';
2747dd7cddfSDavid du Colombier 	}
2757dd7cddfSDavid du Colombier 	if(p == nil)
27680ee5cbfSDavid du Colombier 		strcpy(netdirb, "/net");
27780ee5cbfSDavid du Colombier 	netdir = netdirb;
2787dd7cddfSDavid du Colombier 
2797dd7cddfSDavid du Colombier 	for(;;){
2807dd7cddfSDavid du Colombier 
2817dd7cddfSDavid du Colombier 		/*
2827dd7cddfSDavid du Colombier 		 *  wait for a call (or an error)
2837dd7cddfSDavid du Colombier 		 */
2847dd7cddfSDavid du Colombier 		nctl = listen(dir, ndir);
2857dd7cddfSDavid du Colombier 		if(nctl < 0){
2867dd7cddfSDavid du Colombier 			syslog(0, HTTPLOG, "can't listen on %s: %r", address);
2877dd7cddfSDavid du Colombier 			syslog(0, HTTPLOG, "ctls = %d", ctl);
2887dd7cddfSDavid du Colombier 			return;
2897dd7cddfSDavid du Colombier 		}
290*aeee3693SDavid du Colombier 		swamped = 0;
291*aeee3693SDavid du Colombier 		nci = getnetconninfo(ndir, -1);
292*aeee3693SDavid du Colombier 		if (nci)
293*aeee3693SDavid du Colombier 			swamped = isswamped(nci->rsys);
2947dd7cddfSDavid du Colombier 
2957dd7cddfSDavid du Colombier 		/*
2967dd7cddfSDavid du Colombier 		 *  start a process for the service
2977dd7cddfSDavid du Colombier 		 */
2987dd7cddfSDavid du Colombier 		switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFNAMEG)){
2997dd7cddfSDavid du Colombier 		case -1:
3007dd7cddfSDavid du Colombier 			close(nctl);
3017dd7cddfSDavid du Colombier 			continue;
3027dd7cddfSDavid du Colombier 		case 0:
3037dd7cddfSDavid du Colombier 			/*
3047dd7cddfSDavid du Colombier 			 *  see if we know the service requested
3057dd7cddfSDavid du Colombier 			 */
3067dd7cddfSDavid du Colombier 			data = accept(ctl, ndir);
3079a747e4fSDavid du Colombier 			if(data >= 0 && certificate != nil){
3089a747e4fSDavid du Colombier 				memset(&conn, 0, sizeof(conn));
3099a747e4fSDavid du Colombier 				conn.cert = certificate;
3109a747e4fSDavid du Colombier 				conn.certlen = certlen;
3115ede6b93SDavid du Colombier 				if (certchain != nil)
3125ede6b93SDavid du Colombier 					conn.chain = certchain;
3139a747e4fSDavid du Colombier 				data = tlsServer(data, &conn);
314b39189fdSDavid du Colombier 				scheme = "https";
315b39189fdSDavid du Colombier 			}else
316b39189fdSDavid du Colombier 				scheme = "http";
3177dd7cddfSDavid du Colombier 			if(data < 0){
3187dd7cddfSDavid du Colombier 				syslog(0, HTTPLOG, "can't open %s/data: %r", ndir);
3197dd7cddfSDavid du Colombier 				exits(nil);
3207dd7cddfSDavid du Colombier 			}
3217dd7cddfSDavid du Colombier 			dup(data, 0);
3227dd7cddfSDavid du Colombier 			dup(data, 1);
3237dd7cddfSDavid du Colombier 			dup(data, 2);
3247dd7cddfSDavid du Colombier 			close(data);
3257dd7cddfSDavid du Colombier 			close(ctl);
3267dd7cddfSDavid du Colombier 			close(nctl);
3277dd7cddfSDavid du Colombier 
328*aeee3693SDavid du Colombier 			if (nci == nil)
3299a747e4fSDavid du Colombier 				nci = getnetconninfo(ndir, -1);
330b39189fdSDavid du Colombier 			c = mkconnect(scheme, nci->lserv);
33180ee5cbfSDavid du Colombier 			hp = mkhspriv();
3329a747e4fSDavid du Colombier 			hp->remotesys = nci->rsys;
3339a747e4fSDavid du Colombier 			hp->remoteserv = nci->rserv;
33480ee5cbfSDavid du Colombier 			c->private = hp;
3357dd7cddfSDavid du Colombier 
3367dd7cddfSDavid du Colombier 			hinit(&c->hin, 0, Hread);
3377dd7cddfSDavid du Colombier 			hinit(&c->hout, 1, Hwrite);
3387dd7cddfSDavid du Colombier 
33980ee5cbfSDavid du Colombier 			/*
34080ee5cbfSDavid du Colombier 			 * serve requests until a magic request.
34180ee5cbfSDavid du Colombier 			 * later requests have to come quickly.
34280ee5cbfSDavid du Colombier 			 * only works for http/1.1 or later.
34380ee5cbfSDavid du Colombier 			 */
34480ee5cbfSDavid du Colombier 			for(t = 15*60*1000; ; t = 15*1000){
345*aeee3693SDavid du Colombier 				throttle(nctl, nci, swamped);
34680ee5cbfSDavid du Colombier 				if(hparsereq(c, t) <= 0)
34780ee5cbfSDavid du Colombier 					exits(nil);
34880ee5cbfSDavid du Colombier 				ok = doreq(c);
34980ee5cbfSDavid du Colombier 
3509a747e4fSDavid du Colombier 				hflush(&c->hout);
3519a747e4fSDavid du Colombier 
35280ee5cbfSDavid du Colombier 				if(c->head.closeit || ok < 0)
35380ee5cbfSDavid du Colombier 					exits(nil);
35480ee5cbfSDavid du Colombier 
35580ee5cbfSDavid du Colombier 				hreqcleanup(c);
35680ee5cbfSDavid du Colombier 			}
357b85a8364SDavid du Colombier 			/* not reached */
3587dd7cddfSDavid du Colombier 
3597dd7cddfSDavid du Colombier 		default:
3607dd7cddfSDavid du Colombier 			close(nctl);
3617dd7cddfSDavid du Colombier 			break;
3627dd7cddfSDavid du Colombier 		}
3637dd7cddfSDavid du Colombier 
3647dd7cddfSDavid du Colombier 		if(++spotchk > 50){
3657dd7cddfSDavid du Colombier 			spotchk = 0;
3667dd7cddfSDavid du Colombier 			redirectinit();
3677dd7cddfSDavid du Colombier 			contentinit();
3687dd7cddfSDavid du Colombier 			urlinit();
3697dd7cddfSDavid du Colombier 			statsinit();
3707dd7cddfSDavid du Colombier 		}
3717dd7cddfSDavid du Colombier 	}
3727dd7cddfSDavid du Colombier }
3737dd7cddfSDavid du Colombier 
3747dd7cddfSDavid du Colombier static int
doreq(HConnect * c)37580ee5cbfSDavid du Colombier doreq(HConnect *c)
3767dd7cddfSDavid du Colombier {
37780ee5cbfSDavid du Colombier 	HSPriv *hp;
3787dd7cddfSDavid du Colombier 	Strings ss;
379499069deSDavid du Colombier 	char *magic, *uri, *newuri, *origuri, *newpath, *hb;
38080ee5cbfSDavid du Colombier 	char virtualhost[100], logfd0[10], logfd1[10], vers[16];
381b51d2625SDavid du Colombier 	int n, nredirect;
3826aadf539SDavid du Colombier 	uint flags;
3837dd7cddfSDavid du Colombier 
3847dd7cddfSDavid du Colombier 	/*
38580ee5cbfSDavid du Colombier 	 * munge uri for magic
3867dd7cddfSDavid du Colombier 	 */
38780ee5cbfSDavid du Colombier 	uri = c->req.uri;
388499069deSDavid du Colombier 	nredirect = 0;
389431b537fSDavid du Colombier 	werrstr("");
390499069deSDavid du Colombier top:
391499069deSDavid du Colombier 	if(++nredirect > 10){
392499069deSDavid du Colombier 		if(hparseheaders(c, 15*60*1000) < 0)
393499069deSDavid du Colombier 			exits("failed");
394dc6ece7cSDavid du Colombier 		werrstr("redirection loop");
395499069deSDavid du Colombier 		return hfail(c, HNotFound, uri);
396499069deSDavid du Colombier 	}
39780ee5cbfSDavid du Colombier 	ss = stripmagic(c, uri);
3987dd7cddfSDavid du Colombier 	uri = ss.s1;
399499069deSDavid du Colombier 	origuri = uri;
4007dd7cddfSDavid du Colombier 	magic = ss.s2;
401499069deSDavid du Colombier 	if(magic)
402499069deSDavid du Colombier 		goto magic;
403499069deSDavid du Colombier 
404499069deSDavid du Colombier 	/*
405499069deSDavid du Colombier 	 * Apply redirects.  Do this before reading headers
406499069deSDavid du Colombier 	 * (if possible) so that we can redirect to magic invisibly.
407499069deSDavid du Colombier 	 */
4086aadf539SDavid du Colombier 	flags = 0;
409499069deSDavid du Colombier 	if(origuri[0]=='/' && origuri[1]=='~'){
410499069deSDavid du Colombier 		n = strlen(origuri) + 4 + UTFmax;
411499069deSDavid du Colombier 		newpath = halloc(c, n);
412499069deSDavid du Colombier 		snprint(newpath, n, "/who/%s", origuri+2);
413499069deSDavid du Colombier 		c->req.uri = newpath;
414499069deSDavid du Colombier 		newuri = newpath;
415499069deSDavid du Colombier 	}else if(origuri[0]=='/' && origuri[1]==0){
416dc6ece7cSDavid du Colombier 		/* can't redirect / until we read the headers below */
417499069deSDavid du Colombier 		newuri = nil;
418499069deSDavid du Colombier 	}else
4196aadf539SDavid du Colombier 		newuri = redirect(c, origuri, &flags);
420499069deSDavid du Colombier 
421499069deSDavid du Colombier 	if(newuri != nil){
422431b537fSDavid du Colombier 		if(flags & Redirsilent) {
4236aadf539SDavid du Colombier 			c->req.uri = uri = newuri;
4246aadf539SDavid du Colombier 			logit(c, "%s: silent replacement %s", origuri, uri);
425499069deSDavid du Colombier 			goto top;
426431b537fSDavid du Colombier 		}
427431b537fSDavid du Colombier 		if(hparseheaders(c, 15*60*1000) < 0)
428431b537fSDavid du Colombier 			exits("failed");
429431b537fSDavid du Colombier 		if(flags & Redirperm) {
430431b537fSDavid du Colombier 			logit(c, "%s: permanently moved to %s", origuri, newuri);
431431b537fSDavid du Colombier 			return hmoved(c, newuri);
4326aadf539SDavid du Colombier 		} else if (flags & (Redironly | Redirsubord))
4336aadf539SDavid du Colombier 			logit(c, "%s: top-level or many-to-one replacement %s",
4346aadf539SDavid du Colombier 				origuri, uri);
4356aadf539SDavid du Colombier 
436dc6ece7cSDavid du Colombier 		/*
437431b537fSDavid du Colombier 		 * try temporary redirect instead of permanent
438dc6ece7cSDavid du Colombier 		 */
439dc6ece7cSDavid du Colombier 		if (http11(c))
440dc6ece7cSDavid du Colombier 			return hredirected(c, "307 Temporary Redirect", newuri);
441dc6ece7cSDavid du Colombier 		else
442dc6ece7cSDavid du Colombier 			return hredirected(c, "302 Temporary Redirect", newuri);
443499069deSDavid du Colombier 	}
4447dd7cddfSDavid du Colombier 
44580ee5cbfSDavid du Colombier 	/*
44680ee5cbfSDavid du Colombier 	 * for magic we exec a new program and serve no more requests
44780ee5cbfSDavid du Colombier 	 */
448499069deSDavid du Colombier magic:
44980ee5cbfSDavid du Colombier 	if(magic != nil && strcmp(magic, "httpd") != 0){
45080ee5cbfSDavid du Colombier 		snprint(c->xferbuf, HBufSize, "/bin/ip/httpd/%s", magic);
45180ee5cbfSDavid du Colombier 		snprint(logfd0, sizeof(logfd0), "%d", logall[0]);
45280ee5cbfSDavid du Colombier 		snprint(logfd1, sizeof(logfd1), "%d", logall[1]);
45380ee5cbfSDavid du Colombier 		snprint(vers, sizeof(vers), "HTTP/%d.%d", c->req.vermaj, c->req.vermin);
45480ee5cbfSDavid du Colombier 		hb = hunload(&c->hin);
4559a747e4fSDavid du Colombier 		if(hb == nil){
45680ee5cbfSDavid du Colombier 			hfail(c, HInternal);
4579a747e4fSDavid du Colombier 			return -1;
4589a747e4fSDavid du Colombier 		}
45980ee5cbfSDavid du Colombier 		hp = c->private;
460431b537fSDavid du Colombier 		execl(c->xferbuf, magic, "-d", hmydomain, "-w", webroot,
461b39189fdSDavid du Colombier 			"-s", c->scheme, "-p", c->port,
462431b537fSDavid du Colombier 			"-r", hp->remotesys, "-N", netdir, "-b", hb,
46380ee5cbfSDavid du Colombier 			"-L", logfd0, logfd1, "-R", c->header,
46480ee5cbfSDavid du Colombier 			c->req.meth, vers, uri, c->req.search, nil);
46580ee5cbfSDavid du Colombier 		logit(c, "no magic %s uri %s", magic, uri);
46680ee5cbfSDavid du Colombier 		hfail(c, HNotFound, uri);
46780ee5cbfSDavid du Colombier 		return -1;
46880ee5cbfSDavid du Colombier 	}
4697dd7cddfSDavid du Colombier 
4707dd7cddfSDavid du Colombier 	/*
4717dd7cddfSDavid du Colombier 	 * normal case is just file transfer
4727dd7cddfSDavid du Colombier 	 */
47380ee5cbfSDavid du Colombier 	if(hparseheaders(c, 15*60*1000) < 0)
47480ee5cbfSDavid du Colombier 		exits("failed");
475499069deSDavid du Colombier 	if(origuri[0] == '/' && origuri[1] == 0){
476499069deSDavid du Colombier 		snprint(virtualhost, sizeof virtualhost, "http://%s/", c->head.host);
4776aadf539SDavid du Colombier 		newuri = redirect(c, virtualhost, nil);
478499069deSDavid du Colombier 		if(newuri == nil)
4796aadf539SDavid du Colombier 			newuri = redirect(c, origuri, nil);
480499069deSDavid du Colombier 		if(newuri)
481499069deSDavid du Colombier 			return hmoved(c, newuri);
482499069deSDavid du Colombier 	}
4837dd7cddfSDavid du Colombier 	if(!http11(c) && !c->head.persist)
4847dd7cddfSDavid du Colombier 		c->head.closeit = 1;
48580ee5cbfSDavid du Colombier 	return send(c);
4867dd7cddfSDavid du Colombier }
4877dd7cddfSDavid du Colombier 
4887dd7cddfSDavid du Colombier static int
send(HConnect * c)48980ee5cbfSDavid du Colombier send(HConnect *c)
4907dd7cddfSDavid du Colombier {
4919a747e4fSDavid du Colombier 	Dir *dir;
492fe853e23SDavid du Colombier 	char *w, *w2, *p, *masque;
4937dd7cddfSDavid du Colombier 	int fd, fd1, n, force301, ok;
4947dd7cddfSDavid du Colombier 
495183e6fd7SDavid du Colombier /*
4967dd7cddfSDavid du Colombier 	if(c->req.search)
49780ee5cbfSDavid du Colombier 		return hfail(c, HNoSearch, c->req.uri);
498183e6fd7SDavid du Colombier  */
4997dd7cddfSDavid du Colombier 	if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
50080ee5cbfSDavid du Colombier 		return hunallowed(c, "GET, HEAD");
5017dd7cddfSDavid du Colombier 	if(c->head.expectother || c->head.expectcont)
50280ee5cbfSDavid du Colombier 		return hfail(c, HExpectFail);
5037dd7cddfSDavid du Colombier 
5049a747e4fSDavid du Colombier 	masque = masquerade(c->head.host);
5057dd7cddfSDavid du Colombier 
5067dd7cddfSDavid du Colombier 	/*
5077dd7cddfSDavid du Colombier 	 * check for directory/file mismatch with trailing /,
5087dd7cddfSDavid du Colombier 	 * and send any redirections.
5097dd7cddfSDavid du Colombier 	 */
5109a747e4fSDavid du Colombier 	n = strlen(webroot) + strlen(masque) + strlen(c->req.uri) +
5119a747e4fSDavid du Colombier 		STRLEN("/index.html") + STRLEN("/.httplogin") + 1;
51280ee5cbfSDavid du Colombier 	w = halloc(c, n);
5137dd7cddfSDavid du Colombier 	strcpy(w, webroot);
5149a747e4fSDavid du Colombier 	strcat(w, masque);
5159a747e4fSDavid du Colombier 	strcat(w, c->req.uri);
5163ff48bf5SDavid du Colombier 
5173ff48bf5SDavid du Colombier 	/*
518fe853e23SDavid du Colombier 	 *  favicon can be overridden by hostname.ico
519fe853e23SDavid du Colombier 	 */
520fe853e23SDavid du Colombier 	if(strcmp(c->req.uri, "/favicon.ico") == 0){
521fe853e23SDavid du Colombier 		w2 = halloc(c, n+strlen(c->head.host)+2);
522fe853e23SDavid du Colombier 		strcpy(w2, webroot);
523fe853e23SDavid du Colombier 		strcat(w2, masque);
524fe853e23SDavid du Colombier 		strcat(w2, "/");
525fe853e23SDavid du Colombier 		strcat(w2, c->head.host);
526fe853e23SDavid du Colombier 		strcat(w2, ".ico");
527fe853e23SDavid du Colombier 		if(access(w2, AREAD)==0)
528fe853e23SDavid du Colombier 			w = w2;
529fe853e23SDavid du Colombier 	}
530fe853e23SDavid du Colombier 
531fe853e23SDavid du Colombier 	/*
5323ff48bf5SDavid du Colombier 	 * don't show the contents of .httplogin
5333ff48bf5SDavid du Colombier 	 */
5343ff48bf5SDavid du Colombier 	n = strlen(w);
5353ff48bf5SDavid du Colombier 	if(strcmp(w+n-STRLEN(".httplogin"), ".httplogin") == 0)
5363ff48bf5SDavid du Colombier 		return notfound(c, c->req.uri);
5373ff48bf5SDavid du Colombier 
5389a747e4fSDavid du Colombier 	fd = open(w, OREAD);
5399a747e4fSDavid du Colombier 	if(fd < 0 && strlen(masque)>0 && strncmp(c->req.uri, masque, strlen(masque)) == 0){
5409a747e4fSDavid du Colombier 		// may be a URI from before virtual hosts;  try again without masque
5419a747e4fSDavid du Colombier 		strcpy(w, webroot);
5427dd7cddfSDavid du Colombier 		strcat(w, c->req.uri);
5437dd7cddfSDavid du Colombier 		fd = open(w, OREAD);
5449a747e4fSDavid du Colombier 	}
5457dd7cddfSDavid du Colombier 	if(fd < 0)
5467dd7cddfSDavid du Colombier 		return notfound(c, c->req.uri);
5479a747e4fSDavid du Colombier 	dir = dirfstat(fd);
5489a747e4fSDavid du Colombier 	if(dir == nil){
5497dd7cddfSDavid du Colombier 		close(fd);
55080ee5cbfSDavid du Colombier 		return hfail(c, HInternal);
5517dd7cddfSDavid du Colombier 	}
5527dd7cddfSDavid du Colombier 	p = strchr(w, '\0');
5539a747e4fSDavid du Colombier 	if(dir->mode & DMDIR){
5549a747e4fSDavid du Colombier 		free(dir);
5557dd7cddfSDavid du Colombier 		if(p > w && p[-1] == '/'){
5567dd7cddfSDavid du Colombier 			strcat(w, "index.html");
5577dd7cddfSDavid du Colombier 			force301 = 0;
5587dd7cddfSDavid du Colombier 		}else{
5597dd7cddfSDavid du Colombier 			strcat(w, "/index.html");
5607dd7cddfSDavid du Colombier 			force301 = 1;
5617dd7cddfSDavid du Colombier 		}
5627dd7cddfSDavid du Colombier 		fd1 = open(w, OREAD);
5637dd7cddfSDavid du Colombier 		if(fd1 < 0){
5647dd7cddfSDavid du Colombier 			close(fd);
5657dd7cddfSDavid du Colombier 			return notfound(c, c->req.uri);
5667dd7cddfSDavid du Colombier 		}
5679a747e4fSDavid du Colombier 		c->req.uri = w + strlen(webroot) + strlen(masque);
5687dd7cddfSDavid du Colombier 		if(force301 && c->req.vermaj){
5697dd7cddfSDavid du Colombier 			close(fd);
5707dd7cddfSDavid du Colombier 			close(fd1);
57180ee5cbfSDavid du Colombier 			return hmoved(c, c->req.uri);
5727dd7cddfSDavid du Colombier 		}
5737dd7cddfSDavid du Colombier 		close(fd);
5747dd7cddfSDavid du Colombier 		fd = fd1;
5759a747e4fSDavid du Colombier 		dir = dirfstat(fd);
5769a747e4fSDavid du Colombier 		if(dir == nil){
5777dd7cddfSDavid du Colombier 			close(fd);
57880ee5cbfSDavid du Colombier 			return hfail(c, HInternal);
5797dd7cddfSDavid du Colombier 		}
5807dd7cddfSDavid du Colombier 	}else if(p > w && p[-1] == '/'){
5819a747e4fSDavid du Colombier 		free(dir);
5827dd7cddfSDavid du Colombier 		close(fd);
5837dd7cddfSDavid du Colombier 		*strrchr(c->req.uri, '/') = '\0';
58480ee5cbfSDavid du Colombier 		return hmoved(c, c->req.uri);
5857dd7cddfSDavid du Colombier 	}
5867dd7cddfSDavid du Colombier 
5879a747e4fSDavid du Colombier 	ok = authorize(c, w);
5889a747e4fSDavid du Colombier 	if(ok <= 0){
5899a747e4fSDavid du Colombier 		free(dir);
5909a747e4fSDavid du Colombier 		close(fd);
5919a747e4fSDavid du Colombier 		return ok;
5929a747e4fSDavid du Colombier 	}
5939a747e4fSDavid du Colombier 
5949a747e4fSDavid du Colombier 	return sendfd(c, fd, dir, nil, nil);
5957dd7cddfSDavid du Colombier }
5967dd7cddfSDavid du Colombier 
5977dd7cddfSDavid du Colombier static Strings
stripmagic(HConnect * hc,char * uri)59880ee5cbfSDavid du Colombier stripmagic(HConnect *hc, char *uri)
5997dd7cddfSDavid du Colombier {
6007dd7cddfSDavid du Colombier 	Strings ss;
6017dd7cddfSDavid du Colombier 	char *newuri, *prog, *s;
6027dd7cddfSDavid du Colombier 
6037dd7cddfSDavid du Colombier 	prog = stripprefix("/magic/", uri);
6047dd7cddfSDavid du Colombier 	if(prog == nil){
6057dd7cddfSDavid du Colombier 		ss.s1 = uri;
6067dd7cddfSDavid du Colombier 		ss.s2 = nil;
6077dd7cddfSDavid du Colombier 		return ss;
6087dd7cddfSDavid du Colombier 	}
6097dd7cddfSDavid du Colombier 
6107dd7cddfSDavid du Colombier 	s = strchr(prog, '/');
6117dd7cddfSDavid du Colombier 	if(s == nil)
6127dd7cddfSDavid du Colombier 		newuri = "";
6137dd7cddfSDavid du Colombier 	else{
61480ee5cbfSDavid du Colombier 		newuri = hstrdup(hc, s);
6157dd7cddfSDavid du Colombier 		*s = 0;
6167dd7cddfSDavid du Colombier 		s = strrchr(s, '/');
6177dd7cddfSDavid du Colombier 		if(s != nil && s[1] == 0)
6187dd7cddfSDavid du Colombier 			*s = 0;
6197dd7cddfSDavid du Colombier 	}
6207dd7cddfSDavid du Colombier 	ss.s1 = newuri;
6217dd7cddfSDavid du Colombier 	ss.s2 = prog;
6227dd7cddfSDavid du Colombier 	return ss;
6237dd7cddfSDavid du Colombier }
6247dd7cddfSDavid du Colombier 
6257dd7cddfSDavid du Colombier static char*
stripprefix(char * pre,char * str)6267dd7cddfSDavid du Colombier stripprefix(char *pre, char *str)
6277dd7cddfSDavid du Colombier {
6287dd7cddfSDavid du Colombier 	while(*pre)
6297dd7cddfSDavid du Colombier 		if(*str++ != *pre++)
6307dd7cddfSDavid du Colombier 			return nil;
6317dd7cddfSDavid du Colombier 	return str;
6327dd7cddfSDavid du Colombier }
6337dd7cddfSDavid du Colombier 
6349a747e4fSDavid du Colombier /*
6359a747e4fSDavid du Colombier  * couldn't open a file
6369a747e4fSDavid du Colombier  * figure out why and return and error message
6379a747e4fSDavid du Colombier  */
6389a747e4fSDavid du Colombier static int
notfound(HConnect * c,char * url)6399a747e4fSDavid du Colombier notfound(HConnect *c, char *url)
6409a747e4fSDavid du Colombier {
6419a747e4fSDavid du Colombier 	c->xferbuf[0] = 0;
642dc6ece7cSDavid du Colombier 	rerrstr(c->xferbuf, sizeof c->xferbuf);
6439a747e4fSDavid du Colombier 	if(strstr(c->xferbuf, "file does not exist") != nil)
6449a747e4fSDavid du Colombier 		return hfail(c, HNotFound, url);
6459a747e4fSDavid du Colombier 	if(strstr(c->xferbuf, "permission denied") != nil)
6469a747e4fSDavid du Colombier 		return hfail(c, HUnauth, url);
6479a747e4fSDavid du Colombier 	return hfail(c, HNotFound, url);
6489a747e4fSDavid du Colombier }
6499a747e4fSDavid du Colombier 
6507dd7cddfSDavid du Colombier static char*
sysdom(void)6517dd7cddfSDavid du Colombier sysdom(void)
6527dd7cddfSDavid du Colombier {
6537dd7cddfSDavid du Colombier 	char *dn;
6547dd7cddfSDavid du Colombier 
6557dd7cddfSDavid du Colombier 	dn = csquery("sys" , sysname(), "dom");
6567dd7cddfSDavid du Colombier 	if(dn == nil)
6577dd7cddfSDavid du Colombier 		dn = "who cares";
6587dd7cddfSDavid du Colombier 	return dn;
6597dd7cddfSDavid du Colombier }
6607dd7cddfSDavid du Colombier 
6617dd7cddfSDavid du Colombier /*
6627dd7cddfSDavid du Colombier  *  query the connection server
6637dd7cddfSDavid du Colombier  */
6647dd7cddfSDavid du Colombier static char*
csquery(char * attr,char * val,char * rattr)6657dd7cddfSDavid du Colombier csquery(char *attr, char *val, char *rattr)
6667dd7cddfSDavid du Colombier {
6677dd7cddfSDavid du Colombier 	char token[64+4];
6687dd7cddfSDavid du Colombier 	char buf[256], *p, *sp;
6697dd7cddfSDavid du Colombier 	int fd, n;
6707dd7cddfSDavid du Colombier 
6717dd7cddfSDavid du Colombier 	if(val == nil || val[0] == 0)
6727dd7cddfSDavid du Colombier 		return nil;
6737dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/cs", netdir);
6747dd7cddfSDavid du Colombier 	fd = open(buf, ORDWR);
6757dd7cddfSDavid du Colombier 	if(fd < 0)
6767dd7cddfSDavid du Colombier 		return nil;
6777dd7cddfSDavid du Colombier 	fprint(fd, "!%s=%s", attr, val);
6787dd7cddfSDavid du Colombier 	seek(fd, 0, 0);
6797dd7cddfSDavid du Colombier 	snprint(token, sizeof(token), "%s=", rattr);
6807dd7cddfSDavid du Colombier 	for(;;){
6817dd7cddfSDavid du Colombier 		n = read(fd, buf, sizeof(buf)-1);
6827dd7cddfSDavid du Colombier 		if(n <= 0)
6837dd7cddfSDavid du Colombier 			break;
6847dd7cddfSDavid du Colombier 		buf[n] = 0;
6857dd7cddfSDavid du Colombier 		p = strstr(buf, token);
6867dd7cddfSDavid du Colombier 		if(p != nil && (p == buf || *(p-1) == 0)){
6877dd7cddfSDavid du Colombier 			close(fd);
6887dd7cddfSDavid du Colombier 			sp = strchr(p, ' ');
6897dd7cddfSDavid du Colombier 			if(sp)
6907dd7cddfSDavid du Colombier 				*sp = 0;
6917dd7cddfSDavid du Colombier 			p = strchr(p, '=');
6927dd7cddfSDavid du Colombier 			if(p == nil)
6937dd7cddfSDavid du Colombier 				return nil;
6947dd7cddfSDavid du Colombier 			return estrdup(p+1);
6957dd7cddfSDavid du Colombier 		}
6967dd7cddfSDavid du Colombier 	}
6977dd7cddfSDavid du Colombier 	close(fd);
6987dd7cddfSDavid du Colombier 	return nil;
6997dd7cddfSDavid du Colombier }
700