Coverage Report

Created: 2025-12-11 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/unbound/iterator/iter_fwd.c
Line
Count
Source
1
/*
2
 * iterator/iter_fwd.c - iterative resolver module forward zones.
3
 *
4
 * Copyright (c) 2007, NLnet Labs. All rights reserved.
5
 *
6
 * This software is open source.
7
 * 
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 
12
 * Redistributions of source code must retain the above copyright notice,
13
 * this list of conditions and the following disclaimer.
14
 * 
15
 * Redistributions in binary form must reproduce the above copyright notice,
16
 * this list of conditions and the following disclaimer in the documentation
17
 * and/or other materials provided with the distribution.
18
 * 
19
 * Neither the name of the NLNET LABS nor the names of its contributors may
20
 * be used to endorse or promote products derived from this software without
21
 * specific prior written permission.
22
 * 
23
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
 */
35
36
/**
37
 * \file
38
 *
39
 * This file contains functions to assist the iterator module.
40
 * Keep track of forward zones and config settings.
41
 */
42
#include "config.h"
43
#include "iterator/iter_fwd.h"
44
#include "iterator/iter_delegpt.h"
45
#include "util/log.h"
46
#include "util/config_file.h"
47
#include "util/net_help.h"
48
#include "util/data/dname.h"
49
#include "sldns/rrdef.h"
50
#include "sldns/str2wire.h"
51
52
int
53
fwd_cmp(const void* k1, const void* k2)
54
0
{
55
0
  int m;
56
0
  struct iter_forward_zone* n1 = (struct iter_forward_zone*)k1;
57
0
  struct iter_forward_zone* n2 = (struct iter_forward_zone*)k2;
58
0
  if(n1->dclass != n2->dclass) {
59
0
    if(n1->dclass < n2->dclass)
60
0
      return -1;
61
0
    return 1;
62
0
  }
63
0
  return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs, 
64
0
    &m);
65
0
}
66
67
struct iter_forwards* 
68
forwards_create(void)
69
0
{
70
0
  struct iter_forwards* fwd = (struct iter_forwards*)calloc(1,
71
0
    sizeof(struct iter_forwards));
72
0
  if(!fwd)
73
0
    return NULL;
74
0
  lock_rw_init(&fwd->lock);
75
0
  return fwd;
76
0
}
77
78
static void fwd_zone_free(struct iter_forward_zone* n)
79
0
{
80
0
  if(!n) return;
81
0
  delegpt_free_mlc(n->dp);
82
0
  free(n->name);
83
0
  free(n);
84
0
}
85
86
static void delfwdnode(rbnode_type* n, void* ATTR_UNUSED(arg))
87
0
{
88
0
  struct iter_forward_zone* node = (struct iter_forward_zone*)n;
89
0
  fwd_zone_free(node);
90
0
}
91
92
static void fwd_del_tree(struct iter_forwards* fwd)
93
0
{
94
0
  if(fwd->tree)
95
0
    traverse_postorder(fwd->tree, &delfwdnode, NULL);
96
0
  free(fwd->tree);
97
0
}
98
99
void 
100
forwards_delete(struct iter_forwards* fwd)
101
0
{
102
0
  if(!fwd) 
103
0
    return;
104
0
  lock_rw_destroy(&fwd->lock);
105
0
  fwd_del_tree(fwd);
106
0
  free(fwd);
107
0
}
108
109
/** insert info into forward structure */
110
static int
111
forwards_insert_data(struct iter_forwards* fwd, uint16_t c, uint8_t* nm, 
112
  size_t nmlen, int nmlabs, struct delegpt* dp)
