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