Coverage Report

Created: 2026-07-16 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/catz.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
#include <stdint.h>
19
#include <stdlib.h>
20
#include <unistd.h>
21
22
#include <isc/async.h>
23
#include <isc/hex.h>
24
#include <isc/loop.h>
25
#include <isc/md.h>
26
#include <isc/mem.h>
27
#include <isc/parseint.h>
28
#include <isc/result.h>
29
#include <isc/util.h>
30
#include <isc/work.h>
31
32
#include <dns/catz.h>
33
#include <dns/dbiterator.h>
34
#include <dns/name.h>
35
#include <dns/rdatasetiter.h>
36
#include <dns/view.h>
37
#include <dns/zone.h>
38
39
0
#define DNS_CATZ_ZONE_MAGIC  ISC_MAGIC('c', 'a', 't', 'z')
40
0
#define DNS_CATZ_ZONES_MAGIC ISC_MAGIC('c', 'a', 't', 's')
41
0
#define DNS_CATZ_ENTRY_MAGIC ISC_MAGIC('c', 'a', 't', 'e')
42
0
#define DNS_CATZ_COO_MAGIC   ISC_MAGIC('c', 'a', 't', 'c')
43
44
#define DNS_CATZ_ZONE_VALID(catz)   ISC_MAGIC_VALID(catz, DNS_CATZ_ZONE_MAGIC)
45
#define DNS_CATZ_ZONES_VALID(catzs) ISC_MAGIC_VALID(catzs, DNS_CATZ_ZONES_MAGIC)
46
#define DNS_CATZ_ENTRY_VALID(entry) ISC_MAGIC_VALID(entry, DNS_CATZ_ENTRY_MAGIC)
47
#define DNS_CATZ_COO_VALID(coo)     ISC_MAGIC_VALID(coo, DNS_CATZ_COO_MAGIC)
48
49
0
#define DNS_CATZ_VERSION_UNDEFINED ((uint32_t)(-1))
50
51
/*%
52
 * Change of ownership permissions
53
 */
54
struct dns_catz_coo {
55
  unsigned int magic;
56
  dns_name_t name;
57
  isc_refcount_t references;
58
};
59
60
typedef struct coos {
61
  isc_mutex_t lock;
62
  isc_mem_t *mctx;
63
  isc_ht_t *inner;
64
} coos_t;
65
66
/*%
67
 * Single member zone in a catalog
68
 */
69
struct dns_catz_entry {
70
  unsigned int magic;
71
  dns_name_t name;
72
  dns_catz_options_t opts;
73
  isc_refcount_t references;
74
};
75
76
/*%
77
 * Catalog zone
78
 */
79
struct dns_catz_zone {
80
  unsigned int magic;
81
  isc_loop_t *loop;
82
  dns_name_t name;
83
  dns_catz_zones_t *catzs;
84
  dns_rdata_t soa;
85
  uint32_t version;
86
  /* key in entries is 'mhash', not domain name! */
87
  isc_ht_t *entries;
88
  /* key in coos is domain name */
89
  coos_t coos;
90
91
  /*
92
   * defoptions are taken from named.conf
93
   * zoneoptions are global options from zone
94
   */
95
  dns_catz_options_t defoptions;
96
  dns_catz_options_t zoneoptions;
97
  isc_time_t lastupdated;
98
99
  bool updatepending;       /* there is an update pending */
100
  bool updaterunning;       /* there is an update running */
101
  dns_db_t *db;         /* zones database */
102
  dns_dbversion_t *dbversion;   /* version we will be updating to */
103
  dns_db_t *updb;         /* zones database we're working on */
104
  dns_dbversion_t *updbversion; /* version we're working on */
105
106
  isc_timer_t *updatetimer;
107
108
  bool active;
109
  bool broken;
110
111
  isc_refcount_t references;
112
  isc_mutex_t lock;
113
};
114
115
static void
116
dns__catz_timer_cb(void *);
117
static void
118
dns__catz_timer_start(dns_catz_zone_t *catz);
119
static void
120
dns__catz_timer_stop(void *arg);
121
122
static isc_result_t
123
dns__catz_update_cb(void *data);
124
static void
125
dns__catz_done_cb(void *data, isc_result_t result);
126
127
static isc_result_t
128
catz_process_zones_entry(dns_catz_zone_t *catz, dns_rdataset_t *value,
129
       dns_label_t *mhash);
130
static isc_result_t
131
catz_process_zones_suboption(dns_catz_zone_t *catz, dns_rdataset_t *value,
132
           dns_label_t *mhash, dns_name_t *name);
133
static void
134
catz_entry_add_or_mod(dns_catz_zone_t *catz, isc_ht_t *ht, unsigned char *key,
135
          size_t keysize, dns_catz_entry_t *nentry,
136
          dns_catz_entry_t *oentry, const char *msg,
137
          const char *zname, const char *czname);
138
139
/*%
140
 * Collection of catalog zones for a view
141
 */
142
struct dns_catz_zones {
143
  unsigned int magic;
144
  isc_ht_t *zones;
145
  isc_mem_t *mctx;
146
  isc_refcount_t references;
147
  isc_mutex_t lock;
148
  dns_catz_zonemodmethods_t *zmm;
149
  dns_view_t *view;
150
  atomic_bool shuttingdown;
151
};
152
153
void
154
0
dns_catz_options_init(dns_catz_options_t *options) {
155
0
  REQUIRE(options != NULL);
156
157
0
  dns_ipkeylist_init(&options->masters);
158
159
0
  options->allow_query = NULL;
160
0
  options->allow_transfer = NULL;
161
162
0
  options->allow_query = NULL;
163
0
  options->allow_transfer = NULL;
164
165
0
  options->in_memory = false;
166
0
  options->min_update_interval = 5;
167
0
  options->zonedir = NULL;
168
0
}
169
170
void
171
0
dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx) {
172
0
  REQUIRE(options != NULL);
173
0
  REQUIRE(mctx != NULL);
174
175
0
  if (options->masters.count != 0) {
176
0
    dns_ipkeylist_clear(mctx, &options->masters);
177
0
  }
178
0
  if (options->zonedir != NULL) {
179
0
    isc_mem_free(mctx, options->zonedir);
180
0
  }
181
0
  if (options->allow_query != NULL) {
182
0
    isc_buffer_free(&options->allow_query);
183
0
  }
184
0
  if (options->allow_transfer != NULL) {
185
0
    isc_buffer_free(&options->allow_transfer);
186
0
  }
187
0
}
188
189
void
190
dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src,
191
0
          dns_catz_options_t *dst) {
192
0
  REQUIRE(mctx != NULL);
193
0
  REQUIRE(src != NULL);
194
0
  REQUIRE(dst != NULL);
195
0
  REQUIRE(dst->masters.count == 0);
196
0
  REQUIRE(dst->allow_query == NULL);
197
0
  REQUIRE(dst->allow_transfer == NULL);
198
199
0
  if (src->masters.count != 0) {
200
0
    dns_ipkeylist_copy(mctx, &src->masters, &dst->masters);
201
0
  }
202
203
0
  if (dst->zonedir != NULL) {
204
0
    isc_mem_free(mctx, dst->zonedir);
205
0
  }
206
207
0
  if (src->zonedir != NULL) {
208
0
    dst->zonedir = isc_mem_strdup(mctx, src->zonedir);
209
0
  }
210
211
0
  if (src->allow_query != NULL) {
212
0
    isc_buffer_dup(mctx, &dst->allow_query, src->allow_query);
213
0
  }
214
215
0
  if (src->allow_transfer != NULL) {
216
0
    isc_buffer_dup(mctx, &dst->allow_transfer, src->allow_transfer);
217
0
  }
218
0
}
219
220
void
221
dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults,
222
0
          dns_catz_options_t *opts) {
223
0
  REQUIRE(mctx != NULL);
224
0
  REQUIRE(defaults != NULL);
225
0
  REQUIRE(opts != NULL);
226
227
0
  if (opts->masters.count == 0 && defaults->masters.count != 0) {
228
0
    dns_ipkeylist_copy(mctx, &defaults->masters, &opts->masters);
229
0
  }
230
231
0
  if (defaults->zonedir != NULL) {
232
0
    if (opts->zonedir != NULL) {
233
0
      isc_mem_free(mctx, opts->zonedir);
234
0
    }
235
0
    opts->zonedir = isc_mem_strdup(mctx, defaults->zonedir);
236
0
  }
237
238
0
  if (opts->allow_query == NULL && defaults->allow_query != NULL) {
239
0
    isc_buffer_dup(mctx, &opts->allow_query, defaults->allow_query);
240
0
  }
241
0
  if (opts->allow_transfer == NULL && defaults->allow_transfer != NULL) {
242
0
    isc_buffer_dup(mctx, &opts->allow_transfer,
243
0
             defaults->allow_transfer);
244
0
  }
245
246
  /* This option is always taken from config, so it's always 'default' */
247
0
  opts->in_memory = defaults->in_memory;
248
0
}
249
250
static dns_catz_coo_t *
251
0
catz_coo_new(isc_mem_t *mctx, const dns_name_t *domain) {
252
0
  REQUIRE(mctx != NULL);
253
0
  REQUIRE(domain != NULL);
254
255
0
  dns_catz_coo_t *ncoo = isc_mem_get(mctx, sizeof(*ncoo));
256
0
  *ncoo = (dns_catz_coo_t){
257
0
    .magic = DNS_CATZ_COO_MAGIC,
258
0
  };
259
0
  dns_name_init(&ncoo->name);
260
0
  dns_name_dup(domain, mctx, &ncoo->name);
261
0
  isc_refcount_init(&ncoo->references, 1);
262
263
0
  return ncoo;
264
0
}
265
266
static void
267
0
catz_coo_detach(isc_mem_t *mctx, dns_catz_coo_t **coop) {
268
0
  dns_catz_coo_t *coo;
269
270
0
  REQUIRE(mctx != NULL);
271
0
  REQUIRE(coop != NULL && DNS_CATZ_COO_VALID(*coop));
272
0
  coo = *coop;
273
0
  *coop = NULL;
274
275
0
  if (isc_refcount_decrement(&coo->references) == 1) {
276
0
    coo->magic = 0;
277
0
    isc_refcount_destroy(&coo->references);
278
0
    if (dns_name_dynamic(&coo->name)) {
279
0
      dns_name_free(&coo->name, mctx);
280
0
    }
281
0
    isc_mem_put(mctx, coo, sizeof(*coo));
282
0
  }
283
0
}
284
285
static void
286
0
coos_init(coos_t *coos, isc_mem_t *mctx) {
287
0
  REQUIRE(coos != NULL);
288
0
  REQUIRE(mctx != NULL);
289
290
0
  isc_mutex_init(&coos->lock);
291
0
  isc_mem_attach(mctx, &coos->mctx);
292
0
  isc_ht_init(&coos->inner, coos->mctx, 4, ISC_HT_CASE_INSENSITIVE);
293
0
}
294
295
static isc_ht_t *
296
0
coos_replace(coos_t *coos, isc_ht_t *newinner) {
297
0
  isc_ht_t *oldinner = NULL;
298
299
0
  REQUIRE(coos != NULL);
300
0
  REQUIRE(coos->mctx != NULL);
301
302
0
  LOCK(&coos->lock);
303
0
  oldinner = coos->inner;
304
0
  coos->inner = newinner;
305
0
  UNLOCK(&coos->lock);
306
307
0
  return oldinner;
308
0
}
309
310
static isc_ht_t *
311
0
coos_take(coos_t *coos) {
312
0
  return coos_replace(coos, NULL);
313
0
}
314
315
static void
316
0
coos_destroy_table(coos_t *coos, isc_ht_t **innerp) {
317
0
  isc_ht_iter_t *iter = NULL;
318
0
  isc_ht_t *inner = NULL;
319
0
  isc_result_t result;
320
321
0
  REQUIRE(coos != NULL);
322
0
  REQUIRE(coos->mctx != NULL);
323
0
  REQUIRE(innerp != NULL);
324
325
0
  inner = *innerp;
326
0
  if (inner == NULL) {
327
0
    return;
328
0
  }
329
330
0
  isc_ht_iter_create(inner, &iter);
331
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
332
0
       result = isc_ht_iter_delcurrent_next(iter))
333
0
  {
334
0
    dns_catz_coo_t *coo = NULL;
335
336
0
    isc_ht_iter_current(iter, (void **)&coo);
337
0
    catz_coo_detach(coos->mctx, &coo);
338
0
  }
339
0
  INSIST(result == ISC_R_NOMORE);
340
0
  isc_ht_iter_destroy(&iter);
341
342
  /* The hashtable has to be empty now. */
343
0
  INSIST(isc_ht_count(inner) == 0);
344
0
  isc_ht_destroy(innerp);
