Coverage Report

Created: 2026-09-01 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/deleg.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
#include <isc/magic.h>
14
#include <isc/mem.h>
15
#include <isc/netaddr.h>
16
#include <isc/sieve.h>
17
#include <isc/stdtime.h>
18
#include <isc/urcu.h>
19
#include <isc/uv.h>
20
21
#include <dns/callbacks.h>
22
#include <dns/deleg.h>
23
#include <dns/name.h>
24
#include <dns/qp.h>
25
#include <dns/view.h>
26
27
#include "probes-dns.h"
28
29
0
#define DELEGDB_NODE_MAGIC   ISC_MAGIC('D', 'e', 'G', 'N')
30
#define VALID_DELEGDB_NODE(node) ISC_MAGIC_VALID(node, DELEGDB_NODE_MAGIC)
31
32
0
#define DELEGDB_MAGIC   ISC_MAGIC('D', 'e', 'G', 'D')
33
#define VALID_DELEGDB(db) ISC_MAGIC_VALID(db, DELEGDB_MAGIC)
34
35
0
#define DELEGDB_MINSIZE (1024 * 1024) /* 1MiB */
36
37
typedef struct delegdb_node delegdb_node_t;
38
39
typedef struct qplru {
40
  isc_mem_t *mctx;
41
  isc_refcount_t references;
42
  dns_qpmulti_t *nodes;
43
  ISC_SIEVE(delegdb_node_t) lru;
44
  struct rcu_head rcu_head;
45
} qplru_t;
46
47
static void
48
qplru_destroy(qplru_t *qplru);
49
50
#ifdef DNS_DELEGDB_NODETRACE
51
#define qplru_ref(ptr)   qplru__ref(ptr, __func__, __FILE__, __LINE__)
52
#define qplru_unref(ptr) qplru__unref(ptr, __func__, __FILE__, __LINE__)
53
#define qplru_attach(ptr, ptrp) \
54
  qplru__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
55
#define qplru_detach(ptrp) qplru__detach(ptrp, __func__, __FILE__, __LINE__)
56
ISC_REFCOUNT_STATIC_TRACE_DECL(qplru);
57
ISC_REFCOUNT_STATIC_TRACE_IMPL(qplru, qplru_destroy);
58
#else
59
ISC_REFCOUNT_STATIC_DECL(qplru);
60
0
ISC_REFCOUNT_STATIC_IMPL(qplru, qplru_destroy);
Unexecuted instantiation: deleg.c:qplru_detach
Unexecuted instantiation: deleg.c:qplru_unref
Unexecuted instantiation: deleg.c:qplru_ref
61
0
#endif
62
0
63
0
struct dns_delegdb {
64
0
  unsigned int magic;
65
0
66
0
  /*
67
0
   * The DB uses its own memory context in order to easily enforce
68
0
   * overmem policies based on allocations made from this memory context.
69
0
   */
70
0
  isc_mem_t *mctx;
71
0
  isc_refcount_t references;
72
0
73
0
  qplru_t *qplru;
74
0
75
0
  dns_delegdb_config_t config;
76
0
};
77
0
78
0
static void
79
0
qplru_shutdown_rcu(struct rcu_head *rcu_head);
80
0
81
0
static void
82
0
delegdb_destroy(dns_delegdb_t *delegdb) {
83
0
  REQUIRE(VALID_DELEGDB(delegdb));
84
85
0
  delegdb->magic = 0;
86
87
0
  qplru_t *qplru = rcu_xchg_pointer(&delegdb->qplru, NULL);
88
0
  INSIST(qplru != NULL);
89
90
  /*
91
   * Offload the LRU list node deletion to RCU thread (as well as qptrie
92
   * deletion).
93
   */
94
0
  call_rcu(&qplru->rcu_head, qplru_shutdown_rcu);
95
96
0
  LIBDNS_DELEGDB_SHUTDOWN(delegdb);
97
98
0
  isc_mem_putanddetach(&delegdb->mctx, delegdb, sizeof(*delegdb));
99
0
}
100
101
0
ISC_REFCOUNT_IMPL(dns_delegdb, delegdb_destroy);
Unexecuted instantiation: dns_delegdb_ref
Unexecuted instantiation: dns_delegdb_unref
Unexecuted instantiation: dns_delegdb_detach
102
0
103
0
struct delegdb_node {
104
0
  unsigned int magic;
105
0
106
0
  qplru_t *qplru;
107
0
108
0
  isc_refcount_t references;
109
0
110
0
  /* LRU */
111
0
  ISC_LINK(delegdb_node_t) link;
112
0
  bool visited;
113
0
114
0
  /*
115
0
   * Used to build a list of nodes to be deleted (when running the
116
0
   * delete tree flow).
117
0
   */
118
0
  ISC_LINK(delegdb_node_t) deadlink;
119
0
120
0
  /*
121
0
   * Immutable node data
122
0
   */
123
0
  dns_delegset_t *delegset;
124
0
125
0
  dns_name_t zonecut;
126
0
  uint8_t zonecut_buffer[];
127
0
};
128
0
129
0
static size_t
130
0
delegdb_node_size(const delegdb_node_t *node) {
131
0
  return sizeof(*node) + node->zonecut.length;
132
0
}
133
134
static void
135
0
delegdb_node_destroy(delegdb_node_t *node) {
136
0
  REQUIRE(VALID_DELEGDB_NODE(node));
137
0
  REQUIRE(DNS_DELEGSET_VALID(node->delegset));
138
139
0
  qplru_t *qplru = node->qplru;
140
141
0
  node->magic = 0;
142
143
0
  dns_delegset_detach(&node->delegset);
144
145
0
  isc_mem_put(qplru->mctx, node, delegdb_node_size(node));
146
147
0
  qplru_detach(&qplru);
148
0
}
149
150
#ifdef DNS_DELEGDB_NODETRACE
151
#define delegdb_node_ref(ptr) \
152
  delegdb_node__ref(ptr, __func__, __FILE__, __LINE__)
153
#define delegdb_node_unref(ptr) \
154
  delegdb_node__unref(ptr, __func__, __FILE__, __LINE__)
155
#define delegdb_node_attach(ptr, ptrp) \
156
  delegdb_node__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
157
#define delegdb_node_detach(ptrp) \
158
  delegdb_node__detach(ptrp, __func__, __FILE__, __LINE__)