113
0
{
114
0
  struct iter_forward_zone* node = (struct iter_forward_zone*)malloc(
115
0
    sizeof(struct iter_forward_zone));
116
0
  if(!node) {
117
0
    delegpt_free_mlc(dp);
118
0
    return 0;
119
0
  }
120
0
  node->node.key = node;
121
0
  node->dclass = c;
122
0
  node->name = memdup(nm, nmlen);
123
0
  if(!node->name) {
124
0
    delegpt_free_mlc(dp);
125
0
    free(node);
126
0
    return 0;
127
0
  }
128
0
  node->namelen = nmlen;
129
0
  node->namelabs = nmlabs;
130
0
  node->dp = dp;
131
0
  if(!rbtree_insert(fwd->tree, &node->node)) {
132
0
    char buf[LDNS_MAX_DOMAINLEN];
133
0
    dname_str(nm, buf);
134
0
    log_err("duplicate forward zone %s ignored.", buf);
135
0
    delegpt_free_mlc(dp);
136
0
    free(node->name);
137
0
    free(node);
138
0
  }
139
0
  return 1;
140
0
}
141
142
static struct iter_forward_zone*
143
fwd_zone_find(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
144
0
{
145
0
  struct iter_forward_zone key;
146
0
  key.node.key = &key;
147
0
  key.dclass = c;
148
0
  key.name = nm;
149
0
  key.namelabs = dname_count_size_labels(nm, &key.namelen);
150
0
  return (struct iter_forward_zone*)rbtree_search(fwd->tree, &key);
151
0
}
152
153
/** insert new info into forward structure given dp */
154
static int
155
forwards_insert(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp)
156
0
{
157
0
  return forwards_insert_data(fwd, c, dp->name, dp->namelen,
158
0
    dp->namelabs, dp);
159
0
}
160
161
/** initialise parent pointers in the tree */
162
static void
163
fwd_init_parents(struct iter_forwards* fwd)
164
0
{
165
0
  struct iter_forward_zone* node, *prev = NULL, *p;
166
0
  int m;
167
0
  RBTREE_FOR(node, struct iter_forward_zone*, fwd->tree) {
168
0
    node->parent = NULL;
169
0
    if(!prev || prev->dclass != node->dclass) {
170
0
      prev = node;
171
0
      continue;
172
0
    }
173
0
    (void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
174
0
      node->namelabs, &m); /* we know prev is smaller */
175
    /* sort order like: . com. bla.com. zwb.com. net. */
176
    /* find the previous, or parent-parent-parent */
177
0
    for(p = prev; p; p = p->parent)
178
      /* looking for name with few labels, a parent */
179
0
      if(p->namelabs <= m) {
180
        /* ==: since prev matched m, this is closest*/
181
        /* <: prev matches more, but is not a parent,
182
         * this one is a (grand)parent */
183
0
        node->parent = p;
184
0
        break;
185
0
      }
186
0
    prev = node;
187
0
  }
188
0
}
189
190
/** set zone name */
191
static struct delegpt* 
192
read_fwds_name(struct config_stub* s)
193
0
{
194
0
  struct delegpt* dp;
195
0
  uint8_t* dname;
196
0
  size_t dname_len;
197
0
  if(!s->name) {
198
0
    log_err("forward zone without a name (use name \".\" to forward everything)");
199
0
    return NULL;
200
0
  }
201
0
  dname = sldns_str2wire_dname(s->name, &dname_len);
202
0
  if(!dname) {
203
0
    log_err("cannot parse forward zone name %s", s->name);
204
0
    return NULL;
205
0
  }
206
0
  if(!(dp=delegpt_create_mlc(dname))) {
207
0
    free(dname);
208
0
    log_err("out of memory");
209
0
    return NULL;
210
0
  }
211
0
  free(dname);
212
0
  return dp;
213
0
}
214
215
/** set fwd host names */
216
static int
217
read_fwds_host(struct config_stub* s, struct delegpt* dp)
218
0
{
219
0
  struct config_strlist* p;
220
0
  uint8_t* dname;
221
0
  char* tls_auth_name;
222
0
  int port;
223
0
  for(p = s->hosts; p; p = p->next) {
224
0
    log_assert(p->str);
225
0
    dname = authextstrtodname(p->str, &port, &tls_auth_name);
226
0
    if(!dname) {
227
0
      log_err("cannot parse forward %s server name: '%s'", 
228
0
        s->name, p->str);
229
0
      return 0;
230
0
    }
231
0
    if(dname_subdomain_c(dname, dp->name)) {
232
0
      log_warn("forward-host '%s' may have a circular "
233
0
        "dependency on forward-zone '%s'",
234
0
        p->str, s->name);
235
0
    }
236
#if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
237
    if(tls_auth_name)
238
      log_err("no name verification functionality in "
239
        "ssl library, ignored name for %s", p->str);
240
#endif
241
0
    if(!delegpt_add_ns_mlc(dp, dname, 0, tls_auth_name, port)) {
242
0
      free(dname);
243
0
      log_err("out of memory");
244
0
      return 0;
245
0
    }
246
0
    free(dname);
247
0
  }
248
0
  return 1;
249
0
}
250
251
/** set fwd server addresses */
252
static int 
253
read_fwds_addr(struct config_stub* s, struct delegpt* dp)
254
0
{
255
0
  struct config_strlist* p;
256
0
  struct sockaddr_storage addr;
257
0
  socklen_t addrlen;
258
0
  char* tls_auth_name;
259
0
  for(p = s->addrs; p; p = p->next) {
260
0
    log_assert(p->str);
261
0
    if(!authextstrtoaddr(p->str, &addr, &addrlen, &tls_auth_name)) {
262
0
      log_err("cannot parse forward %s ip address: '%s'", 
263
0
        s->name, p->str);
264
0
      return 0;
265
0
    }
266
#if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
267
    if(tls_auth_name)
268
      log_err("no name verification functionality in "
269
        "ssl library, ignored name for %s", p->str);
270
#endif
271
0
    if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0,
272
0
      tls_auth_name, -1)) {
273
0
      log_err("out of memory");
274
0
      return 0;
275
0
    }
276
0
  }
