/contrib/bind9/lib/isc/httpd.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 1028 lines · 688 code · 166 blank · 174 comment · 165 complexity · 3a11c6d84fbf32ffc2c9d0ce6c4079b7 MD5 · raw file

  1. /*
  2. * Copyright (C) 2006-2008, 2010-2012 Internet Systems Consortium, Inc. ("ISC")
  3. *
  4. * Permission to use, copy, modify, and/or distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  9. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  10. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  12. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  13. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  14. * PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. /* $Id$ */
  17. /*! \file */
  18. #include <config.h>
  19. #include <isc/buffer.h>
  20. #include <isc/httpd.h>
  21. #include <isc/mem.h>
  22. #include <isc/socket.h>
  23. #include <isc/string.h>
  24. #include <isc/task.h>
  25. #include <isc/util.h>
  26. #include <string.h>
  27. /*%
  28. * TODO:
  29. *
  30. * o Put in better checks to make certain things are passed in correctly.
  31. * This includes a magic number for externally-visible structures,
  32. * checking for NULL-ness before dereferencing, etc.
  33. * o Make the URL processing external functions which will fill-in a buffer
  34. * structure we provide, or return an error and we will render a generic
  35. * page and close the client.
  36. */
  37. #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
  38. #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
  39. #ifdef DEBUG_HTTPD
  40. #define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0)
  41. #define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0)
  42. #define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0)
  43. #else
  44. #define ENTER(x) do { } while(0)
  45. #define EXIT(x) do { } while(0)
  46. #define NOTICE(x) do { } while(0)
  47. #endif
  48. #define HTTP_RECVLEN 1024
  49. #define HTTP_SENDGROW 1024
  50. #define HTTP_SEND_MAXLEN 10240
  51. /*%
  52. * HTTP urls. These are the URLs we manage, and the function to call to
  53. * provide the data for it. We pass in the base url (so the same function
  54. * can handle multiple requests), and a structure to fill in to return a
  55. * result to the client. We also pass in a pointer to be filled in for
  56. * the data cleanup function.
  57. */
  58. struct isc_httpdurl {
  59. char *url;
  60. isc_httpdaction_t *action;
  61. void *action_arg;
  62. ISC_LINK(isc_httpdurl_t) link;
  63. };
  64. #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
  65. #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
  66. /*% http client */
  67. struct isc_httpd {
  68. isc_httpdmgr_t *mgr; /*%< our parent */
  69. ISC_LINK(isc_httpd_t) link;
  70. unsigned int state;
  71. isc_socket_t *sock;
  72. /*%
  73. * Received data state.
  74. */
  75. char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
  76. isc_uint32_t recvlen; /*%< length recv'd */
  77. unsigned int method;
  78. char *url;
  79. char *querystring;
  80. char *protocol;
  81. /*
  82. * Flags on the httpd client.
  83. */
  84. int flags;
  85. /*%
  86. * Transmit data state.
  87. *
  88. * This is the data buffer we will transmit.
  89. *
  90. * This free function pointer is filled in by the rendering function
  91. * we call. The free function is called after the data is transmitted
  92. * to the client.
  93. *
  94. * The bufflist is the list of buffers we are currently transmitting.
  95. * The headerdata is where we render our headers to. If we run out of
  96. * space when rendering a header, we will change the size of our
  97. * buffer. We will not free it until we are finished, and will
  98. * allocate an additional HTTP_SENDGROW bytes per header space grow.
  99. *
  100. * We currently use two buffers total, one for the headers (which
  101. * we manage) and another for the client to fill in (which it manages,
  102. * it provides the space for it, etc) -- we will pass that buffer
  103. * structure back to the caller, who is responsible for managing the
  104. * space it may have allocated as backing store for it. This second
  105. * buffer is bodybuffer, and we only allocate the buffer itself, not
  106. * the backing store.
  107. */
  108. isc_bufferlist_t bufflist;
  109. char *headerdata; /*%< send header buf */
  110. unsigned int headerlen; /*%< current header buffer size */
  111. isc_buffer_t headerbuffer;
  112. const char *mimetype;
  113. unsigned int retcode;
  114. const char *retmsg;
  115. isc_buffer_t bodybuffer;
  116. isc_httpdfree_t *freecb;
  117. void *freecb_arg;
  118. };
  119. /*% lightweight socket manager for httpd output */
  120. struct isc_httpdmgr {
  121. isc_mem_t *mctx;
  122. isc_socket_t *sock; /*%< listening socket */
  123. isc_task_t *task; /*%< owning task */
  124. isc_timermgr_t *timermgr;
  125. isc_httpdclientok_t *client_ok; /*%< client validator */
  126. isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
  127. void *cb_arg; /*%< argument for the above */
  128. unsigned int flags;
  129. ISC_LIST(isc_httpd_t) running; /*%< running clients */
  130. isc_mutex_t lock;
  131. ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
  132. isc_httpdaction_t *render_404;
  133. isc_httpdaction_t *render_500;
  134. };
  135. /*%
  136. * HTTP methods.
  137. */
  138. #define ISC_HTTPD_METHODUNKNOWN 0
  139. #define ISC_HTTPD_METHODGET 1
  140. #define ISC_HTTPD_METHODPOST 2
  141. /*%
  142. * Client states.
  143. *
  144. * _IDLE The client is not doing anything at all. This state should
  145. * only occur just after creation, and just before being
  146. * destroyed.
  147. *
  148. * _RECV The client is waiting for data after issuing a socket recv().
  149. *
  150. * _RECVDONE Data has been received, and is being processed.
  151. *
  152. * _SEND All data for a response has completed, and a reply was
  153. * sent via a socket send() call.
  154. *
  155. * _SENDDONE Send is completed.
  156. *
  157. * Badly formatted state table:
  158. *
  159. * IDLE -> RECV when client has a recv() queued.
  160. *
  161. * RECV -> RECVDONE when recvdone event received.
  162. *
  163. * RECVDONE -> SEND if the data for a reply is at hand.
  164. *
  165. * SEND -> RECV when a senddone event was received.
  166. *
  167. * At any time -> RECV on error. If RECV fails, the client will
  168. * self-destroy, closing the socket and freeing memory.
  169. */
  170. #define ISC_HTTPD_STATEIDLE 0
  171. #define ISC_HTTPD_STATERECV 1
  172. #define ISC_HTTPD_STATERECVDONE 2
  173. #define ISC_HTTPD_STATESEND 3
  174. #define ISC_HTTPD_STATESENDDONE 4
  175. #define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV)
  176. #define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
  177. #define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND)
  178. #define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
  179. /*%
  180. * Overall magic test that means we're not idle.
  181. */
  182. #define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV)
  183. #define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE)
  184. #define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND)
  185. #define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE)
  186. static void isc_httpd_accept(isc_task_t *, isc_event_t *);
  187. static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
  188. static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
  189. static void destroy_client(isc_httpd_t **);
  190. static isc_result_t process_request(isc_httpd_t *, int);
  191. static void httpdmgr_destroy(isc_httpdmgr_t *);
  192. static isc_result_t grow_headerspace(isc_httpd_t *);
  193. static void reset_client(isc_httpd_t *httpd);
  194. static isc_result_t render_404(const char *, const char *,
  195. void *,
  196. unsigned int *, const char **,
  197. const char **, isc_buffer_t *,
  198. isc_httpdfree_t **, void **);
  199. static isc_result_t render_500(const char *, const char *,
  200. void *,
  201. unsigned int *, const char **,
  202. const char **, isc_buffer_t *,
  203. isc_httpdfree_t **, void **);
  204. static void
  205. destroy_client(isc_httpd_t **httpdp)
  206. {
  207. isc_httpd_t *httpd = *httpdp;
  208. isc_httpdmgr_t *httpdmgr = httpd->mgr;
  209. *httpdp = NULL;
  210. LOCK(&httpdmgr->lock);
  211. isc_socket_detach(&httpd->sock);
  212. ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
  213. if (httpd->headerlen > 0)
  214. isc_mem_put(httpdmgr->mctx, httpd->headerdata,
  215. httpd->headerlen);
  216. isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
  217. UNLOCK(&httpdmgr->lock);
  218. httpdmgr_destroy(httpdmgr);
  219. }
  220. isc_result_t
  221. isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
  222. isc_httpdclientok_t *client_ok,
  223. isc_httpdondestroy_t *ondestroy, void *cb_arg,
  224. isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp)
  225. {
  226. isc_result_t result;
  227. isc_httpdmgr_t *httpd;
  228. REQUIRE(mctx != NULL);
  229. REQUIRE(sock != NULL);
  230. REQUIRE(task != NULL);
  231. REQUIRE(tmgr != NULL);
  232. REQUIRE(httpdp != NULL && *httpdp == NULL);
  233. httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
  234. if (httpd == NULL)
  235. return (ISC_R_NOMEMORY);
  236. result = isc_mutex_init(&httpd->lock);
  237. if (result != ISC_R_SUCCESS) {
  238. isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
  239. return (result);
  240. }
  241. httpd->mctx = NULL;
  242. isc_mem_attach(mctx, &httpd->mctx);
  243. httpd->sock = NULL;
  244. isc_socket_attach(sock, &httpd->sock);
  245. httpd->task = NULL;
  246. isc_task_attach(task, &httpd->task);
  247. httpd->timermgr = tmgr; /* XXXMLG no attach function? */
  248. httpd->client_ok = client_ok;
  249. httpd->ondestroy = ondestroy;
  250. httpd->cb_arg = cb_arg;
  251. ISC_LIST_INIT(httpd->running);
  252. ISC_LIST_INIT(httpd->urls);
  253. /* XXXMLG ignore errors on isc_socket_listen() */
  254. result = isc_socket_listen(sock, SOMAXCONN);
  255. if (result != ISC_R_SUCCESS) {
  256. UNEXPECTED_ERROR(__FILE__, __LINE__,
  257. "isc_socket_listen() failed: %s",
  258. isc_result_totext(result));
  259. goto cleanup;
  260. }
  261. (void)isc_socket_filter(sock, "httpready");
  262. result = isc_socket_accept(sock, task, isc_httpd_accept, httpd);
  263. if (result != ISC_R_SUCCESS)
  264. goto cleanup;
  265. httpd->render_404 = render_404;
  266. httpd->render_500 = render_500;
  267. *httpdp = httpd;
  268. return (ISC_R_SUCCESS);
  269. cleanup:
  270. isc_task_detach(&httpd->task);
  271. isc_socket_detach(&httpd->sock);
  272. isc_mem_detach(&httpd->mctx);
  273. (void)isc_mutex_destroy(&httpd->lock);
  274. isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
  275. return (result);
  276. }
  277. static void
  278. httpdmgr_destroy(isc_httpdmgr_t *httpdmgr)
  279. {
  280. isc_mem_t *mctx;
  281. isc_httpdurl_t *url;
  282. ENTER("httpdmgr_destroy");
  283. LOCK(&httpdmgr->lock);
  284. if (!MSHUTTINGDOWN(httpdmgr)) {
  285. NOTICE("httpdmgr_destroy not shutting down yet");
  286. UNLOCK(&httpdmgr->lock);
  287. return;
  288. }
  289. /*
  290. * If all clients are not shut down, don't do anything yet.
  291. */
  292. if (!ISC_LIST_EMPTY(httpdmgr->running)) {
  293. NOTICE("httpdmgr_destroy clients still active");
  294. UNLOCK(&httpdmgr->lock);
  295. return;
  296. }
  297. NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
  298. isc_socket_detach(&httpdmgr->sock);
  299. isc_task_detach(&httpdmgr->task);
  300. httpdmgr->timermgr = NULL;
  301. /*
  302. * Clear out the list of all actions we know about. Just free the
  303. * memory.
  304. */
  305. url = ISC_LIST_HEAD(httpdmgr->urls);
  306. while (url != NULL) {
  307. isc_mem_free(httpdmgr->mctx, url->url);
  308. ISC_LIST_UNLINK(httpdmgr->urls, url, link);
  309. isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
  310. url = ISC_LIST_HEAD(httpdmgr->urls);
  311. }
  312. UNLOCK(&httpdmgr->lock);
  313. (void)isc_mutex_destroy(&httpdmgr->lock);
  314. if (httpdmgr->ondestroy != NULL)
  315. (httpdmgr->ondestroy)(httpdmgr->cb_arg);
  316. mctx = httpdmgr->mctx;
  317. isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
  318. EXIT("httpdmgr_destroy");
  319. }
  320. #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
  321. #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
  322. static isc_result_t
  323. process_request(isc_httpd_t *httpd, int length)
  324. {
  325. char *s;
  326. char *p;
  327. int delim;
  328. ENTER("request");
  329. httpd->recvlen += length;
  330. httpd->recvbuf[httpd->recvlen] = 0;
  331. /*
  332. * If we don't find a blank line in our buffer, return that we need
  333. * more data.
  334. */
  335. s = strstr(httpd->recvbuf, "\r\n\r\n");
  336. delim = 1;
  337. if (s == NULL) {
  338. s = strstr(httpd->recvbuf, "\n\n");
  339. delim = 2;
  340. }
  341. if (s == NULL)
  342. return (ISC_R_NOTFOUND);
  343. /*
  344. * Determine if this is a POST or GET method. Any other values will
  345. * cause an error to be returned.
  346. */
  347. if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
  348. httpd->method = ISC_HTTPD_METHODGET;
  349. p = httpd->recvbuf + 4;
  350. } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
  351. httpd->method = ISC_HTTPD_METHODPOST;
  352. p = httpd->recvbuf + 5;
  353. } else {
  354. return (ISC_R_RANGE);
  355. }
  356. /*
  357. * From now on, p is the start of our buffer.
  358. */
  359. /*
  360. * Extract the URL.
  361. */
  362. s = p;
  363. while (LENGTHOK(s) && BUFLENOK(s) &&
  364. (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
  365. s++;
  366. if (!LENGTHOK(s))
  367. return (ISC_R_NOTFOUND);
  368. if (!BUFLENOK(s))
  369. return (ISC_R_NOMEMORY);
  370. *s = 0;
  371. /*
  372. * Make the URL relative.
  373. */
  374. if ((strncmp(p, "http:/", 6) == 0)
  375. || (strncmp(p, "https:/", 7) == 0)) {
  376. /* Skip first / */
  377. while (*p != '/' && *p != 0)
  378. p++;
  379. if (*p == 0)
  380. return (ISC_R_RANGE);
  381. p++;
  382. /* Skip second / */
  383. while (*p != '/' && *p != 0)
  384. p++;
  385. if (*p == 0)
  386. return (ISC_R_RANGE);
  387. p++;
  388. /* Find third / */
  389. while (*p != '/' && *p != 0)
  390. p++;
  391. if (*p == 0) {
  392. p--;
  393. *p = '/';
  394. }
  395. }
  396. httpd->url = p;
  397. p = s + delim;
  398. s = p;
  399. /*
  400. * Now, see if there is a ? mark in the URL. If so, this is
  401. * part of the query string, and we will split it from the URL.
  402. */
  403. httpd->querystring = strchr(httpd->url, '?');
  404. if (httpd->querystring != NULL) {
  405. *(httpd->querystring) = 0;
  406. httpd->querystring++;
  407. }
  408. /*
  409. * Extract the HTTP/1.X protocol. We will bounce on anything but
  410. * HTTP/1.1 for now.
  411. */
  412. while (LENGTHOK(s) && BUFLENOK(s) &&
  413. (*s != '\n' && *s != '\r' && *s != '\0'))
  414. s++;
  415. if (!LENGTHOK(s))
  416. return (ISC_R_NOTFOUND);
  417. if (!BUFLENOK(s))
  418. return (ISC_R_NOMEMORY);
  419. *s = 0;
  420. if ((strncmp(p, "HTTP/1.0", 8) != 0)
  421. && (strncmp(p, "HTTP/1.1", 8) != 0))
  422. return (ISC_R_RANGE);
  423. httpd->protocol = p;
  424. p = s + 1;
  425. s = p;
  426. if (strstr(s, "Connection: close") != NULL)
  427. httpd->flags |= HTTPD_CLOSE;
  428. if (strstr(s, "Host: ") != NULL)
  429. httpd->flags |= HTTPD_FOUNDHOST;
  430. /*
  431. * Standards compliance hooks here.
  432. */
  433. if (strcmp(httpd->protocol, "HTTP/1.1") == 0
  434. && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
  435. return (ISC_R_RANGE);
  436. EXIT("request");
  437. return (ISC_R_SUCCESS);
  438. }
  439. static void
  440. isc_httpd_accept(isc_task_t *task, isc_event_t *ev)
  441. {
  442. isc_result_t result;
  443. isc_httpdmgr_t *httpdmgr = ev->ev_arg;
  444. isc_httpd_t *httpd;
  445. isc_region_t r;
  446. isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
  447. isc_sockaddr_t peeraddr;
  448. ENTER("accept");
  449. LOCK(&httpdmgr->lock);
  450. if (MSHUTTINGDOWN(httpdmgr)) {
  451. NOTICE("accept shutting down, goto out");
  452. goto out;
  453. }
  454. if (nev->result == ISC_R_CANCELED) {
  455. NOTICE("accept canceled, goto out");
  456. goto out;
  457. }
  458. if (nev->result != ISC_R_SUCCESS) {
  459. /* XXXMLG log failure */
  460. NOTICE("accept returned failure, goto requeue");
  461. goto requeue;
  462. }
  463. (void)isc_socket_getpeername(nev->newsocket, &peeraddr);
  464. if (httpdmgr->client_ok != NULL &&
  465. !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
  466. isc_socket_detach(&nev->newsocket);
  467. goto requeue;
  468. }
  469. httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
  470. if (httpd == NULL) {
  471. /* XXXMLG log failure */
  472. NOTICE("accept failed to allocate memory, goto requeue");
  473. isc_socket_detach(&nev->newsocket);
  474. goto requeue;
  475. }
  476. httpd->mgr = httpdmgr;
  477. ISC_LINK_INIT(httpd, link);
  478. ISC_LIST_APPEND(httpdmgr->running, httpd, link);
  479. ISC_HTTPD_SETRECV(httpd);
  480. httpd->sock = nev->newsocket;
  481. isc_socket_setname(httpd->sock, "httpd", NULL);
  482. httpd->flags = 0;
  483. /*
  484. * Initialize the buffer for our headers.
  485. */
  486. httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
  487. if (httpd->headerdata == NULL) {
  488. isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
  489. isc_socket_detach(&nev->newsocket);
  490. goto requeue;
  491. }
  492. httpd->headerlen = HTTP_SENDGROW;
  493. isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
  494. httpd->headerlen);
  495. ISC_LIST_INIT(httpd->bufflist);
  496. isc_buffer_initnull(&httpd->bodybuffer);
  497. reset_client(httpd);
  498. r.base = (unsigned char *)httpd->recvbuf;
  499. r.length = HTTP_RECVLEN - 1;
  500. result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
  501. httpd);
  502. /* FIXME!!! */
  503. POST(result);
  504. NOTICE("accept queued recv on socket");
  505. requeue:
  506. result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
  507. httpdmgr);
  508. if (result != ISC_R_SUCCESS) {
  509. /* XXXMLG what to do? Log failure... */
  510. NOTICE("accept could not reaccept due to failure");
  511. }
  512. out:
  513. UNLOCK(&httpdmgr->lock);
  514. httpdmgr_destroy(httpdmgr);
  515. isc_event_free(&ev);
  516. EXIT("accept");
  517. }
  518. static isc_result_t
  519. render_404(const char *url, const char *querystring,
  520. void *arg,
  521. unsigned int *retcode, const char **retmsg,
  522. const char **mimetype, isc_buffer_t *b,
  523. isc_httpdfree_t **freecb, void **freecb_args)
  524. {
  525. static char msg[] = "No such URL.";
  526. UNUSED(url);
  527. UNUSED(querystring);
  528. UNUSED(arg);
  529. *retcode = 404;
  530. *retmsg = "No such URL";
  531. *mimetype = "text/plain";
  532. isc_buffer_reinit(b, msg, strlen(msg));
  533. isc_buffer_add(b, strlen(msg));
  534. *freecb = NULL;
  535. *freecb_args = NULL;
  536. return (ISC_R_SUCCESS);
  537. }
  538. static isc_result_t
  539. render_500(const char *url, const char *querystring,
  540. void *arg,
  541. unsigned int *retcode, const char **retmsg,
  542. const char **mimetype, isc_buffer_t *b,
  543. isc_httpdfree_t **freecb, void **freecb_args)
  544. {
  545. static char msg[] = "Internal server failure.";
  546. UNUSED(url);
  547. UNUSED(querystring);
  548. UNUSED(arg);
  549. *retcode = 500;
  550. *retmsg = "Internal server failure";
  551. *mimetype = "text/plain";
  552. isc_buffer_reinit(b, msg, strlen(msg));
  553. isc_buffer_add(b, strlen(msg));
  554. *freecb = NULL;
  555. *freecb_args = NULL;
  556. return (ISC_R_SUCCESS);
  557. }
  558. static void
  559. isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev)
  560. {
  561. isc_region_t r;
  562. isc_result_t result;
  563. isc_httpd_t *httpd = ev->ev_arg;
  564. isc_socketevent_t *sev = (isc_socketevent_t *)ev;
  565. isc_httpdurl_t *url;
  566. isc_time_t now;
  567. char datebuf[32]; /* Only need 30, but safety first */
  568. ENTER("recv");
  569. INSIST(ISC_HTTPD_ISRECV(httpd));
  570. if (sev->result != ISC_R_SUCCESS) {
  571. NOTICE("recv destroying client");
  572. destroy_client(&httpd);
  573. goto out;
  574. }
  575. result = process_request(httpd, sev->n);
  576. if (result == ISC_R_NOTFOUND) {
  577. if (httpd->recvlen >= HTTP_RECVLEN - 1) {
  578. destroy_client(&httpd);
  579. goto out;
  580. }
  581. r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
  582. r.length = HTTP_RECVLEN - httpd->recvlen - 1;
  583. /* check return code? */
  584. (void)isc_socket_recv(httpd->sock, &r, 1, task,
  585. isc_httpd_recvdone, httpd);
  586. goto out;
  587. } else if (result != ISC_R_SUCCESS) {
  588. destroy_client(&httpd);
  589. goto out;
  590. }
  591. ISC_HTTPD_SETSEND(httpd);
  592. /*
  593. * XXXMLG Call function here. Provide an add-header function
  594. * which will append the common headers to a response we generate.
  595. */
  596. isc_buffer_initnull(&httpd->bodybuffer);
  597. isc_time_now(&now);
  598. isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
  599. url = ISC_LIST_HEAD(httpd->mgr->urls);
  600. while (url != NULL) {
  601. if (strcmp(httpd->url, url->url) == 0)
  602. break;
  603. url = ISC_LIST_NEXT(url, link);
  604. }
  605. if (url == NULL)
  606. result = httpd->mgr->render_404(httpd->url, httpd->querystring,
  607. NULL,
  608. &httpd->retcode,
  609. &httpd->retmsg,
  610. &httpd->mimetype,
  611. &httpd->bodybuffer,
  612. &httpd->freecb,
  613. &httpd->freecb_arg);
  614. else
  615. result = url->action(httpd->url, httpd->querystring,
  616. url->action_arg,
  617. &httpd->retcode, &httpd->retmsg,
  618. &httpd->mimetype, &httpd->bodybuffer,
  619. &httpd->freecb, &httpd->freecb_arg);
  620. if (result != ISC_R_SUCCESS) {
  621. result = httpd->mgr->render_500(httpd->url, httpd->querystring,
  622. NULL, &httpd->retcode,
  623. &httpd->retmsg,
  624. &httpd->mimetype,
  625. &httpd->bodybuffer,
  626. &httpd->freecb,
  627. &httpd->freecb_arg);
  628. RUNTIME_CHECK(result == ISC_R_SUCCESS);
  629. }
  630. isc_httpd_response(httpd);
  631. isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
  632. isc_httpd_addheader(httpd, "Date", datebuf);
  633. isc_httpd_addheader(httpd, "Expires", datebuf);
  634. isc_httpd_addheader(httpd, "Last-Modified", datebuf);
  635. isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
  636. isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
  637. isc_httpd_addheader(httpd, "Server: libisc", NULL);
  638. isc_httpd_addheaderuint(httpd, "Content-Length",
  639. isc_buffer_usedlength(&httpd->bodybuffer));
  640. isc_httpd_endheaders(httpd); /* done */
  641. ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
  642. /*
  643. * Link the data buffer into our send queue, should we have any data
  644. * rendered into it. If no data is present, we won't do anything
  645. * with the buffer.
  646. */
  647. if (isc_buffer_length(&httpd->bodybuffer) > 0)
  648. ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
  649. /* check return code? */
  650. (void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
  651. isc_httpd_senddone, httpd);
  652. out:
  653. isc_event_free(&ev);
  654. EXIT("recv");
  655. }
  656. void
  657. isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp)
  658. {
  659. isc_httpdmgr_t *httpdmgr;
  660. isc_httpd_t *httpd;
  661. httpdmgr = *httpdmgrp;
  662. *httpdmgrp = NULL;
  663. ENTER("isc_httpdmgr_shutdown");
  664. LOCK(&httpdmgr->lock);
  665. MSETSHUTTINGDOWN(httpdmgr);
  666. isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
  667. httpd = ISC_LIST_HEAD(httpdmgr->running);
  668. while (httpd != NULL) {
  669. isc_socket_cancel(httpd->sock, httpdmgr->task,
  670. ISC_SOCKCANCEL_ALL);
  671. httpd = ISC_LIST_NEXT(httpd, link);
  672. }
  673. UNLOCK(&httpdmgr->lock);
  674. EXIT("isc_httpdmgr_shutdown");
  675. }
  676. static isc_result_t
  677. grow_headerspace(isc_httpd_t *httpd)
  678. {
  679. char *newspace;
  680. unsigned int newlen;
  681. isc_region_t r;
  682. newlen = httpd->headerlen + HTTP_SENDGROW;
  683. if (newlen > HTTP_SEND_MAXLEN)
  684. return (ISC_R_NOSPACE);
  685. newspace = isc_mem_get(httpd->mgr->mctx, newlen);
  686. if (newspace == NULL)
  687. return (ISC_R_NOMEMORY);
  688. isc_buffer_region(&httpd->headerbuffer, &r);
  689. isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
  690. isc_mem_put(httpd->mgr->mctx, r.base, r.length);
  691. return (ISC_R_SUCCESS);
  692. }
  693. isc_result_t
  694. isc_httpd_response(isc_httpd_t *httpd)
  695. {
  696. isc_result_t result;
  697. unsigned int needlen;
  698. needlen = strlen(httpd->protocol) + 1; /* protocol + space */
  699. needlen += 3 + 1; /* room for response code, always 3 bytes */
  700. needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
  701. while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
  702. result = grow_headerspace(httpd);
  703. if (result != ISC_R_SUCCESS)
  704. return (result);
  705. }
  706. sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n",
  707. httpd->protocol, httpd->retcode, httpd->retmsg);
  708. isc_buffer_add(&httpd->headerbuffer, needlen);
  709. return (ISC_R_SUCCESS);
  710. }
  711. isc_result_t
  712. isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
  713. const char *val)
  714. {
  715. isc_result_t result;
  716. unsigned int needlen;
  717. needlen = strlen(name); /* name itself */
  718. if (val != NULL)
  719. needlen += 2 + strlen(val); /* :<space> and val */
  720. needlen += 2; /* CRLF */
  721. while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
  722. result = grow_headerspace(httpd);
  723. if (result != ISC_R_SUCCESS)
  724. return (result);
  725. }
  726. if (val != NULL)
  727. sprintf(isc_buffer_used(&httpd->headerbuffer),
  728. "%s: %s\r\n", name, val);
  729. else
  730. sprintf(isc_buffer_used(&httpd->headerbuffer),
  731. "%s\r\n", name);
  732. isc_buffer_add(&httpd->headerbuffer, needlen);
  733. return (ISC_R_SUCCESS);
  734. }
  735. isc_result_t
  736. isc_httpd_endheaders(isc_httpd_t *httpd)
  737. {
  738. isc_result_t result;
  739. while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
  740. result = grow_headerspace(httpd);
  741. if (result != ISC_R_SUCCESS)
  742. return (result);
  743. }
  744. sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
  745. isc_buffer_add(&httpd->headerbuffer, 2);
  746. return (ISC_R_SUCCESS);
  747. }
  748. isc_result_t
  749. isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
  750. isc_result_t result;
  751. unsigned int needlen;
  752. char buf[sizeof "18446744073709551616"];
  753. sprintf(buf, "%d", val);
  754. needlen = strlen(name); /* name itself */
  755. needlen += 2 + strlen(buf); /* :<space> and val */
  756. needlen += 2; /* CRLF */
  757. while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
  758. result = grow_headerspace(httpd);
  759. if (result != ISC_R_SUCCESS)
  760. return (result);
  761. }
  762. sprintf(isc_buffer_used(&httpd->headerbuffer),
  763. "%s: %s\r\n", name, buf);
  764. isc_buffer_add(&httpd->headerbuffer, needlen);
  765. return (ISC_R_SUCCESS);
  766. }
  767. static void
  768. isc_httpd_senddone(isc_task_t *task, isc_event_t *ev)
  769. {
  770. isc_httpd_t *httpd = ev->ev_arg;
  771. isc_region_t r;
  772. isc_socketevent_t *sev = (isc_socketevent_t *)ev;
  773. ENTER("senddone");
  774. INSIST(ISC_HTTPD_ISSEND(httpd));
  775. /*
  776. * First, unlink our header buffer from the socket's bufflist. This
  777. * is sort of an evil hack, since we know our buffer will be there,
  778. * and we know it's address, so we can just remove it directly.
  779. */
  780. NOTICE("senddone unlinked header");
  781. ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
  782. /*
  783. * We will always want to clean up our receive buffer, even if we
  784. * got an error on send or we are shutting down.
  785. *
  786. * We will pass in the buffer only if there is data in it. If
  787. * there is no data, we will pass in a NULL.
  788. */
  789. if (httpd->freecb != NULL) {
  790. isc_buffer_t *b = NULL;
  791. if (isc_buffer_length(&httpd->bodybuffer) > 0)
  792. b = &httpd->bodybuffer;
  793. httpd->freecb(b, httpd->freecb_arg);
  794. NOTICE("senddone free callback performed");
  795. }
  796. if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
  797. ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
  798. NOTICE("senddone body buffer unlinked");
  799. }
  800. if (sev->result != ISC_R_SUCCESS) {
  801. destroy_client(&httpd);
  802. goto out;
  803. }
  804. if ((httpd->flags & HTTPD_CLOSE) != 0) {
  805. destroy_client(&httpd);
  806. goto out;
  807. }
  808. ISC_HTTPD_SETRECV(httpd);
  809. NOTICE("senddone restarting recv on socket");
  810. reset_client(httpd);
  811. r.base = (unsigned char *)httpd->recvbuf;
  812. r.length = HTTP_RECVLEN - 1;
  813. /* check return code? */
  814. (void)isc_socket_recv(httpd->sock, &r, 1, task,
  815. isc_httpd_recvdone, httpd);
  816. out:
  817. isc_event_free(&ev);
  818. EXIT("senddone");
  819. }
  820. static void
  821. reset_client(isc_httpd_t *httpd)
  822. {
  823. /*
  824. * Catch errors here. We MUST be in RECV mode, and we MUST NOT have
  825. * any outstanding buffers. If we have buffers, we have a leak.
  826. */
  827. INSIST(ISC_HTTPD_ISRECV(httpd));
  828. INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
  829. INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
  830. httpd->recvbuf[0] = 0;
  831. httpd->recvlen = 0;
  832. httpd->method = ISC_HTTPD_METHODUNKNOWN;
  833. httpd->url = NULL;
  834. httpd->querystring = NULL;
  835. httpd->protocol = NULL;
  836. httpd->flags = 0;
  837. isc_buffer_clear(&httpd->headerbuffer);
  838. isc_buffer_invalidate(&httpd->bodybuffer);
  839. }
  840. isc_result_t
  841. isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
  842. isc_httpdaction_t *func, void *arg)
  843. {
  844. isc_httpdurl_t *item;
  845. if (url == NULL) {
  846. httpdmgr->render_404 = func;
  847. return (ISC_R_SUCCESS);
  848. }
  849. item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
  850. if (item == NULL)
  851. return (ISC_R_NOMEMORY);
  852. item->url = isc_mem_strdup(httpdmgr->mctx, url);
  853. if (item->url == NULL) {
  854. isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
  855. return (ISC_R_NOMEMORY);
  856. }
  857. item->action = func;
  858. item->action_arg = arg;
  859. ISC_LINK_INIT(item, link);
  860. ISC_LIST_APPEND(httpdmgr->urls, item, link);
  861. return (ISC_R_SUCCESS);
  862. }