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, ®ion);
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