Coverage Report

Created: 2026-10-17 06:52

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
            &nentry->name, DNS_ZTFIND_EXACT,
686
0
            &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
    /*
754
     * If member name in the old entry is not equal to the member
755
     * name in the new entry, it means the unique label is the same
756
     * but the member name has been changed. We should add the
757
     * member from the new entry while making sure to pass 'oentry'
758
     * as NULL below, so it remains in the hash table and the member
759
     * zone is properly deleted.
760
     */
761
0
    if (!dns_name_equal(&oentry->name, &nentry->name)) {
762
0
      catz_entry_add_or_mod(catz, toadd, key, keysize, nentry,
763
0
                NULL, "adding", zname, czname);
764
0
      continue;
765
0
    }
766
767
0
    if (find_result != ISC_R_SUCCESS) {
768
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
769
0
              DNS_LOGMODULE_CATZ, ISC_LOG_DEBUG(3),
770
0
              "catz: zone '%s' was expected to exist "
771
0
              "but can not be found, will be restored",
772
0
              zname);
773
0
      catz_entry_add_or_mod(catz, toadd, key, keysize, nentry,
774
0
                oentry, "adding", zname, czname);
775
0
      continue;
776
0
    }
777
778
0
    if (dns_catz_entry_cmp(oentry, nentry) != true) {
779
0
      catz_entry_add_or_mod(catz, tomod, key, keysize, nentry,
780
0
                oentry, "modifying", zname,
781
0
                czname);
782
0
      continue;
783
0
    }
784
785
    /*
786
     * Delete the old entry so that it won't accidentally be
787
     * removed as a non-existing entry below.
788
     */
789
0
    dns_catz_entry_detach(catz, &oentry);
790
0
    result = isc_ht_delete(catz->entries, key, (uint32_t)keysize);
791
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
792
0
  }
793
0
  RUNTIME_CHECK(result == ISC_R_NOMORE);
794
0
  isc_ht_iter_destroy(&iter1);
795
796
  /*
797
   * Then - walk the old zone; only deleted entries should remain.
798
   */
799
0
  for (result = isc_ht_iter_first(iter2); result == ISC_R_SUCCESS;
800
0
       result = isc_ht_iter_delcurrent_next(iter2))
801
0
  {
802
0
    dns_catz_entry_t *entry = NULL;
803
0
    isc_ht_iter_current(iter2, (void **)&entry);
804
805
0
    dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
806
0
    result = delzone(entry, catz, catz->catzs->view,
807
0
         catz->catzs->zmm->udata);
808
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
809
0
            ISC_LOG_INFO,
810
0
            "catz: deleting zone '%s' from catalog '%s' - %s",
811
0
            zname, czname, isc_result_totext(result));
812
0
    dns_catz_entry_detach(catz, &entry);
813
0
  }
814
0
  RUNTIME_CHECK(result == ISC_R_NOMORE);
815
0
  isc_ht_iter_destroy(&iter2);
816
  /* At this moment catz->entries has to be empty. */
817
0
  INSIST(isc_ht_count(catz->entries) == 0);
818
0
  isc_ht_destroy(&catz->entries);
819
820
0
  for (result = isc_ht_iter_first(iteradd); result == ISC_R_SUCCESS;
821
0
       result = isc_ht_iter_delcurrent_next(iteradd))
822
0
  {
823
0
    dns_catz_entry_t *entry = NULL;
824
0
    isc_ht_iter_current(iteradd, (void **)&entry);
825
826
0
    dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
827
0
    result = addzone(entry, catz, catz->catzs->view,
828
0
         catz->catzs->zmm->udata);
829
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
830
0
            ISC_LOG_INFO,
831
0
            "catz: adding zone '%s' from catalog "
832
0
            "'%s' - %s",
833
0
            zname, czname, isc_result_totext(result));
834
0
  }
835
836
0
  for (result = isc_ht_iter_first(itermod); result == ISC_R_SUCCESS;
837
0
       result = isc_ht_iter_delcurrent_next(itermod))
838
0
  {
839
0
    dns_catz_entry_t *entry = NULL;
840
0
    isc_ht_iter_current(itermod, (void **)&entry);
841
842
0
    dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
843
0
    result = modzone(entry, catz, catz->catzs->view,
844
0
         catz->catzs->zmm->udata);
845
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
846
0
            ISC_LOG_INFO,
847
0
            "catz: modifying zone '%s' from catalog "
848
0
            "'%s' - %s",
849
0
            zname, czname, isc_result_totext(result));
850
0
  }
851
852
0
  catz->entries = newcatz->entries;
853
0
  newcatz->entries = NULL;
854
855
  /*
856
   * We do not need to merge old coo (change of ownership) permission
857
   * records with the new ones, just replace them.
858
   */
859
0
  isc_ht_t *newcoos = coos_take(&newcatz->coos);
860
0
  isc_ht_t *oldcoos = coos_replace(&catz->coos, newcoos);
861
0
  coos_destroy_table(&catz->coos, &oldcoos);
862
863
0
  result = ISC_R_SUCCESS;
864
865
0
  isc_ht_iter_destroy(&iteradd);
866
0
  isc_ht_iter_destroy(&itermod);
867
0
  isc_ht_destroy(&toadd);
868
0
  isc_ht_destroy(&tomod);
869
870
0
  UNLOCK(&catz->lock);
871
872
0
  return result;
873
0
}
874
875
dns_catz_zones_t *
876
0
dns_catz_zones_new(isc_mem_t *mctx, dns_catz_zonemodmethods_t *zmm) {
877
0
  REQUIRE(mctx != NULL);
878
0
  REQUIRE(zmm != NULL);
879
880
0
  dns_catz_zones_t *catzs = isc_mem_get(mctx, sizeof(*catzs));
881
0
  *catzs = (dns_catz_zones_t){
882
0
    .zmm = zmm,
883
0
    .magic = DNS_CATZ_ZONES_MAGIC,
884
0
  };
885
886
0
  isc_mutex_init(&catzs->lock);
887
0
  isc_refcount_init(&catzs->references, 1);
888
0
  isc_ht_init(&catzs->zones, mctx, 4, ISC_HT_CASE_INSENSITIVE);
889
0
  isc_mem_attach(mctx, &catzs->mctx);
890
891
0
  return catzs;
892
0
}
893
894
void *
895
0
dns_catz_zones_get_udata(dns_catz_zones_t *catzs) {
896
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
897
898
0
  return catzs->zmm->udata;
899
0
}
900
901
void
902
0
dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) {
903
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
904
0
  REQUIRE(DNS_VIEW_VALID(view));
905
  /* Either it's a new one or it's being reconfigured. */
906
0
  REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name));
907
908
0
  if (catzs->view == NULL) {
909
0
    dns_view_weakattach(view, &catzs->view);
910
0
  } else if (catzs->view != view) {
911
0
    dns_view_weakdetach(&catzs->view);
912
0
    dns_view_weakattach(view, &catzs->view);
913
0
  }
914
0
}
915
916
dns_catz_zone_t *
917
0
dns_catz_zone_new(dns_catz_zones_t *catzs, const dns_name_t *name) {
918
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
919
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
920
921
0
  dns_catz_zone_t *catz = isc_mem_get(catzs->mctx, sizeof(*catz));
922
0
  *catz = (dns_catz_zone_t){ .active = true,
923
0
           .version = DNS_CATZ_VERSION_UNDEFINED,
924
0
           .magic = DNS_CATZ_ZONE_MAGIC };
925
926
0
  dns_catz_zones_attach(catzs, &catz->catzs);
927
0
  isc_mutex_init(&catz->lock);
928
0
  isc_refcount_init(&catz->references, 1);
929
0
  isc_ht_init(&catz->entries, catzs->mctx, 4, ISC_HT_CASE_INSENSITIVE);
930
0
  coos_init(&catz->coos, catzs->mctx);
931
0
  isc_time_settoepoch(&catz->lastupdated);
932
0
  dns_catz_options_init(&catz->defoptions);
933
0
  dns_catz_options_init(&catz->zoneoptions);
934
0
  dns_name_init(&catz->name);
935
0
  dns_name_dup(name, catzs->mctx, &catz->name);
936
937
0
  return catz;
938
0
}
939
940
static void
941
0
dns__catz_timer_start(dns_catz_zone_t *catz) {
942
0
  uint64_t tdiff;
943
0
  isc_interval_t interval;
944
0
  isc_time_t now;
945
946
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
947
948
0
  now = isc_time_now();
949
0
  tdiff = isc_time_microdiff(&now, &catz->lastupdated) / 1000000;
950
0
  if (tdiff < catz->defoptions.min_update_interval) {
951
0
    uint64_t defer = catz->defoptions.min_update_interval - tdiff;
952
0
    char dname[DNS_NAME_FORMATSIZE];
953
954
0
    dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
955
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
956
0
            ISC_LOG_INFO,
957
0
            "catz: %s: new zone version came "
958
0
            "too soon, deferring update for "
959
0
            "%" PRIu64 " seconds",
960
0
            dname, defer);
961
0
    isc_interval_set(&interval, (unsigned int)defer, 0);
962
0
  } else {
963
0
    isc_interval_set(&interval, 0, 0);
964
0
  }
965
966
0
  catz->loop = isc_loop();
967
968
0
  isc_timer_create(catz->loop, dns__catz_timer_cb, catz,
969
0
       &catz->updatetimer);
970
0
  isc_timer_start(catz->updatetimer, isc_timertype_once, &interval);
