Home | History | Annotate | Line # | Download | only in netmgr
      1  1.1  christos /*	$NetBSD: http.c,v 1.6 2026/01/29 18:37:55 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  christos  * SPDX-License-Identifier: MPL-2.0
      7  1.1  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  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 #include <ctype.h>
     17  1.1  christos #include <inttypes.h>
     18  1.1  christos #include <limits.h>
     19  1.1  christos #include <nghttp2/nghttp2.h>
     20  1.1  christos #include <signal.h>
     21  1.1  christos #include <string.h>
     22  1.1  christos 
     23  1.4  christos #include <isc/async.h>
     24  1.1  christos #include <isc/base64.h>
     25  1.1  christos #include <isc/log.h>
     26  1.1  christos #include <isc/netmgr.h>
     27  1.1  christos #include <isc/sockaddr.h>
     28  1.1  christos #include <isc/tls.h>
     29  1.1  christos #include <isc/url.h>
     30  1.1  christos #include <isc/util.h>
     31  1.1  christos 
     32  1.1  christos #include "netmgr-int.h"
     33  1.1  christos 
     34  1.1  christos #define AUTHEXTRA 7
     35  1.1  christos 
     36  1.1  christos #define MAX_DNS_MESSAGE_SIZE (UINT16_MAX)
     37  1.1  christos 
     38  1.1  christos #define DNS_MEDIA_TYPE "application/dns-message"
     39  1.1  christos 
     40  1.1  christos /*
     41  1.1  christos  * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
     42  1.1  christos  * for additional details. Basically it means "avoid caching by any
     43  1.1  christos  * means."
     44  1.1  christos  */
     45  1.1  christos #define DEFAULT_CACHE_CONTROL "no-cache, no-store, must-revalidate"
     46  1.1  christos 
     47  1.1  christos /*
     48  1.1  christos  * If server during request processing surpasses any of the limits
     49  1.1  christos  * below, it will just reset the stream without returning any error
     50  1.1  christos  * codes in a response.  Ideally, these parameters should be
     51  1.1  christos  * configurable both globally and per every HTTP endpoint description
     52  1.1  christos  * in the configuration file, but for now it should be enough.
     53  1.1  christos  */
     54  1.1  christos 
     55  1.1  christos /*
     56  1.1  christos  * 128K should be enough to encode 64K of data into base64url inside GET
     57  1.1  christos  * request and have extra space for other headers
     58  1.1  christos  */
     59  1.1  christos #define MAX_ALLOWED_DATA_IN_HEADERS (MAX_DNS_MESSAGE_SIZE * 2)
     60  1.1  christos 
     61  1.1  christos #define MAX_ALLOWED_DATA_IN_POST \
     62  1.1  christos 	(MAX_DNS_MESSAGE_SIZE + MAX_DNS_MESSAGE_SIZE / 2)
     63  1.1  christos 
     64  1.1  christos #define HEADER_MATCH(header, name, namelen)   \
     65  1.1  christos 	(((namelen) == sizeof(header) - 1) && \
     66  1.1  christos 	 (strncasecmp((header), (const char *)(name), (namelen)) == 0))
     67  1.1  christos 
     68  1.1  christos #define MIN_SUCCESSFUL_HTTP_STATUS (200)
     69  1.1  christos #define MAX_SUCCESSFUL_HTTP_STATUS (299)
     70  1.1  christos 
     71  1.1  christos /* This definition sets the upper limit of pending write buffer to an
     72  1.1  christos  * adequate enough value. That is done mostly to fight a limitation
     73  1.1  christos  * for a max TLS record size in flamethrower (2K).  In a perfect world
     74  1.1  christos  * this constant should not be required, if we ever move closer to
     75  1.1  christos  * that state, the constant, and corresponding code, should be
     76  1.1  christos  * removed. For now the limit seems adequate enough to fight
     77  1.1  christos  * "tinygrams" problem. */
     78  1.1  christos #define FLUSH_HTTP_WRITE_BUFFER_AFTER (1536)
     79  1.1  christos 
     80  1.1  christos /* This switch is here mostly to test the code interoperability with
     81  1.1  christos  * buggy implementations */
     82  1.1  christos #define ENABLE_HTTP_WRITE_BUFFERING 1
     83  1.1  christos 
     84  1.1  christos #define SUCCESSFUL_HTTP_STATUS(code)             \
     85  1.1  christos 	((code) >= MIN_SUCCESSFUL_HTTP_STATUS && \
     86  1.1  christos 	 (code) <= MAX_SUCCESSFUL_HTTP_STATUS)
     87  1.1  christos 
     88  1.1  christos #define INITIAL_DNS_MESSAGE_BUFFER_SIZE (512)
     89  1.1  christos 
     90  1.5  christos /*
     91  1.5  christos  * The value should be small enough to not allow a server to open too
     92  1.5  christos  * many streams at once. It should not be too small either because
     93  1.5  christos  * the incoming data will be split into too many chunks with each of
     94  1.5  christos  * them processed asynchronously.
     95  1.5  christos  */
     96  1.5  christos #define INCOMING_DATA_CHUNK_SIZE (256)
     97  1.5  christos 
     98  1.5  christos /*
     99  1.5  christos  * Often processing a chunk does not change the number of streams. In
    100  1.5  christos  * that case we can process more than once, but we still should have a
    101  1.5  christos  * hard limit on that.
    102  1.5  christos  */
    103  1.5  christos #define INCOMING_DATA_MAX_CHUNKS_AT_ONCE (4)
    104  1.5  christos 
    105  1.5  christos /*
    106  1.5  christos  * These constants define the grace period to help detect flooding clients.
    107  1.5  christos  *
    108  1.5  christos  * The first one defines how much data can be processed before opening
    109  1.5  christos  * a first stream and received at least some useful (=DNS) data.
    110  1.5  christos  *
    111  1.5  christos  * The second one defines how much data from a client we read before
    112  1.5  christos  * trying to drop a clients who sends not enough useful data.
    113  1.5  christos  *
    114  1.5  christos  * The third constant defines how many streams we agree to process
    115  1.5  christos  * before checking if there was at least one DNS request received.
    116  1.5  christos  */
    117  1.5  christos #define INCOMING_DATA_INITIAL_STREAM_SIZE (1536)
    118  1.5  christos #define INCOMING_DATA_GRACE_SIZE	  (MAX_ALLOWED_DATA_IN_HEADERS)
    119  1.5  christos #define MAX_STREAMS_BEFORE_FIRST_REQUEST  (50)
    120  1.5  christos 
    121  1.1  christos typedef struct isc_nm_http_response_status {
    122  1.1  christos 	size_t code;
    123  1.1  christos 	size_t content_length;
    124  1.1  christos 	bool content_type_valid;
    125  1.1  christos } isc_nm_http_response_status_t;
    126  1.1  christos 
    127  1.1  christos typedef struct http_cstream {
    128  1.1  christos 	isc_nm_recv_cb_t read_cb;
    129  1.1  christos 	void *read_cbarg;
    130  1.1  christos 	isc_nm_cb_t connect_cb;
    131  1.1  christos 	void *connect_cbarg;
    132  1.1  christos 
    133  1.1  christos 	bool sending;
    134  1.1  christos 	bool reading;
    135  1.1  christos 
    136  1.1  christos 	char *uri;
    137  1.1  christos 	isc_url_parser_t up;
    138  1.1  christos 
    139  1.1  christos 	char *authority;
    140  1.1  christos 	size_t authoritylen;
    141  1.1  christos 	char *path;
    142  1.1  christos 
    143  1.1  christos 	isc_buffer_t *rbuf;
    144  1.1  christos 
    145  1.1  christos 	size_t pathlen;
    146  1.1  christos 	int32_t stream_id;
    147  1.1  christos 
    148  1.1  christos 	bool post; /* POST or GET */
    149  1.1  christos 	isc_buffer_t *postdata;
    150  1.1  christos 	char *GET_path;
    151  1.1  christos 	size_t GET_path_len;
    152  1.1  christos 
    153  1.1  christos 	isc_nm_http_response_status_t response_status;
    154  1.1  christos 	isc_nmsocket_t *httpsock;
    155  1.1  christos 	LINK(struct http_cstream) link;
    156  1.1  christos } http_cstream_t;
    157  1.1  christos 
    158  1.1  christos #define HTTP2_SESSION_MAGIC    ISC_MAGIC('H', '2', 'S', 'S')
    159  1.1  christos #define VALID_HTTP2_SESSION(t) ISC_MAGIC_VALID(t, HTTP2_SESSION_MAGIC)
    160  1.1  christos 
    161  1.1  christos typedef ISC_LIST(isc__nm_uvreq_t) isc__nm_http_pending_callbacks_t;
    162  1.1  christos 
    163  1.1  christos struct isc_nm_http_session {
    164  1.1  christos 	unsigned int magic;
    165  1.1  christos 	isc_refcount_t references;
    166  1.1  christos 	isc_mem_t *mctx;
    167  1.1  christos 
    168  1.1  christos 	size_t sending;
    169  1.1  christos 	bool reading;
    170  1.1  christos 	bool closed;
    171  1.1  christos 	bool closing;
    172  1.1  christos 
    173  1.1  christos 	nghttp2_session *ngsession;
    174  1.1  christos 	bool client;
    175  1.1  christos 
    176  1.1  christos 	ISC_LIST(http_cstream_t) cstreams;
    177  1.1  christos 	ISC_LIST(isc_nmsocket_h2_t) sstreams;
    178  1.1  christos 	size_t nsstreams;
    179  1.5  christos 	uint64_t total_opened_sstreams;
    180  1.1  christos 
    181  1.1  christos 	isc_nmhandle_t *handle;
    182  1.1  christos 	isc_nmhandle_t *client_httphandle;
    183  1.1  christos 	isc_nmsocket_t *serversocket;
    184  1.1  christos 
    185  1.1  christos 	isc_buffer_t *buf;
    186  1.1  christos 
    187  1.1  christos 	isc_tlsctx_t *tlsctx;
    188  1.1  christos 	uint32_t max_concurrent_streams;
    189  1.1  christos 
    190  1.1  christos 	isc__nm_http_pending_callbacks_t pending_write_callbacks;
    191  1.1  christos 	isc_buffer_t *pending_write_data;
    192  1.5  christos 
    193  1.5  christos 	size_t data_in_flight;
    194  1.5  christos 
    195  1.5  christos 	bool async_queued;
    196  1.5  christos 
    197  1.5  christos 	/*
    198  1.5  christos 	 * The statistical values below are for usage on server-side
    199  1.5  christos 	 * only. They are meant to detect clients that are taking too many
    200  1.5  christos 	 * resources from the server.
    201  1.5  christos 	 */
    202  1.5  christos 	uint64_t received;  /* How many requests have been received. */
    203  1.5  christos 	uint64_t submitted; /* How many responses were submitted to send */
    204  1.5  christos 	uint64_t processed; /* How many responses were processed. */
    205  1.5  christos 
    206  1.5  christos 	uint64_t processed_incoming_data;
    207  1.5  christos 	uint64_t processed_useful_data; /* DNS data */
    208  1.1  christos };
    209  1.1  christos 
    210  1.1  christos typedef enum isc_http_error_responses {
    211  1.1  christos 	ISC_HTTP_ERROR_SUCCESS,		       /* 200 */
    212  1.1  christos 	ISC_HTTP_ERROR_NOT_FOUND,	       /* 404 */
    213  1.1  christos 	ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE,      /* 413 */
    214  1.1  christos 	ISC_HTTP_ERROR_URI_TOO_LONG,	       /* 414 */
    215  1.1  christos 	ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE, /* 415 */
    216  1.1  christos 	ISC_HTTP_ERROR_BAD_REQUEST,	       /* 400 */
    217  1.1  christos 	ISC_HTTP_ERROR_NOT_IMPLEMENTED,	       /* 501 */
    218  1.1  christos 	ISC_HTTP_ERROR_GENERIC,		       /* 500 Internal Server Error */
    219  1.1  christos 	ISC_HTTP_ERROR_MAX
    220  1.1  christos } isc_http_error_responses_t;
    221  1.1  christos 
    222  1.1  christos typedef struct isc_http_send_req {
    223  1.1  christos 	isc_nm_http_session_t *session;
    224  1.1  christos 	isc_nmhandle_t *transphandle;
    225  1.1  christos 	isc_nmhandle_t *httphandle;
    226  1.1  christos 	isc_nm_cb_t cb;
    227  1.1  christos 	void *cbarg;
    228  1.1  christos 	isc_buffer_t *pending_write_data;
    229  1.1  christos 	isc__nm_http_pending_callbacks_t pending_write_callbacks;
    230  1.5  christos 	uint64_t submitted;
    231  1.1  christos } isc_http_send_req_t;
    232  1.1  christos 
    233  1.1  christos #define HTTP_ENDPOINTS_MAGIC	ISC_MAGIC('H', 'T', 'E', 'P')
    234  1.1  christos #define VALID_HTTP_ENDPOINTS(t) ISC_MAGIC_VALID(t, HTTP_ENDPOINTS_MAGIC)
    235  1.1  christos 
    236  1.3  christos #define HTTP_HANDLER_MAGIC    ISC_MAGIC('H', 'T', 'H', 'L')
    237  1.3  christos #define VALID_HTTP_HANDLER(t) ISC_MAGIC_VALID(t, HTTP_HANDLER_MAGIC)
    238  1.3  christos 
    239  1.5  christos static void
    240  1.1  christos http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle,
    241  1.1  christos 		   isc_nm_cb_t cb, void *cbarg);
    242  1.1  christos 
    243  1.1  christos static void
    244  1.5  christos http_log_flooding_peer(isc_nm_http_session_t *session);
    245  1.5  christos 
    246  1.5  christos static bool
    247  1.5  christos http_is_flooding_peer(isc_nm_http_session_t *session);
    248  1.5  christos 
    249  1.5  christos static ssize_t
    250  1.5  christos http_process_input_data(isc_nm_http_session_t *session,
    251  1.5  christos 			isc_buffer_t *input_data);
    252  1.5  christos 
    253  1.5  christos static inline bool
    254  1.5  christos http_too_many_active_streams(isc_nm_http_session_t *session);
    255  1.5  christos 
    256  1.5  christos static void
    257  1.1  christos http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle,
    258  1.1  christos 	    isc_nm_cb_t send_cb, void *send_cbarg);
    259  1.1  christos 
    260  1.1  christos static void
    261  1.5  christos http_do_bio_async(isc_nm_http_session_t *session);
    262  1.5  christos 
    263  1.5  christos static void
    264  1.1  christos failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
    265  1.1  christos 			  isc_nm_http_session_t *session);
    266  1.1  christos 
    267  1.1  christos static void
    268  1.1  christos client_call_failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
    269  1.1  christos 
    270  1.1  christos static void
    271  1.1  christos server_call_failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
    272  1.1  christos 
    273  1.1  christos static void
    274  1.1  christos failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
    275  1.1  christos 
    276  1.1  christos static isc_result_t
    277  1.1  christos server_send_error_response(const isc_http_error_responses_t error,
    278  1.1  christos 			   nghttp2_session *ngsession, isc_nmsocket_t *socket);
    279  1.1  christos 
    280  1.1  christos static isc_result_t
    281  1.1  christos client_send(isc_nmhandle_t *handle, const isc_region_t *region);
    282  1.1  christos 
    283  1.1  christos static void
    284  1.1  christos finish_http_session(isc_nm_http_session_t *session);
    285  1.1  christos 
    286  1.1  christos static void
    287  1.1  christos http_transpost_tcp_nodelay(isc_nmhandle_t *transphandle);
    288  1.1  christos 
    289  1.1  christos static void
    290  1.1  christos call_pending_callbacks(isc__nm_http_pending_callbacks_t pending_callbacks,
    291  1.1  christos 		       isc_result_t result);
    292  1.1  christos 
    293  1.1  christos static void
    294  1.3  christos server_call_cb(isc_nmsocket_t *socket, const isc_result_t result,
    295  1.3  christos 	       isc_region_t *data);
    296  1.1  christos 
    297  1.1  christos static isc_nm_httphandler_t *
    298  1.1  christos http_endpoints_find(const char *request_path,
    299  1.1  christos 		    const isc_nm_http_endpoints_t *restrict eps);
    300  1.1  christos 
    301  1.1  christos static void
    302  1.1  christos http_init_listener_endpoints(isc_nmsocket_t *listener,
    303  1.1  christos 			     isc_nm_http_endpoints_t *epset);
    304  1.1  christos 
    305  1.1  christos static void
    306  1.1  christos http_cleanup_listener_endpoints(isc_nmsocket_t *listener);
    307  1.1  christos 
    308  1.1  christos static isc_nm_http_endpoints_t *
    309  1.1  christos http_get_listener_endpoints(isc_nmsocket_t *listener, const int tid);
    310  1.1  christos 
    311  1.4  christos static void
    312  1.4  christos http_initsocket(isc_nmsocket_t *sock);
    313  1.4  christos 
    314  1.1  christos static bool
    315  1.1  christos http_session_active(isc_nm_http_session_t *session) {
    316  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
    317  1.4  christos 	return !session->closed && !session->closing;
    318  1.1  christos }
    319  1.1  christos 
    320  1.1  christos static void *
    321  1.1  christos http_malloc(size_t sz, isc_mem_t *mctx) {
    322  1.4  christos 	return isc_mem_allocate(mctx, sz);
    323  1.1  christos }
    324  1.1  christos 
    325  1.1  christos static void *
    326  1.1  christos http_calloc(size_t n, size_t sz, isc_mem_t *mctx) {
    327  1.4  christos 	return isc_mem_callocate(mctx, n, sz);
    328  1.1  christos }
    329  1.1  christos 
    330  1.1  christos static void *
    331  1.1  christos http_realloc(void *p, size_t newsz, isc_mem_t *mctx) {
    332  1.4  christos 	return isc_mem_reallocate(mctx, p, newsz);
    333  1.1  christos }
    334  1.1  christos 
    335  1.1  christos static void
    336  1.1  christos http_free(void *p, isc_mem_t *mctx) {
    337  1.1  christos 	if (p == NULL) { /* as standard free() behaves */
    338  1.1  christos 		return;
    339  1.1  christos 	}
    340  1.1  christos 	isc_mem_free(mctx, p);
    341  1.1  christos }
    342  1.1  christos 
    343  1.1  christos static void
    344  1.1  christos init_nghttp2_mem(isc_mem_t *mctx, nghttp2_mem *mem) {
    345  1.1  christos 	*mem = (nghttp2_mem){ .malloc = (nghttp2_malloc)http_malloc,
    346  1.1  christos 			      .calloc = (nghttp2_calloc)http_calloc,
    347  1.1  christos 			      .realloc = (nghttp2_realloc)http_realloc,
    348  1.1  christos 			      .free = (nghttp2_free)http_free,
    349  1.1  christos 			      .mem_user_data = mctx };
    350  1.1  christos }
    351  1.1  christos 
    352  1.1  christos static void
    353  1.1  christos new_session(isc_mem_t *mctx, isc_tlsctx_t *tctx,
    354  1.1  christos 	    isc_nm_http_session_t **sessionp) {
    355  1.1  christos 	isc_nm_http_session_t *session = NULL;
    356  1.1  christos 
    357  1.1  christos 	REQUIRE(sessionp != NULL && *sessionp == NULL);
    358  1.1  christos 	REQUIRE(mctx != NULL);
    359  1.1  christos 
    360  1.1  christos 	session = isc_mem_get(mctx, sizeof(isc_nm_http_session_t));
    361  1.1  christos 	*session = (isc_nm_http_session_t){ .magic = HTTP2_SESSION_MAGIC,
    362  1.1  christos 					    .tlsctx = tctx };
    363  1.1  christos 	isc_refcount_init(&session->references, 1);
    364  1.1  christos 	isc_mem_attach(mctx, &session->mctx);
    365  1.1  christos 	ISC_LIST_INIT(session->cstreams);
    366  1.1  christos 	ISC_LIST_INIT(session->sstreams);
    367  1.1  christos 	ISC_LIST_INIT(session->pending_write_callbacks);
    368  1.1  christos 
    369  1.1  christos 	*sessionp = session;
    370  1.1  christos }
    371  1.1  christos 
    372  1.1  christos void
    373  1.1  christos isc__nm_httpsession_attach(isc_nm_http_session_t *source,
    374  1.1  christos 			   isc_nm_http_session_t **targetp) {
    375  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(source));
    376  1.1  christos 	REQUIRE(targetp != NULL && *targetp == NULL);
    377  1.1  christos 
    378  1.1  christos 	isc_refcount_increment(&source->references);
    379  1.1  christos 
    380  1.1  christos 	*targetp = source;
    381  1.1  christos }
    382  1.1  christos 
    383  1.1  christos void
    384  1.1  christos isc__nm_httpsession_detach(isc_nm_http_session_t **sessionp) {
    385  1.1  christos 	isc_nm_http_session_t *session = NULL;
    386  1.1  christos 
    387  1.1  christos 	REQUIRE(sessionp != NULL);
    388  1.1  christos 
    389  1.1  christos 	session = *sessionp;
    390  1.1  christos 	*sessionp = NULL;
    391  1.1  christos 
    392  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
    393  1.1  christos 
    394  1.1  christos 	if (isc_refcount_decrement(&session->references) > 1) {
    395  1.1  christos 		return;
    396  1.1  christos 	}
    397  1.1  christos 
    398  1.1  christos 	finish_http_session(session);
    399  1.1  christos 
    400  1.1  christos 	INSIST(ISC_LIST_EMPTY(session->sstreams));
    401  1.1  christos 	INSIST(ISC_LIST_EMPTY(session->cstreams));
    402  1.1  christos 
    403  1.1  christos 	if (session->ngsession != NULL) {
    404  1.1  christos 		nghttp2_session_del(session->ngsession);
    405  1.1  christos 		session->ngsession = NULL;
    406  1.1  christos 	}
    407  1.1  christos 
    408  1.1  christos 	if (session->buf != NULL) {
    409  1.1  christos 		isc_buffer_free(&session->buf);
    410  1.1  christos 	}
    411  1.1  christos 
    412  1.1  christos 	/* We need an acquire memory barrier here */
    413  1.1  christos 	(void)isc_refcount_current(&session->references);
    414  1.1  christos 
    415  1.1  christos 	session->magic = 0;
    416  1.1  christos 	isc_mem_putanddetach(&session->mctx, session,
    417  1.1  christos 			     sizeof(isc_nm_http_session_t));
    418  1.1  christos }
    419  1.1  christos 
    420  1.4  christos isc_nmhandle_t *
    421  1.4  christos isc__nm_httpsession_handle(isc_nm_http_session_t *session) {
    422  1.4  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
    423  1.4  christos 
    424  1.4  christos 	return session->handle;
    425  1.4  christos }
    426  1.4  christos 
    427  1.1  christos static http_cstream_t *
    428  1.1  christos find_http_cstream(int32_t stream_id, isc_nm_http_session_t *session) {
    429  1.1  christos 	http_cstream_t *cstream = NULL;
    430  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
    431  1.1  christos 
    432  1.1  christos 	if (ISC_LIST_EMPTY(session->cstreams)) {
    433  1.4  christos 		return NULL;
    434  1.1  christos 	}
    435  1.1  christos 
    436  1.1  christos 	for (cstream = ISC_LIST_HEAD(session->cstreams); cstream != NULL;
    437  1.1  christos 	     cstream = ISC_LIST_NEXT(cstream, link))
    438  1.1  christos 	{
    439  1.1  christos 		if (cstream->stream_id == stream_id) {
    440  1.1  christos 			break;
    441  1.1  christos 		}
    442  1.1  christos 	}
    443  1.1  christos 
    444  1.1  christos 	/* LRU-like behaviour */
    445  1.1  christos 	if (cstream && ISC_LIST_HEAD(session->cstreams) != cstream) {
    446  1.1  christos 		ISC_LIST_UNLINK(session->cstreams, cstream, link);
    447  1.1  christos 		ISC_LIST_PREPEND(session->cstreams, cstream, link);
    448  1.1  christos 	}
    449  1.1  christos 
    450  1.4  christos 	return cstream;
    451  1.1  christos }
    452  1.1  christos 
    453  1.1  christos static isc_result_t
    454  1.1  christos new_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) {
    455  1.4  christos 	isc_mem_t *mctx = sock->worker->mctx;
    456  1.1  christos 	const char *uri = NULL;
    457  1.1  christos 	bool post;
    458  1.1  christos 	http_cstream_t *stream = NULL;
    459  1.1  christos 	isc_result_t result;
    460  1.1  christos 
    461  1.4  christos 	uri = sock->h2->session->handle->sock->h2->connect.uri;
    462  1.4  christos 	post = sock->h2->session->handle->sock->h2->connect.post;
    463  1.1  christos 
    464  1.1  christos 	stream = isc_mem_get(mctx, sizeof(http_cstream_t));
    465  1.1  christos 	*stream = (http_cstream_t){ .stream_id = -1,
    466  1.1  christos 				    .post = post,
    467  1.1  christos 				    .uri = isc_mem_strdup(mctx, uri) };
    468  1.1  christos 	ISC_LINK_INIT(stream, link);
    469  1.1  christos 
    470  1.1  christos 	result = isc_url_parse(stream->uri, strlen(stream->uri), 0,
    471  1.1  christos 			       &stream->up);
    472  1.1  christos 	if (result != ISC_R_SUCCESS) {
    473  1.1  christos 		isc_mem_free(mctx, stream->uri);
    474  1.1  christos 		isc_mem_put(mctx, stream, sizeof(http_cstream_t));
    475  1.4  christos 		return result;
    476  1.1  christos 	}
    477  1.1  christos 
    478  1.1  christos 	isc__nmsocket_attach(sock, &stream->httpsock);
    479  1.1  christos 	stream->authoritylen = stream->up.field_data[ISC_UF_HOST].len;
    480  1.1  christos 	stream->authority = isc_mem_get(mctx, stream->authoritylen + AUTHEXTRA);
    481  1.1  christos 	memmove(stream->authority, &uri[stream->up.field_data[ISC_UF_HOST].off],
    482  1.1  christos 		stream->up.field_data[ISC_UF_HOST].len);
    483  1.1  christos 
    484  1.1  christos 	if (stream->up.field_set & (1 << ISC_UF_PORT)) {
    485  1.1  christos 		stream->authoritylen += (size_t)snprintf(
    486  1.1  christos 			stream->authority +
    487  1.1  christos 				stream->up.field_data[ISC_UF_HOST].len,
    488  1.1  christos 			AUTHEXTRA, ":%u", stream->up.port);
    489  1.1  christos 	}
    490  1.1  christos 
    491  1.1  christos 	/* If we don't have path in URI, we use "/" as path. */
    492  1.1  christos 	stream->pathlen = 1;
    493  1.1  christos 	if (stream->up.field_set & (1 << ISC_UF_PATH)) {
    494  1.1  christos 		stream->pathlen = stream->up.field_data[ISC_UF_PATH].len;
    495  1.1  christos 	}
    496  1.1  christos 	if (stream->up.field_set & (1 << ISC_UF_QUERY)) {
    497  1.1  christos 		/* +1 for '?' character */
    498  1.1  christos 		stream->pathlen +=
    499  1.1  christos 			(size_t)(stream->up.field_data[ISC_UF_QUERY].len + 1);
    500  1.1  christos 	}
    501  1.1  christos 
    502  1.1  christos 	stream->path = isc_mem_get(mctx, stream->pathlen);
    503  1.1  christos 	if (stream->up.field_set & (1 << ISC_UF_PATH)) {
    504  1.1  christos 		memmove(stream->path,
    505  1.1  christos 			&uri[stream->up.field_data[ISC_UF_PATH].off],
    506  1.1  christos 			stream->up.field_data[ISC_UF_PATH].len);
    507  1.1  christos 	} else {
    508  1.1  christos 		stream->path[0] = '/';
    509  1.1  christos 	}
    510  1.1  christos 
    511  1.1  christos 	if (stream->up.field_set & (1 << ISC_UF_QUERY)) {
    512  1.1  christos 		stream->path[stream->pathlen -
    513  1.1  christos 			     stream->up.field_data[ISC_UF_QUERY].len - 1] = '?';
    514  1.1  christos 		memmove(stream->path + stream->pathlen -
    515  1.1  christos 				stream->up.field_data[ISC_UF_QUERY].len,
    516  1.1  christos 			&uri[stream->up.field_data[ISC_UF_QUERY].off],
    517  1.1  christos 			stream->up.field_data[ISC_UF_QUERY].len);
    518  1.1  christos 	}
    519  1.1  christos 
    520  1.1  christos 	isc_buffer_allocate(mctx, &stream->rbuf,
    521  1.1  christos 			    INITIAL_DNS_MESSAGE_BUFFER_SIZE);
    522  1.1  christos 
    523  1.4  christos 	ISC_LIST_PREPEND(sock->h2->session->cstreams, stream, link);
    524  1.1  christos 	*streamp = stream;
    525  1.1  christos 
    526  1.4  christos 	return ISC_R_SUCCESS;
    527  1.1  christos }
    528  1.1  christos 
    529  1.1  christos static void
    530  1.1  christos put_http_cstream(isc_mem_t *mctx, http_cstream_t *stream) {
    531  1.1  christos 	isc_mem_put(mctx, stream->path, stream->pathlen);
    532  1.1  christos 	isc_mem_put(mctx, stream->authority,
    533  1.1  christos 		    stream->up.field_data[ISC_UF_HOST].len + AUTHEXTRA);
    534  1.1  christos 	isc_mem_free(mctx, stream->uri);
    535  1.1  christos 	if (stream->GET_path != NULL) {
    536  1.1  christos 		isc_mem_free(mctx, stream->GET_path);
    537  1.1  christos 		stream->GET_path = NULL;
    538  1.1  christos 		stream->GET_path_len = 0;
    539  1.1  christos 	}
    540  1.1  christos 
    541  1.1  christos 	if (stream->postdata != NULL) {
    542  1.1  christos 		INSIST(stream->post);
    543  1.1  christos 		isc_buffer_free(&stream->postdata);
    544  1.1  christos 	}
    545  1.1  christos 
    546  1.4  christos 	if (stream == stream->httpsock->h2->connect.cstream) {
    547  1.4  christos 		stream->httpsock->h2->connect.cstream = NULL;
    548  1.1  christos 	}
    549  1.1  christos 	if (ISC_LINK_LINKED(stream, link)) {
    550  1.4  christos 		ISC_LIST_UNLINK(stream->httpsock->h2->session->cstreams, stream,
    551  1.1  christos 				link);
    552  1.1  christos 	}
    553  1.1  christos 	isc__nmsocket_detach(&stream->httpsock);
    554  1.1  christos 
    555  1.1  christos 	isc_buffer_free(&stream->rbuf);
    556  1.1  christos 	isc_mem_put(mctx, stream, sizeof(http_cstream_t));
    557  1.1  christos }
    558  1.1  christos 
    559  1.1  christos static void
    560  1.1  christos finish_http_session(isc_nm_http_session_t *session) {
    561  1.1  christos 	if (session->closed) {
    562  1.1  christos 		return;
    563  1.1  christos 	}
    564  1.1  christos 
    565  1.1  christos 	if (session->handle != NULL) {
    566  1.1  christos 		if (!session->closed) {
    567  1.1  christos 			session->closed = true;
    568  1.4  christos 			session->reading = false;
    569  1.5  christos 			isc_nm_read_stop(session->handle);
    570  1.5  christos 			isc__nmsocket_timer_stop(session->handle->sock);
    571  1.4  christos 			isc_nmhandle_close(session->handle);
    572  1.1  christos 		}
    573  1.1  christos 
    574  1.5  christos 		/*
    575  1.5  christos 		 * Free any unprocessed incoming data in order to not process
    576  1.5  christos 		 * it during indirect calls to http_do_bio() that might happen
    577  1.5  christos 		 * when calling the failed callbacks.
    578  1.5  christos 		 */
    579  1.5  christos 		if (session->buf != NULL) {
    580  1.5  christos 			isc_buffer_free(&session->buf);
    581  1.5  christos 		}
    582  1.5  christos 
    583  1.1  christos 		if (session->client) {
    584  1.1  christos 			client_call_failed_read_cb(ISC_R_UNEXPECTED, session);
    585  1.1  christos 		} else {
    586  1.1  christos 			server_call_failed_read_cb(ISC_R_UNEXPECTED, session);
    587  1.1  christos 		}
    588  1.1  christos 
    589  1.1  christos 		call_pending_callbacks(session->pending_write_callbacks,
    590  1.1  christos 				       ISC_R_UNEXPECTED);
    591  1.1  christos 		ISC_LIST_INIT(session->pending_write_callbacks);
    592  1.1  christos 
    593  1.1  christos 		if (session->pending_write_data != NULL) {
    594  1.1  christos 			isc_buffer_free(&session->pending_write_data);
    595  1.1  christos 		}
    596  1.1  christos 
    597  1.1  christos 		isc_nmhandle_detach(&session->handle);
    598  1.1  christos 	}
    599  1.1  christos 
    600  1.1  christos 	if (session->client_httphandle != NULL) {
    601  1.1  christos 		isc_nmhandle_detach(&session->client_httphandle);
    602  1.1  christos 	}
    603  1.1  christos 
    604  1.1  christos 	INSIST(ISC_LIST_EMPTY(session->cstreams));
    605  1.1  christos 
    606  1.1  christos 	/* detach from server socket */
    607  1.1  christos 	if (session->serversocket != NULL) {
    608  1.1  christos 		isc__nmsocket_detach(&session->serversocket);
    609  1.1  christos 	}
    610  1.1  christos 	session->closed = true;
    611  1.1  christos }
    612  1.1  christos 
    613  1.1  christos static int
    614  1.1  christos on_client_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data,
    615  1.1  christos 				   size_t len, isc_nm_http_session_t *session) {
    616  1.1  christos 	http_cstream_t *cstream = find_http_cstream(stream_id, session);
    617  1.1  christos 
    618  1.1  christos 	if (cstream != NULL) {
    619  1.1  christos 		size_t new_rbufsize = len;
    620  1.1  christos 		INSIST(cstream->rbuf != NULL);
    621  1.1  christos 		new_rbufsize += isc_buffer_usedlength(cstream->rbuf);
    622  1.1  christos 		if (new_rbufsize <= MAX_DNS_MESSAGE_SIZE &&
    623  1.1  christos 		    new_rbufsize <= cstream->response_status.content_length)
    624  1.1  christos 		{
    625  1.1  christos 			isc_buffer_putmem(cstream->rbuf, data, len);
    626  1.1  christos 		} else {
    627  1.4  christos 			return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
    628  1.1  christos 		}
    629  1.1  christos 	} else {
    630  1.4  christos 		return NGHTTP2_ERR_CALLBACK_FAILURE;
    631  1.1  christos 	}
    632  1.1  christos 
    633  1.4  christos 	return 0;
    634  1.1  christos }
    635  1.1  christos 
    636  1.1  christos static int
    637  1.1  christos on_server_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data,
    638  1.1  christos 				   size_t len, isc_nm_http_session_t *session) {
    639  1.1  christos 	isc_nmsocket_h2_t *h2 = ISC_LIST_HEAD(session->sstreams);
    640  1.4  christos 	isc_mem_t *mctx = h2->psock->worker->mctx;
    641  1.4  christos 
    642  1.1  christos 	while (h2 != NULL) {
    643  1.1  christos 		if (stream_id == h2->stream_id) {
    644  1.1  christos 			if (isc_buffer_base(&h2->rbuf) == NULL) {
    645  1.1  christos 				isc_buffer_init(
    646  1.1  christos 					&h2->rbuf,
    647  1.4  christos 					isc_mem_allocate(mctx,
    648  1.1  christos 							 h2->content_length),
    649  1.1  christos 					MAX_DNS_MESSAGE_SIZE);
    650  1.1  christos 			}
    651  1.1  christos 			size_t new_bufsize = isc_buffer_usedlength(&h2->rbuf) +
    652  1.1  christos 					     len;
    653  1.1  christos 			if (new_bufsize <= MAX_DNS_MESSAGE_SIZE &&
    654  1.1  christos 			    new_bufsize <= h2->content_length)
    655  1.1  christos 			{
    656  1.5  christos 				session->processed_useful_data += len;
    657  1.1  christos 				isc_buffer_putmem(&h2->rbuf, data, len);
    658  1.1  christos 				break;
    659  1.1  christos 			}
    660  1.1  christos 
    661  1.4  christos 			return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
    662  1.1  christos 		}
    663  1.1  christos 		h2 = ISC_LIST_NEXT(h2, link);
    664  1.1  christos 	}
    665  1.1  christos 	if (h2 == NULL) {
    666  1.4  christos 		return NGHTTP2_ERR_CALLBACK_FAILURE;
    667  1.1  christos 	}
    668  1.1  christos 
    669  1.4  christos 	return 0;
    670  1.1  christos }
    671  1.1  christos 
    672  1.1  christos static int
    673  1.1  christos on_data_chunk_recv_callback(nghttp2_session *ngsession, uint8_t flags,
    674  1.1  christos 			    int32_t stream_id, const uint8_t *data, size_t len,
    675  1.1  christos 			    void *user_data) {
    676  1.1  christos 	isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
    677  1.1  christos 	int rv;
    678  1.1  christos 
    679  1.1  christos 	UNUSED(ngsession);
    680  1.1  christos 	UNUSED(flags);
    681  1.1  christos 
    682  1.1  christos 	if (session->client) {
    683  1.1  christos 		rv = on_client_data_chunk_recv_callback(stream_id, data, len,
    684  1.1  christos 							session);
    685  1.1  christos 	} else {
    686  1.1  christos 		rv = on_server_data_chunk_recv_callback(stream_id, data, len,
    687  1.1  christos 							session);
    688  1.1  christos 	}
    689  1.1  christos 
    690  1.4  christos 	return rv;
    691  1.1  christos }
    692  1.1  christos 
    693  1.1  christos static void
    694  1.1  christos call_unlink_cstream_readcb(http_cstream_t *cstream,
    695  1.1  christos 			   isc_nm_http_session_t *session,
    696  1.1  christos 			   isc_result_t result) {
    697  1.1  christos 	isc_region_t read_data;
    698  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
    699  1.1  christos 	REQUIRE(cstream != NULL);
    700  1.1  christos 	ISC_LIST_UNLINK(session->cstreams, cstream, link);
    701  1.1  christos 	INSIST(VALID_NMHANDLE(session->client_httphandle));
    702  1.1  christos 	isc_buffer_usedregion(cstream->rbuf, &read_data);
    703  1.1  christos 	cstream->read_cb(session->client_httphandle, result, &read_data,
    704  1.1  christos 			 cstream->read_cbarg);
    705  1.5  christos 	if (result == ISC_R_SUCCESS) {
    706  1.5  christos 		isc__nmsocket_timer_restart(session->handle->sock);
    707  1.5  christos 	}
    708  1.1  christos 	put_http_cstream(session->mctx, cstream);
    709  1.1  christos }
    710  1.1  christos 
    711  1.1  christos static int
    712  1.1  christos on_client_stream_close_callback(int32_t stream_id,
    713  1.1  christos 				isc_nm_http_session_t *session) {
    714  1.1  christos 	http_cstream_t *cstream = find_http_cstream(stream_id, session);
    715  1.1  christos 
    716  1.1  christos 	if (cstream != NULL) {
    717  1.1  christos 		isc_result_t result =
    718  1.1  christos 			SUCCESSFUL_HTTP_STATUS(cstream->response_status.code)
    719  1.1  christos 				? ISC_R_SUCCESS
    720  1.1  christos 				: ISC_R_FAILURE;
    721  1.1  christos 		call_unlink_cstream_readcb(cstream, session, result);
    722  1.1  christos 		if (ISC_LIST_EMPTY(session->cstreams)) {
    723  1.1  christos 			int rv = 0;
    724  1.1  christos 			rv = nghttp2_session_terminate_session(
    725  1.1  christos 				session->ngsession, NGHTTP2_NO_ERROR);
    726  1.1  christos 			if (rv != 0) {
    727  1.4  christos 				return rv;
    728  1.1  christos 			}
    729  1.1  christos 			/* Mark the session as closing one to finish it on a
    730  1.1  christos 			 * subsequent call to http_do_bio() */
    731  1.1  christos 			session->closing = true;
    732  1.1  christos 		}
    733  1.1  christos 	} else {
    734  1.4  christos 		return NGHTTP2_ERR_CALLBACK_FAILURE;
    735  1.1  christos 	}
    736  1.1  christos 
    737  1.4  christos 	return 0;
    738  1.1  christos }
    739  1.1  christos 
    740  1.1  christos static int
    741  1.1  christos on_server_stream_close_callback(int32_t stream_id,
    742  1.1  christos 				isc_nm_http_session_t *session) {
    743  1.1  christos 	isc_nmsocket_t *sock = nghttp2_session_get_stream_user_data(
    744  1.1  christos 		session->ngsession, stream_id);
    745  1.1  christos 	int rv = 0;
    746  1.1  christos 
    747  1.4  christos 	ISC_LIST_UNLINK(session->sstreams, sock->h2, link);
    748  1.1  christos 	session->nsstreams--;
    749  1.5  christos 	if (sock->h2->request_received) {
    750  1.5  christos 		session->submitted++;
    751  1.5  christos 	}
    752  1.1  christos 
    753  1.1  christos 	/*
    754  1.1  christos 	 * By making a call to isc__nmsocket_prep_destroy(), we ensure that
    755  1.1  christos 	 * the socket gets marked as inactive, allowing the HTTP/2 data
    756  1.1  christos 	 * associated with it to be properly disposed of eventually.
    757  1.1  christos 	 *
    758  1.1  christos 	 * An HTTP/2 stream socket will normally be marked as inactive in
    759  1.1  christos 	 * the normal course of operation. However, when browsers terminate
    760  1.1  christos 	 * HTTP/2 streams prematurely (e.g. by sending RST_STREAM),
    761  1.1  christos 	 * corresponding sockets can remain marked as active, retaining
    762  1.1  christos 	 * references to the HTTP/2 data (most notably the session objects),
    763  1.1  christos 	 * preventing them from being correctly freed and leading to BIND
    764  1.1  christos 	 * hanging on shutdown.  Calling isc__nmsocket_prep_destroy()
    765  1.1  christos 	 * ensures that this will not happen.
    766  1.1  christos 	 */
    767  1.1  christos 	isc__nmsocket_prep_destroy(sock);
    768  1.1  christos 	isc__nmsocket_detach(&sock);
    769  1.4  christos 	return rv;
    770  1.1  christos }
    771  1.1  christos 
    772  1.1  christos static int
    773  1.1  christos on_stream_close_callback(nghttp2_session *ngsession, int32_t stream_id,
    774  1.1  christos 			 uint32_t error_code, void *user_data) {
    775  1.1  christos 	isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
    776  1.1  christos 	int rv = 0;
    777  1.1  christos 
    778  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
    779  1.1  christos 	REQUIRE(session->ngsession == ngsession);
    780  1.1  christos 
    781  1.1  christos 	UNUSED(error_code);
    782  1.1  christos 
    783  1.1  christos 	if (session->client) {
    784  1.1  christos 		rv = on_client_stream_close_callback(stream_id, session);
    785  1.1  christos 	} else {
    786  1.1  christos 		rv = on_server_stream_close_callback(stream_id, session);
    787  1.1  christos 	}
    788  1.1  christos 
    789  1.4  christos 	return rv;
    790  1.1  christos }
    791  1.1  christos 
    792  1.1  christos static bool
    793  1.1  christos client_handle_status_header(http_cstream_t *cstream, const uint8_t *value,
    794  1.1  christos 			    const size_t valuelen) {
    795  1.1  christos 	char tmp[32] = { 0 };
    796  1.1  christos 	const size_t tmplen = sizeof(tmp) - 1;
    797  1.1  christos 
    798  1.1  christos 	strncpy(tmp, (const char *)value, ISC_MIN(tmplen, valuelen));
    799  1.1  christos 	cstream->response_status.code = strtoul(tmp, NULL, 10);
    800  1.1  christos 
    801  1.1  christos 	if (SUCCESSFUL_HTTP_STATUS(cstream->response_status.code)) {
    802  1.4  christos 		return true;
    803  1.1  christos 	}
    804  1.1  christos 
    805  1.4  christos 	return false;
    806  1.1  christos }
    807  1.1  christos 
    808  1.1  christos static bool
    809  1.1  christos client_handle_content_length_header(http_cstream_t *cstream,
    810  1.1  christos 				    const uint8_t *value,
    811  1.1  christos 				    const size_t valuelen) {
    812  1.1  christos 	char tmp[32] = { 0 };
    813  1.1  christos 	const size_t tmplen = sizeof(tmp) - 1;
    814  1.1  christos 
    815  1.1  christos 	strncpy(tmp, (const char *)value, ISC_MIN(tmplen, valuelen));
    816  1.1  christos 	cstream->response_status.content_length = strtoul(tmp, NULL, 10);
    817  1.1  christos 
    818  1.1  christos 	if (cstream->response_status.content_length == 0 ||
    819  1.1  christos 	    cstream->response_status.content_length > MAX_DNS_MESSAGE_SIZE)
    820  1.1  christos 	{
    821  1.4  christos 		return false;
    822  1.1  christos 	}
    823  1.1  christos 
    824  1.4  christos 	return true;
    825  1.1  christos }
    826  1.1  christos 
    827  1.1  christos static bool
    828  1.1  christos client_handle_content_type_header(http_cstream_t *cstream, const uint8_t *value,
    829  1.1  christos 				  const size_t valuelen) {
    830  1.1  christos 	const char type_dns_message[] = DNS_MEDIA_TYPE;
    831  1.1  christos 	const size_t len = sizeof(type_dns_message) - 1;
    832  1.1  christos 
    833  1.1  christos 	UNUSED(valuelen);
    834  1.1  christos 
    835  1.1  christos 	if (strncasecmp((const char *)value, type_dns_message, len) == 0) {
    836  1.1  christos 		cstream->response_status.content_type_valid = true;
    837  1.4  christos 		return true;
    838  1.1  christos 	}
    839  1.1  christos 
    840  1.4  christos 	return false;
    841  1.1  christos }
    842  1.1  christos 
    843  1.1  christos static int
    844  1.1  christos client_on_header_callback(nghttp2_session *ngsession,
    845  1.1  christos 			  const nghttp2_frame *frame, const uint8_t *name,
    846  1.1  christos 			  size_t namelen, const uint8_t *value, size_t valuelen,
    847  1.1  christos 			  uint8_t flags, void *user_data) {
    848  1.1  christos 	isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
    849  1.1  christos 	http_cstream_t *cstream = NULL;
    850  1.1  christos 	const char status[] = ":status";
    851  1.1  christos 	const char content_length[] = "Content-Length";
    852  1.1  christos 	const char content_type[] = "Content-Type";
    853  1.1  christos 	bool header_ok = true;
    854  1.1  christos 
    855  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
    856  1.1  christos 	REQUIRE(session->client);
    857  1.1  christos 
    858  1.1  christos 	UNUSED(flags);
    859  1.1  christos 	UNUSED(ngsession);
    860  1.1  christos 
    861  1.1  christos 	cstream = find_http_cstream(frame->hd.stream_id, session);
    862  1.1  christos 	if (cstream == NULL) {
    863  1.1  christos 		/*
    864  1.1  christos 		 * This could happen in two cases:
    865  1.1  christos 		 * - the server sent us bad data, or
    866  1.1  christos 		 * - we closed the session prematurely before receiving all
    867  1.1  christos 		 *   responses (i.e., because of a belated or partial response).
    868  1.1  christos 		 */
    869  1.4  christos 		return NGHTTP2_ERR_CALLBACK_FAILURE;
    870  1.1  christos 	}
    871  1.1  christos 
    872  1.1  christos 	INSIST(!ISC_LIST_EMPTY(session->cstreams));
    873  1.1  christos 
    874  1.1  christos 	switch (frame->hd.type) {
    875  1.1  christos 	case NGHTTP2_HEADERS:
    876  1.1  christos 		if (frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
    877  1.1  christos 			break;
    878  1.1  christos 		}
    879  1.1  christos 
    880  1.1  christos 		if (HEADER_MATCH(status, name, namelen)) {
    881  1.1  christos 			header_ok = client_handle_status_header(cstream, value,
    882  1.1  christos 								valuelen);
    883  1.1  christos 		} else if (HEADER_MATCH(content_length, name, namelen)) {
    884  1.1  christos 			header_ok = client_handle_content_length_header(
    885  1.1  christos 				cstream, value, valuelen);
    886  1.1  christos 		} else if (HEADER_MATCH(content_type, name, namelen)) {
    887  1.1  christos 			header_ok = client_handle_content_type_header(
    888  1.1  christos 				cstream, value, valuelen);
    889  1.1  christos 		}
    890  1.1  christos 		break;
    891  1.1  christos 	}
    892  1.1  christos 
    893  1.1  christos 	if (!header_ok) {
    894  1.4  christos 		return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
    895  1.1  christos 	}
    896  1.1  christos 
    897  1.4  christos 	return 0;
    898  1.1  christos }
    899  1.1  christos 
    900  1.1  christos static void
    901  1.1  christos initialize_nghttp2_client_session(isc_nm_http_session_t *session) {
    902  1.1  christos 	nghttp2_session_callbacks *callbacks = NULL;
    903  1.1  christos 	nghttp2_option *option = NULL;
    904  1.1  christos 	nghttp2_mem mem;
    905  1.1  christos 
    906  1.1  christos 	init_nghttp2_mem(session->mctx, &mem);
    907  1.1  christos 	RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
    908  1.1  christos 	RUNTIME_CHECK(nghttp2_option_new(&option) == 0);
    909  1.1  christos 
    910  1.1  christos #if NGHTTP2_VERSION_NUM >= (0x010c00)
    911  1.1  christos 	nghttp2_option_set_max_send_header_block_length(
    912  1.1  christos 		option, MAX_ALLOWED_DATA_IN_HEADERS);
    913  1.1  christos #endif
    914  1.1  christos 
    915  1.1  christos 	nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
    916  1.1  christos 		callbacks, on_data_chunk_recv_callback);
    917  1.1  christos 
    918  1.1  christos 	nghttp2_session_callbacks_set_on_stream_close_callback(
    919  1.1  christos 		callbacks, on_stream_close_callback);
    920  1.1  christos 
    921  1.1  christos 	nghttp2_session_callbacks_set_on_header_callback(
    922  1.1  christos 		callbacks, client_on_header_callback);
    923  1.1  christos 
    924  1.1  christos 	RUNTIME_CHECK(nghttp2_session_client_new3(&session->ngsession,
    925  1.1  christos 						  callbacks, session, option,
    926  1.1  christos 						  &mem) == 0);
    927  1.1  christos 
    928  1.1  christos 	nghttp2_option_del(option);
    929  1.1  christos 	nghttp2_session_callbacks_del(callbacks);
    930  1.1  christos }
    931  1.1  christos 
    932  1.1  christos static bool
    933  1.1  christos send_client_connection_header(isc_nm_http_session_t *session) {
    934  1.1  christos 	nghttp2_settings_entry iv[] = { { NGHTTP2_SETTINGS_ENABLE_PUSH, 0 } };
    935  1.1  christos 	int rv;
    936  1.1  christos 
    937  1.1  christos 	rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv,
    938  1.1  christos 				     sizeof(iv) / sizeof(iv[0]));
    939  1.1  christos 	if (rv != 0) {
    940  1.4  christos 		return false;
    941  1.1  christos 	}
    942  1.1  christos 
    943  1.4  christos 	return true;
    944  1.1  christos }
    945  1.1  christos 
    946  1.3  christos #define MAKE_NV(NAME, VALUE, VALUELEN)                                 \
    947  1.3  christos 	{ (uint8_t *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \
    948  1.3  christos 	  sizeof(NAME) - 1, VALUELEN, NGHTTP2_NV_FLAG_NONE }
    949  1.3  christos 
    950  1.3  christos #define MAKE_NV2(NAME, VALUE)                                          \
    951  1.3  christos 	{ (uint8_t *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \
    952  1.3  christos 	  sizeof(NAME) - 1, sizeof(VALUE) - 1, NGHTTP2_NV_FLAG_NONE }
    953  1.1  christos 
    954  1.1  christos static ssize_t
    955  1.1  christos client_read_callback(nghttp2_session *ngsession, int32_t stream_id,
    956  1.1  christos 		     uint8_t *buf, size_t length, uint32_t *data_flags,
    957  1.1  christos 		     nghttp2_data_source *source, void *user_data) {
    958  1.1  christos 	isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
    959  1.1  christos 	http_cstream_t *cstream = NULL;
    960  1.1  christos 
    961  1.1  christos 	REQUIRE(session->client);
    962  1.1  christos 	REQUIRE(!ISC_LIST_EMPTY(session->cstreams));
    963  1.1  christos 
    964  1.1  christos 	UNUSED(ngsession);
    965  1.1  christos 	UNUSED(source);
    966  1.1  christos 
    967  1.1  christos 	cstream = find_http_cstream(stream_id, session);
    968  1.1  christos 	if (!cstream || cstream->stream_id != stream_id) {
    969  1.1  christos 		/* We haven't found the stream, so we are not reading */
    970  1.4  christos 		return NGHTTP2_ERR_CALLBACK_FAILURE;
    971  1.1  christos 	}
    972  1.1  christos 
    973  1.1  christos 	if (cstream->post) {
    974  1.1  christos 		size_t len = isc_buffer_remaininglength(cstream->postdata);
    975  1.1  christos 
    976  1.1  christos 		if (len > length) {
    977  1.1  christos 			len = length;
    978  1.1  christos 		}
    979  1.1  christos 
    980  1.1  christos 		if (len > 0) {
    981  1.1  christos 			memmove(buf, isc_buffer_current(cstream->postdata),
    982  1.1  christos 				len);
    983  1.1  christos 			isc_buffer_forward(cstream->postdata, len);
    984  1.1  christos 		}
    985  1.1  christos 
    986  1.1  christos 		if (isc_buffer_remaininglength(cstream->postdata) == 0) {
    987  1.1  christos 			*data_flags |= NGHTTP2_DATA_FLAG_EOF;
    988  1.1  christos 		}
    989  1.1  christos 
    990  1.4  christos 		return len;
    991  1.1  christos 	} else {
    992  1.1  christos 		*data_flags |= NGHTTP2_DATA_FLAG_EOF;
    993  1.4  christos 		return 0;
    994  1.1  christos 	}
    995  1.1  christos 
    996  1.4  christos 	return 0;
    997  1.1  christos }
    998  1.1  christos 
    999  1.1  christos /*
   1000  1.1  christos  * Send HTTP request to the remote peer.
   1001  1.1  christos  */
   1002  1.1  christos static isc_result_t
   1003  1.1  christos client_submit_request(isc_nm_http_session_t *session, http_cstream_t *stream) {
   1004  1.1  christos 	int32_t stream_id;
   1005  1.1  christos 	char *uri = stream->uri;
   1006  1.1  christos 	isc_url_parser_t *up = &stream->up;
   1007  1.1  christos 	nghttp2_data_provider dp;
   1008  1.1  christos 
   1009  1.1  christos 	if (stream->post) {
   1010  1.1  christos 		char p[64];
   1011  1.1  christos 		snprintf(p, sizeof(p), "%u",
   1012  1.1  christos 			 isc_buffer_usedlength(stream->postdata));
   1013  1.1  christos 		nghttp2_nv hdrs[] = {
   1014  1.1  christos 			MAKE_NV2(":method", "POST"),
   1015  1.1  christos 			MAKE_NV(":scheme",
   1016  1.1  christos 				&uri[up->field_data[ISC_UF_SCHEMA].off],
   1017  1.1  christos 				up->field_data[ISC_UF_SCHEMA].len),
   1018  1.1  christos 			MAKE_NV(":authority", stream->authority,
   1019  1.1  christos 				stream->authoritylen),
   1020  1.1  christos 			MAKE_NV(":path", stream->path, stream->pathlen),
   1021  1.1  christos 			MAKE_NV2("content-type", DNS_MEDIA_TYPE),
   1022  1.1  christos 			MAKE_NV2("accept", DNS_MEDIA_TYPE),
   1023  1.1  christos 			MAKE_NV("content-length", p, strlen(p)),
   1024  1.1  christos 			MAKE_NV2("cache-control", DEFAULT_CACHE_CONTROL)
   1025  1.1  christos 		};
   1026  1.1  christos 
   1027  1.1  christos 		dp = (nghttp2_data_provider){ .read_callback =
   1028  1.1  christos 						      client_read_callback };
   1029  1.1  christos 		stream_id = nghttp2_submit_request(
   1030  1.1  christos 			session->ngsession, NULL, hdrs,
   1031  1.1  christos 			sizeof(hdrs) / sizeof(hdrs[0]), &dp, stream);
   1032  1.1  christos 	} else {
   1033  1.1  christos 		INSIST(stream->GET_path != NULL);
   1034  1.1  christos 		INSIST(stream->GET_path_len != 0);
   1035  1.1  christos 		nghttp2_nv hdrs[] = {
   1036  1.1  christos 			MAKE_NV2(":method", "GET"),
   1037  1.1  christos 			MAKE_NV(":scheme",
   1038  1.1  christos 				&uri[up->field_data[ISC_UF_SCHEMA].off],
   1039  1.1  christos 				up->field_data[ISC_UF_SCHEMA].len),
   1040  1.1  christos 			MAKE_NV(":authority", stream->authority,
   1041  1.1  christos 				stream->authoritylen),
   1042  1.1  christos 			MAKE_NV(":path", stream->GET_path,
   1043  1.1  christos 				stream->GET_path_len),
   1044  1.1  christos 			MAKE_NV2("accept", DNS_MEDIA_TYPE),
   1045  1.1  christos 			MAKE_NV2("cache-control", DEFAULT_CACHE_CONTROL)
   1046  1.1  christos 		};
   1047  1.1  christos 
   1048  1.1  christos 		dp = (nghttp2_data_provider){ .read_callback =
   1049  1.1  christos 						      client_read_callback };
   1050  1.1  christos 		stream_id = nghttp2_submit_request(
   1051  1.1  christos 			session->ngsession, NULL, hdrs,
   1052  1.1  christos 			sizeof(hdrs) / sizeof(hdrs[0]), &dp, stream);
   1053  1.1  christos 	}
   1054  1.1  christos 	if (stream_id < 0) {
   1055  1.4  christos 		return ISC_R_FAILURE;
   1056  1.1  christos 	}
   1057  1.1  christos 
   1058  1.1  christos 	stream->stream_id = stream_id;
   1059  1.1  christos 
   1060  1.4  christos 	return ISC_R_SUCCESS;
   1061  1.1  christos }
   1062  1.1  christos 
   1063  1.5  christos static inline size_t
   1064  1.5  christos http_in_flight_data_size(isc_nm_http_session_t *session) {
   1065  1.5  christos 	size_t in_flight = 0;
   1066  1.5  christos 
   1067  1.5  christos 	if (session->pending_write_data != NULL) {
   1068  1.5  christos 		in_flight += isc_buffer_usedlength(session->pending_write_data);
   1069  1.5  christos 	}
   1070  1.5  christos 
   1071  1.5  christos 	in_flight += session->data_in_flight;
   1072  1.5  christos 
   1073  1.5  christos 	return in_flight;
   1074  1.5  christos }
   1075  1.5  christos 
   1076  1.5  christos static ssize_t
   1077  1.5  christos http_process_input_data(isc_nm_http_session_t *session,
   1078  1.5  christos 			isc_buffer_t *input_data) {
   1079  1.5  christos 	ssize_t readlen = 0;
   1080  1.5  christos 	ssize_t processed = 0;
   1081  1.5  christos 	isc_region_t chunk = { 0 };
   1082  1.5  christos 	size_t before, after;
   1083  1.5  christos 	size_t i;
   1084  1.5  christos 
   1085  1.5  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
   1086  1.5  christos 	REQUIRE(input_data != NULL);
   1087  1.5  christos 
   1088  1.5  christos 	if (!http_session_active(session)) {
   1089  1.5  christos 		return 0;
   1090  1.5  christos 	}
   1091  1.5  christos 
   1092  1.5  christos 	/*
   1093  1.5  christos 	 * For clients that initiate request themselves just process
   1094  1.5  christos 	 * everything.
   1095  1.5  christos 	 */
   1096  1.5  christos 	if (session->client) {
   1097  1.5  christos 		isc_buffer_remainingregion(input_data, &chunk);
   1098  1.5  christos 		if (chunk.length == 0) {
   1099  1.5  christos 			return 0;
   1100  1.5  christos 		}
   1101  1.5  christos 
   1102  1.5  christos 		readlen = nghttp2_session_mem_recv(session->ngsession,
   1103  1.5  christos 						   chunk.base, chunk.length);
   1104  1.5  christos 
   1105  1.5  christos 		if (readlen >= 0) {
   1106  1.5  christos 			isc_buffer_forward(input_data, readlen);
   1107  1.5  christos 			session->processed_incoming_data += readlen;
   1108  1.5  christos 		}
   1109  1.5  christos 
   1110  1.5  christos 		return readlen;
   1111  1.5  christos 	}
   1112  1.5  christos 
   1113  1.5  christos 	/*
   1114  1.5  christos 	 * If no streams are created during processing, we might process
   1115  1.5  christos 	 * more than one chunk at a time. Still we should not overdo that
   1116  1.5  christos 	 * to avoid processing too much data at once as such behaviour is
   1117  1.5  christos 	 * known for trashing the memory allocator at times.
   1118  1.5  christos 	 */
   1119  1.5  christos 	for (before = after = session->nsstreams, i = 0;
   1120  1.5  christos 	     after <= before && i < INCOMING_DATA_MAX_CHUNKS_AT_ONCE;
   1121  1.5  christos 	     after = session->nsstreams, i++)
   1122  1.5  christos 	{
   1123  1.5  christos 		const uint64_t active_streams =
   1124  1.5  christos 			(session->received - session->processed);
   1125  1.5  christos 
   1126  1.5  christos 		/*
   1127  1.5  christos 		 * If there is too much outgoing data in flight - let's not
   1128  1.5  christos 		 * process any incoming data, as it could lead to piling up
   1129  1.5  christos 		 * too much send data in send buffers. With many clients
   1130  1.5  christos 		 * connected it can lead to excessive memory consumption on
   1131  1.5  christos 		 * the server instance.
   1132  1.5  christos 		 */
   1133  1.5  christos 		const size_t in_flight = http_in_flight_data_size(session);
   1134  1.5  christos 		if (in_flight >= ISC_NETMGR_TCP_SENDBUF_SIZE) {
   1135  1.5  christos 			break;
   1136  1.5  christos 		}
   1137  1.5  christos 
   1138  1.5  christos 		/*
   1139  1.5  christos 		 * If we have reached the maximum number of streams used, we
   1140  1.5  christos 		 * might stop processing for now, as nghttp2 will happily
   1141  1.5  christos 		 * consume as much data as possible.
   1142  1.5  christos 		 */
   1143  1.5  christos 		if (session->nsstreams >= session->max_concurrent_streams &&
   1144  1.5  christos 		    active_streams > 0)
   1145  1.5  christos 		{
   1146  1.5  christos 			break;
   1147  1.5  christos 		}
   1148  1.5  christos 
   1149  1.5  christos 		if (http_too_many_active_streams(session)) {
   1150  1.5  christos 			break;
   1151  1.5  christos 		}
   1152  1.5  christos 
   1153  1.5  christos 		isc_buffer_remainingregion(input_data, &chunk);
   1154  1.5  christos 		if (chunk.length == 0) {
   1155  1.5  christos 			break;
   1156  1.5  christos 		}
   1157  1.5  christos 
   1158  1.5  christos 		chunk.length = ISC_MIN(chunk.length, INCOMING_DATA_CHUNK_SIZE);
   1159  1.5  christos 
   1160  1.5  christos 		readlen = nghttp2_session_mem_recv(session->ngsession,
   1161  1.5  christos 						   chunk.base, chunk.length);
   1162  1.5  christos 
   1163  1.5  christos 		if (readlen >= 0) {
   1164  1.5  christos 			isc_buffer_forward(input_data, readlen);
   1165  1.5  christos 			session->processed_incoming_data += readlen;
   1166  1.5  christos 			processed += readlen;
   1167  1.5  christos 		} else {
   1168  1.5  christos 			isc_buffer_clear(input_data);
   1169  1.5  christos 			return readlen;
   1170  1.5  christos 		}
   1171  1.5  christos 	}
   1172  1.5  christos 
   1173  1.5  christos 	return processed;
   1174  1.5  christos }
   1175  1.5  christos 
   1176  1.5  christos static void
   1177  1.5  christos http_log_flooding_peer(isc_nm_http_session_t *session) {
   1178  1.5  christos 	const int log_level = ISC_LOG_DEBUG(1);
   1179  1.5  christos 	if (session->handle != NULL && isc_log_wouldlog(isc_lctx, log_level)) {
   1180  1.5  christos 		char client_sabuf[ISC_SOCKADDR_FORMATSIZE];
   1181  1.5  christos 		char local_sabuf[ISC_SOCKADDR_FORMATSIZE];
   1182  1.5  christos 
   1183  1.5  christos 		isc_sockaddr_format(&session->handle->sock->peer, client_sabuf,
   1184  1.5  christos 				    sizeof(client_sabuf));
   1185  1.5  christos 		isc_sockaddr_format(&session->handle->sock->iface, local_sabuf,
   1186  1.5  christos 				    sizeof(local_sabuf));
   1187  1.5  christos 		isc__nmsocket_log(session->handle->sock, log_level,
   1188  1.5  christos 				  "Dropping a flooding HTTP/2 peer "
   1189  1.5  christos 				  "%s (on %s) - processed: %" PRIu64
   1190  1.5  christos 				  " bytes, of them useful: %" PRIu64 "",
   1191  1.5  christos 				  client_sabuf, local_sabuf,
   1192  1.5  christos 				  session->processed_incoming_data,
   1193  1.5  christos 				  session->processed_useful_data);
   1194  1.5  christos 	}
   1195  1.5  christos }
   1196  1.5  christos 
   1197  1.5  christos static bool
   1198  1.5  christos http_is_flooding_peer(isc_nm_http_session_t *session) {
   1199  1.5  christos 	if (session->client) {
   1200  1.5  christos 		return false;
   1201  1.5  christos 	}
   1202  1.5  christos 
   1203  1.5  christos 	/*
   1204  1.5  christos 	 * A flooding client can try to open a lot of streams before
   1205  1.5  christos 	 * submitting a request. Let's drop such clients.
   1206  1.5  christos 	 */
   1207  1.5  christos 	if (session->received == 0 &&
   1208  1.5  christos 	    session->total_opened_sstreams > MAX_STREAMS_BEFORE_FIRST_REQUEST)
   1209  1.5  christos 	{
   1210  1.5  christos 		return true;
   1211  1.5  christos 	}
   1212  1.5  christos 
   1213  1.5  christos 	/*
   1214  1.5  christos 	 * We have processed enough data to open at least one stream and
   1215  1.5  christos 	 * get some useful data.
   1216  1.5  christos 	 */
   1217  1.5  christos 	if (session->processed_incoming_data >
   1218  1.5  christos 		    INCOMING_DATA_INITIAL_STREAM_SIZE &&
   1219  1.5  christos 	    (session->total_opened_sstreams == 0 ||
   1220  1.5  christos 	     session->processed_useful_data == 0))
   1221  1.5  christos 	{
   1222  1.5  christos 		return true;
   1223  1.5  christos 	}
   1224  1.5  christos 
   1225  1.5  christos 	if (session->processed_incoming_data < INCOMING_DATA_GRACE_SIZE) {
   1226  1.5  christos 		return false;
   1227  1.5  christos 	}
   1228  1.5  christos 
   1229  1.5  christos 	/*
   1230  1.5  christos 	 * The overhead of DoH per DNS message can be minimum 160-180
   1231  1.5  christos 	 * bytes. We should allow more for extra information that can be
   1232  1.5  christos 	 * included in headers, so let's use 256 bytes. Minimum DNS
   1233  1.5  christos 	 * message size is 12 bytes. So, (256+12)/12=22. Even that can be
   1234  1.5  christos 	 * too restricting for some edge cases, but should be good enough
   1235  1.5  christos 	 * for any practical purposes. Not to mention that HTTP/2 may
   1236  1.5  christos 	 * include legitimate data that is completely useless for DNS
   1237  1.5  christos 	 * purposes...
   1238  1.5  christos 	 *
   1239  1.5  christos 	 * Anyway, at that point we should have processed enough requests
   1240  1.5  christos 	 * for such clients (if any).
   1241  1.5  christos 	 */
   1242  1.5  christos 	if (session->processed_useful_data == 0 ||
   1243  1.5  christos 	    (session->processed_incoming_data /
   1244  1.5  christos 	     session->processed_useful_data) > 22)
   1245  1.5  christos 	{
   1246  1.5  christos 		return true;
   1247  1.5  christos 	}
   1248  1.5  christos 
   1249  1.5  christos 	return false;
   1250  1.5  christos }
   1251  1.5  christos 
   1252  1.1  christos /*
   1253  1.1  christos  * Read callback from TLS socket.
   1254  1.1  christos  */
   1255  1.1  christos static void
   1256  1.4  christos http_readcb(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result,
   1257  1.4  christos 	    isc_region_t *region, void *data) {
   1258  1.1  christos 	isc_nm_http_session_t *session = (isc_nm_http_session_t *)data;
   1259  1.3  christos 	isc_nm_http_session_t *tmpsess = NULL;
   1260  1.1  christos 	ssize_t readlen;
   1261  1.5  christos 	isc_buffer_t input;
   1262  1.1  christos 
   1263  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
   1264  1.1  christos 
   1265  1.3  christos 	/*
   1266  1.3  christos 	 * Let's ensure that HTTP/2 session and its associated data will
   1267  1.3  christos 	 * not go "out of scope" too early.
   1268  1.3  christos 	 */
   1269  1.3  christos 	isc__nm_httpsession_attach(session, &tmpsess);
   1270  1.1  christos 
   1271  1.1  christos 	if (result != ISC_R_SUCCESS) {
   1272  1.1  christos 		if (result != ISC_R_TIMEDOUT) {
   1273  1.1  christos 			session->reading = false;
   1274  1.1  christos 		}
   1275  1.1  christos 		failed_read_cb(result, session);
   1276  1.3  christos 		goto done;
   1277  1.1  christos 	}
   1278  1.1  christos 
   1279  1.5  christos 	isc_buffer_init(&input, region->base, region->length);
   1280  1.5  christos 	isc_buffer_add(&input, region->length);
   1281  1.5  christos 
   1282  1.5  christos 	readlen = http_process_input_data(session, &input);
   1283  1.1  christos 	if (readlen < 0) {
   1284  1.1  christos 		failed_read_cb(ISC_R_UNEXPECTED, session);
   1285  1.3  christos 		goto done;
   1286  1.5  christos 	} else if (http_is_flooding_peer(session)) {
   1287  1.5  christos 		http_log_flooding_peer(session);
   1288  1.5  christos 		failed_read_cb(ISC_R_RANGE, session);
   1289  1.5  christos 		goto done;
   1290  1.1  christos 	}
   1291  1.1  christos 
   1292  1.1  christos 	if ((size_t)readlen < region->length) {
   1293  1.1  christos 		size_t unread_size = region->length - readlen;
   1294  1.1  christos 		if (session->buf == NULL) {
   1295  1.1  christos 			isc_buffer_allocate(session->mctx, &session->buf,
   1296  1.1  christos 					    unread_size);
   1297  1.1  christos 		}
   1298  1.1  christos 		isc_buffer_putmem(session->buf, region->base + readlen,
   1299  1.1  christos 				  unread_size);
   1300  1.5  christos 		if (session->handle != NULL) {
   1301  1.5  christos 			INSIST(VALID_NMHANDLE(session->handle));
   1302  1.5  christos 			isc_nm_read_stop(session->handle);
   1303  1.5  christos 		}
   1304  1.5  christos 		http_do_bio_async(session);
   1305  1.5  christos 	} else {
   1306  1.5  christos 		/* We might have something to receive or send, do IO */
   1307  1.5  christos 		http_do_bio(session, NULL, NULL, NULL);
   1308  1.1  christos 	}
   1309  1.1  christos 
   1310  1.3  christos done:
   1311  1.3  christos 	isc__nm_httpsession_detach(&tmpsess);
   1312  1.1  christos }
   1313  1.1  christos 
   1314  1.1  christos static void
   1315  1.1  christos call_pending_callbacks(isc__nm_http_pending_callbacks_t pending_callbacks,
   1316  1.1  christos 		       isc_result_t result) {
   1317  1.1  christos 	isc__nm_uvreq_t *cbreq = ISC_LIST_HEAD(pending_callbacks);
   1318  1.1  christos 	while (cbreq != NULL) {
   1319  1.1  christos 		isc__nm_uvreq_t *next = ISC_LIST_NEXT(cbreq, link);
   1320  1.1  christos 		ISC_LIST_UNLINK(pending_callbacks, cbreq, link);
   1321  1.4  christos 		isc__nm_sendcb(cbreq->handle->sock, cbreq, result, true);
   1322  1.1  christos 		cbreq = next;
   1323  1.1  christos 	}
   1324  1.1  christos }
   1325  1.1  christos 
   1326  1.1  christos static void
   1327  1.1  christos http_writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
   1328  1.1  christos 	isc_http_send_req_t *req = (isc_http_send_req_t *)arg;
   1329  1.1  christos 	isc_nm_http_session_t *session = req->session;
   1330  1.1  christos 	isc_nmhandle_t *transphandle = req->transphandle;
   1331  1.1  christos 
   1332  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
   1333  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   1334  1.1  christos 
   1335  1.1  christos 	if (http_session_active(session)) {
   1336  1.1  christos 		INSIST(session->handle == handle);
   1337  1.1  christos 	}
   1338  1.1  christos 
   1339  1.1  christos 	call_pending_callbacks(req->pending_write_callbacks, result);
   1340  1.1  christos 
   1341  1.1  christos 	if (req->cb != NULL) {
   1342  1.1  christos 		req->cb(req->httphandle, result, req->cbarg);
   1343  1.1  christos 		isc_nmhandle_detach(&req->httphandle);
   1344  1.1  christos 	}
   1345  1.1  christos 
   1346  1.5  christos 	session->data_in_flight -=
   1347  1.5  christos 		isc_buffer_usedlength(req->pending_write_data);
   1348  1.1  christos 	isc_buffer_free(&req->pending_write_data);
   1349  1.5  christos 	session->processed += req->submitted;
   1350  1.1  christos 	isc_mem_put(session->mctx, req, sizeof(*req));
   1351  1.1  christos 
   1352  1.1  christos 	session->sending--;
   1353  1.5  christos 
   1354  1.5  christos 	if (result == ISC_R_SUCCESS) {
   1355  1.5  christos 		http_do_bio(session, NULL, NULL, NULL);
   1356  1.5  christos 	} else {
   1357  1.1  christos 		finish_http_session(session);
   1358  1.1  christos 	}
   1359  1.5  christos 	isc_nmhandle_detach(&transphandle);
   1360  1.5  christos 
   1361  1.1  christos 	isc__nm_httpsession_detach(&session);
   1362  1.1  christos }
   1363  1.1  christos 
   1364  1.1  christos static void
   1365  1.1  christos move_pending_send_callbacks(isc_nm_http_session_t *session,
   1366  1.1  christos 			    isc_http_send_req_t *send) {
   1367  1.1  christos 	STATIC_ASSERT(
   1368  1.1  christos 		sizeof(session->pending_write_callbacks) ==
   1369  1.1  christos 			sizeof(send->pending_write_callbacks),
   1370  1.1  christos 		"size of pending writes requests callbacks lists differs");
   1371  1.1  christos 	memmove(&send->pending_write_callbacks,
   1372  1.1  christos 		&session->pending_write_callbacks,
   1373  1.1  christos 		sizeof(session->pending_write_callbacks));
   1374  1.1  christos 	ISC_LIST_INIT(session->pending_write_callbacks);
   1375  1.1  christos }
   1376  1.1  christos 
   1377  1.5  christos static inline void
   1378  1.5  christos http_append_pending_send_request(isc_nm_http_session_t *session,
   1379  1.5  christos 				 isc_nmhandle_t *httphandle, isc_nm_cb_t cb,
   1380  1.5  christos 				 void *cbarg) {
   1381  1.5  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
   1382  1.5  christos 	REQUIRE(VALID_NMHANDLE(httphandle));
   1383  1.5  christos 	REQUIRE(cb != NULL);
   1384  1.5  christos 
   1385  1.5  christos 	isc__nm_uvreq_t *newcb = isc__nm_uvreq_get(httphandle->sock);
   1386  1.5  christos 
   1387  1.5  christos 	newcb->cb.send = cb;
   1388  1.5  christos 	newcb->cbarg = cbarg;
   1389  1.5  christos 	isc_nmhandle_attach(httphandle, &newcb->handle);
   1390  1.5  christos 	ISC_LIST_APPEND(session->pending_write_callbacks, newcb, link);
   1391  1.5  christos }
   1392  1.5  christos 
   1393  1.5  christos static void
   1394  1.1  christos http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle,
   1395  1.1  christos 		   isc_nm_cb_t cb, void *cbarg) {
   1396  1.1  christos 	isc_http_send_req_t *send = NULL;
   1397  1.1  christos 	size_t total = 0;
   1398  1.1  christos 	isc_region_t send_data = { 0 };
   1399  1.1  christos 	isc_nmhandle_t *transphandle = NULL;
   1400  1.1  christos #ifdef ENABLE_HTTP_WRITE_BUFFERING
   1401  1.1  christos 	size_t max_total_write_size = 0;
   1402  1.1  christos #endif /* ENABLE_HTTP_WRITE_BUFFERING */
   1403  1.1  christos 
   1404  1.5  christos 	if (!http_session_active(session)) {
   1405  1.5  christos 		if (cb != NULL) {
   1406  1.5  christos 			isc__nm_uvreq_t *req =
   1407  1.5  christos 				isc__nm_uvreq_get(httphandle->sock);
   1408  1.5  christos 
   1409  1.5  christos 			req->cb.send = cb;
   1410  1.5  christos 			req->cbarg = cbarg;
   1411  1.5  christos 			isc_nmhandle_attach(httphandle, &req->handle);
   1412  1.5  christos 			isc__nm_sendcb(httphandle->sock, req, ISC_R_CANCELED,
   1413  1.5  christos 				       true);
   1414  1.5  christos 		}
   1415  1.5  christos 		return;
   1416  1.5  christos 	} else if (!nghttp2_session_want_write(session->ngsession) &&
   1417  1.5  christos 		   session->pending_write_data == NULL)
   1418  1.1  christos 	{
   1419  1.5  christos 		if (cb != NULL) {
   1420  1.5  christos 			http_append_pending_send_request(session, httphandle,
   1421  1.5  christos 							 cb, cbarg);
   1422  1.5  christos 		}
   1423  1.5  christos 		return;
   1424  1.1  christos 	}
   1425  1.1  christos 
   1426  1.4  christos 	/*
   1427  1.4  christos 	 * We need to attach to the session->handle earlier because as an
   1428  1.1  christos 	 * indirect result of the nghttp2_session_mem_send() the session
   1429  1.1  christos 	 * might get closed and the handle detached. However, there is
   1430  1.1  christos 	 * still some outgoing data to handle and we need to call it
   1431  1.1  christos 	 * anyway if only to get the write callback passed here to get
   1432  1.4  christos 	 * called properly.
   1433  1.4  christos 	 */
   1434  1.1  christos 	isc_nmhandle_attach(session->handle, &transphandle);
   1435  1.1  christos 
   1436  1.1  christos 	while (nghttp2_session_want_write(session->ngsession)) {
   1437  1.1  christos 		const uint8_t *data = NULL;
   1438  1.1  christos 		const size_t pending =
   1439  1.1  christos 			nghttp2_session_mem_send(session->ngsession, &data);
   1440  1.1  christos 		const size_t new_total = total + pending;
   1441  1.1  christos 
   1442  1.4  christos 		/*
   1443  1.4  christos 		 * Sometimes nghttp2_session_mem_send() does not return any
   1444  1.1  christos 		 * data to send even though nghttp2_session_want_write()
   1445  1.4  christos 		 * returns success.
   1446  1.4  christos 		 */
   1447  1.1  christos 		if (pending == 0 || data == NULL) {
   1448  1.1  christos 			break;
   1449  1.1  christos 		}
   1450  1.1  christos 
   1451  1.1  christos 		/* reallocate buffer if required */
   1452  1.1  christos 		if (session->pending_write_data == NULL) {
   1453  1.1  christos 			isc_buffer_allocate(session->mctx,
   1454  1.1  christos 					    &session->pending_write_data,
   1455  1.1  christos 					    INITIAL_DNS_MESSAGE_BUFFER_SIZE);
   1456  1.1  christos 		}
   1457  1.1  christos 		isc_buffer_putmem(session->pending_write_data, data, pending);
   1458  1.1  christos 		total = new_total;
   1459  1.1  christos 	}
   1460  1.1  christos 
   1461  1.1  christos #ifdef ENABLE_HTTP_WRITE_BUFFERING
   1462  1.1  christos 	if (session->pending_write_data != NULL) {
   1463  1.1  christos 		max_total_write_size =
   1464  1.1  christos 			isc_buffer_usedlength(session->pending_write_data);
   1465  1.1  christos 	}
   1466  1.1  christos 
   1467  1.4  christos 	/*
   1468  1.4  christos 	 * Here we are trying to flush the pending writes buffer earlier
   1469  1.1  christos 	 * to avoid hitting unnecessary limitations on a TLS record size
   1470  1.4  christos 	 * within some tools (e.g. flamethrower).
   1471  1.4  christos 	 */
   1472  1.5  christos 	if (cb != NULL) {
   1473  1.4  christos 		/*
   1474  1.5  christos 		 * Case 0: The callback is specified, that means that a DNS
   1475  1.5  christos 		 * message is ready. Let's flush the the buffer.
   1476  1.5  christos 		 */
   1477  1.5  christos 		total = max_total_write_size;
   1478  1.5  christos 	} else if (max_total_write_size >= FLUSH_HTTP_WRITE_BUFFER_AFTER) {
   1479  1.5  christos 		/*
   1480  1.5  christos 		 * Case 1: We have equal or more than
   1481  1.5  christos 		 * FLUSH_HTTP_WRITE_BUFFER_AFTER bytes to send. Let's flush it.
   1482  1.1  christos 		 */
   1483  1.1  christos 		total = max_total_write_size;
   1484  1.1  christos 	} else if (session->sending > 0 && total > 0) {
   1485  1.4  christos 		/*
   1486  1.4  christos 		 * Case 2: There is one or more write requests in flight and
   1487  1.5  christos 		 * we have some new data form nghttp2 to send.
   1488  1.5  christos 		 * Then let's return from the function: as soon as the
   1489  1.1  christos 		 * "in-flight" write callback get's called or we have reached
   1490  1.1  christos 		 * FLUSH_HTTP_WRITE_BUFFER_AFTER bytes in the write buffer, we
   1491  1.5  christos 		 * will flush the buffer. */
   1492  1.5  christos 		INSIST(cb == NULL);
   1493  1.1  christos 		goto nothing_to_send;
   1494  1.1  christos 	} else if (session->sending == 0 && total == 0 &&
   1495  1.1  christos 		   session->pending_write_data != NULL)
   1496  1.1  christos 	{
   1497  1.4  christos 		/*
   1498  1.4  christos 		 * Case 3: There is no write in flight and we haven't got
   1499  1.1  christos 		 * anything new from nghttp2, but there is some data pending
   1500  1.4  christos 		 * in the write buffer. Let's flush the buffer.
   1501  1.4  christos 		 */
   1502  1.1  christos 		isc_region_t region = { 0 };
   1503  1.1  christos 		total = isc_buffer_usedlength(session->pending_write_data);
   1504  1.1  christos 		INSIST(total > 0);
   1505  1.1  christos 		isc_buffer_usedregion(session->pending_write_data, &region);
   1506  1.1  christos 		INSIST(total == region.length);
   1507  1.1  christos 	} else {
   1508  1.4  christos 		/*
   1509  1.4  christos 		 * The other cases are uninteresting, fall-through ones.
   1510  1.4  christos 		 * In the following cases (4-6) we will just bail out:
   1511  1.4  christos 		 *
   1512  1.4  christos 		 * Case 4: There is nothing new to send, nor anything in the
   1513  1.4  christos 		 * write buffer.
   1514  1.4  christos 		 * Case 5: There is nothing new to send and there are write
   1515  1.4  christos 		 * request(s) in flight.
   1516  1.4  christos 		 * Case 6: There is nothing new to send nor are there any
   1517  1.4  christos 		 * write requests in flight.
   1518  1.4  christos 		 *
   1519  1.4  christos 		 * Case 7: There is some new data to send and there are no
   1520  1.4  christos 		 * write requests in flight: Let's send the data.
   1521  1.4  christos 		 */
   1522  1.1  christos 		INSIST((total == 0 && session->pending_write_data == NULL) ||
   1523  1.1  christos 		       (total == 0 && session->sending > 0) ||
   1524  1.1  christos 		       (total == 0 && session->sending == 0) ||
   1525  1.1  christos 		       (total > 0 && session->sending == 0));
   1526  1.1  christos 	}
   1527  1.1  christos #endif /* ENABLE_HTTP_WRITE_BUFFERING */
   1528  1.1  christos 
   1529  1.1  christos 	if (total == 0) {
   1530  1.1  christos 		/* No data returned */
   1531  1.5  christos 		if (cb != NULL) {
   1532  1.5  christos 			http_append_pending_send_request(session, httphandle,
   1533  1.5  christos 							 cb, cbarg);
   1534  1.5  christos 		}
   1535  1.1  christos 		goto nothing_to_send;
   1536  1.1  christos 	}
   1537  1.1  christos 
   1538  1.4  christos 	/*
   1539  1.4  christos 	 * If we have reached this point it means that we need to send some
   1540  1.4  christos 	 * data and flush the outgoing buffer. The code below does that.
   1541  1.4  christos 	 */
   1542  1.1  christos 	send = isc_mem_get(session->mctx, sizeof(*send));
   1543  1.1  christos 
   1544  1.1  christos 	*send = (isc_http_send_req_t){ .pending_write_data =
   1545  1.1  christos 					       session->pending_write_data,
   1546  1.1  christos 				       .cb = cb,
   1547  1.5  christos 				       .cbarg = cbarg,
   1548  1.5  christos 				       .submitted = session->submitted };
   1549  1.5  christos 	session->submitted = 0;
   1550  1.1  christos 	session->pending_write_data = NULL;
   1551  1.1  christos 	move_pending_send_callbacks(session, send);
   1552  1.1  christos 
   1553  1.1  christos 	send->transphandle = transphandle;
   1554  1.1  christos 	isc__nm_httpsession_attach(session, &send->session);
   1555  1.1  christos 
   1556  1.1  christos 	if (cb != NULL) {
   1557  1.1  christos 		INSIST(VALID_NMHANDLE(httphandle));
   1558  1.1  christos 		isc_nmhandle_attach(httphandle, &send->httphandle);
   1559  1.1  christos 	}
   1560  1.1  christos 
   1561  1.1  christos 	session->sending++;
   1562  1.1  christos 	isc_buffer_usedregion(send->pending_write_data, &send_data);
   1563  1.5  christos 	session->data_in_flight += send_data.length;
   1564  1.1  christos 	isc_nm_send(transphandle, &send_data, http_writecb, send);
   1565  1.5  christos 	return;
   1566  1.4  christos 
   1567  1.1  christos nothing_to_send:
   1568  1.1  christos 	isc_nmhandle_detach(&transphandle);
   1569  1.5  christos }
   1570  1.5  christos 
   1571  1.5  christos static inline bool
   1572  1.5  christos http_too_many_active_streams(isc_nm_http_session_t *session) {
   1573  1.5  christos 	const uint64_t active_streams = session->received - session->processed;
   1574  1.5  christos 	/*
   1575  1.5  christos 	 * The motivation behind capping the maximum active streams number
   1576  1.5  christos 	 * to a third of maximum streams is to allow the value to scale
   1577  1.5  christos 	 * with the max number of streams.
   1578  1.5  christos 	 *
   1579  1.5  christos 	 * We do not want to have too many active streams at once as every
   1580  1.5  christos 	 * stream is processed as a separate virtual connection by the
   1581  1.5  christos 	 * higher level code. If a client sends a bulk of requests without
   1582  1.5  christos 	 * waiting for the previous ones to complete we might want to
   1583  1.5  christos 	 * throttle it as it might be not a friend knocking at the
   1584  1.5  christos 	 * door. We already have some job to do for it.
   1585  1.5  christos 	 */
   1586  1.5  christos 	const uint64_t max_active_streams =
   1587  1.5  christos 		ISC_MAX(ISC_NETMGR_MAX_STREAM_CLIENTS_PER_CONN,
   1588  1.5  christos 			(session->max_concurrent_streams * 6) / 10); /* 60% */
   1589  1.5  christos 
   1590  1.5  christos 	if (session->client) {
   1591  1.5  christos 		return false;
   1592  1.5  christos 	}
   1593  1.5  christos 
   1594  1.5  christos 	/*
   1595  1.5  christos 	 * Do not process incoming data if there are too many active DNS
   1596  1.5  christos 	 * clients (streams) per connection.
   1597  1.5  christos 	 */
   1598  1.5  christos 	if (active_streams >= max_active_streams) {
   1599  1.5  christos 		return true;
   1600  1.5  christos 	}
   1601  1.5  christos 
   1602  1.4  christos 	return false;
   1603  1.1  christos }
   1604  1.1  christos 
   1605  1.1  christos static void
   1606  1.1  christos http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle,
   1607  1.1  christos 	    isc_nm_cb_t send_cb, void *send_cbarg) {
   1608  1.5  christos 	isc__nm_uvreq_t *req = NULL;
   1609  1.5  christos 	size_t remaining = 0;
   1610  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
   1611  1.1  christos 
   1612  1.1  christos 	if (session->closed) {
   1613  1.5  christos 		goto cancel;
   1614  1.1  christos 	} else if (session->closing) {
   1615  1.1  christos 		/*
   1616  1.1  christos 		 * There might be leftover callbacks waiting to be received
   1617  1.1  christos 		 */
   1618  1.1  christos 		if (session->sending == 0) {
   1619  1.1  christos 			finish_http_session(session);
   1620  1.1  christos 		}
   1621  1.5  christos 		goto cancel;
   1622  1.1  christos 	} else if (nghttp2_session_want_read(session->ngsession) == 0 &&
   1623  1.1  christos 		   nghttp2_session_want_write(session->ngsession) == 0 &&
   1624  1.1  christos 		   session->pending_write_data == NULL)
   1625  1.1  christos 	{
   1626  1.1  christos 		session->closing = true;
   1627  1.5  christos 		if (session->handle != NULL) {
   1628  1.5  christos 			isc_nm_read_stop(session->handle);
   1629  1.5  christos 		}
   1630  1.5  christos 		if (session->sending == 0) {
   1631  1.5  christos 			finish_http_session(session);
   1632  1.5  christos 		}
   1633  1.5  christos 		goto cancel;
   1634  1.5  christos 	}
   1635  1.5  christos 
   1636  1.5  christos 	else if (session->buf != NULL)
   1637  1.5  christos 	{
   1638  1.5  christos 		remaining = isc_buffer_remaininglength(session->buf);
   1639  1.1  christos 	}
   1640  1.1  christos 
   1641  1.1  christos 	if (nghttp2_session_want_read(session->ngsession) != 0) {
   1642  1.1  christos 		if (!session->reading) {
   1643  1.5  christos 			/* We have not yet started reading from this handle */
   1644  1.5  christos 			isc__nmsocket_timer_start(session->handle->sock);
   1645  1.1  christos 			isc_nm_read(session->handle, http_readcb, session);
   1646  1.1  christos 			session->reading = true;
   1647  1.5  christos 		} else if (session->buf != NULL && remaining > 0) {
   1648  1.5  christos 			/* Leftover data in the buffer, use it */
   1649  1.5  christos 			size_t remaining_after = 0;
   1650  1.5  christos 			ssize_t readlen = 0;
   1651  1.5  christos 			isc_nm_http_session_t *tmpsess = NULL;
   1652  1.5  christos 
   1653  1.5  christos 			/*
   1654  1.5  christos 			 * Let's ensure that HTTP/2 session and its associated
   1655  1.5  christos 			 * data will not go "out of scope" too early.
   1656  1.5  christos 			 */
   1657  1.5  christos 			isc__nm_httpsession_attach(session, &tmpsess);
   1658  1.5  christos 
   1659  1.5  christos 			readlen = http_process_input_data(session,
   1660  1.5  christos 							  session->buf);
   1661  1.5  christos 
   1662  1.5  christos 			remaining_after =
   1663  1.1  christos 				isc_buffer_remaininglength(session->buf);
   1664  1.1  christos 
   1665  1.5  christos 			if (readlen < 0) {
   1666  1.5  christos 				failed_read_cb(ISC_R_UNEXPECTED, session);
   1667  1.5  christos 			} else if (http_is_flooding_peer(session)) {
   1668  1.5  christos 				http_log_flooding_peer(session);
   1669  1.5  christos 				failed_read_cb(ISC_R_RANGE, session);
   1670  1.5  christos 			} else if ((size_t)readlen == remaining) {
   1671  1.5  christos 				isc_buffer_clear(session->buf);
   1672  1.5  christos 				isc_buffer_compact(session->buf);
   1673  1.5  christos 				http_do_bio(session, send_httphandle, send_cb,
   1674  1.5  christos 					    send_cbarg);
   1675  1.5  christos 				isc__nm_httpsession_detach(&tmpsess);
   1676  1.5  christos 				return;
   1677  1.5  christos 			} else if (remaining_after > 0 &&
   1678  1.5  christos 				   remaining_after < remaining)
   1679  1.5  christos 			{
   1680  1.5  christos 				/*
   1681  1.5  christos 				 * We have processed a part of the data, now
   1682  1.5  christos 				 * let's delay processing of whatever is left
   1683  1.5  christos 				 * here. We want it to be an async operation so
   1684  1.5  christos 				 * that we will:
   1685  1.5  christos 				 *
   1686  1.5  christos 				 * a) let other things run;
   1687  1.5  christos 				 * b) have finer grained control over how much
   1688  1.5  christos 				 * data is processed at once, because nghttp2
   1689  1.5  christos 				 * would happily consume as much data we pass to
   1690  1.5  christos 				 * it and that could overwhelm the server.
   1691  1.5  christos 				 */
   1692  1.5  christos 				http_do_bio_async(session);
   1693  1.1  christos 			}
   1694  1.5  christos 			isc__nm_httpsession_detach(&tmpsess);
   1695  1.5  christos 		} else if (session->handle != NULL) {
   1696  1.5  christos 			INSIST(VALID_NMHANDLE(session->handle));
   1697  1.5  christos 			/*
   1698  1.5  christos 			 * Resume reading, it's idempotent, wait for more
   1699  1.4  christos 			 */
   1700  1.5  christos 			isc__nmsocket_timer_start(session->handle->sock);
   1701  1.4  christos 			isc_nm_read(session->handle, http_readcb, session);
   1702  1.1  christos 		}
   1703  1.5  christos 	} else if (session->handle != NULL) {
   1704  1.5  christos 		INSIST(VALID_NMHANDLE(session->handle));
   1705  1.1  christos 		/* We don't want more data, stop reading for now */
   1706  1.4  christos 		isc_nm_read_stop(session->handle);
   1707  1.1  christos 	}
   1708  1.1  christos 
   1709  1.5  christos 	/* we might have some data to send after processing */
   1710  1.5  christos 	http_send_outgoing(session, send_httphandle, send_cb, send_cbarg);
   1711  1.5  christos 
   1712  1.5  christos 	return;
   1713  1.5  christos cancel:
   1714  1.5  christos 	if (send_cb == NULL) {
   1715  1.5  christos 		return;
   1716  1.1  christos 	}
   1717  1.5  christos 	req = isc__nm_uvreq_get(send_httphandle->sock);
   1718  1.1  christos 
   1719  1.5  christos 	req->cb.send = send_cb;
   1720  1.5  christos 	req->cbarg = send_cbarg;
   1721  1.5  christos 	isc_nmhandle_attach(send_httphandle, &req->handle);
   1722  1.5  christos 	isc__nm_sendcb(send_httphandle->sock, req, ISC_R_CANCELED, true);
   1723  1.5  christos }
   1724  1.5  christos 
   1725  1.5  christos static void
   1726  1.5  christos http_do_bio_async_cb(void *arg) {
   1727  1.5  christos 	isc_nm_http_session_t *session = arg;
   1728  1.5  christos 
   1729  1.5  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
   1730  1.5  christos 
   1731  1.5  christos 	session->async_queued = false;
   1732  1.5  christos 
   1733  1.5  christos 	if (session->handle != NULL &&
   1734  1.5  christos 	    !isc__nmsocket_closing(session->handle->sock))
   1735  1.5  christos 	{
   1736  1.5  christos 		http_do_bio(session, NULL, NULL, NULL);
   1737  1.5  christos 	}
   1738  1.5  christos 
   1739  1.5  christos 	isc__nm_httpsession_detach(&session);
   1740  1.5  christos }
   1741  1.5  christos 
   1742  1.5  christos static void
   1743  1.5  christos http_do_bio_async(isc_nm_http_session_t *session) {
   1744  1.5  christos 	isc_nm_http_session_t *tmpsess = NULL;
   1745  1.5  christos 
   1746  1.5  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
   1747  1.5  christos 
   1748  1.5  christos 	if (session->handle == NULL ||
   1749  1.5  christos 	    isc__nmsocket_closing(session->handle->sock) ||
   1750  1.5  christos 	    session->async_queued)
   1751  1.5  christos 	{
   1752  1.5  christos 		return;
   1753  1.5  christos 	}
   1754  1.5  christos 	session->async_queued = true;
   1755  1.5  christos 	isc__nm_httpsession_attach(session, &tmpsess);
   1756  1.5  christos 	isc_async_run(session->handle->sock->worker->loop, http_do_bio_async_cb,
   1757  1.5  christos 		      tmpsess);
   1758  1.1  christos }
   1759  1.1  christos 
   1760  1.1  christos static isc_result_t
   1761  1.1  christos get_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) {
   1762  1.4  christos 	http_cstream_t *cstream = sock->h2->connect.cstream;
   1763  1.1  christos 	isc_result_t result;
   1764  1.1  christos 
   1765  1.1  christos 	REQUIRE(streamp != NULL && *streamp == NULL);
   1766  1.1  christos 
   1767  1.4  christos 	sock->h2->connect.cstream = NULL;
   1768  1.1  christos 
   1769  1.1  christos 	if (cstream == NULL) {
   1770  1.1  christos 		result = new_http_cstream(sock, &cstream);
   1771  1.1  christos 		if (result != ISC_R_SUCCESS) {
   1772  1.1  christos 			INSIST(cstream == NULL);
   1773  1.4  christos 			return result;
   1774  1.1  christos 		}
   1775  1.1  christos 	}
   1776  1.1  christos 
   1777  1.1  christos 	*streamp = cstream;
   1778  1.4  christos 	return ISC_R_SUCCESS;
   1779  1.1  christos }
   1780  1.1  christos 
   1781  1.1  christos static void
   1782  1.1  christos http_call_connect_cb(isc_nmsocket_t *sock, isc_nm_http_session_t *session,
   1783  1.1  christos 		     isc_result_t result) {
   1784  1.1  christos 	isc_nmhandle_t *httphandle = isc__nmhandle_get(sock, &sock->peer,
   1785  1.1  christos 						       &sock->iface);
   1786  1.4  christos 	void *cbarg;
   1787  1.4  christos 	isc_nm_cb_t connect_cb;
   1788  1.1  christos 
   1789  1.1  christos 	REQUIRE(sock->connect_cb != NULL);
   1790  1.1  christos 
   1791  1.4  christos 	cbarg = sock->connect_cbarg;
   1792  1.4  christos 	connect_cb = sock->connect_cb;
   1793  1.4  christos 	isc__nmsocket_clearcb(sock);
   1794  1.1  christos 	if (result == ISC_R_SUCCESS) {
   1795  1.1  christos 		if (session != NULL) {
   1796  1.1  christos 			session->client_httphandle = httphandle;
   1797  1.1  christos 		}
   1798  1.4  christos 		connect_cb(httphandle, result, cbarg);
   1799  1.1  christos 	} else {
   1800  1.1  christos 		connect_cb(httphandle, result, cbarg);
   1801  1.1  christos 		isc_nmhandle_detach(&httphandle);
   1802  1.1  christos 	}
   1803  1.1  christos }
   1804  1.1  christos 
   1805  1.1  christos static void
   1806  1.1  christos transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
   1807  1.1  christos 	isc_nmsocket_t *http_sock = (isc_nmsocket_t *)cbarg;
   1808  1.1  christos 	isc_nmsocket_t *transp_sock = NULL;
   1809  1.1  christos 	isc_nm_http_session_t *session = NULL;
   1810  1.1  christos 	http_cstream_t *cstream = NULL;
   1811  1.1  christos 	isc_mem_t *mctx = NULL;
   1812  1.1  christos 
   1813  1.1  christos 	REQUIRE(VALID_NMSOCK(http_sock));
   1814  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   1815  1.1  christos 
   1816  1.1  christos 	transp_sock = handle->sock;
   1817  1.1  christos 
   1818  1.1  christos 	REQUIRE(VALID_NMSOCK(transp_sock));
   1819  1.1  christos 
   1820  1.4  christos 	mctx = transp_sock->worker->mctx;
   1821  1.1  christos 
   1822  1.4  christos 	INSIST(http_sock->h2->connect.uri != NULL);
   1823  1.1  christos 
   1824  1.4  christos 	http_sock->h2->connect.tls_peer_verify_string =
   1825  1.1  christos 		isc_nm_verify_tls_peer_result_string(handle);
   1826  1.1  christos 	if (result != ISC_R_SUCCESS) {
   1827  1.1  christos 		goto error;
   1828  1.1  christos 	}
   1829  1.1  christos 
   1830  1.4  christos 	http_initsocket(transp_sock);
   1831  1.4  christos 	new_session(mctx, http_sock->h2->connect.tlsctx, &session);
   1832  1.1  christos 	session->client = true;
   1833  1.4  christos 	transp_sock->h2->session = session;
   1834  1.4  christos 	http_sock->h2->connect.tlsctx = NULL;
   1835  1.1  christos 	/* otherwise we will get some garbage output in DIG */
   1836  1.4  christos 	http_sock->iface = isc_nmhandle_localaddr(handle);
   1837  1.4  christos 	http_sock->peer = isc_nmhandle_peeraddr(handle);
   1838  1.1  christos 
   1839  1.4  christos 	transp_sock->h2->connect.post = http_sock->h2->connect.post;
   1840  1.4  christos 	transp_sock->h2->connect.uri = http_sock->h2->connect.uri;
   1841  1.4  christos 	http_sock->h2->connect.uri = NULL;
   1842  1.4  christos 	isc__nm_httpsession_attach(session, &http_sock->h2->session);
   1843  1.1  christos 
   1844  1.1  christos 	if (session->tlsctx != NULL) {
   1845  1.1  christos 		const unsigned char *alpn = NULL;
   1846  1.1  christos 		unsigned int alpnlen = 0;
   1847  1.1  christos 
   1848  1.4  christos 		INSIST(transp_sock->type == isc_nm_tlssocket ||
   1849  1.4  christos 		       transp_sock->type == isc_nm_proxystreamsocket);
   1850  1.1  christos 
   1851  1.4  christos 		isc__nmhandle_get_selected_alpn(handle, &alpn, &alpnlen);
   1852  1.1  christos 		if (alpn == NULL || alpnlen != NGHTTP2_PROTO_VERSION_ID_LEN ||
   1853  1.1  christos 		    memcmp(NGHTTP2_PROTO_VERSION_ID, alpn,
   1854  1.1  christos 			   NGHTTP2_PROTO_VERSION_ID_LEN) != 0)
   1855  1.1  christos 		{
   1856  1.1  christos 			/*
   1857  1.4  christos 			 * HTTP/2 negotiation error.
   1858  1.4  christos 			 * Any sensible DoH client
   1859  1.4  christos 			 * will fail if HTTP/2 cannot
   1860  1.4  christos 			 * be negotiated via ALPN.
   1861  1.1  christos 			 */
   1862  1.1  christos 			result = ISC_R_HTTP2ALPNERROR;
   1863  1.1  christos 			goto error;
   1864  1.1  christos 		}
   1865  1.1  christos 	}
   1866  1.1  christos 
   1867  1.1  christos 	isc_nmhandle_attach(handle, &session->handle);
   1868  1.1  christos 
   1869  1.1  christos 	initialize_nghttp2_client_session(session);
   1870  1.1  christos 	if (!send_client_connection_header(session)) {
   1871  1.1  christos 		goto error;
   1872  1.1  christos 	}
   1873  1.1  christos 
   1874  1.1  christos 	result = get_http_cstream(http_sock, &cstream);
   1875  1.4  christos 	http_sock->h2->connect.cstream = cstream;
   1876  1.1  christos 	if (result != ISC_R_SUCCESS) {
   1877  1.1  christos 		goto error;
   1878  1.1  christos 	}
   1879  1.1  christos 
   1880  1.1  christos 	http_transpost_tcp_nodelay(handle);
   1881  1.5  christos 	isc__nmhandle_set_manual_timer(session->handle, true);
   1882  1.1  christos 
   1883  1.1  christos 	http_call_connect_cb(http_sock, session, result);
   1884  1.1  christos 
   1885  1.1  christos 	http_do_bio(session, NULL, NULL, NULL);
   1886  1.1  christos 	isc__nmsocket_detach(&http_sock);
   1887  1.1  christos 	return;
   1888  1.1  christos 
   1889  1.1  christos error:
   1890  1.1  christos 	http_call_connect_cb(http_sock, session, result);
   1891  1.1  christos 
   1892  1.4  christos 	if (http_sock->h2->connect.uri != NULL) {
   1893  1.4  christos 		isc_mem_free(http_sock->worker->mctx,
   1894  1.4  christos 			     http_sock->h2->connect.uri);
   1895  1.1  christos 	}
   1896  1.1  christos 
   1897  1.1  christos 	isc__nmsocket_prep_destroy(http_sock);
   1898  1.1  christos 	isc__nmsocket_detach(&http_sock);
   1899  1.1  christos }
   1900  1.1  christos 
   1901  1.1  christos void
   1902  1.1  christos isc_nm_httpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
   1903  1.1  christos 		   const char *uri, bool post, isc_nm_cb_t cb, void *cbarg,
   1904  1.5  christos 		   isc_tlsctx_t *tlsctx, const char *sni_hostname,
   1905  1.1  christos 		   isc_tlsctx_client_session_cache_t *client_sess_cache,
   1906  1.4  christos 		   unsigned int timeout, isc_nm_proxy_type_t proxy_type,
   1907  1.4  christos 		   isc_nm_proxyheader_info_t *proxy_info) {
   1908  1.1  christos 	isc_sockaddr_t local_interface;
   1909  1.1  christos 	isc_nmsocket_t *sock = NULL;
   1910  1.4  christos 	isc__networker_t *worker = NULL;
   1911  1.1  christos 
   1912  1.1  christos 	REQUIRE(VALID_NM(mgr));
   1913  1.1  christos 	REQUIRE(cb != NULL);
   1914  1.1  christos 	REQUIRE(peer != NULL);
   1915  1.1  christos 	REQUIRE(uri != NULL);
   1916  1.1  christos 	REQUIRE(*uri != '\0');
   1917  1.1  christos 
   1918  1.4  christos 	worker = &mgr->workers[isc_tid()];
   1919  1.4  christos 
   1920  1.4  christos 	if (isc__nm_closing(worker)) {
   1921  1.4  christos 		cb(NULL, ISC_R_SHUTTINGDOWN, cbarg);
   1922  1.4  christos 		return;
   1923  1.4  christos 	}
   1924  1.4  christos 
   1925  1.1  christos 	if (local == NULL) {
   1926  1.1  christos 		isc_sockaddr_anyofpf(&local_interface, peer->type.sa.sa_family);
   1927  1.1  christos 		local = &local_interface;
   1928  1.1  christos 	}
   1929  1.1  christos 
   1930  1.4  christos 	sock = isc_mempool_get(worker->nmsocket_pool);
   1931  1.4  christos 	isc__nmsocket_init(sock, worker, isc_nm_httpsocket, local, NULL);
   1932  1.4  christos 	http_initsocket(sock);
   1933  1.1  christos 
   1934  1.1  christos 	sock->connect_timeout = timeout;
   1935  1.1  christos 	sock->connect_cb = cb;
   1936  1.1  christos 	sock->connect_cbarg = cbarg;
   1937  1.4  christos 	sock->client = true;
   1938  1.1  christos 
   1939  1.4  christos 	if (isc__nm_closing(worker)) {
   1940  1.4  christos 		isc__nm_uvreq_t *req = isc__nm_uvreq_get(sock);
   1941  1.1  christos 
   1942  1.1  christos 		req->cb.connect = cb;
   1943  1.1  christos 		req->cbarg = cbarg;
   1944  1.1  christos 		req->peer = *peer;
   1945  1.1  christos 		req->local = *local;
   1946  1.1  christos 		req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface);
   1947  1.1  christos 
   1948  1.1  christos 		isc__nmsocket_clearcb(sock);
   1949  1.1  christos 		isc__nm_connectcb(sock, req, ISC_R_SHUTTINGDOWN, true);
   1950  1.1  christos 		isc__nmsocket_prep_destroy(sock);
   1951  1.1  christos 		isc__nmsocket_detach(&sock);
   1952  1.1  christos 		return;
   1953  1.1  christos 	}
   1954  1.1  christos 
   1955  1.4  christos 	*sock->h2 = (isc_nmsocket_h2_t){ .connect.uri = isc_mem_strdup(
   1956  1.4  christos 						 sock->worker->mctx, uri),
   1957  1.4  christos 					 .connect.post = post,
   1958  1.4  christos 					 .connect.tlsctx = tlsctx };
   1959  1.4  christos 	ISC_LINK_INIT(sock->h2, link);
   1960  1.1  christos 
   1961  1.1  christos 	/*
   1962  1.1  christos 	 * We need to prevent the interface object data from going out of
   1963  1.1  christos 	 * scope too early.
   1964  1.1  christos 	 */
   1965  1.1  christos 	if (local == &local_interface) {
   1966  1.4  christos 		sock->h2->connect.local_interface = local_interface;
   1967  1.4  christos 		sock->iface = sock->h2->connect.local_interface;
   1968  1.1  christos 	}
   1969  1.1  christos 
   1970  1.4  christos 	switch (proxy_type) {
   1971  1.4  christos 	case ISC_NM_PROXY_NONE:
   1972  1.4  christos 		if (tlsctx != NULL) {
   1973  1.4  christos 			isc_nm_tlsconnect(mgr, local, peer,
   1974  1.4  christos 					  transport_connect_cb, sock, tlsctx,
   1975  1.5  christos 					  sni_hostname, client_sess_cache,
   1976  1.5  christos 					  timeout, false, NULL);
   1977  1.4  christos 		} else {
   1978  1.4  christos 			isc_nm_tcpconnect(mgr, local, peer,
   1979  1.4  christos 					  transport_connect_cb, sock, timeout);
   1980  1.4  christos 		}
   1981  1.4  christos 		break;
   1982  1.4  christos 	case ISC_NM_PROXY_PLAIN:
   1983  1.4  christos 		if (tlsctx != NULL) {
   1984  1.4  christos 			isc_nm_tlsconnect(mgr, local, peer,
   1985  1.4  christos 					  transport_connect_cb, sock, tlsctx,
   1986  1.5  christos 					  sni_hostname, client_sess_cache,
   1987  1.5  christos 					  timeout, true, proxy_info);
   1988  1.4  christos 		} else {
   1989  1.4  christos 			isc_nm_proxystreamconnect(
   1990  1.4  christos 				mgr, local, peer, transport_connect_cb, sock,
   1991  1.5  christos 				timeout, NULL, NULL, NULL, proxy_info);
   1992  1.4  christos 		}
   1993  1.4  christos 		break;
   1994  1.4  christos 	case ISC_NM_PROXY_ENCRYPTED:
   1995  1.4  christos 		INSIST(tlsctx != NULL);
   1996  1.4  christos 		isc_nm_proxystreamconnect(
   1997  1.4  christos 			mgr, local, peer, transport_connect_cb, sock, timeout,
   1998  1.5  christos 			tlsctx, sni_hostname, client_sess_cache, proxy_info);
   1999  1.4  christos 		break;
   2000  1.4  christos 	default:
   2001  1.4  christos 		UNREACHABLE();
   2002  1.1  christos 	}
   2003  1.1  christos }
   2004  1.1  christos 
   2005  1.1  christos static isc_result_t
   2006  1.1  christos client_send(isc_nmhandle_t *handle, const isc_region_t *region) {
   2007  1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
   2008  1.1  christos 	isc_nmsocket_t *sock = handle->sock;
   2009  1.4  christos 	isc_mem_t *mctx = sock->worker->mctx;
   2010  1.4  christos 	isc_nm_http_session_t *session = sock->h2->session;
   2011  1.4  christos 	http_cstream_t *cstream = sock->h2->connect.cstream;
   2012  1.1  christos 
   2013  1.4  christos 	REQUIRE(VALID_HTTP2_SESSION(handle->sock->h2->session));
   2014  1.1  christos 	REQUIRE(session->client);
   2015  1.1  christos 	REQUIRE(region != NULL);
   2016  1.1  christos 	REQUIRE(region->base != NULL);
   2017  1.1  christos 	REQUIRE(region->length <= MAX_DNS_MESSAGE_SIZE);
   2018  1.1  christos 
   2019  1.1  christos 	if (session->closed) {
   2020  1.4  christos 		return ISC_R_CANCELED;
   2021  1.1  christos 	}
   2022  1.1  christos 
   2023  1.1  christos 	INSIST(cstream != NULL);
   2024  1.1  christos 
   2025  1.1  christos 	if (cstream->post) {
   2026  1.1  christos 		/* POST */
   2027  1.1  christos 		isc_buffer_allocate(mctx, &cstream->postdata, region->length);
   2028  1.1  christos 		isc_buffer_putmem(cstream->postdata, region->base,
   2029  1.1  christos 				  region->length);
   2030  1.1  christos 	} else {
   2031  1.1  christos 		/* GET */
   2032  1.1  christos 		size_t path_size = 0;
   2033  1.1  christos 		char *base64url_data = NULL;
   2034  1.1  christos 		size_t base64url_data_len = 0;
   2035  1.1  christos 		isc_buffer_t *buf = NULL;
   2036  1.1  christos 		isc_region_t data = *region;
   2037  1.1  christos 		isc_region_t base64_region;
   2038  1.1  christos 		size_t base64_len = ((4 * data.length / 3) + 3) & ~3;
   2039  1.1  christos 
   2040  1.1  christos 		isc_buffer_allocate(mctx, &buf, base64_len);
   2041  1.1  christos 
   2042  1.1  christos 		result = isc_base64_totext(&data, -1, "", buf);
   2043  1.1  christos 		if (result != ISC_R_SUCCESS) {
   2044  1.1  christos 			isc_buffer_free(&buf);
   2045  1.1  christos 			goto error;
   2046  1.1  christos 		}
   2047  1.1  christos 
   2048  1.1  christos 		isc_buffer_usedregion(buf, &base64_region);
   2049  1.1  christos 		INSIST(base64_region.length == base64_len);
   2050  1.1  christos 
   2051  1.1  christos 		base64url_data = isc__nm_base64_to_base64url(
   2052  1.1  christos 			mctx, (const char *)base64_region.base,
   2053  1.1  christos 			base64_region.length, &base64url_data_len);
   2054  1.1  christos 		isc_buffer_free(&buf);
   2055  1.1  christos 		if (base64url_data == NULL) {
   2056  1.1  christos 			goto error;
   2057  1.1  christos 		}
   2058  1.1  christos 
   2059  1.1  christos 		/* len("?dns=") + len(path) + len(base64url) + len("\0") */
   2060  1.1  christos 		path_size = cstream->pathlen + base64url_data_len + 5 + 1;
   2061  1.1  christos 		cstream->GET_path = isc_mem_allocate(mctx, path_size);
   2062  1.1  christos 		cstream->GET_path_len = (size_t)snprintf(
   2063  1.1  christos 			cstream->GET_path, path_size, "%.*s?dns=%s",
   2064  1.1  christos 			(int)cstream->pathlen, cstream->path, base64url_data);
   2065  1.1  christos 
   2066  1.1  christos 		INSIST(cstream->GET_path_len == (path_size - 1));
   2067  1.1  christos 		isc_mem_free(mctx, base64url_data);
   2068  1.1  christos 	}
   2069  1.1  christos 
   2070  1.1  christos 	cstream->sending = true;
   2071  1.1  christos 
   2072  1.4  christos 	sock->h2->connect.cstream = NULL;
   2073  1.1  christos 	result = client_submit_request(session, cstream);
   2074  1.1  christos 	if (result != ISC_R_SUCCESS) {
   2075  1.1  christos 		put_http_cstream(session->mctx, cstream);
   2076  1.1  christos 		goto error;
   2077  1.1  christos 	}
   2078  1.1  christos 
   2079  1.1  christos error:
   2080  1.4  christos 	return result;
   2081  1.1  christos }
   2082  1.1  christos 
   2083  1.1  christos isc_result_t
   2084  1.1  christos isc__nm_http_request(isc_nmhandle_t *handle, isc_region_t *region,
   2085  1.1  christos 		     isc_nm_recv_cb_t cb, void *cbarg) {
   2086  1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
   2087  1.1  christos 	isc_nmsocket_t *sock = NULL;
   2088  1.1  christos 	http_cstream_t *cstream = NULL;
   2089  1.1  christos 
   2090  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   2091  1.1  christos 	REQUIRE(VALID_NMSOCK(handle->sock));
   2092  1.4  christos 	REQUIRE(handle->sock->tid == isc_tid());
   2093  1.4  christos 	REQUIRE(handle->sock->client);
   2094  1.1  christos 
   2095  1.1  christos 	REQUIRE(cb != NULL);
   2096  1.1  christos 
   2097  1.1  christos 	sock = handle->sock;
   2098  1.1  christos 
   2099  1.1  christos 	isc__nm_http_read(handle, cb, cbarg);
   2100  1.4  christos 	if (!http_session_active(handle->sock->h2->session)) {
   2101  1.1  christos 		/* the callback was called by isc__nm_http_read() */
   2102  1.4  christos 		return ISC_R_CANCELED;
   2103  1.1  christos 	}
   2104  1.1  christos 	result = client_send(handle, region);
   2105  1.1  christos 	if (result != ISC_R_SUCCESS) {
   2106  1.1  christos 		goto error;
   2107  1.1  christos 	}
   2108  1.1  christos 
   2109  1.4  christos 	return ISC_R_SUCCESS;
   2110  1.1  christos 
   2111  1.1  christos error:
   2112  1.4  christos 	cstream = sock->h2->connect.cstream;
   2113  1.1  christos 	if (cstream->read_cb != NULL) {
   2114  1.1  christos 		cstream->read_cb(handle, result, NULL, cstream->read_cbarg);
   2115  1.1  christos 	}
   2116  1.4  christos 	return result;
   2117  1.1  christos }
   2118  1.1  christos 
   2119  1.1  christos static int
   2120  1.1  christos server_on_begin_headers_callback(nghttp2_session *ngsession,
   2121  1.1  christos 				 const nghttp2_frame *frame, void *user_data) {
   2122  1.1  christos 	isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
   2123  1.1  christos 	isc_nmsocket_t *socket = NULL;
   2124  1.4  christos 	isc__networker_t *worker = NULL;
   2125  1.4  christos 	isc_sockaddr_t local;
   2126  1.1  christos 
   2127  1.1  christos 	if (frame->hd.type != NGHTTP2_HEADERS ||
   2128  1.1  christos 	    frame->headers.cat != NGHTTP2_HCAT_REQUEST)
   2129  1.1  christos 	{
   2130  1.4  christos 		return 0;
   2131  1.1  christos 	} else if (frame->hd.length > MAX_ALLOWED_DATA_IN_HEADERS) {
   2132  1.4  christos 		return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
   2133  1.1  christos 	}
   2134  1.1  christos 
   2135  1.1  christos 	if (session->nsstreams >= session->max_concurrent_streams) {
   2136  1.4  christos 		return NGHTTP2_ERR_CALLBACK_FAILURE;
   2137  1.1  christos 	}
   2138  1.1  christos 
   2139  1.4  christos 	INSIST(session->handle->sock->tid == isc_tid());
   2140  1.4  christos 
   2141  1.4  christos 	worker = session->handle->sock->worker;
   2142  1.4  christos 	socket = isc_mempool_get(worker->nmsocket_pool);
   2143  1.4  christos 	local = isc_nmhandle_localaddr(session->handle);
   2144  1.4  christos 	isc__nmsocket_init(socket, worker, isc_nm_httpsocket, &local, NULL);
   2145  1.4  christos 	http_initsocket(socket);
   2146  1.4  christos 	socket->peer = isc_nmhandle_peeraddr(session->handle);
   2147  1.4  christos 	*socket->h2 = (isc_nmsocket_h2_t){
   2148  1.1  christos 		.psock = socket,
   2149  1.1  christos 		.stream_id = frame->hd.stream_id,
   2150  1.1  christos 		.headers_error_code = ISC_HTTP_ERROR_SUCCESS,
   2151  1.1  christos 		.request_type = ISC_HTTP_REQ_UNSUPPORTED,
   2152  1.4  christos 		.request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED,
   2153  1.4  christos 		.link = ISC_LINK_INITIALIZER,
   2154  1.1  christos 	};
   2155  1.4  christos 	isc_buffer_initnull(&socket->h2->rbuf);
   2156  1.4  christos 	isc_buffer_initnull(&socket->h2->wbuf);
   2157  1.3  christos 	isc_nm_http_endpoints_attach(
   2158  1.3  christos 		http_get_listener_endpoints(session->serversocket, socket->tid),
   2159  1.4  christos 		&socket->h2->peer_endpoints);
   2160  1.1  christos 	session->nsstreams++;
   2161  1.4  christos 	isc__nm_httpsession_attach(session, &socket->h2->session);
   2162  1.4  christos 	ISC_LIST_APPEND(session->sstreams, socket->h2, link);
   2163  1.5  christos 	session->total_opened_sstreams++;
   2164  1.1  christos 
   2165  1.1  christos 	nghttp2_session_set_stream_user_data(ngsession, frame->hd.stream_id,
   2166  1.1  christos 					     socket);
   2167  1.4  christos 	return 0;
   2168  1.1  christos }
   2169  1.1  christos 
   2170  1.1  christos static isc_http_error_responses_t
   2171  1.1  christos server_handle_path_header(isc_nmsocket_t *socket, const uint8_t *value,
   2172  1.1  christos 			  const size_t valuelen) {
   2173  1.1  christos 	isc_nm_httphandler_t *handler = NULL;
   2174  1.1  christos 	const uint8_t *qstr = NULL;
   2175  1.1  christos 	size_t vlen = valuelen;
   2176  1.1  christos 
   2177  1.1  christos 	qstr = memchr(value, '?', valuelen);
   2178  1.1  christos 	if (qstr != NULL) {
   2179  1.1  christos 		vlen = qstr - value;
   2180  1.1  christos 	}
   2181  1.1  christos 
   2182  1.4  christos 	if (socket->h2->request_path != NULL) {
   2183  1.4  christos 		isc_mem_free(socket->worker->mctx, socket->h2->request_path);
   2184  1.1  christos 	}
   2185  1.4  christos 	socket->h2->request_path = isc_mem_strndup(
   2186  1.4  christos 		socket->worker->mctx, (const char *)value, vlen + 1);
   2187  1.1  christos 
   2188  1.4  christos 	if (!isc_nm_http_path_isvalid(socket->h2->request_path)) {
   2189  1.4  christos 		isc_mem_free(socket->worker->mctx, socket->h2->request_path);
   2190  1.4  christos 		socket->h2->request_path = NULL;
   2191  1.4  christos 		return ISC_HTTP_ERROR_BAD_REQUEST;
   2192  1.1  christos 	}
   2193  1.1  christos 
   2194  1.4  christos 	handler = http_endpoints_find(socket->h2->request_path,
   2195  1.4  christos 				      socket->h2->peer_endpoints);
   2196  1.1  christos 	if (handler != NULL) {
   2197  1.4  christos 		socket->h2->cb = handler->cb;
   2198  1.4  christos 		socket->h2->cbarg = handler->cbarg;
   2199  1.4  christos 	} else {
   2200  1.4  christos 		isc_mem_free(socket->worker->mctx, socket->h2->request_path);
   2201  1.4  christos 		socket->h2->request_path = NULL;
   2202  1.4  christos 		return ISC_HTTP_ERROR_NOT_FOUND;
   2203  1.1  christos 	}
   2204  1.1  christos 
   2205  1.1  christos 	if (qstr != NULL) {
   2206  1.1  christos 		const char *dns_value = NULL;
   2207  1.1  christos 		size_t dns_value_len = 0;
   2208  1.1  christos 
   2209  1.1  christos 		if (isc__nm_parse_httpquery((const char *)qstr, &dns_value,
   2210  1.1  christos 					    &dns_value_len))
   2211  1.1  christos 		{
   2212  1.1  christos 			const size_t decoded_size = dns_value_len / 4 * 3;
   2213  1.1  christos 			if (decoded_size <= MAX_DNS_MESSAGE_SIZE) {
   2214  1.4  christos 				if (socket->h2->query_data != NULL) {
   2215  1.4  christos 					isc_mem_free(socket->worker->mctx,
   2216  1.4  christos 						     socket->h2->query_data);
   2217  1.1  christos 				}
   2218  1.4  christos 				socket->h2->query_data =
   2219  1.1  christos 					isc__nm_base64url_to_base64(
   2220  1.4  christos 						socket->worker->mctx, dns_value,
   2221  1.1  christos 						dns_value_len,
   2222  1.4  christos 						&socket->h2->query_data_len);
   2223  1.5  christos 				socket->h2->session->processed_useful_data +=
   2224  1.5  christos 					dns_value_len;
   2225  1.1  christos 			} else {
   2226  1.4  christos 				socket->h2->query_too_large = true;
   2227  1.4  christos 				return ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
   2228  1.1  christos 			}
   2229  1.1  christos 		} else {
   2230  1.4  christos 			return ISC_HTTP_ERROR_BAD_REQUEST;
   2231  1.1  christos 		}
   2232  1.1  christos 	}
   2233  1.4  christos 	return ISC_HTTP_ERROR_SUCCESS;
   2234  1.1  christos }
   2235  1.1  christos 
   2236  1.1  christos static isc_http_error_responses_t
   2237  1.1  christos server_handle_method_header(isc_nmsocket_t *socket, const uint8_t *value,
   2238  1.1  christos 			    const size_t valuelen) {
   2239  1.1  christos 	const char get[] = "GET";
   2240  1.1  christos 	const char post[] = "POST";
   2241  1.1  christos 
   2242  1.1  christos 	if (HEADER_MATCH(get, value, valuelen)) {
   2243  1.4  christos 		socket->h2->request_type = ISC_HTTP_REQ_GET;
   2244  1.1  christos 	} else if (HEADER_MATCH(post, value, valuelen)) {
   2245  1.4  christos 		socket->h2->request_type = ISC_HTTP_REQ_POST;
   2246  1.1  christos 	} else {
   2247  1.4  christos 		return ISC_HTTP_ERROR_NOT_IMPLEMENTED;
   2248  1.1  christos 	}
   2249  1.4  christos 	return ISC_HTTP_ERROR_SUCCESS;
   2250  1.1  christos }
   2251  1.1  christos 
   2252  1.1  christos static isc_http_error_responses_t
   2253  1.1  christos server_handle_scheme_header(isc_nmsocket_t *socket, const uint8_t *value,
   2254  1.1  christos 			    const size_t valuelen) {
   2255  1.1  christos 	const char http[] = "http";
   2256  1.1  christos 	const char http_secure[] = "https";
   2257  1.1  christos 
   2258  1.1  christos 	if (HEADER_MATCH(http_secure, value, valuelen)) {
   2259  1.4  christos 		socket->h2->request_scheme = ISC_HTTP_SCHEME_HTTP_SECURE;
   2260  1.1  christos 	} else if (HEADER_MATCH(http, value, valuelen)) {
   2261  1.4  christos 		socket->h2->request_scheme = ISC_HTTP_SCHEME_HTTP;
   2262  1.1  christos 	} else {
   2263  1.4  christos 		return ISC_HTTP_ERROR_BAD_REQUEST;
   2264  1.1  christos 	}
   2265  1.4  christos 	return ISC_HTTP_ERROR_SUCCESS;
   2266  1.1  christos }
   2267  1.1  christos 
   2268  1.1  christos static isc_http_error_responses_t
   2269  1.1  christos server_handle_content_length_header(isc_nmsocket_t *socket,
   2270  1.1  christos 				    const uint8_t *value,
   2271  1.1  christos 				    const size_t valuelen) {
   2272  1.1  christos 	char tmp[32] = { 0 };
   2273  1.1  christos 	const size_t tmplen = sizeof(tmp) - 1;
   2274  1.1  christos 
   2275  1.1  christos 	strncpy(tmp, (const char *)value,
   2276  1.1  christos 		valuelen > tmplen ? tmplen : valuelen);
   2277  1.4  christos 	socket->h2->content_length = strtoul(tmp, NULL, 10);
   2278  1.4  christos 	if (socket->h2->content_length > MAX_DNS_MESSAGE_SIZE) {
   2279  1.4  christos 		return ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
   2280  1.4  christos 	} else if (socket->h2->content_length == 0) {
   2281  1.4  christos 		return ISC_HTTP_ERROR_BAD_REQUEST;
   2282  1.1  christos 	}
   2283  1.4  christos 	return ISC_HTTP_ERROR_SUCCESS;
   2284  1.1  christos }
   2285  1.1  christos 
   2286  1.1  christos static isc_http_error_responses_t
   2287  1.1  christos server_handle_content_type_header(isc_nmsocket_t *socket, const uint8_t *value,
   2288  1.1  christos 				  const size_t valuelen) {
   2289  1.1  christos 	const char type_dns_message[] = DNS_MEDIA_TYPE;
   2290  1.1  christos 	isc_http_error_responses_t resp = ISC_HTTP_ERROR_SUCCESS;
   2291  1.1  christos 
   2292  1.1  christos 	UNUSED(socket);
   2293  1.1  christos 
   2294  1.1  christos 	if (!HEADER_MATCH(type_dns_message, value, valuelen)) {
   2295  1.1  christos 		resp = ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE;
   2296  1.1  christos 	}
   2297  1.4  christos 	return resp;
   2298  1.1  christos }
   2299  1.1  christos 
   2300  1.1  christos static isc_http_error_responses_t
   2301  1.1  christos server_handle_header(isc_nmsocket_t *socket, const uint8_t *name,
   2302  1.1  christos 		     size_t namelen, const uint8_t *value,
   2303  1.1  christos 		     const size_t valuelen) {
   2304  1.1  christos 	isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
   2305  1.1  christos 	bool was_error;
   2306  1.1  christos 	const char path[] = ":path";
   2307  1.1  christos 	const char method[] = ":method";
   2308  1.1  christos 	const char scheme[] = ":scheme";
   2309  1.1  christos 	const char content_length[] = "Content-Length";
   2310  1.1  christos 	const char content_type[] = "Content-Type";
   2311  1.1  christos 
   2312  1.4  christos 	was_error = socket->h2->headers_error_code != ISC_HTTP_ERROR_SUCCESS;
   2313  1.1  christos 	/*
   2314  1.1  christos 	 * process Content-Length even when there was an error,
   2315  1.1  christos 	 * to drop the connection earlier if required.
   2316  1.1  christos 	 */
   2317  1.1  christos 	if (HEADER_MATCH(content_length, name, namelen)) {
   2318  1.1  christos 		code = server_handle_content_length_header(socket, value,
   2319  1.1  christos 							   valuelen);
   2320  1.1  christos 	} else if (!was_error && HEADER_MATCH(path, name, namelen)) {
   2321  1.1  christos 		code = server_handle_path_header(socket, value, valuelen);
   2322  1.1  christos 	} else if (!was_error && HEADER_MATCH(method, name, namelen)) {
   2323  1.1  christos 		code = server_handle_method_header(socket, value, valuelen);
   2324  1.1  christos 	} else if (!was_error && HEADER_MATCH(scheme, name, namelen)) {
   2325  1.1  christos 		code = server_handle_scheme_header(socket, value, valuelen);
   2326  1.1  christos 	} else if (!was_error && HEADER_MATCH(content_type, name, namelen)) {
   2327  1.1  christos 		code = server_handle_content_type_header(socket, value,
   2328  1.1  christos 							 valuelen);
   2329  1.1  christos 	}
   2330  1.1  christos 
   2331  1.4  christos 	return code;
   2332  1.1  christos }
   2333  1.1  christos 
   2334  1.1  christos static int
   2335  1.1  christos server_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
   2336  1.1  christos 			  const uint8_t *name, size_t namelen,
   2337  1.1  christos 			  const uint8_t *value, size_t valuelen, uint8_t flags,
   2338  1.1  christos 			  void *user_data) {
   2339  1.1  christos 	isc_nmsocket_t *socket = NULL;
   2340  1.1  christos 	isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
   2341  1.1  christos 
   2342  1.1  christos 	UNUSED(flags);
   2343  1.1  christos 	UNUSED(user_data);
   2344  1.1  christos 
   2345  1.1  christos 	socket = nghttp2_session_get_stream_user_data(session,
   2346  1.1  christos 						      frame->hd.stream_id);
   2347  1.1  christos 	if (socket == NULL) {
   2348  1.4  christos 		return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
   2349  1.1  christos 	}
   2350  1.1  christos 
   2351  1.4  christos 	socket->h2->headers_data_processed += (namelen + valuelen);
   2352  1.1  christos 
   2353  1.1  christos 	switch (frame->hd.type) {
   2354  1.1  christos 	case NGHTTP2_HEADERS:
   2355  1.1  christos 		if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
   2356  1.1  christos 			break;
   2357  1.1  christos 		}
   2358  1.1  christos 		code = server_handle_header(socket, name, namelen, value,
   2359  1.1  christos 					    valuelen);
   2360  1.1  christos 		break;
   2361  1.1  christos 	}
   2362  1.1  christos 
   2363  1.1  christos 	INSIST(socket != NULL);
   2364  1.1  christos 
   2365  1.4  christos 	if (socket->h2->headers_data_processed > MAX_ALLOWED_DATA_IN_HEADERS) {
   2366  1.4  christos 		return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
   2367  1.4  christos 	} else if (socket->h2->content_length > MAX_ALLOWED_DATA_IN_POST) {
   2368  1.4  christos 		return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
   2369  1.1  christos 	}
   2370  1.1  christos 
   2371  1.1  christos 	if (code == ISC_HTTP_ERROR_SUCCESS) {
   2372  1.4  christos 		return 0;
   2373  1.1  christos 	} else {
   2374  1.4  christos 		socket->h2->headers_error_code = code;
   2375  1.1  christos 	}
   2376  1.1  christos 
   2377  1.4  christos 	return 0;
   2378  1.1  christos }
   2379  1.1  christos 
   2380  1.1  christos static ssize_t
   2381  1.1  christos server_read_callback(nghttp2_session *ngsession, int32_t stream_id,
   2382  1.1  christos 		     uint8_t *buf, size_t length, uint32_t *data_flags,
   2383  1.1  christos 		     nghttp2_data_source *source, void *user_data) {
   2384  1.1  christos 	isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
   2385  1.1  christos 	isc_nmsocket_t *socket = (isc_nmsocket_t *)source->ptr;
   2386  1.1  christos 	size_t buflen;
   2387  1.1  christos 
   2388  1.4  christos 	REQUIRE(socket->h2->stream_id == stream_id);
   2389  1.1  christos 
   2390  1.1  christos 	UNUSED(ngsession);
   2391  1.1  christos 	UNUSED(session);
   2392  1.1  christos 
   2393  1.4  christos 	buflen = isc_buffer_remaininglength(&socket->h2->wbuf);
   2394  1.1  christos 	if (buflen > length) {
   2395  1.1  christos 		buflen = length;
   2396  1.1  christos 	}
   2397  1.1  christos 
   2398  1.1  christos 	if (buflen > 0) {
   2399  1.4  christos 		(void)memmove(buf, isc_buffer_current(&socket->h2->wbuf),
   2400  1.1  christos 			      buflen);
   2401  1.4  christos 		isc_buffer_forward(&socket->h2->wbuf, buflen);
   2402  1.1  christos 	}
   2403  1.1  christos 
   2404  1.4  christos 	if (isc_buffer_remaininglength(&socket->h2->wbuf) == 0) {
   2405  1.1  christos 		*data_flags |= NGHTTP2_DATA_FLAG_EOF;
   2406  1.1  christos 	}
   2407  1.1  christos 
   2408  1.4  christos 	return buflen;
   2409  1.1  christos }
   2410  1.1  christos 
   2411  1.1  christos static isc_result_t
   2412  1.1  christos server_send_response(nghttp2_session *ngsession, int32_t stream_id,
   2413  1.1  christos 		     const nghttp2_nv *nva, size_t nvlen,
   2414  1.1  christos 		     isc_nmsocket_t *socket) {
   2415  1.1  christos 	nghttp2_data_provider data_prd;
   2416  1.1  christos 	int rv;
   2417  1.1  christos 
   2418  1.4  christos 	if (socket->h2->response_submitted) {
   2419  1.1  christos 		/* NGHTTP2 will gladly accept new response (write request)
   2420  1.1  christos 		 * from us even though we cannot send more than one over the
   2421  1.1  christos 		 * same HTTP/2 stream. Thus, we need to handle this case
   2422  1.1  christos 		 * manually. We will return failure code so that it will be
   2423  1.1  christos 		 * passed to the write callback. */
   2424  1.4  christos 		return ISC_R_FAILURE;
   2425  1.1  christos 	}
   2426  1.1  christos 
   2427  1.1  christos 	data_prd.source.ptr = socket;
   2428  1.1  christos 	data_prd.read_callback = server_read_callback;
   2429  1.1  christos 
   2430  1.1  christos 	rv = nghttp2_submit_response(ngsession, stream_id, nva, nvlen,
   2431  1.1  christos 				     &data_prd);
   2432  1.1  christos 	if (rv != 0) {
   2433  1.4  christos 		return ISC_R_FAILURE;
   2434  1.1  christos 	}
   2435  1.1  christos 
   2436  1.4  christos 	socket->h2->response_submitted = true;
   2437  1.4  christos 	return ISC_R_SUCCESS;
   2438  1.1  christos }
   2439  1.1  christos 
   2440  1.3  christos #define MAKE_ERROR_REPLY(tag, code, desc) \
   2441  1.3  christos 	{ tag, MAKE_NV2(":status", #code), desc }
   2442  1.1  christos 
   2443  1.1  christos /*
   2444  1.1  christos  * Here we use roughly the same error codes that Unbound uses.
   2445  1.1  christos  * (https://blog.nlnetlabs.nl/dns-over-https-in-unbound/)
   2446  1.1  christos  */
   2447  1.1  christos 
   2448  1.1  christos static struct http_error_responses {
   2449  1.1  christos 	const isc_http_error_responses_t type;
   2450  1.1  christos 	const nghttp2_nv header;
   2451  1.1  christos 	const char *desc;
   2452  1.1  christos } error_responses[] = {
   2453  1.1  christos 	MAKE_ERROR_REPLY(ISC_HTTP_ERROR_BAD_REQUEST, 400, "Bad Request"),
   2454  1.1  christos 	MAKE_ERROR_REPLY(ISC_HTTP_ERROR_NOT_FOUND, 404, "Not Found"),
   2455  1.1  christos 	MAKE_ERROR_REPLY(ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE, 413,
   2456  1.1  christos 			 "Payload Too Large"),
   2457  1.1  christos 	MAKE_ERROR_REPLY(ISC_HTTP_ERROR_URI_TOO_LONG, 414, "URI Too Long"),
   2458  1.1  christos 	MAKE_ERROR_REPLY(ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE, 415,
   2459  1.1  christos 			 "Unsupported Media Type"),
   2460  1.1  christos 	MAKE_ERROR_REPLY(ISC_HTTP_ERROR_GENERIC, 500, "Internal Server Error"),
   2461  1.1  christos 	MAKE_ERROR_REPLY(ISC_HTTP_ERROR_NOT_IMPLEMENTED, 501, "Not Implemented")
   2462  1.1  christos };
   2463  1.1  christos 
   2464  1.1  christos static void
   2465  1.1  christos log_server_error_response(const isc_nmsocket_t *socket,
   2466  1.1  christos 			  const struct http_error_responses *response) {
   2467  1.1  christos 	const int log_level = ISC_LOG_DEBUG(1);
   2468  1.1  christos 	char client_sabuf[ISC_SOCKADDR_FORMATSIZE];
   2469  1.1  christos 	char local_sabuf[ISC_SOCKADDR_FORMATSIZE];
   2470  1.1  christos 
   2471  1.1  christos 	if (!isc_log_wouldlog(isc_lctx, log_level)) {
   2472  1.1  christos 		return;
   2473  1.1  christos 	}
   2474  1.1  christos 
   2475  1.3  christos 	isc_sockaddr_format(&socket->peer, client_sabuf, sizeof(client_sabuf));
   2476  1.3  christos 	isc_sockaddr_format(&socket->iface, local_sabuf, sizeof(local_sabuf));
   2477  1.4  christos 	isc__nmsocket_log(socket, log_level,
   2478  1.4  christos 			  "HTTP/2 request from %s (on %s) failed: %s %s",
   2479  1.4  christos 			  client_sabuf, local_sabuf, response->header.value,
   2480  1.4  christos 			  response->desc);
   2481  1.1  christos }
   2482  1.1  christos 
   2483  1.1  christos static isc_result_t
   2484  1.1  christos server_send_error_response(const isc_http_error_responses_t error,
   2485  1.1  christos 			   nghttp2_session *ngsession, isc_nmsocket_t *socket) {
   2486  1.1  christos 	void *base;
   2487  1.1  christos 
   2488  1.1  christos 	REQUIRE(error != ISC_HTTP_ERROR_SUCCESS);
   2489  1.1  christos 
   2490  1.4  christos 	base = isc_buffer_base(&socket->h2->rbuf);
   2491  1.1  christos 	if (base != NULL) {
   2492  1.4  christos 		isc_mem_free(socket->h2->session->mctx, base);
   2493  1.4  christos 		isc_buffer_initnull(&socket->h2->rbuf);
   2494  1.1  christos 	}
   2495  1.1  christos 
   2496  1.1  christos 	/* We do not want the error response to be cached anywhere. */
   2497  1.4  christos 	socket->h2->min_ttl = 0;
   2498  1.1  christos 
   2499  1.1  christos 	for (size_t i = 0;
   2500  1.1  christos 	     i < sizeof(error_responses) / sizeof(error_responses[0]); i++)
   2501  1.1  christos 	{
   2502  1.1  christos 		if (error_responses[i].type == error) {
   2503  1.1  christos 			log_server_error_response(socket, &error_responses[i]);
   2504  1.4  christos 			return server_send_response(
   2505  1.4  christos 				ngsession, socket->h2->stream_id,
   2506  1.4  christos 				&error_responses[i].header, 1, socket);
   2507  1.1  christos 		}
   2508  1.1  christos 	}
   2509  1.1  christos 
   2510  1.4  christos 	return server_send_error_response(ISC_HTTP_ERROR_GENERIC, ngsession,
   2511  1.4  christos 					  socket);
   2512  1.1  christos }
   2513  1.1  christos 
   2514  1.1  christos static void
   2515  1.3  christos server_call_cb(isc_nmsocket_t *socket, const isc_result_t result,
   2516  1.3  christos 	       isc_region_t *data) {
   2517  1.1  christos 	isc_nmhandle_t *handle = NULL;
   2518  1.1  christos 
   2519  1.1  christos 	REQUIRE(VALID_NMSOCK(socket));
   2520  1.1  christos 
   2521  1.3  christos 	/*
   2522  1.3  christos 	 * In some cases the callback could not have been set (e.g. when
   2523  1.3  christos 	 * the stream was closed prematurely (before processing its HTTP
   2524  1.3  christos 	 * path).
   2525  1.3  christos 	 */
   2526  1.4  christos 	if (socket->h2->cb == NULL) {
   2527  1.3  christos 		return;
   2528  1.3  christos 	}
   2529  1.3  christos 
   2530  1.3  christos 	handle = isc__nmhandle_get(socket, NULL, NULL);
   2531  1.3  christos 	if (result != ISC_R_SUCCESS) {
   2532  1.3  christos 		data = NULL;
   2533  1.5  christos 	} else if (socket->h2->session->handle != NULL) {
   2534  1.5  christos 		isc__nmsocket_timer_restart(socket->h2->session->handle->sock);
   2535  1.5  christos 	}
   2536  1.5  christos 	if (result == ISC_R_SUCCESS) {
   2537  1.5  christos 		socket->h2->request_received = true;
   2538  1.5  christos 		socket->h2->session->received++;
   2539  1.3  christos 	}
   2540  1.4  christos 	socket->h2->cb(handle, result, data, socket->h2->cbarg);
   2541  1.1  christos 	isc_nmhandle_detach(&handle);
   2542  1.1  christos }
   2543  1.1  christos 
   2544  1.1  christos void
   2545  1.1  christos isc__nm_http_bad_request(isc_nmhandle_t *handle) {
   2546  1.1  christos 	isc_nmsocket_t *sock = NULL;
   2547  1.1  christos 
   2548  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   2549  1.1  christos 	REQUIRE(VALID_NMSOCK(handle->sock));
   2550  1.1  christos 	sock = handle->sock;
   2551  1.1  christos 	REQUIRE(sock->type == isc_nm_httpsocket);
   2552  1.4  christos 	REQUIRE(!sock->client);
   2553  1.4  christos 	REQUIRE(VALID_HTTP2_SESSION(sock->h2->session));
   2554  1.1  christos 
   2555  1.5  christos 	if (sock->h2->response_submitted ||
   2556  1.5  christos 	    !http_session_active(sock->h2->session))
   2557  1.5  christos 	{
   2558  1.5  christos 		return;
   2559  1.5  christos 	}
   2560  1.5  christos 
   2561  1.1  christos 	(void)server_send_error_response(ISC_HTTP_ERROR_BAD_REQUEST,
   2562  1.4  christos 					 sock->h2->session->ngsession, sock);
   2563  1.1  christos }
   2564  1.1  christos 
   2565  1.1  christos static int
   2566  1.3  christos server_on_request_recv(nghttp2_session *ngsession, isc_nmsocket_t *socket) {
   2567  1.1  christos 	isc_result_t result;
   2568  1.1  christos 	isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
   2569  1.1  christos 	isc_region_t data;
   2570  1.1  christos 	uint8_t tmp_buf[MAX_DNS_MESSAGE_SIZE];
   2571  1.1  christos 
   2572  1.4  christos 	code = socket->h2->headers_error_code;
   2573  1.1  christos 	if (code != ISC_HTTP_ERROR_SUCCESS) {
   2574  1.1  christos 		goto error;
   2575  1.1  christos 	}
   2576  1.1  christos 
   2577  1.4  christos 	if (socket->h2->request_path == NULL || socket->h2->cb == NULL) {
   2578  1.1  christos 		code = ISC_HTTP_ERROR_NOT_FOUND;
   2579  1.4  christos 	} else if (socket->h2->request_type == ISC_HTTP_REQ_POST &&
   2580  1.4  christos 		   socket->h2->content_length == 0)
   2581  1.1  christos 	{
   2582  1.1  christos 		code = ISC_HTTP_ERROR_BAD_REQUEST;
   2583  1.4  christos 	} else if (socket->h2->request_type == ISC_HTTP_REQ_POST &&
   2584  1.4  christos 		   isc_buffer_usedlength(&socket->h2->rbuf) >
   2585  1.4  christos 			   socket->h2->content_length)
   2586  1.1  christos 	{
   2587  1.1  christos 		code = ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
   2588  1.4  christos 	} else if (socket->h2->request_type == ISC_HTTP_REQ_POST &&
   2589  1.4  christos 		   isc_buffer_usedlength(&socket->h2->rbuf) !=
   2590  1.4  christos 			   socket->h2->content_length)
   2591  1.1  christos 	{
   2592  1.1  christos 		code = ISC_HTTP_ERROR_BAD_REQUEST;
   2593  1.4  christos 	} else if (socket->h2->request_type == ISC_HTTP_REQ_POST &&
   2594  1.4  christos 		   socket->h2->query_data != NULL)
   2595  1.1  christos 	{
   2596  1.1  christos 		/* The spec does not mention which value the query string for
   2597  1.1  christos 		 * POST should have. For GET we use its value to decode a DNS
   2598  1.1  christos 		 * message from it, for POST the message is transferred in the
   2599  1.1  christos 		 * body of the request. Taking it into account, it is much safer
   2600  1.1  christos 		 * to treat POST
   2601  1.1  christos 		 * requests with query strings as malformed ones. */
   2602  1.1  christos 		code = ISC_HTTP_ERROR_BAD_REQUEST;
   2603  1.4  christos 	} else if (socket->h2->request_type == ISC_HTTP_REQ_GET &&
   2604  1.4  christos 		   socket->h2->content_length > 0)
   2605  1.1  christos 	{
   2606  1.1  christos 		code = ISC_HTTP_ERROR_BAD_REQUEST;
   2607  1.4  christos 	} else if (socket->h2->request_type == ISC_HTTP_REQ_GET &&
   2608  1.4  christos 		   socket->h2->query_data == NULL)
   2609  1.1  christos 	{
   2610  1.1  christos 		/* A GET request without any query data - there is nothing to
   2611  1.1  christos 		 * decode. */
   2612  1.4  christos 		INSIST(socket->h2->query_data_len == 0);
   2613  1.1  christos 		code = ISC_HTTP_ERROR_BAD_REQUEST;
   2614  1.1  christos 	}
   2615  1.1  christos 
   2616  1.1  christos 	if (code != ISC_HTTP_ERROR_SUCCESS) {
   2617  1.1  christos 		goto error;
   2618  1.1  christos 	}
   2619  1.1  christos 
   2620  1.4  christos 	if (socket->h2->request_type == ISC_HTTP_REQ_GET) {
   2621  1.1  christos 		isc_buffer_t decoded_buf;
   2622  1.1  christos 		isc_buffer_init(&decoded_buf, tmp_buf, sizeof(tmp_buf));
   2623  1.4  christos 		if (isc_base64_decodestring(socket->h2->query_data,
   2624  1.1  christos 					    &decoded_buf) != ISC_R_SUCCESS)
   2625  1.1  christos 		{
   2626  1.1  christos 			code = ISC_HTTP_ERROR_BAD_REQUEST;
   2627  1.1  christos 			goto error;
   2628  1.1  christos 		}
   2629  1.1  christos 		isc_buffer_usedregion(&decoded_buf, &data);
   2630  1.4  christos 	} else if (socket->h2->request_type == ISC_HTTP_REQ_POST) {
   2631  1.4  christos 		INSIST(socket->h2->content_length > 0);
   2632  1.4  christos 		isc_buffer_usedregion(&socket->h2->rbuf, &data);
   2633  1.1  christos 	} else {
   2634  1.1  christos 		UNREACHABLE();
   2635  1.1  christos 	}
   2636  1.1  christos 
   2637  1.3  christos 	server_call_cb(socket, ISC_R_SUCCESS, &data);
   2638  1.1  christos 
   2639  1.4  christos 	return 0;
   2640  1.1  christos 
   2641  1.1  christos error:
   2642  1.1  christos 	result = server_send_error_response(code, ngsession, socket);
   2643  1.1  christos 	if (result != ISC_R_SUCCESS) {
   2644  1.4  christos 		return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
   2645  1.1  christos 	}
   2646  1.4  christos 	return 0;
   2647  1.1  christos }
   2648  1.1  christos 
   2649  1.4  christos static void
   2650  1.4  christos http_send_cb(void *arg);
   2651  1.4  christos 
   2652  1.1  christos void
   2653  1.1  christos isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
   2654  1.1  christos 		  isc_nm_cb_t cb, void *cbarg) {
   2655  1.1  christos 	isc_nmsocket_t *sock = NULL;
   2656  1.1  christos 	isc__nm_uvreq_t *uvreq = NULL;
   2657  1.1  christos 
   2658  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   2659  1.1  christos 
   2660  1.1  christos 	sock = handle->sock;
   2661  1.1  christos 
   2662  1.1  christos 	REQUIRE(VALID_NMSOCK(sock));
   2663  1.4  christos 	REQUIRE(sock->tid == isc_tid());
   2664  1.1  christos 
   2665  1.4  christos 	uvreq = isc__nm_uvreq_get(sock);
   2666  1.1  christos 	isc_nmhandle_attach(handle, &uvreq->handle);
   2667  1.1  christos 	uvreq->cb.send = cb;
   2668  1.1  christos 	uvreq->cbarg = cbarg;
   2669  1.1  christos 
   2670  1.1  christos 	uvreq->uvbuf.base = (char *)region->base;
   2671  1.1  christos 	uvreq->uvbuf.len = region->length;
   2672  1.1  christos 
   2673  1.4  christos 	isc_job_run(sock->worker->loop, &uvreq->job, http_send_cb, uvreq);
   2674  1.1  christos }
   2675  1.1  christos 
   2676  1.1  christos static void
   2677  1.1  christos failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
   2678  1.1  christos 	       isc_result_t eresult) {
   2679  1.1  christos 	REQUIRE(VALID_NMSOCK(sock));
   2680  1.1  christos 	REQUIRE(VALID_UVREQ(req));
   2681  1.1  christos 
   2682  1.1  christos 	if (req->cb.send != NULL) {
   2683  1.1  christos 		isc__nm_sendcb(sock, req, eresult, true);
   2684  1.1  christos 	} else {
   2685  1.4  christos 		isc__nm_uvreq_put(&req);
   2686  1.1  christos 	}
   2687  1.1  christos }
   2688  1.1  christos 
   2689  1.1  christos static void
   2690  1.1  christos client_httpsend(isc_nmhandle_t *handle, isc_nmsocket_t *sock,
   2691  1.1  christos 		isc__nm_uvreq_t *req) {
   2692  1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
   2693  1.1  christos 	isc_nm_cb_t cb = req->cb.send;
   2694  1.1  christos 	void *cbarg = req->cbarg;
   2695  1.1  christos 
   2696  1.1  christos 	result = client_send(
   2697  1.1  christos 		handle,
   2698  1.1  christos 		&(isc_region_t){ (uint8_t *)req->uvbuf.base, req->uvbuf.len });
   2699  1.1  christos 	if (result != ISC_R_SUCCESS) {
   2700  1.1  christos 		failed_send_cb(sock, req, result);
   2701  1.1  christos 		return;
   2702  1.1  christos 	}
   2703  1.1  christos 
   2704  1.4  christos 	http_do_bio(sock->h2->session, handle, cb, cbarg);
   2705  1.4  christos 	isc__nm_uvreq_put(&req);
   2706  1.1  christos }
   2707  1.1  christos 
   2708  1.1  christos static void
   2709  1.1  christos server_httpsend(isc_nmhandle_t *handle, isc_nmsocket_t *sock,
   2710  1.1  christos 		isc__nm_uvreq_t *req) {
   2711  1.1  christos 	size_t content_len_buf_len, cache_control_buf_len;
   2712  1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
   2713  1.1  christos 	isc_nm_cb_t cb = req->cb.send;
   2714  1.1  christos 	void *cbarg = req->cbarg;
   2715  1.1  christos 	if (isc__nmsocket_closing(sock) ||
   2716  1.1  christos 	    !http_session_active(handle->httpsession))
   2717  1.1  christos 	{
   2718  1.1  christos 		failed_send_cb(sock, req, ISC_R_CANCELED);
   2719  1.1  christos 		return;
   2720  1.1  christos 	}
   2721  1.1  christos 
   2722  1.4  christos 	INSIST(handle->sock->tid == isc_tid());
   2723  1.1  christos 	INSIST(VALID_NMHANDLE(handle->httpsession->handle));
   2724  1.1  christos 	INSIST(VALID_NMSOCK(handle->httpsession->handle->sock));
   2725  1.1  christos 
   2726  1.4  christos 	isc_buffer_init(&sock->h2->wbuf, req->uvbuf.base, req->uvbuf.len);
   2727  1.4  christos 	isc_buffer_add(&sock->h2->wbuf, req->uvbuf.len);
   2728  1.1  christos 
   2729  1.4  christos 	content_len_buf_len = snprintf(sock->h2->clenbuf,
   2730  1.4  christos 				       sizeof(sock->h2->clenbuf), "%lu",
   2731  1.1  christos 				       (unsigned long)req->uvbuf.len);
   2732  1.4  christos 	if (sock->h2->min_ttl == 0) {
   2733  1.1  christos 		cache_control_buf_len =
   2734  1.4  christos 			snprintf(sock->h2->cache_control_buf,
   2735  1.4  christos 				 sizeof(sock->h2->cache_control_buf), "%s",
   2736  1.1  christos 				 DEFAULT_CACHE_CONTROL);
   2737  1.1  christos 	} else {
   2738  1.1  christos 		cache_control_buf_len =
   2739  1.4  christos 			snprintf(sock->h2->cache_control_buf,
   2740  1.4  christos 				 sizeof(sock->h2->cache_control_buf),
   2741  1.4  christos 				 "max-age=%" PRIu32, sock->h2->min_ttl);
   2742  1.1  christos 	}
   2743  1.1  christos 	const nghttp2_nv hdrs[] = { MAKE_NV2(":status", "200"),
   2744  1.1  christos 				    MAKE_NV2("Content-Type", DNS_MEDIA_TYPE),
   2745  1.4  christos 				    MAKE_NV("Content-Length", sock->h2->clenbuf,
   2746  1.1  christos 					    content_len_buf_len),
   2747  1.1  christos 				    MAKE_NV("Cache-Control",
   2748  1.4  christos 					    sock->h2->cache_control_buf,
   2749  1.1  christos 					    cache_control_buf_len) };
   2750  1.1  christos 
   2751  1.1  christos 	result = server_send_response(handle->httpsession->ngsession,
   2752  1.4  christos 				      sock->h2->stream_id, hdrs,
   2753  1.1  christos 				      sizeof(hdrs) / sizeof(nghttp2_nv), sock);
   2754  1.1  christos 
   2755  1.1  christos 	if (result == ISC_R_SUCCESS) {
   2756  1.1  christos 		http_do_bio(handle->httpsession, handle, cb, cbarg);
   2757  1.1  christos 	} else {
   2758  1.1  christos 		cb(handle, result, cbarg);
   2759  1.1  christos 	}
   2760  1.4  christos 	isc__nm_uvreq_put(&req);
   2761  1.1  christos }
   2762  1.1  christos 
   2763  1.4  christos static void
   2764  1.4  christos http_send_cb(void *arg) {
   2765  1.4  christos 	isc__nm_uvreq_t *req = arg;
   2766  1.4  christos 
   2767  1.4  christos 	REQUIRE(VALID_UVREQ(req));
   2768  1.1  christos 
   2769  1.4  christos 	isc_nmsocket_t *sock = req->sock;
   2770  1.1  christos 
   2771  1.1  christos 	REQUIRE(VALID_NMSOCK(sock));
   2772  1.4  christos 	REQUIRE(VALID_HTTP2_SESSION(sock->h2->session));
   2773  1.1  christos 
   2774  1.4  christos 	isc_nmhandle_t *handle = req->handle;
   2775  1.1  christos 
   2776  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   2777  1.1  christos 
   2778  1.4  christos 	isc_nm_http_session_t *session = sock->h2->session;
   2779  1.1  christos 	if (session != NULL && session->client) {
   2780  1.1  christos 		client_httpsend(handle, sock, req);
   2781  1.1  christos 	} else {
   2782  1.1  christos 		server_httpsend(handle, sock, req);
   2783  1.1  christos 	}
   2784  1.1  christos }
   2785  1.1  christos 
   2786  1.1  christos void
   2787  1.1  christos isc__nm_http_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
   2788  1.1  christos 	isc_result_t result;
   2789  1.1  christos 	http_cstream_t *cstream = NULL;
   2790  1.1  christos 	isc_nm_http_session_t *session = NULL;
   2791  1.1  christos 
   2792  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   2793  1.1  christos 
   2794  1.4  christos 	session = handle->sock->h2->session;
   2795  1.1  christos 	if (!http_session_active(session)) {
   2796  1.1  christos 		cb(handle, ISC_R_CANCELED, NULL, cbarg);
   2797  1.1  christos 		return;
   2798  1.1  christos 	}
   2799  1.1  christos 
   2800  1.1  christos 	result = get_http_cstream(handle->sock, &cstream);
   2801  1.1  christos 	if (result != ISC_R_SUCCESS) {
   2802  1.1  christos 		return;
   2803  1.1  christos 	}
   2804  1.1  christos 
   2805  1.4  christos 	handle->sock->h2->connect.cstream = cstream;
   2806  1.1  christos 	cstream->read_cb = cb;
   2807  1.1  christos 	cstream->read_cbarg = cbarg;
   2808  1.1  christos 	cstream->reading = true;
   2809  1.1  christos 
   2810  1.1  christos 	if (cstream->sending) {
   2811  1.1  christos 		result = client_submit_request(session, cstream);
   2812  1.1  christos 		if (result != ISC_R_SUCCESS) {
   2813  1.1  christos 			put_http_cstream(session->mctx, cstream);
   2814  1.1  christos 			return;
   2815  1.1  christos 		}
   2816  1.1  christos 
   2817  1.1  christos 		http_do_bio(session, NULL, NULL, NULL);
   2818  1.1  christos 	}
   2819  1.1  christos }
   2820  1.1  christos 
   2821  1.1  christos static int
   2822  1.1  christos server_on_frame_recv_callback(nghttp2_session *ngsession,
   2823  1.1  christos 			      const nghttp2_frame *frame, void *user_data) {
   2824  1.1  christos 	isc_nmsocket_t *socket = NULL;
   2825  1.1  christos 
   2826  1.3  christos 	UNUSED(user_data);
   2827  1.3  christos 
   2828  1.1  christos 	switch (frame->hd.type) {
   2829  1.1  christos 	case NGHTTP2_DATA:
   2830  1.1  christos 	case NGHTTP2_HEADERS:
   2831  1.1  christos 		/* Check that the client request has finished */
   2832  1.1  christos 		if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
   2833  1.1  christos 			socket = nghttp2_session_get_stream_user_data(
   2834  1.1  christos 				ngsession, frame->hd.stream_id);
   2835  1.1  christos 
   2836  1.1  christos 			/*
   2837  1.4  christos 			 * For DATA and HEADERS frame,
   2838  1.4  christos 			 * this callback may be called
   2839  1.4  christos 			 * after
   2840  1.4  christos 			 * on_stream_close_callback.
   2841  1.4  christos 			 * Check that the stream is
   2842  1.4  christos 			 * still alive.
   2843  1.1  christos 			 */
   2844  1.1  christos 			if (socket == NULL) {
   2845  1.4  christos 				return 0;
   2846  1.1  christos 			}
   2847  1.1  christos 
   2848  1.4  christos 			return server_on_request_recv(ngsession, socket);
   2849  1.1  christos 		}
   2850  1.1  christos 		break;
   2851  1.1  christos 	default:
   2852  1.1  christos 		break;
   2853  1.1  christos 	}
   2854  1.4  christos 	return 0;
   2855  1.1  christos }
   2856  1.1  christos 
   2857  1.1  christos static void
   2858  1.1  christos initialize_nghttp2_server_session(isc_nm_http_session_t *session) {
   2859  1.1  christos 	nghttp2_session_callbacks *callbacks = NULL;
   2860  1.1  christos 	nghttp2_mem mem;
   2861  1.1  christos 
   2862  1.1  christos 	init_nghttp2_mem(session->mctx, &mem);
   2863  1.1  christos 
   2864  1.1  christos 	RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
   2865  1.1  christos 
   2866  1.1  christos 	nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
   2867  1.1  christos 		callbacks, on_data_chunk_recv_callback);
   2868  1.1  christos 
   2869  1.1  christos 	nghttp2_session_callbacks_set_on_stream_close_callback(
   2870  1.1  christos 		callbacks, on_stream_close_callback);
   2871  1.1  christos 
   2872  1.1  christos 	nghttp2_session_callbacks_set_on_header_callback(
   2873  1.1  christos 		callbacks, server_on_header_callback);
   2874  1.1  christos 
   2875  1.1  christos 	nghttp2_session_callbacks_set_on_begin_headers_callback(
   2876  1.1  christos 		callbacks, server_on_begin_headers_callback);
   2877  1.1  christos 
   2878  1.1  christos 	nghttp2_session_callbacks_set_on_frame_recv_callback(
   2879  1.1  christos 		callbacks, server_on_frame_recv_callback);
   2880  1.1  christos 
   2881  1.1  christos 	RUNTIME_CHECK(nghttp2_session_server_new3(&session->ngsession,
   2882  1.1  christos 						  callbacks, session, NULL,
   2883  1.1  christos 						  &mem) == 0);
   2884  1.1  christos 
   2885  1.1  christos 	nghttp2_session_callbacks_del(callbacks);
   2886  1.1  christos }
   2887  1.1  christos 
   2888  1.1  christos static int
   2889  1.1  christos server_send_connection_header(isc_nm_http_session_t *session) {
   2890  1.1  christos 	nghttp2_settings_entry iv[1] = {
   2891  1.1  christos 		{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
   2892  1.1  christos 		  session->max_concurrent_streams }
   2893  1.1  christos 	};
   2894  1.1  christos 	int rv;
   2895  1.1  christos 
   2896  1.1  christos 	rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv,
   2897  1.1  christos 				     1);
   2898  1.1  christos 	if (rv != 0) {
   2899  1.4  christos 		return -1;
   2900  1.1  christos 	}
   2901  1.4  christos 	return 0;
   2902  1.1  christos }
   2903  1.1  christos 
   2904  1.1  christos /*
   2905  1.1  christos  * It is advisable to disable Nagle's algorithm for HTTP/2
   2906  1.1  christos  * connections because multiple HTTP/2 streams could be multiplexed
   2907  1.1  christos  * over one transport connection. Thus, delays when delivering small
   2908  1.1  christos  * packets could bring down performance for the whole session.
   2909  1.1  christos  * HTTP/2 is meant to be used this way.
   2910  1.1  christos  */
   2911  1.1  christos static void
   2912  1.1  christos http_transpost_tcp_nodelay(isc_nmhandle_t *transphandle) {
   2913  1.4  christos 	(void)isc_nmhandle_set_tcp_nodelay(transphandle, true);
   2914  1.1  christos }
   2915  1.1  christos 
   2916  1.1  christos static isc_result_t
   2917  1.1  christos httplisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
   2918  1.4  christos 	isc_nmsocket_t *httpserver = (isc_nmsocket_t *)cbarg;
   2919  1.1  christos 	isc_nm_http_session_t *session = NULL;
   2920  1.1  christos 
   2921  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   2922  1.1  christos 	REQUIRE(VALID_NMSOCK(handle->sock));
   2923  1.1  christos 
   2924  1.4  christos 	if (isc__nm_closing(handle->sock->worker)) {
   2925  1.4  christos 		return ISC_R_SHUTTINGDOWN;
   2926  1.4  christos 	} else if (result != ISC_R_SUCCESS) {
   2927  1.4  christos 		return result;
   2928  1.1  christos 	}
   2929  1.1  christos 
   2930  1.4  christos 	REQUIRE(VALID_NMSOCK(httpserver));
   2931  1.4  christos 	REQUIRE(httpserver->type == isc_nm_httplistener);
   2932  1.1  christos 
   2933  1.4  christos 	http_initsocket(handle->sock);
   2934  1.1  christos 
   2935  1.1  christos 	http_transpost_tcp_nodelay(handle);
   2936  1.1  christos 
   2937  1.4  christos 	new_session(handle->sock->worker->mctx, NULL, &session);
   2938  1.1  christos 	session->max_concurrent_streams =
   2939  1.4  christos 		atomic_load_relaxed(&httpserver->h2->max_concurrent_streams);
   2940  1.1  christos 	initialize_nghttp2_server_session(session);
   2941  1.4  christos 	handle->sock->h2->session = session;
   2942  1.1  christos 
   2943  1.1  christos 	isc_nmhandle_attach(handle, &session->handle);
   2944  1.4  christos 	isc__nmsocket_attach(httpserver, &session->serversocket);
   2945  1.1  christos 	server_send_connection_header(session);
   2946  1.1  christos 
   2947  1.5  christos 	isc__nmhandle_set_manual_timer(session->handle, true);
   2948  1.5  christos 
   2949  1.1  christos 	/* TODO H2 */
   2950  1.1  christos 	http_do_bio(session, NULL, NULL, NULL);
   2951  1.4  christos 	return ISC_R_SUCCESS;
   2952  1.1  christos }
   2953  1.1  christos 
   2954  1.1  christos isc_result_t
   2955  1.4  christos isc_nm_listenhttp(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
   2956  1.4  christos 		  int backlog, isc_quota_t *quota, isc_tlsctx_t *ctx,
   2957  1.1  christos 		  isc_nm_http_endpoints_t *eps, uint32_t max_concurrent_streams,
   2958  1.4  christos 		  isc_nm_proxy_type_t proxy_type, isc_nmsocket_t **sockp) {
   2959  1.1  christos 	isc_nmsocket_t *sock = NULL;
   2960  1.4  christos 	isc_result_t result = ISC_R_FAILURE;
   2961  1.4  christos 	isc__networker_t *worker = NULL;
   2962  1.1  christos 
   2963  1.1  christos 	REQUIRE(VALID_NM(mgr));
   2964  1.1  christos 	REQUIRE(!ISC_LIST_EMPTY(eps->handlers));
   2965  1.1  christos 	REQUIRE(atomic_load(&eps->in_use) == false);
   2966  1.4  christos 	REQUIRE(isc_tid() == 0);
   2967  1.1  christos 
   2968  1.4  christos 	worker = &mgr->workers[isc_tid()];
   2969  1.4  christos 	sock = isc_mempool_get(worker->nmsocket_pool);
   2970  1.4  christos 	isc__nmsocket_init(sock, worker, isc_nm_httplistener, iface, NULL);
   2971  1.4  christos 	http_initsocket(sock);
   2972  1.4  christos 	atomic_init(&sock->h2->max_concurrent_streams,
   2973  1.1  christos 		    NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS);
   2974  1.1  christos 
   2975  1.1  christos 	isc_nmsocket_set_max_streams(sock, max_concurrent_streams);
   2976  1.1  christos 
   2977  1.1  christos 	atomic_store(&eps->in_use, true);
   2978  1.1  christos 	http_init_listener_endpoints(sock, eps);
   2979  1.1  christos 
   2980  1.4  christos 	switch (proxy_type) {
   2981  1.4  christos 	case ISC_NM_PROXY_NONE:
   2982  1.4  christos 		if (ctx != NULL) {
   2983  1.4  christos 			result = isc_nm_listentls(
   2984  1.4  christos 				mgr, workers, iface, httplisten_acceptcb, sock,
   2985  1.4  christos 				backlog, quota, ctx, false, &sock->outer);
   2986  1.4  christos 		} else {
   2987  1.4  christos 			result = isc_nm_listentcp(mgr, workers, iface,
   2988  1.4  christos 						  httplisten_acceptcb, sock,
   2989  1.4  christos 						  backlog, quota, &sock->outer);
   2990  1.4  christos 		}
   2991  1.4  christos 		break;
   2992  1.4  christos 	case ISC_NM_PROXY_PLAIN:
   2993  1.4  christos 		if (ctx != NULL) {
   2994  1.4  christos 			result = isc_nm_listentls(
   2995  1.4  christos 				mgr, workers, iface, httplisten_acceptcb, sock,
   2996  1.4  christos 				backlog, quota, ctx, true, &sock->outer);
   2997  1.4  christos 		} else {
   2998  1.4  christos 			result = isc_nm_listenproxystream(
   2999  1.4  christos 				mgr, workers, iface, httplisten_acceptcb, sock,
   3000  1.4  christos 				backlog, quota, NULL, &sock->outer);
   3001  1.4  christos 		}
   3002  1.4  christos 		break;
   3003  1.4  christos 	case ISC_NM_PROXY_ENCRYPTED:
   3004  1.4  christos 		INSIST(ctx != NULL);
   3005  1.4  christos 		result = isc_nm_listenproxystream(
   3006  1.4  christos 			mgr, workers, iface, httplisten_acceptcb, sock, backlog,
   3007  1.4  christos 			quota, ctx, &sock->outer);
   3008  1.4  christos 		break;
   3009  1.4  christos 	default:
   3010  1.4  christos 		UNREACHABLE();
   3011  1.1  christos 	}
   3012  1.1  christos 
   3013  1.1  christos 	if (result != ISC_R_SUCCESS) {
   3014  1.4  christos 		sock->closed = true;
   3015  1.1  christos 		isc__nmsocket_detach(&sock);
   3016  1.4  christos 		return result;
   3017  1.1  christos 	}
   3018  1.1  christos 
   3019  1.1  christos 	sock->nchildren = sock->outer->nchildren;
   3020  1.1  christos 	sock->fd = (uv_os_sock_t)-1;
   3021  1.1  christos 
   3022  1.1  christos 	*sockp = sock;
   3023  1.4  christos 	return ISC_R_SUCCESS;
   3024  1.1  christos }
   3025  1.1  christos 
   3026  1.1  christos isc_nm_http_endpoints_t *
   3027  1.1  christos isc_nm_http_endpoints_new(isc_mem_t *mctx) {
   3028  1.1  christos 	isc_nm_http_endpoints_t *restrict eps;
   3029  1.1  christos 	REQUIRE(mctx != NULL);
   3030  1.1  christos 
   3031  1.1  christos 	eps = isc_mem_get(mctx, sizeof(*eps));
   3032  1.1  christos 	*eps = (isc_nm_http_endpoints_t){ .mctx = NULL };
   3033  1.1  christos 
   3034  1.1  christos 	isc_mem_attach(mctx, &eps->mctx);
   3035  1.1  christos 	ISC_LIST_INIT(eps->handlers);
   3036  1.1  christos 	isc_refcount_init(&eps->references, 1);
   3037  1.1  christos 	atomic_init(&eps->in_use, false);
   3038  1.1  christos 	eps->magic = HTTP_ENDPOINTS_MAGIC;
   3039  1.1  christos 
   3040  1.1  christos 	return eps;
   3041  1.1  christos }
   3042  1.1  christos 
   3043  1.1  christos void
   3044  1.1  christos isc_nm_http_endpoints_detach(isc_nm_http_endpoints_t **restrict epsp) {
   3045  1.1  christos 	isc_nm_http_endpoints_t *restrict eps;
   3046  1.1  christos 	isc_mem_t *mctx;
   3047  1.1  christos 	isc_nm_httphandler_t *handler = NULL;
   3048  1.1  christos 
   3049  1.1  christos 	REQUIRE(epsp != NULL);
   3050  1.1  christos 	eps = *epsp;
   3051  1.1  christos 	REQUIRE(VALID_HTTP_ENDPOINTS(eps));
   3052  1.1  christos 
   3053  1.1  christos 	if (isc_refcount_decrement(&eps->references) > 1) {
   3054  1.1  christos 		*epsp = NULL;
   3055  1.1  christos 		return;
   3056  1.1  christos 	}
   3057  1.1  christos 
   3058  1.1  christos 	mctx = eps->mctx;
   3059  1.1  christos 
   3060  1.1  christos 	/* Delete all handlers */
   3061  1.1  christos 	handler = ISC_LIST_HEAD(eps->handlers);
   3062  1.1  christos 	while (handler != NULL) {
   3063  1.1  christos 		isc_nm_httphandler_t *next = NULL;
   3064  1.1  christos 
   3065  1.1  christos 		next = ISC_LIST_NEXT(handler, link);
   3066  1.1  christos 		ISC_LIST_DEQUEUE(eps->handlers, handler, link);
   3067  1.1  christos 		isc_mem_free(mctx, handler->path);
   3068  1.3  christos 		handler->magic = 0;
   3069  1.1  christos 		isc_mem_put(mctx, handler, sizeof(*handler));
   3070  1.1  christos 		handler = next;
   3071  1.1  christos 	}
   3072  1.1  christos 
   3073  1.1  christos 	eps->magic = 0;
   3074  1.1  christos 
   3075  1.1  christos 	isc_mem_putanddetach(&mctx, eps, sizeof(*eps));
   3076  1.1  christos 	*epsp = NULL;
   3077  1.1  christos }
   3078  1.1  christos 
   3079  1.1  christos void
   3080  1.1  christos isc_nm_http_endpoints_attach(isc_nm_http_endpoints_t *source,
   3081  1.1  christos 			     isc_nm_http_endpoints_t **targetp) {
   3082  1.1  christos 	REQUIRE(VALID_HTTP_ENDPOINTS(source));
   3083  1.1  christos 	REQUIRE(targetp != NULL && *targetp == NULL);
   3084  1.1  christos 
   3085  1.1  christos 	isc_refcount_increment(&source->references);
   3086  1.1  christos 
   3087  1.1  christos 	*targetp = source;
   3088  1.1  christos }
   3089  1.1  christos 
   3090  1.1  christos static isc_nm_httphandler_t *
   3091  1.1  christos http_endpoints_find(const char *request_path,
   3092  1.1  christos 		    const isc_nm_http_endpoints_t *restrict eps) {
   3093  1.1  christos 	isc_nm_httphandler_t *handler = NULL;
   3094  1.1  christos 
   3095  1.1  christos 	REQUIRE(VALID_HTTP_ENDPOINTS(eps));
   3096  1.1  christos 
   3097  1.1  christos 	if (request_path == NULL || *request_path == '\0') {
   3098  1.4  christos 		return NULL;
   3099  1.1  christos 	}
   3100  1.1  christos 
   3101  1.1  christos 	for (handler = ISC_LIST_HEAD(eps->handlers); handler != NULL;
   3102  1.1  christos 	     handler = ISC_LIST_NEXT(handler, link))
   3103  1.1  christos 	{
   3104  1.1  christos 		if (!strcmp(request_path, handler->path)) {
   3105  1.3  christos 			INSIST(VALID_HTTP_HANDLER(handler));
   3106  1.3  christos 			INSIST(handler->cb != NULL);
   3107  1.1  christos 			break;
   3108  1.1  christos 		}
   3109  1.1  christos 	}
   3110  1.1  christos 
   3111  1.4  christos 	return handler;
   3112  1.1  christos }
   3113  1.1  christos 
   3114  1.1  christos isc_result_t
   3115  1.1  christos isc_nm_http_endpoints_add(isc_nm_http_endpoints_t *restrict eps,
   3116  1.1  christos 			  const char *uri, const isc_nm_recv_cb_t cb,
   3117  1.4  christos 			  void *cbarg) {
   3118  1.1  christos 	isc_mem_t *mctx;
   3119  1.1  christos 	isc_nm_httphandler_t *restrict handler = NULL;
   3120  1.1  christos 
   3121  1.1  christos 	REQUIRE(VALID_HTTP_ENDPOINTS(eps));
   3122  1.1  christos 	REQUIRE(isc_nm_http_path_isvalid(uri));
   3123  1.3  christos 	REQUIRE(cb != NULL);
   3124  1.1  christos 	REQUIRE(atomic_load(&eps->in_use) == false);
   3125  1.1  christos 
   3126  1.1  christos 	mctx = eps->mctx;
   3127  1.1  christos 
   3128  1.1  christos 	if (http_endpoints_find(uri, eps) == NULL) {
   3129  1.1  christos 		handler = isc_mem_get(mctx, sizeof(*handler));
   3130  1.1  christos 		*handler = (isc_nm_httphandler_t){
   3131  1.3  christos 			.cb = cb,
   3132  1.3  christos 			.cbarg = cbarg,
   3133  1.3  christos 			.path = isc_mem_strdup(mctx, uri),
   3134  1.3  christos 			.link = ISC_LINK_INITIALIZER,
   3135  1.3  christos 			.magic = HTTP_HANDLER_MAGIC
   3136  1.1  christos 		};
   3137  1.1  christos 
   3138  1.3  christos 		ISC_LIST_APPEND(eps->handlers, handler, link);
   3139  1.1  christos 	}
   3140  1.1  christos 
   3141  1.4  christos 	return ISC_R_SUCCESS;
   3142  1.1  christos }
   3143  1.1  christos 
   3144  1.1  christos void
   3145  1.1  christos isc__nm_http_stoplistening(isc_nmsocket_t *sock) {
   3146  1.1  christos 	REQUIRE(VALID_NMSOCK(sock));
   3147  1.1  christos 	REQUIRE(sock->type == isc_nm_httplistener);
   3148  1.4  christos 	REQUIRE(isc_tid() == sock->tid);
   3149  1.1  christos 
   3150  1.1  christos 	isc__nmsocket_stop(sock);
   3151  1.1  christos }
   3152  1.1  christos 
   3153  1.1  christos static void
   3154  1.1  christos http_close_direct(isc_nmsocket_t *sock) {
   3155  1.1  christos 	isc_nm_http_session_t *session = NULL;
   3156  1.1  christos 
   3157  1.1  christos 	REQUIRE(VALID_NMSOCK(sock));
   3158  1.1  christos 
   3159  1.4  christos 	sock->closed = true;
   3160  1.4  christos 	sock->active = false;
   3161  1.4  christos 	session = sock->h2->session;
   3162  1.1  christos 
   3163  1.1  christos 	if (session != NULL && session->sending == 0 && !session->reading) {
   3164  1.1  christos 		/*
   3165  1.1  christos 		 * The socket is going to be closed too early without been
   3166  1.1  christos 		 * used even once (might happen in a case of low level
   3167  1.1  christos 		 * error).
   3168  1.1  christos 		 */
   3169  1.1  christos 		finish_http_session(session);
   3170  1.1  christos 	} else if (session != NULL && session->handle) {
   3171  1.1  christos 		http_do_bio(session, NULL, NULL, NULL);
   3172  1.1  christos 	}
   3173  1.1  christos }
   3174  1.1  christos 
   3175  1.4  christos static void
   3176  1.4  christos http_close_cb(void *arg) {
   3177  1.4  christos 	isc_nmsocket_t *sock = arg;
   3178  1.4  christos 	REQUIRE(VALID_NMSOCK(sock));
   3179  1.4  christos 
   3180  1.4  christos 	http_close_direct(sock);
   3181  1.4  christos 	isc__nmsocket_detach(&sock);
   3182  1.4  christos }
   3183  1.4  christos 
   3184  1.1  christos void
   3185  1.1  christos isc__nm_http_close(isc_nmsocket_t *sock) {
   3186  1.1  christos 	bool destroy = false;
   3187  1.1  christos 	REQUIRE(VALID_NMSOCK(sock));
   3188  1.1  christos 	REQUIRE(sock->type == isc_nm_httpsocket);
   3189  1.1  christos 	REQUIRE(!isc__nmsocket_active(sock));
   3190  1.4  christos 	REQUIRE(!sock->closing);
   3191  1.1  christos 
   3192  1.4  christos 	sock->closing = true;
   3193  1.1  christos 
   3194  1.4  christos 	if (sock->h2->session != NULL && sock->h2->session->closed &&
   3195  1.4  christos 	    sock->tid == isc_tid())
   3196  1.1  christos 	{
   3197  1.4  christos 		isc__nm_httpsession_detach(&sock->h2->session);
   3198  1.1  christos 		destroy = true;
   3199  1.4  christos 	} else if (sock->h2->session == NULL && sock->tid == isc_tid()) {
   3200  1.1  christos 		destroy = true;
   3201  1.1  christos 	}
   3202  1.1  christos 
   3203  1.1  christos 	if (destroy) {
   3204  1.1  christos 		http_close_direct(sock);
   3205  1.1  christos 		isc__nmsocket_prep_destroy(sock);
   3206  1.1  christos 		return;
   3207  1.1  christos 	}
   3208  1.1  christos 
   3209  1.4  christos 	isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
   3210  1.4  christos 	isc_async_run(sock->worker->loop, http_close_cb, sock);
   3211  1.1  christos }
   3212  1.1  christos 
   3213  1.1  christos static void
   3214  1.1  christos failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
   3215  1.1  christos 			  isc_nm_http_session_t *session) {
   3216  1.1  christos 	isc_region_t data;
   3217  1.1  christos 	REQUIRE(VALID_NMSOCK(sock));
   3218  1.1  christos 	INSIST(sock->type == isc_nm_httpsocket);
   3219  1.1  christos 
   3220  1.4  christos 	if (sock->h2->request_path == NULL) {
   3221  1.1  christos 		return;
   3222  1.1  christos 	}
   3223  1.1  christos 
   3224  1.1  christos 	(void)nghttp2_submit_rst_stream(
   3225  1.4  christos 		session->ngsession, NGHTTP2_FLAG_END_STREAM,
   3226  1.4  christos 		sock->h2->stream_id, NGHTTP2_REFUSED_STREAM);
   3227  1.4  christos 	isc_buffer_usedregion(&sock->h2->rbuf, &data);
   3228  1.3  christos 	server_call_cb(sock, result, &data);
   3229  1.1  christos }
   3230  1.1  christos 
   3231  1.1  christos static void
   3232  1.1  christos client_call_failed_read_cb(isc_result_t result,
   3233  1.1  christos 			   isc_nm_http_session_t *session) {
   3234  1.1  christos 	http_cstream_t *cstream = NULL;
   3235  1.1  christos 
   3236  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
   3237  1.1  christos 	REQUIRE(result != ISC_R_SUCCESS);
   3238  1.1  christos 
   3239  1.1  christos 	cstream = ISC_LIST_HEAD(session->cstreams);
   3240  1.1  christos 	while (cstream != NULL) {
   3241  1.1  christos 		http_cstream_t *next = ISC_LIST_NEXT(cstream, link);
   3242  1.1  christos 
   3243  1.1  christos 		/*
   3244  1.1  christos 		 * read_cb could be NULL if cstream was allocated and added
   3245  1.1  christos 		 * to the tracking list, but was not properly initialized due
   3246  1.1  christos 		 * to a low-level error. It is safe to get rid of the object
   3247  1.1  christos 		 * in such a case.
   3248  1.1  christos 		 */
   3249  1.1  christos 		if (cstream->read_cb != NULL) {
   3250  1.1  christos 			isc_region_t read_data;
   3251  1.1  christos 			isc_buffer_usedregion(cstream->rbuf, &read_data);
   3252  1.1  christos 			cstream->read_cb(session->client_httphandle, result,
   3253  1.1  christos 					 &read_data, cstream->read_cbarg);
   3254  1.1  christos 		}
   3255  1.1  christos 
   3256  1.1  christos 		if (result != ISC_R_TIMEDOUT || cstream->read_cb == NULL ||
   3257  1.3  christos 		    !(session->handle != NULL &&
   3258  1.3  christos 		      isc__nmsocket_timer_running(session->handle->sock)))
   3259  1.1  christos 		{
   3260  1.1  christos 			ISC_LIST_DEQUEUE(session->cstreams, cstream, link);
   3261  1.1  christos 			put_http_cstream(session->mctx, cstream);
   3262  1.1  christos 		}
   3263  1.1  christos 
   3264  1.1  christos 		cstream = next;
   3265  1.1  christos 	}
   3266  1.1  christos }
   3267  1.1  christos 
   3268  1.1  christos static void
   3269  1.1  christos server_call_failed_read_cb(isc_result_t result,
   3270  1.1  christos 			   isc_nm_http_session_t *session) {
   3271  1.1  christos 	isc_nmsocket_h2_t *h2data = NULL; /* stream socket */
   3272  1.1  christos 
   3273  1.1  christos 	REQUIRE(VALID_HTTP2_SESSION(session));
   3274  1.1  christos 	REQUIRE(result != ISC_R_SUCCESS);
   3275  1.1  christos 
   3276  1.1  christos 	for (h2data = ISC_LIST_HEAD(session->sstreams); h2data != NULL;
   3277  1.1  christos 	     h2data = ISC_LIST_NEXT(h2data, link))
   3278  1.1  christos 	{
   3279  1.1  christos 		failed_httpstream_read_cb(h2data->psock, result, session);
   3280  1.1  christos 	}
   3281  1.1  christos 
   3282  1.1  christos 	h2data = ISC_LIST_HEAD(session->sstreams);
   3283  1.1  christos 	while (h2data != NULL) {
   3284  1.1  christos 		isc_nmsocket_h2_t *next = ISC_LIST_NEXT(h2data, link);
   3285  1.1  christos 		ISC_LIST_DEQUEUE(session->sstreams, h2data, link);
   3286  1.1  christos 		/* Cleanup socket in place */
   3287  1.4  christos 		h2data->psock->active = false;
   3288  1.4  christos 		h2data->psock->closed = true;
   3289  1.1  christos 		isc__nmsocket_detach(&h2data->psock);
   3290  1.1  christos 
   3291  1.1  christos 		h2data = next;
   3292  1.1  christos 	}
   3293  1.1  christos }
   3294  1.1  christos 
   3295  1.1  christos static void
   3296  1.1  christos failed_read_cb(isc_result_t result, isc_nm_http_session_t *session) {
   3297  1.1  christos 	if (session->client) {
   3298  1.1  christos 		client_call_failed_read_cb(result, session);
   3299  1.1  christos 		/*
   3300  1.1  christos 		 * If result was ISC_R_TIMEDOUT and the timer was reset,
   3301  1.1  christos 		 * then we still have active streams and should not close
   3302  1.1  christos 		 * the session.
   3303  1.1  christos 		 */
   3304  1.1  christos 		if (ISC_LIST_EMPTY(session->cstreams)) {
   3305  1.1  christos 			finish_http_session(session);
   3306  1.1  christos 		}
   3307  1.1  christos 	} else {
   3308  1.1  christos 		server_call_failed_read_cb(result, session);
   3309  1.1  christos 		/*
   3310  1.1  christos 		 * All streams are now destroyed; close the session.
   3311  1.1  christos 		 */
   3312  1.1  christos 		finish_http_session(session);
   3313  1.1  christos 	}
   3314  1.1  christos }
   3315  1.1  christos 
   3316  1.1  christos void
   3317  1.1  christos isc__nm_http_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) {
   3318  1.1  christos 	isc_nm_http_session_t *session;
   3319  1.1  christos 	isc_nmsocket_t *sock;
   3320  1.1  christos 
   3321  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   3322  1.1  christos 	REQUIRE(VALID_NMSOCK(handle->sock));
   3323  1.1  christos 
   3324  1.1  christos 	sock = handle->sock;
   3325  1.4  christos 	session = sock->h2->session;
   3326  1.1  christos 
   3327  1.1  christos 	INSIST(VALID_HTTP2_SESSION(session));
   3328  1.1  christos 	INSIST(!session->client);
   3329  1.1  christos 
   3330  1.4  christos 	sock->h2->min_ttl = ttl;
   3331  1.1  christos }
   3332  1.1  christos 
   3333  1.1  christos bool
   3334  1.1  christos isc__nm_http_has_encryption(const isc_nmhandle_t *handle) {
   3335  1.1  christos 	isc_nm_http_session_t *session;
   3336  1.1  christos 	isc_nmsocket_t *sock;
   3337  1.1  christos 
   3338  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   3339  1.1  christos 	REQUIRE(VALID_NMSOCK(handle->sock));
   3340  1.1  christos 
   3341  1.1  christos 	sock = handle->sock;
   3342  1.4  christos 	session = sock->h2->session;
   3343  1.1  christos 
   3344  1.1  christos 	INSIST(VALID_HTTP2_SESSION(session));
   3345  1.1  christos 
   3346  1.3  christos 	if (session->handle == NULL) {
   3347  1.4  christos 		return false;
   3348  1.3  christos 	}
   3349  1.3  christos 
   3350  1.4  christos 	return isc_nm_has_encryption(session->handle);
   3351  1.1  christos }
   3352  1.1  christos 
   3353  1.1  christos const char *
   3354  1.1  christos isc__nm_http_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
   3355  1.1  christos 	isc_nmsocket_t *sock = NULL;
   3356  1.1  christos 	isc_nm_http_session_t *session;
   3357  1.1  christos 
   3358  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   3359  1.1  christos 	REQUIRE(VALID_NMSOCK(handle->sock));
   3360  1.1  christos 	REQUIRE(handle->sock->type == isc_nm_httpsocket);
   3361  1.1  christos 
   3362  1.1  christos 	sock = handle->sock;
   3363  1.4  christos 	session = sock->h2->session;
   3364  1.1  christos 
   3365  1.1  christos 	/*
   3366  1.1  christos 	 * In the case of a low-level error the session->handle is not
   3367  1.1  christos 	 * attached nor session object is created.
   3368  1.1  christos 	 */
   3369  1.4  christos 	if (session == NULL && sock->h2->connect.tls_peer_verify_string != NULL)
   3370  1.1  christos 	{
   3371  1.4  christos 		return sock->h2->connect.tls_peer_verify_string;
   3372  1.1  christos 	}
   3373  1.1  christos 
   3374  1.1  christos 	if (session == NULL) {
   3375  1.4  christos 		return NULL;
   3376  1.1  christos 	}
   3377  1.1  christos 
   3378  1.1  christos 	INSIST(VALID_HTTP2_SESSION(session));
   3379  1.1  christos 
   3380  1.3  christos 	if (session->handle == NULL) {
   3381  1.4  christos 		return NULL;
   3382  1.3  christos 	}
   3383  1.3  christos 
   3384  1.4  christos 	return isc_nm_verify_tls_peer_result_string(session->handle);
   3385  1.1  christos }
   3386  1.1  christos 
   3387  1.1  christos void
   3388  1.1  christos isc__nm_http_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
   3389  1.1  christos 	REQUIRE(VALID_NMSOCK(listener));
   3390  1.1  christos 	REQUIRE(listener->type == isc_nm_httplistener);
   3391  1.1  christos 
   3392  1.1  christos 	isc_nmsocket_set_tlsctx(listener->outer, tlsctx);
   3393  1.1  christos }
   3394  1.1  christos 
   3395  1.1  christos void
   3396  1.1  christos isc__nm_http_set_max_streams(isc_nmsocket_t *listener,
   3397  1.1  christos 			     const uint32_t max_concurrent_streams) {
   3398  1.1  christos 	uint32_t max_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
   3399  1.1  christos 
   3400  1.1  christos 	REQUIRE(VALID_NMSOCK(listener));
   3401  1.1  christos 	REQUIRE(listener->type == isc_nm_httplistener);
   3402  1.1  christos 
   3403  1.1  christos 	if (max_concurrent_streams > 0 &&
   3404  1.1  christos 	    max_concurrent_streams < NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS)
   3405  1.1  christos 	{
   3406  1.1  christos 		max_streams = max_concurrent_streams;
   3407  1.1  christos 	}
   3408  1.1  christos 
   3409  1.4  christos 	atomic_store_relaxed(&listener->h2->max_concurrent_streams,
   3410  1.4  christos 			     max_streams);
   3411  1.4  christos }
   3412  1.4  christos 
   3413  1.4  christos typedef struct http_endpoints_data {
   3414  1.4  christos 	isc_nmsocket_t *listener;
   3415  1.4  christos 	isc_nm_http_endpoints_t *endpoints;
   3416  1.4  christos } http_endpoints_data_t;
   3417  1.4  christos 
   3418  1.4  christos static void
   3419  1.4  christos http_set_endpoints_cb(void *arg) {
   3420  1.4  christos 	http_endpoints_data_t *data = arg;
   3421  1.4  christos 	const int tid = isc_tid();
   3422  1.4  christos 	isc_nmsocket_t *listener = data->listener;
   3423  1.4  christos 	isc_nm_http_endpoints_t *endpoints = data->endpoints;
   3424  1.4  christos 	isc__networker_t *worker = &listener->worker->netmgr->workers[tid];
   3425  1.4  christos 
   3426  1.4  christos 	isc_mem_put(worker->loop->mctx, data, sizeof(*data));
   3427  1.4  christos 
   3428  1.4  christos 	isc_nm_http_endpoints_detach(&listener->h2->listener_endpoints[tid]);
   3429  1.4  christos 	isc_nm_http_endpoints_attach(endpoints,
   3430  1.4  christos 				     &listener->h2->listener_endpoints[tid]);
   3431  1.4  christos 
   3432  1.4  christos 	isc_nm_http_endpoints_detach(&endpoints);
   3433  1.4  christos 	isc__nmsocket_detach(&listener);
   3434  1.1  christos }
   3435  1.1  christos 
   3436  1.1  christos void
   3437  1.1  christos isc_nm_http_set_endpoints(isc_nmsocket_t *listener,
   3438  1.1  christos 			  isc_nm_http_endpoints_t *eps) {
   3439  1.4  christos 	isc_loopmgr_t *loopmgr = NULL;
   3440  1.1  christos 
   3441  1.1  christos 	REQUIRE(VALID_NMSOCK(listener));
   3442  1.1  christos 	REQUIRE(listener->type == isc_nm_httplistener);
   3443  1.1  christos 	REQUIRE(VALID_HTTP_ENDPOINTS(eps));
   3444  1.1  christos 
   3445  1.4  christos 	loopmgr = listener->worker->netmgr->loopmgr;
   3446  1.4  christos 
   3447  1.1  christos 	atomic_store(&eps->in_use, true);
   3448  1.1  christos 
   3449  1.4  christos 	for (size_t i = 0; i < isc_loopmgr_nloops(loopmgr); i++) {
   3450  1.4  christos 		isc__networker_t *worker =
   3451  1.4  christos 			&listener->worker->netmgr->workers[i];
   3452  1.4  christos 		http_endpoints_data_t *data = isc_mem_cget(worker->loop->mctx,
   3453  1.4  christos 							   1, sizeof(*data));
   3454  1.4  christos 
   3455  1.4  christos 		isc__nmsocket_attach(listener, &data->listener);
   3456  1.4  christos 		isc_nm_http_endpoints_attach(eps, &data->endpoints);
   3457  1.4  christos 
   3458  1.4  christos 		isc_async_run(worker->loop, http_set_endpoints_cb, data);
   3459  1.1  christos 	}
   3460  1.1  christos }
   3461  1.1  christos 
   3462  1.1  christos static void
   3463  1.1  christos http_init_listener_endpoints(isc_nmsocket_t *listener,
   3464  1.1  christos 			     isc_nm_http_endpoints_t *epset) {
   3465  1.4  christos 	size_t nworkers;
   3466  1.4  christos 	isc_loopmgr_t *loopmgr = NULL;
   3467  1.1  christos 
   3468  1.1  christos 	REQUIRE(VALID_NMSOCK(listener));
   3469  1.4  christos 	REQUIRE(listener->worker != NULL && VALID_NM(listener->worker->netmgr));
   3470  1.1  christos 	REQUIRE(VALID_HTTP_ENDPOINTS(epset));
   3471  1.1  christos 
   3472  1.4  christos 	loopmgr = listener->worker->netmgr->loopmgr;
   3473  1.4  christos 	nworkers = (size_t)isc_loopmgr_nloops(loopmgr);
   3474  1.4  christos 	INSIST(nworkers > 0);
   3475  1.4  christos 
   3476  1.4  christos 	listener->h2->listener_endpoints =
   3477  1.4  christos 		isc_mem_cget(listener->worker->mctx, nworkers,
   3478  1.4  christos 			     sizeof(isc_nm_http_endpoints_t *));
   3479  1.4  christos 	listener->h2->n_listener_endpoints = nworkers;
   3480  1.4  christos 	for (size_t i = 0; i < nworkers; i++) {
   3481  1.4  christos 		listener->h2->listener_endpoints[i] = NULL;
   3482  1.1  christos 		isc_nm_http_endpoints_attach(
   3483  1.4  christos 			epset, &listener->h2->listener_endpoints[i]);
   3484  1.1  christos 	}
   3485  1.1  christos }
   3486  1.1  christos 
   3487  1.1  christos static void
   3488  1.1  christos http_cleanup_listener_endpoints(isc_nmsocket_t *listener) {
   3489  1.4  christos 	REQUIRE(listener->worker != NULL && VALID_NM(listener->worker->netmgr));
   3490  1.1  christos 
   3491  1.4  christos 	if (listener->h2->listener_endpoints == NULL) {
   3492  1.1  christos 		return;
   3493  1.1  christos 	}
   3494  1.1  christos 
   3495  1.4  christos 	for (size_t i = 0; i < listener->h2->n_listener_endpoints; i++) {
   3496  1.1  christos 		isc_nm_http_endpoints_detach(
   3497  1.4  christos 			&listener->h2->listener_endpoints[i]);
   3498  1.1  christos 	}
   3499  1.4  christos 	isc_mem_cput(listener->worker->mctx, listener->h2->listener_endpoints,
   3500  1.4  christos 		     listener->h2->n_listener_endpoints,
   3501  1.4  christos 		     sizeof(isc_nm_http_endpoints_t *));
   3502  1.4  christos 	listener->h2->n_listener_endpoints = 0;
   3503  1.1  christos }
   3504  1.1  christos 
   3505  1.1  christos static isc_nm_http_endpoints_t *
   3506  1.1  christos http_get_listener_endpoints(isc_nmsocket_t *listener, const int tid) {
   3507  1.1  christos 	isc_nm_http_endpoints_t *eps;
   3508  1.1  christos 	REQUIRE(VALID_NMSOCK(listener));
   3509  1.1  christos 	REQUIRE(tid >= 0);
   3510  1.4  christos 	REQUIRE((size_t)tid < listener->h2->n_listener_endpoints);
   3511  1.1  christos 
   3512  1.4  christos 	eps = listener->h2->listener_endpoints[tid];
   3513  1.1  christos 	INSIST(eps != NULL);
   3514  1.4  christos 	return eps;
   3515  1.1  christos }
   3516  1.1  christos 
   3517  1.1  christos static const bool base64url_validation_table[256] = {
   3518  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3519  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3520  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3521  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3522  1.1  christos 	false, false, false, false, false, true,  false, false, true,  true,
   3523  1.1  christos 	true,  true,  true,  true,  true,  true,  true,	 true,	false, false,
   3524  1.1  christos 	false, false, false, false, false, true,  true,	 true,	true,  true,
   3525  1.1  christos 	true,  true,  true,  true,  true,  true,  true,	 true,	true,  true,
   3526  1.1  christos 	true,  true,  true,  true,  true,  true,  true,	 true,	true,  true,
   3527  1.1  christos 	true,  false, false, false, false, true,  false, true,	true,  true,
   3528  1.1  christos 	true,  true,  true,  true,  true,  true,  true,	 true,	true,  true,
   3529  1.1  christos 	true,  true,  true,  true,  true,  true,  true,	 true,	true,  true,
   3530  1.1  christos 	true,  true,  true,  false, false, false, false, false, false, false,
   3531  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3532  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3533  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3534  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3535  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3536  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3537  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3538  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3539  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3540  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3541  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3542  1.1  christos 	false, false, false, false, false, false, false, false, false, false,
   3543  1.1  christos 	false, false, false, false, false, false
   3544  1.1  christos };
   3545  1.1  christos 
   3546  1.1  christos char *
   3547  1.1  christos isc__nm_base64url_to_base64(isc_mem_t *mem, const char *base64url,
   3548  1.1  christos 			    const size_t base64url_len, size_t *res_len) {
   3549  1.1  christos 	char *res = NULL;
   3550  1.1  christos 	size_t i, k, len;
   3551  1.1  christos 
   3552  1.1  christos 	if (mem == NULL || base64url == NULL || base64url_len == 0) {
   3553  1.4  christos 		return NULL;
   3554  1.1  christos 	}
   3555  1.1  christos 
   3556  1.1  christos 	len = base64url_len % 4 ? base64url_len + (4 - base64url_len % 4)
   3557  1.1  christos 				: base64url_len;
   3558  1.1  christos 	res = isc_mem_allocate(mem, len + 1); /* '\0' */
   3559  1.1  christos 
   3560  1.1  christos 	for (i = 0; i < base64url_len; i++) {
   3561  1.1  christos 		switch (base64url[i]) {
   3562  1.1  christos 		case '-':
   3563  1.1  christos 			res[i] = '+';
   3564  1.1  christos 			break;
   3565  1.1  christos 		case '_':
   3566  1.1  christos 			res[i] = '/';
   3567  1.1  christos 			break;
   3568  1.1  christos 		default:
   3569  1.1  christos 			if (base64url_validation_table[(size_t)base64url[i]]) {
   3570  1.1  christos 				res[i] = base64url[i];
   3571  1.1  christos 			} else {
   3572  1.1  christos 				isc_mem_free(mem, res);
   3573  1.4  christos 				return NULL;
   3574  1.1  christos 			}
   3575  1.1  christos 			break;
   3576  1.1  christos 		}
   3577  1.1  christos 	}
   3578  1.1  christos 
   3579  1.1  christos 	if (base64url_len % 4 != 0) {
   3580  1.1  christos 		for (k = 0; k < (4 - base64url_len % 4); k++, i++) {
   3581  1.1  christos 			res[i] = '=';
   3582  1.1  christos 		}
   3583  1.1  christos 	}
   3584  1.1  christos 
   3585  1.1  christos 	INSIST(i == len);
   3586  1.1  christos 
   3587  1.6  christos 	SET_IF_NOT_NULL(res_len, len);
   3588  1.1  christos 
   3589  1.1  christos 	res[len] = '\0';
   3590  1.1  christos 
   3591  1.4  christos 	return res;
   3592  1.1  christos }
   3593  1.1  christos 
   3594  1.1  christos char *
   3595  1.1  christos isc__nm_base64_to_base64url(isc_mem_t *mem, const char *base64,
   3596  1.1  christos 			    const size_t base64_len, size_t *res_len) {
   3597  1.1  christos 	char *res = NULL;
   3598  1.1  christos 	size_t i;
   3599  1.1  christos 
   3600  1.1  christos 	if (mem == NULL || base64 == NULL || base64_len == 0) {
   3601  1.4  christos 		return NULL;
   3602  1.1  christos 	}
   3603  1.1  christos 
   3604  1.1  christos 	res = isc_mem_allocate(mem, base64_len + 1); /* '\0' */
   3605  1.1  christos 
   3606  1.1  christos 	for (i = 0; i < base64_len; i++) {
   3607  1.1  christos 		switch (base64[i]) {
   3608  1.1  christos 		case '+':
   3609  1.1  christos 			res[i] = '-';
   3610  1.1  christos 			break;
   3611  1.1  christos 		case '/':
   3612  1.1  christos 			res[i] = '_';
   3613  1.1  christos 			break;
   3614  1.1  christos 		case '=':
   3615  1.1  christos 			goto end;
   3616  1.1  christos 			break;
   3617  1.1  christos 		default:
   3618  1.1  christos 			/*
   3619  1.4  christos 			 * All other characters from
   3620  1.4  christos 			 * the alphabet are the same
   3621  1.4  christos 			 * for both base64 and
   3622  1.4  christos 			 * base64url, so we can reuse
   3623  1.4  christos 			 * the validation table for
   3624  1.4  christos 			 * the rest of the characters.
   3625  1.1  christos 			 */
   3626  1.1  christos 			if (base64[i] != '-' && base64[i] != '_' &&
   3627  1.1  christos 			    base64url_validation_table[(size_t)base64[i]])
   3628  1.1  christos 			{
   3629  1.1  christos 				res[i] = base64[i];
   3630  1.1  christos 			} else {
   3631  1.1  christos 				isc_mem_free(mem, res);
   3632  1.4  christos 				return NULL;
   3633  1.1  christos 			}
   3634  1.1  christos 			break;
   3635  1.1  christos 		}
   3636  1.1  christos 	}
   3637  1.1  christos end:
   3638  1.6  christos 	SET_IF_NOT_NULL(res_len, i);
   3639  1.1  christos 
   3640  1.1  christos 	res[i] = '\0';
   3641  1.1  christos 
   3642  1.4  christos 	return res;
   3643  1.1  christos }
   3644  1.1  christos 
   3645  1.4  christos static void
   3646  1.4  christos http_initsocket(isc_nmsocket_t *sock) {
   3647  1.1  christos 	REQUIRE(sock != NULL);
   3648  1.1  christos 
   3649  1.4  christos 	sock->h2 = isc_mem_get(sock->worker->mctx, sizeof(*sock->h2));
   3650  1.4  christos 	*sock->h2 = (isc_nmsocket_h2_t){
   3651  1.1  christos 		.request_type = ISC_HTTP_REQ_UNSUPPORTED,
   3652  1.1  christos 		.request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED,
   3653  1.1  christos 	};
   3654  1.1  christos }
   3655  1.1  christos 
   3656  1.1  christos void
   3657  1.1  christos isc__nm_http_cleanup_data(isc_nmsocket_t *sock) {
   3658  1.4  christos 	switch (sock->type) {
   3659  1.4  christos 	case isc_nm_httplistener:
   3660  1.4  christos 	case isc_nm_httpsocket:
   3661  1.1  christos 		if (sock->type == isc_nm_httplistener &&
   3662  1.4  christos 		    sock->h2->listener_endpoints != NULL)
   3663  1.1  christos 		{
   3664  1.1  christos 			/* Delete all handlers */
   3665  1.1  christos 			http_cleanup_listener_endpoints(sock);
   3666  1.1  christos 		}
   3667  1.1  christos 
   3668  1.3  christos 		if (sock->type == isc_nm_httpsocket &&
   3669  1.4  christos 		    sock->h2->peer_endpoints != NULL)
   3670  1.3  christos 		{
   3671  1.4  christos 			isc_nm_http_endpoints_detach(&sock->h2->peer_endpoints);
   3672  1.3  christos 		}
   3673  1.3  christos 
   3674  1.4  christos 		if (sock->h2->request_path != NULL) {
   3675  1.4  christos 			isc_mem_free(sock->worker->mctx,
   3676  1.4  christos 				     sock->h2->request_path);
   3677  1.4  christos 			sock->h2->request_path = NULL;
   3678  1.4  christos 		}
   3679  1.4  christos 
   3680  1.4  christos 		if (sock->h2->query_data != NULL) {
   3681  1.4  christos 			isc_mem_free(sock->worker->mctx, sock->h2->query_data);
   3682  1.4  christos 			sock->h2->query_data = NULL;
   3683  1.4  christos 		}
   3684  1.4  christos 
   3685  1.4  christos 		INSIST(sock->h2->connect.cstream == NULL);
   3686  1.4  christos 
   3687  1.4  christos 		if (isc_buffer_base(&sock->h2->rbuf) != NULL) {
   3688  1.4  christos 			void *base = isc_buffer_base(&sock->h2->rbuf);
   3689  1.4  christos 			isc_mem_free(sock->worker->mctx, base);
   3690  1.4  christos 			isc_buffer_initnull(&sock->h2->rbuf);
   3691  1.4  christos 		}
   3692  1.4  christos 		FALLTHROUGH;
   3693  1.4  christos 	case isc_nm_proxystreamlistener:
   3694  1.4  christos 	case isc_nm_proxystreamsocket:
   3695  1.4  christos 	case isc_nm_tcpsocket:
   3696  1.4  christos 	case isc_nm_tlssocket:
   3697  1.4  christos 		if (sock->h2 != NULL) {
   3698  1.4  christos 			if (sock->h2->session != NULL) {
   3699  1.4  christos 				if (sock->h2->connect.uri != NULL) {
   3700  1.4  christos 					isc_mem_free(sock->worker->mctx,
   3701  1.4  christos 						     sock->h2->connect.uri);
   3702  1.4  christos 					sock->h2->connect.uri = NULL;
   3703  1.4  christos 				}
   3704  1.4  christos 				isc__nm_httpsession_detach(&sock->h2->session);
   3705  1.4  christos 			}
   3706  1.1  christos 
   3707  1.4  christos 			isc_mem_put(sock->worker->mctx, sock->h2,
   3708  1.4  christos 				    sizeof(*sock->h2));
   3709  1.4  christos 		};
   3710  1.4  christos 		break;
   3711  1.4  christos 	default:
   3712  1.4  christos 		break;
   3713  1.1  christos 	}
   3714  1.1  christos }
   3715  1.1  christos 
   3716  1.1  christos void
   3717  1.1  christos isc__nm_http_cleartimeout(isc_nmhandle_t *handle) {
   3718  1.1  christos 	isc_nmsocket_t *sock = NULL;
   3719  1.1  christos 
   3720  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   3721  1.1  christos 	REQUIRE(VALID_NMSOCK(handle->sock));
   3722  1.1  christos 	REQUIRE(handle->sock->type == isc_nm_httpsocket);
   3723  1.1  christos 
   3724  1.1  christos 	sock = handle->sock;
   3725  1.4  christos 	if (sock->h2->session != NULL && sock->h2->session->handle != NULL) {
   3726  1.4  christos 		INSIST(VALID_HTTP2_SESSION(sock->h2->session));
   3727  1.4  christos 		INSIST(VALID_NMHANDLE(sock->h2->session->handle));
   3728  1.4  christos 		isc_nmhandle_cleartimeout(sock->h2->session->handle);
   3729  1.1  christos 	}
   3730  1.1  christos }
   3731  1.1  christos 
   3732  1.1  christos void
   3733  1.1  christos isc__nm_http_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
   3734  1.1  christos 	isc_nmsocket_t *sock = NULL;
   3735  1.1  christos 
   3736  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   3737  1.1  christos 	REQUIRE(VALID_NMSOCK(handle->sock));
   3738  1.1  christos 	REQUIRE(handle->sock->type == isc_nm_httpsocket);
   3739  1.1  christos 
   3740  1.1  christos 	sock = handle->sock;
   3741  1.4  christos 	if (sock->h2->session != NULL && sock->h2->session->handle != NULL) {
   3742  1.4  christos 		INSIST(VALID_HTTP2_SESSION(sock->h2->session));
   3743  1.4  christos 		INSIST(VALID_NMHANDLE(sock->h2->session->handle));
   3744  1.4  christos 		isc_nmhandle_settimeout(sock->h2->session->handle, timeout);
   3745  1.1  christos 	}
   3746  1.1  christos }
   3747  1.1  christos 
   3748  1.1  christos void
   3749  1.1  christos isc__nmhandle_http_keepalive(isc_nmhandle_t *handle, bool value) {
   3750  1.1  christos 	isc_nmsocket_t *sock = NULL;
   3751  1.1  christos 
   3752  1.1  christos 	REQUIRE(VALID_NMHANDLE(handle));
   3753  1.1  christos 	REQUIRE(VALID_NMSOCK(handle->sock));
   3754  1.1  christos 	REQUIRE(handle->sock->type == isc_nm_httpsocket);
   3755  1.1  christos 
   3756  1.1  christos 	sock = handle->sock;
   3757  1.4  christos 	if (sock->h2->session != NULL && sock->h2->session->handle) {
   3758  1.4  christos 		INSIST(VALID_HTTP2_SESSION(sock->h2->session));
   3759  1.4  christos 		INSIST(VALID_NMHANDLE(sock->h2->session->handle));
   3760  1.1  christos 
   3761  1.4  christos 		isc_nmhandle_keepalive(sock->h2->session->handle, value);
   3762  1.1  christos 	}
   3763  1.1  christos }
   3764  1.1  christos 
   3765  1.1  christos void
   3766  1.1  christos isc_nm_http_makeuri(const bool https, const isc_sockaddr_t *sa,
   3767  1.1  christos 		    const char *hostname, const uint16_t http_port,
   3768  1.1  christos 		    const char *abs_path, char *outbuf,
   3769  1.1  christos 		    const size_t outbuf_len) {
   3770  1.1  christos 	char saddr[INET6_ADDRSTRLEN] = { 0 };
   3771  1.1  christos 	int family;
   3772  1.1  christos 	bool ipv6_addr = false;
   3773  1.1  christos 	struct sockaddr_in6 sa6;
   3774  1.1  christos 	uint16_t host_port = http_port;
   3775  1.1  christos 	const char *host = NULL;
   3776  1.1  christos 
   3777  1.1  christos 	REQUIRE(outbuf != NULL);
   3778  1.1  christos 	REQUIRE(outbuf_len != 0);
   3779  1.1  christos 	REQUIRE(isc_nm_http_path_isvalid(abs_path));
   3780  1.1  christos 
   3781  1.1  christos 	/* If hostname is specified, use that. */
   3782  1.1  christos 	if (hostname != NULL && hostname[0] != '\0') {
   3783  1.1  christos 		/*
   3784  1.1  christos 		 * The host name could be an IPv6 address. If so,
   3785  1.1  christos 		 * wrap it between [ and ].
   3786  1.1  christos 		 */
   3787  1.1  christos 		if (inet_pton(AF_INET6, hostname, &sa6) == 1 &&
   3788  1.1  christos 		    hostname[0] != '[')
   3789  1.1  christos 		{
   3790  1.1  christos 			ipv6_addr = true;
   3791  1.1  christos 		}
   3792  1.1  christos 		host = hostname;
   3793  1.1  christos 	} else {
   3794  1.1  christos 		/*
   3795  1.1  christos 		 * A hostname was not specified; build one from
   3796  1.1  christos 		 * the given IP address.
   3797  1.1  christos 		 */
   3798  1.1  christos 		INSIST(sa != NULL);
   3799  1.1  christos 		family = ((const struct sockaddr *)&sa->type.sa)->sa_family;
   3800  1.1  christos 		host_port = ntohs(family == AF_INET ? sa->type.sin.sin_port
   3801  1.1  christos 						    : sa->type.sin6.sin6_port);
   3802  1.1  christos 		ipv6_addr = family == AF_INET6;
   3803  1.1  christos 		(void)inet_ntop(
   3804  1.1  christos 			family,
   3805  1.1  christos 			family == AF_INET
   3806  1.1  christos 				? (const struct sockaddr *)&sa->type.sin.sin_addr
   3807  1.1  christos 				: (const struct sockaddr *)&sa->type.sin6
   3808  1.1  christos 					  .sin6_addr,
   3809  1.1  christos 			saddr, sizeof(saddr));
   3810  1.1  christos 		host = saddr;
   3811  1.1  christos 	}
   3812  1.1  christos 
   3813  1.1  christos 	/*
   3814  1.1  christos 	 * If the port number was not specified, the default
   3815  1.1  christos 	 * depends on whether we're using encryption or not.
   3816  1.1  christos 	 */
   3817  1.1  christos 	if (host_port == 0) {
   3818  1.1  christos 		host_port = https ? 443 : 80;
   3819  1.1  christos 	}
   3820  1.1  christos 
   3821  1.1  christos 	(void)snprintf(outbuf, outbuf_len, "%s://%s%s%s:%u%s",
   3822  1.1  christos 		       https ? "https" : "http", ipv6_addr ? "[" : "", host,
   3823  1.1  christos 		       ipv6_addr ? "]" : "", host_port, abs_path);
   3824  1.1  christos }
   3825  1.1  christos 
   3826  1.1  christos /*
   3827  1.1  christos  * DoH GET Query String Scanner-less Recursive Descent Parser/Verifier
   3828  1.1  christos  *
   3829  1.1  christos  * It is based on the following grammar (using WSN/EBNF):
   3830  1.1  christos  *
   3831  1.1  christos  * S                = query-string.
   3832  1.1  christos  * query-string     = ['?'] { key-value-pair } EOF.
   3833  1.1  christos  * key-value-pair   = key '=' value [ '&' ].
   3834  1.1  christos  * key              = ('_' | alpha) { '_' | alnum}.
   3835  1.1  christos  * value            = value-char {value-char}.
   3836  1.1  christos  * value-char       = unreserved-char | percent-charcode.
   3837  1.1  christos  * unreserved-char  = alnum |'_' | '.' | '-' | '~'. (* RFC3986, Section 2.3 *)
   3838  1.1  christos  * percent-charcode = '%' hexdigit hexdigit.
   3839  1.1  christos  * ...
   3840  1.1  christos  *
   3841  1.1  christos  * Should be good enough.
   3842  1.1  christos  */
   3843  1.1  christos typedef struct isc_httpparser_state {
   3844  1.1  christos 	const char *str;
   3845  1.1  christos 
   3846  1.1  christos 	const char *last_key;
   3847  1.1  christos 	size_t last_key_len;
   3848  1.1  christos 
   3849  1.1  christos 	const char *last_value;
   3850  1.1  christos 	size_t last_value_len;
   3851  1.1  christos 
   3852  1.1  christos 	bool query_found;
   3853  1.1  christos 	const char *query;
   3854  1.1  christos 	size_t query_len;
   3855  1.1  christos } isc_httpparser_state_t;
   3856  1.1  christos 
   3857  1.1  christos #define MATCH(ch)      (st->str[0] == (ch))
   3858  1.1  christos #define MATCH_ALPHA()  isalpha((unsigned char)(st->str[0]))
   3859  1.1  christos #define MATCH_DIGIT()  isdigit((unsigned char)(st->str[0]))
   3860  1.1  christos #define MATCH_ALNUM()  isalnum((unsigned char)(st->str[0]))
   3861  1.1  christos #define MATCH_XDIGIT() isxdigit((unsigned char)(st->str[0]))
   3862  1.1  christos #define ADVANCE()      st->str++
   3863  1.1  christos #define GETP()	       (st->str)
   3864  1.1  christos 
   3865  1.1  christos static bool
   3866  1.1  christos rule_query_string(isc_httpparser_state_t *st);
   3867  1.1  christos 
   3868  1.1  christos bool
   3869  1.1  christos isc__nm_parse_httpquery(const char *query_string, const char **start,
   3870  1.1  christos 			size_t *len) {
   3871  1.1  christos 	isc_httpparser_state_t state;
   3872  1.1  christos 
   3873  1.1  christos 	REQUIRE(start != NULL);
   3874  1.1  christos 	REQUIRE(len != NULL);
   3875  1.1  christos 
   3876  1.1  christos 	if (query_string == NULL || query_string[0] == '\0') {
   3877  1.4  christos 		return false;
   3878  1.1  christos 	}
   3879  1.1  christos 
   3880  1.1  christos 	state = (isc_httpparser_state_t){ .str = query_string };
   3881  1.1  christos 	if (!rule_query_string(&state)) {
   3882  1.4  christos 		return false;
   3883  1.1  christos 	}
   3884  1.1  christos 
   3885  1.1  christos 	if (!state.query_found) {
   3886  1.4  christos 		return false;
   3887  1.1  christos 	}
   3888  1.1  christos 
   3889  1.1  christos 	*start = state.query;
   3890  1.1  christos 	*len = state.query_len;
   3891  1.1  christos 
   3892  1.4  christos 	return true;
   3893  1.1  christos }
   3894  1.1  christos 
   3895  1.1  christos static bool
   3896  1.1  christos rule_key_value_pair(isc_httpparser_state_t *st);
   3897  1.1  christos 
   3898  1.1  christos static bool
   3899  1.1  christos rule_key(isc_httpparser_state_t *st);
   3900  1.1  christos 
   3901  1.1  christos static bool
   3902  1.1  christos rule_value(isc_httpparser_state_t *st);
   3903  1.1  christos 
   3904  1.1  christos static bool
   3905  1.1  christos rule_value_char(isc_httpparser_state_t *st);
   3906  1.1  christos 
   3907  1.1  christos static bool
   3908  1.1  christos rule_percent_charcode(isc_httpparser_state_t *st);
   3909  1.1  christos 
   3910  1.1  christos static bool
   3911  1.1  christos rule_unreserved_char(isc_httpparser_state_t *st);
   3912  1.1  christos 
   3913  1.1  christos static bool
   3914  1.1  christos rule_query_string(isc_httpparser_state_t *st) {
   3915  1.1  christos 	if (MATCH('?')) {
   3916  1.1  christos 		ADVANCE();
   3917  1.1  christos 	}
   3918  1.1  christos 
   3919  1.1  christos 	while (rule_key_value_pair(st)) {
   3920  1.1  christos 		/* skip */;
   3921  1.1  christos 	}
   3922  1.1  christos 
   3923  1.1  christos 	if (!MATCH('\0')) {
   3924  1.4  christos 		return false;
   3925  1.1  christos 	}
   3926  1.1  christos 
   3927  1.1  christos 	ADVANCE();
   3928  1.4  christos 	return true;
   3929  1.1  christos }
   3930  1.1  christos 
   3931  1.1  christos static bool
   3932  1.1  christos rule_key_value_pair(isc_httpparser_state_t *st) {
   3933  1.1  christos 	if (!rule_key(st)) {
   3934  1.4  christos 		return false;
   3935  1.1  christos 	}
   3936  1.1  christos 
   3937  1.1  christos 	if (MATCH('=')) {
   3938  1.1  christos 		ADVANCE();
   3939  1.1  christos 	} else {
   3940  1.4  christos 		return false;
   3941  1.1  christos 	}
   3942  1.1  christos 
   3943  1.1  christos 	if (rule_value(st)) {
   3944  1.1  christos 		const char dns[] = "dns";
   3945  1.1  christos 		if (st->last_key_len == sizeof(dns) - 1 &&
   3946  1.1  christos 		    memcmp(st->last_key, dns, sizeof(dns) - 1) == 0)
   3947  1.1  christos 		{
   3948  1.1  christos 			st->query_found = true;
   3949  1.1  christos 			st->query = st->last_value;
   3950  1.1  christos 			st->query_len = st->last_value_len;
   3951  1.1  christos 		}
   3952  1.1  christos 	} else {
   3953  1.4  christos 		return false;
   3954  1.1  christos 	}
   3955  1.1  christos 
   3956  1.1  christos 	if (MATCH('&')) {
   3957  1.1  christos 		ADVANCE();
   3958  1.1  christos 	}
   3959  1.1  christos 
   3960  1.4  christos 	return true;
   3961  1.1  christos }
   3962  1.1  christos 
   3963  1.1  christos static bool
   3964  1.1  christos rule_key(isc_httpparser_state_t *st) {
   3965  1.1  christos 	if (MATCH('_') || MATCH_ALPHA()) {
   3966  1.1  christos 		st->last_key = GETP();
   3967  1.1  christos 		ADVANCE();
   3968  1.1  christos 	} else {
   3969  1.4  christos 		return false;
   3970  1.1  christos 	}
   3971  1.1  christos 
   3972  1.1  christos 	while (MATCH('_') || MATCH_ALNUM()) {
   3973  1.1  christos 		ADVANCE();
   3974  1.1  christos 	}
   3975  1.1  christos 
   3976  1.1  christos 	st->last_key_len = GETP() - st->last_key;
   3977  1.4  christos 	return true;
   3978  1.1  christos }
   3979  1.1  christos 
   3980  1.1  christos static bool
   3981  1.1  christos rule_value(isc_httpparser_state_t *st) {
   3982  1.1  christos 	const char *s = GETP();
   3983  1.1  christos 	if (!rule_value_char(st)) {
   3984  1.4  christos 		return false;
   3985  1.1  christos 	}
   3986  1.1  christos 
   3987  1.1  christos 	st->last_value = s;
   3988  1.1  christos 	while (rule_value_char(st)) {
   3989  1.1  christos 		/* skip */;
   3990  1.1  christos 	}
   3991  1.1  christos 	st->last_value_len = GETP() - st->last_value;
   3992  1.4  christos 	return true;
   3993  1.1  christos }
   3994  1.1  christos 
   3995  1.1  christos static bool
   3996  1.1  christos rule_value_char(isc_httpparser_state_t *st) {
   3997  1.1  christos 	if (rule_unreserved_char(st)) {
   3998  1.4  christos 		return true;
   3999  1.1  christos 	}
   4000  1.1  christos 
   4001  1.4  christos 	return rule_percent_charcode(st);
   4002  1.1  christos }
   4003  1.1  christos 
   4004  1.1  christos static bool
   4005  1.1  christos rule_unreserved_char(isc_httpparser_state_t *st) {
   4006  1.1  christos 	if (MATCH_ALNUM() || MATCH('_') || MATCH('.') || MATCH('-') ||
   4007  1.1  christos 	    MATCH('~'))
   4008  1.1  christos 	{
   4009  1.1  christos 		ADVANCE();
   4010  1.4  christos 		return true;
   4011  1.1  christos 	}
   4012  1.4  christos 	return false;
   4013  1.1  christos }
   4014  1.1  christos 
   4015  1.1  christos static bool
   4016  1.1  christos rule_percent_charcode(isc_httpparser_state_t *st) {
   4017  1.1  christos 	if (MATCH('%')) {
   4018  1.1  christos 		ADVANCE();
   4019  1.1  christos 	} else {
   4020  1.4  christos 		return false;
   4021  1.1  christos 	}
   4022  1.1  christos 
   4023  1.1  christos 	if (!MATCH_XDIGIT()) {
   4024  1.4  christos 		return false;
   4025  1.1  christos 	}
   4026  1.1  christos 	ADVANCE();
   4027  1.1  christos 
   4028  1.1  christos 	if (!MATCH_XDIGIT()) {
   4029  1.4  christos 		return false;
   4030  1.1  christos 	}
   4031  1.1  christos 	ADVANCE();
   4032  1.1  christos 
   4033  1.4  christos 	return true;
   4034  1.1  christos }
   4035  1.1  christos 
   4036  1.1  christos /*
   4037  1.1  christos  * DoH URL Location Verifier. Based on the following grammar (EBNF/WSN
   4038  1.1  christos  * notation):
   4039  1.1  christos  *
   4040  1.1  christos  * S             = path_absolute.
   4041  1.1  christos  * path_absolute = '/' [ segments ] '\0'.
   4042  1.1  christos  * segments      = segment_nz { slash_segment }.
   4043  1.1  christos  * slash_segment = '/' segment.
   4044  1.1  christos  * segment       = { pchar }.
   4045  1.1  christos  * segment_nz    = pchar { pchar }.
   4046  1.1  christos  * pchar         = unreserved | pct_encoded | sub_delims | ':' | '@'.
   4047  1.1  christos  * unreserved    = ALPHA | DIGIT | '-' | '.' | '_' | '~'.
   4048  1.1  christos  * pct_encoded   = '%' XDIGIT XDIGIT.
   4049  1.1  christos  * sub_delims    = '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' |
   4050  1.1  christos  *                 ',' | ';' | '='.
   4051  1.1  christos  *
   4052  1.1  christos  * The grammar is extracted from RFC 3986. It is slightly modified to
   4053  1.1  christos  * aid in parser creation, but the end result is the same
   4054  1.1  christos  * (path_absolute is defined slightly differently - split into
   4055  1.1  christos  * multiple productions).
   4056  1.1  christos  *
   4057  1.1  christos  * https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
   4058  1.1  christos  */
   4059  1.1  christos 
   4060  1.1  christos typedef struct isc_http_location_parser_state {
   4061  1.1  christos 	const char *str;
   4062  1.1  christos } isc_http_location_parser_state_t;
   4063  1.1  christos 
   4064  1.1  christos static bool
   4065  1.1  christos rule_loc_path_absolute(isc_http_location_parser_state_t *);
   4066  1.1  christos 
   4067  1.1  christos static bool
   4068  1.1  christos rule_loc_segments(isc_http_location_parser_state_t *);
   4069  1.1  christos 
   4070  1.1  christos static bool
   4071  1.1  christos rule_loc_slash_segment(isc_http_location_parser_state_t *);
   4072  1.1  christos 
   4073  1.1  christos static bool
   4074  1.1  christos rule_loc_segment(isc_http_location_parser_state_t *);
   4075  1.1  christos 
   4076  1.1  christos static bool
   4077  1.1  christos rule_loc_segment_nz(isc_http_location_parser_state_t *);
   4078  1.1  christos 
   4079  1.1  christos static bool
   4080  1.1  christos rule_loc_pchar(isc_http_location_parser_state_t *);
   4081  1.1  christos 
   4082  1.1  christos static bool
   4083  1.1  christos rule_loc_unreserved(isc_http_location_parser_state_t *);
   4084  1.1  christos 
   4085  1.1  christos static bool
   4086  1.1  christos rule_loc_pct_encoded(isc_http_location_parser_state_t *);
   4087  1.1  christos 
   4088  1.1  christos static bool
   4089  1.1  christos rule_loc_sub_delims(isc_http_location_parser_state_t *);
   4090  1.1  christos 
   4091  1.1  christos static bool
   4092  1.1  christos rule_loc_path_absolute(isc_http_location_parser_state_t *st) {
   4093  1.1  christos 	if (MATCH('/')) {
   4094  1.1  christos 		ADVANCE();
   4095  1.1  christos 	} else {
   4096  1.4  christos 		return false;
   4097  1.1  christos 	}
   4098  1.1  christos 
   4099  1.1  christos 	(void)rule_loc_segments(st);
   4100  1.1  christos 
   4101  1.1  christos 	if (MATCH('\0')) {
   4102  1.1  christos 		ADVANCE();
   4103  1.1  christos 	} else {
   4104  1.4  christos 		return false;
   4105  1.1  christos 	}
   4106  1.1  christos 
   4107  1.4  christos 	return true;
   4108  1.1  christos }
   4109  1.1  christos 
   4110  1.1  christos static bool
   4111  1.1  christos rule_loc_segments(isc_http_location_parser_state_t *st) {
   4112  1.1  christos 	if (!rule_loc_segment_nz(st)) {
   4113  1.4  christos 		return false;
   4114  1.1  christos 	}
   4115  1.1  christos 
   4116  1.1  christos 	while (rule_loc_slash_segment(st)) {
   4117  1.1  christos 		/* zero or more */;
   4118  1.1  christos 	}
   4119  1.1  christos 
   4120  1.4  christos 	return true;
   4121  1.1  christos }
   4122  1.1  christos 
   4123  1.1  christos static bool
   4124  1.1  christos rule_loc_slash_segment(isc_http_location_parser_state_t *st) {
   4125  1.1  christos 	if (MATCH('/')) {
   4126  1.1  christos 		ADVANCE();
   4127  1.1  christos 	} else {
   4128  1.4  christos 		return false;
   4129  1.1  christos 	}
   4130  1.1  christos 
   4131  1.4  christos 	return rule_loc_segment(st);
   4132  1.1  christos }
   4133  1.1  christos 
   4134  1.1  christos static bool
   4135  1.1  christos rule_loc_segment(isc_http_location_parser_state_t *st) {
   4136  1.1  christos 	while (rule_loc_pchar(st)) {
   4137  1.1  christos 		/* zero or more */;
   4138  1.1  christos 	}
   4139  1.1  christos 
   4140  1.4  christos 	return true;
   4141  1.1  christos }
   4142  1.1  christos 
   4143  1.1  christos static bool
   4144  1.1  christos rule_loc_segment_nz(isc_http_location_parser_state_t *st) {
   4145  1.1  christos 	if (!rule_loc_pchar(st)) {
   4146  1.4  christos 		return false;
   4147  1.1  christos 	}
   4148  1.1  christos 
   4149  1.1  christos 	while (rule_loc_pchar(st)) {
   4150  1.1  christos 		/* zero or more */;
   4151  1.1  christos 	}
   4152  1.1  christos 
   4153  1.4  christos 	return true;
   4154  1.1  christos }
   4155  1.1  christos 
   4156  1.1  christos static bool
   4157  1.1  christos rule_loc_pchar(isc_http_location_parser_state_t *st) {
   4158  1.1  christos 	if (rule_loc_unreserved(st)) {
   4159  1.4  christos 		return true;
   4160  1.1  christos 	} else if (rule_loc_pct_encoded(st)) {
   4161  1.4  christos 		return true;
   4162  1.1  christos 	} else if (rule_loc_sub_delims(st)) {
   4163  1.4  christos 		return true;
   4164  1.1  christos 	} else if (MATCH(':') || MATCH('@')) {
   4165  1.1  christos 		ADVANCE();
   4166  1.4  christos 		return true;
   4167  1.1  christos 	}
   4168  1.1  christos 
   4169  1.4  christos 	return false;
   4170  1.1  christos }
   4171  1.1  christos 
   4172  1.1  christos static bool
   4173  1.1  christos rule_loc_unreserved(isc_http_location_parser_state_t *st) {
   4174  1.1  christos 	if (MATCH_ALPHA() | MATCH_DIGIT() | MATCH('-') | MATCH('.') |
   4175  1.1  christos 	    MATCH('_') | MATCH('~'))
   4176  1.1  christos 	{
   4177  1.1  christos 		ADVANCE();
   4178  1.4  christos 		return true;
   4179  1.1  christos 	}
   4180  1.4  christos 	return false;
   4181  1.1  christos }
   4182  1.1  christos 
   4183  1.1  christos static bool
   4184  1.1  christos rule_loc_pct_encoded(isc_http_location_parser_state_t *st) {
   4185  1.1  christos 	if (!MATCH('%')) {
   4186  1.4  christos 		return false;
   4187  1.1  christos 	}
   4188  1.1  christos 	ADVANCE();
   4189  1.1  christos 
   4190  1.1  christos 	if (!MATCH_XDIGIT()) {
   4191  1.4  christos 		return false;
   4192  1.1  christos 	}
   4193  1.1  christos 	ADVANCE();
   4194  1.1  christos 
   4195  1.1  christos 	if (!MATCH_XDIGIT()) {
   4196  1.4  christos 		return false;
   4197  1.1  christos 	}
   4198  1.1  christos 	ADVANCE();
   4199  1.1  christos 
   4200  1.4  christos 	return true;
   4201  1.1  christos }
   4202  1.1  christos 
   4203  1.1  christos static bool
   4204  1.1  christos rule_loc_sub_delims(isc_http_location_parser_state_t *st) {
   4205  1.1  christos 	if (MATCH('!') | MATCH('$') | MATCH('&') | MATCH('\'') | MATCH('(') |
   4206  1.1  christos 	    MATCH(')') | MATCH('*') | MATCH('+') | MATCH(',') | MATCH(';') |
   4207  1.1  christos 	    MATCH('='))
   4208  1.1  christos 	{
   4209  1.1  christos 		ADVANCE();
   4210  1.4  christos 		return true;
   4211  1.1  christos 	}
   4212  1.1  christos 
   4213  1.4  christos 	return false;
   4214  1.1  christos }
   4215  1.1  christos 
   4216  1.1  christos bool
   4217  1.1  christos isc_nm_http_path_isvalid(const char *path) {
   4218  1.1  christos 	isc_http_location_parser_state_t state = { 0 };
   4219  1.1  christos 
   4220  1.1  christos 	REQUIRE(path != NULL);
   4221  1.1  christos 
   4222  1.1  christos 	state.str = path;
   4223  1.1  christos 
   4224  1.4  christos 	return rule_loc_path_absolute(&state);
   4225  1.1  christos }
   4226