Coverage Report

Created: 2025-11-24 06:17

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