345
0
}
346
347
static void
348
0
coos_destroy(coos_t *coos) {
349
0
  isc_ht_t *inner = NULL;
350
351
0
  REQUIRE(coos != NULL);
352
0
  REQUIRE(coos->mctx != NULL);
353
354
0
  inner = coos_take(coos);
355
0
  coos_destroy_table(coos, &inner);
356
0
  isc_mutex_destroy(&coos->lock);
357
0
  isc_mem_detach(&coos->mctx);
358
0
}
359
360
static void
361
0
coos_add(coos_t *coos, dns_catz_entry_t *entry, const dns_name_t *domain) {
362
0
  dns_catz_coo_t *coo = NULL;
363
0
  isc_result_t result;
364
365
0
  REQUIRE(coos != NULL);
366
0
  REQUIRE(coos->mctx != NULL);
367
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
368
0
  REQUIRE(domain != NULL);
369
370
0
  LOCK(&coos->lock);
371
0
  INSIST(coos->inner != NULL);
372
0
  result = isc_ht_find(coos->inner, entry->name.ndata, entry->name.length,
373
0
           (void **)&coo);
374
0
  if (result != ISC_R_SUCCESS) {
375
0
    coo = catz_coo_new(coos->mctx, domain);
376
0
    result = isc_ht_add(coos->inner, entry->name.ndata,
377
0
            entry->name.length, coo);
378
0
  }
379
0
  UNLOCK(&coos->lock);
380
381
0
  INSIST(result == ISC_R_SUCCESS);
382
0
}
383
384
static bool
385
0
coos_match(coos_t *coos, const dns_name_t *zone, const dns_name_t *catz) {
386
0
  dns_catz_coo_t *coo = NULL;
387
0
  bool match = false;
388
389
0
  REQUIRE(coos != NULL);
390
0
  REQUIRE(coos->mctx != NULL);
391
0
  REQUIRE(zone != NULL);
392
0
  REQUIRE(catz != NULL);
393
394
0
  LOCK(&coos->lock);
395
0
  if (coos->inner != NULL &&
396
0
      isc_ht_find(coos->inner, zone->ndata, zone->length,
397
0
      (void **)&coo) == ISC_R_SUCCESS)
398
0
  {
399
0
    match = dns_name_equal(&coo->name, catz);
400
0
  }
401
0
  UNLOCK(&coos->lock);
402
403
0
  return match;
404
0
}
405
406
dns_catz_entry_t *
407
0
dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain) {
408
0
  REQUIRE(mctx != NULL);
409
410
0
  dns_catz_entry_t *nentry = isc_mem_get(mctx, sizeof(*nentry));
411
0
  *nentry = (dns_catz_entry_t){
412
0
    .magic = DNS_CATZ_ENTRY_MAGIC,
413
0
  };
414
415
0
  dns_name_init(&nentry->name);
416
0
  if (domain != NULL) {
417
0
    dns_name_dup(domain, mctx, &nentry->name);
418
0
  }
419
420
0
  dns_catz_options_init(&nentry->opts);
421
0
  isc_refcount_init(&nentry->references, 1);
422
423
0
  return nentry;
424
0
}
425
426
dns_name_t *
427
0
dns_catz_entry_getname(dns_catz_entry_t *entry) {
428
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
429
0
  return &entry->name;
430
0
}
431
432
dns_catz_entry_t *
433
0
dns_catz_entry_copy(dns_catz_zone_t *catz, const dns_catz_entry_t *entry) {
434
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
435
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
436
437
0
  dns_catz_entry_t *nentry = dns_catz_entry_new(catz->catzs->mctx,
438
0
                  &entry->name);
439
440
0
  dns_catz_options_copy(catz->catzs->mctx, &entry->opts, &nentry->opts);
441
442
0
  return nentry;
443
0
}
444
445
void
446
0
dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp) {
447
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
448
0
  REQUIRE(entryp != NULL && *entryp == NULL);
449
450
0
  isc_refcount_increment(&entry->references);
451
0
  *entryp = entry;
452
0
}
453
454
void
455
0
dns_catz_entry_detach(dns_catz_zone_t *catz, dns_catz_entry_t **entryp) {
456
0
  dns_catz_entry_t *entry;
457
458
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
459
0
  REQUIRE(entryp != NULL && DNS_CATZ_ENTRY_VALID(*entryp));
460
0
  entry = *entryp;
461
0
  *entryp = NULL;
462
463
0
  if (isc_refcount_decrement(&entry->references) == 1) {
464
0
    isc_mem_t *mctx = catz->catzs->mctx;
465
0
    entry->magic = 0;
466
0
    isc_refcount_destroy(&entry->references);
467
0
    dns_catz_options_free(&entry->opts, mctx);
468
0
    if (dns_name_dynamic(&entry->name)) {
469
0
      dns_name_free(&entry->name, mctx);
470
0
    }
471
0
    isc_mem_put(mctx, entry, sizeof(*entry));
472
0
  }
473
0
}
474
475
bool
476
0
dns_catz_entry_validate(const dns_catz_entry_t *entry) {
477
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
478
0
  UNUSED(entry);
479
480
0
  return true;
481
0
}
482
483
bool
484
0
dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) {
485
0
  isc_region_t ra, rb;
486
487
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(ea));
488
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(eb));
489
490
0
  if (ea == eb) {
491
0
    return true;
492
0
  }
493
494
0
  if (ea->opts.masters.count != eb->opts.masters.count) {
495
0
    return false;
496
0
  }
497
498
0
  if (memcmp(ea->opts.masters.addrs, eb->opts.masters.addrs,
499
0
       ea->opts.masters.count * sizeof(isc_sockaddr_t)))
500
0
  {
501
0
    return false;
502
0
  }
503
504
0
  for (size_t i = 0; i < eb->opts.masters.count; i++) {
505
0
    if ((ea->opts.masters.keys[i] == NULL) !=
506
0
        (eb->opts.masters.keys[i] == NULL))
507
0
    {
508
0
      return false;
509
0
    }
510
0
    if (ea->opts.masters.keys[i] == NULL) {
511
0
      continue;
512
0
    }
513
0
    if (!dns_name_equal(ea->opts.masters.keys[i],
514
0
            eb->opts.masters.keys[i]))
515
0
    {
516
0
      return false;
517
0
    }
518
0
  }
519
520
0
  for (size_t i = 0; i < eb->opts.masters.count; i++) {
521
0
    if ((ea->opts.masters.tlss[i] == NULL) !=
522
0
        (eb->opts.masters.tlss[i] == NULL))
523
0
    {
524
0
      return false;
525
0
    }
526
0
    if (ea->opts.masters.tlss[i] == NULL) {
527
0
      continue;
528
0
    }
529
0
    if (!dns_name_equal(ea->opts.masters.tlss[i],
530
0
            eb->opts.masters.tlss[i]))
531
0
    {
532
0
      return false;
533
0
    }
534
0
  }
535
536
  /* If one is NULL and the other isn't, the entries don't match */
537
0
  if ((ea->opts.allow_query == NULL) != (eb->opts.allow_query == NULL)) {
538
0
    return false;
539
0
  }
540
541
  /* If one is non-NULL, then they both are */
542
0
  if (ea->opts.allow_query != NULL) {
543
0
    isc_buffer_usedregion(ea->opts.allow_query, &ra);
544
0
    isc_buffer_usedregion(eb->opts.allow_query, &rb);
545
0
    if (isc_region_compare(&ra, &rb)) {
546
0
      return false;
547
0
    }
548
0
  }
549
550
  /* Repeat the above checks with allow_transfer */
551
0
  if ((ea->opts.allow_transfer == NULL) !=
552
0
      (eb->opts.allow_transfer == NULL))
553
0
  {
554
0
    return false;
555
0
  }
556
557
0
  if (ea->opts.allow_transfer != NULL) {
558
0
    isc_buffer_usedregion(ea->opts.allow_transfer, &ra);
559
0
    isc_buffer_usedregion(eb->opts.allow_transfer, &rb);
560
0
    if (isc_region_compare(&ra, &rb)) {
561
0
      return false;
562
0
    }
563
0
  }
564
565
0
  return true;
566
0
}
567
568
dns_name_t *
569
0
dns_catz_zone_getname(dns_catz_zone_t *catz) {
570
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
571
572
0
  return &catz->name;
573
0
}
574
575
dns_catz_options_t *
576
0
dns_catz_zone_getdefoptions(dns_catz_zone_t *catz) {
577
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
578
579
0
  return &catz->defoptions;
580
0
}
581
582
void
583
0
dns_catz_zone_resetdefoptions(dns_catz_zone_t *catz) {
584
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
585
586
0
  dns_catz_options_free(&catz->defoptions, catz->catzs->mctx);
587
0
  dns_catz_options_init(&catz->defoptions);
588
0
}
589
590
/*%<
591
 * Merge 'newcatz' into 'catz', calling addzone/delzone/modzone
592
 * (from catz->catzs->zmm) for appropriate member zones.
593
 *
594
 * Requires:
595
 * \li  'catz' is a valid dns_catz_zone_t.
596
 * \li  'newcatz' is a valid dns_catz_zone_t.
597
 *
598
 */
599
static isc_result_t
600
0
dns__catz_zones_merge(dns_catz_zone_t *catz, dns_catz_zone_t *newcatz) {
601
0
  isc_result_t result;
602
0
  isc_ht_iter_t *iter1 = NULL, *iter2 = NULL;
603
0
  isc_ht_iter_t *iteradd = NULL, *itermod = NULL;
604
0
  isc_ht_t *toadd = NULL, *tomod = NULL;
605
0
  bool delcur = false;
606
0
  char czname[DNS_NAME_FORMATSIZE];
607
0
  char zname[DNS_NAME_FORMATSIZE];
608
0
  dns_catz_zoneop_fn_t addzone, modzone, delzone;
609
610
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
611
0
  REQUIRE(DNS_CATZ_ZONE_VALID(newcatz));
612
613
0
  LOCK(&catz->lock);
614
615
  /* TODO verify the new zone first! */
616
617
0
  addzone = catz->catzs->zmm->addzone;
618
0
  modzone = catz->catzs->zmm->modzone;
619
0
  delzone = catz->catzs->zmm->delzone;
620
621
  /* Copy zoneoptions from newcatz into catz. */
622
623
0
  dns_catz_options_free(&catz->zoneoptions, catz->catzs->mctx);
624
0
  dns_catz_options_copy(catz->catzs->mctx, &newcatz->zoneoptions,
625
0
            &catz->zoneoptions);
626
0
  dns_catz_options_setdefault(catz->catzs->mctx, &catz->defoptions,
627
0
            &catz->zoneoptions);
628
629
0
  dns_name_format(&catz->name, czname, DNS_NAME_FORMATSIZE);
630
631
0
  isc_ht_init(&toadd, catz->catzs->mctx, 1, ISC_HT_CASE_INSENSITIVE);
632
0
  isc_ht_init(&tomod, catz->catzs->mctx, 1, ISC_HT_CASE_INSENSITIVE);
633
0
  isc_ht_iter_create(newcatz->entries, &iter1);
634
0
  isc_ht_iter_create(catz->entries, &iter2);
635
636
  /*
637
   * We can create those iterators now, even though toadd and tomod are
638
   * empty
639
   */
640
0
  isc_ht_iter_create(toadd, &iteradd);
641
0
  isc_ht_iter_create(tomod, &itermod);
642
643
  /*
644
   * First - walk the new zone and find all nodes that are not in the
645
   * old zone, or are in both zones and are modified.
646
   */
647
0
  for (result = isc_ht_iter_first(iter1); result == ISC_R_SUCCESS;
648
0
       result = delcur ? isc_ht_iter_delcurrent_next(iter1)
649
0
           : isc_ht_iter_next(iter1))
650
0
  {
651
0
    isc_result_t find_result;
652
0
    dns_catz_zone_t *parentcatz = NULL;
653
0
    dns_catz_entry_t *nentry = NULL;
654
0
    dns_catz_entry_t *oentry = NULL;
655
0
    dns_zone_t *zone = NULL;
656
0
    unsigned char *key = NULL;
657
0
    size_t keysize;
658
0
    delcur = false;
659
660
0
    isc_ht_iter_current(iter1, (void **)&nentry);
661
0
    isc_ht_iter_currentkey(iter1, &key, &keysize);
662
663
    /*
664
     * Spurious record that came from suboption without main
665
     * record, removed.
666
     * xxxwpk: make it a separate verification phase?
667
     */
668
0
    if (nentry->name.length == 0) {
669
0
      dns_catz_entry_detach(newcatz, &nentry);
670
0
      delcur = true;
671
0
      continue;
672
0
    }
673
674
0
    dns_name_format(&nentry->name, zname, DNS_NAME_FORMATSIZE);
675
676
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
677
0
            ISC_LOG_DEBUG(3),
678
0
            "catz: iterating over '%s' from catalog '%s'",
679
0
            zname, czname);
680
0
    dns_catz_options_setdefault(catz->catzs->mctx,
681
0
              &catz->zoneoptions, &nentry->opts);
682
683
    /* Try to find the zone in the view */
684
0
    find_result = dns_view_findzone(catz->catzs->view,
685
0
            dns_catz_entry_getname(nentry),
686
0
            DNS_ZTFIND_EXACT, &zone);
687
0
    if (find_result == ISC_R_SUCCESS) {
688
0
      char pczname[DNS_NAME_FORMATSIZE];
689
0
      bool coo_match = false;
690
691
      /*
692
       * Change of ownership (coo) processing, if required
693
       */
694
0
      parentcatz = dns_zone_get_parentcatz(zone);
695
0
      if (parentcatz != NULL && parentcatz != catz) {
696
0
        coo_match = coos_match(&parentcatz->coos,
697
0
                   &nentry->name,
698
0
                   &catz->name);
699
0
      }
700
0
      if (coo_match) {
701
0
        dns_name_format(&parentcatz->name, pczname,
702
0
            DNS_NAME_FORMATSIZE);
703
0
        isc_log_write(DNS_LOGCATEGORY_GENERAL,
704
0
                DNS_LOGMODULE_CATZ,
705
0
                ISC_LOG_DEBUG(3),
706
0
                "catz: zone '%s' "
707
0
                "change of ownership from "
708
0
                "'%s' to '%s'",
709
0
                zname, pczname, czname);
710
0
        result = delzone(nentry, parentcatz,
711
0
             parentcatz->catzs->view,
712
0
             parentcatz->catzs->zmm->udata);
713
0
        isc_log_write(DNS_LOGCATEGORY_GENERAL,
714
0
                DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
715
0
                "catz: deleting zone '%s' "
716
0
                "from catalog '%s' - %s",
717
0
                zname, pczname,
718
0
                isc_result_totext(result));
719
0
      }
720
0
      dns_zone_detach(&zone);
721
0
    }
722
723
    /* Try to find the zone in the old catalog zone */
724
0
    result = isc_ht_find(catz->entries, key, (uint32_t)keysize,
725
0
             (void **)&oentry);
726
0
    if (result != ISC_R_SUCCESS) {
727
0
      if (find_result == ISC_R_SUCCESS && parentcatz == catz)
728
0
      {
729
        /*
730
         * This means that the zone's unique label
731
         * has been changed, in that case we must
732
         * reset the zone's internal state by removing
733
         * and re-adding it.
734
         *
735
         * Scheduling the addition now, the removal will
736
         * be scheduled below, when walking the old
737
         * zone for remaining entries, and then we will
738
         * perform deletions earlier than additions and
739
         * modifications.
740
         */
741
0
        isc_log_write(DNS_LOGCATEGORY_GENERAL,
742
0
                DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
743
0
                "catz: zone '%s' unique label "
744
0
                "has changed, reset state",
745
0
                zname);
746
0
      }
747
748
0
      catz_entry_add_or_mod(catz, toadd, key, keysize, nentry,
749
0
                NULL, "adding", zname, czname);
750
0
      continue;
751
0
    }
752
753
0
    if (find_result != ISC_R_SUCCESS) {
754
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
755
0
              DNS_LOGMODULE_CATZ, ISC_LOG_DEBUG(3),
756
0
              "catz: zone '%s' was expected to exist "
757
0
              "but can not be found, will be restored",
758
0
              zname);
759
0
      catz_entry_add_or_mod(catz, toadd, key, keysize, nentry,
760
0
                oentry, "adding", zname, czname);
761
0
      continue;
762
0
    }
