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