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