httpd.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <auth.h>
  4. #include <mp.h>
  5. #include <libsec.h>
  6. #include "httpd.h"
  7. #include "httpsrv.h"
  8. enum {
  9. Nbuckets = 256,
  10. };
  11. typedef struct Strings Strings;
  12. typedef struct System System;
  13. struct Strings
  14. {
  15. char *s1;
  16. char *s2;
  17. };
  18. struct System {
  19. char *rsys;
  20. ulong reqs;
  21. ulong first;
  22. ulong last;
  23. System *next; /* next in chain */
  24. };
  25. char *netdir;
  26. char *HTTPLOG = "httpd/log";
  27. static char netdirb[256];
  28. static char *namespace;
  29. static System syss[Nbuckets];
  30. static void becomenone(char*);
  31. static char *csquery(char*, char*, char*);
  32. static void dolisten(char*);
  33. static int doreq(HConnect*);
  34. static int send(HConnect*);
  35. static Strings stripmagic(HConnect*, char*);
  36. static char* stripprefix(char*, char*);
  37. static char* sysdom(void);
  38. static int notfound(HConnect *c, char *url);
  39. uchar *certificate;
  40. int certlen;
  41. PEMChain *certchain;
  42. void
  43. usage(void)
  44. {
  45. fprint(2, "usage: httpd [-c certificate] [-C CAchain] [-a srvaddress] "
  46. "[-d domain] [-n namespace] [-w webroot]\n");
  47. exits("usage");
  48. }
  49. void
  50. main(int argc, char **argv)
  51. {
  52. char *address;
  53. namespace = nil;
  54. address = nil;
  55. hmydomain = nil;
  56. netdir = "/net";
  57. fmtinstall('D', hdatefmt);
  58. fmtinstall('H', httpfmt);
  59. fmtinstall('U', hurlfmt);
  60. ARGBEGIN{
  61. case 'c':
  62. certificate = readcert(EARGF(usage()), &certlen);
  63. if(certificate == nil)
  64. sysfatal("reading certificate: %r");
  65. break;
  66. case 'C':
  67. certchain = readcertchain(EARGF(usage()));
  68. if (certchain == nil)
  69. sysfatal("reading certificate chain: %r");
  70. break;
  71. case 'n':
  72. namespace = EARGF(usage());
  73. break;
  74. case 'a':
  75. address = EARGF(usage());
  76. break;
  77. case 'd':
  78. hmydomain = EARGF(usage());
  79. break;
  80. case 'w':
  81. webroot = EARGF(usage());
  82. break;
  83. default:
  84. usage();
  85. break;
  86. }ARGEND
  87. if(argc)
  88. usage();
  89. if(namespace == nil)
  90. namespace = "/lib/namespace.httpd";
  91. if(address == nil)
  92. address = "*";
  93. if(webroot == nil)
  94. webroot = "/usr/web";
  95. else{
  96. cleanname(webroot);
  97. if(webroot[0] != '/')
  98. webroot = "/usr/web";
  99. }
  100. switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNAMEG)) {
  101. case -1:
  102. sysfatal("fork");
  103. case 0:
  104. break;
  105. default:
  106. exits(nil);
  107. }
  108. /*
  109. * open all files we might need before castrating namespace
  110. */
  111. time(nil);
  112. if(hmydomain == nil)
  113. hmydomain = sysdom();
  114. syslog(0, HTTPLOG, nil);
  115. logall[0] = open("/sys/log/httpd/0", OWRITE);
  116. logall[1] = open("/sys/log/httpd/1", OWRITE);
  117. logall[2] = open("/sys/log/httpd/clf", OWRITE);
  118. redirectinit();
  119. contentinit();
  120. urlinit();
  121. statsinit();
  122. becomenone(namespace);
  123. dolisten(netmkaddr(address, "tcp", certificate == nil ? "http" : "https"));
  124. exits(nil);
  125. }
  126. static void
  127. becomenone(char *namespace)
  128. {
  129. int fd;
  130. fd = open("#c/user", OWRITE);
  131. if(fd < 0 || write(fd, "none", strlen("none")) < 0)
  132. sysfatal("can't become none");
  133. close(fd);
  134. if(newns("none", nil) < 0)
  135. sysfatal("can't build normal namespace");
  136. if(addns("none", namespace) < 0)
  137. sysfatal("can't build httpd namespace");
  138. }
  139. static HConnect*
  140. mkconnect(char *scheme, char *port)
  141. {
  142. HConnect *c;
  143. c = ezalloc(sizeof(HConnect));
  144. c->hpos = c->header;
  145. c->hstop = c->header;
  146. c->replog = writelog;
  147. c->scheme = scheme;
  148. c->port = port;
  149. return c;
  150. }
  151. static HSPriv*
  152. mkhspriv(void)
  153. {
  154. HSPriv *p;
  155. p = ezalloc(sizeof(HSPriv));
  156. return p;
  157. }
  158. static uint
  159. hashstr(char* key)
  160. {
  161. /* asu works better than pjw for urls */
  162. uchar *k = (unsigned char*)key;
  163. uint h = 0;
  164. while(*k!=0)
  165. h = 65599*h + *k++;
  166. return h;
  167. }
  168. static System *
  169. hashsys(char *rsys)
  170. {
  171. int notme;
  172. System *sys;
  173. sys = syss + hashstr(rsys) % nelem(syss);
  174. /* if the bucket is empty, just use it, else find or allocate ours */
  175. if(sys->rsys != nil) {
  176. /* find match or chain end */
  177. for(; notme = (strcmp(sys->rsys, rsys) != 0) &&
  178. sys->next != nil; sys = sys->next)
  179. ;
  180. if(notme) {
  181. sys->next = malloc(sizeof *sys); /* extend chain */
  182. sys = sys->next;
  183. } else
  184. return sys;
  185. }
  186. if(sys != nil) {
  187. memset(sys, 0, sizeof *sys);
  188. sys->rsys = strdup(rsys);
  189. }
  190. return sys;
  191. }
  192. /*
  193. * be sure to call this at least once per listen in the parent,
  194. * to update the hash chains.
  195. * it's okay to call it in the child too, but then sys->reqs only gets
  196. * updated in the child.
  197. */
  198. static int
  199. isswamped(char *rsys)
  200. {
  201. ulong period;
  202. System *sys = hashsys(rsys);
  203. if(sys == nil)
  204. return 0;
  205. sys->last = time(nil);
  206. if(sys->first == 0)
  207. sys->first = sys->last;
  208. period = sys->first - sys->last;
  209. return ++sys->reqs > 30 && period > 30 && sys->reqs / period >= 2;
  210. }
  211. /* must only be called in child */
  212. static void
  213. throttle(int nctl, NetConnInfo *nci, int swamped)
  214. {
  215. if(swamped || isswamped(nci->rsys)) { /* shed load */
  216. syslog(0, HTTPLOG, "overloaded by %s", nci->rsys);
  217. sleep(30);
  218. close(nctl);
  219. exits(nil);
  220. }
  221. }
  222. static void
  223. dolisten(char *address)
  224. {
  225. HSPriv *hp;
  226. HConnect *c;
  227. NetConnInfo *nci;
  228. char ndir[NETPATHLEN], dir[NETPATHLEN], *p, *scheme;
  229. int ctl, nctl, data, t, ok, spotchk, swamped;
  230. TLSconn conn;
  231. spotchk = 0;
  232. syslog(0, HTTPLOG, "httpd starting");
  233. ctl = announce(address, dir);
  234. if(ctl < 0){
  235. syslog(0, HTTPLOG, "can't announce on %s: %r", address);
  236. return;
  237. }
  238. strcpy(netdirb, dir);
  239. p = nil;
  240. if(netdir[0] == '/'){
  241. p = strchr(netdirb+1, '/');
  242. if(p != nil)
  243. *p = '\0';
  244. }
  245. if(p == nil)
  246. strcpy(netdirb, "/net");
  247. netdir = netdirb;
  248. for(;;){
  249. /*
  250. * wait for a call (or an error)
  251. */
  252. nctl = listen(dir, ndir);
  253. if(nctl < 0){
  254. syslog(0, HTTPLOG, "can't listen on %s: %r", address);
  255. syslog(0, HTTPLOG, "ctls = %d", ctl);
  256. return;
  257. }
  258. swamped = 0;
  259. nci = getnetconninfo(ndir, -1);
  260. if (nci)
  261. swamped = isswamped(nci->rsys);
  262. /*
  263. * start a process for the service
  264. */
  265. switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFNAMEG)){
  266. case -1:
  267. close(nctl);
  268. continue;
  269. case 0:
  270. /*
  271. * see if we know the service requested
  272. */
  273. data = accept(ctl, ndir);
  274. if(data >= 0 && certificate != nil){
  275. memset(&conn, 0, sizeof(conn));
  276. conn.cert = certificate;
  277. conn.certlen = certlen;
  278. if (certchain != nil)
  279. conn.chain = certchain;
  280. data = tlsServer(data, &conn);
  281. scheme = "https";
  282. }else
  283. scheme = "http";
  284. if(data < 0){
  285. syslog(0, HTTPLOG, "can't open %s/data: %r", ndir);
  286. exits(nil);
  287. }
  288. dup(data, 0);
  289. dup(data, 1);
  290. dup(data, 2);
  291. close(data);
  292. close(ctl);
  293. close(nctl);
  294. if (nci == nil)
  295. nci = getnetconninfo(ndir, -1);
  296. c = mkconnect(scheme, nci->lserv);
  297. hp = mkhspriv();
  298. hp->remotesys = nci->rsys;
  299. hp->remoteserv = nci->rserv;
  300. c->private = hp;
  301. hinit(&c->hin, 0, Hread);
  302. hinit(&c->hout, 1, Hwrite);
  303. /*
  304. * serve requests until a magic request.
  305. * later requests have to come quickly.
  306. * only works for http/1.1 or later.
  307. */
  308. for(t = 15*60*1000; ; t = 15*1000){
  309. throttle(nctl, nci, swamped);
  310. if(hparsereq(c, t) <= 0)
  311. exits(nil);
  312. ok = doreq(c);
  313. hflush(&c->hout);
  314. if(c->head.closeit || ok < 0)
  315. exits(nil);
  316. hreqcleanup(c);
  317. }
  318. /* not reached */
  319. default:
  320. close(nctl);
  321. break;
  322. }
  323. if(++spotchk > 50){
  324. spotchk = 0;
  325. redirectinit();
  326. contentinit();
  327. urlinit();
  328. statsinit();
  329. }
  330. }
  331. }
  332. static int
  333. doreq(HConnect *c)
  334. {
  335. HSPriv *hp;
  336. Strings ss;
  337. char *magic, *uri, *newuri, *origuri, *newpath, *hb;
  338. char virtualhost[100], logfd0[10], logfd1[10], vers[16];
  339. int n, nredirect;
  340. uint flags;
  341. /*
  342. * munge uri for magic
  343. */
  344. uri = c->req.uri;
  345. nredirect = 0;
  346. werrstr("");
  347. top:
  348. if(++nredirect > 10){
  349. if(hparseheaders(c, 15*60*1000) < 0)
  350. exits("failed");
  351. werrstr("redirection loop");
  352. return hfail(c, HNotFound, uri);
  353. }
  354. ss = stripmagic(c, uri);
  355. uri = ss.s1;
  356. origuri = uri;
  357. magic = ss.s2;
  358. if(magic)
  359. goto magic;
  360. /*
  361. * Apply redirects. Do this before reading headers
  362. * (if possible) so that we can redirect to magic invisibly.
  363. */
  364. flags = 0;
  365. if(origuri[0]=='/' && origuri[1]=='~'){
  366. n = strlen(origuri) + 4 + UTFmax;
  367. newpath = halloc(c, n);
  368. snprint(newpath, n, "/who/%s", origuri+2);
  369. c->req.uri = newpath;
  370. newuri = newpath;
  371. }else if(origuri[0]=='/' && origuri[1]==0){
  372. /* can't redirect / until we read the headers below */
  373. newuri = nil;
  374. }else
  375. newuri = redirect(c, origuri, &flags);
  376. if(newuri != nil){
  377. if(flags & Redirsilent) {
  378. c->req.uri = uri = newuri;
  379. logit(c, "%s: silent replacement %s", origuri, uri);
  380. goto top;
  381. }
  382. if(hparseheaders(c, 15*60*1000) < 0)
  383. exits("failed");
  384. if(flags & Redirperm) {
  385. logit(c, "%s: permanently moved to %s", origuri, newuri);
  386. return hmoved(c, newuri);
  387. } else if (flags & (Redironly | Redirsubord))
  388. logit(c, "%s: top-level or many-to-one replacement %s",
  389. origuri, uri);
  390. /*
  391. * try temporary redirect instead of permanent
  392. */
  393. if (http11(c))
  394. return hredirected(c, "307 Temporary Redirect", newuri);
  395. else
  396. return hredirected(c, "302 Temporary Redirect", newuri);
  397. }
  398. /*
  399. * for magic we exec a new program and serve no more requests
  400. */
  401. magic:
  402. if(magic != nil && strcmp(magic, "httpd") != 0){
  403. snprint(c->xferbuf, HBufSize, "/bin/ip/httpd/%s", magic);
  404. snprint(logfd0, sizeof(logfd0), "%d", logall[0]);
  405. snprint(logfd1, sizeof(logfd1), "%d", logall[1]);
  406. snprint(vers, sizeof(vers), "HTTP/%d.%d", c->req.vermaj, c->req.vermin);
  407. hb = hunload(&c->hin);
  408. if(hb == nil){
  409. hfail(c, HInternal);
  410. return -1;
  411. }
  412. hp = c->private;
  413. execl(c->xferbuf, magic, "-d", hmydomain, "-w", webroot,
  414. "-s", c->scheme, "-p", c->port,
  415. "-r", hp->remotesys, "-N", netdir, "-b", hb,
  416. "-L", logfd0, logfd1, "-R", c->header,
  417. c->req.meth, vers, uri, c->req.search, nil);
  418. logit(c, "no magic %s uri %s", magic, uri);
  419. hfail(c, HNotFound, uri);
  420. return -1;
  421. }
  422. /*
  423. * normal case is just file transfer
  424. */
  425. if(hparseheaders(c, 15*60*1000) < 0)
  426. exits("failed");
  427. if(origuri[0] == '/' && origuri[1] == 0){
  428. snprint(virtualhost, sizeof virtualhost, "http://%s/", c->head.host);
  429. newuri = redirect(c, virtualhost, nil);
  430. if(newuri == nil)
  431. newuri = redirect(c, origuri, nil);
  432. if(newuri)
  433. return hmoved(c, newuri);
  434. }
  435. if(!http11(c) && !c->head.persist)
  436. c->head.closeit = 1;
  437. return send(c);
  438. }
  439. static int
  440. send(HConnect *c)
  441. {
  442. Dir *dir;
  443. char *w, *w2, *p, *masque;
  444. int fd, fd1, n, force301, ok;
  445. /*
  446. if(c->req.search)
  447. return hfail(c, HNoSearch, c->req.uri);
  448. */
  449. if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
  450. return hunallowed(c, "GET, HEAD");
  451. if(c->head.expectother || c->head.expectcont)
  452. return hfail(c, HExpectFail);
  453. masque = masquerade(c->head.host);
  454. /*
  455. * check for directory/file mismatch with trailing /,
  456. * and send any redirections.
  457. */
  458. n = strlen(webroot) + strlen(masque) + strlen(c->req.uri) +
  459. STRLEN("/index.html") + STRLEN("/.httplogin") + 1;
  460. w = halloc(c, n);
  461. strcpy(w, webroot);
  462. strcat(w, masque);
  463. strcat(w, c->req.uri);
  464. /*
  465. * favicon can be overridden by hostname.ico
  466. */
  467. if(strcmp(c->req.uri, "/favicon.ico") == 0){
  468. w2 = halloc(c, n+strlen(c->head.host)+2);
  469. strcpy(w2, webroot);
  470. strcat(w2, masque);
  471. strcat(w2, "/");
  472. strcat(w2, c->head.host);
  473. strcat(w2, ".ico");
  474. if(access(w2, AREAD)==0)
  475. w = w2;
  476. }
  477. /*
  478. * don't show the contents of .httplogin
  479. */
  480. n = strlen(w);
  481. if(strcmp(w+n-STRLEN(".httplogin"), ".httplogin") == 0)
  482. return notfound(c, c->req.uri);
  483. fd = open(w, OREAD);
  484. if(fd < 0 && strlen(masque)>0 && strncmp(c->req.uri, masque, strlen(masque)) == 0){
  485. // may be a URI from before virtual hosts; try again without masque
  486. strcpy(w, webroot);
  487. strcat(w, c->req.uri);
  488. fd = open(w, OREAD);
  489. }
  490. if(fd < 0)
  491. return notfound(c, c->req.uri);
  492. dir = dirfstat(fd);
  493. if(dir == nil){
  494. close(fd);
  495. return hfail(c, HInternal);
  496. }
  497. p = strchr(w, '\0');
  498. if(dir->mode & DMDIR){
  499. free(dir);
  500. if(p > w && p[-1] == '/'){
  501. strcat(w, "index.html");
  502. force301 = 0;
  503. }else{
  504. strcat(w, "/index.html");
  505. force301 = 1;
  506. }
  507. fd1 = open(w, OREAD);
  508. if(fd1 < 0){
  509. close(fd);
  510. return notfound(c, c->req.uri);
  511. }
  512. c->req.uri = w + strlen(webroot) + strlen(masque);
  513. if(force301 && c->req.vermaj){
  514. close(fd);
  515. close(fd1);
  516. return hmoved(c, c->req.uri);
  517. }
  518. close(fd);
  519. fd = fd1;
  520. dir = dirfstat(fd);
  521. if(dir == nil){
  522. close(fd);
  523. return hfail(c, HInternal);
  524. }
  525. }else if(p > w && p[-1] == '/'){
  526. free(dir);
  527. close(fd);
  528. *strrchr(c->req.uri, '/') = '\0';
  529. return hmoved(c, c->req.uri);
  530. }
  531. ok = authorize(c, w);
  532. if(ok <= 0){
  533. free(dir);
  534. close(fd);
  535. return ok;
  536. }
  537. return sendfd(c, fd, dir, nil, nil);
  538. }
  539. static Strings
  540. stripmagic(HConnect *hc, char *uri)
  541. {
  542. Strings ss;
  543. char *newuri, *prog, *s;
  544. prog = stripprefix("/magic/", uri);
  545. if(prog == nil){
  546. ss.s1 = uri;
  547. ss.s2 = nil;
  548. return ss;
  549. }
  550. s = strchr(prog, '/');
  551. if(s == nil)
  552. newuri = "";
  553. else{
  554. newuri = hstrdup(hc, s);
  555. *s = 0;
  556. s = strrchr(s, '/');
  557. if(s != nil && s[1] == 0)
  558. *s = 0;
  559. }
  560. ss.s1 = newuri;
  561. ss.s2 = prog;
  562. return ss;
  563. }
  564. static char*
  565. stripprefix(char *pre, char *str)
  566. {
  567. while(*pre)
  568. if(*str++ != *pre++)
  569. return nil;
  570. return str;
  571. }
  572. /*
  573. * couldn't open a file
  574. * figure out why and return and error message
  575. */
  576. static int
  577. notfound(HConnect *c, char *url)
  578. {
  579. c->xferbuf[0] = 0;
  580. rerrstr(c->xferbuf, sizeof c->xferbuf);
  581. if(strstr(c->xferbuf, "file does not exist") != nil)
  582. return hfail(c, HNotFound, url);
  583. if(strstr(c->xferbuf, "permission denied") != nil)
  584. return hfail(c, HUnauth, url);
  585. return hfail(c, HNotFound, url);
  586. }
  587. static char*
  588. sysdom(void)
  589. {
  590. char *dn;
  591. dn = csquery("sys" , sysname(), "dom");
  592. if(dn == nil)
  593. dn = "who cares";
  594. return dn;
  595. }
  596. /*
  597. * query the connection server
  598. */
  599. static char*
  600. csquery(char *attr, char *val, char *rattr)
  601. {
  602. char token[64+4];
  603. char buf[256], *p, *sp;
  604. int fd, n;
  605. if(val == nil || val[0] == 0)
  606. return nil;
  607. snprint(buf, sizeof(buf), "%s/cs", netdir);
  608. fd = open(buf, ORDWR);
  609. if(fd < 0)
  610. return nil;
  611. fprint(fd, "!%s=%s", attr, val);
  612. seek(fd, 0, 0);
  613. snprint(token, sizeof(token), "%s=", rattr);
  614. for(;;){
  615. n = read(fd, buf, sizeof(buf)-1);
  616. if(n <= 0)
  617. break;
  618. buf[n] = 0;
  619. p = strstr(buf, token);
  620. if(p != nil && (p == buf || *(p-1) == 0)){
  621. close(fd);
  622. sp = strchr(p, ' ');
  623. if(sp)
  624. *sp = 0;
  625. p = strchr(p, '=');
  626. if(p == nil)
  627. return nil;
  628. return estrdup(p+1);
  629. }
  630. }
  631. close(fd);
  632. return nil;
  633. }