streamdns.c revision 1.4 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