Coverage Report

Created: 2026-03-07 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/nta.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
/*! \file */
15
16
#include <inttypes.h>
17
#include <stdbool.h>
18
19
#include <isc/async.h>
20
#include <isc/buffer.h>
21
#include <isc/log.h>
22
#include <isc/loop.h>
23
#include <isc/mem.h>
24
#include <isc/result.h>
25
#include <isc/rwlock.h>
26
#include <isc/string.h>
27
#include <isc/time.h>
28
#include <isc/timer.h>
29
#include <isc/util.h>
30
31
#include <dns/db.h>
32
#include <dns/fixedname.h>
33
#include <dns/name.h>
34
#include <dns/nta.h>
35
#include <dns/qp.h>
36
#include <dns/rdataset.h>
37
#include <dns/resolver.h>
38
#include <dns/time.h>
39
40
struct dns_ntatable {
41
  unsigned int magic;
42
  isc_mem_t *mctx;
43
  dns_view_t *view;
44
  isc_rwlock_t rwlock;
45
  isc_refcount_t references;
46
  dns_qpmulti_t *table;
47
  atomic_bool shuttingdown;
48
};
49
50
struct dns__nta {
51
  unsigned int magic;
52
  isc_mem_t *mctx;
53
  isc_loop_t *loop;
54
  isc_refcount_t references;
55
  dns_ntatable_t *ntatable;
56
  bool forced;
57
  isc_timer_t *timer;
58
  dns_fetch_t *fetch;
59
  dns_rdataset_t rdataset;
60
  dns_rdataset_t sigrdataset;
61
  dns_name_t name;
62
  isc_stdtime_t expiry;
63
  bool shuttingdown;
64
};
65
66
0
#define NTA_MAGIC     ISC_MAGIC('N', 'T', 'A', 'n')
67
#define VALID_NTA(nn) ISC_MAGIC_VALID(nn, NTA_MAGIC)
68
69
static void
70
dns__nta_shutdown(dns__nta_t *nta);
71
72
static void
73
qp_attach(void *uctx, void *pval, uint32_t ival);
74
static void
75
qp_detach(void *uctx, void *pval, uint32_t ival);
76
static size_t
77
qp_makekey(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival);
78
static void
79
qp_triename(void *uctx, char *buf, size_t size);
80
81
static dns_qpmethods_t qpmethods = {
82
  qp_attach,
83
  qp_detach,
84
  qp_makekey,
85
  qp_triename,
86
};
87
88
static void
89
0
dns__nta_destroy(dns__nta_t *nta) {
90
0
  REQUIRE(nta->timer == NULL);
91
92
0
  nta->magic = 0;
93
0
  dns_rdataset_cleanup(&nta->rdataset);
94
0
  dns_rdataset_cleanup(&nta->sigrdataset);
95
0
  if (nta->fetch != NULL) {
96
0
    dns_resolver_cancelfetch(nta->fetch);
97
0
    dns_resolver_destroyfetch(&nta->fetch);
98
0
  }
99
0
  isc_loop_detach(&nta->loop);
100
0
  dns_name_free(&nta->name, nta->mctx);
101
0
  isc_mem_putanddetach(&nta->mctx, nta, sizeof(*nta));
102
0
}
103
104
#if DNS_NTA_TRACE
105
ISC_REFCOUNT_TRACE_IMPL(dns__nta, dns__nta_destroy);
106
#else
107
0
ISC_REFCOUNT_IMPL(dns__nta, dns__nta_destroy);
Unexecuted instantiation: dns__nta_ref
Unexecuted instantiation: dns__nta_unref
Unexecuted instantiation: dns__nta_detach
108
0
#endif
109
0
110
0
void
111
0
dns_ntatable_create(dns_view_t *view, dns_ntatable_t **ntatablep) {
112
0
  dns_ntatable_t *ntatable = NULL;
113
114
0
  REQUIRE(ntatablep != NULL && *ntatablep == NULL);
115
116
0
  ntatable = isc_mem_get(view->mctx, sizeof(*ntatable));
117
0
  *ntatable = (dns_ntatable_t){
118
0
    .mctx = isc_mem_ref(view->mctx),
119
0
  };
120
121
0
  dns_view_weakattach(view, &ntatable->view);
122
123
0
  isc_rwlock_init(&ntatable->rwlock);
124
0
  dns_qpmulti_create(view->mctx, &qpmethods, view, &ntatable->table);
125
126
0
  isc_refcount_init(&ntatable->references, 1);
127
128
0
  ntatable->magic = NTATABLE_MAGIC;
129
0
  *ntatablep = ntatable;
130
0
}
131
132
static void
133
0
dns__ntatable_destroy(dns_ntatable_t *ntatable) {
134
0
  ntatable->magic = 0;
135
0
  isc_rwlock_destroy(&ntatable->rwlock);
136
0
  dns_qpmulti_destroy(&ntatable->table);
137
0
  INSIST(ntatable->view == NULL);
138
0
  isc_mem_putanddetach(&ntatable->mctx, ntatable, sizeof(*ntatable));
139
0
}
140
141
#if DNS_NTA_TRACE
142
ISC_REFCOUNT_TRACE_IMPL(dns_ntatable, dns__ntatable_destroy);
143
#else
144
0
ISC_REFCOUNT_IMPL(dns_ntatable, dns__ntatable_destroy);
Unexecuted instantiation: dns_ntatable_ref
Unexecuted instantiation: dns_ntatable_unref
Unexecuted instantiation: dns_ntatable_detach
145
0
#endif
146
0
147
0
static void
148
0
fetch_done(void *arg) {
149
0
  dns_fetchresponse_t *resp = (dns_fetchresponse_t *)arg;
150
0
  dns__nta_t *nta = resp->arg;
151
0
  isc_result_t eresult = resp->result;
152
0
  dns_ntatable_t *ntatable = nta->ntatable;
153
0
  dns_view_t *view = ntatable->view;
154
0
  isc_stdtime_t now = isc_stdtime_now();
155
156
0
  dns_rdataset_cleanup(&nta->rdataset);
157
0
  dns_rdataset_cleanup(&nta->sigrdataset);
158
0
  if (nta->fetch == resp->fetch) {
159
0
    nta->fetch = NULL;
160
0
  }
161
0
  dns_resolver_destroyfetch(&resp->fetch);
162
163
0
  if (resp->node != NULL) {
164
0
    dns_db_detachnode(&resp->node);
165
0
  }
166
0
  if (resp->cache != NULL) {
167
0
    dns_db_detach(&resp->cache);
168
0
  }
169
170
0
  dns_resolver_freefresp(&resp);
171
172
0
  switch (eresult) {
173
0
  case ISC_R_SUCCESS:
174
0
  case DNS_R_NCACHENXDOMAIN:
175
0
  case DNS_R_NXDOMAIN:
176
0
  case DNS_R_NCACHENXRRSET:
177
0
  case DNS_R_NXRRSET:
178
0
    RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
179
0
    if (nta->expiry > now) {
180
0
      nta->expiry = now;
181
0
    }
182
0
    RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
183
0
    break;
184
0
  default:
185
0
    break;
186
0
  }
187
188
  /*
189
   * If we're expiring before the next recheck, we might
190
   * as well stop the timer now.
191
   */
192
0
  RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
193
0
  if (nta->timer != NULL && nta->expiry - now < view->nta_recheck) {
194
0
    isc_timer_stop(nta->timer);
195
0
  }
196
0
  RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
197
198
0
  dns__nta_detach(&nta); /* for dns_resolver_createfetch() */
199
0
}
200
201
static void
202
0
checkbogus(void *arg) {
203
0
  dns__nta_t *nta = arg;
204
0
  dns_ntatable_t *ntatable = nta->ntatable;
205
0
  dns_resolver_t *resolver = NULL;
206
0
  isc_result_t result;
207
208
0
  if (nta->fetch != NULL) {
209
0
    dns_resolver_cancelfetch(nta->fetch);
210
0
    nta->fetch = NULL;
211
0
  }
212
0
  dns_rdataset_cleanup(&nta->rdataset);
213
0
  dns_rdataset_cleanup(&nta->sigrdataset);
214
215
0
  if (atomic_load(&ntatable->shuttingdown)) {
216
0
    isc_timer_stop(nta->timer);
217
0
    return;
218
0
  }
219
220
0
  result = dns_view_getresolver(ntatable->view, &resolver);
221
0
  if (result != ISC_R_SUCCESS) {
222
0
    return;
223
0
  }
224
225
0
  dns__nta_ref(nta); /* for dns_resolver_createfetch */
226
0
  result = dns_resolver_createfetch(
227
0
    resolver, &nta->name, dns_rdatatype_nsec, NULL, NULL, NULL,
228
0
    NULL, 0, DNS_FETCHOPT_NONTA, 0, NULL, NULL, NULL, nta->loop,
229
0
    fetch_done, nta, NULL, &nta->rdataset, &nta->sigrdataset,
230
0
    &nta->fetch);
231
0
  if (result != ISC_R_SUCCESS) {
232
0
    dns__nta_detach(&nta); /* for dns_resolver_createfetch() */
233
0
  }
234
0
  dns_resolver_detach(&resolver);
235
0
}
236
237
static void
238
0
settimer(dns_ntatable_t *ntatable, dns__nta_t *nta, uint32_t lifetime) {
239
0
  dns_view_t *view = NULL;
240
0
  isc_interval_t interval;
241
242
0
  REQUIRE(VALID_NTATABLE(ntatable));
243
0
  REQUIRE(VALID_NTA(nta));
244
245
0
  view = ntatable->view;
246
0
  if (view->nta_recheck == 0 || lifetime <= view->nta_recheck) {
247
0
    return;
248
0
  }
249
250
0
  isc_timer_create(nta->loop, checkbogus, nta, &nta->timer);
251
0
  isc_interval_set(&interval, view->nta_recheck, 0);
252
0
  isc_timer_start(nta->timer, isc_timertype_ticker, &interval);
253
0
}
254
255
static void
256
nta_create(dns_ntatable_t *ntatable, const dns_name_t *name,
257
0
     dns__nta_t **target) {
258
0
  dns__nta_t *nta = NULL;
259
260
0
  REQUIRE(VALID_NTATABLE(ntatable));
261
0
  REQUIRE(target != NULL && *target == NULL);
262
263
0
  nta = isc_mem_get(ntatable->mctx, sizeof(dns__nta_t));
264
0
  *nta = (dns__nta_t){
265
0
    .ntatable = ntatable,
266
0
    .name = DNS_NAME_INITEMPTY,
267
0
    .magic = NTA_MAGIC,
268
0
  };
269
0
  isc_mem_attach(ntatable->mctx, &nta->mctx);
270
0
  isc_loop_attach(isc_loop(), &nta->loop);
271
272
0
  dns_rdataset_init(&nta->rdataset);
273
0
  dns_rdataset_init(&nta->sigrdataset);
274
275
0
  isc_refcount_init(&nta->references, 1);
276
277
0
  dns_name_dup(name, nta->mctx, &nta->name);
278
279
0
  *target = nta;
280
0
}
281
282
isc_result_t
283
dns_ntatable_add(dns_ntatable_t *ntatable, const dns_name_t *name, bool force,
284
0
     isc_stdtime_t now, uint32_t lifetime) {
285
0
  isc_result_t result = ISC_R_SUCCESS;
286
0
  dns__nta_t *nta = NULL;
287
0
  dns_qp_t *qp = NULL;
288
0
  void *pval = NULL;
289
290
0
  REQUIRE(VALID_NTATABLE(ntatable));
291
292
0
  if (atomic_load(&ntatable->shuttingdown)) {
293
0
    return ISC_R_SUCCESS;
294
0
  }
295
296
0
  RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
297
0
  dns_qpmulti_write(ntatable->table, &qp);
298
0
  nta_create(ntatable, name, &nta);
299
0
  nta->forced = force;
300
301
0
  result = dns_qp_insert(qp, nta, 0);
302
0
  switch (result) {
303
0
  case ISC_R_EXISTS:
304
0
    result = dns_qp_getname(qp, &nta->name, DNS_DBNAMESPACE_NORMAL,
305
0
          &pval, NULL);
306
0
    if (result == ISC_R_SUCCESS) {
307
      /*
308
       * an NTA already existed: throw away the
309
       * new one and update the old one.
310
       */
311
0
      dns__nta_detach(&nta); /* for nta_create */
312
0
      nta = pval;
313
0
      break;
314
0
    }
315
    /* update the NTA's timer as if it were new */
316
0
    FALLTHROUGH;
317
0
  case ISC_R_SUCCESS:
318
0
    nta->expiry = now + lifetime;
319
0
    if (!force) {
320
0
      settimer(ntatable, nta, lifetime);
321
0
    }
322
0
    break;
323
0
  default:
324
0
    break;
325
0
  }
326
327
0
  dns_qp_compact(qp, DNS_QPGC_MAYBE);
328
0
  dns_qpmulti_commit(ntatable->table, &qp);
329
0
  RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
330
331
0
  return result;
332
0
}
333
334
isc_result_t
335
0
dns_ntatable_delete(dns_ntatable_t *ntatable, const dns_name_t *name) {
336
0
  isc_result_t result;
337
0
  dns_qp_t *qp = NULL;
338
0
  void *pval = NULL;
339
340
0
  REQUIRE(VALID_NTATABLE(ntatable));
341
0
  REQUIRE(name != NULL);
342
343
0
  dns_qpmulti_write(ntatable->table, &qp);
344
0
  result = dns_qp_deletename(qp, name, DNS_DBNAMESPACE_NORMAL, &pval,
345
0
           NULL);
346
0
  if (result == ISC_R_SUCCESS) {
347
0
    dns__nta_t *n = pval;
348
0
    dns__nta_shutdown(n);
349
0
    dns__nta_detach(&n);
350
0
  }
351
0
  dns_qp_compact(qp, DNS_QPGC_MAYBE);
352
0
  dns_qpmulti_commit(ntatable->table, &qp);
353
354
0
  return result;
355
0
}
356
357
static void
358
0
delete_expired(void *arg) {
359
0
  dns__nta_t *nta = arg;
360
0
  dns_ntatable_t *ntatable = nta->ntatable;
361
0
  isc_result_t result;
362
0
  dns_qp_t *qp = NULL;
363
0
  void *pval = NULL;
364
365
0
  REQUIRE(VALID_NTATABLE(ntatable));
366
367
0
  RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
368
0
  dns_qpmulti_write(ntatable->table, &qp);
369
0
  result = dns_qp_getname(qp, &nta->name, DNS_DBNAMESPACE_NORMAL, &pval,
370
0
        NULL);
371
0
  if (result == ISC_R_SUCCESS &&
372
0
      ((dns__nta_t *)pval)->expiry == nta->expiry && !nta->shuttingdown)
373
0
  {
374
0
    char nb[DNS_NAME_FORMATSIZE];
375
0
    dns_name_format(&nta->name, nb, sizeof(nb));
376
0
    isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_NTA,
377
0
            ISC_LOG_INFO, "deleting expired NTA at %s", nb);
378
0
    dns_qp_deletename(qp, &nta->name, DNS_DBNAMESPACE_NORMAL, NULL,
379
0
          NULL);
380
0
    dns__nta_shutdown(nta);
381
0
    dns__nta_unref(nta);
382
0
  }
