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