763
764
0
    if (dns_catz_entry_cmp(oentry, nentry) != true) {
765
0
      catz_entry_add_or_mod(catz, tomod, key, keysize, nentry,
766
0
                oentry, "modifying", zname,
767
0
                czname);
768
0
      continue;
769
0
    }
770
771
    /*
772
     * Delete the old entry so that it won't accidentally be
773
     * removed as a non-existing entry below.
774
     */
775
0
    dns_catz_entry_detach(catz, &oentry);
776
0
    result = isc_ht_delete(catz->entries, key, (uint32_t)keysize);
777
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
778
0
  }
779
0
  RUNTIME_CHECK(result == ISC_R_NOMORE);
780
0
  isc_ht_iter_destroy(&iter1);
781
782
  /*
783
   * Then - walk the old zone; only deleted entries should remain.
784
   */
785
0
  for (result = isc_ht_iter_first(iter2); result == ISC_R_SUCCESS;
786
0
       result = isc_ht_iter_delcurrent_next(iter2))
787
0
  {
788
0
    dns_catz_entry_t *entry = NULL;
789
0
    isc_ht_iter_current(iter2, (void **)&entry);
790
791
0
    dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
792
0
    result = delzone(entry, catz, catz->catzs->view,
793
0
         catz->catzs->zmm->udata);
794
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
795
0
            ISC_LOG_INFO,
796
0
            "catz: deleting zone '%s' from catalog '%s' - %s",
797
0
            zname, czname, isc_result_totext(result));
798
0
    dns_catz_entry_detach(catz, &entry);
799
0
  }
800
0
  RUNTIME_CHECK(result == ISC_R_NOMORE);
801
0
  isc_ht_iter_destroy(&iter2);
802
  /* At this moment catz->entries has to be empty. */
803
0
  INSIST(isc_ht_count(catz->entries) == 0);
804
0
  isc_ht_destroy(&catz->entries);
805
806
0
  for (result = isc_ht_iter_first(iteradd); result == ISC_R_SUCCESS;
807
0
       result = isc_ht_iter_delcurrent_next(iteradd))
808
0
  {
809
0
    dns_catz_entry_t *entry = NULL;
810
0
    isc_ht_iter_current(iteradd, (void **)&entry);
811
812
0
    dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
813
0
    result = addzone(entry, catz, catz->catzs->view,
814
0
         catz->catzs->zmm->udata);
815
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
816
0
            ISC_LOG_INFO,
817
0
            "catz: adding zone '%s' from catalog "
818
0
            "'%s' - %s",
819
0
            zname, czname, isc_result_totext(result));
820
0
  }
821
822
0
  for (result = isc_ht_iter_first(itermod); result == ISC_R_SUCCESS;
823
0
       result = isc_ht_iter_delcurrent_next(itermod))
824
0
  {
825
0
    dns_catz_entry_t *entry = NULL;
826
0
    isc_ht_iter_current(itermod, (void **)&entry);
827
828
0
    dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
829
0
    result = modzone(entry, catz, catz->catzs->view,
830
0
         catz->catzs->zmm->udata);
831
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
832
0
            ISC_LOG_INFO,
833
0
            "catz: modifying zone '%s' from catalog "
834
0
            "'%s' - %s",
835
0
            zname, czname, isc_result_totext(result));
836
0
  }
837
838
0
  catz->entries = newcatz->entries;
839
0
  newcatz->entries = NULL;
840
841
  /*
842
   * We do not need to merge old coo (change of ownership) permission
843
   * records with the new ones, just replace them.
844
   */
845
0
  isc_ht_t *newcoos = coos_take(&newcatz->coos);
846
0
  isc_ht_t *oldcoos = coos_replace(&catz->coos, newcoos);
847
0
  coos_destroy_table(&catz->coos, &oldcoos);
848
849
0
  result = ISC_R_SUCCESS;
850
851
0
  isc_ht_iter_destroy(&iteradd);
852
0
  isc_ht_iter_destroy(&itermod);
853
0
  isc_ht_destroy(&toadd);
854
0
  isc_ht_destroy(&tomod);
855
856
0
  UNLOCK(&catz->lock);
857
858
0
  return result;
859
0
}
860
861
dns_catz_zones_t *
862
0
dns_catz_zones_new(isc_mem_t *mctx, dns_catz_zonemodmethods_t *zmm) {
863
0
  REQUIRE(mctx != NULL);
864
0
  REQUIRE(zmm != NULL);
865
866
0
  dns_catz_zones_t *catzs = isc_mem_get(mctx, sizeof(*catzs));
867
0
  *catzs = (dns_catz_zones_t){
868
0
    .zmm = zmm,
869
0
    .magic = DNS_CATZ_ZONES_MAGIC,
870
0
  };
871
872
0
  isc_mutex_init(&catzs->lock);
873
0
  isc_refcount_init(&catzs->references, 1);
874
0
  isc_ht_init(&catzs->zones, mctx, 4, ISC_HT_CASE_INSENSITIVE);
875
0
  isc_mem_attach(mctx, &catzs->mctx);
876
877
0
  return catzs;
878
0
}
879
880
void *
881
0
dns_catz_zones_get_udata(dns_catz_zones_t *catzs) {
882
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
883
884
0
  return catzs->zmm->udata;
885
0
}
886
887
void
888
0
dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) {
889
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
890
0
  REQUIRE(DNS_VIEW_VALID(view));
891
  /* Either it's a new one or it's being reconfigured. */
892
0
  REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name));
893
894
0
  if (catzs->view == NULL) {
895
0
    dns_view_weakattach(view, &catzs->view);
896
0
  } else if (catzs->view != view) {
897
0
    dns_view_weakdetach(&catzs->view);
898
0
    dns_view_weakattach(view, &catzs->view);
899
0
  }
900
0
}
901
902
dns_catz_zone_t *
903
0
dns_catz_zone_new(dns_catz_zones_t *catzs, const dns_name_t *name) {
904
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
905
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
906
907
0
  dns_catz_zone_t *catz = isc_mem_get(catzs->mctx, sizeof(*catz));
908
0
  *catz = (dns_catz_zone_t){ .active = true,
909
0
           .version = DNS_CATZ_VERSION_UNDEFINED,
910
0
           .magic = DNS_CATZ_ZONE_MAGIC };
911
912
0
  dns_catz_zones_attach(catzs, &catz->catzs);
913
0
  isc_mutex_init(&catz->lock);
914
0
  isc_refcount_init(&catz->references, 1);
915
0
  isc_ht_init(&catz->entries, catzs->mctx, 4, ISC_HT_CASE_INSENSITIVE);
916
0
  coos_init(&catz->coos, catzs->mctx);
917
0
  isc_time_settoepoch(&catz->lastupdated);
918
0
  dns_catz_options_init(&catz->defoptions);
919
0
  dns_catz_options_init(&catz->zoneoptions);
920
0
  dns_name_init(&catz->name);
921
0
  dns_name_dup(name, catzs->mctx, &catz->name);
922
923
0
  return catz;
924
0
}
925
926
static void
927
0
dns__catz_timer_start(dns_catz_zone_t *catz) {
928
0
  uint64_t tdiff;
929
0
  isc_interval_t interval;
930
0
  isc_time_t now;
931
932
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
933
934
0
  now = isc_time_now();
935
0
  tdiff = isc_time_microdiff(&now, &catz->lastupdated) / 1000000;
936
0
  if (tdiff < catz->defoptions.min_update_interval) {
937
0
    uint64_t defer = catz->defoptions.min_update_interval - tdiff;
938
0
    char dname[DNS_NAME_FORMATSIZE];
939
940
0
    dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
941
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
942
0
            ISC_LOG_INFO,
943
0
            "catz: %s: new zone version came "
944
0
            "too soon, deferring update for "
945
0
            "%" PRIu64 " seconds",
946
0
            dname, defer);
947
0
    isc_interval_set(&interval, (unsigned int)defer, 0);
948
0
  } else {
949
0
    isc_interval_set(&interval, 0, 0);
950
0
  }
951
952
0
  catz->loop = isc_loop();
953
954
0
  isc_timer_create(catz->loop, dns__catz_timer_cb, catz,
955
0
       &catz->updatetimer);
956
0
  isc_timer_start(catz->updatetimer, isc_timertype_once, &interval);
957
0
}
958
959
static void
960
0
dns__catz_timer_stop(void *arg) {
961
0
  dns_catz_zone_t *catz = arg;
962
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
963
964
0
  isc_timer_stop(catz->updatetimer);
965
0
  isc_timer_destroy(&catz->updatetimer);
966
0
  catz->loop = NULL;
967
968
0
  dns_catz_zone_detach(&catz);
969
0
}
970
971
isc_result_t
972
dns_catz_zone_add(dns_catz_zones_t *catzs, const dns_name_t *name,
973
0
      dns_catz_zone_t **catzp) {
974
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
975
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
976
0
  REQUIRE(catzp != NULL && *catzp == NULL);
977
978
0
  dns_catz_zone_t *catz = NULL;
979
0
  isc_result_t result;
980
0
  char zname[DNS_NAME_FORMATSIZE];
981
982
0
  dns_name_format(name, zname, DNS_NAME_FORMATSIZE);
983
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
984
0
          ISC_LOG_DEBUG(3), "catz: dns_catz_zone_add %s", zname);
985
986
0
  LOCK(&catzs->lock);
987
988
  /*
989
   * This function is called only during a (re)configuration, while
990
   * 'catzs->zones' can become NULL only during shutdown.
991
   */
992
0
  INSIST(catzs->zones != NULL);
993
0
  INSIST(!atomic_load(&catzs->shuttingdown));
994
995
0
  result = isc_ht_find(catzs->zones, name->ndata, name->length,
996
0
           (void **)&catz);
997
0
  switch (result) {
998
0
  case ISC_R_SUCCESS:
999
0
    INSIST(!catz->active);
1000
0
    catz->active = true;
1001
0
    result = ISC_R_EXISTS;
1002
0
    break;
1003
0
  case ISC_R_NOTFOUND:
1004
0
    catz = dns_catz_zone_new(catzs, name);
1005
1006
0
    result = isc_ht_add(catzs->zones, catz->name.ndata,
1007
0
            catz->name.length, catz);
1008
0
    INSIST(result == ISC_R_SUCCESS);
1009
0
    break;
1010
0
  default:
1011
0
    UNREACHABLE();
1012
0
  }
1013
1014
0
  UNLOCK(&catzs->lock);
1015
1016
0
  *catzp = catz;
1017
1018
0
  return result;
