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