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