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;
177 syslog(0, HTTPLOG, "httpd starting");
178 ctl = announce(address, dir);
180 syslog(0, HTTPLOG, "can't announce on %s: %r", address);
183 strcpy(netdirb, dir);
185 if(netdir[0] == '/'){
186 p = strchr(netdirb+1, '/');
191 strcpy(netdirb, "/net");
197 * wait for a call (or an error)
199 nctl = listen(dir, ndir);
201 syslog(0, HTTPLOG, "can't listen on %s: %r", address);
202 syslog(0, HTTPLOG, "ctls = %d", ctl);
207 * start a process for the service
209 switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFNAMEG)){
215 * see if we know the service requested
217 data = accept(ctl, ndir);
218 if(data >= 0 && certificate != nil){
221 memset(&conn, 0, sizeof(conn));
222 conn.cert = certificate;
223 conn.certlen = certlen;
224 if (certchain != nil)
225 conn.chain = certchain;
226 data = tlsServer(data, &conn);
228 free(conn.sessionID);
233 syslog(0, HTTPLOG, "can't open %s/data: %r", ndir);
243 nci = getnetconninfo(ndir, -1);
244 c = mkconnect(scheme, nci->lserv);
246 hp->remotesys = nci->rsys;
247 hp->remoteserv = nci->rserv;
250 hinit(&c->hin, 0, Hread);
251 hinit(&c->hout, 1, Hwrite);
254 * serve requests until a magic request.
255 * later requests have to come quickly.
256 * only works for http/1.1 or later.
258 for(t = 15*60*1000; ; t = 15*1000){
259 if(hparsereq(c, t) <= 0)
265 if(c->head.closeit || ok < 0)
292 char *magic, *uri, *newuri, *origuri, *newpath, *hb;
293 char virtualhost[100], logfd0[10], logfd1[10], vers[16];
298 * munge uri for magic
304 if(++nredirect > 10){
305 if(hparseheaders(c, 15*60*1000) < 0)
307 werrstr("redirection loop");
308 return hfail(c, HNotFound, uri);
310 ss = stripmagic(c, uri);
318 * Apply redirects. Do this before reading headers
319 * (if possible) so that we can redirect to magic invisibly.
322 if(origuri[0]=='/' && origuri[1]=='~'){
323 n = strlen(origuri) + 4 + UTFmax;
324 newpath = halloc(c, n);
325 snprint(newpath, n, "/who/%s", origuri+2);
326 c->req.uri = newpath;
328 }else if(origuri[0]=='/' && origuri[1]==0){
329 /* can't redirect / until we read the headers below */
332 newuri = redirect(c, origuri, &flags);
335 if(flags & Redirsilent) {
336 c->req.uri = uri = newuri;
337 logit(c, "%s: silent replacement %s", origuri, uri);
340 if(hparseheaders(c, 15*60*1000) < 0)
342 if(flags & Redirperm) {
343 logit(c, "%s: permanently moved to %s", origuri, newuri);
344 return hmoved(c, newuri);
345 } else if (flags & (Redironly | Redirsubord))
346 logit(c, "%s: top-level or many-to-one replacement %s",
350 * try temporary redirect instead of permanent
353 return hredirected(c, "307 Temporary Redirect", newuri);
355 return hredirected(c, "302 Temporary Redirect", newuri);
359 * for magic we exec a new program and serve no more requests
362 if(magic != nil && strcmp(magic, "httpd") != 0){
363 snprint(c->xferbuf, HBufSize, "/bin/ip/httpd/%s", magic);
364 snprint(logfd0, sizeof(logfd0), "%d", logall[0]);
365 snprint(logfd1, sizeof(logfd1), "%d", logall[1]);
366 snprint(vers, sizeof(vers), "HTTP/%d.%d", c->req.vermaj, c->req.vermin);
367 hb = hunload(&c->hin);
373 execl(c->xferbuf, magic, "-d", hmydomain, "-w", webroot,
374 "-s", c->scheme, "-p", c->port,
375 "-r", hp->remotesys, "-N", netdir, "-b", hb,
376 "-L", logfd0, logfd1, "-R", c->header,
377 c->req.meth, vers, uri, c->req.search, nil);
378 logit(c, "no magic %s uri %s", magic, uri);
379 hfail(c, HNotFound, uri);
384 * normal case is just file transfer
386 if(hparseheaders(c, 15*60*1000) < 0)
388 if(origuri[0] == '/' && origuri[1] == 0){
389 snprint(virtualhost, sizeof virtualhost, "http://%s/", c->head.host);
390 newuri = redirect(c, virtualhost, nil);
392 newuri = redirect(c, origuri, nil);
394 return hmoved(c, newuri);
396 if(!http11(c) && !c->head.persist)
405 char *w, *w2, *p, *masque;
406 int fd, fd1, n, force301, ok;
410 return hfail(c, HNoSearch, c->req.uri);
412 if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
413 return hunallowed(c, "GET, HEAD");
414 if(c->head.expectother || c->head.expectcont)
415 return hfail(c, HExpectFail);
417 masque = masquerade(c->head.host);
420 * check for directory/file mismatch with trailing /,
421 * and send any redirections.
423 n = strlen(webroot) + strlen(masque) + strlen(c->req.uri) +
424 STRLEN("/index.html") + STRLEN("/.httplogin") + 1;
428 strcat(w, c->req.uri);
431 * favicon can be overridden by hostname.ico
433 if(strcmp(c->req.uri, "/favicon.ico") == 0){
434 w2 = halloc(c, n+strlen(c->head.host)+2);
438 strcat(w2, c->head.host);
440 if(access(w2, AREAD)==0)
445 * don't show the contents of .httplogin
448 if(strcmp(w+n-STRLEN(".httplogin"), ".httplogin") == 0)
449 return notfound(c, c->req.uri);
452 if(fd < 0 && strlen(masque)>0 && strncmp(c->req.uri, masque, strlen(masque)) == 0){
453 // may be a URI from before virtual hosts; try again without masque
455 strcat(w, c->req.uri);
459 return notfound(c, c->req.uri);
463 return hfail(c, HInternal);
466 if(dir->mode & DMDIR){
468 if(p > w && p[-1] == '/'){
469 strcat(w, "index.html");
472 strcat(w, "/index.html");
475 fd1 = open(w, OREAD);
478 return notfound(c, c->req.uri);
480 c->req.uri = w + strlen(webroot) + strlen(masque);
481 if(force301 && c->req.vermaj){
484 return hmoved(c, c->req.uri);
491 return hfail(c, HInternal);
493 }else if(p > w && p[-1] == '/'){
496 *strrchr(c->req.uri, '/') = '\0';
497 return hmoved(c, c->req.uri);
500 ok = authorize(c, w);
507 return sendfd(c, fd, dir, nil, nil);
511 stripmagic(HConnect *hc, char *uri)
514 char *newuri, *prog, *s;
516 prog = stripprefix("/magic/", uri);
523 s = strchr(prog, '/');
527 newuri = hstrdup(hc, s);
530 if(s != nil && s[1] == 0)
539 stripprefix(char *pre, char *str)
548 * couldn't open a file
549 * figure out why and return and error message
552 notfound(HConnect *c, char *url)
555 rerrstr(c->xferbuf, sizeof c->xferbuf);
556 if(strstr(c->xferbuf, "file does not exist") != nil)
557 return hfail(c, HNotFound, url);
558 if(strstr(c->xferbuf, "permission denied") != nil)
559 return hfail(c, HUnauth, url);
560 return hfail(c, HNotFound, url);
568 dn = csquery("sys" , sysname(), "dom");
575 * query the connection server
578 csquery(char *attr, char *val, char *rattr)
581 char buf[256], *p, *sp;
584 if(val == nil || val[0] == 0)
586 snprint(buf, sizeof(buf), "%s/cs", netdir);
587 fd = open(buf, ORDWR);
590 fprint(fd, "!%s=%s", attr, val);
592 snprint(token, sizeof(token), "%s=", rattr);
594 n = read(fd, buf, sizeof(buf)-1);
598 p = strstr(buf, token);
599 if(p != nil && (p == buf || *(p-1) == 0)){