1019
0
}
1020
1021
dns_catz_zone_t *
1022
0
dns_catz_zone_get(dns_catz_zones_t *catzs, const dns_name_t *name) {
1023
0
  isc_result_t result;
1024
0
  dns_catz_zone_t *found = NULL;
1025
1026
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
1027
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1028
1029
0
  LOCK(&catzs->lock);
1030
0
  if (catzs->zones == NULL) {
1031
0
    UNLOCK(&catzs->lock);
1032
0
    return NULL;
1033
0
  }
1034
0
  result = isc_ht_find(catzs->zones, name->ndata, name->length,
1035
0
           (void **)&found);
1036
0
  UNLOCK(&catzs->lock);
1037
0
  if (result != ISC_R_SUCCESS) {
1038
0
    return NULL;
1039
0
  }
1040
1041
0
  return found;
1042
0
}
1043
1044
static void
1045
0
dns__catz_zone_shutdown(dns_catz_zone_t *catz) {
1046
  /* lock must be locked */
1047
0
  if (catz->updatetimer != NULL) {
1048
    /* Don't wait for timer to trigger for shutdown */
1049
0
    INSIST(catz->loop != NULL);
1050
1051
0
    isc_async_run(catz->loop, dns__catz_timer_stop, catz);
1052
0
  } else {
1053
0
    dns_catz_zone_detach(&catz);
1054
0
  }
1055
0
}
1056
1057
static void
1058
0
dns__catz_zone_destroy(dns_catz_zone_t *catz) {
1059
0
  isc_mem_t *mctx = catz->catzs->mctx;
1060
1061
0
  if (catz->entries != NULL) {
1062
0
    isc_ht_iter_t *iter = NULL;
1063
0
    isc_result_t result;
1064
0
    isc_ht_iter_create(catz->entries, &iter);
1065
0
    for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
1066
0
         result = isc_ht_iter_delcurrent_next(iter))
1067
0
    {
1068
0
      dns_catz_entry_t *entry = NULL;
1069
1070
0
      isc_ht_iter_current(iter, (void **)&entry);
1071
0
      dns_catz_entry_detach(catz, &entry);
1072
0
    }
1073
0
    INSIST(result == ISC_R_NOMORE);
1074
0
    isc_ht_iter_destroy(&iter);
1075
1076
    /* The hashtable has to be empty now. */
1077
0
    INSIST(isc_ht_count(catz->entries) == 0);
1078
0
    isc_ht_destroy(&catz->entries);
1079
0
  }
1080
0
  coos_destroy(&catz->coos);
1081
0
  catz->magic = 0;
1082
0
  isc_mutex_destroy(&catz->lock);
1083
1084
0
  if (catz->updatetimer != NULL) {
1085
0
    isc_timer_async_destroy(&catz->updatetimer);
1086
0
  }
1087
1088
0
  if (catz->db != NULL) {
1089
0
    if (catz->dbversion != NULL) {
1090
0
      dns_db_closeversion(catz->db, &catz->dbversion, false);
1091
0
    }
1092
0
    dns_db_updatenotify_unregister(
1093
0
      catz->db, dns_catz_dbupdate_callback, catz->catzs);
1094
0
    dns_db_detach(&catz->db);
1095
0
  }
1096
1097
0
  INSIST(!catz->updaterunning);
1098
1099
0
  dns_name_free(&catz->name, mctx);
1100
0
  dns_catz_options_free(&catz->defoptions, mctx);
1101
0
  dns_catz_options_free(&catz->zoneoptions, mctx);
1102
1103
0
  dns_catz_zones_detach(&catz->catzs);
1104
1105
0
  isc_mem_put(mctx, catz, sizeof(*catz));
1106
0
}
1107
1108
static void
1109
0
dns__catz_zones_destroy(dns_catz_zones_t *catzs) {
1110
0
  REQUIRE(atomic_load(&catzs->shuttingdown));
1111
0
  REQUIRE(catzs->zones == NULL);
1112
1113
0
  catzs->magic = 0;
1114
0
  isc_mutex_destroy(&catzs->lock);
1115
0
  if (catzs->view != NULL) {
1116
0
    dns_view_weakdetach(&catzs->view);
1117
0
  }
1118
0
  isc_mem_putanddetach(&catzs->mctx, catzs, sizeof(*catzs));
1119
0
}
1120
1121
void
1122
0
dns_catz_zones_shutdown(dns_catz_zones_t *catzs) {
1123
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
1124
1125
0
  if (!atomic_compare_exchange_strong(&catzs->shuttingdown,
1126
0
              &(bool){ false }, true))
1127
0
  {
1128
0
    return;
1129
0
  }
1130
1131
0
  LOCK(&catzs->lock);
1132
0
  if (catzs->zones != NULL) {
1133
0
    isc_ht_iter_t *iter = NULL;
1134
0
    isc_result_t result;
1135
0
    isc_ht_iter_create(catzs->zones, &iter);
1136
0
    for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;)
1137
0
    {
1138
0
      dns_catz_zone_t *catz = NULL;
1139
0
      isc_ht_iter_current(iter, (void **)&catz);
1140
0
      result = isc_ht_iter_delcurrent_next(iter);
1141
0
      dns__catz_zone_shutdown(catz);
1142
0
    }
1143
0
    INSIST(result == ISC_R_NOMORE);
1144
0
    isc_ht_iter_destroy(&iter);
1145
0
    INSIST(isc_ht_count(catzs->zones) == 0);
1146
0
    isc_ht_destroy(&catzs->zones);
1147
0
  }
1148
0
  UNLOCK(&catzs->lock);
1149
0
}
1150
1151
#ifdef DNS_CATZ_TRACE
1152
ISC_REFCOUNT_TRACE_IMPL(dns_catz_zone, dns__catz_zone_destroy);
1153
ISC_REFCOUNT_TRACE_IMPL(dns_catz_zones, dns__catz_zones_destroy);
1154
#else
1155
0
ISC_REFCOUNT_IMPL(dns_catz_zone, dns__catz_zone_destroy);
Unexecuted instantiation: dns_catz_zone_ref
Unexecuted instantiation: dns_catz_zone_unref
Unexecuted instantiation: dns_catz_zone_detach
1156
0
ISC_REFCOUNT_IMPL(dns_catz_zones, dns__catz_zones_destroy);
Unexecuted instantiation: dns_catz_zones_ref
Unexecuted instantiation: dns_catz_zones_unref
Unexecuted instantiation: dns_catz_zones_detach
1157
0
#endif
1158
0
1159
0
typedef enum {
1160
0
  CATZ_OPT_NONE,
1161
0
  CATZ_OPT_ZONES,
1162
0
  CATZ_OPT_COO,
1163
0
  CATZ_OPT_VERSION,
1164
0
  CATZ_OPT_CUSTOM_START, /* CATZ custom properties must go below this */
1165
0
  CATZ_OPT_EXT,
1166
0
  CATZ_OPT_PRIMARIES,
1167
0
  CATZ_OPT_ALLOW_QUERY,
1168
0
  CATZ_OPT_ALLOW_TRANSFER,
1169
0
} catz_opt_t;
1170
0
1171
0
static bool
1172
0
catz_opt_cmp(const dns_label_t *option, const char *opt) {
1173
0
  size_t len = strlen(opt);
1174
1175
0
  if (option->length - 1 == len &&
1176
0
      memcmp(opt, option->base + 1, len) == 0)
1177
0
  {
1178
0
    return true;
1179
0
  } else {
1180
0
    return false;
1181
0
  }
1182
0
}
1183
1184
static catz_opt_t
1185
0
catz_get_option(const dns_label_t *option) {
1186
0
  if (catz_opt_cmp(option, "ext")) {
1187
0
    return CATZ_OPT_EXT;
1188
0
  } else if (catz_opt_cmp(option, "zones")) {
1189
0
    return CATZ_OPT_ZONES;
1190
0
  } else if (catz_opt_cmp(option, "masters") ||
1191
0
       catz_opt_cmp(option, "primaries"))
1192
0
  {
1193
0
    return CATZ_OPT_PRIMARIES;
1194
0
  } else if (catz_opt_cmp(option, "allow-query")) {
1195
0
    return CATZ_OPT_ALLOW_QUERY;
1196
0
  } else if (catz_opt_cmp(option, "allow-transfer")) {
1197
0
    return CATZ_OPT_ALLOW_TRANSFER;
1198
0
  } else if (catz_opt_cmp(option, "coo")) {
1199
0
    return CATZ_OPT_COO;
1200
0
  } else if (catz_opt_cmp(option, "version")) {
1201
0
    return CATZ_OPT_VERSION;
1202
0
  } else {
1203
0
    return CATZ_OPT_NONE;
1204
0
  }
1205
0
}
1206
1207
static isc_result_t
1208
catz_process_zones(dns_catz_zone_t *catz, dns_rdataset_t *value,
1209
0
       dns_name_t *name) {
1210
0
  dns_label_t mhash;
1211
0
  dns_name_t opt;
1212
1213
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1214
0
  REQUIRE(DNS_RDATASET_VALID(value));
1215
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1216
1217
0
  uint8_t labels = dns_name_countlabels(name);
1218
1219
0
  if (labels == 0) {
1220
0
    return ISC_R_FAILURE;
1221
0
  }
1222
1223
0
  dns_name_getlabel(name, labels - 1, &mhash);
1224
1225
0
  if (labels == 1) {
1226
0
    return catz_process_zones_entry(catz, value, &mhash);
1227
0
  } else {
1228
0
    dns_name_init(&opt);
1229
0
    dns_name_split(name, 1, &opt, NULL);
1230
0
    return catz_process_zones_suboption(catz, value, &mhash, &opt);
1231
0
  }
1232
0
}
1233
1234
static isc_result_t
1235
catz_process_coo(dns_catz_zone_t *catz, dns_label_t *mhash,
1236
0
     dns_rdataset_t *value) {
1237
0
  isc_result_t result;
1238
0
  dns_rdata_t rdata;
1239
0
  dns_rdata_ptr_t ptr;
1240
0
  dns_catz_entry_t *entry = NULL;
1241
1242
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1243
0
  REQUIRE(mhash != NULL);
1244
0
  REQUIRE(DNS_RDATASET_VALID(value));
1245
1246
  /* Change of Ownership was introduced in version "2" of the schema. */
1247
0
  if (catz->version < 2) {
1248
0
    return ISC_R_FAILURE;
1249
0
  }
1250
1251
0
  if (value->type != dns_rdatatype_ptr) {
1252
0
    return ISC_R_FAILURE;
1253
0
  }
1254
1255
0
  if (dns_rdataset_count(value) != 1) {
1256
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1257
0
            ISC_LOG_WARNING,
1258
0
            "catz: 'coo' property PTR RRset contains "
1259
0
            "more than one record, which is invalid");
1260
0
    catz->broken = true;
1261
0
    return ISC_R_FAILURE;
1262
0
  }
1263
1264
0
  RETERR(dns_rdataset_first(value));
1265
1266
0
  dns_rdata_init(&rdata);
1267
0
  dns_rdataset_current(value, &rdata);
1268
1269
0
  RETERR(dns_rdata_tostruct(&rdata, &ptr, NULL));
1270
1271
0
  if (dns_name_countlabels(&ptr.ptr) == 0) {
1272
0
    CLEANUP(ISC_R_FAILURE);
1273
0
  }
1274
1275
0
  CHECK(isc_ht_find(catz->entries, mhash->base, mhash->length,
1276
0
        (void **)&entry));
1277
1278
0
  if (dns_name_countlabels(&entry->name) == 0) {
1279
0
    CLEANUP(ISC_R_FAILURE);
1280
0
  }
1281
1282
0
  coos_add(&catz->coos, entry, &ptr.ptr);
1283
1284
0
cleanup:
1285
0
  dns_rdata_freestruct(&ptr);
1286
1287
0
  return result;
1288
0
}
1289
1290
static isc_result_t
1291
catz_process_zones_entry(dns_catz_zone_t *catz, dns_rdataset_t *value,
1292
0
       dns_label_t *mhash) {
1293
0
  isc_result_t result;
1294
0
  dns_rdata_t rdata;
1295
0
  dns_rdata_ptr_t ptr;
1296
0
  dns_catz_entry_t *entry = NULL;
1297
1298
0
  if (value->type != dns_rdatatype_ptr) {
1299
0
    return ISC_R_FAILURE;
1300
0
  }
1301
1302
0
  if (dns_rdataset_count(value) != 1) {
1303
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1304
0
            ISC_LOG_WARNING,
1305
0
            "catz: member zone PTR RRset contains "
1306
0
            "more than one record, which is invalid");
1307
0
    catz->broken = true;
1308
0
    return ISC_R_FAILURE;
1309
0
  }
1310
1311
0
  RETERR(dns_rdataset_first(value));
1312
1313
0
  dns_rdata_init(&rdata);
1314
0
  dns_rdataset_current(value, &rdata);
1315
1316
0
  RETERR(dns_rdata_tostruct(&rdata, &ptr, NULL));
1317
1318
0
  result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1319
0
           (void **)&entry);
1320
0
  if (result == ISC_R_SUCCESS) {
1321
0
    if (dns_name_countlabels(&entry->name) != 0) {
1322
      /* We have a duplicate. */
1323
0
      dns_rdata_freestruct(&ptr);
1324
0
      return ISC_R_FAILURE;
1325
0
    } else {
1326
0
      dns_name_dup(&ptr.ptr, catz->catzs->mctx, &entry->name);
1327
0
    }
1328
0
  } else {
1329
0
    entry = dns_catz_entry_new(catz->catzs->mctx, &ptr.ptr);
1330
1331
0
    result = isc_ht_add(catz->entries, mhash->base, mhash->length,
1332
0
            entry);
1333
0
  }
1334
0
  INSIST(result == ISC_R_SUCCESS);
1335
1336
0
  dns_rdata_freestruct(&ptr);
1337
1338
0
  return ISC_R_SUCCESS;
1339
0
}
1340
1341
static isc_result_t
1342
0
catz_process_version(dns_catz_zone_t *catz, dns_rdataset_t *value) {
1343
0
  isc_result_t result;
1344
0
  dns_rdata_t rdata;
1345
0
  dns_rdata_txt_t rdatatxt;
1346
0
  dns_rdata_txt_string_t rdatastr;
1347
0
  uint32_t tversion;
1348
0
  char t[16];
1349
1350
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1351
0
  REQUIRE(DNS_RDATASET_VALID(value));
1352
1353
0
  if (value->type != dns_rdatatype_txt) {
1354
0
    return ISC_R_FAILURE;
1355
0
  }
1356
1357
0
  if (dns_rdataset_count(value) != 1) {
1358
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1359
0
            ISC_LOG_WARNING,
1360
0
            "catz: 'version' property TXT RRset contains "
1361
0
            "more than one record, which is invalid");
1362
0
    catz->broken = true;
1363
0
    return ISC_R_FAILURE;
1364
0
  }