383
0
  dns_qp_compact(qp, DNS_QPGC_MAYBE);
384
0
  dns_qpmulti_commit(ntatable->table, &qp);
385
0
  RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
386
0
  dns__nta_detach(&nta);
387
0
  dns_ntatable_detach(&ntatable);
388
0
}
389
390
bool
391
dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
392
0
         const dns_name_t *name, const dns_name_t *anchor) {
393
0
  isc_result_t result;
394
0
  dns__nta_t *nta = NULL;
395
0
  bool answer = false;
396
0
  dns_qpread_t qpr;
397
0
  void *pval = NULL;
398
399
0
  REQUIRE(VALID_NTATABLE(ntatable));
400
0
  REQUIRE(dns_name_isabsolute(name));
401
402
0
  RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
403
0
  dns_qpmulti_query(ntatable->table, &qpr);
404
0
  result = dns_qp_lookup(&qpr, name, DNS_DBNAMESPACE_NORMAL, NULL, NULL,
405
0
             &pval, NULL);
406
0
  nta = pval;
407
408
0
  switch (result) {
409
0
  case ISC_R_SUCCESS:
410
    /* Exact match */
411
0
    break;
412
0
  case DNS_R_PARTIALMATCH:
413
    /*
414
     * Found a NTA that's an ancestor of 'name'; we
415
     * now have to make sure 'anchor' isn't below it.
416
     */
417
0
    if (!dns_name_issubdomain(&nta->name, anchor)) {
418
0
      goto done;
419
0
    }
420
    /* Ancestor match */
421
0
    break;
422
0
  default:
423
    /* Found nothing */
424
0
    goto done;
425
0
  }
426
427
0
  if (nta->expiry <= now) {
428
    /* NTA is expired */
429
0
    dns__nta_ref(nta);
430
0
    dns_ntatable_ref(nta->ntatable);
431
0
    isc_async_current(delete_expired, nta);
432
0
    goto done;
433
0
  }
434
435
0
  answer = true;
436
0
done:
437
0
  RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
438
0
  dns_qpread_destroy(ntatable->table, &qpr);
439
0
  return answer;
440
0
}
441
442
static isc_result_t
443
0
putstr(isc_buffer_t *b, const char *str) {
444
0
  RETERR(isc_buffer_reserve(b, strlen(str)));
445
446
0
  isc_buffer_putstr(b, str);
447
0
  return ISC_R_SUCCESS;
448
0
}
449
450
isc_result_t
451
dns_ntatable_totext(dns_ntatable_t *ntatable, const char *view,
452
0
        isc_buffer_t *buf) {
453
0
  isc_result_t result = ISC_R_SUCCESS;
454
0
  isc_stdtime_t now = isc_stdtime_now();
455
0
  dns_qpread_t qpr;
456
0
  dns_qpiter_t iter;
457
0
  bool first = true;
458
0
  void *pval = NULL;
459
460
0
  REQUIRE(VALID_NTATABLE(ntatable));
461
462
0
  RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
463
0
  dns_qpmulti_query(ntatable->table, &qpr);
464
0
  dns_qpiter_init(&qpr, &iter);
465
466
0
  while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) {
467
0
    dns__nta_t *n = pval;
468
0
    char nbuf[DNS_NAME_FORMATSIZE];
469
0
    char tbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
470
0
    char obuf[DNS_NAME_FORMATSIZE + ISC_FORMATHTTPTIMESTAMP_SIZE +
471
0
        sizeof("expired:  \n")];
472
0
    isc_time_t t;
473
474
0
    dns_name_format(&n->name, nbuf, sizeof(nbuf));
475
476
0
    if (n->expiry != 0xffffffffU) {
477
      /* Normal NTA entries */
478
0
      isc_time_set(&t, n->expiry, 0);
479
0
      isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
480
481
0
      snprintf(obuf, sizeof(obuf), "%s%s%s%s: %s %s",
482
0
         first ? "" : "\n", nbuf,
483
0
         view != NULL ? "/" : "",
484
0
         view != NULL ? view : "",
485
0
         n->expiry <= now ? "expired" : "expiry", tbuf);
486
0
    } else {
487
      /* "validate-except" entries */
488
0
      snprintf(obuf, sizeof(obuf), "%s%s%s%s: %s",
489
0
         first ? "" : "\n", nbuf,
490
0
         view != NULL ? "/" : "",
491
0
         view != NULL ? view : "", "permanent");
492
0
    }
493
494
0
    first = false;
495
0
    CHECK(putstr(buf, obuf));
496
0
  }