971
0
}
972
973
static void
974
0
dns__catz_timer_stop(void *arg) {
975
0
  dns_catz_zone_t *catz = arg;
976
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
977
978
0
  isc_timer_stop(catz->updatetimer);
979
0
  isc_timer_destroy(&catz->updatetimer);
980
0
  catz->loop = NULL;
981
982
0
  dns_catz_zone_detach(&catz);
983
0
}
984
985
isc_result_t
986
dns_catz_zone_add(dns_catz_zones_t *catzs, const dns_name_t *name,
987
0
      dns_catz_zone_t **catzp) {
988
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
989
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
990
0
  REQUIRE(catzp != NULL && *catzp == NULL);
991
992
0
  dns_catz_zone_t *catz = NULL;
993
0
  isc_result_t result;
994
0
  char zname[DNS_NAME_FORMATSIZE];
995
996
0
  dns_name_format(name, zname, DNS_NAME_FORMATSIZE);
997
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
998
0
          ISC_LOG_DEBUG(3), "catz: dns_catz_zone_add %s", zname);
999
1000
0
  LOCK(&catzs->lock);
1001
1002
  /*
1003
   * This function is called only during a (re)configuration, while
1004
   * 'catzs->zones' can become NULL only during shutdown.
1005
   */
1006
0
  INSIST(catzs->zones != NULL);
1007
0
  INSIST(!atomic_load(&catzs->shuttingdown));
1008
1009
0
  result = isc_ht_find(catzs->zones, name->ndata, name->length,
1010
0
           (void **)&catz);
1011
0
  switch (result) {
1012
0
  case ISC_R_SUCCESS:
1013
0
    INSIST(!catz->active);
1014
0
    catz->active = true;
1015
0
    result = ISC_R_EXISTS;
1016
0
    break;
1017
0
  case ISC_R_NOTFOUND:
1018
0
    catz = dns_catz_zone_new(catzs, name);
1019
1020
0
    result = isc_ht_add(catzs->zones, catz->name.ndata,
1021
0
            catz->name.length, catz);
1022
0
    INSIST(result == ISC_R_SUCCESS);
1023
0
    break;
1024
0
  default:
1025
0
    UNREACHABLE();
1026
0
  }
1027
1028
0
  UNLOCK(&catzs->lock);
1029
1030
0
  *catzp = catz;
1031
1032
0
  return result;
1033
0
}
1034
1035
dns_catz_zone_t *
1036
0
dns_catz_zone_get(dns_catz_zones_t *catzs, const dns_name_t *name) {
1037
0
  isc_result_t result;
1038
0
  dns_catz_zone_t *found = NULL;
1039
1040
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
1041
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1042
1043
0
  LOCK(&catzs->lock);
1044
0
  if (catzs->zones == NULL) {
1045
0
    UNLOCK(&catzs->lock);
1046
0
    return NULL;
1047
0
  }
1048
0
  result = isc_ht_find(catzs->zones, name->ndata, name->length,
1049
0
           (void **)&found);
1050
0
  UNLOCK(&catzs->lock);
1051
0
  if (result != ISC_R_SUCCESS) {
1052
0
    return NULL;
1053
0
  }
1054
1055
0
  return found;
1056
0
}
1057
1058
static void
1059
0
dns__catz_zone_shutdown(dns_catz_zone_t *catz) {
1060
  /* lock must be locked */
1061
0
  if (catz->updatetimer != NULL) {
1062
    /* Don't wait for timer to trigger for shutdown */
1063
0
    INSIST(catz->loop != NULL);
1064
1065
0
    isc_async_run(catz->loop, dns__catz_timer_stop, catz);
1066
0
  } else {
1067
0
    dns_catz_zone_detach(&catz);
1068
0
  }
1069
0
}
1070
1071
static void
1072
0
dns__catz_zone_destroy(dns_catz_zone_t *catz) {
1073
0
  isc_mem_t *mctx = catz->catzs->mctx;
1074
1075
0
  if (catz->entries != NULL) {
1076
0
    isc_ht_iter_t *iter = NULL;
1077
0
    isc_result_t result;
1078
0
    isc_ht_iter_create(catz->entries, &iter);
1079
0
    for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
1080
0
         result = isc_ht_iter_delcurrent_next(iter))
1081
0
    {
1082
0
      dns_catz_entry_t *entry = NULL;
1083
1084
0
      isc_ht_iter_current(iter, (void **)&entry);
1085
0
      dns_catz_entry_detach(catz, &entry);
1086
0
    }
1087
0
    INSIST(result == ISC_R_NOMORE);
1088
0
    isc_ht_iter_destroy(&iter);
1089
1090
    /* The hashtable has to be empty now. */
1091
0
    INSIST(isc_ht_count(catz->entries) == 0);
1092
0
    isc_ht_destroy(&catz->entries);
1093
0
  }
1094
0
  coos_destroy(&catz->coos);
1095
0
  catz->magic = 0;
1096
0
  isc_mutex_destroy(&catz->lock);
1097
1098
0
  if (catz->updatetimer != NULL) {
1099
0
    isc_timer_async_destroy(&catz->updatetimer);
1100
0
  }
1101
1102
0
  if (catz->db != NULL) {
1103
0
    if (catz->dbversion != NULL) {
1104
0
      dns_db_closeversion(catz->db, &catz->dbversion, false);
1105
0
    }
1106
0
    dns_db_updatenotify_unregister(
1107
0
      catz->db, dns_catz_dbupdate_callback, catz->catzs);
1108
0
    dns_db_detach(&catz->db);
1109
0
  }
1110
1111
0
  INSIST(!catz->updaterunning);
1112
1113
0
  dns_name_free(&catz->name, mctx);
1114
0
  dns_catz_options_free(&catz->defoptions, mctx);
1115
0
  dns_catz_options_free(&catz->zoneoptions, mctx);
1116
1117
0
  dns_catz_zones_detach(&catz->catzs);
1118
1119
0
  isc_mem_put(mctx, catz, sizeof(*catz));
1120
0
}
1121
1122
static void
1123
0
dns__catz_zones_destroy(dns_catz_zones_t *catzs) {
1124
0
  REQUIRE(atomic_load(&catzs->shuttingdown));
1125
0
  REQUIRE(catzs->zones == NULL);
1126
1127
0
  catzs->magic = 0;
1128
0
  isc_mutex_destroy(&catzs->lock);
1129
0
  if (catzs->view != NULL) {
1130
0
    dns_view_weakdetach(&catzs->view);
1131
0
  }
1132
0
  isc_mem_putanddetach(&catzs->mctx, catzs, sizeof(*catzs));
1133
0
}
1134
1135
void
1136
0
dns_catz_zones_shutdown(dns_catz_zones_t *catzs) {
1137
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
1138
1139
0
  if (!atomic_compare_exchange_strong(&catzs->shuttingdown,
1140
0
              &(bool){ false }, true))
1141
0
  {
1142
0
    return;
1143
0
  }
1144
1145
0
  LOCK(&catzs->lock);
1146
0
  if (catzs->zones != NULL) {
1147
0
    isc_ht_iter_t *iter = NULL;
1148
0
    isc_result_t result;
1149
0
    isc_ht_iter_create(catzs->zones, &iter);
1150
0
    for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;)
1151
0
    {
1152
0
      dns_catz_zone_t *catz = NULL;
1153
0
      isc_ht_iter_current(iter, (void **)&catz);
1154
0
      result = isc_ht_iter_delcurrent_next(iter);
1155
0
      dns__catz_zone_shutdown(catz);
1156
0
    }
1157
0
    INSIST(result == ISC_R_NOMORE);
1158
0
    isc_ht_iter_destroy(&iter);
1159
0
    INSIST(isc_ht_count(catzs->zones) == 0);
1160
0
    isc_ht_destroy(&catzs->zones);
1161
0
  }
1162
0
  UNLOCK(&catzs->lock);
