httpd.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  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;
  254. /*
  255. * munge uri for magic
  256. */
  257. uri = c->req.uri;
  258. nredirect = 0;
  259. top:
  260. if(++nredirect > 10){
  261. if(hparseheaders(c, 15*60*1000) < 0)
  262. exits("failed");
  263. return hfail(c, HNotFound, uri);
  264. }
  265. ss = stripmagic(c, uri);
  266. uri = ss.s1;
  267. origuri = uri;
  268. magic = ss.s2;
  269. if(magic)
  270. goto magic;
  271. /*
  272. * Apply redirects. Do this before reading headers
  273. * (if possible) so that we can redirect to magic invisibly.
  274. */
  275. if(origuri[0]=='/' && origuri[1]=='~'){
  276. n = strlen(origuri) + 4 + UTFmax;
  277. newpath = halloc(c, n);
  278. snprint(newpath, n, "/who/%s", origuri+2);
  279. c->req.uri = newpath;
  280. newuri = newpath;
  281. }else if(origuri[0]=='/' && origuri[1]==0){
  282. /* can't redirect / until we read the headers */
  283. newuri = nil;
  284. }else
  285. newuri = redirect(c, origuri);
  286. if(newuri != nil){
  287. if(newuri[0] == '@'){
  288. c->req.uri = newuri+1;
  289. uri = newuri+1;
  290. goto top;
  291. }
  292. if(hparseheaders(c, 15*60*1000) < 0)
  293. exits("failed");
  294. return hmoved(c, newuri);
  295. }
  296. /*
  297. * for magic we exec a new program and serve no more requests
  298. */
  299. magic:
  300. if(magic != nil && strcmp(magic, "httpd") != 0){
  301. snprint(c->xferbuf, HBufSize, "/bin/ip/httpd/%s", magic);
  302. snprint(logfd0, sizeof(logfd0), "%d", logall[0]);
  303. snprint(logfd1, sizeof(logfd1), "%d", logall[1]);
  304. snprint(vers, sizeof(vers), "HTTP/%d.%d", c->req.vermaj, c->req.vermin);
  305. hb = hunload(&c->hin);
  306. if(hb == nil){
  307. hfail(c, HInternal);
  308. return -1;
  309. }
  310. hp = c->private;
  311. execl(c->xferbuf, magic, "-d", hmydomain, "-w", webroot, "-r", hp->remotesys, "-N", netdir, "-b", hb,
  312. "-L", logfd0, logfd1, "-R", c->header,
  313. c->req.meth, vers, uri, c->req.search, nil);
  314. logit(c, "no magic %s uri %s", magic, uri);
  315. hfail(c, HNotFound, uri);
  316. return -1;
  317. }
  318. /*
  319. * normal case is just file transfer
  320. */
  321. if(hparseheaders(c, 15*60*1000) < 0)
  322. exits("failed");
  323. if(origuri[0] == '/' && origuri[1] == 0){
  324. snprint(virtualhost, sizeof virtualhost, "http://%s/", c->head.host);
  325. newuri = redirect(c, virtualhost);
  326. if(newuri == nil)
  327. newuri = redirect(c, origuri);
  328. if(newuri)
  329. return hmoved(c, newuri);
  330. }
  331. if(!http11(c) && !c->head.persist)
  332. c->head.closeit = 1;
  333. return send(c);
  334. }
  335. static int
  336. send(HConnect *c)
  337. {
  338. Dir *dir;
  339. char *w, *w2, *p, *masque;
  340. int fd, fd1, n, force301, ok;
  341. if(c->req.search)
  342. return hfail(c, HNoSearch, c->req.uri);
  343. if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
  344. return hunallowed(c, "GET, HEAD");
  345. if(c->head.expectother || c->head.expectcont)
  346. return hfail(c, HExpectFail);
  347. masque = masquerade(c->head.host);
  348. /*
  349. * check for directory/file mismatch with trailing /,
  350. * and send any redirections.
  351. */
  352. n = strlen(webroot) + strlen(masque) + strlen(c->req.uri) +
  353. STRLEN("/index.html") + STRLEN("/.httplogin") + 1;
  354. w = halloc(c, n);
  355. strcpy(w, webroot);
  356. strcat(w, masque);
  357. strcat(w, c->req.uri);
  358. /*
  359. * favicon can be overridden by hostname.ico
  360. */
  361. if(strcmp(c->req.uri, "/favicon.ico") == 0){
  362. w2 = halloc(c, n+strlen(c->head.host)+2);
  363. strcpy(w2, webroot);
  364. strcat(w2, masque);
  365. strcat(w2, "/");
  366. strcat(w2, c->head.host);
  367. strcat(w2, ".ico");
  368. if(access(w2, AREAD)==0)
  369. w = w2;
  370. }
  371. /*
  372. * don't show the contents of .httplogin
  373. */
  374. n = strlen(w);
  375. if(strcmp(w+n-STRLEN(".httplogin"), ".httplogin") == 0)
  376. return notfound(c, c->req.uri);
  377. fd = open(w, OREAD);
  378. if(fd < 0 && strlen(masque)>0 && strncmp(c->req.uri, masque, strlen(masque)) == 0){
  379. // may be a URI from before virtual hosts; try again without masque
  380. strcpy(w, webroot);
  381. strcat(w, c->req.uri);
  382. fd = open(w, OREAD);
  383. }
  384. if(fd < 0)
  385. return notfound(c, c->req.uri);
  386. dir = dirfstat(fd);
  387. if(dir == nil){
  388. close(fd);
  389. return hfail(c, HInternal);
  390. }
  391. p = strchr(w, '\0');
  392. if(dir->mode & DMDIR){
  393. free(dir);
  394. if(p > w && p[-1] == '/'){
  395. strcat(w, "index.html");
  396. force301 = 0;
  397. }else{
  398. strcat(w, "/index.html");
  399. force301 = 1;
  400. }
  401. fd1 = open(w, OREAD);
  402. if(fd1 < 0){
  403. close(fd);
  404. return notfound(c, c->req.uri);
  405. }
  406. c->req.uri = w + strlen(webroot) + strlen(masque);
  407. if(force301 && c->req.vermaj){
  408. close(fd);
  409. close(fd1);
  410. return hmoved(c, c->req.uri);
  411. }
  412. close(fd);
  413. fd = fd1;
  414. dir = dirfstat(fd);
  415. if(dir == nil){
  416. close(fd);
  417. return hfail(c, HInternal);
  418. }
  419. }else if(p > w && p[-1] == '/'){
  420. free(dir);
  421. close(fd);
  422. *strrchr(c->req.uri, '/') = '\0';
  423. return hmoved(c, c->req.uri);
  424. }
  425. ok = authorize(c, w);
  426. if(ok <= 0){
  427. free(dir);
  428. close(fd);
  429. return ok;
  430. }
  431. return sendfd(c, fd, dir, nil, nil);
  432. }
  433. static Strings
  434. stripmagic(HConnect *hc, char *uri)
  435. {
  436. Strings ss;
  437. char *newuri, *prog, *s;
  438. prog = stripprefix("/magic/", uri);
  439. if(prog == nil){
  440. ss.s1 = uri;
  441. ss.s2 = nil;
  442. return ss;
  443. }
  444. s = strchr(prog, '/');
  445. if(s == nil)
  446. newuri = "";
  447. else{
  448. newuri = hstrdup(hc, s);
  449. *s = 0;
  450. s = strrchr(s, '/');
  451. if(s != nil && s[1] == 0)
  452. *s = 0;
  453. }
  454. ss.s1 = newuri;
  455. ss.s2 = prog;
  456. return ss;
  457. }
  458. static char*
  459. stripprefix(char *pre, char *str)
  460. {
  461. while(*pre)
  462. if(*str++ != *pre++)
  463. return nil;
  464. return str;
  465. }
  466. /*
  467. * couldn't open a file
  468. * figure out why and return and error message
  469. */
  470. static int
  471. notfound(HConnect *c, char *url)
  472. {
  473. c->xferbuf[0] = 0;
  474. errstr(c->xferbuf, sizeof c->xferbuf);
  475. if(strstr(c->xferbuf, "file does not exist") != nil)
  476. return hfail(c, HNotFound, url);
  477. if(strstr(c->xferbuf, "permission denied") != nil)
  478. return hfail(c, HUnauth, url);
  479. return hfail(c, HNotFound, url);
  480. }
  481. static char*
  482. sysdom(void)
  483. {
  484. char *dn;
  485. dn = csquery("sys" , sysname(), "dom");
  486. if(dn == nil)
  487. dn = "who cares";
  488. return dn;
  489. }
  490. /*
  491. * query the connection server
  492. */
  493. static char*
  494. csquery(char *attr, char *val, char *rattr)
  495. {
  496. char token[64+4];
  497. char buf[256], *p, *sp;
  498. int fd, n;
  499. if(val == nil || val[0] == 0)
  500. return nil;
  501. snprint(buf, sizeof(buf), "%s/cs", netdir);
  502. fd = open(buf, ORDWR);
  503. if(fd < 0)
  504. return nil;
  505. fprint(fd, "!%s=%s", attr, val);
  506. seek(fd, 0, 0);
  507. snprint(token, sizeof(token), "%s=", rattr);
  508. for(;;){
  509. n = read(fd, buf, sizeof(buf)-1);
  510. if(n <= 0)
  511. break;
  512. buf[n] = 0;
  513. p = strstr(buf, token);
  514. if(p != nil && (p == buf || *(p-1) == 0)){
  515. close(fd);
  516. sp = strchr(p, ' ');
  517. if(sp)
  518. *sp = 0;
  519. p = strchr(p, '=');
  520. if(p == nil)
  521. return nil;
  522. return estrdup(p+1);
  523. }
  524. }
  525. close(fd);
  526. return nil;
  527. }