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