1365
1366
0
  RETERR(dns_rdataset_first(value));
1367
1368
0
  dns_rdata_init(&rdata);
1369
0
  dns_rdataset_current(value, &rdata);
1370
1371
0
  RETERR(dns_rdata_tostruct(&rdata, &rdatatxt, NULL));
1372
1373
0
  CHECK(dns_rdata_txt_first(&rdatatxt));
1374
1375
0
  CHECK(dns_rdata_txt_current(&rdatatxt, &rdatastr));
1376
1377
0
  result = dns_rdata_txt_next(&rdatatxt);
1378
0
  if (result != ISC_R_NOMORE) {
1379
0
    CLEANUP(ISC_R_FAILURE);
1380
0
  }
1381
0
  if (rdatastr.length > 15) {
1382
0
    CLEANUP(ISC_R_BADNUMBER);
1383
0
  }
1384
0
  memmove(t, rdatastr.data, rdatastr.length);
1385
0
  t[rdatastr.length] = 0;
1386
0
  CHECK(isc_parse_uint32(&tversion, t, 10));
1387
0
  catz->version = tversion;
1388
0
  result = ISC_R_SUCCESS;
1389
1390
0
cleanup:
1391
0
  dns_rdata_freestruct(&rdatatxt);
1392
0
  if (result != ISC_R_SUCCESS) {
1393
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1394
0
            ISC_LOG_WARNING,
1395
0
            "catz: invalid record for the catalog "
1396
0
            "zone version property");
1397
0
    catz->broken = true;
1398
0
  }
1399
0
  return result;
1400
0
}
1401
1402
static isc_result_t
1403
catz_process_primaries(dns_catz_zone_t *catz, dns_ipkeylist_t *ipkl,
1404
0
           dns_rdataset_t *value, dns_name_t *name) {
1405
0
  isc_result_t result;
1406
0
  dns_rdata_in_a_t rdata_a;
1407
0
  dns_rdata_in_aaaa_t rdata_aaaa;
1408
0
  dns_rdata_txt_t rdata_txt;
1409
0
  dns_rdata_txt_string_t rdatastr;
1410
0
  dns_name_t *keyname = NULL;
1411
0
  isc_mem_t *mctx;
1412
0
  char keycbuf[DNS_NAME_FORMATSIZE];
1413
0
  isc_buffer_t keybuf;
1414
0
  unsigned int rcount;
1415
1416
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1417
0
  REQUIRE(ipkl != NULL);
1418
0
  REQUIRE(DNS_RDATASET_VALID(value));
1419
0
  REQUIRE(dns_rdataset_isassociated(value));
1420
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1421
1422
0
  mctx = catz->catzs->mctx;
1423
0
  memset(&rdata_a, 0, sizeof(rdata_a));
1424
0
  memset(&rdata_aaaa, 0, sizeof(rdata_aaaa));
1425
0
  memset(&rdata_txt, 0, sizeof(rdata_txt));
1426
0
  isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf));
1427
1428
  /*
1429
   * We have three possibilities here:
1430
   * - either empty name and IN A/IN AAAA record
1431
   * - label and IN A/IN AAAA
1432
   * - label and IN TXT - TSIG key name
1433
   */
1434
0
  if (name->length != 0) {
1435
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
1436
0
    isc_sockaddr_t sockaddr;
1437
0
    size_t i;
1438
1439
    /*
1440
     * We're pre-preparing the data once, we'll put it into
1441
     * the right spot in the primaries array once we find it.
1442
     */
1443
0
    result = dns_rdataset_first(value);
1444
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1445
0
    dns_rdataset_current(value, &rdata);
1446
0
    switch (value->type) {
1447
0
    case dns_rdatatype_a:
1448
0
      result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
1449
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1450
0
      isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0);
1451
0
      dns_rdata_freestruct(&rdata_a);
1452
0
      break;
1453
0
    case dns_rdatatype_aaaa:
1454
0
      result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
1455
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1456
0
      isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr,
1457
0
               0);
1458
0
      dns_rdata_freestruct(&rdata_aaaa);
1459
0
      break;
1460
0
    case dns_rdatatype_txt:
1461
0
      result = dns_rdata_tostruct(&rdata, &rdata_txt, NULL);
1462
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1463
1464
0
      result = dns_rdata_txt_first(&rdata_txt);
1465
0
      if (result != ISC_R_SUCCESS) {
1466
0
        dns_rdata_freestruct(&rdata_txt);
1467
0
        return result;
1468
0
      }
1469
1470
0
      result = dns_rdata_txt_current(&rdata_txt, &rdatastr);
1471
0
      if (result != ISC_R_SUCCESS) {
1472
0
        dns_rdata_freestruct(&rdata_txt);
1473
0
        return result;
1474
0
      }
1475
1476
0
      result = dns_rdata_txt_next(&rdata_txt);
1477
0
      if (result != ISC_R_NOMORE) {
1478
0
        dns_rdata_freestruct(&rdata_txt);
1479
0
        return ISC_R_FAILURE;
1480
0
      }
1481
1482
      /* rdatastr.length < DNS_NAME_MAXTEXT */
1483
0
      keyname = isc_mem_get(mctx, sizeof(*keyname));
1484
0
      dns_name_init(keyname);
1485
0
      memmove(keycbuf, rdatastr.data, rdatastr.length);
1486
0
      keycbuf[rdatastr.length] = 0;
1487
0
      dns_rdata_freestruct(&rdata_txt);
1488
0
      result = dns_name_fromstring(keyname, keycbuf,
1489
0
                 dns_rootname, 0, mctx);
1490
0
      if (result != ISC_R_SUCCESS) {
1491
0
        isc_mem_put(mctx, keyname, sizeof(*keyname));
1492
0
        return result;
1493
0
      }
1494
0
      break;
1495
0
    default:
1496
0
      return ISC_R_FAILURE;
1497
0
    }
1498
1499
    /*
1500
     * We have to find the appropriate labeled record in
1501
     * primaries if it exists.  In the common case we'll
1502
     * have no more than 3-4 records here, so no optimization.
1503
     */
1504
0
    for (i = 0; i < ipkl->count; i++) {
1505
0
      if (ipkl->labels[i] != NULL &&
1506
0
          !dns_name_compare(name, ipkl->labels[i]))
1507
0
      {
1508
0
        break;
1509
0
      }
1510
0
    }
1511
1512
0
    if (i < ipkl->count) { /* we have this record already */
1513
0
      if (value->type == dns_rdatatype_txt) {
1514
0
        if (ipkl->keys[i] != NULL) {
1515
0
          if (dns_name_dynamic(ipkl->keys[i])) {
1516
0
            dns_name_free(ipkl->keys[i],
1517
0
                    mctx);
1518
0
          }
1519
0
          isc_mem_put(mctx, ipkl->keys[i],
1520
0
                sizeof(*ipkl->keys[i]));
1521
0
        }
1522
0
        ipkl->keys[i] = keyname;
1523
0
      } else { /* A/AAAA */
1524
0
        memmove(&ipkl->addrs[i], &sockaddr,
1525
0
          sizeof(sockaddr));
1526
0
      }
1527
0
    } else {
1528
0
      dns_ipkeylist_resize(mctx, ipkl, i + 1);
1529
1530
0
      ipkl->labels[i] = isc_mem_get(mctx,
1531
0
                  sizeof(*ipkl->labels[0]));
1532
0
      dns_name_init(ipkl->labels[i]);
1533
0
      dns_name_dup(name, mctx, ipkl->labels[i]);
1534
1535
0
      if (value->type == dns_rdatatype_txt) {
1536
0
        ipkl->keys[i] = keyname;
1537
0
      } else { /* A/AAAA */
1538
0
        memmove(&ipkl->addrs[i], &sockaddr,
1539
0
          sizeof(sockaddr));
1540
0
      }
1541
0
      ipkl->count++;
1542
0
    }
1543
0
    return ISC_R_SUCCESS;
1544
0
  }
1545
  /* else - 'simple' case - without labels */
1546
1547
0
  if (!dns_rdatatype_isaddr(value->type)) {
1548
0
    return ISC_R_FAILURE;
1549
0
  }
1550
1551
0
  rcount = dns_rdataset_count(value) + ipkl->count;
1552
1553
0
  dns_ipkeylist_resize(mctx, ipkl, rcount);
1554
1555
0
  DNS_RDATASET_FOREACH(value) {
1556
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
1557
0
    dns_rdataset_current(value, &rdata);
1558
    /*
1559
     * port 0 == take the default
1560
     */
1561
0
    if (value->type == dns_rdatatype_a) {
1562
0
      result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
1563
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1564
0
      isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
1565
0
              &rdata_a.in_addr, 0);
1566
0
      dns_rdata_freestruct(&rdata_a);
1567
0
    } else {
1568
0
      result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
1569
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1570
0
      isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count],
1571
0
               &rdata_aaaa.in6_addr, 0);
1572
0
      dns_rdata_freestruct(&rdata_aaaa);
1573
0
    }
1574
0
    ipkl->keys[ipkl->count] = NULL;
1575
0
    ipkl->labels[ipkl->count] = NULL;
1576
0
    ipkl->count++;
1577
0
  }
1578
0
  return ISC_R_SUCCESS;
1579
0
}
1580
1581
static isc_result_t
1582
catz_process_apl(dns_catz_zone_t *catz, isc_buffer_t **aclbp,
1583
0
     dns_rdataset_t *value) {
1584
0
  REQUIRE(DNS_RDATASET_VALID(value));
1585
0
  REQUIRE(dns_rdataset_isassociated(value));
1586
1587
0
  if (value->type != dns_rdatatype_apl) {
1588
0
    return ISC_R_FAILURE;
1589
0
  }
1590
1591
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1592
0
  REQUIRE(aclbp != NULL);
1593
0
  REQUIRE(*aclbp == NULL);
1594
1595
0
  isc_result_t result = ISC_R_SUCCESS;
1596
0
  dns_rdata_t rdata;
1597
0
  dns_rdata_in_apl_t rdata_apl;
1598
0
  dns_rdata_apl_ent_t apl_ent;
1599
0
  isc_netaddr_t addr;
1600
0
  isc_buffer_t *aclb = NULL;
1601
0
  unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */
1602
1603
0
  if (dns_rdataset_count(value) > 1) {
1604
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1605
0
            ISC_LOG_WARNING,
1606
0
            "catz: more than one APL entry for member zone, "
1607
0
            "result is undefined");
1608
0
  }
1609
0
  result = dns_rdataset_first(value);
1610
0
  RUNTIME_CHECK(result == ISC_R_SUCCESS);
1611
0
  dns_rdata_init(&rdata);
1612
0
  dns_rdataset_current(value, &rdata);
1613
0
  RETERR(dns_rdata_tostruct(&rdata, &rdata_apl, catz->catzs->mctx));
1614
0
  isc_buffer_allocate(catz->catzs->mctx, &aclb, 16);
1615
0
  for (result = dns_rdata_apl_first(&rdata_apl); result == ISC_R_SUCCESS;
1616
0
       result = dns_rdata_apl_next(&rdata_apl))
1617
0
  {
1618
0
    result = dns_rdata_apl_current(&rdata_apl, &apl_ent);
1619
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1620
0
    memset(buf, 0, sizeof(buf));
1621
0
    if (apl_ent.data != NULL && apl_ent.length > 0) {
1622
0
      memmove(buf, apl_ent.data, apl_ent.length);
1623
0
    }
1624
0
    if (apl_ent.family == 1) {
1625
0
      isc_netaddr_fromin(&addr, (struct in_addr *)buf);
1626
0
    } else if (apl_ent.family == 2) {
1627
0
      isc_netaddr_fromin6(&addr, (struct in6_addr *)buf);
1628
0
    } else {
1629
0
      continue; /* xxxwpk log it or simply ignore? */
1630
0
    }
1631
0
    if (apl_ent.negative) {
1632
0
      isc_buffer_putuint8(aclb, '!');
1633
0
    }
1634
0
    isc_buffer_reserve(aclb, INET6_ADDRSTRLEN);
1635
0
    result = isc_netaddr_totext(&addr, aclb);
1636
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1637
0
    if ((apl_ent.family == 1 && apl_ent.prefix < 32) ||
1638
0
        (apl_ent.family == 2 && apl_ent.prefix < 128))
1639
0
    {
1640
0
      isc_buffer_putuint8(aclb, '/');
1641
0
      isc_buffer_printf(aclb, "%" PRId8, apl_ent.prefix);
1642
0
    }
1643
0
    isc_buffer_putstr(aclb, "; ");
1644
0
  }
1645
0
  if (result == ISC_R_NOMORE) {
1646
0
    result = ISC_R_SUCCESS;
1647
0
  } else {
1648
0
    goto cleanup;
1649
0
  }
1650
0
  *aclbp = aclb;
1651
0
  aclb = NULL;
1652
0
cleanup:
1653
0
  if (aclb != NULL) {
1654
0
    isc_buffer_free(&aclb);
1655
0
  }
1656
0
  dns_rdata_freestruct(&rdata_apl);
1657
0
  return result;
