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