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