1658
0
}
1659
1660
static isc_result_t
1661
catz_process_zones_suboption(dns_catz_zone_t *catz, dns_rdataset_t *value,
1662
0
           dns_label_t *mhash, dns_name_t *name) {
1663
0
  isc_result_t result;
1664
0
  dns_catz_entry_t *entry = NULL;
1665
0
  dns_label_t option;
1666
0
  dns_name_t prefix;
1667
0
  catz_opt_t opt;
1668
0
  unsigned int suffix_labels = 1;
1669
1670
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1671
0
  REQUIRE(mhash != NULL);
1672
0
  REQUIRE(DNS_RDATASET_VALID(value));
1673
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1674
1675
0
  uint8_t labels = dns_name_countlabels(name);
1676
1677
0
  if (labels < 1) {
1678
0
    return ISC_R_FAILURE;
1679
0
  }
1680
0
  dns_name_getlabel(name, labels - 1, &option);
1681
0
  opt = catz_get_option(&option);
1682
1683
  /*
1684
   * The custom properties in version 2 schema must be placed under the
1685
   * "ext" label.
1686
   */
1687
0
  if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) {
1688
0
    if (opt != CATZ_OPT_EXT || labels < 2) {
1689
0
      return ISC_R_FAILURE;
1690
0
    }
1691
0
    suffix_labels++;
1692
0
    dns_name_getlabel(name, labels - 2, &option);
1693
0
    opt = catz_get_option(&option);
1694
0
  }
1695
1696
  /*
1697
   * We're adding this entry now, in case the option is invalid we'll get
1698
   * rid of it in verification phase.
1699
   */
1700
0
  result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1701
0
           (void **)&entry);
1702
0
  if (result != ISC_R_SUCCESS) {
1703
0
    entry = dns_catz_entry_new(catz->catzs->mctx, NULL);
1704
0
    result = isc_ht_add(catz->entries, mhash->base, mhash->length,
1705
0
            entry);
1706
0
  }
1707
0
  INSIST(result == ISC_R_SUCCESS);
1708
1709
0
  dns_name_init(&prefix);
1710
0
  dns_name_split(name, suffix_labels, &prefix, NULL);
1711
0
  switch (opt) {
1712
0
  case CATZ_OPT_COO:
1713
0
    return catz_process_coo(catz, mhash, value);
1714
0
  case CATZ_OPT_PRIMARIES:
1715
0
    return catz_process_primaries(catz, &entry->opts.masters, value,
1716
0
                &prefix);
1717
0
  case CATZ_OPT_ALLOW_QUERY:;
1718
0
    if (prefix.length != 0) {
1719
0
      return ISC_R_FAILURE;
1720
0
    }
1721
0
    return catz_process_apl(catz, &entry->opts.allow_query, value);
1722
0
  case CATZ_OPT_ALLOW_TRANSFER:
1723
0
    if (prefix.length != 0) {
1724
0
      return ISC_R_FAILURE;
1725
0
    }
1726
0
    return catz_process_apl(catz, &entry->opts.allow_transfer,
1727
0
          value);
1728
0
  default:
1729
0
    return ISC_R_FAILURE;
1730
0
  }
1731
1732
0
  return ISC_R_FAILURE;
1733
0
}
1734
1735
static void
1736
catz_entry_add_or_mod(dns_catz_zone_t *catz, isc_ht_t *ht, unsigned char *key,
1737
          size_t keysize, dns_catz_entry_t *nentry,
1738
          dns_catz_entry_t *oentry, const char *msg,
1739
0
          const char *zname, const char *czname) {
1740
0
  isc_result_t result = isc_ht_add(ht, key, (uint32_t)keysize, nentry);
1741
1742
0
  if (result != ISC_R_SUCCESS) {
1743
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1744
0
            ISC_LOG_ERROR,
1745
0
            "catz: error %s zone '%s' from catalog '%s' - %s",
1746
0
            msg, zname, czname, isc_result_totext(result));
1747
0
  }
1748
0
  if (oentry != NULL) {
1749
0
    dns_catz_entry_detach(catz, &oentry);
1750
0
    result = isc_ht_delete(catz->entries, key, (uint32_t)keysize);
1751
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1752
0
  }
1753
0
}
1754
1755
static isc_result_t
1756
catz_process_value(dns_catz_zone_t *catz, dns_name_t *name,
1757
0
       dns_rdataset_t *rdataset) {
1758
0
  dns_label_t option;
1759
0
  dns_name_t prefix;
1760
0
  catz_opt_t opt;
1761
0
  unsigned int suffix_labels = 1;
1762
1763
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1764
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1765
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
1766
1767
0
  uint8_t labels = dns_name_countlabels(name);
1768
1769
0
  if (labels < 1) {
1770
0
    return ISC_R_FAILURE;
1771
0
  }
1772
0
  dns_name_getlabel(name, labels - 1, &option);
1773
0
  opt = catz_get_option(&option);
1774
1775
  /*
1776
   * The custom properties in version 2 schema must be placed under the
1777
   * "ext" label.
1778
   */
1779
0
  if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) {
1780
0
    if (opt != CATZ_OPT_EXT || labels < 2) {
1781
0
      return ISC_R_FAILURE;
1782
0
    }
1783
0
    suffix_labels++;
1784
0
    dns_name_getlabel(name, labels - 2, &option);
1785
0
    opt = catz_get_option(&option);
1786
0
  }
1787
1788
0
  dns_name_init(&prefix);
1789
0
  dns_name_split(name, suffix_labels, &prefix, NULL);
1790
1791
0
  switch (opt) {
1792
0
  case CATZ_OPT_ZONES:
1793
0
    return catz_process_zones(catz, rdataset, &prefix);
1794
0
  case CATZ_OPT_PRIMARIES:
1795
0
    return catz_process_primaries(catz, &catz->zoneoptions.masters,
1796
0
                rdataset, &prefix);
1797
0
  case CATZ_OPT_ALLOW_QUERY:
1798
0
    if (prefix.length != 0) {
1799
0
      return ISC_R_FAILURE;
1800
0
    }
1801
0
    return catz_process_apl(catz, &catz->zoneoptions.allow_query,
1802
0
          rdataset);
1803
0
  case CATZ_OPT_ALLOW_TRANSFER:
1804
0
    if (prefix.length != 0) {
1805
0
      return ISC_R_FAILURE;
1806
0
    }
1807
0
    return catz_process_apl(catz, &catz->zoneoptions.allow_transfer,
1808
0
          rdataset);
1809
0
  case CATZ_OPT_VERSION:
1810
0
    if (prefix.length != 0) {
1811
0
      return ISC_R_FAILURE;
1812
0
    }
1813
0
    return catz_process_version(catz, rdataset);
1814
0
  default:
1815
0
    return ISC_R_FAILURE;
1816
0
  }
1817
0
}
1818
1819
/*%<
1820
 * Process a single rdataset from a catalog zone 'catz' update, src_name is the
1821
 * record name.
1822
 *
1823
 * Requires:
1824
 * \li  'catz' is a valid dns_catz_zone_t.
1825
 * \li  'src_name' is a valid dns_name_t.
1826
 * \li  'rdataset' is valid rdataset.
1827
 */
1828
static isc_result_t
1829
dns__catz_update_process(dns_catz_zone_t *catz, const dns_name_t *src_name,
1830
0
       dns_rdataset_t *rdataset) {
1831
0
  isc_result_t result;
1832
0
  int order;
1833
0
  unsigned int nlabels;
1834
0
  dns_namereln_t nrres;
1835
0
  dns_rdata_t rdata = DNS_RDATA_INIT;
1836
0
  dns_rdata_soa_t soa;
1837
0
  dns_name_t prefix;
1838
1839
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1840
0
  REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC));
1841
1842
0
  if (rdataset->rdclass != dns_rdataclass_in) {
1843
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1844
0
            ISC_LOG_ERROR,
1845
0
            "catz: RR found which has a non-IN class");
1846
0
    catz->broken = true;
1847
0
    return ISC_R_FAILURE;
1848
0
  }
1849
1850
0
  nrres = dns_name_fullcompare(src_name, &catz->name, &order, &nlabels);
1851
0
  if (nrres == dns_namereln_equal) {
1852
0
    if (rdataset->type == dns_rdatatype_soa) {
1853
0
      RETERR(dns_rdataset_first(rdataset));
1854
1855
0
      dns_rdataset_current(rdataset, &rdata);
1856
0
      result = dns_rdata_tostruct(&rdata, &soa, NULL);
1857
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1858
1859
      /*
1860
       * xxxwpk TODO do we want to save something from SOA?
1861
       */
1862
0
      dns_rdata_freestruct(&soa);
1863
0
      return result;
1864
0
    } else if (rdataset->type == dns_rdatatype_ns) {
1865
0
      return ISC_R_SUCCESS;
1866
0
    } else {
1867
0
      return ISC_R_UNEXPECTED;
1868
0
    }
1869
0
  } else if (nrres != dns_namereln_subdomain) {
1870
0
    return ISC_R_UNEXPECTED;
1871
0
  }
1872
1873
0
  uint8_t labels = dns_name_countlabels(&catz->name);
1874
0
  dns_name_init(&prefix);
1875
0
  dns_name_split(src_name, labels, &prefix, NULL);
1876
0
  result = catz_process_value(catz, &prefix, rdataset);
1877
1878
0
  return result;
1879
0
}
1880
1881
static isc_result_t
1882
digest2hex(unsigned char *digest, unsigned int digestlen, char *hash,
1883
0
     size_t hashlen) {
1884
0
  unsigned int i;
1885
0
  for (i = 0; i < digestlen; i++) {
1886
0
    size_t left = hashlen - i * 2;
1887
0
    int ret = snprintf(hash + i * 2, left, "%02x", digest[i]);
1888
0
    if (ret < 0 || (size_t)ret >= left) {
1889
0
      return ISC_R_NOSPACE;
1890
0
    }
1891
0
  }
1892
0
  return ISC_R_SUCCESS;
1893
0
}
1894
1895
isc_result_t
1896
dns_catz_generate_masterfilename(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
1897
0
         isc_buffer_t **buffer) {
1898
0
  isc_buffer_t *tbuf = NULL;
1899
0
  isc_region_t r;
1900
0
  isc_result_t result;
1901
0
  size_t rlen;
1902
0
  bool special = false;
1903
1904
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1905
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1906
0
  REQUIRE(buffer != NULL && *buffer != NULL);
1907
1908
0
  isc_buffer_allocate(catz->catzs->mctx, &tbuf,
1909
0
          strlen(catz->catzs->view->name) +
1910
0
            2 * DNS_NAME_FORMATSIZE + 2);
1911
1912
0
  isc_buffer_putstr(tbuf, catz->catzs->view->name);
1913
0
  isc_buffer_putstr(tbuf, "_");
1914
0
  CHECK(dns_name_totext(&catz->name, DNS_NAME_OMITFINALDOT, tbuf));
1915
1916
0
  isc_buffer_putstr(tbuf, "_");
1917
0
  CHECK(dns_name_totext(&entry->name, DNS_NAME_OMITFINALDOT, tbuf));
1918
1919
  /*
1920
   * Search for slash and other special characters in the view and
1921
   * zone names.  Add a null terminator so we can use strpbrk(), then
1922
   * remove it.
1923
   */
1924
0
  isc_buffer_putuint8(tbuf, 0);
1925
0
  if (strpbrk(isc_buffer_base(tbuf), "\\/:%$") != NULL) {
1926
0
    special = true;
1927
0
  }
1928
0
  isc_buffer_subtract(tbuf, 1);
1929
1930
  /* __catz__<digest>.db */
1931
0
  rlen = (ISC_SHA256_DIGESTLENGTH * 2 + 1) + 12;
1932
1933
  /* optionally prepend with <zonedir>/ */
1934
0
  if (entry->opts.zonedir != NULL) {
1935
0
    rlen += strlen(entry->opts.zonedir) + 1;
1936
0
  }
1937
1938
0
  CHECK(isc_buffer_reserve(*buffer, (unsigned int)rlen));
1939
1940
0
  if (entry->opts.zonedir != NULL) {
1941
0
    isc_buffer_putstr(*buffer, entry->opts.zonedir);
1942
0
    isc_buffer_putstr(*buffer, "/");
1943
0
  }
1944
1945
0
  isc_buffer_usedregion(tbuf, &r);
1946
0
  isc_buffer_putstr(*buffer, "__catz__");
1947
0
  if (special || tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) {
1948
0
    unsigned char digest[ISC_MAX_MD_SIZE];
1949
0
    unsigned int digestlen;
1950
1951
    /* we can do that because digest string < 2 * DNS_NAME */
1952
0
    CHECK(isc_md(ISC_MD_SHA256, r.base, r.length, digest,
1953
0
           &digestlen));
1954
0
    CHECK(digest2hex(digest, digestlen, (char *)r.base,
1955
0
         ISC_SHA256_DIGESTLENGTH * 2 + 1));
1956
0
    isc_buffer_putstr(*buffer, (char *)r.base);
1957
0
  } else {
1958
0
    isc_buffer_copyregion(*buffer, &r);
1959
0
  }
1960
1961
0
  isc_buffer_putstr(*buffer, ".db");
1962
0
  result = ISC_R_SUCCESS;
1963
1964
0
cleanup:
1965
0
  isc_buffer_free(&tbuf);
1966
0
  return result;
1967
0
}
1968
1969
/*
1970
 * We have to generate a text buffer with regular zone config:
1971
 * zone "foo.bar" {
1972
 *  type secondary;
1973
 *  primaries { ip1 port port1; ip2 port port2; };
1974
 * }
1975
 */
