9 typedef struct Strings Strings;
18 char *HTTPLOG = "httpd/log";
20 static char netdirb[256];
21 static char *namespace;
23 static void becomenone(char*);
24 static char *csquery(char*, char*, char*);
25 static void dolisten(char*);
26 static int doreq(HConnect*);
27 static int send(HConnect*);
28 static Strings stripmagic(HConnect*, char*);
29 static char* stripprefix(char*, char*);
30 static char* sysdom(void);
31 static int notfound(HConnect *c, char *url);
40 fprint(2, "usage: httpd [-c certificate] [-C CAchain] [-a srvaddress] "
41 "[-d domain] [-n namespace] [-w webroot]\n");
46 main(int argc, char **argv)
54 fmtinstall('D', hdatefmt);
55 fmtinstall('H', httpfmt);
56 fmtinstall('U', hurlfmt);
59 certificate = readcert(EARGF(usage()), &certlen);
60 if(certificate == nil)
61 sysfatal("reading certificate: %r");
64 certchain = readcertchain(EARGF(usage()));
66 sysfatal("reading certificate chain: %r");
69 namespace = EARGF(usage());
72 address = EARGF(usage());
75 hmydomain = EARGF(usage());
78 webroot = EARGF(usage());
89 namespace = "/lib/namespace.httpd";
100 switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNAMEG)) {
110 * open all files we might need before castrating namespace
114 hmydomain = sysdom();
115 syslog(0, HTTPLOG, nil);
116 logall[0] = open("/sys/log/httpd/0", OWRITE);
117 logall[1] = open("/sys/log/httpd/1", OWRITE);
118 logall[2] = open("/sys/log/httpd/clf", OWRITE);
124 becomenone(namespace);
125 dolisten(netmkaddr(address, "tcp", certificate == nil ? "http" : "https"));
130 becomenone(char *namespace)
134 fd = open("#c/user", OWRITE);
135 if(fd < 0 || write(fd, "none", strlen("none")) < 0)
136 sysfatal("can't become none");
138 if(newns("none", nil) < 0)
139 sysfatal("can't build normal namespace");
140 if(addns("none", namespace) < 0)
141 sysfatal("can't build httpd namespace");
145 mkconnect(char *scheme, char *port)
149 c = ezalloc(sizeof(HConnect));
151 c->hstop = c->header;
152 c->replog = writelog;
163 p = ezalloc(sizeof(HSPriv));
168 dolisten(char *address)
173 char ndir[NETPATHLEN], dir[NETPATHLEN], *p, *scheme;
174 int ctl, nctl, data, t, ok, spotchk;
178 syslog(0, HTTPLOG, "httpd starting");
179 ctl = announce(address, dir);
181 syslog(0, HTTPLOG, "can't announce on %s: %r", address);
184 strcpy(netdirb, dir);
186 if(netdir[0] == '/'){
187 p = strchr(netdirb+1, '/');
192 strcpy(netdirb, "/net");
198 * wait for a call (or an error)
200 nctl = listen(dir, ndir);
202 syslog(0, HTTPLOG, "can't listen on %s: %r", address);
203 syslog(0, HTTPLOG, "ctls = %d", ctl);
208 * start a process for the service
210 switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFNAMEG)){
216 * see if we know the service requested
218 data = accept(ctl, ndir);
219 if(data >= 0 && certificate != nil){
220 memset(&conn, 0, sizeof(conn));
221 conn.cert = certificate;
222 conn.certlen = certlen;
223 if (certchain != nil)
224 conn.chain = certchain;
225 data = tlsServer(data, &conn);
230 syslog(0, HTTPLOG, "can't open %s/data: %r", ndir);
240 nci = getnetconninfo(ndir, -1);
241 c = mkconnect(scheme, nci->lserv);
243 hp->remotesys = nci->rsys;
244 hp->remoteserv = nci->rserv;
247 hinit(&c->hin, 0, Hread);
248 hinit(&c->hout, 1, Hwrite);
251 * serve requests until a magic request.
252 * later requests have to come quickly.
253 * only works for http/1.1 or later.
255 for(t = 15*60*1000; ; t = 15*1000){
256 if(hparsereq(c, t) <= 0)
262 if(c->head.closeit || ok < 0)
289 char *magic, *uri, *newuri, *origuri, *newpath, *hb;
290 char virtualhost[100], logfd0[10], logfd1[10], vers[16];
295 * munge uri for magic
301 if(++nredirect > 10){
302 if(hparseheaders(c, 15*60*1000) < 0)
304 werrstr("redirection loop");
305 return hfail(c, HNotFound, uri);
307 ss = stripmagic(c, uri);
315 * Apply redirects. Do this before reading headers
316 * (if possible) so that we can redirect to magic invisibly.
319 if(origuri[0]=='/' && origuri[1]=='~'){
320 n = strlen(origuri) + 4 + UTFmax;
321 newpath = halloc(c, n);
322 snprint(newpath, n, "/who/%s", origuri+2);
323 c->req.uri = newpath;
325 }else if(origuri[0]=='/' && origuri[1]==0){
326 /* can't redirect / until we read the headers below */
329 newuri = redirect(c, origuri, &flags);
332 if(flags & Redirsilent) {
333 c->req.uri = uri = newuri;
334 logit(c, "%s: silent replacement %s", origuri, uri);
337 if(hparseheaders(c, 15*60*1000) < 0)
339 if(flags & Redirperm) {
340 logit(c, "%s: permanently moved to %s", origuri, newuri);
341 return hmoved(c, newuri);
342 } else if (flags & (Redironly | Redirsubord))
343 logit(c, "%s: top-level or many-to-one replacement %s",
347 * try temporary redirect instead of permanent
350 return hredirected(c, "307 Temporary Redirect", newuri);
352 return hredirected(c, "302 Temporary Redirect", newuri);
356 * for magic we exec a new program and serve no more requests
359 if(magic != nil && strcmp(magic, "httpd") != 0){
360 snprint(c->xferbuf, HBufSize, "/bin/ip/httpd/%s", magic);
361 snprint(logfd0, sizeof(logfd0), "%d", logall[0]);
362 snprint(logfd1, sizeof(logfd1), "%d", logall[1]);
363 snprint(vers, sizeof(vers), "HTTP/%d.%d", c->req.vermaj, c->req.vermin);
364 hb = hunload(&c->hin);
370 execl(c->xferbuf, magic, "-d", hmydomain, "-w", webroot,
371 "-s", c->scheme, "-p", c->port,
372 "-r", hp->remotesys, "-N", netdir, "-b", hb,
373 "-L", logfd0, logfd1, "-R", c->header,
374 c->req.meth, vers, uri, c->req.search, nil);
375 logit(c, "no magic %s uri %s", magic, uri);
376 hfail(c, HNotFound, uri);
381 * normal case is just file transfer
383 if(hparseheaders(c, 15*60*1000) < 0)
385 if(origuri[0] == '/' && origuri[1] == 0){
386 snprint(virtualhost, sizeof virtualhost, "http://%s/", c->head.host);
387 newuri = redirect(c, virtualhost, nil);
389 newuri = redirect(c, origuri, nil);
391 return hmoved(c, newuri);
393 if(!http11(c) && !c->head.persist)
402 char *w, *w2, *p, *masque;
403 int fd, fd1, n, force301, ok;
407 return hfail(c, HNoSearch, c->req.uri);
409 if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
410 return hunallowed(c, "GET, HEAD");
411 if(c->head.expectother || c->head.expectcont)
412 return hfail(c, HExpectFail);
414 masque = masquerade(c->head.host);
417 * check for directory/file mismatch with trailing /,
418 * and send any redirections.
420 n = strlen(webroot) + strlen(masque) + strlen(c->req.uri) +
421 STRLEN("/index.html") + STRLEN("/.httplogin") + 1;
425 strcat(w, c->req.uri);
428 * favicon can be overridden by hostname.ico
430 if(strcmp(c->req.uri, "/favicon.ico") == 0){
431 w2 = halloc(c, n+strlen(c->head.host)+2);
435 strcat(w2, c->head.host);
437 if(access(w2, AREAD)==0)
442 * don't show the contents of .httplogin
445 if(strcmp(w+n-STRLEN(".httplogin"), ".httplogin") == 0)
446 return notfound(c, c->req.uri);
449 if(fd < 0 && strlen(masque)>0 && strncmp(c->req.uri, masque, strlen(masque)) == 0){
450 // may be a URI from before virtual hosts; try again without masque
452 strcat(w, c->req.uri);
456 return notfound(c, c->req.uri);
460 return hfail(c, HInternal);
463 if(dir->mode & DMDIR){
465 if(p > w && p[-1] == '/'){
466 strcat(w, "index.html");
469 strcat(w, "/index.html");
472 fd1 = open(w, OREAD);
475 return notfound(c, c->req.uri);
477 c->req.uri = w + strlen(webroot) + strlen(masque);
478 if(force301 && c->req.vermaj){
481 return hmoved(c, c->req.uri);
488 return hfail(c, HInternal);
490 }else if(p > w && p[-1] == '/'){
493 *strrchr(c->req.uri, '/') = '\0';
494 return hmoved(c, c->req.uri);
497 ok = authorize(c, w);
504 return sendfd(c, fd, dir, nil, nil);
508 stripmagic(HConnect *hc, char *uri)
511 char *newuri, *prog, *s;
513 prog = stripprefix("/magic/", uri);
520 s = strchr(prog, '/');
524 newuri = hstrdup(hc, s);
527 if(s != nil && s[1] == 0)
536 stripprefix(char *pre, char *str)
545 * couldn't open a file
546 * figure out why and return and error message
549 notfound(HConnect *c, char *url)
552 rerrstr(c->xferbuf, sizeof c->xferbuf);
553 if(strstr(c->xferbuf, "file does not exist") != nil)
554 return hfail(c, HNotFound, url);
555 if(strstr(c->xferbuf, "permission denied") != nil)
556 return hfail(c, HUnauth, url);
557 return hfail(c, HNotFound, url);
565 dn = csquery("sys" , sysname(), "dom");
572 * query the connection server
575 csquery(char *attr, char *val, char *rattr)
578 char buf[256], *p, *sp;
581 if(val == nil || val[0] == 0)
583 snprint(buf, sizeof(buf), "%s/cs", netdir);
584 fd = open(buf, ORDWR);
587 fprint(fd, "!%s=%s", attr, val);
589 snprint(token, sizeof(token), "%s=", rattr);
591 n = read(fd, buf, sizeof(buf)-1);
595 p = strstr(buf, token);
596 if(p != nil && (p == buf || *(p-1) == 0)){