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