1163
0
}
1164
1165
#ifdef DNS_CATZ_TRACE
1166
ISC_REFCOUNT_TRACE_IMPL(dns_catz_zone, dns__catz_zone_destroy);
1167
ISC_REFCOUNT_TRACE_IMPL(dns_catz_zones, dns__catz_zones_destroy);
1168
#else
1169
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
1170
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
1171
0
#endif
1172
0
1173
0
typedef enum {
1174
0
  CATZ_OPT_NONE,
1175
0
  CATZ_OPT_ZONES,
1176
0
  CATZ_OPT_COO,
1177
0
  CATZ_OPT_VERSION,
1178
0
  CATZ_OPT_CUSTOM_START, /* CATZ custom properties must go below this */
1179
0
  CATZ_OPT_EXT,
1180
0
  CATZ_OPT_PRIMARIES,
1181
0
  CATZ_OPT_ALLOW_QUERY,
1182
0
  CATZ_OPT_ALLOW_TRANSFER,
1183
0
} catz_opt_t;
1184
0
1185
0
static bool
1186
0
catz_opt_cmp(const dns_label_t *option, const char *opt) {
1187
0
  size_t len = strlen(opt);
1188
1189
0
  if (option->length - 1 == len &&
1190
0
      memcmp(opt, option->base + 1, len) == 0)
1191
0
  {
1192
0
    return true;
1193
0
  } else {
1194
0
    return false;
1195
0
  }
1196
0
}
1197
1198
static catz_opt_t
1199
0
catz_get_option(const dns_label_t *option) {
1200
0
  if (catz_opt_cmp(option, "ext")) {
1201
0
    return CATZ_OPT_EXT;
1202
0
  } else if (catz_opt_cmp(option, "zones")) {
1203
0
    return CATZ_OPT_ZONES;
1204
0
  } else if (catz_opt_cmp(option, "masters") ||
1205
0
       catz_opt_cmp(option, "primaries"))
1206
0
  {
1207
0
    return CATZ_OPT_PRIMARIES;
1208
0
  } else if (catz_opt_cmp(option, "allow-query")) {
1209
0
    return CATZ_OPT_ALLOW_QUERY;
1210
0
  } else if (catz_opt_cmp(option, "allow-transfer")) {
1211
0
    return CATZ_OPT_ALLOW_TRANSFER;
1212
0
  } else if (catz_opt_cmp(option, "coo")) {
1213
0
    return CATZ_OPT_COO;
1214
0
  } else if (catz_opt_cmp(option, "version")) {
1215
0
    return CATZ_OPT_VERSION;
1216
0
  } else {
1217
0
    return CATZ_OPT_NONE;
1218
0
  }
1219
0
}
1220
1221
static isc_result_t
1222
catz_process_zones(dns_catz_zone_t *catz, dns_rdataset_t *value,
1223
0
       dns_name_t *name) {
1224
0
  dns_label_t mhash;
1225
0
  dns_name_t opt;
1226
1227
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1228
0
  REQUIRE(DNS_RDATASET_VALID(value));
1229
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1230
1231
0
  uint8_t labels = dns_name_countlabels(name);
1232
1233
0
  if (labels == 0) {
1234
0
    return ISC_R_FAILURE;
1235
0
  }
1236
1237
0
  dns_name_getlabel(name, labels - 1, &mhash);
1238
1239
0
  if (labels == 1) {
1240
0
    return catz_process_zones_entry(catz, value, &mhash);
1241
0
  } else {
1242
0
    dns_name_init(&opt);
1243
0
    dns_name_split(name, 1, &opt, NULL);
1244
0
    return catz_process_zones_suboption(catz, value, &mhash, &opt);
1245
0
  }
1246
0
}
1247
1248
static isc_result_t
1249
catz_process_coo(dns_catz_zone_t *catz, dns_label_t *mhash,
1250
0
     dns_rdataset_t *value) {
1251
0
  isc_result_t result;
1252
0
  dns_rdata_t rdata;
1253
0
  dns_rdata_ptr_t ptr;
1254
0
  dns_catz_entry_t *entry = NULL;
1255
1256
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1257
0
  REQUIRE(mhash != NULL);
1258
0
  REQUIRE(DNS_RDATASET_VALID(value));
1259
1260
  /* Change of Ownership was introduced in version "2" of the schema. */
1261
0
  if (catz->version < 2) {
1262
0
    return ISC_R_FAILURE;
1263
0
  }
1264
1265
0
  if (value->type != dns_rdatatype_ptr) {
1266
0
    return ISC_R_FAILURE;
1267
0
  }
1268
1269
0
  if (dns_rdataset_count(value) != 1) {
1270
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1271
0
            ISC_LOG_WARNING,
1272
0
            "catz: 'coo' property PTR RRset contains "
1273
0
            "more than one record, which is invalid");
1274
0
    catz->broken = true;
1275
0
    return ISC_R_FAILURE;
1276
0
  }
1277
1278
0
  RETERR(dns_rdataset_first(value));
1279
1280
0
  dns_rdata_init(&rdata);
1281
0
  dns_rdataset_current(value, &rdata);
1282
1283
0
  RETERR(dns_rdata_tostruct(&rdata, &ptr));
1284
1285
0
  if (dns_name_empty(&ptr.ptr)) {
1286
0
    CLEANUP(ISC_R_FAILURE);
1287
0
  }
1288
1289
0
  CHECK(isc_ht_find(catz->entries, mhash->base, mhash->length,
1290
0
        (void **)&entry));
1291
1292
0
  if (dns_name_empty(&entry->name)) {
1293
0
    CLEANUP(ISC_R_FAILURE);
1294
0
  }
1295
1296
0
  coos_add(&catz->coos, entry, &ptr.ptr);
1297
1298
0
cleanup:
1299
0
  return result;
1300
0
}
1301
1302
static isc_result_t
1303
catz_process_zones_entry(dns_catz_zone_t *catz, dns_rdataset_t *value,
1304
0
       dns_label_t *mhash) {
1305
0
  isc_result_t result;
1306
0
  dns_rdata_t rdata;
1307
0
  dns_rdata_ptr_t ptr;
1308
0
  dns_catz_entry_t *entry = NULL;
1309
1310
0
  if (value->type != dns_rdatatype_ptr) {
1311
0
    return ISC_R_FAILURE;
1312
0
  }
1313
1314
0
  if (dns_rdataset_count(value) != 1) {
1315
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1316
0
            ISC_LOG_WARNING,
1317
0
            "catz: member zone PTR RRset contains "
1318
0
            "more than one record, which is invalid");
1319
0
    catz->broken = true;
1320
0
    return ISC_R_FAILURE;
1321
0
  }
1322
1323
0
  RETERR(dns_rdataset_first(value));
1324
1325
0
  dns_rdata_init(&rdata);
1326
0
  dns_rdataset_current(value, &rdata);
1327
1328
0
  RETERR(dns_rdata_tostruct(&rdata, &ptr));
1329
1330
0
  result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1331
0
           (void **)&entry);
1332
0
  if (result == ISC_R_SUCCESS) {
1333
0
    if (!dns_name_empty(&entry->name)) {
1334
      /* We have a duplicate. */
1335
0
      return ISC_R_FAILURE;
1336
0
    } else {
1337
0
      dns_name_dup(&ptr.ptr, catz->catzs->mctx, &entry->name);
1338
0
    }
1339
0
  } else {
1340
0
    entry = dns_catz_entry_new(catz->catzs->mctx, &ptr.ptr);
1341
1342
0
    result = isc_ht_add(catz->entries, mhash->base, mhash->length,
1343
0
            entry);
1344
0
  }
1345
0
  INSIST(result == ISC_R_SUCCESS);
1346
1347
0
  return ISC_R_SUCCESS;
1348
0
}
1349
1350
static isc_result_t
1351
0
catz_process_version(dns_catz_zone_t *catz, dns_rdataset_t *value) {
1352
0
  isc_result_t result;
1353
0
  dns_rdata_t rdata;
1354
0
  dns_rdata_txt_t rdatatxt;
1355
0
  dns_rdata_txt_string_t rdatastr;
1356
0
  uint32_t tversion;
1357
0
  char t[16];
1358
1359
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1360
0
  REQUIRE(DNS_RDATASET_VALID(value));
1361
1362
0
  if (value->type != dns_rdatatype_txt) {
1363
0
    return ISC_R_FAILURE;
1364
0
  }
1365
1366
0
  if (dns_rdataset_count(value) != 1) {
1367
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1368
0
            ISC_LOG_WARNING,
1369
0
            "catz: 'version' property TXT RRset contains "
1370
0
            "more than one record, which is invalid");
1371
0
    catz->broken = true;
1372
0
    return ISC_R_FAILURE;
1373
0
  }
1374
1375
0
  RETERR(dns_rdataset_first(value));
1376
1377
0
  dns_rdata_init(&rdata);
1378
0
  dns_rdataset_current(value, &rdata);
1379
1380
0
  RETERR(dns_rdata_tostruct(&rdata, &rdatatxt));
1381
1382
0
  CHECK(dns_rdata_txt_first(&rdatatxt));
1383
1384
0
  CHECK(dns_rdata_txt_current(&rdatatxt, &rdatastr));
1385
1386
0
  result = dns_rdata_txt_next(&rdatatxt);
1387
0
  if (result != ISC_R_NOMORE) {
1388
0
    CLEANUP(ISC_R_FAILURE);
1389
0
  }
1390
0
  if (rdatastr.length > 15) {
1391
0
    CLEANUP(ISC_R_BADNUMBER);
1392
0
  }
1393
0
  memmove(t, rdatastr.data, rdatastr.length);
1394
0
  t[rdatastr.length] = 0;
1395
0
  CHECK(isc_parse_uint32(&tversion, t, 10));
1396
0
  catz->version = tversion;
1397
0
  result = ISC_R_SUCCESS;
1398
1399
0
cleanup:
1400
0
  if (result != ISC_R_SUCCESS) {
1401
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1402
0
            ISC_LOG_WARNING,
1403
0
            "catz: invalid record for the catalog "
1404
0
            "zone version property");
1405
0
    catz->broken = true;
1406
0
  }
1407
0
  return result;
1408
0
}
1409
1410
static isc_result_t
1411
catz_process_primaries(dns_catz_zone_t *catz, dns_ipkeylist_t *ipkl,
1412
0
           dns_rdataset_t *value, dns_name_t *name) {
1413
0
  isc_result_t result;
1414
0
  dns_rdata_in_a_t rdata_a;
1415
0
  dns_rdata_in_aaaa_t rdata_aaaa;
1416
0
  dns_rdata_txt_t rdata_txt;
1417
0
  dns_rdata_txt_string_t rdatastr;
1418
0
  dns_name_t *keyname = NULL;
1419
0
  isc_mem_t *mctx;
1420
0
  char keycbuf[DNS_NAME_FORMATSIZE];
1421
0
  isc_buffer_t keybuf;
1422
0
  unsigned int rcount;
1423
1424
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1425
0
  REQUIRE(ipkl != NULL);
1426
0
  REQUIRE(DNS_RDATASET_VALID(value));
1427
0
  REQUIRE(dns_rdataset_isassociated(value));
1428
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1429
1430
0
  mctx = catz->catzs->mctx;
1431
0
  memset(&rdata_a, 0, sizeof(rdata_a));
1432
0
  memset(&rdata_aaaa, 0, sizeof(rdata_aaaa));
1433
0
  memset(&rdata_txt, 0, sizeof(rdata_txt));
1434
0
  isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf));
1435
1436
  /*
1437
   * We have three possibilities here:
1438
   * - either empty name and IN A/IN AAAA record
1439
   * - label and IN A/IN AAAA
1440
   * - label and IN TXT - TSIG key name
1441
   */
