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