1976
isc_result_t
1977
dns_catz_generate_zonecfg(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
1978
0
        isc_buffer_t **buf) {
1979
0
  isc_buffer_t *buffer = NULL;
1980
0
  isc_region_t region;
1981
0
  isc_result_t result;
1982
0
  uint32_t i;
1983
0
  isc_netaddr_t netaddr;
1984
0
  char pbuf[sizeof("65535")]; /* used for port number */
1985
0
  char namebuf[DNS_NAME_FORMATSIZE];
1986
1987
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1988
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1989
0
  REQUIRE(buf != NULL && *buf == NULL);
1990
1991
  /*
1992
   * The buffer will be reallocated if something won't fit,
1993
   * ISC_BUFFER_INCR seems like a good start.
1994
   */
1995
0
  isc_buffer_allocate(catz->catzs->mctx, &buffer, ISC_BUFFER_INCR);
1996
1997
0
  isc_buffer_putstr(buffer, "zone \"");
1998
0
  dns_name_format(&entry->name, namebuf, sizeof(namebuf));
1999
0
  isc_buffer_putstr(buffer, namebuf);
2000
0
  isc_buffer_putstr(buffer, "\" { type secondary; primaries");
2001
2002
0
  isc_buffer_putstr(buffer, " { ");
2003
0
  for (i = 0; i < entry->opts.masters.count; i++) {
2004
    /*
2005
     * Every primary must have an IP address assigned.
2006
     */
2007
0
    switch (entry->opts.masters.addrs[i].type.sa.sa_family) {
2008
0
    case AF_INET:
2009
0
    case AF_INET6:
2010
0
      break;
2011
0
    default:
2012
0
      dns_name_format(&entry->name, namebuf, sizeof(namebuf));
2013
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2014
0
              DNS_LOGMODULE_CATZ, ISC_LOG_ERROR,
2015
0
              "catz: zone '%s' uses an invalid primary "
2016
0
              "(no IP address assigned)",
2017
0
              namebuf);
2018
0
      CLEANUP(ISC_R_FAILURE);
2019
0
    }
2020
0
    isc_netaddr_fromsockaddr(&netaddr,
2021
0
           &entry->opts.masters.addrs[i]);
2022
0
    isc_buffer_reserve(buffer, INET6_ADDRSTRLEN);
2023
0
    result = isc_netaddr_totext(&netaddr, buffer);
2024
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
2025
2026
0
    isc_buffer_putstr(buffer, " port ");
2027
0
    snprintf(pbuf, sizeof(pbuf), "%u",
2028
0
       isc_sockaddr_getport(&entry->opts.masters.addrs[i]));
2029
0
    isc_buffer_putstr(buffer, pbuf);
2030
2031
0
    if (entry->opts.masters.keys[i] != NULL) {
2032
0
      isc_buffer_putstr(buffer, " key ");
2033
0
      dns_name_format(entry->opts.masters.keys[i], namebuf,
2034
0
          sizeof(namebuf));
2035
0
      isc_buffer_putstr(buffer, namebuf);
2036
0
    }
2037
2038
0
    if (entry->opts.masters.tlss[i] != NULL) {
2039
0
      isc_buffer_putstr(buffer, " tls ");
2040
0
      dns_name_format(entry->opts.masters.tlss[i], namebuf,
2041
0
          sizeof(namebuf));
2042
0
      isc_buffer_putstr(buffer, namebuf);
2043
0
    }
2044
0
    isc_buffer_putstr(buffer, "; ");
2045
0
  }
2046
0
  isc_buffer_putstr(buffer, "}; ");
2047
0
  if (!entry->opts.in_memory) {
2048
0
    isc_buffer_putstr(buffer, "file \"");
2049
0
    CHECK(dns_catz_generate_masterfilename(catz, entry, &buffer));
2050
0
    isc_buffer_putstr(buffer, "\"; ");
2051
0
  }
2052
0
  if (entry->opts.allow_query != NULL) {
2053
0
    isc_buffer_putstr(buffer, "allow-query { ");
2054
0
    isc_buffer_usedregion(entry->opts.allow_query, &region);
2055
0
    isc_buffer_copyregion(buffer, &region);
2056
0
    isc_buffer_putstr(buffer, "}; ");
2057
0
  }
2058
0
  if (entry->opts.allow_transfer != NULL) {
2059
0
    isc_buffer_putstr(buffer, "allow-transfer { ");
2060
0
    isc_buffer_usedregion(entry->opts.allow_transfer, &region);
2061
0
    isc_buffer_copyregion(buffer, &region);
2062
0
    isc_buffer_putstr(buffer, "}; ");
2063
0
  }
2064
2065
0
  isc_buffer_putstr(buffer, "};");
2066
0
  *buf = buffer;
2067
2068
0
  return ISC_R_SUCCESS;
2069
2070
0
cleanup:
2071
0
  isc_buffer_free(&buffer);
2072
0
  return result;
2073
0
}
2074
2075
static void
2076
0
dns__catz_timer_cb(void *arg) {
2077
0
  char domain[DNS_NAME_FORMATSIZE];
2078
0
  dns_catz_zone_t *catz = (dns_catz_zone_t *)arg;
2079
2080
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2081
2082
0
  if (atomic_load(&catz->catzs->shuttingdown)) {
2083
0
    return;
2084
0
  }
2085
2086
0
  LOCK(&catz->catzs->lock);
2087
2088
0
  INSIST(DNS_DB_VALID(catz->db));
2089
0
  INSIST(catz->dbversion != NULL);
2090
0
  INSIST(catz->updb == NULL);
2091
0
  INSIST(catz->updbversion == NULL);
2092
2093
0
  catz->updatepending = false;
2094
0
  catz->updaterunning = true;
2095
2096
0
  dns_name_format(&catz->name, domain, DNS_NAME_FORMATSIZE);
2097
2098
0
  if (!catz->active) {
2099
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2100
0
            ISC_LOG_INFO,
2101
0
            "catz: %s: no longer active, reload is canceled",
2102
0
            domain);
2103
0
    catz->updaterunning = false;
2104
0
    goto exit;
2105
0
  }
2106
2107
0
  dns_db_attach(catz->db, &catz->updb);
2108
0
  catz->updbversion = catz->dbversion;
2109
0
  catz->dbversion = NULL;
2110
2111
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
2112
0
          "catz: %s: reload start", domain);
2113
2114
0
  dns_catz_zone_ref(catz);
2115
0
  isc_work_enqueue(catz->loop, ISC_WORKLANE_SLOW, dns__catz_update_cb,
2116
0
       dns__catz_done_cb, catz);
2117
2118
0
exit:
2119
0
  isc_timer_destroy(&catz->updatetimer);
2120
0
  catz->loop = NULL;
2121
2122
0
  catz->lastupdated = isc_time_now();
2123
2124
0
  UNLOCK(&catz->catzs->lock);
2125
0
}
2126
2127
isc_result_t
2128
0
dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
2129
0
  dns_catz_zones_t *catzs = NULL;
2130
0
  dns_catz_zone_t *catz = NULL;
2131
0
  isc_result_t result = ISC_R_SUCCESS;
2132
0
  isc_region_t r;
2133
2134
0
  REQUIRE(DNS_DB_VALID(db));
2135
0
  REQUIRE(DNS_CATZ_ZONES_VALID(fn_arg));
2136
0
  catzs = (dns_catz_zones_t *)fn_arg;
2137
2138
0
  if (atomic_load(&catzs->shuttingdown)) {
2139
0
    return ISC_R_SHUTTINGDOWN;
2140
0
  }
2141
2142
0
  dns_name_toregion(&db->origin, &r);
2143
2144
0
  LOCK(&catzs->lock);
2145
0
  if (catzs->zones == NULL) {
2146
0
    CLEANUP(ISC_R_SHUTTINGDOWN);
2147
0
  }
2148
0
  CHECK(isc_ht_find(catzs->zones, r.base, r.length, (void **)&catz));
2149
2150
  /* New zone came as AXFR */
2151
0
  if (catz->db != NULL && catz->db != db) {
2152
    /* Old db cleanup. */
2153
0
    if (catz->dbversion != NULL) {
2154
0
      dns_db_closeversion(catz->db, &catz->dbversion, false);
2155
0
    }
2156
0
    dns_db_updatenotify_unregister(
2157
0
      catz->db, dns_catz_dbupdate_callback, catz->catzs);
2158
0
    dns_db_detach(&catz->db);
2159
0
  }
2160
0
  if (catz->db == NULL) {
2161
    /* New db registration. */
2162
0
    dns_db_attach(db, &catz->db);
2163
0
    dns_db_updatenotify_register(db, dns_catz_dbupdate_callback,
2164
0
               catz->catzs);
2165
0
  }
2166
2167
0
  if (!catz->updatepending && !catz->updaterunning) {
2168
0
    catz->updatepending = true;
2169
0
    dns_db_currentversion(db, &catz->dbversion);
2170
0
    dns__catz_timer_start(catz);
2171
0
  } else {
2172
0
    char dname[DNS_NAME_FORMATSIZE];
2173
2174
0
    catz->updatepending = true;
2175
0
    dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
2176
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2177
0
            ISC_LOG_DEBUG(3),
2178
0
            "catz: %s: update already queued or running",
2179
0
            dname);
2180
0
    if (catz->dbversion != NULL) {
2181
0
      dns_db_closeversion(catz->db, &catz->dbversion, false);
2182
0
    }
2183
0
    dns_db_currentversion(catz->db, &catz->dbversion);
2184
0
  }
2185
2186
0
cleanup:
2187
0
  UNLOCK(&catzs->lock);
2188
2189
0
  return result;
2190
0
}
2191
2192
void
2193
0
dns_catz_dbupdate_unregister(dns_db_t *db, dns_catz_zones_t *catzs) {
2194
0
  REQUIRE(DNS_DB_VALID(db));
2195
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2196
2197
0
  dns_db_updatenotify_unregister(db, dns_catz_dbupdate_callback, catzs);
2198
0
}
2199
2200
void
2201
0
dns_catz_dbupdate_register(dns_db_t *db, dns_catz_zones_t *catzs) {
2202
0
  REQUIRE(DNS_DB_VALID(db));
2203
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2204
2205
0
  dns_db_updatenotify_register(db, dns_catz_dbupdate_callback, catzs);
2206
0
}
2207
2208
static bool
2209
0
catz_rdatatype_is_processable(const dns_rdatatype_t type) {
2210
0
  return !dns_rdatatype_isdnssec(type) && type != dns_rdatatype_cds &&
2211
0
         type != dns_rdatatype_cdnskey && type != dns_rdatatype_zonemd;
2212
0
}
2213
2214
/*
2215
 * Process an updated database for a catalog zone.
2216
 * It creates a new catz, iterates over database to fill it with content, and
2217
 * then merges new catz into old catz.
2218
 */
2219
static isc_result_t
2220
0
dns__catz_update_cb(void *data) {
2221
0
  dns_catz_zone_t *catz = (dns_catz_zone_t *)data;
2222
0
  dns_db_t *updb = NULL;
2223
0
  dns_catz_zones_t *catzs = NULL;
2224
0
  dns_catz_zone_t *oldcatz = NULL, *newcatz = NULL;
2225
0
  isc_result_t result;
2226
0
  isc_region_t r;
2227
0
  dns_dbnode_t *node = NULL;
2228
0
  const dns_dbnode_t *vers_node = NULL;
2229
0
  dns_dbiterator_t *updbit = NULL;
2230
0
  dns_fixedname_t fixname;
2231
0
  dns_name_t *name = NULL;
2232
0
  dns_rdatasetiter_t *rdsiter = NULL;
2233
0
  dns_rdataset_t rdataset;
2234
0
  char bname[DNS_NAME_FORMATSIZE];
2235
0
  char cname[DNS_NAME_FORMATSIZE];
2236
0
  bool is_vers_processed = false;
2237
0
  bool is_active;
2238
0
  uint32_t vers;
2239
0
  uint32_t catz_vers;
2240
2241
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2242
0
  REQUIRE(DNS_DB_VALID(catz->updb));
2243
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catz->catzs));
2244
2245
0
  updb = catz->updb;
2246
0
  catzs = catz->catzs;
2247
2248
0
  if (atomic_load(&catzs->shuttingdown)) {
2249
0
    return ISC_R_SHUTTINGDOWN;
2250
0
  }
2251
2252
0
  dns_name_format(&updb->origin, bname, DNS_NAME_FORMATSIZE);
2253
2254
  /*
2255
   * Create a new catz in the same context as current catz.
2256
   */
2257
0
  dns_name_toregion(&updb->origin, &r);
2258
0
  LOCK(&catzs->lock);
2259
0
  if (catzs->zones == NULL) {
2260
0
    UNLOCK(&catzs->lock);
2261
0
    return ISC_R_SHUTTINGDOWN;
2262
0
  }
2263
0
  result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldcatz);
2264
0
  is_active = (result == ISC_R_SUCCESS && oldcatz->active);
2265
0
  UNLOCK(&catzs->lock);
2266
0
  if (result != ISC_R_SUCCESS) {
2267
    /* This can happen if we remove the zone in the meantime. */
2268
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2269
0
            ISC_LOG_ERROR, "catz: zone '%s' not in config",
2270
0
            bname);
2271
0
    return result;
2272
0
  }
2273
2274
0
  if (!is_active) {
2275
    /* This can happen during a reconfiguration. */
2276
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2277
0
            ISC_LOG_INFO,
2278
0
            "catz: zone '%s' is no longer active", bname);