1442
0
  if (name->length != 0) {
1443
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
1444
0
    isc_sockaddr_t sockaddr;
1445
0
    size_t i;
1446
1447
    /*
1448
     * We're pre-preparing the data once, we'll put it into
1449
     * the right spot in the primaries array once we find it.
1450
     */
1451
0
    result = dns_rdataset_first(value);
1452
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1453
0
    dns_rdataset_current(value, &rdata);
1454
0
    switch (value->type) {
1455
0
    case dns_rdatatype_a:
1456
0
      result = dns_rdata_tostruct(&rdata, &rdata_a);
1457
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1458
0
      isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0);
1459
0
      break;
1460
0
    case dns_rdatatype_aaaa:
1461
0
      result = dns_rdata_tostruct(&rdata, &rdata_aaaa);
1462
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1463
0
      isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr,
1464
0
               0);
1465
0
      break;
1466
0
    case dns_rdatatype_txt:
1467
0
      result = dns_rdata_tostruct(&rdata, &rdata_txt);
1468
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1469
1470
0
      RETERR(dns_rdata_txt_first(&rdata_txt));
1471
1472
0
      RETERR(dns_rdata_txt_current(&rdata_txt, &rdatastr));
1473
1474
0
      result = dns_rdata_txt_next(&rdata_txt);
1475
0
      if (result != ISC_R_NOMORE) {
1476
0
        return ISC_R_FAILURE;
1477
0
      }
1478
1479
      /* rdatastr.length < DNS_NAME_MAXTEXT */
1480
0
      keyname = isc_mem_get(mctx, sizeof(*keyname));
1481
0
      dns_name_init(keyname);
1482
0
      memmove(keycbuf, rdatastr.data, rdatastr.length);
1483
0
      keycbuf[rdatastr.length] = 0;
1484
0
      result = dns_name_fromstring(keyname, keycbuf,
1485
0
                 dns_rootname, 0, mctx);
1486
0
      if (result != ISC_R_SUCCESS) {
1487
0
        isc_mem_put(mctx, keyname, sizeof(*keyname));
1488
0
        return result;
1489
0
      }
1490
0
      break;
1491
0
    default:
1492
0
      return ISC_R_FAILURE;
1493
0
    }
1494
1495
    /*
1496
     * We have to find the appropriate labeled record in
1497
     * primaries if it exists.  In the common case we'll
1498
     * have no more than 3-4 records here, so no optimization.
1499
     */
1500
0
    for (i = 0; i < ipkl->count; i++) {
1501
0
      if (ipkl->labels[i] != NULL &&
1502
0
          !dns_name_compare(name, ipkl->labels[i]))
1503
0
      {
1504
0
        break;
1505
0
      }
1506
0
    }
1507
1508
0
    if (i < ipkl->count) { /* we have this record already */
1509
0
      if (value->type == dns_rdatatype_txt) {
1510
0
        if (ipkl->keys[i] != NULL) {
1511
0
          if (dns_name_dynamic(ipkl->keys[i])) {
1512
0
            dns_name_free(ipkl->keys[i],
1513
0
                    mctx);
1514
0
          }
1515
0
          isc_mem_put(mctx, ipkl->keys[i],
1516
0
                sizeof(*ipkl->keys[i]));
1517
0
        }
1518
0
        ipkl->keys[i] = keyname;
1519
0
      } else { /* A/AAAA */
1520
0
        memmove(&ipkl->addrs[i], &sockaddr,
1521
0
          sizeof(sockaddr));
1522
0
      }
1523
0
    } else {
1524
0
      dns_ipkeylist_resize(mctx, ipkl, i + 1);
1525
1526
0
      ipkl->labels[i] = isc_mem_get(mctx,
1527
0
                  sizeof(*ipkl->labels[0]));
1528
0
      dns_name_init(ipkl->labels[i]);
1529
0
      dns_name_dup(name, mctx, ipkl->labels[i]);
1530
1531
0
      if (value->type == dns_rdatatype_txt) {
1532
0
        ipkl->keys[i] = keyname;
1533
0
      } else { /* A/AAAA */
1534
0
        memmove(&ipkl->addrs[i], &sockaddr,
1535
0
          sizeof(sockaddr));
1536
0
      }
1537
0
      ipkl->count++;
1538
0
    }
1539
0
    return ISC_R_SUCCESS;
1540
0
  }
1541
  /* else - 'simple' case - without labels */
1542
1543
0
  if (!dns_rdatatype_isaddr(value->type)) {
1544
0
    return ISC_R_FAILURE;
1545
0
  }
1546
1547
0
  rcount = dns_rdataset_count(value) + ipkl->count;
1548
1549
0
  dns_ipkeylist_resize(mctx, ipkl, rcount);
1550
1551
0
  DNS_RDATASET_FOREACH(value) {
1552
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
1553
0
    dns_rdataset_current(value, &rdata);
1554
    /*
1555
     * port 0 == take the default
1556
     */
1557
0
    if (value->type == dns_rdatatype_a) {
1558
0
      result = dns_rdata_tostruct(&rdata, &rdata_a);
1559
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1560
0
      isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
1561
0
              &rdata_a.in_addr, 0);
1562
0
    } else {
1563
0
      result = dns_rdata_tostruct(&rdata, &rdata_aaaa);
1564
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1565
0
      isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count],
1566
0
               &rdata_aaaa.in6_addr, 0);
1567
0
    }
1568
0
    ipkl->keys[ipkl->count] = NULL;
1569
0
    ipkl->labels[ipkl->count] = NULL;
1570
0
    ipkl->count++;
1571
0
  }
1572
0
  return ISC_R_SUCCESS;
1573
0
}
1574
1575
static isc_result_t
1576
catz_process_apl(dns_catz_zone_t *catz, isc_buffer_t **aclbp,
1577
0
     dns_rdataset_t *value) {
1578
0
  REQUIRE(DNS_RDATASET_VALID(value));
1579
0
  REQUIRE(dns_rdataset_isassociated(value));
1580
1581
0
  if (value->type != dns_rdatatype_apl) {
1582
0
    return ISC_R_FAILURE;
1583
0
  }
1584
1585
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1586
0
  REQUIRE(aclbp != NULL);
1587
0
  REQUIRE(*aclbp == NULL);
1588
1589
0
  isc_result_t result = ISC_R_SUCCESS;
1590
0
  dns_rdata_t rdata;
1591
0
  dns_rdata_in_apl_t rdata_apl;
1592
0
  dns_rdata_apl_ent_t apl_ent;
1593
0
  isc_netaddr_t addr;
1594
0
  isc_buffer_t *aclb = NULL;
1595
0
  unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */
1596
1597
0
  if (dns_rdataset_count(value) > 1) {
1598
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1599
0
            ISC_LOG_WARNING,
1600
0
            "catz: more than one APL entry for member zone, "
1601
0
            "result is undefined");
1602
0
  }
1603
0
  result = dns_rdataset_first(value);
1604
0
  RUNTIME_CHECK(result == ISC_R_SUCCESS);
1605
0
  dns_rdata_init(&rdata);
1606
0
  dns_rdataset_current(value, &rdata);
1607
0
  RETERR(dns_rdata_tostruct(&rdata, &rdata_apl));
1608
0
  isc_buffer_allocate(catz->catzs->mctx, &aclb, 16);
1609
0
  for (result = dns_rdata_apl_first(&rdata_apl); result == ISC_R_SUCCESS;
1610
0
       result = dns_rdata_apl_next(&rdata_apl))
1611
0
  {
1612
0
    result = dns_rdata_apl_current(&rdata_apl, &apl_ent);
1613
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1614
0
    memset(buf, 0, sizeof(buf));
1615
0
    if (apl_ent.data != NULL && apl_ent.length > 0) {
1616
0
      memmove(buf, apl_ent.data, apl_ent.length);
1617
0
    }
1618
0
    if (apl_ent.family == 1) {
1619
0
      isc_netaddr_fromin(&addr, (struct in_addr *)buf);
1620
0
    } else if (apl_ent.family == 2) {
1621
0
      isc_netaddr_fromin6(&addr, (struct in6_addr *)buf);
1622
0
    } else {
1623
0
      continue; /* xxxwpk log it or simply ignore? */
1624
0
    }
1625
0
    if (apl_ent.negative) {
1626
0
      isc_buffer_putuint8(aclb, '!');
1627
0
    }
1628
0
    isc_buffer_reserve(aclb, INET6_ADDRSTRLEN);
1629
0
    result = isc_netaddr_totext(&addr, aclb);
1630
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1631
0
    if ((apl_ent.family == 1 && apl_ent.prefix < 32) ||
1632
0
        (apl_ent.family == 2 && apl_ent.prefix < 128))
1633
0
    {
1634
0
      isc_buffer_putuint8(aclb, '/');
1635
0
      isc_buffer_printf(aclb, "%" PRId8, apl_ent.prefix);
1636
0
    }
1637
0
    isc_buffer_putstr(aclb, "; ");
1638
0
  }
1639
0
  if (result == ISC_R_NOMORE) {
1640
0
    result = ISC_R_SUCCESS;
1641
0
  } else {
1642
0
    goto cleanup;
1643
0
  }
1644
0
  *aclbp = aclb;
1645
0
  aclb = NULL;
1646
0
cleanup:
1647
0
  if (aclb != NULL) {
1648
0
    isc_buffer_free(&aclb);
1649
0
  }
1650
0
  return result;
