Home | History | Annotate | Line # | Download | only in isc
httpd.c revision 1.10.2.1
      1  1.10.2.1    martin /*	$NetBSD: httpd.c,v 1.10.2.1 2026/05/07 16:18:49 martin Exp $	*/
      2       1.1  christos 
      3       1.1  christos /*
      4       1.1  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5       1.1  christos  *
      6       1.7  christos  * SPDX-License-Identifier: MPL-2.0
      7       1.7  christos  *
      8       1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      9       1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10       1.6  christos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11       1.1  christos  *
     12       1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     13       1.1  christos  * information regarding copyright ownership.
     14       1.1  christos  */
     15       1.1  christos 
     16       1.1  christos /*! \file */
     17       1.1  christos 
     18       1.9  christos #include <ctype.h>
     19       1.3  christos #include <inttypes.h>
     20       1.3  christos #include <stdbool.h>
     21       1.3  christos #include <string.h>
     22       1.3  christos 
     23       1.1  christos #include <isc/buffer.h>
     24       1.1  christos #include <isc/httpd.h>
     25      1.10  christos #include <isc/list.h>
     26       1.1  christos #include <isc/mem.h>
     27       1.9  christos #include <isc/netmgr.h>
     28       1.4  christos #include <isc/refcount.h>
     29       1.9  christos #include <isc/sockaddr.h>
     30       1.1  christos #include <isc/string.h>
     31       1.1  christos #include <isc/time.h>
     32       1.9  christos #include <isc/url.h>
     33       1.1  christos #include <isc/util.h>
     34       1.1  christos 
     35       1.9  christos #include "netmgr/netmgr-int.h"
     36       1.9  christos #include "picohttpparser.h"
     37       1.9  christos 
     38       1.1  christos #ifdef HAVE_ZLIB
     39       1.1  christos #include <zlib.h>
     40       1.4  christos #endif /* ifdef HAVE_ZLIB */
     41       1.1  christos 
     42       1.9  christos /*
     43       1.9  christos  * Size the recv buffer to hold at maximum two full buffers from isc_nm_read(),
     44       1.9  christos  * so we don't have to handle the truncation.
     45       1.1  christos  */
     46       1.9  christos #define HTTP_RECVLEN	     ISC_NETMGR_TCP_RECVBUF_SIZE * 2
     47       1.9  christos #define HTTP_SENDLEN	     ISC_NETMGR_TCP_RECVBUF_SIZE
     48       1.9  christos #define HTTP_HEADERS_NUM     100
     49       1.9  christos #define HTTP_MAX_REQUEST_LEN 4096
     50       1.9  christos 
     51       1.9  christos typedef enum httpd_flags {
     52       1.9  christos 	CONNECTION_CLOSE = 1 << 0,	/* connection must close */
     53       1.9  christos 	CONNECTION_KEEP_ALIVE = 1 << 1, /* response needs a keep-alive header */
     54       1.9  christos 	ACCEPT_DEFLATE = 1 << 2,	/* response can be compressed */
     55       1.9  christos } httpd_flags_t;
     56       1.4  christos 
     57       1.4  christos #define HTTPD_MAGIC    ISC_MAGIC('H', 't', 'p', 'd')
     58       1.4  christos #define VALID_HTTPD(m) ISC_MAGIC_VALID(m, HTTPD_MAGIC)
     59       1.4  christos 
     60       1.4  christos #define HTTPDMGR_MAGIC	  ISC_MAGIC('H', 'p', 'd', 'm')
     61       1.4  christos #define VALID_HTTPDMGR(m) ISC_MAGIC_VALID(m, HTTPDMGR_MAGIC)
     62       1.1  christos 
     63       1.9  christos /*%
     64       1.9  christos  * HTTP methods.
     65       1.9  christos  */
     66       1.9  christos typedef enum { METHOD_UNKNOWN = 0, METHOD_GET = 1, METHOD_POST = 2 } method_t;
     67       1.9  christos 
     68       1.9  christos /*%
     69       1.9  christos  * HTTP urls.  These are the URLs we manage, and the function to call to
     70       1.9  christos  * provide the data for it.  We pass in the base url (so the same function
     71       1.9  christos  * can handle multiple requests), and a structure to fill in to return a
     72       1.9  christos  * result to the client.  We also pass in a pointer to be filled in for
     73       1.9  christos  * the data cleanup function.
     74       1.9  christos  */
     75       1.9  christos struct isc_httpdurl {
     76       1.9  christos 	char *url;
     77       1.9  christos 	isc_httpdaction_t *action;
     78       1.9  christos 	void *action_arg;
     79       1.9  christos 	bool isstatic;
     80       1.9  christos 	isc_time_t loadtime;
     81       1.9  christos 	ISC_LINK(isc_httpdurl_t) link;
     82       1.9  christos };
     83       1.9  christos 
     84       1.1  christos /*% http client */
     85       1.1  christos struct isc_httpd {
     86       1.4  christos 	unsigned int magic; /* HTTPD_MAGIC */
     87      1.10  christos 	isc_refcount_t references;
     88       1.9  christos 
     89       1.4  christos 	isc_httpdmgr_t *mgr; /*%< our parent */
     90       1.4  christos 	ISC_LINK(isc_httpd_t) link;
     91       1.9  christos 
     92      1.10  christos 	isc_nmhandle_t *handle; /* Permanent pointer to handle */
     93       1.1  christos 
     94       1.1  christos 	/*%
     95       1.1  christos 	 * Received data state.
     96       1.1  christos 	 */
     97       1.4  christos 	char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
     98       1.9  christos 	size_t recvlen;		    /*%< length recv'd */
     99       1.9  christos 	size_t consume;		    /*%< length of last command */
    100       1.1  christos 
    101       1.9  christos 	method_t method;
    102       1.9  christos 	int minor_version;
    103       1.9  christos 	httpd_flags_t flags;
    104       1.9  christos 	const char *path;
    105       1.9  christos 	isc_url_parser_t up;
    106       1.9  christos 	isc_time_t if_modified_since;
    107       1.1  christos };
    108       1.1  christos 
    109      1.10  christos #if ISC_HTTPD_TRACE
    110      1.10  christos #define isc_httpd_ref(ptr)   isc_httpd__ref(ptr, __func__, __FILE__, __LINE__)
    111      1.10  christos #define isc_httpd_unref(ptr) isc_httpd__unref(ptr, __func__, __FILE__, __LINE__)
    112      1.10  christos #define isc_httpd_attach(ptr, ptrp) \
    113      1.10  christos 	isc_httpd__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
    114      1.10  christos #define isc_httpd_detach(ptrp) \
    115      1.10  christos 	isc_httpd__detach(ptrp, __func__, __FILE__, __LINE__)
    116      1.10  christos ISC_REFCOUNT_TRACE_DECL(isc_httpd);
    117      1.10  christos #else
    118      1.10  christos ISC_REFCOUNT_DECL(isc_httpd);
    119      1.10  christos #endif
    120      1.10  christos 
    121       1.1  christos struct isc_httpdmgr {
    122       1.4  christos 	unsigned int magic; /* HTTPDMGR_MAGIC */
    123       1.4  christos 	isc_refcount_t references;
    124       1.4  christos 	isc_mem_t *mctx;
    125       1.9  christos 	isc_nmsocket_t *sock;
    126       1.4  christos 
    127       1.4  christos 	isc_httpdclientok_t *client_ok;	 /*%< client validator */
    128       1.4  christos 	isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
    129       1.4  christos 	void *cb_arg;			 /*%< argument for the above */
    130       1.4  christos 
    131       1.4  christos 	unsigned int flags;
    132       1.4  christos 	ISC_LIST(isc_httpd_t) running; /*%< running clients */
    133       1.4  christos 
    134       1.4  christos 	isc_mutex_t lock;
    135       1.4  christos 
    136       1.4  christos 	ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
    137       1.4  christos 	isc_httpdaction_t *render_404;
    138       1.4  christos 	isc_httpdaction_t *render_500;
    139       1.1  christos };
    140       1.1  christos 
    141      1.10  christos #if ISC_HTTPD_TRACE
    142      1.10  christos #define isc_httpdmgr_ref(ptr) \
    143      1.10  christos 	isc_httpdmgr__ref(ptr, __func__, __FILE__, __LINE__)
    144      1.10  christos #define isc_httpdmgr_unref(ptr) \
    145      1.10  christos 	isc_httpdmgr__unref(ptr, __func__, __FILE__, __LINE__)
    146      1.10  christos #define isc_httpdmgr_attach(ptr, ptrp) \
    147      1.10  christos 	isc_httpdmgr__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
    148      1.10  christos #define isc_httpdmgr_detach(ptrp) \
    149      1.10  christos 	isc_httpdmgr__detach(ptrp, __func__, __FILE__, __LINE__)
    150      1.10  christos ISC_REFCOUNT_TRACE_DECL(isc_httpdmgr);
    151      1.10  christos #else
    152      1.10  christos ISC_REFCOUNT_DECL(isc_httpdmgr);
    153      1.10  christos #endif
    154      1.10  christos 
    155       1.9  christos typedef struct isc_httpd_sendreq {
    156       1.9  christos 	isc_mem_t *mctx;
    157       1.9  christos 	isc_httpd_t *httpd;
    158       1.1  christos 
    159       1.9  christos 	/*%
    160       1.9  christos 	 * Transmit data state.
    161       1.9  christos 	 *
    162       1.9  christos 	 * This is the data buffer we will transmit.
    163       1.9  christos 	 *
    164       1.9  christos 	 * This free function pointer is filled in by the rendering function
    165       1.9  christos 	 * we call.  The free function is called after the data is transmitted
    166       1.9  christos 	 * to the client.
    167       1.9  christos 	 *
    168       1.9  christos 	 * We currently use three buffers total:
    169       1.9  christos 	 *
    170       1.9  christos 	 * sendbuffer - gets filled as we gather the data
    171       1.9  christos 	 *
    172       1.9  christos 	 * bodybuffer - for the client to fill in (which it manages, it provides
    173       1.9  christos 	 * the space for it, etc) -- we will pass that buffer structure back to
    174       1.9  christos 	 * the caller, who is responsible for managing the space it may have
    175       1.9  christos 	 * allocated as backing store for it.  we only allocate the buffer
    176       1.9  christos 	 * itself, not the backing store.
    177       1.9  christos 	 *
    178       1.9  christos 	 * compbuffer - managed by us, that contains the compressed HTTP data,
    179       1.9  christos 	 * if compression is used.
    180       1.9  christos 	 */
    181       1.9  christos 	isc_buffer_t *sendbuffer;
    182       1.9  christos 	isc_buffer_t *compbuffer;
    183       1.1  christos 
    184       1.9  christos 	isc_buffer_t bodybuffer;
    185       1.4  christos 
    186       1.9  christos 	const char *mimetype;
    187       1.9  christos 	unsigned int retcode;
    188       1.9  christos 	const char *retmsg;
    189       1.9  christos 	isc_httpdfree_t *freecb;
    190       1.9  christos 	void *freecb_arg;
    191       1.1  christos 
    192       1.9  christos } isc_httpd_sendreq_t;
    193       1.1  christos 
    194       1.9  christos static isc_result_t
    195       1.9  christos httpd_newconn(isc_nmhandle_t *, isc_result_t, void *);
    196       1.1  christos static void
    197       1.9  christos httpd_request(isc_nmhandle_t *, isc_result_t, isc_region_t *, void *);
    198       1.4  christos static void
    199       1.9  christos httpd_senddone(isc_nmhandle_t *, isc_result_t, void *);
    200       1.4  christos static void
    201      1.10  christos httpd_free(isc_httpd_t *httpd);
    202       1.4  christos 
    203       1.4  christos static void
    204       1.9  christos httpd_addheader(isc_httpd_sendreq_t *, const char *, const char *);
    205       1.4  christos static void
    206       1.9  christos httpd_addheaderuint(isc_httpd_sendreq_t *, const char *, int);
    207       1.4  christos static void
    208       1.9  christos httpd_endheaders(isc_httpd_sendreq_t *);
    209       1.7  christos static void
    210       1.9  christos httpd_response(isc_httpd_t *, isc_httpd_sendreq_t *);
    211       1.1  christos 
    212       1.9  christos static isc_result_t
    213       1.9  christos process_request(isc_httpd_t *, size_t);
    214       1.8  christos 
    215       1.9  christos static isc_httpdaction_t render_404;
    216       1.9  christos static isc_httpdaction_t render_500;
    217       1.4  christos 
    218       1.4  christos #if ENABLE_AFL
    219       1.9  christos static void (*finishhook)(void) = NULL;
    220       1.4  christos #endif /* ENABLE_AFL */
    221       1.4  christos 
    222       1.1  christos isc_result_t
    223       1.9  christos isc_httpdmgr_create(isc_nm_t *nm, isc_mem_t *mctx, isc_sockaddr_t *addr,
    224       1.1  christos 		    isc_httpdclientok_t *client_ok,
    225       1.1  christos 		    isc_httpdondestroy_t *ondestroy, void *cb_arg,
    226       1.9  christos 		    isc_httpdmgr_t **httpdmgrp) {
    227       1.1  christos 	isc_result_t result;
    228       1.9  christos 	isc_httpdmgr_t *httpdmgr = NULL;
    229       1.1  christos 
    230       1.9  christos 	REQUIRE(nm != NULL);
    231       1.1  christos 	REQUIRE(mctx != NULL);
    232       1.1  christos 	REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL);
    233       1.1  christos 
    234       1.1  christos 	httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
    235       1.9  christos 	*httpdmgr = (isc_httpdmgr_t){ .client_ok = client_ok,
    236       1.4  christos 				      .ondestroy = ondestroy,
    237       1.4  christos 				      .cb_arg = cb_arg,
    238       1.4  christos 				      .render_404 = render_404,
    239       1.4  christos 				      .render_500 = render_500 };
    240       1.1  christos 
    241       1.3  christos 	isc_mutex_init(&httpdmgr->lock);
    242       1.1  christos 	isc_mem_attach(mctx, &httpdmgr->mctx);
    243       1.1  christos 
    244       1.1  christos 	ISC_LIST_INIT(httpdmgr->running);
    245       1.1  christos 	ISC_LIST_INIT(httpdmgr->urls);
    246       1.1  christos 
    247       1.4  christos 	isc_refcount_init(&httpdmgr->references, 1);
    248       1.4  christos 
    249      1.10  christos 	CHECK(isc_nm_listentcp(nm, ISC_NM_LISTEN_ONE, addr, httpd_newconn,
    250      1.10  christos 			       httpdmgr, 5, NULL, &httpdmgr->sock));
    251       1.1  christos 
    252       1.4  christos 	httpdmgr->magic = HTTPDMGR_MAGIC;
    253       1.9  christos 	*httpdmgrp = httpdmgr;
    254       1.4  christos 
    255      1.10  christos 	return ISC_R_SUCCESS;
    256       1.1  christos 
    257       1.4  christos cleanup:
    258       1.4  christos 	httpdmgr->magic = 0;
    259       1.6  christos 	isc_refcount_decrementz(&httpdmgr->references);
    260       1.4  christos 	isc_refcount_destroy(&httpdmgr->references);
    261       1.1  christos 	isc_mem_detach(&httpdmgr->mctx);
    262       1.3  christos 	isc_mutex_destroy(&httpdmgr->lock);
    263       1.1  christos 	isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
    264       1.9  christos 
    265      1.10  christos 	return result;
    266       1.4  christos }
    267       1.4  christos 
    268       1.4  christos static void
    269       1.4  christos destroy_httpdmgr(isc_httpdmgr_t *httpdmgr) {
    270       1.4  christos 	isc_refcount_destroy(&httpdmgr->references);
    271       1.1  christos 
    272       1.1  christos 	LOCK(&httpdmgr->lock);
    273       1.1  christos 
    274       1.9  christos 	REQUIRE((httpdmgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) != 0);
    275       1.9  christos 	REQUIRE(ISC_LIST_EMPTY(httpdmgr->running));
    276       1.9  christos 
    277       1.4  christos 	httpdmgr->magic = 0;
    278       1.1  christos 
    279       1.9  christos 	if (httpdmgr->sock != NULL) {
    280       1.9  christos 		isc_nmsocket_close(&httpdmgr->sock);
    281       1.9  christos 	}
    282       1.1  christos 
    283       1.1  christos 	/*
    284       1.1  christos 	 * Clear out the list of all actions we know about.  Just free the
    285       1.1  christos 	 * memory.
    286       1.1  christos 	 */
    287      1.10  christos 	isc_httpdurl_t *url, *next;
    288  1.10.2.1    martin 	ISC_LIST_FOREACH_SAFE(httpdmgr->urls, url, link, next) {
    289       1.1  christos 		isc_mem_free(httpdmgr->mctx, url->url);
    290       1.1  christos 		ISC_LIST_UNLINK(httpdmgr->urls, url, link);
    291       1.1  christos 		isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
    292       1.1  christos 	}
    293       1.1  christos 
    294       1.1  christos 	UNLOCK(&httpdmgr->lock);
    295       1.3  christos 	isc_mutex_destroy(&httpdmgr->lock);
    296       1.1  christos 
    297       1.4  christos 	if (httpdmgr->ondestroy != NULL) {
    298       1.1  christos 		(httpdmgr->ondestroy)(httpdmgr->cb_arg);
    299       1.4  christos 	}
    300       1.4  christos 	isc_mem_putanddetach(&httpdmgr->mctx, httpdmgr, sizeof(isc_httpdmgr_t));
    301       1.1  christos }
    302       1.1  christos 
    303      1.10  christos #if ISC_HTTPD_TRACE
    304      1.10  christos ISC_REFCOUNT_TRACE_IMPL(isc_httpdmgr, destroy_httpdmgr)
    305      1.10  christos #else
    306      1.10  christos ISC_REFCOUNT_IMPL(isc_httpdmgr, destroy_httpdmgr);
    307      1.10  christos #endif
    308      1.10  christos 
    309       1.9  christos static bool
    310       1.9  christos name_match(const struct phr_header *header, const char *match) {
    311       1.9  christos 	size_t match_len = strlen(match);
    312       1.9  christos 	if (match_len != header->name_len) {
    313      1.10  christos 		return false;
    314       1.9  christos 	}
    315      1.10  christos 	return strncasecmp(header->name, match, match_len) == 0;
    316       1.9  christos }
    317       1.1  christos 
    318       1.3  christos static bool
    319       1.9  christos value_match(const struct phr_header *header, const char *match) {
    320       1.9  christos 	size_t match_len = strlen(match);
    321       1.9  christos 	size_t limit;
    322       1.9  christos 
    323       1.9  christos 	if (match_len > header->value_len) {
    324      1.10  christos 		return false;
    325       1.1  christos 	}
    326       1.1  christos 
    327       1.9  christos 	limit = header->value_len - match_len + 1;
    328       1.1  christos 
    329       1.9  christos 	for (size_t i = 0; i < limit; i++) {
    330       1.9  christos 		if (isspace((unsigned char)header->value[i])) {
    331       1.9  christos 			while (i < limit &&
    332       1.9  christos 			       isspace((unsigned char)header->value[i]))
    333       1.9  christos 			{
    334       1.9  christos 				i++;
    335       1.4  christos 			}
    336       1.1  christos 			continue;
    337       1.1  christos 		}
    338       1.1  christos 
    339       1.9  christos 		if (strncasecmp(&header->value[i], match, match_len) == 0) {
    340       1.9  christos 			i += match_len;
    341       1.1  christos 			/*
    342       1.9  christos 			 * Sanity check; f.e. for 'deflate' match only
    343       1.9  christos 			 * 'deflate[,;]', but not 'deflateyou'
    344       1.1  christos 			 */
    345       1.9  christos 			if (i == header->value_len || header->value[i] == ',' ||
    346       1.9  christos 			    header->value[i] == ';')
    347       1.9  christos 			{
    348      1.10  christos 				return true;
    349       1.4  christos 			}
    350       1.1  christos 		}
    351       1.9  christos 
    352       1.9  christos 		while (i < limit && header->value[i] != ',') {
    353       1.9  christos 			i++;
    354       1.9  christos 		}
    355       1.1  christos 	}
    356      1.10  christos 	return false;
    357       1.1  christos }
    358       1.1  christos 
    359       1.1  christos static isc_result_t
    360       1.9  christos process_request(isc_httpd_t *httpd, size_t last_len) {
    361       1.9  christos 	int pret;
    362       1.9  christos 	const char *method = NULL;
    363       1.9  christos 	size_t method_len = 0;
    364       1.9  christos 	const char *path;
    365       1.9  christos 	size_t path_len = 0;
    366       1.9  christos 	struct phr_header headers[HTTP_HEADERS_NUM];
    367       1.9  christos 	size_t num_headers;
    368       1.9  christos 	isc_result_t result;
    369       1.1  christos 
    370       1.9  christos 	num_headers = ARRAY_SIZE(headers);
    371       1.1  christos 
    372       1.9  christos 	pret = phr_parse_request(httpd->recvbuf, httpd->recvlen, &method,
    373       1.9  christos 				 &method_len, &path, &path_len,
    374       1.9  christos 				 &httpd->minor_version, headers, &num_headers,
    375       1.9  christos 				 last_len);
    376       1.1  christos 
    377       1.9  christos 	if (pret == -1) {
    378       1.9  christos 		/* Parse Error */
    379      1.10  christos 		return ISC_R_UNEXPECTED;
    380       1.9  christos 	} else if (pret == -2) {
    381       1.9  christos 		/* Need more data */
    382      1.10  christos 		return ISC_R_NOMORE;
    383       1.1  christos 	}
    384       1.9  christos 
    385       1.9  christos 	INSIST(pret > 0);
    386       1.9  christos 
    387       1.9  christos 	if (pret > HTTP_MAX_REQUEST_LEN) {
    388      1.10  christos 		return ISC_R_RANGE;
    389       1.4  christos 	}
    390       1.1  christos 
    391       1.9  christos 	httpd->consume = pret;
    392       1.1  christos 
    393       1.1  christos 	/*
    394       1.1  christos 	 * Determine if this is a POST or GET method.  Any other values will
    395       1.1  christos 	 * cause an error to be returned.
    396       1.1  christos 	 */
    397       1.9  christos 	if (strncmp(method, "GET ", method_len) == 0) {
    398       1.9  christos 		httpd->method = METHOD_GET;
    399       1.9  christos 	} else if (strncmp(method, "POST ", method_len) == 0) {
    400       1.9  christos 		httpd->method = METHOD_POST;
    401       1.1  christos 	} else {
    402      1.10  christos 		return ISC_R_RANGE;
    403       1.1  christos 	}
    404       1.1  christos 
    405       1.1  christos 	/*
    406       1.9  christos 	 * Parse the URL
    407       1.1  christos 	 */
    408       1.9  christos 	result = isc_url_parse(path, path_len, 0, &httpd->up);
    409       1.9  christos 	if (result != ISC_R_SUCCESS) {
    410      1.10  christos 		return result;
    411       1.9  christos 	}
    412       1.9  christos 	httpd->path = path;
    413       1.1  christos 
    414       1.1  christos 	/*
    415       1.9  christos 	 * Examine headers that can affect this request's response
    416       1.1  christos 	 */
    417       1.9  christos 	httpd->flags = 0;
    418       1.9  christos 
    419       1.9  christos 	size_t content_len = 0;
    420       1.9  christos 	bool keep_alive = false;
    421       1.9  christos 	bool host_header = false;
    422       1.9  christos 
    423       1.9  christos 	isc_time_set(&httpd->if_modified_since, 0, 0);
    424       1.9  christos 
    425       1.9  christos 	for (size_t i = 0; i < num_headers; i++) {
    426       1.9  christos 		struct phr_header *header = &headers[i];
    427       1.9  christos 
    428       1.9  christos 		if (name_match(header, "Content-Length")) {
    429       1.9  christos 			char *endptr;
    430       1.9  christos 			errno = 0;
    431  1.10.2.1    martin 			long val = strtol(header->value, &endptr, 10);
    432       1.1  christos 
    433       1.9  christos 			/* ensure we consumed all digits */
    434       1.9  christos 			if ((header->value + header->value_len) != endptr) {
    435      1.10  christos 				return ISC_R_BADNUMBER;
    436       1.9  christos 			}
    437       1.9  christos 			/* ensure there was no minus sign */
    438       1.9  christos 			if (val < 0) {
    439      1.10  christos 				return ISC_R_BADNUMBER;
    440       1.9  christos 			}
    441       1.9  christos 			/* ensure it did not overflow */
    442       1.9  christos 			if (errno != 0) {
    443      1.10  christos 				return ISC_R_RANGE;
    444       1.9  christos 			}
    445       1.9  christos 			content_len = val;
    446       1.9  christos 		} else if (name_match(header, "Connection")) {
    447       1.9  christos 			/* multiple fields in a connection header are allowed */
    448       1.9  christos 			if (value_match(header, "close")) {
    449       1.9  christos 				httpd->flags |= CONNECTION_CLOSE;
    450       1.9  christos 			}
    451       1.9  christos 			if (value_match(header, "keep-alive")) {
    452       1.9  christos 				keep_alive = true;
    453       1.9  christos 			}
    454       1.9  christos 		} else if (name_match(header, "Host")) {
    455       1.9  christos 			host_header = true;
    456       1.9  christos 		} else if (name_match(header, "Accept-Encoding")) {
    457       1.9  christos 			if (value_match(header, "deflate")) {
    458       1.9  christos 				httpd->flags |= ACCEPT_DEFLATE;
    459       1.9  christos 			}
    460       1.9  christos 		} else if (name_match(header, "If-Modified-Since") &&
    461       1.9  christos 			   header->value_len < ISC_FORMATHTTPTIMESTAMP_SIZE)
    462       1.9  christos 		{
    463       1.9  christos 			char timestamp[ISC_FORMATHTTPTIMESTAMP_SIZE + 1];
    464       1.9  christos 			memmove(timestamp, header->value, header->value_len);
    465       1.9  christos 			timestamp[header->value_len] = 0;
    466       1.9  christos 
    467       1.9  christos 			/* Ignore the value if it can't be parsed */
    468       1.9  christos 			(void)isc_time_parsehttptimestamp(
    469       1.9  christos 				timestamp, &httpd->if_modified_since);
    470       1.1  christos 		}
    471       1.1  christos 	}
    472       1.1  christos 
    473       1.1  christos 	/*
    474       1.9  christos 	 * The Content-Length is optional in an HTTP request.
    475       1.9  christos 	 * For a GET the length must be zero.
    476       1.1  christos 	 */
    477       1.9  christos 	if (httpd->method == METHOD_GET && content_len != 0) {
    478      1.10  christos 		return ISC_R_BADNUMBER;
    479       1.1  christos 	}
    480       1.1  christos 
    481       1.9  christos 	if (content_len >= HTTP_MAX_REQUEST_LEN) {
    482      1.10  christos 		return ISC_R_RANGE;
    483       1.4  christos 	}
    484       1.1  christos 
    485       1.9  christos 	size_t consume = httpd->consume + content_len;
    486       1.9  christos 	if (consume > httpd->recvlen) {
    487       1.9  christos 		/* The request data isn't complete yet. */
    488      1.10  christos 		return ISC_R_NOMORE;
    489       1.4  christos 	}
    490       1.1  christos 
    491       1.9  christos 	/* Consume the request's data, which we do not use. */
    492       1.9  christos 	httpd->consume = consume;
    493       1.1  christos 
    494       1.9  christos 	switch (httpd->minor_version) {
    495       1.9  christos 	case 0:
    496       1.9  christos 		/*
    497       1.9  christos 		 * RFC 9112 section 9.3 says close takes priority if
    498       1.9  christos 		 * keep-alive is also present
    499       1.9  christos 		 */
    500       1.9  christos 		if ((httpd->flags & CONNECTION_CLOSE) == 0 && keep_alive) {
    501       1.9  christos 			httpd->flags |= CONNECTION_KEEP_ALIVE;
    502       1.4  christos 		} else {
    503       1.9  christos 			httpd->flags |= CONNECTION_CLOSE;
    504       1.9  christos 		}
    505       1.9  christos 		break;
    506       1.9  christos 	case 1:
    507       1.9  christos 		if (!host_header) {
    508      1.10  christos 			return ISC_R_RANGE;
    509       1.4  christos 		}
    510       1.9  christos 		break;
    511       1.9  christos 	default:
    512      1.10  christos 		return ISC_R_UNEXPECTED;
    513       1.1  christos 	}
    514       1.1  christos 
    515       1.1  christos 	/*
    516       1.9  christos 	 * Looks like a a valid request, so now we know we won't have
    517       1.9  christos 	 * to process this buffer again. We can NULL-terminate the
    518       1.9  christos 	 * URL for the caller's benefit, and set recvlen to 0 so
    519       1.9  christos 	 * the next read will overwrite this one instead of appending
    520       1.9  christos 	 * to the buffer.
    521       1.1  christos 	 */
    522       1.1  christos 
    523      1.10  christos 	return ISC_R_SUCCESS;
    524       1.4  christos }
    525       1.1  christos 
    526       1.4  christos static void
    527      1.10  christos httpd_free(isc_httpd_t *httpd) {
    528       1.9  christos 	isc_httpdmgr_t *httpdmgr = NULL;
    529       1.9  christos 
    530       1.9  christos 	REQUIRE(VALID_HTTPD(httpd));
    531       1.9  christos 
    532       1.9  christos 	httpdmgr = httpd->mgr;
    533       1.4  christos 
    534       1.4  christos 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
    535       1.4  christos 
    536       1.9  christos 	LOCK(&httpdmgr->lock);
    537       1.9  christos 	ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
    538       1.9  christos 	UNLOCK(&httpdmgr->lock);
    539       1.9  christos 
    540       1.9  christos 	httpd->recvbuf[0] = 0;
    541       1.9  christos 	httpd->recvlen = 0;
    542       1.9  christos 	httpd->consume = 0;
    543       1.9  christos 	httpd->method = METHOD_UNKNOWN;
    544       1.9  christos 	httpd->flags = 0;
    545       1.9  christos 
    546       1.9  christos 	httpd->minor_version = -1;
    547       1.9  christos 	httpd->path = NULL;
    548       1.9  christos 	httpd->up = (isc_url_parser_t){ 0 };
    549       1.9  christos 	isc_time_set(&httpd->if_modified_since, 0, 0);
    550      1.10  christos 
    551      1.10  christos 	httpd->magic = 0;
    552      1.10  christos 	httpd->mgr = NULL;
    553      1.10  christos 
    554      1.10  christos 	isc_mem_put(httpdmgr->mctx, httpd, sizeof(*httpd));
    555      1.10  christos 
    556      1.10  christos 	isc_httpdmgr_detach(&httpdmgr);
    557      1.10  christos 
    558      1.10  christos #if ENABLE_AFL
    559      1.10  christos 	if (finishhook != NULL) {
    560      1.10  christos 		finishhook();
    561      1.10  christos 	}
    562      1.10  christos #endif /* ENABLE_AFL */
    563       1.9  christos }
    564       1.9  christos 
    565      1.10  christos #if ISC_HTTPD_TRACE
    566      1.10  christos ISC_REFCOUNT_TRACE_IMPL(isc_httpd, httpd_free)
    567      1.10  christos #else
    568      1.10  christos ISC_REFCOUNT_IMPL(isc_httpd, httpd_free);
    569      1.10  christos #endif
    570      1.10  christos 
    571       1.9  christos static void
    572       1.9  christos isc__httpd_sendreq_free(isc_httpd_sendreq_t *req) {
    573       1.9  christos 	/* Clean up buffers */
    574       1.9  christos 
    575       1.9  christos 	isc_buffer_free(&req->sendbuffer);
    576       1.9  christos 
    577       1.9  christos 	isc_mem_putanddetach(&req->mctx, req, sizeof(*req));
    578       1.9  christos }
    579       1.9  christos 
    580       1.9  christos static isc_httpd_sendreq_t *
    581       1.9  christos isc__httpd_sendreq_new(isc_httpd_t *httpd) {
    582       1.9  christos 	isc_httpdmgr_t *httpdmgr = httpd->mgr;
    583       1.9  christos 	isc_httpd_sendreq_t *req;
    584       1.4  christos 
    585       1.9  christos 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
    586       1.4  christos 
    587       1.9  christos 	req = isc_mem_get(httpdmgr->mctx, sizeof(*req));
    588       1.9  christos 	*req = (isc_httpd_sendreq_t){ 0 };
    589       1.4  christos 
    590       1.9  christos 	isc_mem_attach(httpdmgr->mctx, &req->mctx);
    591       1.4  christos 
    592       1.4  christos 	/*
    593       1.4  christos 	 * Initialize the buffer for our headers.
    594       1.4  christos 	 */
    595       1.9  christos 	isc_buffer_allocate(req->mctx, &req->sendbuffer, HTTP_SENDLEN);
    596       1.9  christos 	isc_buffer_clear(req->sendbuffer);
    597       1.9  christos 
    598       1.9  christos 	isc_buffer_initnull(&req->bodybuffer);
    599       1.4  christos 
    600      1.10  christos 	isc_httpd_attach(httpd, &req->httpd);
    601       1.4  christos 
    602      1.10  christos 	return req;
    603       1.1  christos }
    604       1.1  christos 
    605       1.1  christos static void
    606       1.9  christos new_httpd(isc_httpdmgr_t *httpdmgr, isc_nmhandle_t *handle) {
    607       1.4  christos 	isc_httpd_t *httpd = NULL;
    608       1.1  christos 
    609       1.4  christos 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
    610       1.1  christos 
    611      1.10  christos 	httpd = isc_mem_get(httpdmgr->mctx, sizeof(*httpd));
    612      1.10  christos 	*httpd = (isc_httpd_t){
    613      1.10  christos 		.magic = HTTPD_MAGIC,
    614      1.10  christos 		.link = ISC_LINK_INITIALIZER,
    615      1.10  christos 		.references = ISC_REFCOUNT_INITIALIZER(1),
    616      1.10  christos 	};
    617       1.1  christos 
    618      1.10  christos 	isc_nmhandle_attach(handle, &httpd->handle);
    619       1.1  christos 
    620      1.10  christos 	isc_httpdmgr_attach(httpdmgr, &httpd->mgr);
    621       1.1  christos 
    622       1.9  christos 	LOCK(&httpdmgr->lock);
    623       1.9  christos 	ISC_LIST_APPEND(httpdmgr->running, httpd, link);
    624       1.9  christos 	UNLOCK(&httpdmgr->lock);
    625       1.1  christos 
    626      1.10  christos 	isc_nm_read(handle, httpd_request, httpd);
    627       1.9  christos }
    628       1.1  christos 
    629       1.9  christos static isc_result_t
    630       1.9  christos httpd_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
    631       1.9  christos 	isc_httpdmgr_t *httpdmgr = (isc_httpdmgr_t *)arg;
    632       1.9  christos 	isc_sockaddr_t peeraddr;
    633       1.4  christos 
    634       1.9  christos 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
    635       1.1  christos 
    636       1.9  christos 	if ((httpdmgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) != 0) {
    637      1.10  christos 		return ISC_R_CANCELED;
    638       1.9  christos 	} else if (result == ISC_R_CANCELED) {
    639       1.9  christos 		isc_httpdmgr_shutdown(&httpdmgr);
    640      1.10  christos 		return result;
    641       1.9  christos 	} else if (result != ISC_R_SUCCESS) {
    642      1.10  christos 		return result;
    643       1.9  christos 	}
    644       1.1  christos 
    645       1.9  christos 	peeraddr = isc_nmhandle_peeraddr(handle);
    646       1.9  christos 	if (httpdmgr->client_ok != NULL &&
    647       1.9  christos 	    !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg))
    648       1.9  christos 	{
    649      1.10  christos 		return ISC_R_FAILURE;
    650       1.4  christos 	}
    651       1.1  christos 
    652       1.9  christos 	new_httpd(httpdmgr, handle);
    653       1.9  christos 
    654      1.10  christos 	return ISC_R_SUCCESS;
    655       1.1  christos }
    656       1.1  christos 
    657       1.1  christos static isc_result_t
    658       1.9  christos render_404(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg,
    659       1.9  christos 	   unsigned int *retcode, const char **retmsg, const char **mimetype,
    660       1.9  christos 	   isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
    661       1.1  christos 	static char msg[] = "No such URL.\r\n";
    662       1.1  christos 
    663       1.9  christos 	UNUSED(httpd);
    664       1.1  christos 	UNUSED(urlinfo);
    665       1.1  christos 	UNUSED(arg);
    666       1.1  christos 
    667       1.1  christos 	*retcode = 404;
    668       1.1  christos 	*retmsg = "No such URL";
    669       1.1  christos 	*mimetype = "text/plain";
    670       1.1  christos 	isc_buffer_reinit(b, msg, strlen(msg));
    671       1.1  christos 	isc_buffer_add(b, strlen(msg));
    672       1.1  christos 	*freecb = NULL;
    673       1.1  christos 	*freecb_args = NULL;
    674       1.1  christos 
    675      1.10  christos 	return ISC_R_SUCCESS;
    676       1.1  christos }
    677       1.1  christos 
    678       1.1  christos static isc_result_t
    679       1.9  christos render_500(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg,
    680       1.9  christos 	   unsigned int *retcode, const char **retmsg, const char **mimetype,
    681       1.9  christos 	   isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
    682       1.1  christos 	static char msg[] = "Internal server failure.\r\n";
    683       1.1  christos 
    684       1.9  christos 	UNUSED(httpd);
    685       1.1  christos 	UNUSED(urlinfo);
    686       1.1  christos 	UNUSED(arg);
    687       1.1  christos 
    688       1.1  christos 	*retcode = 500;
    689       1.1  christos 	*retmsg = "Internal server failure";
    690       1.1  christos 	*mimetype = "text/plain";
    691       1.1  christos 	isc_buffer_reinit(b, msg, strlen(msg));
    692       1.1  christos 	isc_buffer_add(b, strlen(msg));
    693       1.1  christos 	*freecb = NULL;
    694       1.1  christos 	*freecb_args = NULL;
    695       1.1  christos 
    696      1.10  christos 	return ISC_R_SUCCESS;
    697       1.1  christos }
    698       1.1  christos 
    699       1.1  christos #ifdef HAVE_ZLIB
    700       1.1  christos /*%<
    701       1.1  christos  * Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it
    702       1.1  christos  * if necessary.
    703       1.1  christos  *
    704       1.1  christos  * Requires:
    705       1.1  christos  *\li	httpd a valid isc_httpd_t object
    706       1.1  christos  *
    707       1.1  christos  * Returns:
    708       1.1  christos  *\li	#ISC_R_SUCCESS	  -- all is well.
    709       1.1  christos  *\li	#ISC_R_NOMEMORY	  -- not enough memory to compress data
    710       1.1  christos  *\li	#ISC_R_FAILURE	  -- error during compression or compressed
    711       1.1  christos  *			     data would be larger than input data
    712       1.1  christos  */
    713       1.1  christos static isc_result_t
    714       1.9  christos httpd_compress(isc_httpd_sendreq_t *req) {
    715       1.1  christos 	z_stream zstr;
    716       1.9  christos 	int ret, inputlen;
    717       1.1  christos 
    718       1.1  christos 	/*
    719       1.1  christos 	 * We're setting output buffer size to input size so it fails if the
    720       1.1  christos 	 * compressed data size would be bigger than the input size.
    721       1.1  christos 	 */
    722       1.9  christos 	inputlen = isc_buffer_usedlength(&req->bodybuffer);
    723       1.9  christos 	if (inputlen == 0) {
    724      1.10  christos 		return ISC_R_FAILURE;
    725       1.9  christos 	}
    726       1.9  christos 
    727       1.9  christos 	isc_buffer_allocate(req->mctx, &req->compbuffer, inputlen);
    728       1.9  christos 	isc_buffer_clear(req->compbuffer);
    729       1.1  christos 
    730       1.9  christos 	zstr = (z_stream){
    731       1.9  christos 		.total_in = inputlen,
    732       1.9  christos 		.avail_out = inputlen,
    733       1.9  christos 		.avail_in = inputlen,
    734       1.9  christos 		.next_in = isc_buffer_base(&req->bodybuffer),
    735       1.9  christos 		.next_out = isc_buffer_base(req->compbuffer),
    736       1.9  christos 	};
    737       1.1  christos 
    738       1.1  christos 	ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION);
    739       1.1  christos 	if (ret == Z_OK) {
    740       1.1  christos 		ret = deflate(&zstr, Z_FINISH);
    741       1.1  christos 	}
    742       1.1  christos 	deflateEnd(&zstr);
    743       1.1  christos 	if (ret == Z_STREAM_END) {
    744       1.9  christos 		isc_buffer_add(req->compbuffer, zstr.total_out);
    745      1.10  christos 		return ISC_R_SUCCESS;
    746       1.1  christos 	} else {
    747       1.9  christos 		isc_buffer_free(&req->compbuffer);
    748      1.10  christos 		return ISC_R_FAILURE;
    749       1.1  christos 	}
    750       1.1  christos }
    751       1.4  christos #endif /* ifdef HAVE_ZLIB */
    752       1.1  christos 
    753       1.1  christos static void
    754      1.10  christos prepare_response(void *arg) {
    755      1.10  christos 	isc_httpd_sendreq_t *req = arg;
    756      1.10  christos 	isc_httpd_t *httpd = req->httpd;
    757      1.10  christos 	isc_httpdmgr_t *mgr = httpd->mgr;
    758      1.10  christos 	isc_time_t now = isc_time_now();
    759       1.9  christos 	char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
    760       1.9  christos 	const char *path = "/";
    761       1.9  christos 	size_t path_len = 1;
    762       1.3  christos 	bool is_compressed = false;
    763       1.9  christos 	isc_httpdurl_t *url = NULL;
    764       1.9  christos 	isc_result_t result;
    765       1.1  christos 
    766       1.4  christos 	REQUIRE(VALID_HTTPD(httpd));
    767      1.10  christos 	REQUIRE(req != NULL);
    768       1.1  christos 
    769       1.9  christos 	isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
    770       1.1  christos 
    771       1.9  christos 	if (httpd->up.field_set & (1 << ISC_UF_PATH)) {
    772       1.9  christos 		path = &httpd->path[httpd->up.field_data[ISC_UF_PATH].off];
    773       1.9  christos 		path_len = httpd->up.field_data[ISC_UF_PATH].len;
    774       1.1  christos 	}
    775       1.1  christos 
    776       1.9  christos 	LOCK(&mgr->lock);
    777  1.10.2.1    martin 	ISC_LIST_FOREACH(mgr->urls, url, link) {
    778       1.9  christos 		if (strncmp(path, url->url, path_len) == 0) {
    779       1.1  christos 			break;
    780       1.4  christos 		}
    781       1.1  christos 	}
    782       1.9  christos 	UNLOCK(&mgr->lock);
    783       1.9  christos 
    784       1.4  christos 	if (url == NULL) {
    785       1.9  christos 		result = mgr->render_404(httpd, NULL, NULL, &req->retcode,
    786       1.9  christos 					 &req->retmsg, &req->mimetype,
    787       1.9  christos 					 &req->bodybuffer, &req->freecb,
    788       1.9  christos 					 &req->freecb_arg);
    789       1.4  christos 	} else {
    790       1.9  christos 		result = url->action(httpd, url, url->action_arg, &req->retcode,
    791       1.9  christos 				     &req->retmsg, &req->mimetype,
    792       1.9  christos 				     &req->bodybuffer, &req->freecb,
    793       1.9  christos 				     &req->freecb_arg);
    794       1.4  christos 	}
    795       1.1  christos 	if (result != ISC_R_SUCCESS) {
    796       1.9  christos 		result = mgr->render_500(httpd, url, NULL, &req->retcode,
    797       1.9  christos 					 &req->retmsg, &req->mimetype,
    798       1.9  christos 					 &req->bodybuffer, &req->freecb,
    799       1.9  christos 					 &req->freecb_arg);
    800       1.1  christos 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    801       1.1  christos 	}
    802       1.1  christos 
    803       1.1  christos #ifdef HAVE_ZLIB
    804       1.9  christos 	if ((httpd->flags & ACCEPT_DEFLATE) != 0) {
    805       1.9  christos 		result = httpd_compress(req);
    806       1.4  christos 		if (result == ISC_R_SUCCESS) {
    807       1.4  christos 			is_compressed = true;
    808       1.4  christos 		}
    809       1.1  christos 	}
    810       1.4  christos #endif /* ifdef HAVE_ZLIB */
    811       1.1  christos 
    812       1.9  christos 	httpd_response(httpd, req);
    813       1.9  christos 	/* RFC 9112  9.6: SHOULD send Connection: close in last response */
    814       1.9  christos 	if ((httpd->flags & CONNECTION_CLOSE) != 0) {
    815       1.9  christos 		httpd_addheader(req, "Connection", "close");
    816       1.9  christos 	} else if ((httpd->flags & CONNECTION_KEEP_ALIVE) != 0) {
    817       1.9  christos 		httpd_addheader(req, "Connection", "Keep-Alive");
    818       1.9  christos 	}
    819       1.9  christos 	httpd_addheader(req, "Content-Type", req->mimetype);
    820       1.9  christos 	httpd_addheader(req, "Date", datebuf);
    821       1.9  christos 	httpd_addheader(req, "Expires", datebuf);
    822       1.1  christos 
    823       1.1  christos 	if (url != NULL && url->isstatic) {
    824       1.1  christos 		char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
    825       1.4  christos 		isc_time_formathttptimestamp(&url->loadtime, loadbuf,
    826       1.4  christos 					     sizeof(loadbuf));
    827       1.9  christos 		httpd_addheader(req, "Last-Modified", loadbuf);
    828       1.9  christos 		httpd_addheader(req, "Cache-Control: public", NULL);
    829       1.1  christos 	} else {
    830       1.9  christos 		httpd_addheader(req, "Last-Modified", datebuf);
    831       1.9  christos 		httpd_addheader(req, "Pragma: no-cache", NULL);
    832       1.9  christos 		httpd_addheader(req, "Cache-Control: no-cache", NULL);
    833       1.1  christos 	}
    834       1.1  christos 
    835       1.9  christos 	httpd_addheader(req, "Server: libisc", NULL);
    836       1.1  christos 
    837       1.5  christos 	if (is_compressed) {
    838       1.9  christos 		httpd_addheader(req, "Content-Encoding", "deflate");
    839       1.9  christos 		httpd_addheaderuint(req, "Content-Length",
    840       1.9  christos 				    isc_buffer_usedlength(req->compbuffer));
    841       1.1  christos 	} else {
    842       1.9  christos 		httpd_addheaderuint(req, "Content-Length",
    843       1.9  christos 				    isc_buffer_usedlength(&req->bodybuffer));
    844       1.1  christos 	}
    845       1.1  christos 
    846       1.9  christos 	httpd_endheaders(req); /* done */
    847       1.1  christos 
    848       1.1  christos 	/*
    849       1.3  christos 	 * Append either the compressed or the non-compressed response body to
    850       1.3  christos 	 * the response headers and store the result in httpd->sendbuffer.
    851       1.1  christos 	 */
    852       1.9  christos 	if (is_compressed) {
    853       1.9  christos 		isc_buffer_putmem(req->sendbuffer,
    854       1.9  christos 				  isc_buffer_base(req->compbuffer),
    855       1.9  christos 				  isc_buffer_usedlength(req->compbuffer));
    856       1.9  christos 		isc_buffer_free(&req->compbuffer);
    857       1.9  christos 	} else {
    858       1.9  christos 		isc_buffer_putmem(req->sendbuffer,
    859       1.9  christos 				  isc_buffer_base(&req->bodybuffer),
    860       1.9  christos 				  isc_buffer_usedlength(&req->bodybuffer));
    861       1.9  christos 	}
    862       1.9  christos 
    863       1.9  christos 	/* Free the bodybuffer */
    864       1.9  christos 	if (req->freecb != NULL && isc_buffer_length(&req->bodybuffer) > 0) {
    865       1.9  christos 		req->freecb(&req->bodybuffer, req->freecb_arg);
    866       1.9  christos 	}
    867       1.9  christos 
    868       1.9  christos 	/* Consume the request from the recv buffer. */
    869       1.9  christos 	INSIST(httpd->consume != 0);
    870       1.9  christos 	INSIST(httpd->consume <= httpd->recvlen);
    871       1.9  christos 	if (httpd->consume < httpd->recvlen) {
    872       1.9  christos 		memmove(httpd->recvbuf, httpd->recvbuf + httpd->consume,
    873       1.9  christos 			httpd->recvlen - httpd->consume);
    874       1.9  christos 	}
    875       1.9  christos 	httpd->recvlen -= httpd->consume;
    876       1.9  christos 	httpd->consume = 0;
    877      1.10  christos }
    878      1.10  christos 
    879      1.10  christos static void
    880      1.10  christos prepare_response_done(void *arg) {
    881      1.10  christos 	isc_region_t r;
    882      1.10  christos 	isc_httpd_sendreq_t *req = arg;
    883      1.10  christos 	isc_httpd_t *httpd = req->httpd;
    884       1.9  christos 
    885       1.9  christos 	/*
    886      1.10  christos 	 * Determine total response size.
    887       1.9  christos 	 */
    888      1.10  christos 	isc_buffer_usedregion(req->sendbuffer, &r);
    889       1.9  christos 
    890      1.10  christos 	isc_nm_send(httpd->handle, &r, httpd_senddone, req);
    891       1.9  christos }
    892       1.9  christos 
    893       1.9  christos static void
    894       1.9  christos httpd_request(isc_nmhandle_t *handle, isc_result_t eresult,
    895       1.9  christos 	      isc_region_t *region, void *arg) {
    896      1.10  christos 	isc_httpd_t *httpd = arg;
    897      1.10  christos 	isc_httpdmgr_t *mgr = httpd->mgr;
    898      1.10  christos 	size_t last_len = 0;
    899       1.9  christos 	isc_result_t result;
    900       1.9  christos 
    901       1.9  christos 	REQUIRE(VALID_HTTPD(httpd));
    902       1.9  christos 
    903       1.9  christos 	REQUIRE(httpd->handle == handle);
    904       1.9  christos 
    905       1.9  christos 	if (eresult != ISC_R_SUCCESS) {
    906       1.9  christos 		goto close_readhandle;
    907       1.9  christos 	}
    908       1.9  christos 
    909      1.10  christos 	REQUIRE((mgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) == 0);
    910       1.9  christos 
    911      1.10  christos 	isc_nm_read_stop(handle);
    912       1.9  christos 
    913       1.9  christos 	/*
    914       1.9  christos 	 * If we are being called from httpd_senddone(), the last HTTP request
    915       1.9  christos 	 * was processed successfully, reset the last_len to 0, even if there's
    916       1.9  christos 	 * data in the httpd->recvbuf.
    917       1.9  christos 	 */
    918       1.9  christos 	last_len = (region == NULL) ? 0 : httpd->recvlen;
    919       1.9  christos 
    920       1.9  christos 	/* Store the received data into the recvbuf */
    921       1.9  christos 	if (region != NULL) {
    922       1.9  christos 		if (httpd->recvlen + region->length > sizeof(httpd->recvbuf)) {
    923       1.9  christos 			goto close_readhandle;
    924       1.9  christos 		}
    925       1.9  christos 
    926       1.9  christos 		memmove(httpd->recvbuf + httpd->recvlen, region->base,
    927       1.9  christos 			region->length);
    928       1.9  christos 		httpd->recvlen += region->length;
    929       1.9  christos 	}
    930       1.9  christos 
    931       1.9  christos 	result = process_request(httpd, last_len);
    932       1.9  christos 
    933       1.9  christos 	if (result == ISC_R_NOMORE) {
    934       1.9  christos 		if (httpd->recvlen > HTTP_MAX_REQUEST_LEN) {
    935       1.9  christos 			goto close_readhandle;
    936       1.9  christos 		}
    937       1.9  christos 
    938      1.10  christos 		/* Wait for more data, the handle is still attached */
    939      1.10  christos 		isc_nm_read(handle, httpd_request, arg);
    940       1.9  christos 		return;
    941       1.9  christos 	}
    942       1.9  christos 
    943       1.9  christos 	/* XXXFANF it would be more polite to reply 400 bad request */
    944       1.9  christos 	if (result != ISC_R_SUCCESS) {
    945       1.9  christos 		goto close_readhandle;
    946       1.9  christos 	}
    947       1.9  christos 
    948      1.10  christos 	isc_httpd_sendreq_t *req = isc__httpd_sendreq_new(httpd);
    949      1.10  christos 	isc_nmhandle_ref(handle);
    950      1.10  christos 	isc_work_enqueue(isc_loop(), prepare_response, prepare_response_done,
    951      1.10  christos 			 req);
    952       1.9  christos 	return;
    953       1.9  christos 
    954       1.9  christos close_readhandle:
    955      1.10  christos 	isc_nmhandle_close(httpd->handle);
    956      1.10  christos 	isc_nmhandle_detach(&httpd->handle);
    957      1.10  christos 
    958      1.10  christos 	isc_httpd_detach(&httpd);
    959       1.1  christos }
    960       1.1  christos 
    961       1.1  christos void
    962       1.1  christos isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
    963      1.10  christos 	isc_httpdmgr_t *httpdmgr = NULL;
    964       1.4  christos 
    965       1.4  christos 	REQUIRE(httpdmgrp != NULL);
    966       1.9  christos 	REQUIRE(VALID_HTTPDMGR(*httpdmgrp));
    967       1.9  christos 
    968       1.1  christos 	httpdmgr = *httpdmgrp;
    969       1.1  christos 	*httpdmgrp = NULL;
    970       1.9  christos 
    971       1.9  christos 	isc_nm_stoplistening(httpdmgr->sock);
    972       1.1  christos 
    973       1.1  christos 	LOCK(&httpdmgr->lock);
    974      1.10  christos 
    975      1.10  christos 	isc_httpd_t *httpd = NULL, *next = NULL;
    976  1.10.2.1    martin 	ISC_LIST_FOREACH_SAFE(httpdmgr->running, httpd, link, next) {
    977      1.10  christos 		if (httpd->handle != NULL) {
    978      1.10  christos 			httpd_request(httpd->handle, ISC_R_SUCCESS, NULL,
    979      1.10  christos 				      httpd);
    980      1.10  christos 		}
    981      1.10  christos 	}
    982      1.10  christos 
    983       1.9  christos 	httpdmgr->flags |= ISC_HTTPDMGR_SHUTTINGDOWN;
    984       1.1  christos 
    985       1.1  christos 	UNLOCK(&httpdmgr->lock);
    986       1.1  christos 
    987       1.9  christos 	isc_nmsocket_close(&httpdmgr->sock);
    988       1.1  christos 
    989      1.10  christos 	isc_httpdmgr_detach(&httpdmgr);
    990       1.1  christos }
    991       1.1  christos 
    992       1.9  christos static void
    993       1.9  christos httpd_response(isc_httpd_t *httpd, isc_httpd_sendreq_t *req) {
    994       1.1  christos 	isc_result_t result;
    995       1.1  christos 
    996       1.9  christos 	result = isc_buffer_printf(req->sendbuffer, "HTTP/1.%u %03u %s\r\n",
    997       1.9  christos 				   httpd->minor_version, req->retcode,
    998       1.9  christos 				   req->retmsg);
    999       1.4  christos 
   1000       1.9  christos 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1001       1.1  christos }
   1002       1.1  christos 
   1003       1.9  christos static void
   1004       1.9  christos httpd_addheader(isc_httpd_sendreq_t *req, const char *name, const char *val) {
   1005       1.1  christos 	isc_result_t result;
   1006       1.4  christos 
   1007       1.4  christos 	if (val != NULL) {
   1008       1.9  christos 		result = isc_buffer_printf(req->sendbuffer, "%s: %s\r\n", name,
   1009       1.9  christos 					   val);
   1010       1.9  christos 	} else {
   1011       1.9  christos 		result = isc_buffer_printf(req->sendbuffer, "%s\r\n", name);
   1012       1.4  christos 	}
   1013       1.1  christos 
   1014       1.9  christos 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1015       1.1  christos }
   1016       1.1  christos 
   1017       1.9  christos static void
   1018       1.9  christos httpd_endheaders(isc_httpd_sendreq_t *req) {
   1019       1.1  christos 	isc_result_t result;
   1020       1.1  christos 
   1021       1.9  christos 	result = isc_buffer_printf(req->sendbuffer, "\r\n");
   1022       1.4  christos 
   1023       1.9  christos 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1024       1.1  christos }
   1025       1.1  christos 
   1026       1.9  christos static void
   1027       1.9  christos httpd_addheaderuint(isc_httpd_sendreq_t *req, const char *name, int val) {
   1028       1.1  christos 	isc_result_t result;
   1029       1.4  christos 
   1030       1.9  christos 	result = isc_buffer_printf(req->sendbuffer, "%s: %d\r\n", name, val);
   1031       1.1  christos 
   1032       1.9  christos 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1033       1.1  christos }
   1034       1.1  christos 
   1035       1.1  christos static void
   1036       1.9  christos httpd_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
   1037       1.9  christos 	isc_httpd_sendreq_t *req = (isc_httpd_sendreq_t *)arg;
   1038       1.9  christos 	isc_httpd_t *httpd = req->httpd;
   1039       1.1  christos 
   1040       1.4  christos 	REQUIRE(VALID_HTTPD(httpd));
   1041       1.4  christos 
   1042      1.10  christos 	if ((httpd->mgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) != 0) {
   1043       1.9  christos 		goto detach;
   1044       1.1  christos 	}
   1045       1.1  christos 
   1046      1.10  christos 	if (eresult == ISC_R_SUCCESS && (httpd->flags & CONNECTION_CLOSE) != 0)
   1047       1.9  christos 	{
   1048      1.10  christos 		eresult = ISC_R_EOF;
   1049       1.1  christos 	}
   1050       1.1  christos 
   1051      1.10  christos 	/*
   1052      1.10  christos 	 * Calling httpd_request() with region NULL restarts reading.
   1053      1.10  christos 	 */
   1054      1.10  christos 	httpd_request(handle, eresult, NULL, httpd);
   1055      1.10  christos 
   1056       1.9  christos detach:
   1057       1.9  christos 	isc_nmhandle_detach(&handle);
   1058       1.9  christos 	isc__httpd_sendreq_free(req);
   1059      1.10  christos 	isc_httpd_detach(&httpd);
   1060       1.1  christos }
   1061       1.1  christos 
   1062       1.1  christos isc_result_t
   1063       1.9  christos isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, bool isstatic,
   1064       1.4  christos 		    isc_httpdaction_t *func, void *arg) {
   1065       1.1  christos 	isc_httpdurl_t *item;
   1066       1.1  christos 
   1067       1.4  christos 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
   1068       1.4  christos 
   1069       1.1  christos 	if (url == NULL) {
   1070       1.1  christos 		httpdmgr->render_404 = func;
   1071      1.10  christos 		return ISC_R_SUCCESS;
   1072       1.1  christos 	}
   1073       1.1  christos 
   1074       1.1  christos 	item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
   1075       1.1  christos 
   1076       1.1  christos 	item->url = isc_mem_strdup(httpdmgr->mctx, url);
   1077       1.1  christos 
   1078       1.1  christos 	item->action = func;
   1079       1.1  christos 	item->action_arg = arg;
   1080       1.1  christos 	item->isstatic = isstatic;
   1081      1.10  christos 	item->loadtime = isc_time_now();
   1082       1.1  christos 
   1083       1.1  christos 	ISC_LINK_INIT(item, link);
   1084       1.4  christos 
   1085       1.4  christos 	LOCK(&httpdmgr->lock);
   1086       1.1  christos 	ISC_LIST_APPEND(httpdmgr->urls, item, link);
   1087       1.4  christos 	UNLOCK(&httpdmgr->lock);
   1088       1.1  christos 
   1089      1.10  christos 	return ISC_R_SUCCESS;
   1090       1.1  christos }
   1091       1.1  christos 
   1092       1.1  christos void
   1093       1.4  christos isc_httpd_setfinishhook(void (*fn)(void)) {
   1094       1.4  christos #if ENABLE_AFL
   1095       1.1  christos 	finishhook = fn;
   1096       1.4  christos #else  /* ENABLE_AFL */
   1097       1.4  christos 	UNUSED(fn);
   1098       1.4  christos #endif /* ENABLE_AFL */
   1099       1.1  christos }
   1100       1.9  christos 
   1101       1.9  christos bool
   1102       1.9  christos isc_httpdurl_isstatic(const isc_httpdurl_t *url) {
   1103      1.10  christos 	return url->isstatic;
   1104       1.9  christos }
   1105       1.9  christos 
   1106       1.9  christos const isc_time_t *
   1107       1.9  christos isc_httpdurl_loadtime(const isc_httpdurl_t *url) {
   1108      1.10  christos 	return &url->loadtime;
   1109       1.9  christos }
   1110       1.9  christos 
   1111       1.9  christos const isc_time_t *
   1112       1.9  christos isc_httpd_if_modified_since(const isc_httpd_t *httpd) {
   1113      1.10  christos 	return (const isc_time_t *)&httpd->if_modified_since;
   1114       1.9  christos }
   1115