277
0
  return 1;
278
0
}
279
280
/** read forwards config */
281
static int 
282
read_forwards(struct iter_forwards* fwd, struct config_file* cfg)
283
0
{
284
0
  struct config_stub* s;
285
0
  for(s = cfg->forwards; s; s = s->next) {
286
0
    struct delegpt* dp;
287
0
    if(!(dp=read_fwds_name(s)))
288
0
      return 0;
289
0
    if(!read_fwds_host(s, dp) || !read_fwds_addr(s, dp)) {
290
0
      delegpt_free_mlc(dp);
291
0
      return 0;
292
0
    }
293
    /* set flag that parent side NS information is included.
294
     * Asking a (higher up) server on the internet is not useful */
295
    /* the flag is turned off for 'forward-first' so that the
296
     * last resort will ask for parent-side NS record and thus
297
     * fallback to the internet name servers on a failure */
298
0
    dp->has_parent_side_NS = (uint8_t)!s->isfirst;
299
    /* Do not cache if set. */
300
0
    dp->no_cache = s->no_cache;
301
    /* use SSL for queries to this forwarder */
302
0
    dp->ssl_upstream = (uint8_t)s->ssl_upstream;
303
    /* use TCP for queries to this forwarder */
304
0
    dp->tcp_upstream = (uint8_t)s->tcp_upstream;
305
0
    verbose(VERB_QUERY, "Forward zone server list:");
306
0
    delegpt_log(VERB_QUERY, dp);
307
0
    if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp))
308
0
      return 0;
309
0
  }
310
0
  return 1;
