Home | History | Annotate | Line # | Download | only in isc
httpd.c revision 1.7
      1 /*	$NetBSD: httpd.c,v 1.7 2022/09/23 12:15:33 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
    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
    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 
    255 static void
    256 destroy_httpd(isc_httpd_t *httpd) {
    257 	isc_httpdmgr_t *httpdmgr;
    258 
    259 	REQUIRE(VALID_HTTPD(httpd));
    260 
    261 	httpdmgr = httpd->mgr;
    262 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
    263 
    264 	/*
    265 	 * Unlink before calling isc_socket_detach so
    266 	 * isc_httpdmgr_shutdown does not dereference a NULL pointer
    267 	 * when calling isc_socket_cancel().
    268 	 */
    269 	LOCK(&httpdmgr->lock);
    270 	ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
    271 	UNLOCK(&httpdmgr->lock);
    272 
    273 	httpd->magic = 0;
    274 	isc_refcount_destroy(&httpd->references);
    275 	isc_socket_detach(&httpd->sock);
    276 
    277 	free_buffer(httpdmgr->mctx, &httpd->headerbuffer);
    278 	free_buffer(httpdmgr->mctx, &httpd->compbuffer);
    279 
    280 	isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
    281 
    282 #if ENABLE_AFL
    283 	if (finishhook != NULL) {
    284 		finishhook();
    285 	}
    286 #endif /* ENABLE_AFL */
    287 
    288 	isc_httpdmgr_detach(&httpdmgr);
    289 }
    290 
    291 static isc_result_t
    292 httpdmgr_socket_accept(isc_task_t *task, isc_httpdmgr_t *httpdmgr) {
    293 	isc_result_t result = ISC_R_SUCCESS;
    294 
    295 	/* decremented in isc_httpd_accept */
    296 	isc_refcount_increment(&httpdmgr->references);
    297 	result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
    298 				   httpdmgr);
    299 	if (result != ISC_R_SUCCESS) {
    300 		INSIST(isc_refcount_decrement(&httpdmgr->references) > 1);
    301 	}
    302 	return (result);
    303 }
    304 
    305 static void
    306 httpd_socket_recv(isc_httpd_t *httpd, isc_region_t *region, isc_task_t *task) {
    307 	isc_result_t result = ISC_R_SUCCESS;
    308 
    309 	/* decremented in isc_httpd_recvdone */
    310 	(void)isc_refcount_increment(&httpd->references);
    311 	result = isc_socket_recv(httpd->sock, region, 1, task,
    312 				 isc_httpd_recvdone, httpd);
    313 	if (result != ISC_R_SUCCESS) {
    314 		INSIST(isc_refcount_decrement(&httpd->references) > 1);
    315 	}
    316 }
    317 
    318 static void
    319 httpd_socket_send(isc_httpd_t *httpd, isc_region_t *region, isc_task_t *task) {
    320 	isc_result_t result = ISC_R_SUCCESS;
    321 
    322 	/* decremented in isc_httpd_senddone */
    323 	(void)isc_refcount_increment(&httpd->references);
    324 	result = isc_socket_send(httpd->sock, region, task, isc_httpd_senddone,
    325 				 httpd);
    326 	if (result != ISC_R_SUCCESS) {
    327 		INSIST(isc_refcount_decrement(&httpd->references) > 1);
    328 	}
    329 }
    330 
    331 isc_result_t
    332 isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
    333 		    isc_httpdclientok_t *client_ok,
    334 		    isc_httpdondestroy_t *ondestroy, void *cb_arg,
    335 		    isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdmgrp) {
    336 	isc_result_t result;
    337 	isc_httpdmgr_t *httpdmgr;
    338 
    339 	REQUIRE(mctx != NULL);
    340 	REQUIRE(sock != NULL);
    341 	REQUIRE(task != NULL);
    342 	REQUIRE(tmgr != NULL);
    343 	REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL);
    344 
    345 	httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
    346 
    347 	*httpdmgr = (isc_httpdmgr_t){ .timermgr = tmgr, /* XXXMLG no attach
    348 							 * function? */
    349 				      .client_ok = client_ok,
    350 				      .ondestroy = ondestroy,
    351 				      .cb_arg = cb_arg,
    352 				      .render_404 = render_404,
    353 				      .render_500 = render_500 };
    354 
    355 	isc_mutex_init(&httpdmgr->lock);
    356 	isc_mem_attach(mctx, &httpdmgr->mctx);
    357 	isc_socket_attach(sock, &httpdmgr->sock);
    358 	isc_task_attach(task, &httpdmgr->task);
    359 
    360 	ISC_LIST_INIT(httpdmgr->running);
    361 	ISC_LIST_INIT(httpdmgr->urls);
    362 
    363 	isc_refcount_init(&httpdmgr->references, 1);
    364 
    365 	/* XXXMLG ignore errors on isc_socket_listen() */
    366 	result = isc_socket_listen(sock, SOMAXCONN);
    367 	if (result != ISC_R_SUCCESS) {
    368 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    369 				 "isc_socket_listen() failed: %s",
    370 				 isc_result_totext(result));
    371 		goto cleanup;
    372 	}
    373 
    374 	(void)isc_socket_filter(sock, "httpready");
    375 
    376 	httpdmgr->magic = HTTPDMGR_MAGIC;
    377 
    378 	result = httpdmgr_socket_accept(task, httpdmgr);
    379 	if (result != ISC_R_SUCCESS) {
    380 		goto cleanup;
    381 	}
    382 
    383 	*httpdmgrp = httpdmgr;
    384 	return (ISC_R_SUCCESS);
    385 
    386 cleanup:
    387 	httpdmgr->magic = 0;
    388 	isc_refcount_decrementz(&httpdmgr->references);
    389 	isc_refcount_destroy(&httpdmgr->references);
    390 	isc_task_detach(&httpdmgr->task);
    391 	isc_socket_detach(&httpdmgr->sock);
    392 	isc_mem_detach(&httpdmgr->mctx);
    393 	isc_mutex_destroy(&httpdmgr->lock);
    394 	isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
    395 	return (result);
    396 }
    397 
    398 static void
    399 isc_httpdmgr_attach(isc_httpdmgr_t *source, isc_httpdmgr_t **targetp) {
    400 	REQUIRE(VALID_HTTPDMGR(source));
    401 	REQUIRE(targetp != NULL && *targetp == NULL);
    402 
    403 	isc_refcount_increment(&source->references);
    404 
    405 	*targetp = source;
    406 }
    407 
    408 static void
    409 isc_httpdmgr_detach(isc_httpdmgr_t **httpdmgrp) {
    410 	REQUIRE(httpdmgrp != NULL && VALID_HTTPDMGR(*httpdmgrp));
    411 	isc_httpdmgr_t *httpdmgr = *httpdmgrp;
    412 	*httpdmgrp = NULL;
    413 
    414 	maybe_destroy_httpdmgr(httpdmgr);
    415 }
    416 
    417 static void
    418 maybe_destroy_httpdmgr(isc_httpdmgr_t *httpdmgr) {
    419 	if (isc_refcount_decrement(&httpdmgr->references) == 1) {
    420 		destroy_httpdmgr(httpdmgr);
    421 	}
    422 }
    423 
    424 static void
    425 destroy_httpdmgr(isc_httpdmgr_t *httpdmgr) {
    426 	isc_httpdurl_t *url;
    427 
    428 	isc_refcount_destroy(&httpdmgr->references);
    429 
    430 	LOCK(&httpdmgr->lock);
    431 
    432 	httpdmgr->magic = 0;
    433 
    434 	INSIST(MSHUTTINGDOWN(httpdmgr));
    435 	INSIST(ISC_LIST_EMPTY(httpdmgr->running));
    436 
    437 	isc_socket_detach(&httpdmgr->sock);
    438 	isc_task_detach(&httpdmgr->task);
    439 	httpdmgr->timermgr = NULL;
    440 
    441 	/*
    442 	 * Clear out the list of all actions we know about.  Just free the
    443 	 * memory.
    444 	 */
    445 	url = ISC_LIST_HEAD(httpdmgr->urls);
    446 	while (url != NULL) {
    447 		isc_mem_free(httpdmgr->mctx, url->url);
    448 		ISC_LIST_UNLINK(httpdmgr->urls, url, link);
    449 		isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
    450 		url = ISC_LIST_HEAD(httpdmgr->urls);
    451 	}
    452 
    453 	UNLOCK(&httpdmgr->lock);
    454 	isc_mutex_destroy(&httpdmgr->lock);
    455 
    456 	if (httpdmgr->ondestroy != NULL) {
    457 		(httpdmgr->ondestroy)(httpdmgr->cb_arg);
    458 	}
    459 	isc_mem_putanddetach(&httpdmgr->mctx, httpdmgr, sizeof(isc_httpdmgr_t));
    460 }
    461 
    462 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
    463 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
    464 
    465 /*
    466  * Look for the given header in headers.
    467  * If value is specified look for it terminated with a character in eov.
    468  */
    469 static bool
    470 have_header(isc_httpd_t *httpd, const char *header, const char *value,
    471 	    const char *eov) {
    472 	char *cr, *nl, *h;
    473 	size_t hlen, vlen = 0;
    474 
    475 	h = httpd->headers;
    476 	hlen = strlen(header);
    477 	if (value != NULL) {
    478 		INSIST(eov != NULL);
    479 		vlen = strlen(value);
    480 	}
    481 
    482 	for (;;) {
    483 		if (strncasecmp(h, header, hlen) != 0) {
    484 			/*
    485 			 * Skip to next line;
    486 			 */
    487 			cr = strchr(h, '\r');
    488 			if (cr != NULL && cr[1] == '\n') {
    489 				cr++;
    490 			}
    491 			nl = strchr(h, '\n');
    492 
    493 			/* last header? */
    494 			h = cr;
    495 			if (h == NULL || (nl != NULL && nl < h)) {
    496 				h = nl;
    497 			}
    498 			if (h == NULL) {
    499 				return (false);
    500 			}
    501 			h++;
    502 			continue;
    503 		}
    504 
    505 		if (value == NULL) {
    506 			return (true);
    507 		}
    508 
    509 		/*
    510 		 * Skip optional leading white space.
    511 		 */
    512 		h += hlen;
    513 		while (*h == ' ' || *h == '\t') {
    514 			h++;
    515 		}
    516 		/*
    517 		 * Terminate token search on NULL or EOL.
    518 		 */
    519 		while (*h != 0 && *h != '\r' && *h != '\n') {
    520 			if (strncasecmp(h, value, vlen) == 0) {
    521 				if (strchr(eov, h[vlen]) != NULL) {
    522 					return (true);
    523 					/*
    524 					 * Skip to next token.
    525 					 */
    526 				}
    527 			}
    528 			/*
    529 			 * Skip to next token.
    530 			 */
    531 			h += strcspn(h, eov);
    532 			if (h[0] == '\r' && h[1] == '\n') {
    533 				h++;
    534 			}
    535 			if (h[0] != 0) {
    536 				h++;
    537 			}
    538 		}
    539 		return (false);
    540 	}
    541 }
    542 
    543 static isc_result_t
    544 process_request(isc_httpd_t *httpd, int length) {
    545 	char *s;
    546 	char *p;
    547 	int delim;
    548 
    549 	httpd->recvlen += length;
    550 
    551 	httpd->recvbuf[httpd->recvlen] = 0;
    552 	httpd->headers = NULL;
    553 
    554 	/*
    555 	 * If we don't find a blank line in our buffer, return that we need
    556 	 * more data.
    557 	 */
    558 	s = strstr(httpd->recvbuf, "\r\n\r\n");
    559 	delim = 2;
    560 	if (s == NULL) {
    561 		s = strstr(httpd->recvbuf, "\n\n");
    562 		delim = 1;
    563 	}
    564 	if (s == NULL) {
    565 		return (ISC_R_NOTFOUND);
    566 	}
    567 
    568 	/*
    569 	 * NUL terminate request at the blank line.
    570 	 */
    571 	s[delim] = 0;
    572 
    573 	/*
    574 	 * Determine if this is a POST or GET method.  Any other values will
    575 	 * cause an error to be returned.
    576 	 */
    577 	if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
    578 		httpd->method = ISC_HTTPD_METHODGET;
    579 		p = httpd->recvbuf + 4;
    580 	} else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
    581 		httpd->method = ISC_HTTPD_METHODPOST;
    582 		p = httpd->recvbuf + 5;
    583 	} else {
    584 		return (ISC_R_RANGE);
    585 	}
    586 
    587 	/*
    588 	 * From now on, p is the start of our buffer.
    589 	 */
    590 
    591 	/*
    592 	 * Extract the URL.
    593 	 */
    594 	s = p;
    595 	while (LENGTHOK(s) && BUFLENOK(s) &&
    596 	       (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
    597 	{
    598 		s++;
    599 	}
    600 	if (!LENGTHOK(s)) {
    601 		return (ISC_R_NOTFOUND);
    602 	}
    603 	if (!BUFLENOK(s)) {
    604 		return (ISC_R_NOMEMORY);
    605 	}
    606 	*s = 0;
    607 
    608 	/*
    609 	 * Make the URL relative.
    610 	 */
    611 	if ((strncmp(p, "http:/", 6) == 0) || (strncmp(p, "https:/", 7) == 0)) {
    612 		/* Skip first / */
    613 		while (*p != '/' && *p != 0) {
    614 			p++;
    615 		}
    616 		if (*p == 0) {
    617 			return (ISC_R_RANGE);
    618 		}
    619 		p++;
    620 		/* Skip second / */
    621 		while (*p != '/' && *p != 0) {
    622 			p++;
    623 		}
    624 		if (*p == 0) {
    625 			return (ISC_R_RANGE);
    626 		}
    627 		p++;
    628 		/* Find third / */
    629 		while (*p != '/' && *p != 0) {
    630 			p++;
    631 		}
    632 		if (*p == 0) {
    633 			p--;
    634 			*p = '/';
    635 		}
    636 	}
    637 
    638 	httpd->url = p;
    639 	p = s + 1;
    640 	s = p;
    641 
    642 	/*
    643 	 * Now, see if there is a ? mark in the URL.  If so, this is
    644 	 * part of the query string, and we will split it from the URL.
    645 	 */
    646 	httpd->querystring = strchr(httpd->url, '?');
    647 	if (httpd->querystring != NULL) {
    648 		*(httpd->querystring) = 0;
    649 		httpd->querystring++;
    650 	}
    651 
    652 	/*
    653 	 * Extract the HTTP/1.X protocol.  We will bounce on anything but
    654 	 * HTTP/1.0 or HTTP/1.1 for now.
    655 	 */
    656 	while (LENGTHOK(s) && BUFLENOK(s) &&
    657 	       (*s != '\n' && *s != '\r' && *s != '\0')) {
    658 		s++;
    659 	}
    660 	if (!LENGTHOK(s)) {
    661 		return (ISC_R_NOTFOUND);
    662 	}
    663 	if (!BUFLENOK(s)) {
    664 		return (ISC_R_NOMEMORY);
    665 	}
    666 	/*
    667 	 * Check that we have the expected eol delimiter.
    668 	 */
    669 	if (strncmp(s, delim == 1 ? "\n" : "\r\n", delim) != 0) {
    670 		return (ISC_R_RANGE);
    671 	}
    672 	*s = 0;
    673 	if ((strncmp(p, "HTTP/1.0", 8) != 0) &&
    674 	    (strncmp(p, "HTTP/1.1", 8) != 0)) {
    675 		return (ISC_R_RANGE);
    676 	}
    677 	httpd->protocol = p;
    678 	p = s + delim; /* skip past eol */
    679 	s = p;
    680 
    681 	httpd->headers = s;
    682 
    683 	if (have_header(httpd, "Connection:", "close", ", \t\r\n")) {
    684 		httpd->flags |= HTTPD_CLOSE;
    685 	}
    686 
    687 	if (have_header(httpd, "Host:", NULL, NULL)) {
    688 		httpd->flags |= HTTPD_FOUNDHOST;
    689 	}
    690 
    691 	if (strncmp(httpd->protocol, "HTTP/1.0", 8) == 0) {
    692 		if (have_header(httpd, "Connection:", "Keep-Alive", ", \t\r\n"))
    693 		{
    694 			httpd->flags |= HTTPD_KEEPALIVE;
    695 		} else {
    696 			httpd->flags |= HTTPD_CLOSE;
    697 		}
    698 	}
    699 
    700 	/*
    701 	 * Check for Accept-Encoding:
    702 	 */
    703 #ifdef HAVE_ZLIB
    704 	if (have_header(httpd, "Accept-Encoding:", "deflate", ";, \t\r\n")) {
    705 		httpd->flags |= HTTPD_ACCEPT_DEFLATE;
    706 	}
    707 #endif /* ifdef HAVE_ZLIB */
    708 
    709 	/*
    710 	 * Standards compliance hooks here.
    711 	 */
    712 	if (strcmp(httpd->protocol, "HTTP/1.1") == 0 &&
    713 	    ((httpd->flags & HTTPD_FOUNDHOST) == 0))
    714 	{
    715 		return (ISC_R_RANGE);
    716 	}
    717 
    718 	return (ISC_R_SUCCESS);
    719 }
    720 
    721 static void
    722 isc_httpd_create(isc_httpdmgr_t *httpdmgr, isc_socket_t *sock,
    723 		 isc_httpd_t **httpdp) {
    724 	isc_httpd_t *httpd;
    725 	char *headerdata;
    726 
    727 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
    728 	REQUIRE(httpdp != NULL && *httpdp == NULL);
    729 
    730 	httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
    731 
    732 	*httpd = (isc_httpd_t){ .sock = sock };
    733 
    734 	isc_httpdmgr_attach(httpdmgr, &httpd->mgr);
    735 
    736 	isc_refcount_init(&httpd->references, 1);
    737 	ISC_HTTPD_SETRECV(httpd);
    738 	isc_socket_setname(httpd->sock, "httpd", NULL);
    739 
    740 	/*
    741 	 * Initialize the buffer for our headers.
    742 	 */
    743 	headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
    744 	isc_buffer_init(&httpd->headerbuffer, headerdata, HTTP_SENDGROW);
    745 
    746 	isc_buffer_initnull(&httpd->compbuffer);
    747 	isc_buffer_initnull(&httpd->bodybuffer);
    748 	reset_client(httpd);
    749 
    750 	ISC_LINK_INIT(httpd, link);
    751 	ISC_LIST_APPEND(httpdmgr->running, httpd, link);
    752 
    753 	httpd->magic = HTTPD_MAGIC;
    754 
    755 	*httpdp = httpd;
    756 }
    757 
    758 static void
    759 isc_httpd_accept(isc_task_t *task, isc_event_t *ev) {
    760 	isc_httpdmgr_t *httpdmgr = ev->ev_arg;
    761 	isc_httpd_t *httpd = NULL;
    762 	isc_region_t r;
    763 	isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
    764 	isc_sockaddr_t peeraddr;
    765 
    766 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
    767 
    768 	LOCK(&httpdmgr->lock);
    769 	if (MSHUTTINGDOWN(httpdmgr)) {
    770 		goto out;
    771 	}
    772 
    773 	if (nev->result == ISC_R_CANCELED) {
    774 		goto out;
    775 	}
    776 
    777 	if (nev->result != ISC_R_SUCCESS) {
    778 		/* XXXMLG log failure */
    779 		goto requeue;
    780 	}
    781 
    782 	(void)isc_socket_getpeername(nev->newsocket, &peeraddr);
    783 	if (httpdmgr->client_ok != NULL &&
    784 	    !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg))
    785 	{
    786 		isc_socket_detach(&nev->newsocket);
    787 		goto requeue;
    788 	}
    789 
    790 	isc_httpd_create(httpdmgr, nev->newsocket, &httpd);
    791 
    792 	r.base = (unsigned char *)httpd->recvbuf;
    793 	r.length = HTTP_RECVLEN - 1;
    794 
    795 	httpd_socket_recv(httpd, &r, task);
    796 
    797 requeue:
    798 	(void)httpdmgr_socket_accept(task, httpdmgr);
    799 
    800 out:
    801 	UNLOCK(&httpdmgr->lock);
    802 
    803 	if (httpd != NULL) {
    804 		maybe_destroy_httpd(httpd);
    805 	}
    806 	maybe_destroy_httpdmgr(httpdmgr);
    807 
    808 	isc_event_free(&ev);
    809 }
    810 
    811 static isc_result_t
    812 render_404(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
    813 	   const char *headers, void *arg, unsigned int *retcode,
    814 	   const char **retmsg, const char **mimetype, isc_buffer_t *b,
    815 	   isc_httpdfree_t **freecb, void **freecb_args) {
    816 	static char msg[] = "No such URL.\r\n";
    817 
    818 	UNUSED(url);
    819 	UNUSED(urlinfo);
    820 	UNUSED(querystring);
    821 	UNUSED(headers);
    822 	UNUSED(arg);
    823 
    824 	*retcode = 404;
    825 	*retmsg = "No such URL";
    826 	*mimetype = "text/plain";
    827 	isc_buffer_reinit(b, msg, strlen(msg));
    828 	isc_buffer_add(b, strlen(msg));
    829 	*freecb = NULL;
    830 	*freecb_args = NULL;
    831 
    832 	return (ISC_R_SUCCESS);
    833 }
    834 
    835 static isc_result_t
    836 render_500(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
    837 	   const char *headers, void *arg, unsigned int *retcode,
    838 	   const char **retmsg, const char **mimetype, isc_buffer_t *b,
    839 	   isc_httpdfree_t **freecb, void **freecb_args) {
    840 	static char msg[] = "Internal server failure.\r\n";
    841 
    842 	UNUSED(url);
    843 	UNUSED(urlinfo);
    844 	UNUSED(querystring);
    845 	UNUSED(headers);
    846 	UNUSED(arg);
    847 
    848 	*retcode = 500;
    849 	*retmsg = "Internal server failure";
    850 	*mimetype = "text/plain";
    851 	isc_buffer_reinit(b, msg, strlen(msg));
    852 	isc_buffer_add(b, strlen(msg));
    853 	*freecb = NULL;
    854 	*freecb_args = NULL;
    855 
    856 	return (ISC_R_SUCCESS);
    857 }
    858 
    859 #ifdef HAVE_ZLIB
    860 /*%<
    861  * Reallocates compbuffer to size, does nothing if compbuffer is already
    862  * larger than size.
    863  *
    864  * Requires:
    865  *\li	httpd a valid isc_httpd_t object
    866  *
    867  * Returns:
    868  *\li	#ISC_R_SUCCESS		-- all is well.
    869  *\li	#ISC_R_NOMEMORY		-- not enough memory to extend buffer
    870  */
    871 static isc_result_t
    872 alloc_compspace(isc_httpd_t *httpd, unsigned int size) {
    873 	char *newspace;
    874 	isc_region_t r;
    875 
    876 	isc_buffer_region(&httpd->compbuffer, &r);
    877 	if (size < r.length) {
    878 		return (ISC_R_SUCCESS);
    879 	}
    880 
    881 	newspace = isc_mem_get(httpd->mgr->mctx, size);
    882 	isc_buffer_reinit(&httpd->compbuffer, newspace, size);
    883 
    884 	if (r.base != NULL) {
    885 		isc_mem_put(httpd->mgr->mctx, r.base, r.length);
    886 	}
    887 
    888 	return (ISC_R_SUCCESS);
    889 }
    890 
    891 /*%<
    892  * Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it
    893  * if necessary.
    894  *
    895  * Requires:
    896  *\li	httpd a valid isc_httpd_t object
    897  *
    898  * Returns:
    899  *\li	#ISC_R_SUCCESS	  -- all is well.
    900  *\li	#ISC_R_NOMEMORY	  -- not enough memory to compress data
    901  *\li	#ISC_R_FAILURE	  -- error during compression or compressed
    902  *			     data would be larger than input data
    903  */
    904 static isc_result_t
    905 isc_httpd_compress(isc_httpd_t *httpd) {
    906 	z_stream zstr;
    907 	isc_region_t r;
    908 	isc_result_t result;
    909 	int ret;
    910 	int inputlen;
    911 
    912 	inputlen = isc_buffer_usedlength(&httpd->bodybuffer);
    913 	result = alloc_compspace(httpd, inputlen);
    914 	if (result != ISC_R_SUCCESS) {
    915 		return (result);
    916 	}
    917 	isc_buffer_region(&httpd->compbuffer, &r);
    918 
    919 	/*
    920 	 * We're setting output buffer size to input size so it fails if the
    921 	 * compressed data size would be bigger than the input size.
    922 	 */
    923 	memset(&zstr, 0, sizeof(zstr));
    924 	zstr.total_in = zstr.avail_in = zstr.total_out = zstr.avail_out =
    925 		inputlen;
    926 
    927 	zstr.next_in = isc_buffer_base(&httpd->bodybuffer);
    928 	zstr.next_out = r.base;
    929 
    930 	ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION);
    931 	if (ret == Z_OK) {
    932 		ret = deflate(&zstr, Z_FINISH);
    933 	}
    934 	deflateEnd(&zstr);
    935 	if (ret == Z_STREAM_END) {
    936 		isc_buffer_add(&httpd->compbuffer, inputlen - zstr.avail_out);
    937 		return (ISC_R_SUCCESS);
    938 	} else {
    939 		return (ISC_R_FAILURE);
    940 	}
    941 }
    942 #endif /* ifdef HAVE_ZLIB */
    943 
    944 static void
    945 isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) {
    946 	isc_result_t result;
    947 	isc_httpd_t *httpd = ev->ev_arg;
    948 	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
    949 	isc_buffer_t *databuffer;
    950 	isc_httpdurl_t *url;
    951 	isc_time_t now;
    952 	isc_region_t r;
    953 	bool is_compressed = false;
    954 	char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
    955 
    956 	REQUIRE(VALID_HTTPD(httpd));
    957 
    958 	INSIST(ISC_HTTPD_ISRECV(httpd));
    959 
    960 	if (sev->result != ISC_R_SUCCESS) {
    961 		goto out;
    962 	}
    963 
    964 	result = process_request(httpd, sev->n);
    965 	if (result == ISC_R_NOTFOUND) {
    966 		if (httpd->recvlen >= HTTP_RECVLEN - 1) {
    967 			goto out;
    968 		}
    969 		r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
    970 		r.length = HTTP_RECVLEN - httpd->recvlen - 1;
    971 
    972 		httpd_socket_recv(httpd, &r, task);
    973 		goto out;
    974 	} else if (result != ISC_R_SUCCESS) {
    975 		goto out;
    976 	}
    977 
    978 	ISC_HTTPD_SETSEND(httpd);
    979 
    980 	/*
    981 	 * XXXMLG Call function here.  Provide an add-header function
    982 	 * which will append the common headers to a response we generate.
    983 	 */
    984 	isc_buffer_initnull(&httpd->bodybuffer);
    985 	isc_time_now(&now);
    986 	isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
    987 	LOCK(&httpd->mgr->lock);
    988 	url = ISC_LIST_HEAD(httpd->mgr->urls);
    989 	while (url != NULL) {
    990 		if (strcmp(httpd->url, url->url) == 0) {
    991 			break;
    992 		}
    993 		url = ISC_LIST_NEXT(url, link);
    994 	}
    995 	UNLOCK(&httpd->mgr->lock);
    996 
    997 	if (url == NULL) {
    998 		result = httpd->mgr->render_404(
    999 			httpd->url, NULL, httpd->querystring, NULL, NULL,
   1000 			&httpd->retcode, &httpd->retmsg, &httpd->mimetype,
   1001 			&httpd->bodybuffer, &httpd->freecb, &httpd->freecb_arg);
   1002 	} else {
   1003 		result = url->action(httpd->url, url, httpd->querystring,
   1004 				     httpd->headers, url->action_arg,
   1005 				     &httpd->retcode, &httpd->retmsg,
   1006 				     &httpd->mimetype, &httpd->bodybuffer,
   1007 				     &httpd->freecb, &httpd->freecb_arg);
   1008 	}
   1009 	if (result != ISC_R_SUCCESS) {
   1010 		result = httpd->mgr->render_500(
   1011 			httpd->url, url, httpd->querystring, NULL, NULL,
   1012 			&httpd->retcode, &httpd->retmsg, &httpd->mimetype,
   1013 			&httpd->bodybuffer, &httpd->freecb, &httpd->freecb_arg);
   1014 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1015 	}
   1016 
   1017 #ifdef HAVE_ZLIB
   1018 	if ((httpd->flags & HTTPD_ACCEPT_DEFLATE) != 0) {
   1019 		result = isc_httpd_compress(httpd);
   1020 		if (result == ISC_R_SUCCESS) {
   1021 			is_compressed = true;
   1022 		}
   1023 	}
   1024 #endif /* ifdef HAVE_ZLIB */
   1025 
   1026 	isc_httpd_response(httpd);
   1027 	if ((httpd->flags & HTTPD_KEEPALIVE) != 0) {
   1028 		isc_httpd_addheader(httpd, "Connection", "Keep-Alive");
   1029 	}
   1030 	isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
   1031 	isc_httpd_addheader(httpd, "Date", datebuf);
   1032 	isc_httpd_addheader(httpd, "Expires", datebuf);
   1033 
   1034 	if (url != NULL && url->isstatic) {
   1035 		char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
   1036 		isc_time_formathttptimestamp(&url->loadtime, loadbuf,
   1037 					     sizeof(loadbuf));
   1038 		isc_httpd_addheader(httpd, "Last-Modified", loadbuf);
   1039 		isc_httpd_addheader(httpd, "Cache-Control: public", NULL);
   1040 	} else {
   1041 		isc_httpd_addheader(httpd, "Last-Modified", datebuf);
   1042 		isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
   1043 		isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
   1044 	}
   1045 
   1046 	isc_httpd_addheader(httpd, "Server: libisc", NULL);
   1047 
   1048 	if (is_compressed) {
   1049 		isc_httpd_addheader(httpd, "Content-Encoding", "deflate");
   1050 		isc_httpd_addheaderuint(
   1051 			httpd, "Content-Length",
   1052 			isc_buffer_usedlength(&httpd->compbuffer));
   1053 	} else {
   1054 		isc_httpd_addheaderuint(
   1055 			httpd, "Content-Length",
   1056 			isc_buffer_usedlength(&httpd->bodybuffer));
   1057 	}
   1058 
   1059 	isc_httpd_endheaders(httpd); /* done */
   1060 
   1061 	/*
   1062 	 * Append either the compressed or the non-compressed response body to
   1063 	 * the response headers and store the result in httpd->sendbuffer.
   1064 	 */
   1065 	isc_buffer_dup(httpd->mgr->mctx, &httpd->sendbuffer,
   1066 		       &httpd->headerbuffer);
   1067 	isc_buffer_setautorealloc(httpd->sendbuffer, true);
   1068 	databuffer = (is_compressed ? &httpd->compbuffer : &httpd->bodybuffer);
   1069 	isc_buffer_usedregion(databuffer, &r);
   1070 	result = isc_buffer_copyregion(httpd->sendbuffer, &r);
   1071 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1072 	/*
   1073 	 * Determine total response size.
   1074 	 */
   1075 	isc_buffer_usedregion(httpd->sendbuffer, &r);
   1076 
   1077 	httpd_socket_send(httpd, &r, task);
   1078 
   1079 out:
   1080 	maybe_destroy_httpd(httpd);
   1081 	isc_event_free(&ev);
   1082 }
   1083 
   1084 void
   1085 isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
   1086 	isc_httpdmgr_t *httpdmgr;
   1087 	isc_httpd_t *httpd;
   1088 
   1089 	REQUIRE(httpdmgrp != NULL);
   1090 	httpdmgr = *httpdmgrp;
   1091 	*httpdmgrp = NULL;
   1092 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
   1093 
   1094 	LOCK(&httpdmgr->lock);
   1095 
   1096 	MSETSHUTTINGDOWN(httpdmgr);
   1097 
   1098 	isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
   1099 
   1100 	httpd = ISC_LIST_HEAD(httpdmgr->running);
   1101 	while (httpd != NULL) {
   1102 		isc_socket_cancel(httpd->sock, httpdmgr->task,
   1103 				  ISC_SOCKCANCEL_ALL);
   1104 		httpd = ISC_LIST_NEXT(httpd, link);
   1105 	}
   1106 
   1107 	UNLOCK(&httpdmgr->lock);
   1108 
   1109 	maybe_destroy_httpdmgr(httpdmgr);
   1110 }
   1111 
   1112 static isc_result_t
   1113 grow_headerspace(isc_httpd_t *httpd) {
   1114 	char *newspace;
   1115 	unsigned int newlen;
   1116 	isc_region_t r;
   1117 
   1118 	isc_buffer_region(&httpd->headerbuffer, &r);
   1119 	newlen = r.length + HTTP_SENDGROW;
   1120 	if (newlen > HTTP_SEND_MAXLEN) {
   1121 		return (ISC_R_NOSPACE);
   1122 	}
   1123 
   1124 	newspace = isc_mem_get(httpd->mgr->mctx, newlen);
   1125 
   1126 	isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
   1127 
   1128 	isc_mem_put(httpd->mgr->mctx, r.base, r.length);
   1129 
   1130 	return (ISC_R_SUCCESS);
   1131 }
   1132 
   1133 isc_result_t
   1134 isc_httpd_response(isc_httpd_t *httpd) {
   1135 	isc_result_t result;
   1136 	unsigned int needlen;
   1137 
   1138 	REQUIRE(VALID_HTTPD(httpd));
   1139 
   1140 	needlen = strlen(httpd->protocol) + 1; /* protocol + space */
   1141 	needlen += 3 + 1; /* room for response code, always 3 bytes */
   1142 	needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
   1143 
   1144 	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
   1145 		result = grow_headerspace(httpd);
   1146 		if (result != ISC_R_SUCCESS) {
   1147 			return (result);
   1148 		}
   1149 	}
   1150 
   1151 	return (isc_buffer_printf(&httpd->headerbuffer, "%s %03u %s\r\n",
   1152 				  httpd->protocol, httpd->retcode,
   1153 				  httpd->retmsg));
   1154 }
   1155 
   1156 isc_result_t
   1157 isc_httpd_addheader(isc_httpd_t *httpd, const char *name, const char *val) {
   1158 	isc_result_t result;
   1159 	unsigned int needlen;
   1160 
   1161 	REQUIRE(VALID_HTTPD(httpd));
   1162 
   1163 	needlen = strlen(name); /* name itself */
   1164 	if (val != NULL) {
   1165 		needlen += 2 + strlen(val); /* :<space> and val */
   1166 	}
   1167 	needlen += 2; /* CRLF */
   1168 
   1169 	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
   1170 		result = grow_headerspace(httpd);
   1171 		if (result != ISC_R_SUCCESS) {
   1172 			return (result);
   1173 		}
   1174 	}
   1175 
   1176 	if (val != NULL) {
   1177 		return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n",
   1178 					  name, val));
   1179 	} else {
   1180 		return (isc_buffer_printf(&httpd->headerbuffer, "%s\r\n",
   1181 					  name));
   1182 	}
   1183 }
   1184 
   1185 isc_result_t
   1186 isc_httpd_endheaders(isc_httpd_t *httpd) {
   1187 	isc_result_t result;
   1188 
   1189 	REQUIRE(VALID_HTTPD(httpd));
   1190 
   1191 	while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
   1192 		result = grow_headerspace(httpd);
   1193 		if (result != ISC_R_SUCCESS) {
   1194 			return (result);
   1195 		}
   1196 	}
   1197 
   1198 	return (isc_buffer_printf(&httpd->headerbuffer, "\r\n"));
   1199 }
   1200 
   1201 isc_result_t
   1202 isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
   1203 	isc_result_t result;
   1204 	unsigned int needlen;
   1205 	char buf[sizeof "18446744073709551616"];
   1206 
   1207 	REQUIRE(VALID_HTTPD(httpd));
   1208 
   1209 	snprintf(buf, sizeof(buf), "%d", val);
   1210 
   1211 	needlen = strlen(name);	    /* name itself */
   1212 	needlen += 2 + strlen(buf); /* :<space> and val */
   1213 	needlen += 2;		    /* CRLF */
   1214 
   1215 	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
   1216 		result = grow_headerspace(httpd);
   1217 		if (result != ISC_R_SUCCESS) {
   1218 			return (result);
   1219 		}
   1220 	}
   1221 
   1222 	return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n", name,
   1223 				  buf));
   1224 }
   1225 
   1226 static void
   1227 isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) {
   1228 	isc_httpd_t *httpd = ev->ev_arg;
   1229 	isc_region_t r;
   1230 	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
   1231 
   1232 	REQUIRE(VALID_HTTPD(httpd));
   1233 
   1234 	INSIST(ISC_HTTPD_ISSEND(httpd));
   1235 
   1236 	isc_buffer_free(&httpd->sendbuffer);
   1237 
   1238 	/*
   1239 	 * We will always want to clean up our receive buffer, even if we
   1240 	 * got an error on send or we are shutting down.
   1241 	 *
   1242 	 * We will pass in the buffer only if there is data in it.  If
   1243 	 * there is no data, we will pass in a NULL.
   1244 	 */
   1245 	if (httpd->freecb != NULL) {
   1246 		isc_buffer_t *b = NULL;
   1247 		if (isc_buffer_length(&httpd->bodybuffer) > 0) {
   1248 			b = &httpd->bodybuffer;
   1249 			httpd->freecb(b, httpd->freecb_arg);
   1250 		}
   1251 	}
   1252 
   1253 	if (sev->result != ISC_R_SUCCESS) {
   1254 		goto out;
   1255 	}
   1256 
   1257 	if ((httpd->flags & HTTPD_CLOSE) != 0) {
   1258 		goto out;
   1259 	}
   1260 
   1261 	ISC_HTTPD_SETRECV(httpd);
   1262 
   1263 	reset_client(httpd);
   1264 
   1265 	r.base = (unsigned char *)httpd->recvbuf;
   1266 	r.length = HTTP_RECVLEN - 1;
   1267 
   1268 	httpd_socket_recv(httpd, &r, task);
   1269 
   1270 out:
   1271 	maybe_destroy_httpd(httpd);
   1272 	isc_event_free(&ev);
   1273 }
   1274 
   1275 static void
   1276 reset_client(isc_httpd_t *httpd) {
   1277 	/*
   1278 	 * Catch errors here.  We MUST be in RECV mode, and we MUST NOT have
   1279 	 * any outstanding buffers.  If we have buffers, we have a leak.
   1280 	 */
   1281 	INSIST(ISC_HTTPD_ISRECV(httpd));
   1282 	INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
   1283 	INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
   1284 
   1285 	httpd->recvbuf[0] = 0;
   1286 	httpd->recvlen = 0;
   1287 	httpd->headers = NULL;
   1288 	httpd->method = ISC_HTTPD_METHODUNKNOWN;
   1289 	httpd->url = NULL;
   1290 	httpd->querystring = NULL;
   1291 	httpd->protocol = NULL;
   1292 	httpd->flags = 0;
   1293 
   1294 	isc_buffer_clear(&httpd->headerbuffer);
   1295 	isc_buffer_clear(&httpd->compbuffer);
   1296 	isc_buffer_invalidate(&httpd->bodybuffer);
   1297 }
   1298 
   1299 isc_result_t
   1300 isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
   1301 		    isc_httpdaction_t *func, void *arg) {
   1302 	/* REQUIRE(VALID_HTTPDMGR(httpdmgr)); Dummy function */
   1303 
   1304 	return (isc_httpdmgr_addurl2(httpdmgr, url, false, func, arg));
   1305 }
   1306 
   1307 isc_result_t
   1308 isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url, bool isstatic,
   1309 		     isc_httpdaction_t *func, void *arg) {
   1310 	isc_httpdurl_t *item;
   1311 
   1312 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
   1313 
   1314 	if (url == NULL) {
   1315 		httpdmgr->render_404 = func;
   1316 		return (ISC_R_SUCCESS);
   1317 	}
   1318 
   1319 	item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
   1320 
   1321 	item->url = isc_mem_strdup(httpdmgr->mctx, url);
   1322 
   1323 	item->action = func;
   1324 	item->action_arg = arg;
   1325 	item->isstatic = isstatic;
   1326 	isc_time_now(&item->loadtime);
   1327 
   1328 	ISC_LINK_INIT(item, link);
   1329 
   1330 	LOCK(&httpdmgr->lock);
   1331 	ISC_LIST_APPEND(httpdmgr->urls, item, link);
   1332 	UNLOCK(&httpdmgr->lock);
   1333 
   1334 	return (ISC_R_SUCCESS);
   1335 }
   1336 
   1337 void
   1338 isc_httpd_setfinishhook(void (*fn)(void)) {
   1339 #if ENABLE_AFL
   1340 	finishhook = fn;
   1341 #else  /* ENABLE_AFL */
   1342 	UNUSED(fn);
   1343 #endif /* ENABLE_AFL */
   1344 }
   1345