2279
0
    return ISC_R_CANCELED;
2280
0
  }
2281
2282
0
  result = dns_db_getsoaserial(updb, oldcatz->updbversion, &vers);
2283
0
  if (result != ISC_R_SUCCESS) {
2284
    /* A zone without SOA record?!? */
2285
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2286
0
            ISC_LOG_ERROR,
2287
0
            "catz: zone '%s' has no SOA record (%s)", bname,
2288
0
            isc_result_totext(result));
2289
0
    return result;
2290
0
  }
2291
2292
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
2293
0
          "catz: updating catalog zone '%s' with serial %" PRIu32,
2294
0
          bname, vers);
2295
2296
0
  result = dns_db_createiterator(updb, DNS_DB_NONSEC3, &updbit);
2297
0
  if (result != ISC_R_SUCCESS) {
2298
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2299
0
            ISC_LOG_ERROR,
2300
0
            "catz: failed to create DB iterator - %s",
2301
0
            isc_result_totext(result));
2302
0
    return result;
2303
0
  }
2304
2305
0
  name = dns_fixedname_initname(&fixname);
2306
2307
  /*
2308
   * Take the version record to process first, because the other
2309
   * records might be processed differently depending on the version of
2310
   * the catalog zone's schema.
2311
   */
2312
0
  result = dns_name_fromstring(name, "version", &updb->origin, 0, NULL);
2313
0
  if (result != ISC_R_SUCCESS) {
2314
0
    dns_dbiterator_destroy(&updbit);
2315
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2316
0
            ISC_LOG_ERROR,
2317
0
            "catz: failed to create name from string - %s",
2318
0
            isc_result_totext(result));
2319
0
    return result;
2320
0
  }
2321
2322
0
  result = dns_dbiterator_seek(updbit, name);
2323
0
  if (result != ISC_R_SUCCESS) {
2324
0
    dns_dbiterator_destroy(&updbit);
2325
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2326
0
            ISC_LOG_ERROR,
2327
0
            "catz: zone '%s' has no 'version' record (%s) "
2328
0
            "and will not be processed",
2329
0
            bname, isc_result_totext(result));
2330
2331
0
    return result;
2332
0
  }
2333
2334
0
  newcatz = dns_catz_zone_new(catzs, &updb->origin);
2335
0
  name = dns_fixedname_initname(&fixname);
2336
2337
  /*
2338
   * Iterate over database to fill the new zone.
2339
   */
2340
0
  while (result == ISC_R_SUCCESS) {
2341
0
    if (atomic_load(&catzs->shuttingdown)) {
2342
0
      result = ISC_R_SHUTTINGDOWN;
2343
0
      break;
2344
0
    }
2345
2346
0
    result = dns_dbiterator_current(updbit, &node, name);
2347
0
    if (result != ISC_R_SUCCESS) {
2348
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2349
0
              DNS_LOGMODULE_CATZ, ISC_LOG_ERROR,
2350
0
              "catz: failed to get db iterator - %s",
2351
0
              isc_result_totext(result));
2352
0
      break;
2353
0
    }
2354
2355
0
    result = dns_dbiterator_pause(updbit);
2356
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
2357
2358
0
    if (!is_vers_processed) {
2359
      /* Keep the version node to skip it later in the loop */
2360
0
      vers_node = node;
2361
0
    } else if (node == vers_node) {
2362
      /* Skip the already processed version node */
2363
0
      dns_db_detachnode(&node);
2364
0
      result = dns_dbiterator_next(updbit);
2365
0
      continue;
2366
0
    }
2367
2368
0
    result = dns_db_allrdatasets(updb, node, oldcatz->updbversion,
2369
0
               0, 0, &rdsiter);
2370
0
    if (result != ISC_R_SUCCESS) {
2371
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2372
0
              DNS_LOGMODULE_CATZ, ISC_LOG_ERROR,
2373
0
              "catz: failed to fetch rrdatasets - %s",
2374
0
              isc_result_totext(result));
2375
0
      dns_db_detachnode(&node);
2376
0
      break;
2377
0
    }
2378
2379
0
    dns_rdataset_init(&rdataset);
2380
0
    DNS_RDATASETITER_FOREACH(rdsiter) {
2381
0
      dns_rdatasetiter_current(rdsiter, &rdataset);
2382
2383
      /*
2384
       * Skip processing DNSSEC-related and ZONEMD types,
2385
       * because we are not interested in them in the context
2386
       * of a catalog zone, and processing them will fail
2387
       * and produce an unnecessary warning message.
2388
       */
2389
0
      if (!catz_rdatatype_is_processable(rdataset.type)) {
2390
0
        dns_rdataset_disassociate(&rdataset);
2391
0
        continue;
2392
0
      }
2393
2394
0
      result = dns__catz_update_process(newcatz, name,
2395
0
                &rdataset);
2396
0
      if (result != ISC_R_SUCCESS) {
2397
0
        char typebuf[DNS_RDATATYPE_FORMATSIZE];
2398
0
        char classbuf[DNS_RDATACLASS_FORMATSIZE];
2399
2400
0
        dns_name_format(name, cname,
2401
0
            DNS_NAME_FORMATSIZE);
2402
0
        dns_rdataclass_format(rdataset.rdclass,
2403
0
                  classbuf,
2404
0
                  sizeof(classbuf));
2405
0
        dns_rdatatype_format(rdataset.type, typebuf,
2406
0
                 sizeof(typebuf));
2407
0
        isc_log_write(DNS_LOGCATEGORY_GENERAL,
2408
0
                DNS_LOGMODULE_CATZ,
2409
0
                ISC_LOG_WARNING,
2410
0
                "catz: invalid record in catalog "
2411
0
                "zone - %s %s %s (%s) - ignoring",
2412
0
                cname, classbuf, typebuf,
2413
0
                isc_result_totext(result));
2414
0
      }
2415
2416
0
      dns_rdataset_disassociate(&rdataset);
2417
0
    }
2418
2419
0
    dns_rdatasetiter_destroy(&rdsiter);
2420
2421
0
    dns_db_detachnode(&node);
2422
2423
0
    if (!is_vers_processed) {
2424
0
      is_vers_processed = true;
2425
0
      result = dns_dbiterator_first(updbit);
2426
0
    } else {
2427
0
      result = dns_dbiterator_next(updbit);
2428
0
    }
2429
0
  }
2430
2431
0
  dns_dbiterator_destroy(&updbit);
2432
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2433
0
          ISC_LOG_DEBUG(3),
2434
0
          "catz: update_from_db: iteration finished: %s",
2435
0
          isc_result_totext(result));
2436
2437
  /*
2438
   * Check catalog zone version compatibilites.
2439
   */
2440
0
  catz_vers = (newcatz->version == DNS_CATZ_VERSION_UNDEFINED)
2441
0
          ? oldcatz->version
2442
0
          : newcatz->version;
2443
0
  if (catz_vers == DNS_CATZ_VERSION_UNDEFINED) {
2444
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2445
0
            ISC_LOG_WARNING,
2446
0
            "catz: zone '%s' version is not set", bname);
2447
0
    newcatz->broken = true;
2448
0
  } else if (catz_vers != 1 && catz_vers != 2) {
2449
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2450
0
            ISC_LOG_WARNING,
2451
0
            "catz: zone '%s' unsupported version "
2452
0
            "'%" PRIu32 "'",
2453
0
            bname, catz_vers);
2454
0
    newcatz->broken = true;
2455
0
  } else {
2456
0
    oldcatz->version = catz_vers;
2457
0
  }
2458
2459
0
  if (newcatz->broken) {
2460
0
    dns_name_format(name, cname, DNS_NAME_FORMATSIZE);
2461
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2462
0
            ISC_LOG_ERROR,
2463
0
            "catz: new catalog zone '%s' is broken and "
2464
0
            "will not be processed",
2465
0
            bname);
2466
0
    dns_catz_zone_detach(&newcatz);
2467
0
    return ISC_R_FAILURE;
2468
0
  }
2469
2470
  /*
2471
   * Finally merge new zone into old zone.
2472
   */
2473
0
  result = dns__catz_zones_merge(oldcatz, newcatz);
2474
0
  dns_catz_zone_detach(&newcatz);
2475
0
  if (result != ISC_R_SUCCESS) {
2476
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2477
0
            ISC_LOG_ERROR, "catz: failed merging zones: %s",
2478
0
            isc_result_totext(result));
2479
2480
0
    return result;
2481
0
  }
2482
2483
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2484
0
          ISC_LOG_DEBUG(3),
2485
0
          "catz: update_from_db: new zone merged");
2486
2487
0
  return result;
2488
0
}
2489
2490
static void
2491
0
dns__catz_done_cb(void *data, isc_result_t result) {
2492
0
  dns_catz_zone_t *catz = (dns_catz_zone_t *)data;
2493
0
  char dname[DNS_NAME_FORMATSIZE];
2494
2495
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2496
2497
0
  LOCK(&catz->catzs->lock);
2498
0
  catz->updaterunning = false;
2499
2500
0
  dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
2501
2502
0
  if (catz->updatepending && !atomic_load(&catz->catzs->shuttingdown)) {
2503
    /* Restart the timer */
2504
0
    dns__catz_timer_start(catz);
2505
0
  }
2506
2507
0
  dns_db_closeversion(catz->updb, &catz->updbversion, false);
2508
0
  dns_db_detach(&catz->updb);
2509
2510
0
  UNLOCK(&catz->catzs->lock);
2511
2512
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
2513
0
          "catz: %s: reload done: %s", dname,
2514
0
          isc_result_totext(result));
2515
2516
0
  dns_catz_zone_unref(catz);
2517
0
}
2518
2519
void
2520
0
dns_catz_prereconfig(dns_catz_zones_t *catzs) {
2521
0
  isc_result_t result;
2522
0
  isc_ht_iter_t *iter = NULL;
2523
2524
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2525
2526
0
  LOCK(&catzs->lock);
2527
0
  isc_ht_iter_create(catzs->zones, &iter);
2528
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
2529
0
       result = isc_ht_iter_next(iter))
2530
0
  {
2531
0
    dns_catz_zone_t *catz = NULL;
2532
0
    isc_ht_iter_current(iter, (void **)&catz);
2533
0
    catz->active = false;
2534
0
  }
2535
0
  UNLOCK(&catzs->lock);
2536
0
  INSIST(result == ISC_R_NOMORE);
2537
0
  isc_ht_iter_destroy(&iter);
2538
0
}
2539
2540
void
2541
0
dns_catz_postreconfig(dns_catz_zones_t *catzs) {
2542
0
  isc_result_t result;
2543
0
  dns_catz_zone_t *newcatz = NULL;
2544
0
  isc_ht_iter_t *iter = NULL;
2545
2546
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2547
2548
0
  LOCK(&catzs->lock);
2549
0
  isc_ht_iter_create(catzs->zones, &iter);
2550
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) {
2551
0
    dns_catz_zone_t *catz = NULL;
2552
2553
0
    isc_ht_iter_current(iter, (void **)&catz);
2554
0
    if (!catz->active) {
2555
0
      char cname[DNS_NAME_FORMATSIZE];
2556
0
      dns_name_format(&catz->name, cname,
2557
0
          DNS_NAME_FORMATSIZE);
2558
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2559
0
              DNS_LOGMODULE_CATZ, ISC_LOG_WARNING,
2560
0
              "catz: removing catalog zone %s", cname);
2561
2562
      /*
2563
       * Merge the old zone with an empty one to remove
2564
       * all members.
2565
       */
2566
0
      newcatz = dns_catz_zone_new(catzs, &catz->name);
2567
0
      dns__catz_zones_merge(catz, newcatz);
2568
0
      dns_catz_zone_detach(&newcatz);
2569
2570
      /* Make sure that we have an empty catalog zone. */
2571
0
      INSIST(isc_ht_count(catz->entries) == 0);
2572
0
      result = isc_ht_iter_delcurrent_next(iter);
2573
0
      dns_catz_zone_detach(&catz);
2574
0
    } else {
2575
0
      result = isc_ht_iter_next(iter);
2576
0
    }
2577
0
  }
2578
0
  UNLOCK(&catzs->lock);
2579
0
  RUNTIME_CHECK(result == ISC_R_NOMORE);
2580
0
  isc_ht_iter_destroy(&iter);
2581
0
}
2582
2583
void
2584
0
dns_catz_zone_prereconfig(dns_catz_zone_t *catz) {
2585
0
  LOCK(&catz->lock);
2586
0
}
2587
2588
void
2589
0
dns_catz_zone_postreconfig(dns_catz_zone_t *catz) {
2590
0
  UNLOCK(&catz->lock);
2591
0
}
2592
2593
void
2594
dns_catz_zone_for_each_entry2(dns_catz_zone_t *catz, dns_catz_entry_cb2 cb,
2595
0
            void *arg1, void *arg2) {
2596
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2597
2598
0
  isc_ht_iter_t *iter = NULL;
2599
0
  isc_result_t result;
2600
2601
0
  LOCK(&catz->catzs->lock);
2602
0
  isc_ht_iter_create(catz->entries, &iter);
2603
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
2604
0
       result = isc_ht_iter_next(iter))
2605
0
  {
2606
0
    dns_catz_entry_t *entry = NULL;
2607
2608
0
    isc_ht_iter_current(iter, (void **)&entry);
2609
0
    cb(entry, arg1, arg2);
2610
0
  }
2611
0
  isc_ht_iter_destroy(&iter);
2612
0
  UNLOCK(&catz->catzs->lock);
2613
0
}