311
0
}
312
313
/** insert a stub hole (if necessary) for stub name */
314
static int
315
fwd_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
316
0
{
317
0
  struct iter_forward_zone key;
318
0
  key.node.key = &key;
319
0
  key.dclass = c;
320
0
  key.name = nm;
321
0
  key.namelabs = dname_count_size_labels(key.name, &key.namelen);
322
0
  return forwards_insert_data(fwd, key.dclass, key.name,
323
0
    key.namelen, key.namelabs, NULL);
324
0
}
325
326
/** make NULL entries for stubs */
327
static int
328
make_stub_holes(struct iter_forwards* fwd, struct config_file* cfg)
329
0
{
330
0
  struct config_stub* s;
331
0
  uint8_t* dname;
332
0
  size_t dname_len;
333
0
  for(s = cfg->stubs; s; s = s->next) {
334
0
    if(!s->name) continue;
335
0
    dname = sldns_str2wire_dname(s->name, &dname_len);
336
0
    if(!dname) {
337
0
      log_err("cannot parse stub name '%s'", s->name);
338
0
      return 0;
339
0
    }
340
0
    if(fwd_zone_find(fwd, LDNS_RR_CLASS_IN, dname) != NULL) {
341
      /* Already a forward zone there. */
342
0
      free(dname);
343
0
      continue;
344
0
    }
345
0
    if(!fwd_add_stub_hole(fwd, LDNS_RR_CLASS_IN, dname)) {
346
0
      free(dname);
347
0
      log_err("out of memory");
348
0
      return 0;
349
0
    }
350
0
    free(dname);
351
0
  }
352
0
  return 1;
353
0
}
354
355
/** make NULL entries for auths */
356
static int
357
make_auth_holes(struct iter_forwards* fwd, struct config_file* cfg)
358
0
{
359
0
  struct config_auth* a;
360
0
  uint8_t* dname;
361
0
  size_t dname_len;
362
0
  for(a = cfg->auths; a; a = a->next) {
363
0
    if(!a->name) continue;
364
0
    dname = sldns_str2wire_dname(a->name, &dname_len);
365
0
    if(!dname) {
366
0
      log_err("cannot parse auth name '%s'", a->name);
367
0
      return 0;
368
0
    }
369
0
    if(fwd_zone_find(fwd, LDNS_RR_CLASS_IN, dname) != NULL) {
370
      /* Already a forward zone there. */
371
0
      free(dname);
372
0
      continue;
373
0
    }
374
0
    if(!fwd_add_stub_hole(fwd, LDNS_RR_CLASS_IN, dname)) {
375
0
      free(dname);
376
0
      log_err("out of memory");
377
0
      return 0;
378
0
    }
379
0
    free(dname);
380
0
  }
381
0
  return 1;
382
0
}
383
384
int 
385
forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg)
386
0
{
387
0
  if(fwd->tree) {
388
0
    lock_unprotect(&fwd->lock, fwd->tree);
389
0
  }
390
0
  fwd_del_tree(fwd);
391
0
  fwd->tree = rbtree_create(fwd_cmp);
392
0
  if(!fwd->tree)
393
0
    return 0;
394
0
  lock_protect(&fwd->lock, fwd->tree, sizeof(*fwd->tree));
395
396
0
  lock_rw_wrlock(&fwd->lock);
397
  /* read forward zones */
398
0
  if(!read_forwards(fwd, cfg)) {
399
0
    lock_rw_unlock(&fwd->lock);
400
0
    return 0;
401
0
  }
402
0
  if(!make_stub_holes(fwd, cfg)) {
403
0
    lock_rw_unlock(&fwd->lock);
404
0
    return 0;
405
0
  }
406
  /* TODO: Now we punch holes for auth zones as well so that in
407
   *       iterator:forward_request() we see the configured
408
   *       delegation point, but code flow/naming is hard to follow.
409
   *       Consider having a single tree with configured
410
   *       delegation points for all categories
411
   *       (stubs, forwards, auths). */
412
0
  if(!make_auth_holes(fwd, cfg)) {
413
0
    lock_rw_unlock(&fwd->lock);
414
0
    return 0;
415
0
  }
416
0
  fwd_init_parents(fwd);
417
0
  lock_rw_unlock(&fwd->lock);
418
0
  return 1;
419
0
}
420
421
struct delegpt* 
422
forwards_find(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass,
423
  int nolock)
424
0
{
425
0
  struct iter_forward_zone* res;
426
0
  struct iter_forward_zone key;
427
0
  int has_dp;
428
0
  key.node.key = &key;
429
0
  key.dclass = qclass;
430
0
  key.name = qname;
431
0
  key.namelabs = dname_count_size_labels(qname, &key.namelen);
432
  /* lock_() calls are macros that could be nothing, surround in {} */
433
0
  if(!nolock) { lock_rw_rdlock(&fwd->lock); }
434
0
  res = (struct iter_forward_zone*)rbtree_search(fwd->tree, &key);
435
0
  has_dp = res && res->dp;
436
0
  if(!has_dp && !nolock) { lock_rw_unlock(&fwd->lock); }
437
0
  return has_dp?res->dp:NULL;
438
0
}
439
440
struct delegpt* 
441
forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass,
442
  int nolock)