1651
0
}
1652
1653
static isc_result_t
1654
catz_process_zones_suboption(dns_catz_zone_t *catz, dns_rdataset_t *value,
1655
0
           dns_label_t *mhash, dns_name_t *name) {
1656
0
  isc_result_t result;
1657
0
  dns_catz_entry_t *entry = NULL;
1658
0
  dns_label_t option;
1659
0
  dns_name_t prefix;
1660
0
  catz_opt_t opt;
1661
0
  unsigned int suffix_labels = 1;
1662
1663
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1664
0
  REQUIRE(mhash != NULL);
1665
0
  REQUIRE(DNS_RDATASET_VALID(value));
1666
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1667
1668
0
  uint8_t labels = dns_name_countlabels(name);
1669
1670
0
  if (labels < 1) {
1671
0
    return ISC_R_FAILURE;
1672
0
  }
1673
0
  dns_name_getlabel(name, labels - 1, &option);
1674
0
  opt = catz_get_option(&option);
1675
1676
  /*
1677
   * The custom properties in version 2 schema must be placed under the
1678
   * "ext" label.
1679
   */
1680
0
  if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) {
1681
0
    if (opt != CATZ_OPT_EXT || labels < 2) {
1682
0
      return ISC_R_FAILURE;
1683
0
    }
1684
0
    suffix_labels++;
1685
0
    dns_name_getlabel(name, labels - 2, &option);
1686
0
    opt = catz_get_option(&option);
1687
0
  }
1688
1689
  /*
1690
   * We're adding this entry now, in case the option is invalid we'll get
1691
   * rid of it in verification phase.
1692
   */
1693
0
  result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1694
0
           (void **)&entry);
1695
0
  if (result != ISC_R_SUCCESS) {
1696
0
    entry = dns_catz_entry_new(catz->catzs->mctx, NULL);
1697
0
    result = isc_ht_add(catz->entries, mhash->base, mhash->length,
1698
0
            entry);
1699
0
  }
1700
0
  INSIST(result == ISC_R_SUCCESS);
1701
1702
0
  dns_name_init(&prefix);
1703
0
  dns_name_split(name, suffix_labels, &prefix, NULL);
1704
0
  switch (opt) {
1705
0
  case CATZ_OPT_COO:
1706
0
    return catz_process_coo(catz, mhash, value);
1707
0
  case CATZ_OPT_PRIMARIES:
1708
0
    return catz_process_primaries(catz, &entry->opts.masters, value,
1709
0
                &prefix);
1710
0
  case CATZ_OPT_ALLOW_QUERY:;
1711
0
    if (prefix.length != 0) {
1712
0
      return ISC_R_FAILURE;
1713
0
    }
1714
0
    return catz_process_apl(catz, &entry->opts.allow_query, value);
1715
0
  case CATZ_OPT_ALLOW_TRANSFER:
1716
0
    if (prefix.length != 0) {
1717
0
      return ISC_R_FAILURE;
1718
0
    }
1719
0
    return catz_process_apl(catz, &entry->opts.allow_transfer,
1720
0
          value);
1721
0
  default:
1722
0
    return ISC_R_FAILURE;
1723
0
  }
1724
1725
0
  return ISC_R_FAILURE;
1726
0
}
1727
1728
static void
1729
catz_entry_add_or_mod(dns_catz_zone_t *catz, isc_ht_t *ht, unsigned char *key,
1730
          size_t keysize, dns_catz_entry_t *nentry,
1731
          dns_catz_entry_t *oentry, const char *msg,
1732
0
          const char *zname, const char *czname) {
1733
0
  isc_result_t result = isc_ht_add(ht, key, (uint32_t)keysize, nentry);
1734
1735
0
  if (result != ISC_R_SUCCESS) {
1736
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1737
0
            ISC_LOG_ERROR,
1738
0
            "catz: error %s zone '%s' from catalog '%s' - %s",
1739
0
            msg, zname, czname, isc_result_totext(result));
1740
0
  }
1741
0
  if (oentry != NULL) {
1742
0
    dns_catz_entry_detach(catz, &oentry);
1743
0
    result = isc_ht_delete(catz->entries, key, (uint32_t)keysize);
1744
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1745
0
  }
1746
0
}
1747
1748
static isc_result_t
1749
catz_process_value(dns_catz_zone_t *catz, dns_name_t *name,
1750
0
       dns_rdataset_t *rdataset) {
1751
0
  dns_label_t option;
1752
0
  dns_name_t prefix;
1753
0
  catz_opt_t opt;
1754
0
  unsigned int suffix_labels = 1;
1755
1756
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1757
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1758
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
1759
1760
0
  uint8_t labels = dns_name_countlabels(name);
1761
1762
0
  if (labels < 1) {
1763
0
    return ISC_R_FAILURE;
1764
0
  }
1765
0
  dns_name_getlabel(name, labels - 1, &option);
1766
0
  opt = catz_get_option(&option);
1767
1768
  /*
1769
   * The custom properties in version 2 schema must be placed under the
1770
   * "ext" label.
1771
   */
1772
0
  if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) {
1773
0
    if (opt != CATZ_OPT_EXT || labels < 2) {
1774
0
      return ISC_R_FAILURE;
1775
0
    }
1776
0
    suffix_labels++;
1777
0
    dns_name_getlabel(name, labels - 2, &option);
1778
0
    opt = catz_get_option(&option);
1779
0
  }
1780
1781
0
  dns_name_init(&prefix);
1782
0
  dns_name_split(name, suffix_labels, &prefix, NULL);
1783
1784
0
  switch (opt) {
1785
0
  case CATZ_OPT_ZONES:
1786
0
    return catz_process_zones(catz, rdataset, &prefix);
1787
0
  case CATZ_OPT_PRIMARIES:
1788
0
    return catz_process_primaries(catz, &catz->zoneoptions.masters,
1789
0
                rdataset, &prefix);
1790
0
  case CATZ_OPT_ALLOW_QUERY:
1791
0
    if (prefix.length != 0) {
1792
0
      return ISC_R_FAILURE;
1793
0
    }
1794
0
    return catz_process_apl(catz, &catz->zoneoptions.allow_query,
1795
0
          rdataset);
1796
0
  case CATZ_OPT_ALLOW_TRANSFER:
1797
0
    if (prefix.length != 0) {
1798
0
      return ISC_R_FAILURE;
1799
0
    }
1800
0
    return catz_process_apl(catz, &catz->zoneoptions.allow_transfer,
1801
0
          rdataset);
1802
0
  case CATZ_OPT_VERSION:
1803
0
    if (prefix.length != 0) {
1804
0
      return ISC_R_FAILURE;
1805
0
    }
1806
0
    return catz_process_version(catz, rdataset);
1807
0
  default:
1808
0
    return ISC_R_FAILURE;
1809
0
  }
1810
0
}
1811
1812
/*%<
1813
 * Process a single rdataset from a catalog zone 'catz' update, src_name is the
1814
 * record name.
1815
 *
1816
 * Requires:
1817
 * \li  'catz' is a valid dns_catz_zone_t.
1818
 * \li  'src_name' is a valid dns_name_t.
1819
 * \li  'rdataset' is valid rdataset.
1820
 */
1821
static isc_result_t
1822
dns__catz_update_process(dns_catz_zone_t *catz, const dns_name_t *src_name,
1823
0
       dns_rdataset_t *rdataset) {
1824
0
  isc_result_t result;
1825
0
  int order;
1826
0
  unsigned int nlabels;
1827
0
  dns_namereln_t nrres;
1828
0
  dns_rdata_t rdata = DNS_RDATA_INIT;
1829
0
  dns_rdata_soa_t soa;
1830
0
  dns_name_t prefix;
1831
1832
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1833
0
  REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC));
1834
1835
0
  if (rdataset->rdclass != dns_rdataclass_in) {
1836
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1837
0
            ISC_LOG_ERROR,
1838
0
            "catz: RR found which has a non-IN class");
1839
0
    catz->broken = true;
1840
0
    return ISC_R_FAILURE;
1841
0
  }
1842
1843
0
  nrres = dns_name_fullcompare(src_name, &catz->name, &order, &nlabels);
1844
0
  if (nrres == dns_namereln_equal) {
1845
0
    if (rdataset->type == dns_rdatatype_soa) {
1846
0
      RETERR(dns_rdataset_first(rdataset));
1847
1848
0
      dns_rdataset_current(rdataset, &rdata);
1849
0
      result = dns_rdata_tostruct(&rdata, &soa);
1850
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1851
1852
0
      return result;
1853
0
    } else if (rdataset->type == dns_rdatatype_ns) {
1854
0
      return ISC_R_SUCCESS;
1855
0
    } else {
1856
0
      return ISC_R_UNEXPECTED;
1857
0
    }
1858
0
  } else if (nrres != dns_namereln_subdomain) {
1859
0
    return ISC_R_UNEXPECTED;
1860
0
  }
1861
1862
0
  uint8_t labels = dns_name_countlabels(&catz->name);
1863
0
  dns_name_init(&prefix);
1864
0
  dns_name_split(src_name, labels, &prefix, NULL);
1865
0
  result = catz_process_value(catz, &prefix, rdataset);
1866
1867
0
  return result;
