Coverage Report

Created: 2026-03-07 06:55

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