443
0
{
444
  /* lookup the forward zone in the tree */
445
0
  rbnode_type* res = NULL;
446
0
  struct iter_forward_zone *result;
447
0
  struct iter_forward_zone key;
448
0
  int has_dp;
449
0
  key.node.key = &key;
450
0
  key.dclass = qclass;
451
0
  key.name = qname;
452
0
  key.namelabs = dname_count_size_labels(qname, &key.namelen);
453
  /* lock_() calls are macros that could be nothing, surround in {} */
454
0
  if(!nolock) { lock_rw_rdlock(&fwd->lock); }
455
0
  if(rbtree_find_less_equal(fwd->tree, &key, &res)) {
456
    /* exact */
457
0
    result = (struct iter_forward_zone*)res;
458
0
  } else {
459
    /* smaller element (or no element) */
460
0
    int m;
461
0
    result = (struct iter_forward_zone*)res;
462
0
    if(!result || result->dclass != qclass) {
463
0
      if(!nolock) { lock_rw_unlock(&fwd->lock); }
464
0
      return NULL;
465
0
    }
466
    /* count number of labels matched */
467
0
    (void)dname_lab_cmp(result->name, result->namelabs, key.name,
468
0
      key.namelabs, &m);
469
0
    while(result) { /* go up until qname is subdomain of stub */
470
0
      if(result->namelabs <= m)
471
0
        break;
472
0
      result = result->parent;
473
0
    }
474
0
  }
475
0
  has_dp = result && result->dp;
476
0
  if(!has_dp && !nolock) { lock_rw_unlock(&fwd->lock); }
477
0
  return has_dp?result->dp:NULL;
478
0
}
479
480
struct delegpt* 
481
forwards_lookup_root(struct iter_forwards* fwd, uint16_t qclass, int nolock)
482
0
{
483
0
  uint8_t root = 0;
484
0
  return forwards_lookup(fwd, &root, qclass, nolock);
485
0
}
486
487
/* Finds next root item in forwards lookup tree.
488
 * Caller needs to handle locking of the forwards structure. */
489
static int
490
next_root_locked(struct iter_forwards* fwd, uint16_t* dclass)
491
0
{
492
0
  struct iter_forward_zone key;
493
0
  rbnode_type* n;
494
0
  struct iter_forward_zone* p;
495
0
  if(*dclass == 0) {
496
    /* first root item is first item in tree */
497
0
    n = rbtree_first(fwd->tree);
498
0
    if(n == RBTREE_NULL)
499
0
      return 0;
500
0
    p = (struct iter_forward_zone*)n;
501
0
    if(dname_is_root(p->name)) {
502
0
      *dclass = p->dclass;
503
0
      return 1;
504
0
    }
505
    /* root not first item? search for higher items */
506
0
    *dclass = p->dclass + 1;
507
0
    return next_root_locked(fwd, dclass);
508
0
  }
509
  /* find class n in tree, we may get a direct hit, or if we don't
510
   * this is the last item of the previous class so rbtree_next() takes
511
   * us to the next root (if any) */
512
0
  key.node.key = &key;
513
0
  key.name = (uint8_t*)"\000";
514
0
  key.namelen = 1;
515
0
  key.namelabs = 0;
516
0
  key.dclass = *dclass;
517
0
  n = NULL;
518
0
  if(rbtree_find_less_equal(fwd->tree, &key, &n)) {
519
    /* exact */
520
0
    return 1;
521
0
  } else {
522
    /* smaller element */
523
0
    if(!n || n == RBTREE_NULL)
524
0
      return 0; /* nothing found */
525
0
    n = rbtree_next(n);
526
0
    if(n == RBTREE_NULL)
527
0
      return 0; /* no higher */
528
0
    p = (struct iter_forward_zone*)n;
529
0
    if(dname_is_root(p->name)) {
530
0
      *dclass = p->dclass;
531
0
      return 1;
532
0
    }
533
    /* not a root node, return next higher item */
534
0
    *dclass = p->dclass+1;
535
0
    return next_root_locked(fwd, dclass);
536
0
  }
537
0
}
538
539
int
540
forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass, int nolock)
541
0
{
542
0
  int ret;
543
  /* lock_() calls are macros that could be nothing, surround in {} */
544
0
  if(!nolock) { lock_rw_rdlock(&fwd->lock); }
545
0
  ret = next_root_locked(fwd, dclass);
546
0
  if(!nolock) { lock_rw_unlock(&fwd->lock); }
547
0
  return ret;
548
0
}
549
550
size_t 
551
forwards_get_mem(struct iter_forwards* fwd)
552
0
{
553
0
  struct iter_forward_zone* p;
554
0
  size_t s;
555
0
  if(!fwd)
556
0
    return 0;
557
0
  lock_rw_rdlock(&fwd->lock);
558
0
  s = sizeof(*fwd) + sizeof(*fwd->tree);
559
0
  RBTREE_FOR(p, struct iter_forward_zone*, fwd->tree) {
560
0
    s += sizeof(*p) + p->namelen + delegpt_get_mem(p->dp);
561
0
  }
562
0
  lock_rw_unlock(&fwd->lock);
563
0
  return s;
564
0
}
565
566
int 
567
forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp,
568
  int nolock)
