Coverage Report

Created: 2026-01-10 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/nametree.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 <stdbool.h>
17
18
#include <isc/mem.h>
19
#include <isc/refcount.h>
20
#include <isc/result.h>
21
#include <isc/string.h>
22
#include <isc/urcu.h>
23
#include <isc/util.h>
24
25
#include <dns/fixedname.h>
26
#include <dns/nametree.h>
27
#include <dns/qp.h>
28
29
2
#define NAMETREE_MAGIC     ISC_MAGIC('N', 'T', 'r', 'e')
30
#define VALID_NAMETREE(kt) ISC_MAGIC_VALID(kt, NAMETREE_MAGIC)
31
32
struct dns_nametree {
33
  unsigned int magic;
34
  isc_mem_t *mctx;
35
  isc_refcount_t references;
36
  dns_nametree_type_t type;
37
  dns_qpmulti_t *table;
38
  char name[64];
39
};
40
41
struct dns_ntnode {
42
  isc_mem_t *mctx;
43
  isc_refcount_t references;
44
  dns_name_t name;
45
  bool set;
46
  uint8_t *bits;
47
};
48
49
/* QP trie methods */
50
static void
51
qp_attach(void *uctx, void *pval, uint32_t ival);
52
static void
53
qp_detach(void *uctx, void *pval, uint32_t ival);
54
static size_t
55
qp_makekey(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival);
56
static void
57
qp_triename(void *uctx, char *buf, size_t size);
58
59
static dns_qpmethods_t qpmethods = {
60
  qp_attach,
61
  qp_detach,
62
  qp_makekey,
63
  qp_triename,
64
};
65
66
static void
67
0
destroy_ntnode(dns_ntnode_t *node) {
68
0
  if (node->bits != NULL) {
69
0
    isc_mem_cput(node->mctx, node->bits, node->bits[0],
70
0
           sizeof(char));
71
0
  }
72
0
  dns_name_free(&node->name, node->mctx);
73
0
  isc_mem_putanddetach(&node->mctx, node, sizeof(dns_ntnode_t));
74
0
}
75
76
#if DNS_NAMETREE_TRACE
77
ISC_REFCOUNT_TRACE_IMPL(dns_ntnode, destroy_ntnode);
78
#else
79
6
ISC_REFCOUNT_IMPL(dns_ntnode, destroy_ntnode);
dns_ntnode_ref
Line
Count
Source
79
ISC_REFCOUNT_IMPL(dns_ntnode, destroy_ntnode);
dns_ntnode_unref
Line
Count
Source
79
ISC_REFCOUNT_IMPL(dns_ntnode, destroy_ntnode);
dns_ntnode_detach
Line
Count
Source
79
ISC_REFCOUNT_IMPL(dns_ntnode, destroy_ntnode);
80
6
#endif
81
6
82
6
void
83
6
dns_nametree_create(isc_mem_t *mctx, dns_nametree_type_t type, const char *name,
84
6
        dns_nametree_t **ntp) {
85
2
  dns_nametree_t *nametree = NULL;
86
87
2
  REQUIRE(ntp != NULL && *ntp == NULL);
88
89
2
  nametree = isc_mem_get(mctx, sizeof(*nametree));
90
2
  *nametree = (dns_nametree_t){
91
2
    .magic = NAMETREE_MAGIC,
92
2
    .type = type,
93
2
  };
94
2
  isc_mem_attach(mctx, &nametree->mctx);
95
2
  isc_refcount_init(&nametree->references, 1);
96
97
2
  if (name != NULL) {
98
2
    strlcpy(nametree->name, name, sizeof(nametree->name));
99
2
  }
100
101
2
  dns_qpmulti_create(mctx, &qpmethods, nametree, &nametree->table);
102
2
  *ntp = nametree;
103
2
}
104
105
static void
106
0
destroy_nametree(dns_nametree_t *nametree) {
107
0
  nametree->magic = 0;
108
109
0
  dns_qpmulti_destroy(&nametree->table);
110
111
0
  isc_mem_putanddetach(&nametree->mctx, nametree, sizeof(*nametree));
112
0
}
113
114
#if DNS_NAMETREE_TRACE
115
ISC_REFCOUNT_TRACE_IMPL(dns_nametree, destroy_nametree);
116
#else
117
0
ISC_REFCOUNT_IMPL(dns_nametree, destroy_nametree);
Unexecuted instantiation: dns_nametree_ref
Unexecuted instantiation: dns_nametree_unref
Unexecuted instantiation: dns_nametree_detach
118
0
#endif
119
0
120
0
static dns_ntnode_t *
121
2
newnode(isc_mem_t *mctx, const dns_name_t *name) {
122
2
  dns_ntnode_t *node = isc_mem_get(mctx, sizeof(*node));
123
2
  *node = (dns_ntnode_t){
124
2
    .name = DNS_NAME_INITEMPTY,
125
2
  };
126
2
  isc_mem_attach(mctx, &node->mctx);
127
2
  isc_refcount_init(&node->references, 1);
128
129
2
  dns_name_dup(name, mctx, &node->name);
130
131
2
  return node;
132
2
}
133
134
static bool
135
0
matchbit(unsigned char *bits, uint32_t val) {
136
0
  unsigned int len = val / 8 + 2;
137
0
  unsigned int mask = 1 << (val % 8);
138
139
0
  if (len <= bits[0] && (bits[len - 1] & mask) != 0) {
140
0
    return true;
141
0
  }
142
0
  return false;
143
0
}
144
145
isc_result_t
146
dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *name,
147
2
     uint32_t value) {
148
2
  isc_result_t result;
149
2
  dns_qp_t *qp = NULL;
150
2
  uint32_t size, pos, mask, count = 0;
151
2
  dns_ntnode_t *old = NULL, *new = NULL;
152
153
2
  REQUIRE(VALID_NAMETREE(nametree));
154
2
  REQUIRE(name != NULL);
155
156
2
  dns_qpmulti_write(nametree->table, &qp);
157
158
2
  switch (nametree->type) {
159
0
  case DNS_NAMETREE_BOOL:
160
0
    new = newnode(nametree->mctx, name);
161
0
    new->set = value;
162
0
    break;
163
164
2
  case DNS_NAMETREE_COUNT:
165
2
    new = newnode(nametree->mctx, name);
166
2
    new->set = true;
167
2
    result = dns_qp_deletename(qp, name, DNS_DBNAMESPACE_NORMAL,
168
2
             (void **)&old, &count);
169
2
    if (result == ISC_R_SUCCESS) {
170
0
      count += 1;
171
0
    }
172
2
    break;
173
174
0
  case DNS_NAMETREE_BITS:
175
0
    result = dns_qp_getname(qp, name, DNS_DBNAMESPACE_NORMAL,
176
0
          (void **)&old, NULL);
177
0
    if (result == ISC_R_SUCCESS && matchbit(old->bits, value)) {
178
0
      goto out;
179
0
    }
180
181
0
    size = pos = value / 8 + 2;
182
0
    mask = 1 << (value % 8);
183
184
0
    if (old != NULL && old->bits[0] > pos) {
185
0
      size = old->bits[0];
186
0
    }
187
188
0
    new = newnode(nametree->mctx, name);
189
0
    new->bits = isc_mem_cget(nametree->mctx, size, sizeof(char));
190
0
    if (result == ISC_R_SUCCESS) {
191
0
      memmove(new->bits, old->bits, old->bits[0]);
192
0
      result = dns_qp_deletename(
193
0
        qp, name, DNS_DBNAMESPACE_NORMAL, NULL, NULL);
194
0
      INSIST(result == ISC_R_SUCCESS);
195
0
    }
196
197
0
    new->bits[pos - 1] |= mask;
198
0
    new->bits[0] = size;
199
0
    break;
200
0
  default:
201
0
    UNREACHABLE();
202
2
  }
203
204
2
  result = dns_qp_insert(qp, new, count);
205
  /*
206
   * We detach the node here, so any dns_qp_deletename() will
207
   * destroy the node directly.
208
   */
209
2
  dns_ntnode_detach(&new);
210
211
2
out:
212
2
  dns_qp_compact(qp, DNS_QPGC_MAYBE);
213
2
  dns_qpmulti_commit(nametree->table, &qp);
214
2
  return result;
215
2
}
216
217
isc_result_t
218
0
dns_nametree_delete(dns_nametree_t *nametree, const dns_name_t *name) {
219
0
  isc_result_t result;
220
0
  dns_qp_t *qp = NULL;
221
0
  dns_ntnode_t *old = NULL;
222
0
  uint32_t count;
223
224
0
  REQUIRE(VALID_NAMETREE(nametree));
225
0
  REQUIRE(name != NULL);
226
227
0
  dns_qpmulti_write(nametree->table, &qp);
228
0
  result = dns_qp_deletename(qp, name, DNS_DBNAMESPACE_NORMAL,
229
0
           (void **)&old, &count);
230
0
  switch (nametree->type) {
231
0
  case DNS_NAMETREE_BOOL:
232
0
  case DNS_NAMETREE_BITS:
233
0
    break;
234
235
0
  case DNS_NAMETREE_COUNT:
236
0
    if (result == ISC_R_SUCCESS && count-- != 0) {
237
0
      dns_ntnode_t *new = newnode(nametree->mctx, name);
238
0
      new->set = true;
239
0
      result = dns_qp_insert(qp, new, count);
240
0
      INSIST(result == ISC_R_SUCCESS);
241
0
      dns_ntnode_detach(&new);
242
0
    }
243
0
    break;
244
0
  default:
245
0
    UNREACHABLE();
246
0
  }
247
0
  dns_qp_compact(qp, DNS_QPGC_MAYBE);
248
0
  dns_qpmulti_commit(nametree->table, &qp);
249
250
0
  return result;
251
0
}
252
253
isc_result_t
254
dns_nametree_find(dns_nametree_t *nametree, const dns_name_t *name,
255
0
      dns_ntnode_t **ntnodep) {
256
0
  isc_result_t result;
257
0
  dns_ntnode_t *node = NULL;
258
0
  dns_qpread_t qpr;
259
260
0
  REQUIRE(VALID_NAMETREE(nametree));
261
0
  REQUIRE(name != NULL);
262
0
  REQUIRE(ntnodep != NULL && *ntnodep == NULL);
263
264
0
  dns_qpmulti_query(nametree->table, &qpr);
265
0
  result = dns_qp_getname(&qpr, name, DNS_DBNAMESPACE_NORMAL,
266
0
        (void **)&node, NULL);
267
0
  if (result == ISC_R_SUCCESS) {
268
0
    dns_ntnode_attach(node, ntnodep);
269
0
  }
270
0
  dns_qpread_destroy(nametree->table, &qpr);
271
272
0
  return result;
273
0
}
274
275
bool
276
dns_nametree_covered(dns_nametree_t *nametree, const dns_name_t *name,
277
0
         dns_name_t *found, uint32_t bit) {
278
0
  isc_result_t result;
279
0
  dns_qpread_t qpr;
280
0
  dns_ntnode_t *node = NULL;
281
0
  bool ret = false;
282
283
0
  REQUIRE(VALID_NAMETREE(nametree));
284
285
0
  dns_qpmulti_query(nametree->table, &qpr);
286
0
  result = dns_qp_lookup(&qpr, name, DNS_DBNAMESPACE_NORMAL, NULL, NULL,
287
0
             (void **)&node, NULL);
288
0
  if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
289
0
    if (found != NULL) {
290
0
      dns_name_copy(&node->name, found);
291
0
    }
292
0
    switch (nametree->type) {
293
0
    case DNS_NAMETREE_BOOL:
294
0
      ret = node->set;
295
0
      break;
296
0
    case DNS_NAMETREE_COUNT:
297
0
      ret = true;
298
0
      break;
299
0
    case DNS_NAMETREE_BITS:
300
0
      ret = matchbit(node->bits, bit);
301
0
      break;
302
0
    }
303
0
  }
304
305
0
  dns_qpread_destroy(nametree->table, &qpr);
306
0
  return ret;
307
0
}
308
309
static void
310
qp_attach(void *uctx ISC_ATTR_UNUSED, void *pval,
311
2
    uint32_t ival ISC_ATTR_UNUSED) {
312
2
  dns_ntnode_t *ntnode = pval;
313
2
  dns_ntnode_ref(ntnode);
314
2
}
315
316
static void
317
qp_detach(void *uctx ISC_ATTR_UNUSED, void *pval,
318
0
    uint32_t ival ISC_ATTR_UNUSED) {
319
0
  dns_ntnode_t *ntnode = pval;
320
0
  dns_ntnode_detach(&ntnode);
321
0
}
322
323
static size_t
324
qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval,
325
2
     uint32_t ival ISC_ATTR_UNUSED) {
326
2
  dns_ntnode_t *ntnode = pval;
327
2
  return dns_qpkey_fromname(key, &ntnode->name, DNS_DBNAMESPACE_NORMAL);
328
2
}
329
330
static void
331
0
qp_triename(void *uctx, char *buf, size_t size) {
332
0
  dns_nametree_t *nametree = uctx;
333
0
  snprintf(buf, size, "%s nametree", nametree->name);
334
0
}