497
498
0
cleanup:
499
0
  dns_qpread_destroy(ntatable->table, &qpr);
500
0
  RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
501
0
  return result;
502
0
}
503
504
isc_result_t
505
0
dns_ntatable_save(dns_ntatable_t *ntatable, FILE *fp) {
506
0
  isc_result_t result = ISC_R_SUCCESS;
507
0
  isc_stdtime_t now = isc_stdtime_now();
508
0
  dns_qpread_t qpr;
509
0
  dns_qpiter_t iter;
510
0
  bool written = false;
511
0
  void *pval = NULL;
512
513
0
  REQUIRE(VALID_NTATABLE(ntatable));
514
515
0
  RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
516
0
  dns_qpmulti_query(ntatable->table, &qpr);
517
0
  dns_qpiter_init(&qpr, &iter);
518
519
0
  while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) {
520
0
    dns__nta_t *n = pval;
521
0
    isc_buffer_t b;
522
0
    char nbuf[DNS_NAME_FORMATSIZE + 1], tbuf[80];
523
524
    /*
525
     * Skip this node if the expiry is already in the
526
     * past, or if this is a "validate-except" entry.
527
     */
528
0
    if (n->expiry <= now || n->expiry == 0xffffffffU) {
529
0
      continue;
530
0
    }
531
532
0
    isc_buffer_init(&b, nbuf, sizeof(nbuf));
533
0
    result = dns_name_totext(&n->name, 0, &b);
534
0
    if (result != ISC_R_SUCCESS) {
535
0
      continue;
536
0
    }
537
538
    /* Zero terminate */
539
0
    isc_buffer_putuint8(&b, 0);
540
541
0
    isc_buffer_init(&b, tbuf, sizeof(tbuf));
542
0
    dns_time32_totext(n->expiry, &b);
543
544
    /* Zero terminate */
545
0
    isc_buffer_putuint8(&b, 0);
546
547
0
    fprintf(fp, "%s %s %s\n", nbuf,
548
0
      n->forced ? "forced" : "regular", tbuf);
549
0
    written = true;
550
0
  }
