Home | History | Annotate | Line # | Download | only in isc
httpd.c revision 1.1.1.7
      1 /*	$NetBSD: httpd.c,v 1.1.1.7 2023/01/25 20:36:48 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 	isc_buffer_initnull(buffer);
    255 }
    256 
    257 static void
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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