159
ISC_REFCOUNT_STATIC_TRACE_DECL(delegdb_node);
160
ISC_REFCOUNT_STATIC_TRACE_IMPL(delegdb_node, delegdb_node_destroy);
161
#else
162
ISC_REFCOUNT_STATIC_DECL(delegdb_node);
163
0
ISC_REFCOUNT_STATIC_IMPL(delegdb_node, delegdb_node_destroy);
Unexecuted instantiation: deleg.c:delegdb_node_detach
Unexecuted instantiation: deleg.c:delegdb_node_unref
Unexecuted instantiation: deleg.c:delegdb_node_ref
164
0
#endif
165
0
166
0
static void
167
0
dbnode_attach(ISC_ATTR_UNUSED void *uctx, void *pval,
168
0
        ISC_ATTR_UNUSED uint32_t ival) {
169
0
  delegdb_node_t *node = pval;
170
171
0
  REQUIRE(VALID_DELEGDB_NODE(node));
172
0
  delegdb_node_ref(node);
173
0
}
174
175
static void
176
dbnode_detach(ISC_ATTR_UNUSED void *uctx, void *pval,
177
0
        ISC_ATTR_UNUSED uint32_t ival) {
178
0
  delegdb_node_t *node = pval;
179
180
0
  REQUIRE(VALID_DELEGDB_NODE(node));
181
0
  delegdb_node_unref(node);
182
0
}
183
184
static size_t
185
makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval,
186
0
  uint32_t ival ISC_ATTR_UNUSED) {
187
0
  delegdb_node_t *data = pval;
188
0
  return dns_qpkey_fromname(key, &data->zonecut, DNS_DBNAMESPACE_NORMAL);
189
0
}
190
191
static void
192
0
triename(ISC_ATTR_UNUSED void *uctx, char *buf, size_t size) {
193
0
  (void)strncpy(buf, "delegdb", size);
194
0
}
195
196
static dns_qpmethods_t qpmethods = { .attach = dbnode_attach,
197
             .detach = dbnode_detach,
198
             .makekey = makekey,
199
             .triename = triename };
200
201
void
202
0
dns_delegdb_create(dns_delegdb_t **delegdbp) {
203
0
  isc_mem_t *mctx = NULL;
204
0
  dns_delegdb_t *delegdb = NULL;
205
206
0
  REQUIRE(delegdbp != NULL && *delegdbp == NULL);
207
208
0
  isc_mem_create("dns_delegdb", &mctx);
209
0
  isc_mem_setdestroycheck(mctx, true);
210
211
0
  delegdb = isc_mem_get(mctx, sizeof(*delegdb));
212
0
  *delegdb = (dns_delegdb_t){ .magic = DELEGDB_MAGIC,
213
0
            .mctx = mctx,
214
0
            .references = ISC_REFCOUNT_INITIALIZER(1),
215
0
            .config = {} };
216
217
0
  qplru_t *qplru = isc_mem_get(mctx, sizeof(*qplru));
218
0
  *qplru = (qplru_t){
219
0
    .mctx = isc_mem_ref(mctx),
220
0
    .references = ISC_REFCOUNT_INITIALIZER(1),
221
0
  };
222
223
0
  dns_qpmulti_create(mctx, &qpmethods, &qplru->nodes, &qplru->nodes);
224
0
  ISC_SIEVE_INIT(qplru->lru);
225
226
0
  delegdb->qplru = MOVE_OWNERSHIP(qplru);
227
228
0
  LIBDNS_DELEGDB_CREATE(delegdb);
229
230
0
  *delegdbp = delegdb;
231
0
}
232
233
static void
234
0
qplru_destroy(qplru_t *qplru) {
235
0
  isc_mem_putanddetach(&qplru->mctx, qplru, sizeof(*qplru));
236
0
}
237
238
inline static bool
239
0
isrootnode(delegdb_node_t *node) {
240
0
  return dns_name_isroot(&node->zonecut);
241
0
}
242
243
inline static bool
244
0
isactive(delegdb_node_t *node, dns_ttl_t now) {
245
0
  return node->delegset->expires > now;
246
0
}
247
248
static void
249
0
getparentnode(dns_qpchain_t *chain, delegdb_node_t **node, dns_ttl_t now) {
250
0
  size_t len = dns_qpchain_length(chain);
251
252
0
  while (len >= 2) {
253
0
    delegdb_node_t *parent = NULL;
254
0
    dns_qpchain_node(chain, len - 2, (void **)&parent, NULL);
255
256
0
    if (isactive(parent, now) || isrootnode(parent)) {
257
0
      *node = parent;
258
0
      return;
259
0
    }
260
0
    len--;
261
0
  }
262
263
  /*
264
   * No active proper ancestor was found in the chain.  Signal
265
   * "no parent" so the caller does not mistake the original
266
   * matched node for an ancestor. Except for root node.
267
   */
268
0
  if (*node != NULL && !isrootnode(*node)) {
269
0
    *node = NULL;
270
0
  }
271
0
}
272
273
/*
274
 * NOTE: Caller needs to hold a RCU read critical section.
275
 */
276
static isc_result_t
277
deleg_lookup(dns_delegdb_t *delegdb, dns_qpread_t *qpr, const dns_name_t *name,
278
       isc_stdtime_t optnow, unsigned int options, dns_name_t *zonecut,
279
0
       dns_name_t *deepestzonecut, dns_delegset_t **delegsetp) {
280
0
  isc_result_t result = ISC_R_SUCCESS;
281
0
  delegdb_node_t *node = NULL;
282
0
  isc_stdtime_t now = optnow > 0 ? optnow : isc_stdtime_now();
283
284
0
  dns_qpchain_t chain = {};
285
0
  bool above = (options & DNS_DBFIND_ABOVE) != 0;
286
0
  bool include_hint = (options & DNS_DBFIND_HINTOK) != 0;
287
288
0
  REQUIRE(VALID_DELEGDB(delegdb));
289
0
  REQUIRE(DNS_NAME_VALID(name));
290
0
  REQUIRE(zonecut == NULL || dns_name_hasbuffer(zonecut));
291
0
  REQUIRE(deepestzonecut == NULL || dns_name_hasbuffer(deepestzonecut));
292
293
0
  result = dns_qp_lookup(qpr, name, DNS_DBNAMESPACE_NORMAL, NULL, &chain,
294
0
             (void **)&node, NULL);
295
296
0
  if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH) {
297
0
    return ISC_R_NOTFOUND;
298
0
  }
299
0
  INSIST(VALID_DELEGDB_NODE(node));
300
301
0
  if (zonecut != NULL && deepestzonecut != NULL) {
302
0
    dns_name_copy(&node->zonecut, deepestzonecut);
303
0
  }
304
305
  /*
306
   * Walk up the chain when:
307
   *  - we have an exact match but the caller asked for DNS_DBFIND_ABOVE
308
   *    (i.e. the caller wants the deepest *proper* ancestor), or
309
   *  - the matched node is no longer active and we need to fall
310
   *    back to the closest still-active ancestor (this applies
311
   *    equally to exact and partial matches).
312
   *
313
   * getparentnode() sets 'node' to NULL when no active ancestor
314
   * exists in the chain, so we must NULL-check before dereferencing
315
   * 'node' below.
316
   */
317
0
  if ((result == ISC_R_SUCCESS && above) || !isactive(node, now)) {
318
0
    getparentnode(&chain, &node, now);
319
0
  }
320
321
  /*
322
   * The (non root) expired node will be replaced when the resolver
323
   * fetches a fresh delegation, so there is no need to schedule explicit
324
   * cleanup here.  Stale nodes that are never replaced will
325
   * eventually be evicted by the SIEVE policy under memory
326
   * pressure.
327
   */
328
0
  if (node == NULL) {
329
0
    return ISC_R_NOTFOUND;
330
0
  }
