Coverage Report

Created: 2023-06-07 06:23

/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
}