httpd.c 12 KB

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