331
332
0
  if (isrootnode(node)) {
333
0
    if (!include_hint) {
334
0
      return ISC_R_NOTFOUND;
335
0
    }
336
337
0
    if (zonecut != NULL) {
338
0
      dns_name_copy(&node->zonecut, zonecut);
339
0
    }
340
0
    INSIST(node->delegset);
341
0
    dns_delegset_attach(node->delegset, delegsetp);
342
343
    /*
344
     * The root delegation might be expired, but we still use it,
345
     * and return `DNS_R_EXPIRED` so the caller knows it needs to
346
     * run priming again.
347
     */
348
0
    return isactive(node, now) ? ISC_R_SUCCESS : DNS_R_EXPIRED;
349
0
  }
350
351
0
  if (isactive(node, now)) {
352
0
    if (zonecut != NULL) {
353
0
      dns_name_copy(&node->zonecut, zonecut);
354
0
    }
355
0
    INSIST(node->delegset);
356
0
    dns_delegset_attach(node->delegset, delegsetp);
357
0
    ISC_SIEVE_MARK(node, visited);
358
0
    return ISC_R_SUCCESS;
359
0
  }
360
361
0
  return ISC_R_NOTFOUND;
362
0
}
363
364
isc_result_t
365
dns_delegdb_lookup(dns_delegdb_t *delegdb, const dns_name_t *name,
366
       isc_stdtime_t now, unsigned int options, dns_name_t *zonecut,
367
       dns_name_t *deepestzonecut, dns_delegset_t **delegsetp) {
368
  isc_result_t result = ISC_R_SHUTTINGDOWN;
369
  dns_qpread_t qpr = {};
370
  char namebuf[DNS_NAME_FORMATSIZE];
371
372
  if (LIBDNS_DELEGDB_LOOKUP_START_ENABLED() ||
373
      LIBDNS_DELEGDB_LOOKUP_DONE_ENABLED())
374
  {
375
    dns_name_format(name, namebuf, sizeof(namebuf));
376
  }
377
  LIBDNS_DELEGDB_LOOKUP_START(delegdb, namebuf);
378
379
  dns_qpmulti_query(delegdb->qplru->nodes, &qpr);
380
  result = deleg_lookup(delegdb, &qpr, name, now, options, zonecut,
381
            deepestzonecut, delegsetp);
382
  dns_qpread_destroy(delegdb->qplru->nodes, &qpr);
383
384
  LIBDNS_DELEGDB_LOOKUP_DONE(delegdb, namebuf, result);
385
386
  return result;
387
}
388
389
void
390
0
dns_delegset_allocset(dns_delegdb_t *delegdb, dns_delegset_t **delegsetp) {
391
0
  REQUIRE(VALID_DELEGDB(delegdb));
392
0
  REQUIRE(delegsetp != NULL && *delegsetp == NULL);
393
394
0
  dns_delegset_t *delegset = isc_mem_get(delegdb->mctx,
395
0
                 sizeof(*delegset));
396
0
  *delegset = (dns_delegset_t){
397
0
    .magic = DNS_DELEGSET_MAGIC,
398
0
    .references = ISC_REFCOUNT_INITIALIZER(1),
399
0
    .delegs = ISC_LIST_INITIALIZER,
400
0
  };
401
0
  isc_mem_attach(delegdb->mctx, &delegset->mctx);
402
403
0
  *delegsetp = delegset;
404
0
}
405
406
void
407
dns_delegset_allocdeleg(dns_delegset_t *delegset, dns_deleg_type_t type,
408
0
      dns_deleg_t **delegp) {
409
0
  dns_deleg_t *deleg = NULL;
410
411
0
  REQUIRE(DNS_DELEGSET_VALID(delegset));
412
0
  REQUIRE(delegp != NULL && *delegp == NULL);
413
0
  REQUIRE(type != DNS_DELEGTYPE_UNDEFINED);
414
415
0
  deleg = isc_mem_get(delegset->mctx, sizeof(*deleg));
416
0
  *deleg = (dns_deleg_t){ .addresses = ISC_LIST_INITIALIZER,
417
0
        .names = ISC_LIST_INITIALIZER,
418
0
        .type = type,
419
0
        .link = ISC_LINK_INITIALIZER };
420
421
0
  ISC_LIST_APPEND(delegset->delegs, deleg, link);
422
0
  *delegp = deleg;
423
0
}
424
425
void
426
0
dns_delegset_freedeleg(dns_delegset_t *delegset, dns_deleg_t **delegp) {
427
0
  REQUIRE(DNS_DELEGSET_VALID(delegset));
428
0
  REQUIRE(delegp != NULL && *delegp != NULL);
429
0
  REQUIRE(ISC_LIST_EMPTY((*delegp)->addresses));
430
0
  REQUIRE(ISC_LIST_EMPTY((*delegp)->names));
431
432
0
  dns_deleg_t *deleg = *delegp;
433
0
  *delegp = NULL;
434
435
0
  ISC_LIST_UNLINK(delegset->delegs, deleg, link);
436
437
0
  isc_mem_put(delegset->mctx, deleg, sizeof(*deleg));
438
0
}
439
440
void
441
dns_delegset_addaddr(dns_delegset_t *delegset, dns_deleg_t *deleg,
442
0
         const isc_netaddr_t *addr) {
443
0
  isc_netaddrlink_t *addrlink = NULL;
444
445
0
  REQUIRE(DNS_DELEGSET_VALID(delegset));
446
0
  REQUIRE(deleg != NULL);
447
0
  REQUIRE(addr != NULL);
448
0
  REQUIRE(deleg->type == DNS_DELEGTYPE_DELEG_ADDRESSES ||
449
0
    deleg->type == DNS_DELEGTYPE_NS_GLUES);
450
451
0
  addrlink = isc_mem_get(delegset->mctx, sizeof(*addrlink));
452
0
  *addrlink = (isc_netaddrlink_t){ .addr = *addr,
453
0
           .link = ISC_LINK_INITIALIZER };
454
455
0
  ISC_LIST_APPEND(deleg->addresses, addrlink, link);
456
0
}
457
458
static void
459
addname(dns_delegset_t *delegset, dns_namelist_t *list,
460
0
  const dns_name_t *name) {
461
0
  dns_name_t *clone = NULL;
462
463
0
  REQUIRE(DNS_DELEGSET_VALID(delegset));
464
0
  REQUIRE(DNS_NAME_VALID(name));
465
466
0
  clone = isc_mem_get(delegset->mctx, sizeof(*clone));
467
0
  dns_name_init(clone);
468
0
  dns_name_dup(name, delegset->mctx, clone);
469
0
  ISC_LIST_APPEND(*list, clone, link);
470
0
}
471
472
void
473
dns_delegset_adddelegparam(dns_delegset_t *delegset, dns_deleg_t *deleg,
474
0
         const dns_name_t *name) {
475
0
  REQUIRE(deleg != NULL);
476
0
  REQUIRE(deleg->type == DNS_DELEGTYPE_DELEG_PARAMS);
477
0
  addname(delegset, &deleg->names, name);
478
0
}
479
480
void
481
dns_delegset_addns(dns_delegset_t *delegset, dns_deleg_t *deleg,
482
0
       const dns_name_t *name) {
483
0
  REQUIRE(deleg != NULL);
484
485
0
  REQUIRE(deleg->type == DNS_DELEGTYPE_DELEG_NAMES ||
486
0
    deleg->type == DNS_DELEGTYPE_NS_NAMES);
487
0
  addname(delegset, &deleg->names, name);
488
0
}
489
490
static size_t
491
delegset_size(dns_delegset_t *delegset);
492
493
static void
494
0
delegdb_cleanup(dns_delegdb_t *delegdb, dns_qp_t *qp, size_t requested) {
495
0
  delegdb_node_t *node = NULL;
496
0
  size_t reclaimed = 0;
497
498
0
  if (!isc_mem_isovermem(delegdb->mctx)) {
499
0
    return;
500
0
  }
501
502
0
  LIBDNS_DELEGDB_CLEANUP_START(delegdb, (int)requested);
503
504
0
  while (reclaimed < requested) {
505
0
    node = ISC_SIEVE_NEXT(delegdb->qplru->lru, visited, link);
506
507
0
    if (node == NULL) {
508
0
      break;
509
0
    }
510
0
    reclaimed += delegdb_node_size(node) +
511
0
           delegset_size(node->delegset);
512
513
0
    if (LIBDNS_DELEGDB_EVICT_ENABLED()) {
514
0
      char namebuf[DNS_NAME_FORMATSIZE];
515
0
      dns_name_format(&node->zonecut, namebuf,
516
0
          sizeof(namebuf));
517
0
      LIBDNS_DELEGDB_EVICT(delegdb, node, namebuf);
518
0
    }
519
520
0
    delegdb_node_t *old_node = NULL;
521
0
    isc_result_t result = dns_qp_deletename(
522
0
      qp, &node->zonecut, DNS_DBNAMESPACE_NORMAL,
523
0
      (void *)&old_node, NULL);
524
0
    if (result == ISC_R_SUCCESS) {
525
0
      ISC_SIEVE_UNLINK(delegdb->qplru->lru, old_node, link);
526
0
      delegdb_node_detach(&old_node);
527
0
    }
528
0
  }
529
530
0
  LIBDNS_DELEGDB_CLEANUP_DONE(delegdb, (int)reclaimed);
531
0
}
532
533
static size_t
534
0
delegset_size(dns_delegset_t *delegset) {
535
0
  size_t sz = 0;
536
537
0
  sz += sizeof(*delegset);
538
0
  ISC_LIST_FOREACH(delegset->delegs, deleg, link) {
539
0
    sz += sizeof(*deleg);
540
0
    ISC_LIST_FOREACH(deleg->addresses, address, link) {
541
0
      sz += sizeof(*address);
542
0
    }
543
0
    ISC_LIST_FOREACH(deleg->names, name, link) {
544
0
      sz += sizeof(*name) + dns_name_size(name);
545
0
    }
546
0
  }
547
548
0
  return sz;
549
0
}
550
551
static dns_ttl_t
552
0
normalize_ttl(dns_delegdb_t *delegdb, dns_ttl_t ttl) {
553
0
  dns_ttl_t minttl = delegdb->config.minttl;
554
0
  dns_ttl_t maxttl = delegdb->config.maxttl;
555
556
0
  if (minttl > 0 && ttl < minttl) {
557
0
    return minttl;
558
0
  }
559
560
0
  if (maxttl > 0 && ttl > maxttl) {
561
0
    return maxttl;
562
0
  }
563
564
  /*
565
   * Even if the min ttl is disabled, it doesn't make sense to add an
566
   * already expired delegation. So give it at least one second.
567
   */
568
0
  return ttl == 0 ? 1 : ttl;
569
0
}
570
571
static size_t
572
delegdb_node_prepare(dns_delegdb_t *delegdb, isc_stdtime_t now, dns_ttl_t ttl,
573
         const dns_name_t *zonecut, dns_delegset_t *delegset,
574
0
         delegdb_node_t **nodep) {
575
0
  delegset->expires = ttl + now;
576
577
0
  isc_region_t zonecut_r = { 0 };
578
0
  dns_name_toregion(zonecut, &zonecut_r);
579
580
0
  delegdb_node_t *node = isc_mem_get(delegdb->qplru->mctx,
581
0
             sizeof(*node) + zonecut_r.length);
582
0
  *node = (delegdb_node_t){
583
584
0
    .magic = DELEGDB_NODE_MAGIC,
585
0
    .references = ISC_REFCOUNT_INITIALIZER(1),
586
0
    .link = ISC_LINK_INITIALIZER,
587
0
    .deadlink = ISC_LINK_INITIALIZER,
588
0
    .zonecut = DNS_NAME_INITEMPTY,
589
0
    .qplru = qplru_ref(delegdb->qplru),
590
0
  };
591
0
  dns_delegset_attach(delegset, &node->delegset);
592
593
0
  memmove(node->zonecut_buffer, zonecut_r.base, zonecut_r.length);
594
0
  zonecut_r.base = node->zonecut_buffer;
595
0
  dns_name_fromregion(&node->zonecut, &zonecut_r);
596
597
0
  *nodep = node;
598
599
0
  return delegdb_node_size(node);
600
0
}
601
602
static void
603
insertroot(dns_delegdb_t *delegdb, isc_stdtime_t now, dns_ttl_t ttl,
604
0
     dns_delegset_t *delegset) {
605
0
  delegdb_node_t *node = NULL;
606
0
  dns_qp_t *qp = NULL;
607
608
0
  (void)delegdb_node_prepare(delegdb, now, ttl, dns_rootname, delegset,
609
0
           &node);
610
611
0
  dns_qpmulti_write(delegdb->qplru->nodes, &qp);
612
613
0
  (void)dns_qp_deletename(qp, dns_rootname, DNS_DBNAMESPACE_NORMAL, NULL,
614
0
        NULL);
615
0
  (void)dns_qp_insert(qp, node, 0);
616
0
  delegdb_node_unref(node);
617
618
0
  dns_qp_compact(qp, DNS_QPGC_MAYBE);
619
0
  dns_qpmulti_commit(delegdb->qplru->nodes, &qp);
620
0
}
621
622
isc_result_t
623
dns_delegset_insert(dns_delegdb_t *delegdb, const dns_name_t *zonecut,
624
0
        dns_ttl_t ttl, dns_delegset_t *delegset) {
625
0
  isc_result_t result;
626
0
  delegdb_node_t *node = NULL;
627
0
  dns_qp_t *qp = NULL;
628
0
  dns_qpread_t qpr = {};
629
0
  isc_stdtime_t now = isc_stdtime_now();
630
0
  char zonecutbuf[DNS_NAME_FORMATSIZE];
631
632
0
  REQUIRE(VALID_DELEGDB(delegdb));
633
0
  REQUIRE(DNS_NAME_VALID(zonecut));
634
0
  REQUIRE(DNS_DELEGSET_VALID(delegset));
635
636
  /*
637
   * Only delegset allocated by the delegdb memory context can be added in
638
   * the delegdb. This exclude transient delegset built from rdataset (see
639
   * dns_delegset_fromrdataset()).
640
   */
641
0
  REQUIRE(delegset->mctx == delegdb->mctx);
642
643
0
  if (LIBDNS_DELEGDB_INSERT_START_ENABLED() ||
644
0
      LIBDNS_DELEGDB_INSERT_DONE_ENABLED())
645
0
  {
646
0
    dns_name_format(zonecut, zonecutbuf, sizeof(zonecutbuf));
647
0
  }
648
0
  LIBDNS_DELEGDB_INSERT_START(delegdb, zonecutbuf);
649
650
  /*
651
   * The root zone cut has special handling: it can be proactively
652
   * replaced (even if not expired), and must not be part of the LRU
653
   * list. It is fine to skip the reclamation phase for this, as it is
654
   * really an edge case to have to replace it anyway, and it avoids
655
   * riddling the insertion code with specific root checks.
656
   */
657
0
  if (dns_name_isroot(zonecut)) {
658
0
    insertroot(delegdb, now, ttl, delegset);
659
0
    result = ISC_R_SUCCESS;
660
0
    goto cleanup;
661
0
  }
662
663
  /*
664
   * First, check (without write txn) if the node already exists and is
665
   * still valid.
666
   */
667
0
  dns_qpmulti_query(delegdb->qplru->nodes, &qpr);
668
0
  result = dns_qp_lookup(&qpr, zonecut, DNS_DBNAMESPACE_NORMAL, NULL,
669
0
             NULL, (void **)&node, NULL);
670
0
  if (result == ISC_R_SUCCESS) {
671
0
    INSIST(VALID_DELEGDB_NODE(node));
672
0
    if (node->delegset->expires > now) {
673
0
      dns_qpread_destroy(delegdb->qplru->nodes, &qpr);
674
0
      CLEANUP(ISC_R_EXISTS);
675
0
    }
676
0
  }
677
0
  dns_qpread_destroy(delegdb->qplru->nodes, &qpr);
678
679
  /*
680
   * We're about to add a new delegation, check for state of overmem, and
681
   * clean up expired/least recently used delegation, then allocate and
682
   * initialize a new node.
683
   */
684
0
  ttl = normalize_ttl(delegdb, ttl);
685
0
  size_t requested = delegdb_node_prepare(delegdb, now, ttl, zonecut,
686
0
            delegset, &node) +
687
0
         delegset_size(delegset);
688
689
  /*
690
   * Add the node in the DB
691
   */
692
0
  dns_qpmulti_write(delegdb->qplru->nodes, &qp);
693
694
0
  delegdb_cleanup(delegdb, qp, requested);
695
696
0
  if (result == ISC_R_SUCCESS) {
697
0
    delegdb_node_t *old_node = NULL;
698
0
    result = dns_qp_deletename(qp, zonecut, DNS_DBNAMESPACE_NORMAL,
699
0
             (void *)&old_node, NULL);
700
0
    if (result == ISC_R_SUCCESS) {
701
0
      ISC_SIEVE_UNLINK(delegdb->qplru->lru, old_node, link);
702
0
      delegdb_node_detach(&old_node);
703
0
    }
704
0
  }
705
706
0
  result = dns_qp_insert(qp, node, 0);
707
0
  if (result != ISC_R_SUCCESS) {
708
    /*
709
     * Someone else added the node before (and there was no node to
710
     * delete).
711
     */
712
713
0
    delegdb_node_unref(node);
714
715
    /*
716
     * Since not using an update (but write) transaction,
717
     * _rollback() won't work here.
718
     */
719
0
    dns_qpmulti_commit(delegdb->qplru->nodes, &qp);
720
0
    CLEANUP(ISC_R_EXISTS);
721
0
  }
722
723
  /*
724
   * The new delegation is added, and can be referenced by SIEVE
725
   */
726
0
  delegdb_node_ref(node);
727
0
  ISC_SIEVE_INSERT(delegdb->qplru->lru, node, link);
728
729
0
  delegdb_node_unref(node);
730
0
  dns_qp_compact(qp, DNS_QPGC_MAYBE);
731
0
  dns_qpmulti_commit(delegdb->qplru->nodes, &qp);
732
733
0
cleanup:
734
0
  LIBDNS_DELEGDB_INSERT_DONE(delegdb, zonecutbuf, result);
735
736
0
  return result;
737
0
}
738
739
static void
740
0
delegset_destroy(dns_delegset_t *delegset) {
741
0
  REQUIRE(DNS_DELEGSET_VALID(delegset));
742
743
0
  delegset->magic = 0;
744
0
  ISC_LIST_FOREACH(delegset->delegs, deleg, link) {
745
0
    deleg->type = DNS_DELEGTYPE_UNDEFINED;
746
747
0
    ISC_LIST_UNLINK(delegset->delegs, deleg, link);
748
749
0
    ISC_LIST_FOREACH(deleg->addresses, address, link) {
750
0
      ISC_LIST_UNLINK(deleg->addresses, address, link);
751
0
      isc_mem_put(delegset->mctx, address, sizeof(*address));
752
0
    }
753
754
0
    ISC_LIST_FOREACH(deleg->names, nameserver, link) {
755
0
      ISC_LIST_UNLINK(deleg->names, nameserver, link);
756
0
      dns_name_free(nameserver, delegset->mctx);
757
0
      isc_mem_put(delegset->mctx, nameserver,
758
0
            sizeof(*nameserver));
759
0
    }
760
761
0
    isc_mem_put(delegset->mctx, deleg, sizeof(*deleg));
762
0
  }
763
764
0
  isc_mem_putanddetach(&delegset->mctx, delegset, sizeof(*delegset));
765
0
}
766
0
ISC_REFCOUNT_IMPL(dns_delegset, delegset_destroy);
Unexecuted instantiation: dns_delegset_ref
Unexecuted instantiation: dns_delegset_unref
Unexecuted instantiation: dns_delegset_detach
767
0
768
0
static void
769
0
tostring_namelist(dns_namelist_t *namelist, const char *id, FILE *fp) {
770
0
  if (!ISC_LIST_EMPTY(*namelist)) {
771
0
    fprintf(fp, " %s=", id);
772
0
    ISC_LIST_FOREACH(*namelist, name, link) {
773
0
      isc_buffer_t nameb;
774
0
      char bdata[DNS_NAME_FORMATSIZE] = { 0 };
775
776
0
      isc_buffer_init(&nameb, bdata, sizeof(bdata));
777
0
      dns_name_totext(name, 0, &nameb);
778
0
      fprintf(fp, "%s", bdata);
779
780
0
      if (name != ISC_LIST_TAIL(*namelist)) {
781
0
        fprintf(fp, ",");
782
0
      }
783
0
    }
784
0
  }
785
0
}
786
787
static void
788
0
deleg_tostring_addresses(dns_deleg_t *deleg, FILE *fp) {
789
0
  bool hasv4 = false;
790
0
  bool hasv6 = false;
791
792
0
  ISC_LIST_FOREACH(deleg->addresses, address, link) {
793
0
    if (address->addr.family == AF_INET) {
794
0
      hasv4 = true;
795
0
    } else {
796
0
      hasv6 = true;
797
0
    }
798
0
  }
799
800
0
  if (hasv4) {
801
0
    bool first = true;
802
803
0
    fprintf(fp, " server-ipv4=");
804
0
    ISC_LIST_FOREACH(deleg->addresses, address, link) {
805
0
      char addrstr[] = "255.255.255.255";
806
807
0
      if (address->addr.family == AF_INET6) {
808
0
        continue;
809
0
      }
810
811
0
      if (!first) {
812
0
        fprintf(fp, ",");
813
0
      }
814
0
      first = false;
815
816
0
      inet_ntop(AF_INET, &address->addr.type, addrstr,
817
0
          sizeof(addrstr));
818
0
      fprintf(fp, "%s", addrstr);
819
0
    }
820
0
  }
821
822
0
  if (hasv6) {
823
0
    bool first = true;
824
825
0
    fprintf(fp, " server-ipv6=");
826
0
    ISC_LIST_FOREACH(deleg->addresses, address, link) {
827
0
      char addrstr[INET6_ADDRSTRLEN];
828
829
0
      if (address->addr.family == AF_INET) {
830
0
        continue;
831
0
      }
832
833
0
      if (!first) {
834
0
        fprintf(fp, ",");
835
0
      }
836
0
      first = false;
837
838
0
      inet_ntop(AF_INET6, &address->addr.type, addrstr,
839
0
          sizeof(addrstr));
840
0
      fprintf(fp, "%s", addrstr);
841
0
    }
842
0
  }
843
0
}
844
845
static void
846
delegset_tostring(const dns_name_t *zonecut, dns_delegset_t *delegset,
847
0
      isc_stdtime_t now, FILE *fp) {
848
0
  ISC_LIST_FOREACH(delegset->delegs, deleg, link) {
849
0
    isc_buffer_t zonecutb;
850
0
    char bdata[DNS_NAME_FORMATSIZE];
851
0
    dns_ttl_t ttl = 0;
852
853
0
    if (delegset->expires > now) {
854
0
      ttl = delegset->expires - now;
855
0
    }
856
857
0
    isc_buffer_init(&zonecutb, bdata, sizeof(bdata));
858
0
    dns_name_totext(zonecut, 0, &zonecutb);
859
0
    fprintf(fp, "%s %u DELEG", bdata, ttl);
860
861
0
    if (deleg->type == DNS_DELEGTYPE_DELEG_ADDRESSES ||
862
0
        deleg->type == DNS_DELEGTYPE_NS_GLUES)
863
0
    {
864
0
      deleg_tostring_addresses(deleg, fp);
865
0
    } else if (deleg->type == DNS_DELEGTYPE_DELEG_NAMES ||
866
0
         deleg->type == DNS_DELEGTYPE_NS_NAMES)
867
0
    {
868
0
      tostring_namelist(&deleg->names, "server-name", fp);
869
0
    } else if (deleg->type == DNS_DELEGTYPE_DELEG_PARAMS) {
870
0
      tostring_namelist(&deleg->names, "include-delegparam",
871
0
            fp);
872
0
    } else {
873
0
      UNREACHABLE();
874
0
    }
875
876
0
    fprintf(fp, "\n");
877
0
  }
878
0
}
879
880
void
881
0
dns_delegdb_dump(dns_delegdb_t *delegdb, bool expired, FILE *fp) {
882
0
  REQUIRE(VALID_DELEGDB(delegdb));
883
0
  REQUIRE(fp != NULL);
884
885
0
  dns_qpiter_t it;
886
0
  dns_qpread_t qpr = {};
887
0
  delegdb_node_t *node = NULL;
888
0
  isc_stdtime_t now = isc_stdtime_now();
889
890
0
  dns_qpmulti_query(delegdb->qplru->nodes, &qpr);
891
892
0
  dns_qpiter_init(&qpr, &it);
893
0
  while (dns_qpiter_next(&it, (void **)&node, NULL) == ISC_R_SUCCESS) {
894
0
    if (!expired && !isactive(node, now) && !isrootnode(node)) {
895
0
      continue;
896
0
    }
897
898
0
    delegset_tostring(&node->zonecut, node->delegset, now, fp);
899
0
  }
900
901
0
  dns_qpread_destroy(delegdb->qplru->nodes, &qpr);
902
0
}
903
904
void
905
dns_delegset_fromnsrdataset(isc_mem_t *mctx, dns_rdataset_t *rdataset,
906
0
          dns_delegset_t **delegsetp) {
907
0
  dns_delegset_t *delegset = NULL;
908
0
  dns_deleg_t *deleg = NULL;
909
910
0
  if (rdataset == NULL || !dns_rdataset_isassociated(rdataset) ||
911
0
      delegsetp == NULL || *delegsetp != NULL)
912
0
  {
913
0
    return;
914
0
  }
915
916
0
  REQUIRE(rdataset->type == dns_rdatatype_ns);
917
918
0
  delegset = isc_mem_get(mctx, sizeof(*delegset));
919
0
  *delegset = (dns_delegset_t){
920
0
    .magic = DNS_DELEGSET_MAGIC,
921
0
    .mctx = isc_mem_ref(mctx),
922
0
    .references = ISC_REFCOUNT_INITIALIZER(1),
923
0
    .delegs = ISC_LIST_INITIALIZER,
924
0
    .expires = rdataset->ttl + isc_stdtime_now(),
925
0
    .staticstub = rdataset->attributes.staticstub
926
0
  };
927
928
0
  deleg = isc_mem_get(delegset->mctx, sizeof(*deleg));
929
0
  *deleg = (dns_deleg_t){ .addresses = ISC_LIST_INITIALIZER,
930
0
        .names = ISC_LIST_INITIALIZER,
931
0
        .type = DNS_DELEGTYPE_NS_NAMES,
932
0
        .link = ISC_LINK_INITIALIZER };
933
0
  ISC_LIST_APPEND(delegset->delegs, deleg, link);
934
935
0
  DNS_RDATASET_FOREACH(rdataset) {
936
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
937
0
    dns_rdata_ns_t ns;
938
939
0
    dns_rdataset_current(rdataset, &rdata);
940
0
    dns_rdata_tostruct(&rdata, &ns, NULL);
941
0
    dns_delegset_addns(delegset, deleg, &ns.name);
942
0
  }
943
944
0
  *delegsetp = delegset;
945
0
}
946
947
void
948
dns_delegset_copy(dns_delegset_t *src, dns_delegdb_t *db,
949
0
      dns_delegset_t **delegsetp) {
950
0
  dns_delegset_t *delegset = NULL;
951
952
0
  REQUIRE(DNS_DELEGSET_VALID(src));
953
0
  REQUIRE(VALID_DELEGDB(db));
954
0
  REQUIRE(delegsetp != NULL && *delegsetp == NULL);
955
956
0
  dns_delegset_allocset(db, &delegset);
957
0
  delegset->staticstub = src->staticstub;
958
0
  delegset->expires = src->expires;
959
960
0
  ISC_LIST_FOREACH(src->delegs, srcdeleg, link) {
961
0
    dns_deleg_t *deleg = NULL;
962
963
0
    dns_delegset_allocdeleg(delegset, srcdeleg->type, &deleg);
964
965
    /*
966
     * Only one of these two loops will actually run; this
967
     * avoids having to do conditional checks depending on the
968
     * type.
969
     */
970
0
    ISC_LIST_FOREACH(srcdeleg->addresses, addr, link) {
971
0
      dns_delegset_addaddr(delegset, deleg, &addr->addr);
972
0
    }
973
974
0
    ISC_LIST_FOREACH(srcdeleg->names, name, link) {
975
0
      addname(delegset, &deleg->names, name);
976
0
    }
977
0
  }
978
979
0
  *delegsetp = delegset;
980
0
}
981
982
static isc_result_t
983
0
deleg_deletetree(qplru_t *qplru, dns_qp_t *qp, const dns_name_t *name) {
984
0
  isc_result_t result;
985
0
  delegdb_node_t *node = NULL;
986
0
  dns_qpiter_t it;
987
0
  ISC_LIST(delegdb_node_t) deadnodes = ISC_LIST_INITIALIZER;
988
989
0
  result = dns_qp_lookup(qp, name, DNS_DBNAMESPACE_NORMAL, &it, NULL,
990
0
             (void **)&node, NULL);
991
0
  if (result != ISC_R_SUCCESS) {
992
0
    goto out;
993
0
  }
994
995
0
  INSIST(VALID_DELEGDB_NODE(node));
996
0
  do {
997
    /*
998
     * Because QP doesn't allow deleting a node while using the
999
     * iterator, the approach is different than `deleg_deletenode()`
1000
     * here. Instead of removing the node immediately, we add it
1001
     * into a list that we'll go through after, then delete each
1002
     * node.
1003
     */
1004
0
    ISC_LIST_APPEND(deadnodes, node, deadlink);
1005
1006
0
    result = dns_qpiter_next(&it, (void **)&node, NULL);
1007
0
    if (result == ISC_R_NOMORE) {
1008
0
      result = ISC_R_SUCCESS;
1009
0
      break;
1010
0
    }
1011
1012
0
    INSIST(VALID_DELEGDB_NODE(node));
1013
0
    if (!dns_name_issubdomain(&node->zonecut, name)) {
1014
0
      break;
1015
0
    }
1016
0
  } while (result == ISC_R_SUCCESS);
1017
1018
0
out:
1019
0
  if (ISC_LIST_EMPTY(deadnodes)) {
1020
0
    result = ISC_R_NOTFOUND;
1021
0
  } else {
1022
    /*
1023
     * Let's actually delete the deadnodes!
1024
     */
1025
0
    ISC_LIST_FOREACH(deadnodes, deadnode, deadlink) {
1026
0
      delegdb_node_t *old_node = NULL;
1027
0
      result = dns_qp_deletename(qp, &deadnode->zonecut,
1028
0
               DNS_DBNAMESPACE_NORMAL,
1029
0
               (void *)&old_node, NULL);
1030
0
      INSIST(result == ISC_R_SUCCESS);
1031
0
      INSIST(old_node == deadnode);
1032
0
      ISC_SIEVE_UNLINK(qplru->lru, old_node, link);
1033
0
      delegdb_node_detach(&old_node);
1034
0
    }
1035
0
  }
1036
1037
0
  return result;
1038
0
}
1039
1040
static isc_result_t
1041
0
deleg_deletenode(qplru_t *qplru, dns_qp_t *qp, const dns_name_t *name) {
1042
0
  delegdb_node_t *old_node = NULL;
1043
0
  isc_result_t result = dns_qp_deletename(
1044
0
    qp, name, DNS_DBNAMESPACE_NORMAL, (void *)&old_node, NULL);
1045
0
  if (result == ISC_R_SUCCESS) {
1046
0
    ISC_SIEVE_UNLINK(qplru->lru, old_node, link);
1047
0
    delegdb_node_detach(&old_node);
1048
0
  }
1049
1050
0
  return result;
1051
0
}
1052
1053
isc_result_t
1054
0
dns_delegdb_delete(dns_delegdb_t *delegdb, const dns_name_t *name, bool tree) {
1055
0
  REQUIRE(VALID_DELEGDB(delegdb));
1056
0
  REQUIRE(DNS_NAME_VALID(name));
1057
1058
0
  dns_qp_t *qp = NULL;
1059
0
  isc_result_t result = ISC_R_SHUTTINGDOWN;
1060
0
  char namebuf[DNS_NAME_FORMATSIZE];
1061
1062
  /*
1063
   * Once added, the root node must always remain in the DB.
1064
   */
1065
0
  if (dns_name_isroot(name)) {
1066
0
    return DNS_R_REFUSED;
1067
0
  }
1068
1069
0
  if (LIBDNS_DELEGDB_DELETE_ENABLED()) {
1070
0
    dns_name_format(name, namebuf, sizeof(namebuf));
1071
0
  }
1072
1073
0
  dns_qpmulti_write(delegdb->qplru->nodes, &qp);
1074
0
  if (tree) {
1075
0
    result = deleg_deletetree(delegdb->qplru, qp, name);
1076
0
  } else {
1077
0
    result = deleg_deletenode(delegdb->qplru, qp, name);
1078
0
  }
1079
0
  if (result == ISC_R_SUCCESS) {
1080
0
    dns_qp_compact(qp, DNS_QPGC_MAYBE);
1081
0
  }
1082
0
  dns_qpmulti_commit(delegdb->qplru->nodes, &qp);
1083
1084
0
  LIBDNS_DELEGDB_DELETE(delegdb, namebuf, (int)tree, result);
1085
1086
0
  return result;
1087
0
}
1088
1089
static void
1090
0
qplru_shutdown_rcu(struct rcu_head *rcu_head) {
1091
0
  qplru_t *qplru = caa_container_of(rcu_head, qplru_t, rcu_head);
1092
1093
0
  ISC_SIEVE_FOREACH(qplru->lru, node, link) {
1094
0
    ISC_SIEVE_UNLINK(qplru->lru, node, link);
1095
0
    delegdb_node_detach(&node);
1096
0
  }
1097
1098
0
  dns_qpmulti_destroy(&qplru->nodes);
1099
1100
0
  qplru_detach(&qplru);
1101
0
}
1102
1103
static void
1104
0
delegdb_setsize(dns_delegdb_t *delegdb, size_t size) {
1105
0
  size_t lowater;
1106
0
  size_t hiwater;
1107
1108
0
  REQUIRE(VALID_DELEGDB(delegdb));
1109
1110
0
  if (size != 0 && size < DELEGDB_MINSIZE) {
1111
0
    size = DELEGDB_MINSIZE;
1112
0
  }
1113
1114
0
  hiwater = size - (size >> 3); /* Approximately 7/8ths. */
1115
0
  lowater = size - (size >> 2); /* Approximately 3/4ths. */
1116
1117
0
  if (size == 0 || hiwater == 0 || lowater == 0) {
1118
0
    isc_mem_clearwater(delegdb->mctx);
1119
1120
    /*
1121
     * TODO: Is it worth a warning if size > 0? Sounds like
1122
     * implicit overmem bypass, so the user should be warned...
1123
     */
1124
0
  } else {
1125
0
    isc_mem_setwater(delegdb->mctx, hiwater, lowater);
1126
0
  }
1127
0
}
1128
1129
dns_delegdb_config_t
1130
0
dns_delegdb_getconfig(dns_delegdb_t *delegdb) {
1131
0
  REQUIRE(VALID_DELEGDB(delegdb));
1132
0
  return delegdb->config;
1133
0
}
1134
1135
void
1136
dns_delegdb_setconfig(dns_delegdb_t *delegdb,
1137
0
          const dns_delegdb_config_t *config) {
1138
0
  REQUIRE(isc_loop_get(isc_tid()) == isc_loop_main());
1139
0
  REQUIRE(VALID_DELEGDB(delegdb));
1140
1141
0
  delegdb->config = *config;
1142
1143
0
  delegdb_setsize(delegdb, delegdb->config.dbsize);
1144
0
}
1145
1146
typedef struct {
1147
  dns_delegdb_t *db;
1148
  dns_delegset_t *delegset;
1149
  dns_deleg_t *deleg;
1150
  bool empty_file;
1151
} delegdb_rootns_ctx_t;
1152
1153
static isc_result_t
1154
delegdb_rootns_update(void *arg, const dns_name_t *name,
1155
0
          dns_rdataset_t *rdataset, dns_diffop_t op DNS__DB_FLARG) {
1156
0
  delegdb_rootns_ctx_t *ctx = arg;
1157
1158
0
  REQUIRE(VALID_DELEGDB(ctx->db));
1159
0
  REQUIRE(DNS_DELEGSET_VALID(ctx->delegset));
1160
1161
0
  ctx->empty_file = false;
1162
1163
0
  if (op != DNS_DIFFOP_ADD) {
1164
0
    return ISC_R_NOTIMPLEMENTED;
1165
0
  }
1166
1167
0
  if (dns_name_isroot(name) && rdataset->type == dns_rdatatype_ns) {
1168
    /*
1169
     * We don't need root NS names, we're only interested in the
1170
     * glues. (Otherwise, no point of root hints...)
1171
     *
1172
     * Altough, let's fail (see below) if a non-root NS name is
1173
     * defined here, as it doesn't make any sense.
1174
     */
1175
0
    return ISC_R_SUCCESS;
1176
0
  }
1177
1178
0
  if (rdataset->type != dns_rdatatype_a &&
1179
0
      rdataset->type != dns_rdatatype_aaaa)
1180
0
  {
1181
0
    char namestr[DNS_NAME_FORMATSIZE];
1182
0
    char typestr[DNS_RDATATYPE_FORMATSIZE];
1183
1184
0
    dns_name_format(name, namestr, sizeof(namestr));
1185
0
    dns_rdatatype_format(rdataset->type, typestr, sizeof(typestr));
1186
0
    isc_log_write(DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_HINTS,
1187
0
            ISC_LOG_NOTICE,
1188
0
            "hints loading has skipped the rdataset %s/%s",
1189
0
            namestr, typestr);
1190
1191
    /*
1192
     * Do not fail the whole root hint loading, though this is
1193
     * suspicious, hence the log.
1194
     */
1195
0
    return ISC_R_SUCCESS;
1196
0
  }
1197
1198
0
  DNS_RDATASET_FOREACH(rdataset) {
1199
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
1200
0
    isc_netaddr_t addr = {};
1201
1202
0
    switch (rdataset->type) {
1203
0
    case dns_rdatatype_a: {
1204
0
      dns_rdata_in_a_t a;
1205
1206
0
      dns_rdataset_current(rdataset, &rdata);
1207
0
      dns_rdata_tostruct(&rdata, &a, NULL);
1208
0
      addr.type.in = a.in_addr;
1209
0
      addr.family = AF_INET;
1210
0
      break;
1211
0
    }
1212
0
    case dns_rdatatype_aaaa: {
1213
0
      dns_rdata_in_aaaa_t aaaa;
1214
1215
0
      dns_rdataset_current(rdataset, &rdata);
1216
0
      dns_rdata_tostruct(&rdata, &aaaa, NULL);
1217
0
      addr.type.in6 = aaaa.in6_addr;
1218
0
      addr.family = AF_INET6;
1219
0
      break;
1220
0
    }
1221
0
    default:
1222
0
      UNREACHABLE();
1223
0
    }
1224
1225
0
    dns_delegset_addaddr(ctx->delegset, ctx->deleg, &addr);
1226
0
  }
1227
1228
0
  return ISC_R_SUCCESS;
1229
0
}
1230
1231
void
1232
0
dns_delegdb_rootns_prepare(dns_delegdb_t *db, dns_rdatacallbacks_t *callbacks) {
1233
0
  REQUIRE(VALID_DELEGDB(db));
1234
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
1235
1236
0
  callbacks->update = delegdb_rootns_update;
1237
1238
0
  delegdb_rootns_ctx_t *ctx = isc_mem_cget(db->mctx, 1, sizeof(*ctx));
1239
0
  dns_delegdb_attach(db, &ctx->db);
1240
0
  dns_delegset_allocset(db, &ctx->delegset);
1241
0
  dns_delegset_allocdeleg(ctx->delegset, DNS_DELEGTYPE_NS_GLUES,
1242
0
        &ctx->deleg);
1243
0
  ctx->empty_file = true;
1244
0
  callbacks->add_private = ctx;
1245
0
}
1246
1247
isc_result_t
1248
0
dns_delegdb_rootns_commit(dns_rdatacallbacks_t *callbacks) {
1249
0
  delegdb_rootns_ctx_t *ctx = callbacks->add_private;
1250
0
  isc_result_t result;
1251
1252
0
  REQUIRE(VALID_DELEGDB(ctx->db));
1253
0
  REQUIRE(DNS_DELEGSET_VALID(ctx->delegset));
1254
1255
  /*
1256
   * The root hints file was empty, this is not really an error.
1257
   */
1258
0
  if (ctx->empty_file) {
1259
0
    INSIST(ISC_LIST_EMPTY(ctx->deleg->addresses));
1260
0
    return ISC_R_SUCCESS;
1261
0
  }
1262
1263
  /*
1264
   * No matter what there was in the root hints file, there were no
1265
   * glues at all.
1266
   */
1267
0
  if (ISC_LIST_EMPTY(ctx->deleg->addresses)) {
1268
0
    return ISC_R_FAILURE;
1269
0
  }
1270
1271
  /*
1272
   * Root NS delegset insertion can't fail. TTL of 0, so it will prime
1273
   * first time root hints are used.
1274
   */
1275
0
  result = dns_delegset_insert(ctx->db, dns_rootname, 0, ctx->delegset);
1276
0
  INSIST(result == ISC_R_SUCCESS);
1277
1278
0
  return result;
1279
0
}
1280
1281
void
1282
0
dns_delegdb_rootns_cleanup(dns_rdatacallbacks_t *callbacks) {
1283
0
  delegdb_rootns_ctx_t *ctx = callbacks->add_private;
1284
0
  dns_delegdb_t *db = NULL;
1285
1286
0
  REQUIRE(VALID_DELEGDB(ctx->db));
1287
0
  REQUIRE(DNS_DELEGSET_VALID(ctx->delegset));
1288
1289
0
  dns_delegdb_attach(ctx->db, &db);
1290
0
  dns_delegset_detach(&ctx->delegset);
1291
0
  dns_delegdb_detach(&ctx->db);
1292
0
  isc_mem_cput(db->mctx, ctx, 1, sizeof(*ctx));
1293
  callbacks->add_private = NULL;
1294
0
  dns_delegdb_detach(&db);
1295
0
}