1 /* $NetBSD: httpd.c,v 1.1 2024/02/18 20:57:49 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16 /*! \file */
17
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include <string.h>
21
22 #include <isc/buffer.h>
23 #include <isc/httpd.h>
24 #include <isc/mem.h>
25 #include <isc/print.h>
26 #include <isc/refcount.h>
27 #include <isc/socket.h>
28 #include <isc/string.h>
29 #include <isc/task.h>
30 #include <isc/time.h>
31 #include <isc/util.h>
32
33 #ifdef HAVE_ZLIB
34 #include <zlib.h>
35 #endif /* ifdef HAVE_ZLIB */
36
37 /*%
38 * TODO:
39 *
40 * o Make the URL processing external functions which will fill-in a buffer
41 * structure we provide, or return an error and we will render a generic
42 * page and close the client.
43 */
44
45 #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
46 #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
47
48 #define HTTP_RECVLEN 1024
49 #define HTTP_SENDGROW 1024
50 #define HTTP_SEND_MAXLEN 10240
51
52 #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
53 #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
54 #define HTTPD_KEEPALIVE 0x0004 /* Got a Connection: Keep-Alive */
55 #define HTTPD_ACCEPT_DEFLATE 0x0008
56
57 #define HTTPD_MAGIC ISC_MAGIC('H', 't', 'p', 'd')
58 #define VALID_HTTPD(m) ISC_MAGIC_VALID(m, HTTPD_MAGIC)
59
60 #define HTTPDMGR_MAGIC ISC_MAGIC('H', 'p', 'd', 'm')
61 #define VALID_HTTPDMGR(m) ISC_MAGIC_VALID(m, HTTPDMGR_MAGIC)
62
63 /*% http client */
64 struct isc_httpd {
65 unsigned int magic; /* HTTPD_MAGIC */
66 isc_refcount_t references;
67 isc_httpdmgr_t *mgr; /*%< our parent */
68 ISC_LINK(isc_httpd_t) link;
69 unsigned int state;
70 isc_socket_t *sock;
71
72 /*%
73 * Received data state.
74 */
75 char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
76 uint32_t recvlen; /*%< length recv'd */
77 char *headers; /*%< set in process_request() */
78 unsigned int method;
79 char *url;
80 char *querystring;
81 char *protocol;
82
83 /*
84 * Flags on the httpd client.
85 */
86 int flags;
87
88 /*%
89 * Transmit data state.
90 *
91 * This is the data buffer we will transmit.
92 *
93 * This free function pointer is filled in by the rendering function
94 * we call. The free function is called after the data is transmitted
95 * to the client.
96 *
97 * The bufflist is the list of buffers we are currently transmitting.
98 * The headerbuffer is where we render our headers to. If we run out
99 * of space when rendering a header, we will change the size of our
100 * buffer. We will not free it until we are finished, and will
101 * allocate an additional HTTP_SENDGROW bytes per header space grow.
102 *
103 * We currently use three buffers total, one for the headers (which
104 * we manage), another for the client to fill in (which it manages,
105 * it provides the space for it, etc) -- we will pass that buffer
106 * structure back to the caller, who is responsible for managing the
107 * space it may have allocated as backing store for it. This second
108 * buffer is bodybuffer, and we only allocate the buffer itself, not
109 * the backing store.
110 * The third buffer is compbuffer, managed by us, that contains the
111 * compressed HTTP data, if compression is used.
112 *
113 */
114 isc_buffer_t headerbuffer;
115 isc_buffer_t compbuffer;
116 isc_buffer_t *sendbuffer;
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 unsigned int magic; /* HTTPDMGR_MAGIC */
129 isc_refcount_t references;
130 isc_mem_t *mctx;
131 isc_socket_t *sock; /*%< listening socket */
132 isc_task_t *task; /*%< owning task */
133 isc_timermgr_t *timermgr;
134
135 isc_httpdclientok_t *client_ok; /*%< client validator */
136 isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
137 void *cb_arg; /*%< argument for the above */
138
139 unsigned int flags;
140 ISC_LIST(isc_httpd_t) running; /*%< running clients */
141
142 isc_mutex_t lock;
143
144 ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
145 isc_httpdaction_t *render_404;
146 isc_httpdaction_t *render_500;
147 };
148
149 /*%
150 * HTTP methods.
151 */
152 #define ISC_HTTPD_METHODUNKNOWN 0
153 #define ISC_HTTPD_METHODGET 1
154 #define ISC_HTTPD_METHODPOST 2
155
156 /*%
157 * Client states.
158 *
159 * _IDLE The client is not doing anything at all. This state should
160 * only occur just after creation, and just before being
161 * destroyed.
162 *
163 * _RECV The client is waiting for data after issuing a socket recv().
164 *
165 * _RECVDONE Data has been received, and is being processed.
166 *
167 * _SEND All data for a response has completed, and a reply was
168 * sent via a socket send() call.
169 *
170 * _SENDDONE Send is completed.
171 *
172 * Badly formatted state table:
173 *
174 * IDLE -> RECV when client has a recv() queued.
175 *
176 * RECV -> RECVDONE when recvdone event received.
177 *
178 * RECVDONE -> SEND if the data for a reply is at hand.
179 *
180 * SEND -> RECV when a senddone event was received.
181 *
182 * At any time -> RECV on error. If RECV fails, the client will
183 * self-destroy, closing the socket and freeing memory.
184 */
185 #define ISC_HTTPD_STATEIDLE 0
186 #define ISC_HTTPD_STATERECV 1
187 #define ISC_HTTPD_STATERECVDONE 2
188 #define ISC_HTTPD_STATESEND 3
189 #define ISC_HTTPD_STATESENDDONE 4
190
191 #define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV)
192 #define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
193 #define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND)
194 #define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
195
196 /*%
197 * Overall magic test that means we're not idle.
198 */
199 #define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV)
200 #define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE)
201 #define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND)
202 #define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE)
203
204 static void
205 isc_httpd_accept(isc_task_t *, isc_event_t *);
206 static void
207 isc_httpd_recvdone(isc_task_t *, isc_event_t *);
208 static void
209 isc_httpd_senddone(isc_task_t *, isc_event_t *);
210 static isc_result_t
211 process_request(isc_httpd_t *, int);
212 static isc_result_t
213 grow_headerspace(isc_httpd_t *);
214 static void
215 reset_client(isc_httpd_t *httpd);
216
217 static isc_httpdaction_t render_404;
218 static isc_httpdaction_t render_500;
219
220 #if ENABLE_AFL
221 static void (*finishhook)(void) = NULL;
222 #endif /* ENABLE_AFL */
223
224 static void
225 maybe_destroy_httpd(isc_httpd_t *);
226 static void
227 destroy_httpd(isc_httpd_t *);
228 static void
229 maybe_destroy_httpdmgr(isc_httpdmgr_t *);
230 static void
231 destroy_httpdmgr(isc_httpdmgr_t *);
232
233 static void
234 isc_httpdmgr_attach(isc_httpdmgr_t *, isc_httpdmgr_t **);
235 static void
236 isc_httpdmgr_detach(isc_httpdmgr_t **);
237
238 static void
maybe_destroy_httpd(isc_httpd_t * httpd)239 maybe_destroy_httpd(isc_httpd_t *httpd) {
240 if (isc_refcount_decrement(&httpd->references) == 1) {
241 destroy_httpd(httpd);
242 }
243 }
244
245 static void
free_buffer(isc_mem_t * mctx,isc_buffer_t * buffer)246 free_buffer(isc_mem_t *mctx, isc_buffer_t *buffer) {
247 isc_region_t r;
248
249 isc_buffer_region(buffer, &r);
250 if (r.length > 0) {
251 isc_mem_put(mctx, r.base, r.length);
252 }
253
254 isc_buffer_initnull(buffer);
255 }
256
257 static void
destroy_httpd(isc_httpd_t * httpd)258 destroy_httpd(isc_httpd_t *httpd) {
259 isc_httpdmgr_t *httpdmgr;
260
261 REQUIRE(VALID_HTTPD(httpd));
262
263 httpdmgr = httpd->mgr;
264 REQUIRE(VALID_HTTPDMGR(httpdmgr));
265
266 /*
267 * Unlink before calling isc_socket_detach so
268 * isc_httpdmgr_shutdown does not dereference a NULL pointer
269 * when calling isc_socket_cancel().
270 */
271 LOCK(&httpdmgr->lock);
272 ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
273 UNLOCK(&httpdmgr->lock);
274
275 httpd->magic = 0;
276 isc_refcount_destroy(&httpd->references);
277 isc_socket_detach(&httpd->sock);
278
279 free_buffer(httpdmgr->mctx, &httpd->headerbuffer);
280 free_buffer(httpdmgr->mctx, &httpd->compbuffer);
281
282 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
283
284 #if ENABLE_AFL
285 if (finishhook != NULL) {
286 finishhook();
287 }
288 #endif /* ENABLE_AFL */
289
290 isc_httpdmgr_detach(&httpdmgr);
291 }
292
293 static isc_result_t
httpdmgr_socket_accept(isc_task_t * task,isc_httpdmgr_t * httpdmgr)294 httpdmgr_socket_accept(isc_task_t *task, isc_httpdmgr_t *httpdmgr) {
295 isc_result_t result = ISC_R_SUCCESS;
296
297 /* decremented in isc_httpd_accept */
298 isc_refcount_increment(&httpdmgr->references);
299 result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
300 httpdmgr);
301 if (result != ISC_R_SUCCESS) {
302 INSIST(isc_refcount_decrement(&httpdmgr->references) > 1);
303 }
304 return (result);
305 }
306
307 static void
httpd_socket_recv(isc_httpd_t * httpd,isc_region_t * region,isc_task_t * task)308 httpd_socket_recv(isc_httpd_t *httpd, isc_region_t *region, isc_task_t *task) {
309 isc_result_t result = ISC_R_SUCCESS;
310
311 /* decremented in isc_httpd_recvdone */
312 (void)isc_refcount_increment(&httpd->references);
313 result = isc_socket_recv(httpd->sock, region, 1, task,
314 isc_httpd_recvdone, httpd);
315 if (result != ISC_R_SUCCESS) {
316 INSIST(isc_refcount_decrement(&httpd->references) > 1);
317 }
318 }
319
320 static void
httpd_socket_send(isc_httpd_t * httpd,isc_region_t * region,isc_task_t * task)321 httpd_socket_send(isc_httpd_t *httpd, isc_region_t *region, isc_task_t *task) {
322 isc_result_t result = ISC_R_SUCCESS;
323
324 /* decremented in isc_httpd_senddone */
325 (void)isc_refcount_increment(&httpd->references);
326 result = isc_socket_send(httpd->sock, region, task, isc_httpd_senddone,
327 httpd);
328 if (result != ISC_R_SUCCESS) {
329 INSIST(isc_refcount_decrement(&httpd->references) > 1);
330 }
331 }
332
333 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 ** httpdmgrp)334 isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
335 isc_httpdclientok_t *client_ok,
336 isc_httpdondestroy_t *ondestroy, void *cb_arg,
337 isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdmgrp) {
338 isc_result_t result;
339 isc_httpdmgr_t *httpdmgr;
340
341 REQUIRE(mctx != NULL);
342 REQUIRE(sock != NULL);
343 REQUIRE(task != NULL);
344 REQUIRE(tmgr != NULL);
345 REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL);
346
347 httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
348
349 *httpdmgr = (isc_httpdmgr_t){ .timermgr = tmgr, /* XXXMLG no attach
350 * function? */
351 .client_ok = client_ok,
352 .ondestroy = ondestroy,
353 .cb_arg = cb_arg,
354 .render_404 = render_404,
355 .render_500 = render_500 };
356
357 isc_mutex_init(&httpdmgr->lock);
358 isc_mem_attach(mctx, &httpdmgr->mctx);
359 isc_socket_attach(sock, &httpdmgr->sock);
360 isc_task_attach(task, &httpdmgr->task);
361
362 ISC_LIST_INIT(httpdmgr->running);
363 ISC_LIST_INIT(httpdmgr->urls);
364
365 isc_refcount_init(&httpdmgr->references, 1);
366
367 /* XXXMLG ignore errors on isc_socket_listen() */
368 result = isc_socket_listen(sock, SOMAXCONN);
369 if (result != ISC_R_SUCCESS) {
370 UNEXPECTED_ERROR(__FILE__, __LINE__,
371 "isc_socket_listen() failed: %s",
372 isc_result_totext(result));
373 goto cleanup;
374 }
375
376 (void)isc_socket_filter(sock, "httpready");
377
378 httpdmgr->magic = HTTPDMGR_MAGIC;
379
380 result = httpdmgr_socket_accept(task, httpdmgr);
381 if (result != ISC_R_SUCCESS) {
382 goto cleanup;
383 }
384
385 *httpdmgrp = httpdmgr;
386 return (ISC_R_SUCCESS);
387
388 cleanup:
389 httpdmgr->magic = 0;
390 isc_refcount_decrementz(&httpdmgr->references);
391 isc_refcount_destroy(&httpdmgr->references);
392 isc_task_detach(&httpdmgr->task);
393 isc_socket_detach(&httpdmgr->sock);
394 isc_mem_detach(&httpdmgr->mctx);
395 isc_mutex_destroy(&httpdmgr->lock);
396 isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
397 return (result);
398 }
399
400 static void
isc_httpdmgr_attach(isc_httpdmgr_t * source,isc_httpdmgr_t ** targetp)401 isc_httpdmgr_attach(isc_httpdmgr_t *source, isc_httpdmgr_t **targetp) {
402 REQUIRE(VALID_HTTPDMGR(source));
403 REQUIRE(targetp != NULL && *targetp == NULL);
404
405 isc_refcount_increment(&source->references);
406
407 *targetp = source;
408 }
409
410 static void
isc_httpdmgr_detach(isc_httpdmgr_t ** httpdmgrp)411 isc_httpdmgr_detach(isc_httpdmgr_t **httpdmgrp) {
412 REQUIRE(httpdmgrp != NULL && VALID_HTTPDMGR(*httpdmgrp));
413 isc_httpdmgr_t *httpdmgr = *httpdmgrp;
414 *httpdmgrp = NULL;
415
416 maybe_destroy_httpdmgr(httpdmgr);
417 }
418
419 static void
maybe_destroy_httpdmgr(isc_httpdmgr_t * httpdmgr)420 maybe_destroy_httpdmgr(isc_httpdmgr_t *httpdmgr) {
421 if (isc_refcount_decrement(&httpdmgr->references) == 1) {
422 destroy_httpdmgr(httpdmgr);
423 }
424 }
425
426 static void
destroy_httpdmgr(isc_httpdmgr_t * httpdmgr)427 destroy_httpdmgr(isc_httpdmgr_t *httpdmgr) {
428 isc_httpdurl_t *url;
429
430 isc_refcount_destroy(&httpdmgr->references);
431
432 LOCK(&httpdmgr->lock);
433
434 httpdmgr->magic = 0;
435
436 INSIST(MSHUTTINGDOWN(httpdmgr));
437 INSIST(ISC_LIST_EMPTY(httpdmgr->running));
438
439 isc_socket_detach(&httpdmgr->sock);
440 isc_task_detach(&httpdmgr->task);
441 httpdmgr->timermgr = NULL;
442
443 /*
444 * Clear out the list of all actions we know about. Just free the
445 * memory.
446 */
447 url = ISC_LIST_HEAD(httpdmgr->urls);
448 while (url != NULL) {
449 isc_mem_free(httpdmgr->mctx, url->url);
450 ISC_LIST_UNLINK(httpdmgr->urls, url, link);
451 isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
452 url = ISC_LIST_HEAD(httpdmgr->urls);
453 }
454
455 UNLOCK(&httpdmgr->lock);
456 isc_mutex_destroy(&httpdmgr->lock);
457
458 if (httpdmgr->ondestroy != NULL) {
459 (httpdmgr->ondestroy)(httpdmgr->cb_arg);
460 }
461 isc_mem_putanddetach(&httpdmgr->mctx, httpdmgr, sizeof(isc_httpdmgr_t));
462 }
463
464 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
465 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
466
467 /*
468 * Look for the given header in headers.
469 * If value is specified look for it terminated with a character in eov.
470 */
471 static bool
have_header(isc_httpd_t * httpd,const char * header,const char * value,const char * eov)472 have_header(isc_httpd_t *httpd, const char *header, const char *value,
473 const char *eov) {
474 char *cr, *nl, *h;
475 size_t hlen, vlen = 0;
476
477 h = httpd->headers;
478 hlen = strlen(header);
479 if (value != NULL) {
480 INSIST(eov != NULL);
481 vlen = strlen(value);
482 }
483
484 for (;;) {
485 if (strncasecmp(h, header, hlen) != 0) {
486 /*
487 * Skip to next line;
488 */
489 cr = strchr(h, '\r');
490 if (cr != NULL && cr[1] == '\n') {
491 cr++;
492 }
493 nl = strchr(h, '\n');
494
495 /* last header? */
496 h = cr;
497 if (h == NULL || (nl != NULL && nl < h)) {
498 h = nl;
499 }
500 if (h == NULL) {
501 return (false);
502 }
503 h++;
504 continue;
505 }
506
507 if (value == NULL) {
508 return (true);
509 }
510
511 /*
512 * Skip optional leading white space.
513 */
514 h += hlen;
515 while (*h == ' ' || *h == '\t') {
516 h++;
517 }
518 /*
519 * Terminate token search on NULL or EOL.
520 */
521 while (*h != 0 && *h != '\r' && *h != '\n') {
522 if (strncasecmp(h, value, vlen) == 0) {
523 if (strchr(eov, h[vlen]) != NULL) {
524 return (true);
525 /*
526 * Skip to next token.
527 */
528 }
529 }
530 /*
531 * Skip to next token.
532 */
533 h += strcspn(h, eov);
534 if (h[0] == '\r' && h[1] == '\n') {
535 h++;
536 }
537 if (h[0] != 0) {
538 h++;
539 }
540 }
541 return (false);
542 }
543 }
544
545 static isc_result_t
process_request(isc_httpd_t * httpd,int length)546 process_request(isc_httpd_t *httpd, int length) {
547 char *s;
548 char *p;
549 int delim;
550
551 httpd->recvlen += length;
552
553 httpd->recvbuf[httpd->recvlen] = 0;
554 httpd->headers = NULL;
555
556 /*
557 * If we don't find a blank line in our buffer, return that we need
558 * more data.
559 */
560 s = strstr(httpd->recvbuf, "\r\n\r\n");
561 delim = 2;
562 if (s == NULL) {
563 s = strstr(httpd->recvbuf, "\n\n");
564 delim = 1;
565 }
566 if (s == NULL) {
567 return (ISC_R_NOTFOUND);
568 }
569
570 /*
571 * NUL terminate request at the blank line.
572 */
573 s[delim] = 0;
574
575 /*
576 * Determine if this is a POST or GET method. Any other values will
577 * cause an error to be returned.
578 */
579 if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
580 httpd->method = ISC_HTTPD_METHODGET;
581 p = httpd->recvbuf + 4;
582 } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
583 httpd->method = ISC_HTTPD_METHODPOST;
584 p = httpd->recvbuf + 5;
585 } else {
586 return (ISC_R_RANGE);
587 }
588
589 /*
590 * From now on, p is the start of our buffer.
591 */
592
593 /*
594 * Extract the URL.
595 */
596 s = p;
597 while (LENGTHOK(s) && BUFLENOK(s) &&
598 (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
599 {
600 s++;
601 }
602 if (!LENGTHOK(s)) {
603 return (ISC_R_NOTFOUND);
604 }
605 if (!BUFLENOK(s)) {
606 return (ISC_R_NOMEMORY);
607 }
608 *s = 0;
609
610 /*
611 * Make the URL relative.
612 */
613 if ((strncmp(p, "http:/", 6) == 0) || (strncmp(p, "https:/", 7) == 0)) {
614 /* Skip first / */
615 while (*p != '/' && *p != 0) {
616 p++;
617 }
618 if (*p == 0) {
619 return (ISC_R_RANGE);
620 }
621 p++;
622 /* Skip second / */
623 while (*p != '/' && *p != 0) {
624 p++;
625 }
626 if (*p == 0) {
627 return (ISC_R_RANGE);
628 }
629 p++;
630 /* Find third / */
631 while (*p != '/' && *p != 0) {
632 p++;
633 }
634 if (*p == 0) {
635 p--;
636 *p = '/';
637 }
638 }
639
640 httpd->url = p;
641 p = s + 1;
642 s = p;
643
644 /*
645 * Now, see if there is a ? mark in the URL. If so, this is
646 * part of the query string, and we will split it from the URL.
647 */
648 httpd->querystring = strchr(httpd->url, '?');
649 if (httpd->querystring != NULL) {
650 *(httpd->querystring) = 0;
651 httpd->querystring++;
652 }
653
654 /*
655 * Extract the HTTP/1.X protocol. We will bounce on anything but
656 * HTTP/1.0 or HTTP/1.1 for now.
657 */
658 while (LENGTHOK(s) && BUFLENOK(s) &&
659 (*s != '\n' && *s != '\r' && *s != '\0'))
660 {
661 s++;
662 }
663 if (!LENGTHOK(s)) {
664 return (ISC_R_NOTFOUND);
665 }
666 if (!BUFLENOK(s)) {
667 return (ISC_R_NOMEMORY);
668 }
669 /*
670 * Check that we have the expected eol delimiter.
671 */
672 if (strncmp(s, delim == 1 ? "\n" : "\r\n", delim) != 0) {
673 return (ISC_R_RANGE);
674 }
675 *s = 0;
676 if ((strncmp(p, "HTTP/1.0", 8) != 0) &&
677 (strncmp(p, "HTTP/1.1", 8) != 0))
678 {
679 return (ISC_R_RANGE);
680 }
681 httpd->protocol = p;
682 p = s + delim; /* skip past eol */
683 s = p;
684
685 httpd->headers = s;
686
687 if (have_header(httpd, "Connection:", "close", ", \t\r\n")) {
688 httpd->flags |= HTTPD_CLOSE;
689 }
690
691 if (have_header(httpd, "Host:", NULL, NULL)) {
692 httpd->flags |= HTTPD_FOUNDHOST;
693 }
694
695 if (strncmp(httpd->protocol, "HTTP/1.0", 8) == 0) {
696 if (have_header(httpd, "Connection:", "Keep-Alive", ", \t\r\n"))
697 {
698 httpd->flags |= HTTPD_KEEPALIVE;
699 } else {
700 httpd->flags |= HTTPD_CLOSE;
701 }
702 }
703
704 /*
705 * Check for Accept-Encoding:
706 */
707 #ifdef HAVE_ZLIB
708 if (have_header(httpd, "Accept-Encoding:", "deflate", ";, \t\r\n")) {
709 httpd->flags |= HTTPD_ACCEPT_DEFLATE;
710 }
711 #endif /* ifdef HAVE_ZLIB */
712
713 /*
714 * Standards compliance hooks here.
715 */
716 if (strcmp(httpd->protocol, "HTTP/1.1") == 0 &&
717 ((httpd->flags & HTTPD_FOUNDHOST) == 0))
718 {
719 return (ISC_R_RANGE);
720 }
721
722 return (ISC_R_SUCCESS);
723 }
724
725 static void
isc_httpd_create(isc_httpdmgr_t * httpdmgr,isc_socket_t * sock,isc_httpd_t ** httpdp)726 isc_httpd_create(isc_httpdmgr_t *httpdmgr, isc_socket_t *sock,
727 isc_httpd_t **httpdp) {
728 isc_httpd_t *httpd;
729 char *headerdata;
730
731 REQUIRE(VALID_HTTPDMGR(httpdmgr));
732 REQUIRE(httpdp != NULL && *httpdp == NULL);
733
734 httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
735
736 *httpd = (isc_httpd_t){ .sock = sock };
737
738 isc_httpdmgr_attach(httpdmgr, &httpd->mgr);
739
740 isc_refcount_init(&httpd->references, 1);
741 ISC_HTTPD_SETRECV(httpd);
742 isc_socket_setname(httpd->sock, "httpd", NULL);
743
744 /*
745 * Initialize the buffer for our headers.
746 */
747 headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
748 isc_buffer_init(&httpd->headerbuffer, headerdata, HTTP_SENDGROW);
749
750 isc_buffer_initnull(&httpd->compbuffer);
751 isc_buffer_initnull(&httpd->bodybuffer);
752 reset_client(httpd);
753
754 ISC_LINK_INIT(httpd, link);
755 ISC_LIST_APPEND(httpdmgr->running, httpd, link);
756
757 httpd->magic = HTTPD_MAGIC;
758
759 *httpdp = httpd;
760 }
761
762 static void
isc_httpd_accept(isc_task_t * task,isc_event_t * ev)763 isc_httpd_accept(isc_task_t *task, isc_event_t *ev) {
764 isc_httpdmgr_t *httpdmgr = ev->ev_arg;
765 isc_httpd_t *httpd = NULL;
766 isc_region_t r;
767 isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
768 isc_sockaddr_t peeraddr;
769
770 REQUIRE(VALID_HTTPDMGR(httpdmgr));
771
772 LOCK(&httpdmgr->lock);
773 if (MSHUTTINGDOWN(httpdmgr)) {
774 goto out;
775 }
776
777 if (nev->result == ISC_R_CANCELED) {
778 goto out;
779 }
780
781 if (nev->result != ISC_R_SUCCESS) {
782 /* XXXMLG log failure */
783 goto requeue;
784 }
785
786 (void)isc_socket_getpeername(nev->newsocket, &peeraddr);
787 if (httpdmgr->client_ok != NULL &&
788 !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg))
789 {
790 isc_socket_detach(&nev->newsocket);
791 goto requeue;
792 }
793
794 isc_httpd_create(httpdmgr, nev->newsocket, &httpd);
795
796 r.base = (unsigned char *)httpd->recvbuf;
797 r.length = HTTP_RECVLEN - 1;
798
799 httpd_socket_recv(httpd, &r, task);
800
801 requeue:
802 (void)httpdmgr_socket_accept(task, httpdmgr);
803
804 out:
805 UNLOCK(&httpdmgr->lock);
806
807 if (httpd != NULL) {
808 maybe_destroy_httpd(httpd);
809 }
810 maybe_destroy_httpdmgr(httpdmgr);
811
812 isc_event_free(&ev);
813 }
814
815 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)816 render_404(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
817 const char *headers, void *arg, unsigned int *retcode,
818 const char **retmsg, const char **mimetype, isc_buffer_t *b,
819 isc_httpdfree_t **freecb, void **freecb_args) {
820 static char msg[] = "No such URL.\r\n";
821
822 UNUSED(url);
823 UNUSED(urlinfo);
824 UNUSED(querystring);
825 UNUSED(headers);
826 UNUSED(arg);
827
828 *retcode = 404;
829 *retmsg = "No such URL";
830 *mimetype = "text/plain";
831 isc_buffer_reinit(b, msg, strlen(msg));
832 isc_buffer_add(b, strlen(msg));
833 *freecb = NULL;
834 *freecb_args = NULL;
835
836 return (ISC_R_SUCCESS);
837 }
838
839 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)840 render_500(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
841 const char *headers, void *arg, unsigned int *retcode,
842 const char **retmsg, const char **mimetype, isc_buffer_t *b,
843 isc_httpdfree_t **freecb, void **freecb_args) {
844 static char msg[] = "Internal server failure.\r\n";
845
846 UNUSED(url);
847 UNUSED(urlinfo);
848 UNUSED(querystring);
849 UNUSED(headers);
850 UNUSED(arg);
851
852 *retcode = 500;
853 *retmsg = "Internal server failure";
854 *mimetype = "text/plain";
855 isc_buffer_reinit(b, msg, strlen(msg));
856 isc_buffer_add(b, strlen(msg));
857 *freecb = NULL;
858 *freecb_args = NULL;
859
860 return (ISC_R_SUCCESS);
861 }
862
863 #ifdef HAVE_ZLIB
864 /*%<
865 * Reallocates compbuffer to size, does nothing if compbuffer is already
866 * larger than size.
867 *
868 * Requires:
869 *\li httpd a valid isc_httpd_t object
870 *
871 * Returns:
872 *\li #ISC_R_SUCCESS -- all is well.
873 *\li #ISC_R_NOMEMORY -- not enough memory to extend buffer
874 */
875 static isc_result_t
alloc_compspace(isc_httpd_t * httpd,unsigned int size)876 alloc_compspace(isc_httpd_t *httpd, unsigned int size) {
877 char *newspace;
878 isc_region_t r;
879
880 isc_buffer_region(&httpd->compbuffer, &r);
881 if (size < r.length) {
882 return (ISC_R_SUCCESS);
883 }
884
885 newspace = isc_mem_get(httpd->mgr->mctx, size);
886 isc_buffer_reinit(&httpd->compbuffer, newspace, size);
887
888 if (r.base != NULL) {
889 isc_mem_put(httpd->mgr->mctx, r.base, r.length);
890 }
891
892 return (ISC_R_SUCCESS);
893 }
894
895 /*%<
896 * Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it
897 * if necessary.
898 *
899 * Requires:
900 *\li httpd a valid isc_httpd_t object
901 *
902 * Returns:
903 *\li #ISC_R_SUCCESS -- all is well.
904 *\li #ISC_R_NOMEMORY -- not enough memory to compress data
905 *\li #ISC_R_FAILURE -- error during compression or compressed
906 * data would be larger than input data
907 */
908 static isc_result_t
isc_httpd_compress(isc_httpd_t * httpd)909 isc_httpd_compress(isc_httpd_t *httpd) {
910 z_stream zstr;
911 isc_region_t r;
912 isc_result_t result;
913 int ret;
914 int inputlen;
915
916 inputlen = isc_buffer_usedlength(&httpd->bodybuffer);
917 result = alloc_compspace(httpd, inputlen);
918 if (result != ISC_R_SUCCESS) {
919 return (result);
920 }
921 isc_buffer_clear(&httpd->compbuffer);
922 isc_buffer_region(&httpd->compbuffer, &r);
923
924 /*
925 * We're setting output buffer size to input size so it fails if the
926 * compressed data size would be bigger than the input size.
927 */
928 memset(&zstr, 0, sizeof(zstr));
929 zstr.total_in = zstr.avail_in = zstr.total_out = zstr.avail_out =
930 inputlen;
931
932 zstr.next_in = isc_buffer_base(&httpd->bodybuffer);
933 zstr.next_out = r.base;
934
935 ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION);
936 if (ret == Z_OK) {
937 ret = deflate(&zstr, Z_FINISH);
938 }
939 deflateEnd(&zstr);
940 if (ret == Z_STREAM_END) {
941 isc_buffer_add(&httpd->compbuffer, inputlen - zstr.avail_out);
942 return (ISC_R_SUCCESS);
943 } else {
944 return (ISC_R_FAILURE);
945 }
946 }
947 #endif /* ifdef HAVE_ZLIB */
948
949 static void
isc_httpd_recvdone(isc_task_t * task,isc_event_t * ev)950 isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) {
951 isc_result_t result;
952 isc_httpd_t *httpd = ev->ev_arg;
953 isc_socketevent_t *sev = (isc_socketevent_t *)ev;
954 isc_buffer_t *databuffer;
955 isc_httpdurl_t *url;
956 isc_time_t now;
957 isc_region_t r;
958 bool is_compressed = false;
959 char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
960
961 REQUIRE(VALID_HTTPD(httpd));
962
963 INSIST(ISC_HTTPD_ISRECV(httpd));
964
965 if (sev->result != ISC_R_SUCCESS) {
966 goto out;
967 }
968
969 result = process_request(httpd, sev->n);
970 if (result == ISC_R_NOTFOUND) {
971 if (httpd->recvlen >= HTTP_RECVLEN - 1) {
972 goto out;
973 }
974 r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
975 r.length = HTTP_RECVLEN - httpd->recvlen - 1;
976
977 httpd_socket_recv(httpd, &r, task);
978 goto out;
979 } else if (result != ISC_R_SUCCESS) {
980 goto out;
981 }
982
983 ISC_HTTPD_SETSEND(httpd);
984
985 /*
986 * XXXMLG Call function here. Provide an add-header function
987 * which will append the common headers to a response we generate.
988 */
989 isc_buffer_initnull(&httpd->bodybuffer);
990 isc_time_now(&now);
991 isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
992 LOCK(&httpd->mgr->lock);
993 url = ISC_LIST_HEAD(httpd->mgr->urls);
994 while (url != NULL) {
995 if (strcmp(httpd->url, url->url) == 0) {
996 break;
997 }
998 url = ISC_LIST_NEXT(url, link);
999 }
1000 UNLOCK(&httpd->mgr->lock);
1001
1002 if (url == NULL) {
1003 result = httpd->mgr->render_404(
1004 httpd->url, NULL, httpd->querystring, NULL, NULL,
1005 &httpd->retcode, &httpd->retmsg, &httpd->mimetype,
1006 &httpd->bodybuffer, &httpd->freecb, &httpd->freecb_arg);
1007 } else {
1008 result = url->action(httpd->url, url, httpd->querystring,
1009 httpd->headers, url->action_arg,
1010 &httpd->retcode, &httpd->retmsg,
1011 &httpd->mimetype, &httpd->bodybuffer,
1012 &httpd->freecb, &httpd->freecb_arg);
1013 }
1014 if (result != ISC_R_SUCCESS) {
1015 result = httpd->mgr->render_500(
1016 httpd->url, url, httpd->querystring, NULL, NULL,
1017 &httpd->retcode, &httpd->retmsg, &httpd->mimetype,
1018 &httpd->bodybuffer, &httpd->freecb, &httpd->freecb_arg);
1019 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1020 }
1021
1022 #ifdef HAVE_ZLIB
1023 if ((httpd->flags & HTTPD_ACCEPT_DEFLATE) != 0) {
1024 result = isc_httpd_compress(httpd);
1025 if (result == ISC_R_SUCCESS) {
1026 is_compressed = true;
1027 }
1028 }
1029 #endif /* ifdef HAVE_ZLIB */
1030
1031 isc_httpd_response(httpd);
1032 if ((httpd->flags & HTTPD_KEEPALIVE) != 0) {
1033 isc_httpd_addheader(httpd, "Connection", "Keep-Alive");
1034 }
1035 isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
1036 isc_httpd_addheader(httpd, "Date", datebuf);
1037 isc_httpd_addheader(httpd, "Expires", datebuf);
1038
1039 if (url != NULL && url->isstatic) {
1040 char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
1041 isc_time_formathttptimestamp(&url->loadtime, loadbuf,
1042 sizeof(loadbuf));
1043 isc_httpd_addheader(httpd, "Last-Modified", loadbuf);
1044 isc_httpd_addheader(httpd, "Cache-Control: public", NULL);
1045 } else {
1046 isc_httpd_addheader(httpd, "Last-Modified", datebuf);
1047 isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
1048 isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
1049 }
1050
1051 isc_httpd_addheader(httpd, "Server: libisc", NULL);
1052
1053 if (is_compressed) {
1054 isc_httpd_addheader(httpd, "Content-Encoding", "deflate");
1055 isc_httpd_addheaderuint(
1056 httpd, "Content-Length",
1057 isc_buffer_usedlength(&httpd->compbuffer));
1058 } else {
1059 isc_httpd_addheaderuint(
1060 httpd, "Content-Length",
1061 isc_buffer_usedlength(&httpd->bodybuffer));
1062 }
1063
1064 isc_httpd_endheaders(httpd); /* done */
1065
1066 /*
1067 * Append either the compressed or the non-compressed response body to
1068 * the response headers and store the result in httpd->sendbuffer.
1069 */
1070 isc_buffer_dup(httpd->mgr->mctx, &httpd->sendbuffer,
1071 &httpd->headerbuffer);
1072 isc_buffer_setautorealloc(httpd->sendbuffer, true);
1073 databuffer = (is_compressed ? &httpd->compbuffer : &httpd->bodybuffer);
1074 isc_buffer_usedregion(databuffer, &r);
1075 result = isc_buffer_copyregion(httpd->sendbuffer, &r);
1076 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1077 /*
1078 * Determine total response size.
1079 */
1080 isc_buffer_usedregion(httpd->sendbuffer, &r);
1081
1082 httpd_socket_send(httpd, &r, task);
1083
1084 out:
1085 maybe_destroy_httpd(httpd);
1086 isc_event_free(&ev);
1087 }
1088
1089 void
isc_httpdmgr_shutdown(isc_httpdmgr_t ** httpdmgrp)1090 isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
1091 isc_httpdmgr_t *httpdmgr;
1092 isc_httpd_t *httpd;
1093
1094 REQUIRE(httpdmgrp != NULL);
1095 httpdmgr = *httpdmgrp;
1096 *httpdmgrp = NULL;
1097 REQUIRE(VALID_HTTPDMGR(httpdmgr));
1098
1099 LOCK(&httpdmgr->lock);
1100
1101 MSETSHUTTINGDOWN(httpdmgr);
1102
1103 isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
1104
1105 httpd = ISC_LIST_HEAD(httpdmgr->running);
1106 while (httpd != NULL) {
1107 isc_socket_cancel(httpd->sock, httpdmgr->task,
1108 ISC_SOCKCANCEL_ALL);
1109 httpd = ISC_LIST_NEXT(httpd, link);
1110 }
1111
1112 UNLOCK(&httpdmgr->lock);
1113
1114 maybe_destroy_httpdmgr(httpdmgr);
1115 }
1116
1117 static isc_result_t
grow_headerspace(isc_httpd_t * httpd)1118 grow_headerspace(isc_httpd_t *httpd) {
1119 char *newspace;
1120 unsigned int newlen;
1121 isc_region_t r;
1122
1123 isc_buffer_region(&httpd->headerbuffer, &r);
1124 newlen = r.length + HTTP_SENDGROW;
1125 if (newlen > HTTP_SEND_MAXLEN) {
1126 return (ISC_R_NOSPACE);
1127 }
1128
1129 newspace = isc_mem_get(httpd->mgr->mctx, newlen);
1130
1131 isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
1132
1133 isc_mem_put(httpd->mgr->mctx, r.base, r.length);
1134
1135 return (ISC_R_SUCCESS);
1136 }
1137
1138 isc_result_t
isc_httpd_response(isc_httpd_t * httpd)1139 isc_httpd_response(isc_httpd_t *httpd) {
1140 isc_result_t result;
1141 unsigned int needlen;
1142
1143 REQUIRE(VALID_HTTPD(httpd));
1144
1145 needlen = strlen(httpd->protocol) + 1; /* protocol + space */
1146 needlen += 3 + 1; /* room for response code, always 3 bytes */
1147 needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
1148
1149 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
1150 result = grow_headerspace(httpd);
1151 if (result != ISC_R_SUCCESS) {
1152 return (result);
1153 }
1154 }
1155
1156 return (isc_buffer_printf(&httpd->headerbuffer, "%s %03u %s\r\n",
1157 httpd->protocol, httpd->retcode,
1158 httpd->retmsg));
1159 }
1160
1161 isc_result_t
isc_httpd_addheader(isc_httpd_t * httpd,const char * name,const char * val)1162 isc_httpd_addheader(isc_httpd_t *httpd, const char *name, const char *val) {
1163 isc_result_t result;
1164 unsigned int needlen;
1165
1166 REQUIRE(VALID_HTTPD(httpd));
1167
1168 needlen = strlen(name); /* name itself */
1169 if (val != NULL) {
1170 needlen += 2 + strlen(val); /* :<space> and val */
1171 }
1172 needlen += 2; /* CRLF */
1173
1174 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
1175 result = grow_headerspace(httpd);
1176 if (result != ISC_R_SUCCESS) {
1177 return (result);
1178 }
1179 }
1180
1181 if (val != NULL) {
1182 return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n",
1183 name, val));
1184 } else {
1185 return (isc_buffer_printf(&httpd->headerbuffer, "%s\r\n",
1186 name));
1187 }
1188 }
1189
1190 isc_result_t
isc_httpd_endheaders(isc_httpd_t * httpd)1191 isc_httpd_endheaders(isc_httpd_t *httpd) {
1192 isc_result_t result;
1193
1194 REQUIRE(VALID_HTTPD(httpd));
1195
1196 while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
1197 result = grow_headerspace(httpd);
1198 if (result != ISC_R_SUCCESS) {
1199 return (result);
1200 }
1201 }
1202
1203 return (isc_buffer_printf(&httpd->headerbuffer, "\r\n"));
1204 }
1205
1206 isc_result_t
isc_httpd_addheaderuint(isc_httpd_t * httpd,const char * name,int val)1207 isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
1208 isc_result_t result;
1209 unsigned int needlen;
1210 char buf[sizeof "18446744073709551616"];
1211
1212 REQUIRE(VALID_HTTPD(httpd));
1213
1214 snprintf(buf, sizeof(buf), "%d", val);
1215
1216 needlen = strlen(name); /* name itself */
1217 needlen += 2 + strlen(buf); /* :<space> and val */
1218 needlen += 2; /* CRLF */
1219
1220 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
1221 result = grow_headerspace(httpd);
1222 if (result != ISC_R_SUCCESS) {
1223 return (result);
1224 }
1225 }
1226
1227 return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n", name,
1228 buf));
1229 }
1230
1231 static void
isc_httpd_senddone(isc_task_t * task,isc_event_t * ev)1232 isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) {
1233 isc_httpd_t *httpd = ev->ev_arg;
1234 isc_region_t r;
1235 isc_socketevent_t *sev = (isc_socketevent_t *)ev;
1236
1237 REQUIRE(VALID_HTTPD(httpd));
1238
1239 INSIST(ISC_HTTPD_ISSEND(httpd));
1240
1241 isc_buffer_free(&httpd->sendbuffer);
1242
1243 /*
1244 * We will always want to clean up our receive buffer, even if we
1245 * got an error on send or we are shutting down.
1246 *
1247 * We will pass in the buffer only if there is data in it. If
1248 * there is no data, we will pass in a NULL.
1249 */
1250 if (httpd->freecb != NULL) {
1251 isc_buffer_t *b = NULL;
1252 if (isc_buffer_length(&httpd->bodybuffer) > 0) {
1253 b = &httpd->bodybuffer;
1254 httpd->freecb(b, httpd->freecb_arg);
1255 }
1256 }
1257
1258 if (sev->result != ISC_R_SUCCESS) {
1259 goto out;
1260 }
1261
1262 if ((httpd->flags & HTTPD_CLOSE) != 0) {
1263 goto out;
1264 }
1265
1266 ISC_HTTPD_SETRECV(httpd);
1267
1268 reset_client(httpd);
1269
1270 r.base = (unsigned char *)httpd->recvbuf;
1271 r.length = HTTP_RECVLEN - 1;
1272
1273 httpd_socket_recv(httpd, &r, task);
1274
1275 out:
1276 maybe_destroy_httpd(httpd);
1277 isc_event_free(&ev);
1278 }
1279
1280 static void
reset_client(isc_httpd_t * httpd)1281 reset_client(isc_httpd_t *httpd) {
1282 /*
1283 * Catch errors here. We MUST be in RECV mode, and we MUST NOT have
1284 * any outstanding buffers. If we have buffers, we have a leak.
1285 */
1286 INSIST(ISC_HTTPD_ISRECV(httpd));
1287 INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
1288 INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
1289
1290 httpd->recvbuf[0] = 0;
1291 httpd->recvlen = 0;
1292 httpd->headers = NULL;
1293 httpd->method = ISC_HTTPD_METHODUNKNOWN;
1294 httpd->url = NULL;
1295 httpd->querystring = NULL;
1296 httpd->protocol = NULL;
1297 httpd->flags = 0;
1298
1299 isc_buffer_clear(&httpd->headerbuffer);
1300 isc_buffer_clear(&httpd->compbuffer);
1301 isc_buffer_invalidate(&httpd->bodybuffer);
1302 }
1303
1304 isc_result_t
isc_httpdmgr_addurl(isc_httpdmgr_t * httpdmgr,const char * url,isc_httpdaction_t * func,void * arg)1305 isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
1306 isc_httpdaction_t *func, void *arg) {
1307 /* REQUIRE(VALID_HTTPDMGR(httpdmgr)); Dummy function */
1308
1309 return (isc_httpdmgr_addurl2(httpdmgr, url, false, func, arg));
1310 }
1311
1312 isc_result_t
isc_httpdmgr_addurl2(isc_httpdmgr_t * httpdmgr,const char * url,bool isstatic,isc_httpdaction_t * func,void * arg)1313 isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url, bool isstatic,
1314 isc_httpdaction_t *func, void *arg) {
1315 isc_httpdurl_t *item;
1316
1317 REQUIRE(VALID_HTTPDMGR(httpdmgr));
1318
1319 if (url == NULL) {
1320 httpdmgr->render_404 = func;
1321 return (ISC_R_SUCCESS);
1322 }
1323
1324 item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
1325
1326 item->url = isc_mem_strdup(httpdmgr->mctx, url);
1327
1328 item->action = func;
1329 item->action_arg = arg;
1330 item->isstatic = isstatic;
1331 isc_time_now(&item->loadtime);
1332
1333 ISC_LINK_INIT(item, link);
1334
1335 LOCK(&httpdmgr->lock);
1336 ISC_LIST_APPEND(httpdmgr->urls, item, link);
1337 UNLOCK(&httpdmgr->lock);
1338
1339 return (ISC_R_SUCCESS);
1340 }
1341
1342 void
isc_httpd_setfinishhook(void (* fn)(void))1343 isc_httpd_setfinishhook(void (*fn)(void)) {
1344 #if ENABLE_AFL
1345 finishhook = fn;
1346 #else /* ENABLE_AFL */
1347 UNUSED(fn);
1348 #endif /* ENABLE_AFL */
1349 }
1350