Coverage Report

Created: 2026-01-09 06:41

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