1 1.1 christos /* $NetBSD: streamdns.c,v 1.4 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 <limits.h> 17 1.1 christos #include <unistd.h> 18 1.1 christos 19 1.1 christos #include <isc/async.h> 20 1.1 christos #include <isc/atomic.h> 21 1.1 christos #include <isc/result.h> 22 1.1 christos #include <isc/thread.h> 23 1.1 christos 24 1.1 christos #include "netmgr-int.h" 25 1.1 christos 26 1.1 christos /* 27 1.1 christos * Stream DNS is a unified transport capable of serving both DNS over 28 1.1 christos * TCP and DNS over TLS. It is built on top of 29 1.1 christos * 'isc_dnsstream_assembler_t' which is used for assembling DNS 30 1.1 christos * messages in the format used for DNS over TCP out of incoming data. 31 1.1 christos * It is built on top of 'isc_buffer_t' optimised for small (>= 512 32 1.1 christos * bytes) DNS messages. For small messages it uses a small static 33 1.1 christos * memory buffer, but it can automatically switch to a larger 34 1.1 christos * dynamically allocated memory buffer for larger ones. This way we 35 1.1 christos * avoid unnecessary memory allocation requests in most cases, as most 36 1.1 christos * DNS messages are small. 37 1.1 christos * 38 1.1 christos * The use of 'isc_dnsstream_assembler_t' allows decoupling DNS 39 1.1 christos * message assembling code from networking code itself, making it 40 1.1 christos * easier to test. 41 1.1 christos * 42 1.1 christos * To understand how the part responsible for reading of data works, 43 1.1 christos * start by looking at 'streamdns_on_dnsmessage_data_cb()' (the DNS 44 1.1 christos * message data processing callback) and 45 1.1 christos * 'streamdns_handle_incoming_data()' which passes incoming data to 46 1.1 christos * the 'isc_dnsstream_assembler_t' object within the socket. 47 1.1 christos * 48 1.1 christos * The writing is done in a simpler manner due to the fact that we 49 1.1 christos * have full control over the data. For each write request we attempt 50 1.1 christos * to allocate a 'streamdns_send_req_t' structure, whose main purpose 51 1.1 christos * is to keep the data required for the send request processing. 52 1.1 christos * 53 1.1 christos * When processing write requests there is an important optimisation: 54 1.1 christos * we attempt to reuse 'streamdns_send_req_t' objects again, in order 55 1.1 christos * to avoid memory allocations when requesting memory for the new 56 1.1 christos * 'streamdns_send_req_t' object. 57 1.1 christos * 58 1.1 christos * To understand how sending is done, start by looking at 59 1.1 christos * 'isc__nm_streamdns_send()'. Additionally also take a look at 60 1.1 christos * 'streamdns_get_send_req()' and 'streamdns_put_send_req()' which are 61 1.1 christos * responsible for send requests allocation/reuse and initialisation. 62 1.1 christos * 63 1.1 christos * The rest of the code is mostly wrapping code to expose the 64 1.1 christos * functionality of the underlying transport, which at the moment 65 1.1 christos * could be either TCP or TLS. 66 1.1 christos */ 67 1.1 christos 68 1.1 christos typedef struct streamdns_send_req { 69 1.1 christos isc_nm_cb_t cb; /* send callback */ 70 1.1 christos void *cbarg; /* send callback argument */ 71 1.1 christos isc_nmhandle_t *dnshandle; /* Stream DNS socket handle */ 72 1.1 christos } streamdns_send_req_t; 73 1.1 christos 74 1.1 christos static streamdns_send_req_t * 75 1.1 christos streamdns_get_send_req(isc_nmsocket_t *sock, isc_mem_t *mctx, 76 1.1 christos isc__nm_uvreq_t *req); 77 1.1 christos 78 1.1 christos static void 79 1.1 christos streamdns_put_send_req(isc_mem_t *mctx, streamdns_send_req_t *send_req, 80 1.1 christos const bool force_destroy); 81 1.1 christos 82 1.1 christos static void 83 1.1 christos streamdns_readcb(isc_nmhandle_t *handle, isc_result_t result, 84 1.1 christos isc_region_t *region, void *cbarg); 85 1.1 christos 86 1.1 christos static void 87 1.1 christos streamdns_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result, 88 1.1 christos const bool async); 89 1.1 christos 90 1.1 christos static void 91 1.1 christos streamdns_try_close_unused(isc_nmsocket_t *sock); 92 1.1 christos 93 1.1 christos static bool 94 1.1 christos streamdns_closing(isc_nmsocket_t *sock); 95 1.1 christos 96 1.1 christos static void 97 1.1 christos streamdns_resume_processing(void *arg); 98 1.4 christos static void 99 1.4 christos async_streamdns_resume_processing(void *arg); 100 1.1 christos 101 1.1 christos static void 102 1.1 christos streamdns_resumeread(isc_nmsocket_t *sock, isc_nmhandle_t *transphandle) { 103 1.1 christos if (!sock->streamdns.reading) { 104 1.1 christos sock->streamdns.reading = true; 105 1.1 christos isc_nm_read(transphandle, streamdns_readcb, (void *)sock); 106 1.1 christos } 107 1.1 christos } 108 1.1 christos 109 1.1 christos static void 110 1.1 christos streamdns_readmore(isc_nmsocket_t *sock, isc_nmhandle_t *transphandle) { 111 1.1 christos streamdns_resumeread(sock, transphandle); 112 1.1 christos 113 1.1 christos /* Restart the timer only if there's a last single active handle */ 114 1.1 christos isc_nmhandle_t *handle = ISC_LIST_HEAD(sock->active_handles); 115 1.1 christos INSIST(handle != NULL); 116 1.1 christos if (ISC_LIST_NEXT(handle, active_link) == NULL) { 117 1.1 christos isc__nmsocket_timer_start(sock); 118 1.1 christos } 119 1.1 christos } 120 1.1 christos 121 1.1 christos static void 122 1.1 christos streamdns_pauseread(isc_nmsocket_t *sock, isc_nmhandle_t *transphandle) { 123 1.1 christos if (sock->streamdns.reading) { 124 1.1 christos sock->streamdns.reading = false; 125 1.1 christos isc_nm_read_stop(transphandle); 126 1.1 christos } 127 1.1 christos } 128 1.1 christos 129 1.1 christos static bool 130 1.1 christos streamdns_on_complete_dnsmessage(isc_dnsstream_assembler_t *dnsasm, 131 1.1 christos isc_region_t *restrict region, 132 1.1 christos isc_nmsocket_t *sock, 133 1.1 christos isc_nmhandle_t *transphandle) { 134 1.1 christos const bool last_datum = isc_dnsstream_assembler_remaininglength( 135 1.1 christos dnsasm) == region->length; 136 1.1 christos /* 137 1.1 christos * Stop after one message if a client connection. 138 1.1 christos */ 139 1.1 christos bool stop = sock->client; 140 1.1 christos 141 1.1 christos sock->reading = false; 142 1.1 christos if (sock->recv_cb != NULL) { 143 1.1 christos if (!sock->client) { 144 1.1 christos /* 145 1.1 christos * We must allocate a new handle object, as we 146 1.1 christos * need to ensure that after processing of this 147 1.1 christos * message has been completed and the handle 148 1.1 christos * gets destroyed, 'nsock->closehandle_cb' 149 1.1 christos * (streamdns_resume_processing()) is invoked. 150 1.1 christos * That is required for pipelining support. 151 1.1 christos */ 152 1.1 christos isc_nmhandle_t *handle = isc__nmhandle_get( 153 1.1 christos sock, &sock->peer, &sock->iface); 154 1.1 christos sock->recv_cb(handle, ISC_R_SUCCESS, region, 155 1.1 christos sock->recv_cbarg); 156 1.1 christos isc_nmhandle_detach(&handle); 157 1.1 christos } else { 158 1.1 christos /* 159 1.1 christos * As on the client side we are supposed to stop 160 1.1 christos * reading/processing after receiving one 161 1.1 christos * message, we can use the 'sock->recv_handle' 162 1.1 christos * from which we would need to detach before 163 1.1 christos * calling the read callback anyway. 164 1.1 christos */ 165 1.1 christos isc_nmhandle_t *recv_handle = sock->recv_handle; 166 1.1 christos sock->recv_handle = NULL; 167 1.1 christos sock->recv_cb(recv_handle, ISC_R_SUCCESS, region, 168 1.1 christos sock->recv_cbarg); 169 1.1 christos isc_nmhandle_detach(&recv_handle); 170 1.1 christos } 171 1.1 christos 172 1.1 christos if (streamdns_closing(sock)) { 173 1.1 christos stop = true; 174 1.1 christos } 175 1.1 christos } else { 176 1.1 christos stop = true; 177 1.1 christos } 178 1.1 christos 179 1.1 christos if (sock->active_handles_max != 0 && 180 1.1 christos (sock->active_handles_cur >= sock->active_handles_max)) 181 1.1 christos { 182 1.1 christos stop = true; 183 1.1 christos } 184 1.1 christos INSIST(sock->active_handles_cur <= sock->active_handles_max); 185 1.1 christos 186 1.1 christos isc__nmsocket_timer_stop(sock); 187 1.1 christos if (stop) { 188 1.1 christos streamdns_pauseread(sock, transphandle); 189 1.1 christos } else if (last_datum) { 190 1.1 christos /* 191 1.1 christos * We have processed all data, need to read more. 192 1.1 christos * The call also restarts the timer. 193 1.1 christos */ 194 1.1 christos streamdns_readmore(sock, transphandle); 195 1.1 christos } else { 196 1.1 christos /* 197 1.1 christos * Process more DNS messages in the next loop tick. 198 1.1 christos */ 199 1.1 christos streamdns_pauseread(sock, transphandle); 200 1.4 christos isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL }); 201 1.4 christos isc_async_run(sock->worker->loop, 202 1.4 christos async_streamdns_resume_processing, sock); 203 1.1 christos } 204 1.1 christos 205 1.1 christos return false; 206 1.1 christos } 207 1.1 christos 208 1.1 christos /* 209 1.1 christos * This function, alongside 'streamdns_handle_incoming_data()', 210 1.1 christos * connects networking code to the 'isc_dnsstream_assembler_t'. It is 211 1.1 christos * responsible for making decisions regarding reading from the 212 1.1 christos * underlying transport socket as well as controlling the read timer. 213 1.1 christos */ 214 1.1 christos static bool 215 1.1 christos streamdns_on_dnsmessage_data_cb(isc_dnsstream_assembler_t *dnsasm, 216 1.1 christos const isc_result_t result, 217 1.1 christos isc_region_t *restrict region, void *cbarg, 218 1.1 christos void *userarg) { 219 1.1 christos isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg; 220 1.1 christos isc_nmhandle_t *transphandle = (isc_nmhandle_t *)userarg; 221 1.1 christos 222 1.1 christos switch (result) { 223 1.1 christos case ISC_R_SUCCESS: 224 1.1 christos /* 225 1.1 christos * A complete DNS message has been assembled from the incoming 226 1.1 christos * data. Let's process it. 227 1.1 christos */ 228 1.1 christos return streamdns_on_complete_dnsmessage(dnsasm, region, sock, 229 1.1 christos transphandle); 230 1.1 christos case ISC_R_RANGE: 231 1.1 christos /* 232 1.1 christos * It seems that someone attempts to send us some binary junk 233 1.1 christos * over the socket, as the beginning of the next message tells 234 1.1 christos * us the there is an empty (0-sized) DNS message to receive. 235 1.1 christos * We should treat it as a hard error. 236 1.1 christos */ 237 1.1 christos streamdns_failed_read_cb(sock, result, false); 238 1.1 christos return false; 239 1.1 christos case ISC_R_NOMORE: 240 1.1 christos /* 241 1.1 christos * We do not have enough data to process the next message and 242 1.1 christos * thus we need to resume reading from the socket. 243 1.1 christos */ 244 1.1 christos if (sock->recv_handle != NULL) { 245 1.1 christos streamdns_readmore(sock, transphandle); 246 1.1 christos } 247 1.1 christos return false; 248 1.1 christos default: 249 1.1 christos UNREACHABLE(); 250 1.1 christos }; 251 1.1 christos } 252 1.1 christos 253 1.1 christos static void 254 1.1 christos streamdns_handle_incoming_data(isc_nmsocket_t *sock, 255 1.1 christos isc_nmhandle_t *transphandle, 256 1.1 christos void *restrict data, size_t len) { 257 1.1 christos isc_dnsstream_assembler_t *dnsasm = sock->streamdns.input; 258 1.1 christos 259 1.1 christos /* 260 1.1 christos * Try to process the received data or, when 'data == NULL' and 261 1.1 christos * 'len == 0', try to resume processing of the data within the 262 1.1 christos * internal buffers or resume reading, if there is no any. 263 1.1 christos */ 264 1.1 christos isc_dnsstream_assembler_incoming(dnsasm, transphandle, data, len); 265 1.1 christos streamdns_try_close_unused(sock); 266 1.1 christos } 267 1.1 christos 268 1.1 christos static isc_nmsocket_t * 269 1.1 christos streamdns_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type, 270 1.1 christos isc_sockaddr_t *addr, const bool is_server) { 271 1.1 christos isc_nmsocket_t *sock; 272 1.1 christos INSIST(type == isc_nm_streamdnssocket || 273 1.1 christos type == isc_nm_streamdnslistener); 274 1.1 christos 275 1.1 christos sock = isc_mempool_get(worker->nmsocket_pool); 276 1.1 christos isc__nmsocket_init(sock, worker, type, addr, NULL); 277 1.1 christos sock->result = ISC_R_UNSET; 278 1.1 christos if (type == isc_nm_streamdnssocket) { 279 1.1 christos uint32_t initial = 0; 280 1.1 christos isc_nm_gettimeouts(worker->netmgr, &initial, NULL, NULL, NULL); 281 1.1 christos sock->read_timeout = initial; 282 1.1 christos sock->client = !is_server; 283 1.1 christos sock->connecting = !is_server; 284 1.1 christos sock->streamdns.input = isc_dnsstream_assembler_new( 285 1.1 christos sock->worker->mctx, streamdns_on_dnsmessage_data_cb, 286 1.1 christos sock); 287 1.1 christos } 288 1.1 christos 289 1.1 christos return sock; 290 1.1 christos } 291 1.1 christos 292 1.1 christos static void 293 1.1 christos streamdns_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle, 294 1.1 christos const isc_result_t result) { 295 1.1 christos sock->connecting = false; 296 1.1 christos INSIST(sock->connect_cb != NULL); 297 1.1 christos sock->connect_cb(handle, result, sock->connect_cbarg); 298 1.1 christos if (result != ISC_R_SUCCESS) { 299 1.1 christos isc__nmsocket_clearcb(handle->sock); 300 1.1 christos } else { 301 1.1 christos sock->connected = true; 302 1.1 christos } 303 1.1 christos streamdns_try_close_unused(sock); 304 1.1 christos } 305 1.1 christos 306 1.1 christos static void 307 1.1 christos streamdns_save_alpn_status(isc_nmsocket_t *dnssock, 308 1.1 christos isc_nmhandle_t *transp_handle) { 309 1.1 christos const unsigned char *alpn = NULL; 310 1.1 christos unsigned int alpnlen = 0; 311 1.1 christos 312 1.1 christos isc__nmhandle_get_selected_alpn(transp_handle, &alpn, &alpnlen); 313 1.1 christos if (alpn != NULL && alpnlen == ISC_TLS_DOT_PROTO_ALPN_ID_LEN && 314 1.1 christos memcmp(ISC_TLS_DOT_PROTO_ALPN_ID, alpn, 315 1.1 christos ISC_TLS_DOT_PROTO_ALPN_ID_LEN) == 0) 316 1.1 christos { 317 1.1 christos dnssock->streamdns.dot_alpn_negotiated = true; 318 1.1 christos } 319 1.1 christos } 320 1.1 christos 321 1.1 christos static void 322 1.1 christos streamdns_transport_connected(isc_nmhandle_t *handle, isc_result_t result, 323 1.1 christos void *cbarg) { 324 1.1 christos isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg; 325 1.1 christos isc_nmhandle_t *streamhandle = NULL; 326 1.1 christos 327 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 328 1.1 christos 329 1.1 christos sock->tid = isc_tid(); 330 1.1 christos if (result == ISC_R_EOF) { 331 1.1 christos /* 332 1.1 christos * The transport layer (probably TLS) has returned EOF during 333 1.1 christos * connection establishment. That means that connection has 334 1.1 christos * been "cancelled" (for compatibility with old transport 335 1.1 christos * behaviour). 336 1.1 christos */ 337 1.1 christos result = ISC_R_CANCELED; 338 1.1 christos goto error; 339 1.1 christos } else if (result == ISC_R_TLSERROR) { 340 1.1 christos /* 341 1.1 christos * In some of the cases when the old code would return 342 1.1 christos * ISC_R_CANCELLED, the new code could return generic 343 1.1 christos * ISC_R_TLSERROR code. However, the old code does not expect 344 1.1 christos * that. 345 1.1 christos */ 346 1.1 christos result = ISC_R_CANCELED; 347 1.1 christos goto error; 348 1.1 christos } else if (result != ISC_R_SUCCESS) { 349 1.1 christos goto error; 350 1.1 christos } 351 1.1 christos 352 1.1 christos INSIST(VALID_NMHANDLE(handle)); 353 1.1 christos 354 1.1 christos sock->iface = isc_nmhandle_localaddr(handle); 355 1.1 christos sock->peer = isc_nmhandle_peeraddr(handle); 356 1.1 christos if (isc__nmsocket_closing(handle->sock)) { 357 1.1 christos result = ISC_R_SHUTTINGDOWN; 358 1.1 christos goto error; 359 1.1 christos } 360 1.1 christos 361 1.1 christos isc_nmhandle_attach(handle, &sock->outerhandle); 362 1.1 christos sock->active = true; 363 1.1 christos 364 1.1 christos handle->sock->streamdns.sock = sock; 365 1.1 christos 366 1.1 christos streamdns_save_alpn_status(sock, handle); 367 1.1 christos isc__nmhandle_set_manual_timer(sock->outerhandle, true); 368 1.1 christos streamhandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface); 369 1.1 christos (void)isc_nmhandle_set_tcp_nodelay(sock->outerhandle, true); 370 1.1 christos streamdns_call_connect_cb(sock, streamhandle, result); 371 1.1 christos isc_nmhandle_detach(&streamhandle); 372 1.1 christos 373 1.1 christos return; 374 1.1 christos error: 375 1.1 christos if (handle != NULL) { 376 1.1 christos /* 377 1.1 christos * Let's save the error description (if any) so that 378 1.1 christos * e.g. 'dig' could produce a usable error message. 379 1.1 christos */ 380 1.1 christos INSIST(VALID_NMHANDLE(handle)); 381 1.1 christos sock->streamdns.tls_verify_error = 382 1.1 christos isc_nm_verify_tls_peer_result_string(handle); 383 1.1 christos } 384 1.1 christos streamhandle = isc__nmhandle_get(sock, NULL, NULL); 385 1.1 christos sock->closed = true; 386 1.1 christos streamdns_call_connect_cb(sock, streamhandle, result); 387 1.1 christos isc_nmhandle_detach(&streamhandle); 388 1.1 christos isc__nmsocket_detach(&sock); 389 1.1 christos } 390 1.1 christos 391 1.1 christos void 392 1.1 christos isc_nm_streamdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, 393 1.1 christos isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg, 394 1.1 christos unsigned int timeout, isc_tlsctx_t *tlsctx, 395 1.3 christos const char *sni_hostname, 396 1.1 christos isc_tlsctx_client_session_cache_t *client_sess_cache, 397 1.1 christos isc_nm_proxy_type_t proxy_type, 398 1.1 christos isc_nm_proxyheader_info_t *proxy_info) { 399 1.1 christos isc_nmsocket_t *nsock = NULL; 400 1.1 christos isc__networker_t *worker = NULL; 401 1.1 christos 402 1.1 christos REQUIRE(VALID_NM(mgr)); 403 1.1 christos 404 1.1 christos worker = &mgr->workers[isc_tid()]; 405 1.1 christos 406 1.1 christos if (isc__nm_closing(worker)) { 407 1.1 christos cb(NULL, ISC_R_SHUTTINGDOWN, cbarg); 408 1.1 christos return; 409 1.1 christos } 410 1.1 christos 411 1.1 christos nsock = streamdns_sock_new(worker, isc_nm_streamdnssocket, local, 412 1.1 christos false); 413 1.1 christos nsock->connect_cb = cb; 414 1.1 christos nsock->connect_cbarg = cbarg; 415 1.1 christos nsock->connect_timeout = timeout; 416 1.1 christos 417 1.1 christos switch (proxy_type) { 418 1.1 christos case ISC_NM_PROXY_NONE: 419 1.1 christos if (tlsctx == NULL) { 420 1.1 christos INSIST(client_sess_cache == NULL); 421 1.1 christos isc_nm_tcpconnect(mgr, local, peer, 422 1.1 christos streamdns_transport_connected, nsock, 423 1.1 christos nsock->connect_timeout); 424 1.1 christos } else { 425 1.1 christos isc_nm_tlsconnect( 426 1.1 christos mgr, local, peer, streamdns_transport_connected, 427 1.3 christos nsock, tlsctx, sni_hostname, client_sess_cache, 428 1.1 christos nsock->connect_timeout, false, proxy_info); 429 1.1 christos } 430 1.1 christos break; 431 1.1 christos case ISC_NM_PROXY_PLAIN: 432 1.1 christos if (tlsctx == NULL) { 433 1.1 christos isc_nm_proxystreamconnect(mgr, local, peer, 434 1.1 christos streamdns_transport_connected, 435 1.1 christos nsock, nsock->connect_timeout, 436 1.3 christos NULL, NULL, NULL, proxy_info); 437 1.1 christos } else { 438 1.1 christos isc_nm_tlsconnect( 439 1.1 christos mgr, local, peer, streamdns_transport_connected, 440 1.3 christos nsock, tlsctx, sni_hostname, client_sess_cache, 441 1.1 christos nsock->connect_timeout, true, proxy_info); 442 1.1 christos } 443 1.1 christos break; 444 1.1 christos case ISC_NM_PROXY_ENCRYPTED: 445 1.1 christos INSIST(tlsctx != NULL); 446 1.3 christos isc_nm_proxystreamconnect( 447 1.3 christos mgr, local, peer, streamdns_transport_connected, nsock, 448 1.3 christos nsock->connect_timeout, tlsctx, sni_hostname, 449 1.3 christos client_sess_cache, proxy_info); 450 1.1 christos break; 451 1.1 christos default: 452 1.1 christos UNREACHABLE(); 453 1.1 christos } 454 1.1 christos } 455 1.1 christos 456 1.1 christos bool 457 1.1 christos isc__nmsocket_streamdns_timer_running(isc_nmsocket_t *sock) { 458 1.1 christos isc_nmsocket_t *transp_sock; 459 1.1 christos 460 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 461 1.1 christos REQUIRE(sock->type == isc_nm_streamdnssocket); 462 1.1 christos 463 1.1 christos if (sock->outerhandle == NULL) { 464 1.1 christos return false; 465 1.1 christos } 466 1.1 christos 467 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 468 1.1 christos transp_sock = sock->outerhandle->sock; 469 1.1 christos INSIST(VALID_NMSOCK(transp_sock)); 470 1.1 christos 471 1.1 christos return isc__nmsocket_timer_running(transp_sock); 472 1.1 christos } 473 1.1 christos 474 1.1 christos void 475 1.1 christos isc__nmsocket_streamdns_timer_stop(isc_nmsocket_t *sock) { 476 1.1 christos isc_nmsocket_t *transp_sock; 477 1.1 christos 478 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 479 1.1 christos REQUIRE(sock->type == isc_nm_streamdnssocket); 480 1.1 christos 481 1.1 christos if (sock->outerhandle == NULL) { 482 1.1 christos return; 483 1.1 christos } 484 1.1 christos 485 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 486 1.1 christos transp_sock = sock->outerhandle->sock; 487 1.1 christos INSIST(VALID_NMSOCK(transp_sock)); 488 1.1 christos 489 1.1 christos isc__nmsocket_timer_stop(transp_sock); 490 1.1 christos } 491 1.1 christos 492 1.1 christos void 493 1.1 christos isc__nmsocket_streamdns_timer_restart(isc_nmsocket_t *sock) { 494 1.1 christos isc_nmsocket_t *transp_sock; 495 1.1 christos 496 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 497 1.1 christos REQUIRE(sock->type == isc_nm_streamdnssocket); 498 1.1 christos 499 1.1 christos if (sock->outerhandle == NULL) { 500 1.1 christos return; 501 1.1 christos } 502 1.1 christos 503 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 504 1.1 christos transp_sock = sock->outerhandle->sock; 505 1.1 christos INSIST(VALID_NMSOCK(transp_sock)); 506 1.1 christos 507 1.1 christos isc__nmsocket_timer_restart(transp_sock); 508 1.1 christos } 509 1.1 christos 510 1.1 christos static void 511 1.1 christos streamdns_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result, 512 1.1 christos const bool async) { 513 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 514 1.1 christos REQUIRE(result != ISC_R_SUCCESS); 515 1.1 christos 516 1.1 christos /* Nobody is reading from the socket yet */ 517 1.1 christos if (sock->recv_handle == NULL) { 518 1.1 christos goto destroy; 519 1.1 christos } 520 1.1 christos 521 1.1 christos if (sock->client && result == ISC_R_TIMEDOUT) { 522 1.1 christos if (sock->recv_cb != NULL) { 523 1.1 christos isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL); 524 1.1 christos isc__nm_readcb(sock, req, ISC_R_TIMEDOUT, false); 525 1.1 christos } 526 1.1 christos 527 1.1 christos if (isc__nmsocket_timer_running(sock)) { 528 1.1 christos /* Timer was restarted, bail-out */ 529 1.1 christos return; 530 1.1 christos } 531 1.1 christos 532 1.1 christos isc__nmsocket_clearcb(sock); 533 1.1 christos 534 1.1 christos goto destroy; 535 1.1 christos } 536 1.1 christos 537 1.1 christos isc_dnsstream_assembler_clear(sock->streamdns.input); 538 1.1 christos 539 1.1 christos /* Nobody expects the callback if isc_nm_read() wasn't called */ 540 1.1 christos if (!sock->client || sock->reading) { 541 1.1 christos sock->reading = false; 542 1.1 christos 543 1.1 christos if (sock->recv_cb != NULL) { 544 1.1 christos isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL); 545 1.1 christos isc__nmsocket_clearcb(sock); 546 1.1 christos isc__nm_readcb(sock, req, result, async); 547 1.1 christos } 548 1.1 christos } 549 1.1 christos 550 1.1 christos destroy: 551 1.1 christos isc__nmsocket_prep_destroy(sock); 552 1.1 christos } 553 1.1 christos 554 1.1 christos void 555 1.1 christos isc__nm_streamdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, 556 1.1 christos const bool async) { 557 1.1 christos REQUIRE(result != ISC_R_SUCCESS); 558 1.1 christos REQUIRE(sock->type == isc_nm_streamdnssocket); 559 1.1 christos sock->streamdns.reading = false; 560 1.1 christos streamdns_failed_read_cb(sock, result, async); 561 1.1 christos } 562 1.1 christos 563 1.1 christos static void 564 1.1 christos streamdns_readcb(isc_nmhandle_t *handle, isc_result_t result, 565 1.1 christos isc_region_t *region, void *cbarg) { 566 1.1 christos isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg; 567 1.1 christos 568 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 569 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 570 1.1 christos REQUIRE(sock->tid == isc_tid()); 571 1.1 christos 572 1.1 christos if (result != ISC_R_SUCCESS) { 573 1.1 christos streamdns_failed_read_cb(sock, result, false); 574 1.1 christos return; 575 1.1 christos } else if (streamdns_closing(sock)) { 576 1.1 christos streamdns_failed_read_cb(sock, ISC_R_CANCELED, false); 577 1.1 christos return; 578 1.1 christos } 579 1.1 christos 580 1.1 christos streamdns_handle_incoming_data(sock, handle, region->base, 581 1.1 christos region->length); 582 1.1 christos } 583 1.1 christos 584 1.1 christos static void 585 1.1 christos streamdns_try_close_unused(isc_nmsocket_t *sock) { 586 1.1 christos if (sock->recv_handle == NULL && sock->streamdns.nsending == 0) { 587 1.1 christos /* 588 1.1 christos * The socket is unused after calling the callback. Let's close 589 1.1 christos * the underlying connection. 590 1.1 christos */ 591 1.1 christos /* FIXME: call failed_read_cb(?) */ 592 1.1 christos if (sock->outerhandle != NULL) { 593 1.1 christos isc_nmhandle_detach(&sock->outerhandle); 594 1.1 christos } 595 1.1 christos isc__nmsocket_prep_destroy(sock); 596 1.1 christos } 597 1.1 christos } 598 1.1 christos 599 1.1 christos static streamdns_send_req_t * 600 1.1 christos streamdns_get_send_req(isc_nmsocket_t *sock, isc_mem_t *mctx, 601 1.1 christos isc__nm_uvreq_t *req) { 602 1.1 christos streamdns_send_req_t *send_req; 603 1.1 christos 604 1.1 christos if (sock->streamdns.send_req != NULL) { 605 1.1 christos /* 606 1.1 christos * We have a previously allocated object - let's use that. 607 1.1 christos * That should help reducing stress on the memory allocator. 608 1.1 christos */ 609 1.1 christos send_req = (streamdns_send_req_t *)sock->streamdns.send_req; 610 1.1 christos sock->streamdns.send_req = NULL; 611 1.1 christos } else { 612 1.1 christos /* Allocate a new object. */ 613 1.1 christos send_req = isc_mem_get(mctx, sizeof(*send_req)); 614 1.1 christos *send_req = (streamdns_send_req_t){ 0 }; 615 1.1 christos } 616 1.1 christos 617 1.1 christos /* Initialise the send request object */ 618 1.1 christos send_req->cb = req->cb.send; 619 1.1 christos send_req->cbarg = req->cbarg; 620 1.1 christos isc_nmhandle_attach(req->handle, &send_req->dnshandle); 621 1.1 christos 622 1.1 christos sock->streamdns.nsending++; 623 1.1 christos 624 1.1 christos return send_req; 625 1.1 christos } 626 1.1 christos 627 1.1 christos static void 628 1.1 christos streamdns_put_send_req(isc_mem_t *mctx, streamdns_send_req_t *send_req, 629 1.1 christos const bool force_destroy) { 630 1.1 christos /* 631 1.1 christos * Attempt to put the object for reuse later if we are not 632 1.1 christos * wrapping up. 633 1.1 christos */ 634 1.1 christos if (!force_destroy) { 635 1.1 christos isc_nmsocket_t *sock = send_req->dnshandle->sock; 636 1.1 christos sock->streamdns.nsending--; 637 1.1 christos isc_nmhandle_detach(&send_req->dnshandle); 638 1.1 christos if (sock->streamdns.send_req == NULL) { 639 1.1 christos sock->streamdns.send_req = send_req; 640 1.1 christos /* 641 1.1 christos * An object has been recycled, 642 1.1 christos * if not - we are going to destroy it. 643 1.1 christos */ 644 1.1 christos return; 645 1.1 christos } 646 1.1 christos } 647 1.1 christos 648 1.1 christos isc_mem_put(mctx, send_req, sizeof(*send_req)); 649 1.1 christos } 650 1.1 christos 651 1.1 christos static void 652 1.1 christos streamdns_writecb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { 653 1.1 christos streamdns_send_req_t *send_req = (streamdns_send_req_t *)cbarg; 654 1.1 christos isc_mem_t *mctx; 655 1.1 christos isc_nm_cb_t cb; 656 1.1 christos void *send_cbarg; 657 1.1 christos isc_nmhandle_t *dnshandle = NULL; 658 1.1 christos 659 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 660 1.1 christos REQUIRE(VALID_NMHANDLE(send_req->dnshandle)); 661 1.1 christos REQUIRE(VALID_NMSOCK(send_req->dnshandle->sock)); 662 1.1 christos REQUIRE(send_req->dnshandle->sock->tid == isc_tid()); 663 1.1 christos 664 1.1 christos mctx = send_req->dnshandle->sock->worker->mctx; 665 1.1 christos cb = send_req->cb; 666 1.1 christos send_cbarg = send_req->cbarg; 667 1.1 christos 668 1.1 christos isc_nmhandle_attach(send_req->dnshandle, &dnshandle); 669 1.1 christos /* try to keep the send request object for reuse */ 670 1.1 christos streamdns_put_send_req(mctx, send_req, false); 671 1.1 christos cb(dnshandle, result, send_cbarg); 672 1.1 christos streamdns_try_close_unused(dnshandle->sock); 673 1.1 christos isc_nmhandle_detach(&dnshandle); 674 1.1 christos } 675 1.1 christos 676 1.1 christos static bool 677 1.1 christos streamdns_closing(isc_nmsocket_t *sock) { 678 1.1 christos return isc__nmsocket_closing(sock) || isc__nm_closing(sock->worker) || 679 1.1 christos sock->outerhandle == NULL || 680 1.1 christos (sock->outerhandle != NULL && 681 1.1 christos isc__nmsocket_closing(sock->outerhandle->sock)); 682 1.1 christos } 683 1.1 christos 684 1.1 christos static void 685 1.1 christos streamdns_resume_processing(void *arg) { 686 1.1 christos isc_nmsocket_t *sock = (isc_nmsocket_t *)arg; 687 1.1 christos 688 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 689 1.1 christos REQUIRE(sock->tid == isc_tid()); 690 1.1 christos REQUIRE(!sock->client); 691 1.1 christos 692 1.1 christos if (streamdns_closing(sock)) { 693 1.1 christos return; 694 1.1 christos } 695 1.1 christos 696 1.1 christos if (sock->active_handles_max != 0 && 697 1.1 christos (sock->active_handles_cur >= sock->active_handles_max)) 698 1.1 christos { 699 1.1 christos return; 700 1.1 christos } 701 1.1 christos 702 1.1 christos streamdns_handle_incoming_data(sock, sock->outerhandle, NULL, 0); 703 1.1 christos } 704 1.1 christos 705 1.4 christos static void 706 1.4 christos async_streamdns_resume_processing(void *arg) { 707 1.4 christos isc_nmsocket_t *sock = (isc_nmsocket_t *)arg; 708 1.4 christos 709 1.4 christos streamdns_resume_processing(sock); 710 1.4 christos 711 1.4 christos isc__nmsocket_detach(&sock); 712 1.4 christos } 713 1.4 christos 714 1.1 christos static isc_result_t 715 1.1 christos streamdns_accept_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { 716 1.1 christos isc_nmsocket_t *listensock = (isc_nmsocket_t *)cbarg; 717 1.1 christos isc_nmsocket_t *nsock; 718 1.1 christos isc_sockaddr_t iface; 719 1.1 christos int tid = isc_tid(); 720 1.1 christos uint32_t initial = 0; 721 1.1 christos 722 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 723 1.1 christos REQUIRE(VALID_NMSOCK(handle->sock)); 724 1.1 christos 725 1.1 christos if (isc__nm_closing(handle->sock->worker)) { 726 1.1 christos return ISC_R_SHUTTINGDOWN; 727 1.1 christos } else if (result != ISC_R_SUCCESS) { 728 1.1 christos return result; 729 1.1 christos } 730 1.1 christos 731 1.1 christos REQUIRE(VALID_NMSOCK(listensock)); 732 1.1 christos REQUIRE(listensock->type == isc_nm_streamdnslistener); 733 1.1 christos 734 1.1 christos iface = isc_nmhandle_localaddr(handle); 735 1.1 christos nsock = streamdns_sock_new(handle->sock->worker, isc_nm_streamdnssocket, 736 1.1 christos &iface, true); 737 1.1 christos nsock->recv_cb = listensock->recv_cb; 738 1.1 christos nsock->recv_cbarg = listensock->recv_cbarg; 739 1.1 christos 740 1.1 christos nsock->peer = isc_nmhandle_peeraddr(handle); 741 1.1 christos nsock->tid = tid; 742 1.1 christos isc_nm_gettimeouts(handle->sock->worker->netmgr, &initial, NULL, NULL, 743 1.1 christos NULL); 744 1.1 christos nsock->read_timeout = initial; 745 1.1 christos nsock->accepting = true; 746 1.1 christos nsock->active = true; 747 1.1 christos 748 1.1 christos isc__nmsocket_attach(handle->sock, &nsock->listener); 749 1.1 christos isc_nmhandle_attach(handle, &nsock->outerhandle); 750 1.1 christos handle->sock->streamdns.sock = nsock; 751 1.1 christos 752 1.1 christos streamdns_save_alpn_status(nsock, handle); 753 1.1 christos 754 1.1 christos nsock->recv_handle = isc__nmhandle_get(nsock, NULL, &iface); 755 1.1 christos INSIST(listensock->accept_cb != NULL); 756 1.1 christos result = listensock->accept_cb(nsock->recv_handle, result, 757 1.1 christos listensock->accept_cbarg); 758 1.1 christos if (result != ISC_R_SUCCESS) { 759 1.1 christos isc_nmhandle_detach(&nsock->recv_handle); 760 1.1 christos isc__nmsocket_detach(&nsock->listener); 761 1.1 christos isc_nmhandle_detach(&nsock->outerhandle); 762 1.1 christos nsock->closed = true; 763 1.1 christos goto exit; 764 1.1 christos } 765 1.1 christos 766 1.1 christos nsock->closehandle_cb = streamdns_resume_processing; 767 1.1 christos isc__nmhandle_set_manual_timer(nsock->outerhandle, true); 768 1.1 christos isc_nm_gettimeouts(nsock->worker->netmgr, &initial, NULL, NULL, NULL); 769 1.1 christos /* settimeout restarts the timer */ 770 1.1 christos isc_nmhandle_settimeout(nsock->outerhandle, initial); 771 1.1 christos (void)isc_nmhandle_set_tcp_nodelay(nsock->outerhandle, true); 772 1.1 christos streamdns_handle_incoming_data(nsock, nsock->outerhandle, NULL, 0); 773 1.1 christos 774 1.1 christos exit: 775 1.1 christos nsock->accepting = false; 776 1.1 christos 777 1.1 christos return result; 778 1.1 christos } 779 1.1 christos 780 1.1 christos isc_result_t 781 1.1 christos isc_nm_listenstreamdns(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface, 782 1.1 christos isc_nm_recv_cb_t recv_cb, void *recv_cbarg, 783 1.1 christos isc_nm_accept_cb_t accept_cb, void *accept_cbarg, 784 1.1 christos int backlog, isc_quota_t *quota, isc_tlsctx_t *tlsctx, 785 1.1 christos isc_nm_proxy_type_t proxy_type, isc_nmsocket_t **sockp) { 786 1.1 christos isc_result_t result = ISC_R_FAILURE; 787 1.1 christos isc_nmsocket_t *listener = NULL; 788 1.1 christos isc__networker_t *worker = NULL; 789 1.1 christos 790 1.1 christos REQUIRE(VALID_NM(mgr)); 791 1.1 christos REQUIRE(isc_tid() == 0); 792 1.1 christos 793 1.1 christos worker = &mgr->workers[isc_tid()]; 794 1.1 christos 795 1.1 christos if (isc__nm_closing(worker)) { 796 1.1 christos return ISC_R_SHUTTINGDOWN; 797 1.1 christos } 798 1.1 christos 799 1.1 christos listener = streamdns_sock_new(worker, isc_nm_streamdnslistener, iface, 800 1.1 christos true); 801 1.1 christos listener->accept_cb = accept_cb; 802 1.1 christos listener->accept_cbarg = accept_cbarg; 803 1.1 christos listener->recv_cb = recv_cb; 804 1.1 christos listener->recv_cbarg = recv_cbarg; 805 1.1 christos 806 1.1 christos switch (proxy_type) { 807 1.1 christos case ISC_NM_PROXY_NONE: 808 1.1 christos if (tlsctx == NULL) { 809 1.1 christos result = isc_nm_listentcp( 810 1.1 christos mgr, workers, iface, streamdns_accept_cb, 811 1.1 christos listener, backlog, quota, &listener->outer); 812 1.1 christos } else { 813 1.1 christos result = isc_nm_listentls(mgr, workers, iface, 814 1.1 christos streamdns_accept_cb, listener, 815 1.1 christos backlog, quota, tlsctx, false, 816 1.1 christos &listener->outer); 817 1.1 christos } 818 1.1 christos break; 819 1.1 christos case ISC_NM_PROXY_PLAIN: 820 1.1 christos if (tlsctx == NULL) { 821 1.1 christos result = isc_nm_listenproxystream( 822 1.1 christos mgr, workers, iface, streamdns_accept_cb, 823 1.1 christos listener, backlog, quota, NULL, 824 1.1 christos &listener->outer); 825 1.1 christos } else { 826 1.1 christos result = isc_nm_listentls(mgr, workers, iface, 827 1.1 christos streamdns_accept_cb, listener, 828 1.1 christos backlog, quota, tlsctx, true, 829 1.1 christos &listener->outer); 830 1.1 christos } 831 1.1 christos break; 832 1.1 christos case ISC_NM_PROXY_ENCRYPTED: 833 1.1 christos INSIST(tlsctx != NULL); 834 1.1 christos result = isc_nm_listenproxystream( 835 1.1 christos mgr, workers, iface, streamdns_accept_cb, listener, 836 1.1 christos backlog, quota, tlsctx, &listener->outer); 837 1.1 christos break; 838 1.1 christos default: 839 1.1 christos UNREACHABLE(); 840 1.1 christos }; 841 1.1 christos 842 1.1 christos if (result != ISC_R_SUCCESS) { 843 1.1 christos listener->closed = true; 844 1.1 christos isc__nmsocket_detach(&listener); 845 1.1 christos return result; 846 1.1 christos } 847 1.1 christos 848 1.1 christos /* copy the actual port we're listening on into sock->iface */ 849 1.1 christos if (isc_sockaddr_getport(iface) == 0) { 850 1.1 christos listener->iface = listener->outer->iface; 851 1.1 christos } 852 1.1 christos 853 1.1 christos listener->result = result; 854 1.1 christos listener->active = true; 855 1.1 christos INSIST(listener->outer->streamdns.listener == NULL); 856 1.1 christos listener->nchildren = listener->outer->nchildren; 857 1.1 christos isc__nmsocket_attach(listener, &listener->outer->streamdns.listener); 858 1.1 christos 859 1.1 christos *sockp = listener; 860 1.1 christos 861 1.1 christos return result; 862 1.1 christos } 863 1.1 christos 864 1.1 christos void 865 1.1 christos isc__nm_streamdns_cleanup_data(isc_nmsocket_t *sock) { 866 1.1 christos switch (sock->type) { 867 1.1 christos case isc_nm_streamdnssocket: 868 1.1 christos isc_dnsstream_assembler_free(&sock->streamdns.input); 869 1.1 christos INSIST(sock->streamdns.nsending == 0); 870 1.1 christos if (sock->streamdns.send_req != NULL) { 871 1.1 christos isc_mem_t *mctx = sock->worker->mctx; 872 1.1 christos streamdns_put_send_req(mctx, 873 1.1 christos (streamdns_send_req_t *) 874 1.1 christos sock->streamdns.send_req, 875 1.1 christos true); 876 1.1 christos } 877 1.1 christos break; 878 1.1 christos case isc_nm_streamdnslistener: 879 1.1 christos if (sock->outer) { 880 1.1 christos isc__nmsocket_detach(&sock->outer); 881 1.1 christos } 882 1.1 christos break; 883 1.1 christos case isc_nm_tlslistener: 884 1.1 christos case isc_nm_tcplistener: 885 1.1 christos case isc_nm_proxystreamlistener: 886 1.1 christos if (sock->streamdns.listener != NULL) { 887 1.1 christos isc__nmsocket_detach(&sock->streamdns.listener); 888 1.1 christos } 889 1.1 christos break; 890 1.1 christos case isc_nm_tlssocket: 891 1.1 christos case isc_nm_tcpsocket: 892 1.1 christos case isc_nm_proxystreamsocket: 893 1.1 christos if (sock->streamdns.sock != NULL) { 894 1.1 christos isc__nmsocket_detach(&sock->streamdns.sock); 895 1.1 christos } 896 1.1 christos break; 897 1.1 christos default: 898 1.1 christos return; 899 1.1 christos } 900 1.1 christos } 901 1.1 christos 902 1.1 christos static void 903 1.1 christos streamdns_read_cb(void *arg) { 904 1.1 christos isc_nmsocket_t *sock = arg; 905 1.1 christos 906 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 907 1.1 christos REQUIRE(sock->tid == isc_tid()); 908 1.1 christos 909 1.1 christos if (streamdns_closing(sock)) { 910 1.1 christos streamdns_failed_read_cb(sock, ISC_R_CANCELED, false); 911 1.1 christos goto detach; 912 1.1 christos } 913 1.1 christos 914 1.1 christos if (sock->streamdns.reading) { 915 1.1 christos goto detach; 916 1.1 christos } 917 1.1 christos 918 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 919 1.1 christos streamdns_handle_incoming_data(sock, sock->outerhandle, NULL, 0); 920 1.1 christos detach: 921 1.1 christos isc__nmsocket_detach(&sock); 922 1.1 christos } 923 1.1 christos 924 1.1 christos void 925 1.1 christos isc__nm_streamdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, 926 1.1 christos void *cbarg) { 927 1.1 christos isc_nmsocket_t *sock = NULL; 928 1.1 christos bool closing = false; 929 1.1 christos 930 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 931 1.1 christos sock = handle->sock; 932 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 933 1.1 christos REQUIRE(sock->type == isc_nm_streamdnssocket); 934 1.1 christos REQUIRE(sock->recv_handle == handle || sock->recv_handle == NULL); 935 1.1 christos REQUIRE(sock->tid == isc_tid()); 936 1.1 christos 937 1.1 christos closing = streamdns_closing(sock); 938 1.1 christos 939 1.1 christos sock->recv_cb = cb; 940 1.1 christos sock->recv_cbarg = cbarg; 941 1.1 christos sock->reading = true; 942 1.1 christos if (sock->recv_handle == NULL) { 943 1.1 christos isc_nmhandle_attach(handle, &sock->recv_handle); 944 1.1 christos } 945 1.1 christos 946 1.1 christos /* 947 1.1 christos * In some cases there is little sense in making the operation 948 1.1 christos * asynchronous as we just want to start reading from the 949 1.1 christos * underlying transport. 950 1.1 christos */ 951 1.1 christos if (!closing && isc_dnsstream_assembler_result(sock->streamdns.input) == 952 1.1 christos ISC_R_UNSET) 953 1.1 christos { 954 1.1 christos isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL }); 955 1.1 christos streamdns_read_cb(sock); 956 1.1 christos return; 957 1.1 christos } 958 1.1 christos 959 1.1 christos /* 960 1.1 christos * We want the read operation to be asynchronous in most cases 961 1.1 christos * because: 962 1.1 christos * 963 1.1 christos * 1. A read operation might be initiated from within the read 964 1.1 christos * callback itself. 965 1.1 christos * 966 1.1 christos * 2. Due to the above, we need to make the operation 967 1.1 christos * asynchronous to keep the socket state consistent. 968 1.1 christos */ 969 1.1 christos 970 1.1 christos isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL }); 971 1.1 christos isc_job_run(sock->worker->loop, &sock->job, streamdns_read_cb, sock); 972 1.1 christos } 973 1.1 christos 974 1.1 christos void 975 1.1 christos isc__nm_streamdns_send(isc_nmhandle_t *handle, const isc_region_t *region, 976 1.1 christos isc_nm_cb_t cb, void *cbarg) { 977 1.1 christos isc__nm_uvreq_t *uvreq = NULL; 978 1.1 christos isc_nmsocket_t *sock = NULL; 979 1.1 christos streamdns_send_req_t *send_req; 980 1.1 christos isc_mem_t *mctx; 981 1.1 christos isc_region_t data = { 0 }; 982 1.1 christos 983 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 984 1.1 christos REQUIRE(VALID_NMSOCK(handle->sock)); 985 1.1 christos REQUIRE(region->length <= UINT16_MAX); 986 1.1 christos 987 1.1 christos sock = handle->sock; 988 1.1 christos 989 1.1 christos REQUIRE(sock->type == isc_nm_streamdnssocket); 990 1.1 christos REQUIRE(sock->tid == isc_tid()); 991 1.1 christos 992 1.1 christos uvreq = isc__nm_uvreq_get(sock); 993 1.1 christos isc_nmhandle_attach(handle, &uvreq->handle); 994 1.1 christos uvreq->cb.send = cb; 995 1.1 christos uvreq->cbarg = cbarg; 996 1.1 christos uvreq->uvbuf.base = (char *)region->base; 997 1.1 christos uvreq->uvbuf.len = region->length; 998 1.1 christos 999 1.1 christos if (streamdns_closing(sock)) { 1000 1.1 christos isc__nm_failed_send_cb(sock, uvreq, ISC_R_CANCELED, true); 1001 1.1 christos return; 1002 1.1 christos } 1003 1.1 christos 1004 1.1 christos /* 1005 1.1 christos * As when sending, we, basically, handing data to the underlying 1006 1.1 christos * transport, we can treat the operation synchronously, as the 1007 1.1 christos * transport code will take care of the asynchronicity if required. 1008 1.1 christos */ 1009 1.1 christos mctx = sock->worker->mctx; 1010 1.1 christos send_req = streamdns_get_send_req(sock, mctx, uvreq); 1011 1.1 christos data.base = (unsigned char *)uvreq->uvbuf.base; 1012 1.1 christos data.length = uvreq->uvbuf.len; 1013 1.1 christos isc__nm_senddns(sock->outerhandle, &data, streamdns_writecb, 1014 1.1 christos (void *)send_req); 1015 1.1 christos 1016 1.1 christos isc__nm_uvreq_put(&uvreq); 1017 1.1 christos } 1018 1.1 christos 1019 1.1 christos static void 1020 1.1 christos streamdns_close_direct(isc_nmsocket_t *sock) { 1021 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 1022 1.1 christos REQUIRE(sock->tid == isc_tid()); 1023 1.1 christos 1024 1.1 christos if (sock->outerhandle != NULL) { 1025 1.1 christos sock->streamdns.reading = false; 1026 1.3 christos isc__nmsocket_timer_stop(sock); 1027 1.1 christos isc_nm_read_stop(sock->outerhandle); 1028 1.1 christos isc_nmhandle_close(sock->outerhandle); 1029 1.1 christos isc_nmhandle_detach(&sock->outerhandle); 1030 1.1 christos } 1031 1.1 christos 1032 1.1 christos if (sock->listener != NULL) { 1033 1.1 christos isc__nmsocket_detach(&sock->listener); 1034 1.1 christos } 1035 1.1 christos 1036 1.1 christos if (sock->recv_handle != NULL) { 1037 1.1 christos isc_nmhandle_detach(&sock->recv_handle); 1038 1.1 christos } 1039 1.1 christos 1040 1.1 christos /* Further cleanup performed in isc__nm_streamdns_cleanup_data() */ 1041 1.1 christos isc_dnsstream_assembler_clear(sock->streamdns.input); 1042 1.1 christos sock->closed = true; 1043 1.1 christos sock->active = false; 1044 1.1 christos } 1045 1.1 christos 1046 1.1 christos void 1047 1.1 christos isc__nm_streamdns_close(isc_nmsocket_t *sock) { 1048 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 1049 1.1 christos REQUIRE(sock->type == isc_nm_streamdnssocket); 1050 1.1 christos REQUIRE(sock->tid == isc_tid()); 1051 1.1 christos REQUIRE(!sock->closing); 1052 1.1 christos 1053 1.1 christos sock->closing = true; 1054 1.1 christos 1055 1.1 christos streamdns_close_direct(sock); 1056 1.1 christos } 1057 1.1 christos 1058 1.1 christos void 1059 1.1 christos isc__nm_streamdns_stoplistening(isc_nmsocket_t *sock) { 1060 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 1061 1.1 christos REQUIRE(sock->type == isc_nm_streamdnslistener); 1062 1.1 christos 1063 1.1 christos isc__nmsocket_stop(sock); 1064 1.1 christos } 1065 1.1 christos 1066 1.1 christos void 1067 1.1 christos isc__nmhandle_streamdns_cleartimeout(isc_nmhandle_t *handle) { 1068 1.1 christos isc_nmsocket_t *sock = NULL; 1069 1.1 christos 1070 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 1071 1.1 christos REQUIRE(VALID_NMSOCK(handle->sock)); 1072 1.1 christos REQUIRE(handle->sock->type == isc_nm_streamdnssocket); 1073 1.1 christos 1074 1.1 christos sock = handle->sock; 1075 1.1 christos if (sock->outerhandle != NULL) { 1076 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 1077 1.1 christos isc_nmhandle_cleartimeout(sock->outerhandle); 1078 1.1 christos } 1079 1.1 christos } 1080 1.1 christos 1081 1.1 christos void 1082 1.1 christos isc__nmhandle_streamdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { 1083 1.1 christos isc_nmsocket_t *sock = NULL; 1084 1.1 christos 1085 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 1086 1.1 christos REQUIRE(VALID_NMSOCK(handle->sock)); 1087 1.1 christos REQUIRE(handle->sock->type == isc_nm_streamdnssocket); 1088 1.1 christos 1089 1.1 christos sock = handle->sock; 1090 1.1 christos if (sock->outerhandle != NULL) { 1091 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 1092 1.1 christos isc_nmhandle_settimeout(sock->outerhandle, timeout); 1093 1.1 christos } 1094 1.1 christos } 1095 1.1 christos 1096 1.1 christos void 1097 1.1 christos isc__nmhandle_streamdns_keepalive(isc_nmhandle_t *handle, bool value) { 1098 1.1 christos isc_nmsocket_t *sock = NULL; 1099 1.1 christos 1100 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 1101 1.1 christos REQUIRE(VALID_NMSOCK(handle->sock)); 1102 1.1 christos REQUIRE(handle->sock->type == isc_nm_streamdnssocket); 1103 1.1 christos 1104 1.1 christos sock = handle->sock; 1105 1.1 christos if (sock->outerhandle != NULL) { 1106 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 1107 1.1 christos isc_nmhandle_keepalive(sock->outerhandle, value); 1108 1.1 christos } 1109 1.1 christos } 1110 1.1 christos 1111 1.1 christos void 1112 1.1 christos isc__nmhandle_streamdns_setwritetimeout(isc_nmhandle_t *handle, 1113 1.1 christos uint32_t timeout) { 1114 1.1 christos isc_nmsocket_t *sock = NULL; 1115 1.1 christos 1116 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 1117 1.1 christos REQUIRE(VALID_NMSOCK(handle->sock)); 1118 1.1 christos REQUIRE(handle->sock->type == isc_nm_streamdnssocket); 1119 1.1 christos 1120 1.1 christos sock = handle->sock; 1121 1.1 christos if (sock->outerhandle != NULL) { 1122 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 1123 1.1 christos isc_nmhandle_setwritetimeout(sock->outerhandle, timeout); 1124 1.1 christos } 1125 1.1 christos } 1126 1.1 christos 1127 1.1 christos bool 1128 1.1 christos isc__nm_streamdns_has_encryption(const isc_nmhandle_t *handle) { 1129 1.1 christos isc_nmsocket_t *sock = NULL; 1130 1.1 christos 1131 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 1132 1.1 christos REQUIRE(VALID_NMSOCK(handle->sock)); 1133 1.1 christos REQUIRE(handle->sock->type == isc_nm_streamdnssocket); 1134 1.1 christos 1135 1.1 christos sock = handle->sock; 1136 1.1 christos if (sock->outerhandle != NULL) { 1137 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 1138 1.1 christos return isc_nm_has_encryption(sock->outerhandle); 1139 1.1 christos } 1140 1.1 christos 1141 1.1 christos return false; 1142 1.1 christos } 1143 1.1 christos 1144 1.1 christos const char * 1145 1.1 christos isc__nm_streamdns_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { 1146 1.1 christos isc_nmsocket_t *sock = NULL; 1147 1.1 christos 1148 1.1 christos REQUIRE(VALID_NMHANDLE(handle)); 1149 1.1 christos REQUIRE(VALID_NMSOCK(handle->sock)); 1150 1.1 christos REQUIRE(handle->sock->type == isc_nm_streamdnssocket); 1151 1.1 christos 1152 1.1 christos sock = handle->sock; 1153 1.1 christos if (sock->outerhandle != NULL) { 1154 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 1155 1.1 christos return isc_nm_verify_tls_peer_result_string(sock->outerhandle); 1156 1.1 christos } else if (sock->streamdns.tls_verify_error != NULL) { 1157 1.1 christos return sock->streamdns.tls_verify_error; 1158 1.1 christos } 1159 1.1 christos 1160 1.1 christos return NULL; 1161 1.1 christos } 1162 1.1 christos 1163 1.1 christos void 1164 1.1 christos isc__nm_streamdns_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) { 1165 1.1 christos REQUIRE(VALID_NMSOCK(listener)); 1166 1.1 christos REQUIRE(listener->type == isc_nm_streamdnslistener); 1167 1.1 christos 1168 1.1 christos if (listener->outer != NULL) { 1169 1.1 christos INSIST(VALID_NMSOCK(listener->outer)); 1170 1.1 christos isc_nmsocket_set_tlsctx(listener->outer, tlsctx); 1171 1.1 christos } 1172 1.1 christos } 1173 1.1 christos 1174 1.1 christos isc_result_t 1175 1.1 christos isc__nm_streamdns_xfr_checkperm(isc_nmsocket_t *sock) { 1176 1.1 christos isc_result_t result = ISC_R_NOPERM; 1177 1.1 christos 1178 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 1179 1.1 christos REQUIRE(sock->type == isc_nm_streamdnssocket); 1180 1.1 christos 1181 1.1 christos if (sock->outerhandle != NULL) { 1182 1.1 christos if (isc_nm_has_encryption(sock->outerhandle) && 1183 1.1 christos !sock->streamdns.dot_alpn_negotiated) 1184 1.1 christos { 1185 1.1 christos result = ISC_R_DOTALPNERROR; 1186 1.1 christos } else { 1187 1.1 christos result = ISC_R_SUCCESS; 1188 1.1 christos } 1189 1.1 christos } 1190 1.1 christos 1191 1.1 christos return result; 1192 1.1 christos } 1193 1.1 christos 1194 1.1 christos void 1195 1.1 christos isc__nmsocket_streamdns_reset(isc_nmsocket_t *sock) { 1196 1.1 christos REQUIRE(VALID_NMSOCK(sock)); 1197 1.1 christos REQUIRE(sock->type == isc_nm_streamdnssocket); 1198 1.1 christos 1199 1.1 christos if (sock->outerhandle == NULL) { 1200 1.1 christos return; 1201 1.1 christos } 1202 1.1 christos 1203 1.1 christos INSIST(VALID_NMHANDLE(sock->outerhandle)); 1204 1.1 christos isc__nmsocket_reset(sock->outerhandle->sock); 1205 1.1 christos } 1206