1868
0
}
1869
1870
isc_result_t
1871
dns_catz_generate_masterfilename(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
1872
0
         isc_buffer_t *buffer) {
1873
0
  isc_buffer_t *tbuf = NULL;
1874
0
  isc_region_t r;
1875
0
  isc_result_t result;
1876
0
  size_t rlen;
1877
0
  bool special = false;
1878
1879
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1880
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1881
0
  REQUIRE(buffer != NULL);
1882
1883
0
  isc_buffer_allocate(catz->catzs->mctx, &tbuf,
1884
0
          strlen(catz->catzs->view->name) +
1885
0
            2 * DNS_NAME_FORMATSIZE + 2);
1886
1887
0
  isc_buffer_putstr(tbuf, catz->catzs->view->name);
1888
0
  isc_buffer_putstr(tbuf, "_");
1889
0
  CHECK(dns_name_totext(&catz->name, DNS_NAME_OMITFINALDOT, tbuf));
1890
1891
0
  isc_buffer_putstr(tbuf, "_");
1892
0
  CHECK(dns_name_totext(&entry->name, DNS_NAME_OMITFINALDOT, tbuf));
1893
1894
  /*
1895
   * Search for slash and other special characters in the view and
1896
   * zone names.  Add a null terminator so we can use strpbrk(), then
1897
   * remove it.
1898
   */
1899
0
  isc_buffer_putuint8(tbuf, 0);
1900
0
  if (strpbrk(isc_buffer_base(tbuf), "\\/:%$") != NULL) {
1901
0
    special = true;
1902
0
  }
1903
0
  isc_buffer_subtract(tbuf, 1);
1904
1905
  /* __catz__<digest>.db */
1906
0
  rlen = (ISC_SHA256_DIGESTLENGTH * 2 + 1) + 12;
1907
1908
  /* optionally prepend with <zonedir>/ */
1909
0
  if (entry->opts.zonedir != NULL) {
1910
0
    rlen += strlen(entry->opts.zonedir) + 1;
1911
0
  }
1912
1913
0
  CHECK(isc_buffer_reserve(buffer, (unsigned int)rlen));
1914
1915
0
  if (entry->opts.zonedir != NULL) {
1916
0
    isc_buffer_putstr(buffer, entry->opts.zonedir);
1917
0
    isc_buffer_putstr(buffer, "/");
1918
0
  }
1919
1920
0
  isc_buffer_usedregion(tbuf, &r);
1921
0
  isc_buffer_putstr(buffer, "__catz__");
1922
0
  if (special || tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) {
1923
0
    unsigned char digest[ISC_MAX_MD_SIZE];
1924
0
    isc_region_t dr = { .base = digest };
1925
1926
0
    CHECK(isc_md(ISC_MD_SHA256, r.base, r.length, dr.base,
1927
0
           &dr.length));
1928
0
    CHECK(isc_hex_totextlower(&dr, 0, "", buffer));
1929
0
  } else {
1930
0
    isc_buffer_copyregion(buffer, &r);
1931
0
  }
1932
1933
0
  isc_buffer_putstr(buffer, ".db");
1934
0
  result = ISC_R_SUCCESS;
1935
1936
0
cleanup:
1937
0
  isc_buffer_free(&tbuf);
1938
0
  return result;
1939
0
}
1940
1941
/*
1942
 * We have to generate a text buffer with regular zone config:
1943
 * zone "foo.bar" {
1944
 *  type secondary;
1945
 *  primaries { ip1 port port1; ip2 port port2; };
1946
 * }
1947
 */
1948
isc_result_t
1949
dns_catz_generate_zonecfg(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
1950
0
        isc_buffer_t **buf) {
1951
0
  isc_buffer_t *buffer = NULL;
1952
0
  isc_region_t region;
1953
0
  isc_result_t result;
1954
0
  uint32_t i;
1955
0
  isc_netaddr_t netaddr;
1956
0
  char pbuf[sizeof("65535")]; /* used for port number */
1957
0
  char namebuf[DNS_NAME_FORMATSIZE];
1958
1959
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1960
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1961
0
  REQUIRE(buf != NULL && *buf == NULL);
1962
1963
  /*
1964
   * The buffer will be reallocated if something won't fit,
1965
   * ISC_BUFFER_INCR seems like a good start.
1966
   */
1967
0
  isc_buffer_allocate(catz->catzs->mctx, &buffer, ISC_BUFFER_INCR);
1968
1969
0
  isc_buffer_putstr(buffer, "zone \"");
1970
0
  dns_name_format(&entry->name, namebuf, sizeof(namebuf));
1971
0
  isc_buffer_putstr(buffer, namebuf);
1972
0
  isc_buffer_putstr(buffer, "\" { type secondary; primaries");
1973
1974
0
  isc_buffer_putstr(buffer, " { ");
1975
0
  for (i = 0; i < entry->opts.masters.count; i++) {
1976
    /*
1977
     * Every primary must have an IP address assigned.
1978
     */
1979
0
    switch (entry->opts.masters.addrs[i].type.sa.sa_family) {
1980
0
    case AF_INET:
1981
0
    case AF_INET6:
1982
0
      break;
1983
0
    default:
1984
0
      dns_name_format(&entry->name, namebuf, sizeof(namebuf));
1985
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
1986
0
              DNS_LOGMODULE_CATZ, ISC_LOG_ERROR,
1987
0
              "catz: zone '%s' uses an invalid primary "
1988
0
              "(no IP address assigned)",
1989
0
              namebuf);
1990
0
      CLEANUP(ISC_R_FAILURE);
1991
0
    }
1992
0
    isc_netaddr_fromsockaddr(&netaddr,
1993
0
           &entry->opts.masters.addrs[i]);
1994
0
    isc_buffer_reserve(buffer, INET6_ADDRSTRLEN);
1995
0
    result = isc_netaddr_totext(&netaddr, buffer);
1996
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1997
1998
0
    isc_buffer_putstr(buffer, " port ");
1999
0
    snprintf(pbuf, sizeof(pbuf), "%u",
2000
0
       isc_sockaddr_getport(&entry->opts.masters.addrs[i]));
2001
0
    isc_buffer_putstr(buffer, pbuf);
2002
2003
0
    if (entry->opts.masters.keys[i] != NULL) {
2004
0
      isc_buffer_putstr(buffer, " key ");
2005
0
      dns_name_format(entry->opts.masters.keys[i], namebuf,
2006
0
          sizeof(namebuf));
2007
0
      isc_buffer_putstr(buffer, namebuf);
2008
0
    }
2009
2010
0
    if (entry->opts.masters.tlss[i] != NULL) {
2011
0
      isc_buffer_putstr(buffer, " tls ");
2012
0
      dns_name_format(entry->opts.masters.tlss[i], namebuf,
2013
0
          sizeof(namebuf));
2014
0
      isc_buffer_putstr(buffer, namebuf);
2015
0
    }
2016
0
    isc_buffer_putstr(buffer, "; ");
2017
0
  }
2018
0
  isc_buffer_putstr(buffer, "}; ");
2019
0
  if (!entry->opts.in_memory) {
2020
0
    isc_buffer_putstr(buffer, "file \"");
2021
0
    CHECK(dns_catz_generate_masterfilename(catz, entry, buffer));
2022
0
    isc_buffer_putstr(buffer, "\"; ");
2023
0
  }
2024
0
  if (entry->opts.allow_query != NULL) {
2025
0
    isc_buffer_putstr(buffer, "allow-query { ");
2026
0
    isc_buffer_usedregion(entry->opts.allow_query, &region);
2027
0
    isc_buffer_copyregion(buffer, &region);
2028
0
    isc_buffer_putstr(buffer, "}; ");
2029
0
  }
2030
0
  if (entry->opts.allow_transfer != NULL) {
2031
0
    isc_buffer_putstr(buffer, "allow-transfer { ");
2032
0
    isc_buffer_usedregion(entry->opts.allow_transfer, &region);
2033
0
    isc_buffer_copyregion(buffer, &region);
2034
0
    isc_buffer_putstr(buffer, "}; ");
2035
0
  }
2036
2037
0
  isc_buffer_putstr(buffer, "};");
2038
0
  *buf = buffer;
2039
2040
0
  return ISC_R_SUCCESS;
2041
2042
0
cleanup:
2043
0
  isc_buffer_free(&buffer);
2044
0
  return result;
2045
0
}
2046
2047
static void
2048
0
dns__catz_timer_cb(void *arg) {
2049
0
  char domain[DNS_NAME_FORMATSIZE];
2050
0
  dns_catz_zone_t *catz = (dns_catz_zone_t *)arg;
2051
2052
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2053
2054
0
  if (atomic_load(&catz->catzs->shuttingdown)) {
2055
0
    return;
2056
0
  }
2057
2058
0
  LOCK(&catz->catzs->lock);
2059
2060
0
  INSIST(DNS_DB_VALID(catz->db));
2061
0
  INSIST(catz->dbversion != NULL);
2062
0
  INSIST(catz->updb == NULL);
2063
0
  INSIST(catz->updbversion == NULL);
2064
2065
0
  catz->updatepending = false;
2066
0
  catz->updaterunning = true;
2067
2068
0
  dns_name_format(&catz->name, domain, DNS_NAME_FORMATSIZE);
2069
2070
0
  if (!catz->active) {
2071
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2072
0
            ISC_LOG_INFO,
2073
0
            "catz: %s: no longer active, reload is canceled",
2074
0
            domain);
2075
0
    catz->updaterunning = false;
2076
0
    goto exit;
2077
0
  }
2078
2079
0
  dns_db_attach(catz->db, &catz->updb);
2080
0
  catz->updbversion = catz->dbversion;
2081
0
  catz->dbversion = NULL;
2082
2083
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
2084
0
          "catz: %s: reload start", domain);
2085
2086
0
  dns_catz_zone_ref(catz);
2087
0
  isc_work_enqueue(catz->loop, ISC_WORKLANE_SLOW, dns__catz_update_cb,
2088
0
       dns__catz_done_cb, catz);
2089
2090
0
exit:
2091
0
  isc_timer_destroy(&catz->updatetimer);
2092
0
  catz->loop = NULL;
2093
2094
0
  catz->lastupdated = isc_time_now();
2095
2096
0
  UNLOCK(&catz->catzs->lock);
2097
0
}
2098
2099
isc_result_t
2100
0
dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
2101
0
  dns_catz_zones_t *catzs = NULL;
2102
0
  dns_catz_zone_t *catz = NULL;
2103
0
  isc_result_t result = ISC_R_SUCCESS;
2104
0
  isc_region_t r;
2105
2106
0
  REQUIRE(DNS_DB_VALID(db));
2107
0
  REQUIRE(DNS_CATZ_ZONES_VALID(fn_arg));
2108
0
  catzs = (dns_catz_zones_t *)fn_arg;
2109
2110
0
  if (atomic_load(&catzs->shuttingdown)) {
2111
0
    return ISC_R_SHUTTINGDOWN;
2112
0
  }
2113
2114
0
  dns_name_toregion(&db->origin, &r);