569
0
{
570
0
  struct iter_forward_zone *z;
571
  /* lock_() calls are macros that could be nothing, surround in {} */
572
0
  if(!nolock) { lock_rw_wrlock(&fwd->lock); }
573
0
  if((z=fwd_zone_find(fwd, c, dp->name)) != NULL) {
574
0
    (void)rbtree_delete(fwd->tree, &z->node);
575
0
    fwd_zone_free(z);
576
0
  }
577
0
  if(!forwards_insert(fwd, c, dp)) {
578
0
    if(!nolock) { lock_rw_unlock(&fwd->lock); }
579
0
    return 0;
580
0
  }
581
0
  fwd_init_parents(fwd);
582
0
  if(!nolock) { lock_rw_unlock(&fwd->lock); }
583
0
  return 1;
584
0
}
585
586
void 
587
forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm,
588
  int nolock)
589
0
{
590
0
  struct iter_forward_zone *z;
591
  /* lock_() calls are macros that could be nothing, surround in {} */
592
0
  if(!nolock) { lock_rw_wrlock(&fwd->lock); }
593
0
  if(!(z=fwd_zone_find(fwd, c, nm))) {
594
0
    if(!nolock) { lock_rw_unlock(&fwd->lock); }
595
0
    return; /* nothing to do */
596
0
  }
597
0
  (void)rbtree_delete(fwd->tree, &z->node);
598
0
  fwd_zone_free(z);
599
0
  fwd_init_parents(fwd);
600
0
  if(!nolock) { lock_rw_unlock(&fwd->lock); }
601
0
}
602
603
int
604
forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm,
605
  int nolock)
606
0
{
607
  /* lock_() calls are macros that could be nothing, surround in {} */
608
0
  if(!nolock) { lock_rw_wrlock(&fwd->lock); }
609
0
  if(fwd_zone_find(fwd, c, nm) != NULL) {
610
0
    if(!nolock) { lock_rw_unlock(&fwd->lock); }
611
0
    return 1; /* already a stub zone there */
612
0
  }
613
0
  if(!fwd_add_stub_hole(fwd, c, nm)) {
614
0
    if(!nolock) { lock_rw_unlock(&fwd->lock); }
615
0
    return 0;
616
0
  }
617
0
  fwd_init_parents(fwd);
618
0
  if(!nolock) { lock_rw_unlock(&fwd->lock); }
619
0
  return 1;
620
0
}
621
622
void
623
forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c,
624
  uint8_t* nm, int nolock)
625
0
{
626
0
  struct iter_forward_zone *z;
627
  /* lock_() calls are macros that could be nothing, surround in {} */
628
0
  if(!nolock) { lock_rw_wrlock(&fwd->lock); }
629
0
  if(!(z=fwd_zone_find(fwd, c, nm))) {
630
0
    if(!nolock) { lock_rw_unlock(&fwd->lock); }
631
0
    return; /* nothing to do */
632
0
  }
633
0
  if(z->dp != NULL) {
634
0
    if(!nolock) { lock_rw_unlock(&fwd->lock); }
635
0
    return; /* not a stub hole */
636
0
  }
637
0
  (void)rbtree_delete(fwd->tree, &z->node);
638
0
  fwd_zone_free(z);
639
0
  fwd_init_parents(fwd);
640
0
  if(!nolock) { lock_rw_unlock(&fwd->lock); }
641
0
}
642
643
void
644
forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data)
645
0
{
646
0
  rbtree_type* oldtree = fwd->tree;
647
0
  if(oldtree) {
648
0
    lock_unprotect(&fwd->lock, oldtree);
649
0
  }
650
0
  if(data->tree) {
651
0
    lock_unprotect(&data->lock, data->tree);
652
0
  }
653
0
  fwd->tree = data->tree;
654
0
  data->tree = oldtree;
655
0
  lock_protect(&fwd->lock, fwd->tree, sizeof(*fwd->tree));
656
0
  lock_protect(&data->lock, data->tree, sizeof(*data->tree));
657
0
}