551
552
0
  dns_qpread_destroy(ntatable->table, &qpr);
553
0
  RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
554
555
0
  if (result == ISC_R_SUCCESS && !written) {
556
0
    result = ISC_R_NOTFOUND;
557
0
  }
558
559
0
  return result;
560
0
}
561
562
static void
563
0
dns__nta_shutdown_cb(void *arg) {
564
0
  dns__nta_t *nta = arg;
565
566
0
  REQUIRE(VALID_NTA(nta));
567
568
0
  if (isc_log_wouldlog(ISC_LOG_DEBUG(3))) {
569
0
    char nb[DNS_NAME_FORMATSIZE];
570
0
    dns_name_format(&nta->name, nb, sizeof(nb));
571
0
    isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_NTA,
572
0
            ISC_LOG_DEBUG(3), "shutting down NTA %p at %s",
573
0
            nta, nb);
574
0
  }
575
0
  if (nta->timer) {
576
0
    isc_timer_stop(nta->timer);
577
0
    isc_timer_destroy(&nta->timer);
578
0
  }
579
580
0
  dns__nta_detach(&nta);
581
0
}
582
583
static void
584
0
dns__nta_shutdown(dns__nta_t *nta) {
585
0
  REQUIRE(VALID_NTA(nta));
586
587
0
  dns__nta_ref(nta);
588
0
  isc_async_run(nta->loop, dns__nta_shutdown_cb, nta);
589
0
  nta->shuttingdown = true;
590
0
}
591
592
void
593
0
dns_ntatable_shutdown(dns_ntatable_t *ntatable) {
594
0
  dns_qpread_t qpr;
595
0
  dns_qpiter_t iter;
596
0
  void *pval = NULL;
597
598
0
  REQUIRE(VALID_NTATABLE(ntatable));
599
600
0
  RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
601
0
  dns_qpmulti_query(ntatable->table, &qpr);
602
0
  ntatable->shuttingdown = true;
603
604
0
  dns_qpiter_init(&qpr, &iter);
605
0
  while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) {
606
0
    dns__nta_t *n = pval;
607
0
    dns__nta_shutdown(n);
608
0
    dns__nta_detach(&n);
609
0
  }
610
611
0
  dns_qpread_destroy(ntatable->table, &qpr);
612
0
  dns_view_weakdetach(&ntatable->view);
613
0
  RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
614
0
}
615
616
static void
617
qp_attach(void *uctx ISC_ATTR_UNUSED, void *pval,
618
0
    uint32_t ival ISC_ATTR_UNUSED) {
619
0
  dns__nta_t *nta = pval;
620
0
  dns__nta_ref(nta);
621
0
}
622
623
static void
624
qp_detach(void *uctx ISC_ATTR_UNUSED, void *pval,
625
0
    uint32_t ival ISC_ATTR_UNUSED) {
626
0
  dns__nta_t *nta = pval;
627
0
  dns__nta_detach(&nta);
628
0
}
629
630
static size_t
631
qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval,
632
0
     uint32_t ival ISC_ATTR_UNUSED) {
633
0
  dns__nta_t *nta = pval;
634
0
  return dns_qpkey_fromname(key, &nta->name, DNS_DBNAMESPACE_NORMAL);
635
0
}
636
637
static void
638
0
qp_triename(void *uctx, char *buf, size_t size) {
639
0
  dns_view_t *view = uctx;
640
0
  snprintf(buf, size, "view %s forwarder table", view->name);
641
0
}