2115
2116
0
  LOCK(&catzs->lock);
2117
0
  if (catzs->zones == NULL) {
2118
0
    CLEANUP(ISC_R_SHUTTINGDOWN);
2119
0
  }
2120
0
  CHECK(isc_ht_find(catzs->zones, r.base, r.length, (void **)&catz));
2121
2122
  /* New zone came as AXFR */
2123
0
  if (catz->db != NULL && catz->db != db) {
2124
    /* Old db cleanup. */
2125
0
    if (catz->dbversion != NULL) {
2126
0
      dns_db_closeversion(catz->db, &catz->dbversion, false);
2127
0
    }
2128
0
    dns_db_updatenotify_unregister(
2129
0
      catz->db, dns_catz_dbupdate_callback, catz->catzs);
2130
0
    dns_db_detach(&catz->db);
2131
0
  }
2132
0
  if (catz->db == NULL) {
2133
    /* New db registration. */
2134
0
    dns_db_attach(db, &catz->db);
2135
0
    dns_db_updatenotify_register(db, dns_catz_dbupdate_callback,
2136
0
               catz->catzs);
2137
0
  }
2138
2139
0
  if (!catz->updatepending && !catz->updaterunning) {
2140
0
    catz->updatepending = true;
2141
0
    dns_db_currentversion(db, &catz->dbversion);
2142
0
    dns__catz_timer_start(catz);
2143
0
  } else {
2144
0
    char dname[DNS_NAME_FORMATSIZE];
2145
2146
0
    catz->updatepending = true;
2147
0
    dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
2148
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2149
0
            ISC_LOG_DEBUG(3),
2150
0
            "catz: %s: update already queued or running",
2151
0
            dname);
2152
0
    if (catz->dbversion != NULL) {
2153
0
      dns_db_closeversion(catz->db, &catz->dbversion, false);
2154
0
    }
2155
0
    dns_db_currentversion(catz->db, &catz->dbversion);
2156
0
  }
2157
2158
0
cleanup:
2159
0
  UNLOCK(&catzs->lock);
2160
2161
0
  return result;
2162
0
}
2163
2164
void
2165
0
dns_catz_dbupdate_unregister(dns_db_t *db, dns_catz_zones_t *catzs) {
2166
0
  REQUIRE(DNS_DB_VALID(db));
2167
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2168
2169
0
  dns_db_updatenotify_unregister(db, dns_catz_dbupdate_callback, catzs);
2170
0
}
2171
2172
void
2173
0
dns_catz_dbupdate_register(dns_db_t *db, dns_catz_zones_t *catzs) {
2174
0
  REQUIRE(DNS_DB_VALID(db));
2175
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2176
2177
0
  dns_db_updatenotify_register(db, dns_catz_dbupdate_callback, catzs);
2178
0
}
2179
2180
static bool
2181
0
catz_rdatatype_is_processable(const dns_rdatatype_t type) {
2182
0
  return !dns_rdatatype_isdnssec(type) && type != dns_rdatatype_cds &&
2183
0
         type != dns_rdatatype_cdnskey && type != dns_rdatatype_zonemd;
2184
0
}
2185
2186
/*
2187
 * Process an updated database for a catalog zone.
2188
 * It creates a new catz, iterates over database to fill it with content, and
2189
 * then merges new catz into old catz.
2190
 */
2191
static isc_result_t
2192
0
dns__catz_update_cb(void *data) {
2193
0
  dns_catz_zone_t *catz = (dns_catz_zone_t *)data;
2194
0
  dns_db_t *updb = NULL;
2195
0
  dns_catz_zones_t *catzs = NULL;
2196
0
  dns_catz_zone_t *oldcatz = NULL, *newcatz = NULL;
2197
0
  isc_result_t result;
2198
0
  isc_region_t r;
2199
0
  dns_dbnode_t *node = NULL;
2200
0
  const dns_dbnode_t *vers_node = NULL;
2201
0
  dns_dbiterator_t *updbit = NULL;
2202
0
  dns_fixedname_t fixname;
2203
0
  dns_name_t *name = NULL;
2204
0
  dns_rdatasetiter_t *rdsiter = NULL;
2205
0
  dns_rdataset_t rdataset;
2206
0
  char bname[DNS_NAME_FORMATSIZE];
2207
0
  char cname[DNS_NAME_FORMATSIZE];
2208
0
  bool is_vers_processed = false;
2209
0
  bool is_active;
2210
0
  uint32_t vers;
2211
0
  uint32_t catz_vers;
2212
2213
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2214
0
  REQUIRE(DNS_DB_VALID(catz->updb));
2215
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catz->catzs));
2216
2217
0
  updb = catz->updb;
2218
0
  catzs = catz->catzs;
2219
2220
0
  if (atomic_load(&catzs->shuttingdown)) {
2221
0
    return ISC_R_SHUTTINGDOWN;
2222
0
  }
2223
2224
0
  dns_name_format(&updb->origin, bname, DNS_NAME_FORMATSIZE);
2225
2226
  /*
2227
   * Create a new catz in the same context as current catz.
2228
   */
2229
0
  dns_name_toregion(&updb->origin, &r);
2230
0
  LOCK(&catzs->lock);
2231
0
  if (catzs->zones == NULL) {
2232
0
    UNLOCK(&catzs->lock);
2233
0
    return ISC_R_SHUTTINGDOWN;
2234
0
  }
2235
0
  result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldcatz);
2236
0
  is_active = (result == ISC_R_SUCCESS && oldcatz->active);
2237
0
  UNLOCK(&catzs->lock);
2238
0
  if (result != ISC_R_SUCCESS) {
2239
    /* This can happen if we remove the zone in the meantime. */
2240
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2241
0
            ISC_LOG_ERROR, "catz: zone '%s' not in config",
2242
0
            bname);
2243
0
    return result;
2244
0
  }
2245
2246
0
  if (!is_active) {
2247
    /* This can happen during a reconfiguration. */
2248
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2249
0
            ISC_LOG_INFO,
2250
0
            "catz: zone '%s' is no longer active", bname);
2251
0
    return ISC_R_CANCELED;
2252
0
  }
2253
2254
0
  result = dns_db_getsoaserial(updb, oldcatz->updbversion, &vers);
2255
0
  if (result != ISC_R_SUCCESS) {
2256
    /* A zone without SOA record?!? */
2257
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2258
0
            ISC_LOG_ERROR,
2259
0
            "catz: zone '%s' has no SOA record (%s)", bname,
2260
0
            isc_result_totext(result));
2261
0
    return result;
2262
0
  }
2263
2264
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
2265
0
          "catz: updating catalog zone '%s' with serial %" PRIu32,
2266
0
          bname, vers);
2267
2268
0
  result = dns_db_createiterator(updb, DNS_DB_NONSEC3, &updbit);
2269
0
  if (result != ISC_R_SUCCESS) {
2270
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2271
0
            ISC_LOG_ERROR,
2272
0
            "catz: failed to create DB iterator - %s",
2273
0
            isc_result_totext(result));
2274
0
    return result;
2275
0
  }
2276
2277
0
  name = dns_fixedname_initname(&fixname);
2278
2279
  /*
2280
   * Take the version record to process first, because the other
2281
   * records might be processed differently depending on the version of
2282
   * the catalog zone's schema.
2283
   */
2284
0
  result = dns_name_fromstring(name, "version", &updb->origin, 0, NULL);
2285
0
  if (result != ISC_R_SUCCESS) {
2286
0
    dns_dbiterator_destroy(&updbit);
2287
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2288
0
            ISC_LOG_ERROR,
2289
0
            "catz: failed to create name from string - %s",
2290
0
            isc_result_totext(result));
2291
0
    return result;
2292
0
  }
2293
2294
0
  result = dns_dbiterator_seek(updbit, name);
2295
0
  if (result != ISC_R_SUCCESS) {
2296
0
    dns_dbiterator_destroy(&updbit);
2297
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2298
0
            ISC_LOG_ERROR,
2299
0
            "catz: zone '%s' has no 'version' record (%s) "
2300
0
            "and will not be processed",
2301
0
            bname, isc_result_totext(result));
2302
2303
0
    return result;
2304
0
  }
2305
2306
0
  newcatz = dns_catz_zone_new(catzs, &updb->origin);
2307
0
  name = dns_fixedname_initname(&fixname);
2308
2309
  /*
2310
   * Iterate over database to fill the new zone.
2311
   */
2312
0
  while (result == ISC_R_SUCCESS) {
2313
0
    if (atomic_load(&catzs->shuttingdown)) {
2314
0
      result = ISC_R_SHUTTINGDOWN;
2315
0
      break;
2316
0
    }
2317
2318
0
    result = dns_dbiterator_current(updbit, &node, name);
2319
0
    if (result != ISC_R_SUCCESS) {
2320
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2321
0
              DNS_LOGMODULE_CATZ, ISC_LOG_ERROR,
2322
0
              "catz: failed to get db iterator - %s",
2323
0
              isc_result_totext(result));
2324
0
      break;
2325
0
    }
2326
2327
0
    result = dns_dbiterator_pause(updbit);
2328
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
2329
2330
0
    if (!is_vers_processed) {
2331
      /* Keep the version node to skip it later in the loop */
2332
0
      vers_node = node;
2333
0
    } else if (node == vers_node) {
2334
      /* Skip the already processed version node */
2335
0
      dns_db_detachnode(&node);
2336
0
      result = dns_dbiterator_next(updbit);
2337
0
      continue;
2338
0
    }
2339
2340
0
    result = dns_db_allrdatasets(updb, node, oldcatz->updbversion,
2341
0
               0, 0, &rdsiter);
2342
0
    if (result != ISC_R_SUCCESS) {
2343
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2344
0
              DNS_LOGMODULE_CATZ, ISC_LOG_ERROR,
2345
0
              "catz: failed to fetch rrdatasets - %s",
2346
0
              isc_result_totext(result));
