/src/bind9/lib/dns/transport.c
Line | Count | Source (jump to first uncovered line) |
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 <inttypes.h> |
15 | | |
16 | | #include <isc/list.h> |
17 | | #include <isc/mem.h> |
18 | | #include <isc/netaddr.h> |
19 | | #include <isc/refcount.h> |
20 | | #include <isc/result.h> |
21 | | #include <isc/rwlock.h> |
22 | | #include <isc/sockaddr.h> |
23 | | #include <isc/util.h> |
24 | | |
25 | | #include <dns/name.h> |
26 | | #include <dns/rbt.h> |
27 | | #include <dns/transport.h> |
28 | | |
29 | 0 | #define TRANSPORT_MAGIC ISC_MAGIC('T', 'r', 'n', 's') |
30 | | #define VALID_TRANSPORT(ptr) ISC_MAGIC_VALID(ptr, TRANSPORT_MAGIC) |
31 | | |
32 | 0 | #define TRANSPORT_LIST_MAGIC ISC_MAGIC('T', 'r', 'L', 's') |
33 | | #define VALID_TRANSPORT_LIST(ptr) ISC_MAGIC_VALID(ptr, TRANSPORT_LIST_MAGIC) |
34 | | |
35 | | struct dns_transport_list { |
36 | | unsigned int magic; |
37 | | isc_refcount_t references; |
38 | | isc_mem_t *mctx; |
39 | | isc_rwlock_t lock; |
40 | | dns_rbt_t *transports[DNS_TRANSPORT_COUNT]; |
41 | | }; |
42 | | |
43 | | typedef enum ternary { ter_none = 0, ter_true = 1, ter_false = 2 } ternary_t; |
44 | | |
45 | | struct dns_transport { |
46 | | unsigned int magic; |
47 | | isc_refcount_t references; |
48 | | isc_mem_t *mctx; |
49 | | dns_transport_type_t type; |
50 | | struct { |
51 | | char *tlsname; |
52 | | char *certfile; |
53 | | char *keyfile; |
54 | | char *cafile; |
55 | | char *remote_hostname; |
56 | | char *ciphers; |
57 | | uint32_t protocol_versions; |
58 | | ternary_t prefer_server_ciphers; |
59 | | bool always_verify_remote; |
60 | | } tls; |
61 | | struct { |
62 | | char *endpoint; |
63 | | dns_http_mode_t mode; |
64 | | } doh; |
65 | | }; |
66 | | |
67 | | static void |
68 | 0 | free_dns_transport(void *node, void *arg) { |
69 | 0 | dns_transport_t *transport = node; |
70 | |
|
71 | 0 | REQUIRE(node != NULL); |
72 | |
|
73 | 0 | UNUSED(arg); |
74 | |
|
75 | 0 | dns_transport_detach(&transport); |
76 | 0 | } |
77 | | |
78 | | static isc_result_t |
79 | | list_add(dns_transport_list_t *list, const dns_name_t *name, |
80 | 0 | const dns_transport_type_t type, dns_transport_t *transport) { |
81 | 0 | isc_result_t result; |
82 | 0 | dns_rbt_t *rbt = NULL; |
83 | |
|
84 | 0 | RWLOCK(&list->lock, isc_rwlocktype_write); |
85 | 0 | rbt = list->transports[type]; |
86 | 0 | INSIST(rbt != NULL); |
87 | |
|
88 | 0 | result = dns_rbt_addname(rbt, name, transport); |
89 | |
|
90 | 0 | RWUNLOCK(&list->lock, isc_rwlocktype_write); |
91 | |
|
92 | 0 | return (result); |
93 | 0 | } |
94 | | |
95 | | dns_transport_type_t |
96 | 0 | dns_transport_get_type(dns_transport_t *transport) { |
97 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
98 | |
|
99 | 0 | return (transport->type); |
100 | 0 | } |
101 | | |
102 | | char * |
103 | 0 | dns_transport_get_certfile(dns_transport_t *transport) { |
104 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
105 | |
|
106 | 0 | return (transport->tls.certfile); |
107 | 0 | } |
108 | | |
109 | | char * |
110 | 0 | dns_transport_get_keyfile(dns_transport_t *transport) { |
111 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
112 | |
|
113 | 0 | return (transport->tls.keyfile); |
114 | 0 | } |
115 | | |
116 | | char * |
117 | 0 | dns_transport_get_cafile(dns_transport_t *transport) { |
118 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
119 | |
|
120 | 0 | return (transport->tls.cafile); |
121 | 0 | } |
122 | | |
123 | | char * |
124 | 0 | dns_transport_get_remote_hostname(dns_transport_t *transport) { |
125 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
126 | |
|
127 | 0 | return (transport->tls.remote_hostname); |
128 | 0 | } |
129 | | |
130 | | char * |
131 | 0 | dns_transport_get_endpoint(dns_transport_t *transport) { |
132 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
133 | |
|
134 | 0 | return (transport->doh.endpoint); |
135 | 0 | } |
136 | | |
137 | | dns_http_mode_t |
138 | 0 | dns_transport_get_mode(dns_transport_t *transport) { |
139 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
140 | |
|
141 | 0 | return (transport->doh.mode); |
142 | 0 | } |
143 | | |
144 | | dns_transport_t * |
145 | | dns_transport_new(const dns_name_t *name, dns_transport_type_t type, |
146 | 0 | dns_transport_list_t *list) { |
147 | 0 | dns_transport_t *transport = isc_mem_get(list->mctx, |
148 | 0 | sizeof(*transport)); |
149 | 0 | *transport = (dns_transport_t){ .type = type }; |
150 | 0 | isc_refcount_init(&transport->references, 1); |
151 | 0 | isc_mem_attach(list->mctx, &transport->mctx); |
152 | 0 | transport->magic = TRANSPORT_MAGIC; |
153 | |
|
154 | 0 | list_add(list, name, type, transport); |
155 | |
|
156 | 0 | return (transport); |
157 | 0 | } |
158 | | |
159 | | void |
160 | 0 | dns_transport_set_certfile(dns_transport_t *transport, const char *certfile) { |
161 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
162 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
163 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
164 | |
|
165 | 0 | if (transport->tls.certfile != NULL) { |
166 | 0 | isc_mem_free(transport->mctx, transport->tls.certfile); |
167 | 0 | } |
168 | |
|
169 | 0 | if (certfile != NULL) { |
170 | 0 | transport->tls.certfile = isc_mem_strdup(transport->mctx, |
171 | 0 | certfile); |
172 | 0 | } |
173 | 0 | } |
174 | | |
175 | | void |
176 | 0 | dns_transport_set_keyfile(dns_transport_t *transport, const char *keyfile) { |
177 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
178 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
179 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
180 | |
|
181 | 0 | if (transport->tls.keyfile != NULL) { |
182 | 0 | isc_mem_free(transport->mctx, transport->tls.keyfile); |
183 | 0 | } |
184 | |
|
185 | 0 | if (keyfile != NULL) { |
186 | 0 | transport->tls.keyfile = isc_mem_strdup(transport->mctx, |
187 | 0 | keyfile); |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | void |
192 | 0 | dns_transport_set_cafile(dns_transport_t *transport, const char *cafile) { |
193 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
194 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
195 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
196 | |
|
197 | 0 | if (transport->tls.cafile != NULL) { |
198 | 0 | isc_mem_free(transport->mctx, transport->tls.cafile); |
199 | 0 | } |
200 | |
|
201 | 0 | if (cafile != NULL) { |
202 | 0 | transport->tls.cafile = isc_mem_strdup(transport->mctx, cafile); |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | void |
207 | | dns_transport_set_remote_hostname(dns_transport_t *transport, |
208 | 0 | const char *hostname) { |
209 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
210 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
211 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
212 | |
|
213 | 0 | if (transport->tls.remote_hostname != NULL) { |
214 | 0 | isc_mem_free(transport->mctx, transport->tls.remote_hostname); |
215 | 0 | } |
216 | |
|
217 | 0 | if (hostname != NULL) { |
218 | 0 | transport->tls.remote_hostname = isc_mem_strdup(transport->mctx, |
219 | 0 | hostname); |
220 | 0 | } |
221 | 0 | } |
222 | | |
223 | | void |
224 | 0 | dns_transport_set_endpoint(dns_transport_t *transport, const char *endpoint) { |
225 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
226 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_HTTP); |
227 | |
|
228 | 0 | if (transport->doh.endpoint != NULL) { |
229 | 0 | isc_mem_free(transport->mctx, transport->doh.endpoint); |
230 | 0 | } |
231 | |
|
232 | 0 | if (endpoint != NULL) { |
233 | 0 | transport->doh.endpoint = isc_mem_strdup(transport->mctx, |
234 | 0 | endpoint); |
235 | 0 | } |
236 | 0 | } |
237 | | |
238 | | void |
239 | 0 | dns_transport_set_mode(dns_transport_t *transport, dns_http_mode_t mode) { |
240 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
241 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_HTTP); |
242 | |
|
243 | 0 | transport->doh.mode = mode; |
244 | 0 | } |
245 | | |
246 | | void |
247 | | dns_transport_set_tls_versions(dns_transport_t *transport, |
248 | 0 | const uint32_t tls_versions) { |
249 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
250 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_HTTP || |
251 | 0 | transport->type == DNS_TRANSPORT_TLS); |
252 | |
|
253 | 0 | transport->tls.protocol_versions = tls_versions; |
254 | 0 | } |
255 | | |
256 | | uint32_t |
257 | 0 | dns_transport_get_tls_versions(const dns_transport_t *transport) { |
258 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
259 | |
|
260 | 0 | return (transport->tls.protocol_versions); |
261 | 0 | } |
262 | | |
263 | | void |
264 | 0 | dns_transport_set_ciphers(dns_transport_t *transport, const char *ciphers) { |
265 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
266 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
267 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
268 | |
|
269 | 0 | if (transport->tls.ciphers != NULL) { |
270 | 0 | isc_mem_free(transport->mctx, transport->tls.ciphers); |
271 | 0 | } |
272 | |
|
273 | 0 | if (ciphers != NULL) { |
274 | 0 | transport->tls.ciphers = isc_mem_strdup(transport->mctx, |
275 | 0 | ciphers); |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | void |
280 | 0 | dns_transport_set_tlsname(dns_transport_t *transport, const char *tlsname) { |
281 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
282 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
283 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
284 | |
|
285 | 0 | if (transport->tls.tlsname != NULL) { |
286 | 0 | isc_mem_free(transport->mctx, transport->tls.tlsname); |
287 | 0 | } |
288 | |
|
289 | 0 | if (tlsname != NULL) { |
290 | 0 | transport->tls.tlsname = isc_mem_strdup(transport->mctx, |
291 | 0 | tlsname); |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | | char * |
296 | 0 | dns_transport_get_ciphers(dns_transport_t *transport) { |
297 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
298 | |
|
299 | 0 | return (transport->tls.ciphers); |
300 | 0 | } |
301 | | |
302 | | char * |
303 | 0 | dns_transport_get_tlsname(dns_transport_t *transport) { |
304 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
305 | |
|
306 | 0 | return (transport->tls.tlsname); |
307 | 0 | } |
308 | | |
309 | | void |
310 | | dns_transport_set_prefer_server_ciphers(dns_transport_t *transport, |
311 | 0 | const bool prefer) { |
312 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
313 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
314 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
315 | |
|
316 | 0 | transport->tls.prefer_server_ciphers = prefer ? ter_true : ter_false; |
317 | 0 | } |
318 | | |
319 | | bool |
320 | | dns_transport_get_prefer_server_ciphers(const dns_transport_t *transport, |
321 | 0 | bool *preferp) { |
322 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
323 | 0 | REQUIRE(preferp != NULL); |
324 | 0 | if (transport->tls.prefer_server_ciphers == ter_none) { |
325 | 0 | return (false); |
326 | 0 | } else if (transport->tls.prefer_server_ciphers == ter_true) { |
327 | 0 | *preferp = true; |
328 | 0 | return (true); |
329 | 0 | } else if (transport->tls.prefer_server_ciphers == ter_false) { |
330 | 0 | *preferp = false; |
331 | 0 | return (true); |
332 | 0 | } |
333 | | |
334 | 0 | UNREACHABLE(); |
335 | 0 | return false; |
336 | 0 | } |
337 | | |
338 | | void |
339 | | dns_transport_set_always_verify_remote(dns_transport_t *transport, |
340 | 0 | const bool always_verify_remote) { |
341 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
342 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
343 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
344 | |
|
345 | 0 | transport->tls.always_verify_remote = always_verify_remote; |
346 | 0 | } |
347 | | |
348 | | bool |
349 | 0 | dns_transport_get_always_verify_remote(dns_transport_t *transport) { |
350 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
351 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
352 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
353 | |
|
354 | 0 | return transport->tls.always_verify_remote; |
355 | 0 | } |
356 | | |
357 | | isc_result_t |
358 | | dns_transport_get_tlsctx(dns_transport_t *transport, const isc_sockaddr_t *peer, |
359 | | isc_tlsctx_cache_t *tlsctx_cache, isc_mem_t *mctx, |
360 | | isc_tlsctx_t **pctx, |
361 | 0 | isc_tlsctx_client_session_cache_t **psess_cache) { |
362 | 0 | isc_result_t result = ISC_R_FAILURE; |
363 | 0 | isc_tlsctx_t *tlsctx = NULL, *found = NULL; |
364 | 0 | isc_tls_cert_store_t *store = NULL, *found_store = NULL; |
365 | 0 | isc_tlsctx_client_session_cache_t *sess_cache = NULL; |
366 | 0 | isc_tlsctx_client_session_cache_t *found_sess_cache = NULL; |
367 | 0 | uint32_t tls_versions; |
368 | 0 | const char *ciphers = NULL; |
369 | 0 | bool prefer_server_ciphers; |
370 | 0 | uint16_t family; |
371 | 0 | const char *tlsname = NULL; |
372 | |
|
373 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
374 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS); |
375 | 0 | REQUIRE(peer != NULL); |
376 | 0 | REQUIRE(tlsctx_cache != NULL); |
377 | 0 | REQUIRE(mctx != NULL); |
378 | 0 | REQUIRE(pctx != NULL && *pctx == NULL); |
379 | 0 | REQUIRE(psess_cache != NULL && *psess_cache == NULL); |
380 | |
|
381 | 0 | family = (isc_sockaddr_pf(peer) == PF_INET6) ? AF_INET6 : AF_INET; |
382 | |
|
383 | 0 | tlsname = dns_transport_get_tlsname(transport); |
384 | 0 | INSIST(tlsname != NULL && *tlsname != '\0'); |
385 | | |
386 | | /* |
387 | | * Let's try to re-use the already created context. This way |
388 | | * we have a chance to resume the TLS session, bypassing the |
389 | | * full TLS handshake procedure, making establishing |
390 | | * subsequent TLS connections faster. |
391 | | */ |
392 | 0 | result = isc_tlsctx_cache_find(tlsctx_cache, tlsname, |
393 | 0 | isc_tlsctx_cache_tls, family, &found, |
394 | 0 | &found_store, &found_sess_cache); |
395 | 0 | if (result != ISC_R_SUCCESS) { |
396 | 0 | const char *hostname = |
397 | 0 | dns_transport_get_remote_hostname(transport); |
398 | 0 | const char *ca_file = dns_transport_get_cafile(transport); |
399 | 0 | const char *cert_file = dns_transport_get_certfile(transport); |
400 | 0 | const char *key_file = dns_transport_get_keyfile(transport); |
401 | 0 | const bool always_verify_remote = |
402 | 0 | dns_transport_get_always_verify_remote(transport); |
403 | 0 | char peer_addr_str[INET6_ADDRSTRLEN] = { 0 }; |
404 | 0 | isc_netaddr_t peer_netaddr = { 0 }; |
405 | 0 | bool hostname_ignore_subject; |
406 | | |
407 | | /* |
408 | | * So, no context exists. Let's create one using the |
409 | | * parameters from the configuration file and try to |
410 | | * store it for further reuse. |
411 | | */ |
412 | 0 | result = isc_tlsctx_createclient(&tlsctx); |
413 | 0 | if (result != ISC_R_SUCCESS) { |
414 | 0 | goto failure; |
415 | 0 | } |
416 | 0 | tls_versions = dns_transport_get_tls_versions(transport); |
417 | 0 | if (tls_versions != 0) { |
418 | 0 | isc_tlsctx_set_protocols(tlsctx, tls_versions); |
419 | 0 | } |
420 | 0 | ciphers = dns_transport_get_ciphers(transport); |
421 | 0 | if (ciphers != NULL) { |
422 | 0 | isc_tlsctx_set_cipherlist(tlsctx, ciphers); |
423 | 0 | } |
424 | |
|
425 | 0 | if (dns_transport_get_prefer_server_ciphers( |
426 | 0 | transport, &prefer_server_ciphers)) |
427 | 0 | { |
428 | 0 | isc_tlsctx_prefer_server_ciphers(tlsctx, |
429 | 0 | prefer_server_ciphers); |
430 | 0 | } |
431 | |
|
432 | 0 | if (always_verify_remote || hostname != NULL || ca_file != NULL) |
433 | 0 | { |
434 | | /* |
435 | | * The situation when 'found_store != NULL' while |
436 | | * 'found == NULL' may occur as there is a one-to-many |
437 | | * relation between cert stores and per-transport TLS |
438 | | * contexts. That is, there could be one store |
439 | | * shared between multiple contexts. |
440 | | */ |
441 | 0 | if (found_store == NULL) { |
442 | | /* |
443 | | * 'ca_file' can equal 'NULL' here, in |
444 | | * which case the store with system-wide |
445 | | * CA certificates will be created. |
446 | | */ |
447 | 0 | result = isc_tls_cert_store_create(ca_file, |
448 | 0 | &store); |
449 | |
|
450 | 0 | if (result != ISC_R_SUCCESS) { |
451 | 0 | goto failure; |
452 | 0 | } |
453 | 0 | } else { |
454 | 0 | store = found_store; |
455 | 0 | } |
456 | | |
457 | 0 | INSIST(store != NULL); |
458 | 0 | if (hostname == NULL) { |
459 | | /* |
460 | | * If hostname is not specified, then use the |
461 | | * peer IP address for validation. |
462 | | */ |
463 | 0 | isc_netaddr_fromsockaddr(&peer_netaddr, peer); |
464 | 0 | isc_netaddr_format(&peer_netaddr, peer_addr_str, |
465 | 0 | sizeof(peer_addr_str)); |
466 | 0 | hostname = peer_addr_str; |
467 | 0 | } |
468 | | |
469 | | /* |
470 | | * According to RFC 8310, Subject field MUST NOT |
471 | | * be inspected when verifying hostname for DoT. |
472 | | * Only SubjectAltName must be checked. |
473 | | */ |
474 | 0 | hostname_ignore_subject = true; |
475 | 0 | result = isc_tlsctx_enable_peer_verification( |
476 | 0 | tlsctx, false, store, hostname, |
477 | 0 | hostname_ignore_subject); |
478 | 0 | if (result != ISC_R_SUCCESS) { |
479 | 0 | goto failure; |
480 | 0 | } |
481 | | |
482 | | /* |
483 | | * Let's load client certificate and enable |
484 | | * Mutual TLS. We do that only in the case when |
485 | | * Strict TLS is enabled, because Mutual TLS is |
486 | | * an extension of it. |
487 | | */ |
488 | 0 | if (cert_file != NULL) { |
489 | 0 | INSIST(key_file != NULL); |
490 | |
|
491 | 0 | result = isc_tlsctx_load_certificate( |
492 | 0 | tlsctx, key_file, cert_file); |
493 | 0 | if (result != ISC_R_SUCCESS) { |
494 | 0 | goto failure; |
495 | 0 | } |
496 | 0 | } |
497 | 0 | } |
498 | | |
499 | 0 | isc_tlsctx_enable_dot_client_alpn(tlsctx); |
500 | |
|
501 | 0 | isc_tlsctx_client_session_cache_create( |
502 | 0 | mctx, tlsctx, |
503 | 0 | ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE, |
504 | 0 | &sess_cache); |
505 | |
|
506 | 0 | found_store = NULL; |
507 | 0 | result = isc_tlsctx_cache_add(tlsctx_cache, tlsname, |
508 | 0 | isc_tlsctx_cache_tls, family, |
509 | 0 | tlsctx, store, sess_cache, &found, |
510 | 0 | &found_store, &found_sess_cache); |
511 | 0 | if (result == ISC_R_EXISTS) { |
512 | | /* |
513 | | * It seems the entry has just been created from |
514 | | * within another thread while we were initialising |
515 | | * ours. Although this is unlikely, it could happen |
516 | | * after startup/re-initialisation. In such a case, |
517 | | * discard the new context and associated data and use |
518 | | * the already established one from now on. |
519 | | * |
520 | | * Such situation will not occur after the |
521 | | * initial 'warm-up', so it is not critical |
522 | | * performance-wise. |
523 | | */ |
524 | 0 | INSIST(found != NULL); |
525 | 0 | isc_tlsctx_free(&tlsctx); |
526 | 0 | isc_tls_cert_store_free(&store); |
527 | 0 | isc_tlsctx_client_session_cache_detach(&sess_cache); |
528 | | /* Let's return the data from the cache. */ |
529 | 0 | *psess_cache = found_sess_cache; |
530 | 0 | *pctx = found; |
531 | 0 | } else { |
532 | | /* |
533 | | * Adding the fresh values into the cache has been |
534 | | * successful, let's return them |
535 | | */ |
536 | 0 | INSIST(result == ISC_R_SUCCESS); |
537 | 0 | *psess_cache = sess_cache; |
538 | 0 | *pctx = tlsctx; |
539 | 0 | } |
540 | 0 | } else { |
541 | | /* |
542 | | * The cache lookup has been successful, let's return the |
543 | | * results. |
544 | | */ |
545 | 0 | INSIST(result == ISC_R_SUCCESS); |
546 | 0 | *psess_cache = found_sess_cache; |
547 | 0 | *pctx = found; |
548 | 0 | } |
549 | | |
550 | 0 | return (ISC_R_SUCCESS); |
551 | | |
552 | 0 | failure: |
553 | 0 | if (tlsctx != NULL) { |
554 | 0 | isc_tlsctx_free(&tlsctx); |
555 | 0 | } |
556 | | |
557 | | /* |
558 | | * The 'found_store' is being managed by the TLS context |
559 | | * cache. Thus, we should keep it as it is, as it will get |
560 | | * destroyed alongside the cache. As there is one store per |
561 | | * multiple TLS contexts, we need to handle store deletion in a |
562 | | * special way. |
563 | | */ |
564 | 0 | if (store != NULL && store != found_store) { |
565 | 0 | isc_tls_cert_store_free(&store); |
566 | 0 | } |
567 | |
|
568 | 0 | return (result); |
569 | 0 | } |
570 | | |
571 | | static void |
572 | 0 | transport_destroy(dns_transport_t *transport) { |
573 | 0 | isc_refcount_destroy(&transport->references); |
574 | 0 | transport->magic = 0; |
575 | |
|
576 | 0 | if (transport->doh.endpoint != NULL) { |
577 | 0 | isc_mem_free(transport->mctx, transport->doh.endpoint); |
578 | 0 | } |
579 | 0 | if (transport->tls.remote_hostname != NULL) { |
580 | 0 | isc_mem_free(transport->mctx, transport->tls.remote_hostname); |
581 | 0 | } |
582 | 0 | if (transport->tls.cafile != NULL) { |
583 | 0 | isc_mem_free(transport->mctx, transport->tls.cafile); |
584 | 0 | } |
585 | 0 | if (transport->tls.keyfile != NULL) { |
586 | 0 | isc_mem_free(transport->mctx, transport->tls.keyfile); |
587 | 0 | } |
588 | 0 | if (transport->tls.certfile != NULL) { |
589 | 0 | isc_mem_free(transport->mctx, transport->tls.certfile); |
590 | 0 | } |
591 | 0 | if (transport->tls.ciphers != NULL) { |
592 | 0 | isc_mem_free(transport->mctx, transport->tls.ciphers); |
593 | 0 | } |
594 | |
|
595 | 0 | if (transport->tls.tlsname != NULL) { |
596 | 0 | isc_mem_free(transport->mctx, transport->tls.tlsname); |
597 | 0 | } |
598 | |
|
599 | 0 | isc_mem_putanddetach(&transport->mctx, transport, sizeof(*transport)); |
600 | 0 | } |
601 | | |
602 | | void |
603 | 0 | dns_transport_attach(dns_transport_t *source, dns_transport_t **targetp) { |
604 | 0 | REQUIRE(source != NULL); |
605 | 0 | REQUIRE(targetp != NULL && *targetp == NULL); |
606 | |
|
607 | 0 | isc_refcount_increment(&source->references); |
608 | |
|
609 | 0 | *targetp = source; |
610 | 0 | } |
611 | | |
612 | | void |
613 | 0 | dns_transport_detach(dns_transport_t **transportp) { |
614 | 0 | dns_transport_t *transport = NULL; |
615 | |
|
616 | 0 | REQUIRE(transportp != NULL); |
617 | 0 | REQUIRE(VALID_TRANSPORT(*transportp)); |
618 | |
|
619 | 0 | transport = *transportp; |
620 | 0 | *transportp = NULL; |
621 | |
|
622 | 0 | if (isc_refcount_decrement(&transport->references) == 1) { |
623 | 0 | transport_destroy(transport); |
624 | 0 | } |
625 | 0 | } |
626 | | |
627 | | dns_transport_t * |
628 | | dns_transport_find(const dns_transport_type_t type, const dns_name_t *name, |
629 | 0 | dns_transport_list_t *list) { |
630 | 0 | isc_result_t result; |
631 | 0 | dns_transport_t *transport = NULL; |
632 | 0 | dns_rbt_t *rbt = NULL; |
633 | |
|
634 | 0 | REQUIRE(VALID_TRANSPORT_LIST(list)); |
635 | 0 | REQUIRE(list->transports[type] != NULL); |
636 | |
|
637 | 0 | rbt = list->transports[type]; |
638 | |
|
639 | 0 | RWLOCK(&list->lock, isc_rwlocktype_read); |
640 | 0 | result = dns_rbt_findname(rbt, name, 0, NULL, (void *)&transport); |
641 | 0 | if (result == ISC_R_SUCCESS) { |
642 | 0 | isc_refcount_increment(&transport->references); |
643 | 0 | } |
644 | 0 | RWUNLOCK(&list->lock, isc_rwlocktype_read); |
645 | |
|
646 | 0 | return (transport); |
647 | 0 | } |
648 | | |
649 | | dns_transport_list_t * |
650 | 0 | dns_transport_list_new(isc_mem_t *mctx) { |
651 | 0 | dns_transport_list_t *list = isc_mem_get(mctx, sizeof(*list)); |
652 | |
|
653 | 0 | *list = (dns_transport_list_t){ 0 }; |
654 | |
|
655 | 0 | isc_rwlock_init(&list->lock); |
656 | |
|
657 | 0 | isc_mem_attach(mctx, &list->mctx); |
658 | 0 | isc_refcount_init(&list->references, 1); |
659 | |
|
660 | 0 | list->magic = TRANSPORT_LIST_MAGIC; |
661 | |
|
662 | 0 | for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) { |
663 | 0 | isc_result_t result; |
664 | 0 | result = dns_rbt_create(list->mctx, free_dns_transport, NULL, |
665 | 0 | &list->transports[type]); |
666 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
667 | 0 | } |
668 | |
|
669 | 0 | return (list); |
670 | 0 | } |
671 | | |
672 | | void |
673 | | dns_transport_list_attach(dns_transport_list_t *source, |
674 | 0 | dns_transport_list_t **targetp) { |
675 | 0 | REQUIRE(VALID_TRANSPORT_LIST(source)); |
676 | 0 | REQUIRE(targetp != NULL && *targetp == NULL); |
677 | |
|
678 | 0 | isc_refcount_increment(&source->references); |
679 | |
|
680 | 0 | *targetp = source; |
681 | 0 | } |
682 | | |
683 | | static void |
684 | 0 | transport_list_destroy(dns_transport_list_t *list) { |
685 | 0 | isc_refcount_destroy(&list->references); |
686 | 0 | list->magic = 0; |
687 | |
|
688 | 0 | for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) { |
689 | 0 | if (list->transports[type] != NULL) { |
690 | 0 | dns_rbt_destroy(&list->transports[type]); |
691 | 0 | } |
692 | 0 | } |
693 | 0 | isc_rwlock_destroy(&list->lock); |
694 | 0 | isc_mem_putanddetach(&list->mctx, list, sizeof(*list)); |
695 | 0 | } |
696 | | |
697 | | void |
698 | 0 | dns_transport_list_detach(dns_transport_list_t **listp) { |
699 | 0 | dns_transport_list_t *list = NULL; |
700 | |
|
701 | 0 | REQUIRE(listp != NULL); |
702 | 0 | REQUIRE(VALID_TRANSPORT_LIST(*listp)); |
703 | |
|
704 | 0 | list = *listp; |
705 | 0 | *listp = NULL; |
706 | |
|
707 | 0 | if (isc_refcount_decrement(&list->references) == 1) { |
708 | 0 | transport_list_destroy(list); |
709 | 0 | } |
710 | 0 | } |