2347
0
      dns_db_detachnode(&node);
2348
0
      break;
2349
0
    }
2350
2351
0
    dns_rdataset_init(&rdataset);
2352
0
    DNS_RDATASETITER_FOREACH(rdsiter) {
2353
0
      dns_rdatasetiter_current(rdsiter, &rdataset);
2354
2355
      /*
2356
       * Skip processing DNSSEC-related and ZONEMD types,
2357
       * because we are not interested in them in the context
2358
       * of a catalog zone, and processing them will fail
2359
       * and produce an unnecessary warning message.
2360
       */
2361
0
      if (!catz_rdatatype_is_processable(rdataset.type)) {
2362
0
        dns_rdataset_disassociate(&rdataset);
2363
0
        continue;
2364
0
      }
2365
2366
0
      result = dns__catz_update_process(newcatz, name,
2367
0
                &rdataset);
2368
0
      if (result != ISC_R_SUCCESS) {
2369
0
        char typebuf[DNS_RDATATYPE_FORMATSIZE];
2370
0
        char classbuf[DNS_RDATACLASS_FORMATSIZE];
2371
2372
0
        dns_name_format(name, cname,
2373
0
            DNS_NAME_FORMATSIZE);
2374
0
        dns_rdataclass_format(rdataset.rdclass,
2375
0
                  classbuf,
2376
0
                  sizeof(classbuf));
2377
0
        dns_rdatatype_format(rdataset.type, typebuf,
2378
0
                 sizeof(typebuf));
2379
0
        isc_log_write(DNS_LOGCATEGORY_GENERAL,
2380
0
                DNS_LOGMODULE_CATZ,
2381
0
                ISC_LOG_WARNING,
2382
0
                "catz: invalid record in catalog "
2383
0
                "zone - %s %s %s (%s) - ignoring",
2384
0
                cname, classbuf, typebuf,
2385
0
                isc_result_totext(result));
2386
0
      }
2387
2388
0
      dns_rdataset_disassociate(&rdataset);
2389
0
    }
2390
2391
0
    dns_rdatasetiter_destroy(&rdsiter);
2392
2393
0
    dns_db_detachnode(&node);
2394
2395
0
    if (!is_vers_processed) {
2396
0
      is_vers_processed = true;
2397
0
      result = dns_dbiterator_first(updbit);
2398
0
    } else {
2399
0
      result = dns_dbiterator_next(updbit);
2400
0
    }
2401
0
  }
2402
2403
0
  dns_dbiterator_destroy(&updbit);
2404
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2405
0
          ISC_LOG_DEBUG(3),
2406
0
          "catz: update_from_db: iteration finished: %s",
2407
0
          isc_result_totext(result));
2408
2409
  /*
2410
   * Check catalog zone version compatibilites.
2411
   */
2412
0
  catz_vers = (newcatz->version == DNS_CATZ_VERSION_UNDEFINED)
2413
0
          ? oldcatz->version
2414
0
          : newcatz->version;
2415
0
  if (catz_vers == DNS_CATZ_VERSION_UNDEFINED) {
2416
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2417
0
            ISC_LOG_WARNING,
2418
0
            "catz: zone '%s' version is not set", bname);
2419
0
    newcatz->broken = true;
2420
0
  } else if (catz_vers != 1 && catz_vers != 2) {
2421
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2422
0
            ISC_LOG_WARNING,
2423
0
            "catz: zone '%s' unsupported version "
2424
0
            "'%" PRIu32 "'",
2425
0
            bname, catz_vers);
2426
0
    newcatz->broken = true;
2427
0
  } else {
2428
0
    oldcatz->version = catz_vers;
2429
0
  }
2430
2431
0
  if (newcatz->broken) {
2432
0
    dns_name_format(name, cname, DNS_NAME_FORMATSIZE);
2433
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2434
0
            ISC_LOG_ERROR,
2435
0
            "catz: new catalog zone '%s' is broken and "
2436
0
            "will not be processed",
2437
0
            bname);
2438
0
    dns_catz_zone_detach(&newcatz);
2439
0
    return ISC_R_FAILURE;
2440
0
  }
2441
2442
  /*
2443
   * Finally merge new zone into old zone.
2444
   */
2445
0
  result = dns__catz_zones_merge(oldcatz, newcatz);
2446
0
  dns_catz_zone_detach(&newcatz);
2447
0
  if (result != ISC_R_SUCCESS) {
2448
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2449
0
            ISC_LOG_ERROR, "catz: failed merging zones: %s",
2450
0
            isc_result_totext(result));
2451
2452
0
    return result;
2453
0
  }
2454
2455
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2456
0
          ISC_LOG_DEBUG(3),
2457
0
          "catz: update_from_db: new zone merged");
2458
2459
0
  return result;
2460
0
}
2461
2462
static void
2463
0
dns__catz_done_cb(void *data, isc_result_t result) {
2464
0
  dns_catz_zone_t *catz = (dns_catz_zone_t *)data;
2465
0
  char dname[DNS_NAME_FORMATSIZE];
2466
2467
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2468
2469
0
  LOCK(&catz->catzs->lock);
2470
0
  catz->updaterunning = false;
2471
2472
0
  dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
2473
2474
0
  if (catz->updatepending && !atomic_load(&catz->catzs->shuttingdown)) {
2475
    /* Restart the timer */
2476
0
    dns__catz_timer_start(catz);
2477
0
  }
2478
2479
0
  dns_db_closeversion(catz->updb, &catz->updbversion, false);
2480
0
  dns_db_detach(&catz->updb);
2481
2482
0
  UNLOCK(&catz->catzs->lock);
2483
2484
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
2485
0
          "catz: %s: reload done: %s", dname,
2486
0
          isc_result_totext(result));
2487
2488
0
  dns_catz_zone_unref(catz);
2489
0
}
2490
2491
void
2492
0
dns_catz_prereconfig(dns_catz_zones_t *catzs) {
2493
0
  isc_result_t result;
2494
0
  isc_ht_iter_t *iter = NULL;
2495
2496
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2497
2498
0
  LOCK(&catzs->lock);
2499
0
  isc_ht_iter_create(catzs->zones, &iter);
2500
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
2501
0
       result = isc_ht_iter_next(iter))
2502
0
  {
2503
0
    dns_catz_zone_t *catz = NULL;
2504
0
    isc_ht_iter_current(iter, (void **)&catz);
2505
0
    catz->active = false;
2506
0
  }
2507
0
  UNLOCK(&catzs->lock);
2508
0
  INSIST(result == ISC_R_NOMORE);
2509
0
  isc_ht_iter_destroy(&iter);
2510
0
}
2511
2512
void
2513
0
dns_catz_postreconfig(dns_catz_zones_t *catzs) {
2514
0
  isc_result_t result;
2515
0
  dns_catz_zone_t *newcatz = NULL;
2516
0
  isc_ht_iter_t *iter = NULL;
2517
2518
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2519
2520
0
  LOCK(&catzs->lock);
2521
0
  isc_ht_iter_create(catzs->zones, &iter);
2522
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) {
2523
0
    dns_catz_zone_t *catz = NULL;
2524
2525
0
    isc_ht_iter_current(iter, (void **)&catz);
2526
0
    if (!catz->active) {
2527
0
      char cname[DNS_NAME_FORMATSIZE];
2528
0
      dns_name_format(&catz->name, cname,
2529
0
          DNS_NAME_FORMATSIZE);
2530
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2531
0
              DNS_LOGMODULE_CATZ, ISC_LOG_WARNING,
2532
0
              "catz: removing catalog zone %s", cname);
2533
2534
      /*
2535
       * Merge the old zone with an empty one to remove
2536
       * all members.
2537
       */
2538
0
      newcatz = dns_catz_zone_new(catzs, &catz->name);
2539
0
      dns__catz_zones_merge(catz, newcatz);
2540
0
      dns_catz_zone_detach(&newcatz);
2541
2542
      /* Make sure that we have an empty catalog zone. */
2543
0
      INSIST(isc_ht_count(catz->entries) == 0);
2544
0
      result = isc_ht_iter_delcurrent_next(iter);
2545
0
      dns_catz_zone_detach(&catz);
2546
0
    } else {
2547
0
      result = isc_ht_iter_next(iter);
2548
0
    }
2549
0
  }
2550
0
  UNLOCK(&catzs->lock);
2551
0
  RUNTIME_CHECK(result == ISC_R_NOMORE);
2552
0
  isc_ht_iter_destroy(&iter);
2553
0
}
2554
2555
void
2556
0
dns_catz_zone_prereconfig(dns_catz_zone_t *catz) {
2557
0
  LOCK(&catz->lock);
2558
0
}
2559
2560
void
2561
0
dns_catz_zone_postreconfig(dns_catz_zone_t *catz) {
2562
0
  UNLOCK(&catz->lock);
2563
0
}
2564
2565
void
2566
dns_catz_zone_for_each_entry2(dns_catz_zone_t *catz, dns_catz_entry_cb2 cb,
2567
0
            void *arg1, void *arg2) {
2568
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2569
2570
0
  isc_ht_iter_t *iter = NULL;
2571
0
  isc_result_t result;
2572
2573
0
  LOCK(&catz->catzs->lock);
2574
0
  isc_ht_iter_create(catz->entries, &iter);
2575
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
2576
0
       result = isc_ht_iter_next(iter))
2577
0
  {
2578
0
    dns_catz_entry_t *entry = NULL;
2579
2580
0
    isc_ht_iter_current(iter, (void **)&entry);
2581
0
    cb(entry, arg1, arg2);
2582
0
  }
2583
0
  isc_ht_iter_destroy(&iter);
2584
0
  UNLOCK(&catz->catzs->lock);
2585
0
}