Coverage Report

Created: 2025-08-26 06:59

/src/bind9/lib/dns/catz.c
Line
Count
Source (jump to first uncovered line)
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
ISC_REFCOUNT_IMPL(dns_catz_zone, dns__catz_zone_destroy);
1095
ISC_REFCOUNT_IMPL(dns_catz_zones, dns__catz_zones_destroy);
1096
#endif
1097
1098
typedef enum {
1099
  CATZ_OPT_NONE,
1100
  CATZ_OPT_ZONES,
1101
  CATZ_OPT_COO,
1102
  CATZ_OPT_VERSION,
1103
  CATZ_OPT_CUSTOM_START, /* CATZ custom properties must go below this */
1104
  CATZ_OPT_EXT,
1105
  CATZ_OPT_PRIMARIES,
1106
  CATZ_OPT_ALLOW_QUERY,
1107
  CATZ_OPT_ALLOW_TRANSFER,
1108
} catz_opt_t;
1109
1110
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
  result = dns_rdataset_first(value);
1204
0
  if (result != ISC_R_SUCCESS) {
1205
0
    return result;
1206
0
  }
1207
1208
0
  dns_rdata_init(&rdata);
1209
0
  dns_rdataset_current(value, &rdata);
1210
1211
0
  result = dns_rdata_tostruct(&rdata, &ptr, NULL);
1212
0
  if (result != ISC_R_SUCCESS) {
1213
0
    return result;
1214
0
  }
1215
1216
0
  if (dns_name_countlabels(&ptr.ptr) == 0) {
1217
0
    result = ISC_R_FAILURE;
1218
0
    goto cleanup;
1219
0
  }
1220
1221
0
  result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1222
0
           (void **)&entry);
1223
0
  if (result != ISC_R_SUCCESS) {
1224
    /* The entry was not found .*/
1225
0
    goto cleanup;
1226
0
  }
1227
1228
0
  if (dns_name_countlabels(&entry->name) == 0) {
1229
0
    result = ISC_R_FAILURE;
1230
0
    goto cleanup;
1231
0
  }
1232
1233
0
  catz_coo_add(catz, entry, &ptr.ptr);
1234
1235
0
cleanup:
1236
0
  dns_rdata_freestruct(&ptr);
1237
1238
0
  return result;
1239
0
}
1240
1241
static isc_result_t
1242
catz_process_zones_entry(dns_catz_zone_t *catz, dns_rdataset_t *value,
1243
0
       dns_label_t *mhash) {
1244
0
  isc_result_t result;
1245
0
  dns_rdata_t rdata;
1246
0
  dns_rdata_ptr_t ptr;
1247
0
  dns_catz_entry_t *entry = NULL;
1248
1249
0
  if (value->type != dns_rdatatype_ptr) {
1250
0
    return ISC_R_FAILURE;
1251
0
  }
1252
1253
0
  if (dns_rdataset_count(value) != 1) {
1254
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1255
0
            ISC_LOG_WARNING,
1256
0
            "catz: member zone PTR RRset contains "
1257
0
            "more than one record, which is invalid");
1258
0
    catz->broken = true;
1259
0
    return ISC_R_FAILURE;
1260
0
  }
1261
1262
0
  result = dns_rdataset_first(value);
1263
0
  if (result != ISC_R_SUCCESS) {
1264
0
    return result;
1265
0
  }
1266
1267
0
  dns_rdata_init(&rdata);
1268
0
  dns_rdataset_current(value, &rdata);
1269
1270
0
  result = dns_rdata_tostruct(&rdata, &ptr, NULL);
1271
0
  if (result != ISC_R_SUCCESS) {
1272
0
    return result;
1273
0
  }
1274
1275
0
  result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1276
0
           (void **)&entry);
1277
0
  if (result == ISC_R_SUCCESS) {
1278
0
    if (dns_name_countlabels(&entry->name) != 0) {
1279
      /* We have a duplicate. */
1280
0
      dns_rdata_freestruct(&ptr);
1281
0
      return ISC_R_FAILURE;
1282
0
    } else {
1283
0
      dns_name_dup(&ptr.ptr, catz->catzs->mctx, &entry->name);
1284
0
    }
1285
0
  } else {
1286
0
    entry = dns_catz_entry_new(catz->catzs->mctx, &ptr.ptr);
1287
1288
0
    result = isc_ht_add(catz->entries, mhash->base, mhash->length,
1289
0
            entry);
1290
0
  }
1291
0
  INSIST(result == ISC_R_SUCCESS);
1292
1293
0
  dns_rdata_freestruct(&ptr);
1294
1295
0
  return ISC_R_SUCCESS;
1296
0
}
1297
1298
static isc_result_t
1299
0
catz_process_version(dns_catz_zone_t *catz, dns_rdataset_t *value) {
1300
0
  isc_result_t result;
1301
0
  dns_rdata_t rdata;
1302
0
  dns_rdata_txt_t rdatatxt;
1303
0
  dns_rdata_txt_string_t rdatastr;
1304
0
  uint32_t tversion;
1305
0
  char t[16];
1306
1307
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1308
0
  REQUIRE(DNS_RDATASET_VALID(value));
1309
1310
0
  if (value->type != dns_rdatatype_txt) {
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: 'version' property TXT 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
  result = dns_rdataset_first(value);
1324
0
  if (result != ISC_R_SUCCESS) {
1325
0
    return result;
1326
0
  }
1327
1328
0
  dns_rdata_init(&rdata);
1329
0
  dns_rdataset_current(value, &rdata);
1330
1331
0
  result = dns_rdata_tostruct(&rdata, &rdatatxt, NULL);
1332
0
  if (result != ISC_R_SUCCESS) {
1333
0
    return result;
1334
0
  }
1335
1336
0
  result = dns_rdata_txt_first(&rdatatxt);
1337
0
  if (result != ISC_R_SUCCESS) {
1338
0
    goto cleanup;
1339
0
  }
1340
1341
0
  result = dns_rdata_txt_current(&rdatatxt, &rdatastr);
1342
0
  if (result != ISC_R_SUCCESS) {
1343
0
    goto cleanup;
1344
0
  }
1345
1346
0
  result = dns_rdata_txt_next(&rdatatxt);
1347
0
  if (result != ISC_R_NOMORE) {
1348
0
    result = ISC_R_FAILURE;
1349
0
    goto cleanup;
1350
0
  }
1351
0
  if (rdatastr.length > 15) {
1352
0
    result = ISC_R_BADNUMBER;
1353
0
    goto cleanup;
1354
0
  }
1355
0
  memmove(t, rdatastr.data, rdatastr.length);
1356
0
  t[rdatastr.length] = 0;
1357
0
  result = isc_parse_uint32(&tversion, t, 10);
1358
0
  if (result != ISC_R_SUCCESS) {
1359
0
    goto cleanup;
1360
0
  }
1361
0
  catz->version = tversion;
1362
0
  result = ISC_R_SUCCESS;
1363
1364
0
cleanup:
1365
0
  dns_rdata_freestruct(&rdatatxt);
1366
0
  if (result != ISC_R_SUCCESS) {
1367
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1368
0
            ISC_LOG_WARNING,
1369
0
            "catz: invalid record for the catalog "
1370
0
            "zone version property");
1371
0
    catz->broken = true;
1372
0
  }
1373
0
  return result;
1374
0
}
1375
1376
static isc_result_t
1377
catz_process_primaries(dns_catz_zone_t *catz, dns_ipkeylist_t *ipkl,
1378
0
           dns_rdataset_t *value, dns_name_t *name) {
1379
0
  isc_result_t result;
1380
0
  dns_rdata_in_a_t rdata_a;
1381
0
  dns_rdata_in_aaaa_t rdata_aaaa;
1382
0
  dns_rdata_txt_t rdata_txt;
1383
0
  dns_rdata_txt_string_t rdatastr;
1384
0
  dns_name_t *keyname = NULL;
1385
0
  isc_mem_t *mctx;
1386
0
  char keycbuf[DNS_NAME_FORMATSIZE];
1387
0
  isc_buffer_t keybuf;
1388
0
  unsigned int rcount;
1389
1390
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1391
0
  REQUIRE(ipkl != NULL);
1392
0
  REQUIRE(DNS_RDATASET_VALID(value));
1393
0
  REQUIRE(dns_rdataset_isassociated(value));
1394
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1395
1396
0
  mctx = catz->catzs->mctx;
1397
0
  memset(&rdata_a, 0, sizeof(rdata_a));
1398
0
  memset(&rdata_aaaa, 0, sizeof(rdata_aaaa));
1399
0
  memset(&rdata_txt, 0, sizeof(rdata_txt));
1400
0
  isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf));
1401
1402
  /*
1403
   * We have three possibilities here:
1404
   * - either empty name and IN A/IN AAAA record
1405
   * - label and IN A/IN AAAA
1406
   * - label and IN TXT - TSIG key name
1407
   */
1408
0
  if (name->length != 0) {
1409
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
1410
0
    isc_sockaddr_t sockaddr;
1411
0
    size_t i;
1412
1413
    /*
1414
     * We're pre-preparing the data once, we'll put it into
1415
     * the right spot in the primaries array once we find it.
1416
     */
1417
0
    result = dns_rdataset_first(value);
1418
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1419
0
    dns_rdataset_current(value, &rdata);
1420
0
    switch (value->type) {
1421
0
    case dns_rdatatype_a:
1422
0
      result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
1423
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1424
0
      isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0);
1425
0
      dns_rdata_freestruct(&rdata_a);
1426
0
      break;
1427
0
    case dns_rdatatype_aaaa:
1428
0
      result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
1429
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1430
0
      isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr,
1431
0
               0);
1432
0
      dns_rdata_freestruct(&rdata_aaaa);
1433
0
      break;
1434
0
    case dns_rdatatype_txt:
1435
0
      result = dns_rdata_tostruct(&rdata, &rdata_txt, NULL);
1436
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1437
1438
0
      result = dns_rdata_txt_first(&rdata_txt);
1439
0
      if (result != ISC_R_SUCCESS) {
1440
0
        dns_rdata_freestruct(&rdata_txt);
1441
0
        return result;
1442
0
      }
1443
1444
0
      result = dns_rdata_txt_current(&rdata_txt, &rdatastr);
1445
0
      if (result != ISC_R_SUCCESS) {
1446
0
        dns_rdata_freestruct(&rdata_txt);
1447
0
        return result;
1448
0
      }
1449
1450
0
      result = dns_rdata_txt_next(&rdata_txt);
1451
0
      if (result != ISC_R_NOMORE) {
1452
0
        dns_rdata_freestruct(&rdata_txt);
1453
0
        return ISC_R_FAILURE;
1454
0
      }
1455
1456
      /* rdatastr.length < DNS_NAME_MAXTEXT */
1457
0
      keyname = isc_mem_get(mctx, sizeof(*keyname));
1458
0
      dns_name_init(keyname);
1459
0
      memmove(keycbuf, rdatastr.data, rdatastr.length);
1460
0
      keycbuf[rdatastr.length] = 0;
1461
0
      dns_rdata_freestruct(&rdata_txt);
1462
0
      result = dns_name_fromstring(keyname, keycbuf,
1463
0
                 dns_rootname, 0, mctx);
1464
0
      if (result != ISC_R_SUCCESS) {
1465
0
        dns_name_free(keyname, mctx);
1466
0
        isc_mem_put(mctx, keyname, sizeof(*keyname));
1467
0
        return result;
1468
0
      }
1469
0
      break;
1470
0
    default:
1471
0
      return ISC_R_FAILURE;
1472
0
    }
1473
1474
    /*
1475
     * We have to find the appropriate labeled record in
1476
     * primaries if it exists.  In the common case we'll
1477
     * have no more than 3-4 records here, so no optimization.
1478
     */
1479
0
    for (i = 0; i < ipkl->count; i++) {
1480
0
      if (ipkl->labels[i] != NULL &&
1481
0
          !dns_name_compare(name, ipkl->labels[i]))
1482
0
      {
1483
0
        break;
1484
0
      }
1485
0
    }
1486
1487
0
    if (i < ipkl->count) { /* we have this record already */
1488
0
      if (value->type == dns_rdatatype_txt) {
1489
0
        ipkl->keys[i] = keyname;
1490
0
      } else { /* A/AAAA */
1491
0
        memmove(&ipkl->addrs[i], &sockaddr,
1492
0
          sizeof(sockaddr));
1493
0
      }
1494
0
    } else {
1495
0
      dns_ipkeylist_resize(mctx, ipkl, i + 1);
1496
1497
0
      ipkl->labels[i] = isc_mem_get(mctx,
1498
0
                  sizeof(*ipkl->labels[0]));
1499
0
      dns_name_init(ipkl->labels[i]);
1500
0
      dns_name_dup(name, mctx, ipkl->labels[i]);
1501
1502
0
      if (value->type == dns_rdatatype_txt) {
1503
0
        ipkl->keys[i] = keyname;
1504
0
      } else { /* A/AAAA */
1505
0
        memmove(&ipkl->addrs[i], &sockaddr,
1506
0
          sizeof(sockaddr));
1507
0
      }
1508
0
      ipkl->count++;
1509
0
    }
1510
0
    return ISC_R_SUCCESS;
1511
0
  }
1512
  /* else - 'simple' case - without labels */
1513
1514
0
  if (!dns_rdatatype_isaddr(value->type)) {
1515
0
    return ISC_R_FAILURE;
1516
0
  }
1517
1518
0
  rcount = dns_rdataset_count(value) + ipkl->count;
1519
1520
0
  dns_ipkeylist_resize(mctx, ipkl, rcount);
1521
1522
0
  DNS_RDATASET_FOREACH(value) {
1523
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
1524
0
    dns_rdataset_current(value, &rdata);
1525
    /*
1526
     * port 0 == take the default
1527
     */
1528
0
    if (value->type == dns_rdatatype_a) {
1529
0
      result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
1530
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1531
0
      isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
1532
0
              &rdata_a.in_addr, 0);
1533
0
      dns_rdata_freestruct(&rdata_a);
1534
0
    } else {
1535
0
      result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
1536
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1537
0
      isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count],
1538
0
               &rdata_aaaa.in6_addr, 0);
1539
0
      dns_rdata_freestruct(&rdata_aaaa);
1540
0
    }
1541
0
    ipkl->keys[ipkl->count] = NULL;
1542
0
    ipkl->labels[ipkl->count] = NULL;
1543
0
    ipkl->count++;
1544
0
  }
1545
0
  return ISC_R_SUCCESS;
1546
0
}
1547
1548
static isc_result_t
1549
catz_process_apl(dns_catz_zone_t *catz, isc_buffer_t **aclbp,
1550
0
     dns_rdataset_t *value) {
1551
0
  isc_result_t result = ISC_R_SUCCESS;
1552
0
  dns_rdata_t rdata;
1553
0
  dns_rdata_in_apl_t rdata_apl;
1554
0
  dns_rdata_apl_ent_t apl_ent;
1555
0
  isc_netaddr_t addr;
1556
0
  isc_buffer_t *aclb = NULL;
1557
0
  unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */
1558
1559
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1560
0
  REQUIRE(aclbp != NULL);
1561
0
  REQUIRE(*aclbp == NULL);
1562
0
  REQUIRE(DNS_RDATASET_VALID(value));
1563
0
  REQUIRE(dns_rdataset_isassociated(value));
1564
1565
0
  if (value->type != dns_rdatatype_apl) {
1566
0
    return ISC_R_FAILURE;
1567
0
  }
1568
1569
0
  if (dns_rdataset_count(value) > 1) {
1570
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1571
0
            ISC_LOG_WARNING,
1572
0
            "catz: more than one APL entry for member zone, "
1573
0
            "result is undefined");
1574
0
  }
1575
0
  result = dns_rdataset_first(value);
1576
0
  RUNTIME_CHECK(result == ISC_R_SUCCESS);
1577
0
  dns_rdata_init(&rdata);
1578
0
  dns_rdataset_current(value, &rdata);
1579
0
  result = dns_rdata_tostruct(&rdata, &rdata_apl, catz->catzs->mctx);
1580
0
  if (result != ISC_R_SUCCESS) {
1581
0
    return result;
1582
0
  }
1583
0
  isc_buffer_allocate(catz->catzs->mctx, &aclb, 16);
1584
0
  for (result = dns_rdata_apl_first(&rdata_apl); result == ISC_R_SUCCESS;
1585
0
       result = dns_rdata_apl_next(&rdata_apl))
1586
0
  {
1587
0
    result = dns_rdata_apl_current(&rdata_apl, &apl_ent);
1588
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1589
0
    memset(buf, 0, sizeof(buf));
1590
0
    if (apl_ent.data != NULL && apl_ent.length > 0) {
1591
0
      memmove(buf, apl_ent.data, apl_ent.length);
1592
0
    }
1593
0
    if (apl_ent.family == 1) {
1594
0
      isc_netaddr_fromin(&addr, (struct in_addr *)buf);
1595
0
    } else if (apl_ent.family == 2) {
1596
0
      isc_netaddr_fromin6(&addr, (struct in6_addr *)buf);
1597
0
    } else {
1598
0
      continue; /* xxxwpk log it or simply ignore? */
1599
0
    }
1600
0
    if (apl_ent.negative) {
1601
0
      isc_buffer_putuint8(aclb, '!');
1602
0
    }
1603
0
    isc_buffer_reserve(aclb, INET6_ADDRSTRLEN);
1604
0
    result = isc_netaddr_totext(&addr, aclb);
1605
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1606
0
    if ((apl_ent.family == 1 && apl_ent.prefix < 32) ||
1607
0
        (apl_ent.family == 2 && apl_ent.prefix < 128))
1608
0
    {
1609
0
      isc_buffer_putuint8(aclb, '/');
1610
0
      isc_buffer_printf(aclb, "%" PRId8, apl_ent.prefix);
1611
0
    }
1612
0
    isc_buffer_putstr(aclb, "; ");
1613
0
  }
1614
0
  if (result == ISC_R_NOMORE) {
1615
0
    result = ISC_R_SUCCESS;
1616
0
  } else {
1617
0
    goto cleanup;
1618
0
  }
1619
0
  *aclbp = aclb;
1620
0
  aclb = NULL;
1621
0
cleanup:
1622
0
  if (aclb != NULL) {
1623
0
    isc_buffer_free(&aclb);
1624
0
  }
1625
0
  dns_rdata_freestruct(&rdata_apl);
1626
0
  return result;
1627
0
}
1628
1629
static isc_result_t
1630
catz_process_zones_suboption(dns_catz_zone_t *catz, dns_rdataset_t *value,
1631
0
           dns_label_t *mhash, dns_name_t *name) {
1632
0
  isc_result_t result;
1633
0
  dns_catz_entry_t *entry = NULL;
1634
0
  dns_label_t option;
1635
0
  dns_name_t prefix;
1636
0
  catz_opt_t opt;
1637
0
  unsigned int suffix_labels = 1;
1638
1639
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1640
0
  REQUIRE(mhash != NULL);
1641
0
  REQUIRE(DNS_RDATASET_VALID(value));
1642
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1643
1644
0
  uint8_t labels = dns_name_countlabels(name);
1645
1646
0
  if (labels < 1) {
1647
0
    return ISC_R_FAILURE;
1648
0
  }
1649
0
  dns_name_getlabel(name, labels - 1, &option);
1650
0
  opt = catz_get_option(&option);
1651
1652
  /*
1653
   * The custom properties in version 2 schema must be placed under the
1654
   * "ext" label.
1655
   */
1656
0
  if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) {
1657
0
    if (opt != CATZ_OPT_EXT || labels < 2) {
1658
0
      return ISC_R_FAILURE;
1659
0
    }
1660
0
    suffix_labels++;
1661
0
    dns_name_getlabel(name, labels - 2, &option);
1662
0
    opt = catz_get_option(&option);
1663
0
  }
1664
1665
  /*
1666
   * We're adding this entry now, in case the option is invalid we'll get
1667
   * rid of it in verification phase.
1668
   */
1669
0
  result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1670
0
           (void **)&entry);
1671
0
  if (result != ISC_R_SUCCESS) {
1672
0
    entry = dns_catz_entry_new(catz->catzs->mctx, NULL);
1673
0
    result = isc_ht_add(catz->entries, mhash->base, mhash->length,
1674
0
            entry);
1675
0
  }
1676
0
  INSIST(result == ISC_R_SUCCESS);
1677
1678
0
  dns_name_init(&prefix);
1679
0
  dns_name_split(name, suffix_labels, &prefix, NULL);
1680
0
  switch (opt) {
1681
0
  case CATZ_OPT_COO:
1682
0
    return catz_process_coo(catz, mhash, value);
1683
0
  case CATZ_OPT_PRIMARIES:
1684
0
    return catz_process_primaries(catz, &entry->opts.masters, value,
1685
0
                &prefix);
1686
0
  case CATZ_OPT_ALLOW_QUERY:;
1687
0
    if (prefix.length != 0) {
1688
0
      return ISC_R_FAILURE;
1689
0
    }
1690
0
    return catz_process_apl(catz, &entry->opts.allow_query, value);
1691
0
  case CATZ_OPT_ALLOW_TRANSFER:
1692
0
    if (prefix.length != 0) {
1693
0
      return ISC_R_FAILURE;
1694
0
    }
1695
0
    return catz_process_apl(catz, &entry->opts.allow_transfer,
1696
0
          value);
1697
0
  default:
1698
0
    return ISC_R_FAILURE;
1699
0
  }
1700
1701
0
  return ISC_R_FAILURE;
1702
0
}
1703
1704
static void
1705
catz_entry_add_or_mod(dns_catz_zone_t *catz, isc_ht_t *ht, unsigned char *key,
1706
          size_t keysize, dns_catz_entry_t *nentry,
1707
          dns_catz_entry_t *oentry, const char *msg,
1708
0
          const char *zname, const char *czname) {
1709
0
  isc_result_t result = isc_ht_add(ht, key, (uint32_t)keysize, nentry);
1710
1711
0
  if (result != ISC_R_SUCCESS) {
1712
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1713
0
            ISC_LOG_ERROR,
1714
0
            "catz: error %s zone '%s' from catalog '%s' - %s",
1715
0
            msg, zname, czname, isc_result_totext(result));
1716
0
  }
1717
0
  if (oentry != NULL) {
1718
0
    dns_catz_entry_detach(catz, &oentry);
1719
0
    result = isc_ht_delete(catz->entries, key, (uint32_t)keysize);
1720
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
1721
0
  }
1722
0
}
1723
1724
static isc_result_t
1725
catz_process_value(dns_catz_zone_t *catz, dns_name_t *name,
1726
0
       dns_rdataset_t *rdataset) {
1727
0
  dns_label_t option;
1728
0
  dns_name_t prefix;
1729
0
  catz_opt_t opt;
1730
0
  unsigned int suffix_labels = 1;
1731
1732
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1733
0
  REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1734
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
1735
1736
0
  uint8_t labels = dns_name_countlabels(name);
1737
1738
0
  if (labels < 1) {
1739
0
    return ISC_R_FAILURE;
1740
0
  }
1741
0
  dns_name_getlabel(name, labels - 1, &option);
1742
0
  opt = catz_get_option(&option);
1743
1744
  /*
1745
   * The custom properties in version 2 schema must be placed under the
1746
   * "ext" label.
1747
   */
1748
0
  if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) {
1749
0
    if (opt != CATZ_OPT_EXT || labels < 2) {
1750
0
      return ISC_R_FAILURE;
1751
0
    }
1752
0
    suffix_labels++;
1753
0
    dns_name_getlabel(name, labels - 2, &option);
1754
0
    opt = catz_get_option(&option);
1755
0
  }
1756
1757
0
  dns_name_init(&prefix);
1758
0
  dns_name_split(name, suffix_labels, &prefix, NULL);
1759
1760
0
  switch (opt) {
1761
0
  case CATZ_OPT_ZONES:
1762
0
    return catz_process_zones(catz, rdataset, &prefix);
1763
0
  case CATZ_OPT_PRIMARIES:
1764
0
    return catz_process_primaries(catz, &catz->zoneoptions.masters,
1765
0
                rdataset, &prefix);
1766
0
  case CATZ_OPT_ALLOW_QUERY:
1767
0
    if (prefix.length != 0) {
1768
0
      return ISC_R_FAILURE;
1769
0
    }
1770
0
    return catz_process_apl(catz, &catz->zoneoptions.allow_query,
1771
0
          rdataset);
1772
0
  case CATZ_OPT_ALLOW_TRANSFER:
1773
0
    if (prefix.length != 0) {
1774
0
      return ISC_R_FAILURE;
1775
0
    }
1776
0
    return catz_process_apl(catz, &catz->zoneoptions.allow_transfer,
1777
0
          rdataset);
1778
0
  case CATZ_OPT_VERSION:
1779
0
    if (prefix.length != 0) {
1780
0
      return ISC_R_FAILURE;
1781
0
    }
1782
0
    return catz_process_version(catz, rdataset);
1783
0
  default:
1784
0
    return ISC_R_FAILURE;
1785
0
  }
1786
0
}
1787
1788
/*%<
1789
 * Process a single rdataset from a catalog zone 'catz' update, src_name is the
1790
 * record name.
1791
 *
1792
 * Requires:
1793
 * \li  'catz' is a valid dns_catz_zone_t.
1794
 * \li  'src_name' is a valid dns_name_t.
1795
 * \li  'rdataset' is valid rdataset.
1796
 */
1797
static isc_result_t
1798
dns__catz_update_process(dns_catz_zone_t *catz, const dns_name_t *src_name,
1799
0
       dns_rdataset_t *rdataset) {
1800
0
  isc_result_t result;
1801
0
  int order;
1802
0
  unsigned int nlabels;
1803
0
  dns_namereln_t nrres;
1804
0
  dns_rdata_t rdata = DNS_RDATA_INIT;
1805
0
  dns_rdata_soa_t soa;
1806
0
  dns_name_t prefix;
1807
1808
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1809
0
  REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC));
1810
1811
0
  if (rdataset->rdclass != dns_rdataclass_in) {
1812
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
1813
0
            ISC_LOG_ERROR,
1814
0
            "catz: RR found which has a non-IN class");
1815
0
    catz->broken = true;
1816
0
    return ISC_R_FAILURE;
1817
0
  }
1818
1819
0
  nrres = dns_name_fullcompare(src_name, &catz->name, &order, &nlabels);
1820
0
  if (nrres == dns_namereln_equal) {
1821
0
    if (rdataset->type == dns_rdatatype_soa) {
1822
0
      result = dns_rdataset_first(rdataset);
1823
0
      if (result != ISC_R_SUCCESS) {
1824
0
        return result;
1825
0
      }
1826
1827
0
      dns_rdataset_current(rdataset, &rdata);
1828
0
      result = dns_rdata_tostruct(&rdata, &soa, NULL);
1829
0
      RUNTIME_CHECK(result == ISC_R_SUCCESS);
1830
1831
      /*
1832
       * xxxwpk TODO do we want to save something from SOA?
1833
       */
1834
0
      dns_rdata_freestruct(&soa);
1835
0
      return result;
1836
0
    } else if (rdataset->type == dns_rdatatype_ns) {
1837
0
      return ISC_R_SUCCESS;
1838
0
    } else {
1839
0
      return ISC_R_UNEXPECTED;
1840
0
    }
1841
0
  } else if (nrres != dns_namereln_subdomain) {
1842
0
    return ISC_R_UNEXPECTED;
1843
0
  }
1844
1845
0
  uint8_t labels = dns_name_countlabels(&catz->name);
1846
0
  dns_name_init(&prefix);
1847
0
  dns_name_split(src_name, labels, &prefix, NULL);
1848
0
  result = catz_process_value(catz, &prefix, rdataset);
1849
1850
0
  return result;
1851
0
}
1852
1853
static isc_result_t
1854
digest2hex(unsigned char *digest, unsigned int digestlen, char *hash,
1855
0
     size_t hashlen) {
1856
0
  unsigned int i;
1857
0
  for (i = 0; i < digestlen; i++) {
1858
0
    size_t left = hashlen - i * 2;
1859
0
    int ret = snprintf(hash + i * 2, left, "%02x", digest[i]);
1860
0
    if (ret < 0 || (size_t)ret >= left) {
1861
0
      return ISC_R_NOSPACE;
1862
0
    }
1863
0
  }
1864
0
  return ISC_R_SUCCESS;
1865
0
}
1866
1867
isc_result_t
1868
dns_catz_generate_masterfilename(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
1869
0
         isc_buffer_t **buffer) {
1870
0
  isc_buffer_t *tbuf = NULL;
1871
0
  isc_region_t r;
1872
0
  isc_result_t result;
1873
0
  size_t rlen;
1874
0
  bool special = false;
1875
1876
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1877
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1878
0
  REQUIRE(buffer != NULL && *buffer != NULL);
1879
1880
0
  isc_buffer_allocate(catz->catzs->mctx, &tbuf,
1881
0
          strlen(catz->catzs->view->name) +
1882
0
            2 * DNS_NAME_FORMATSIZE + 2);
1883
1884
0
  isc_buffer_putstr(tbuf, catz->catzs->view->name);
1885
0
  isc_buffer_putstr(tbuf, "_");
1886
0
  result = dns_name_totext(&catz->name, DNS_NAME_OMITFINALDOT, tbuf);
1887
0
  if (result != ISC_R_SUCCESS) {
1888
0
    goto cleanup;
1889
0
  }
1890
1891
0
  isc_buffer_putstr(tbuf, "_");
1892
0
  result = dns_name_totext(&entry->name, DNS_NAME_OMITFINALDOT, tbuf);
1893
0
  if (result != ISC_R_SUCCESS) {
1894
0
    goto cleanup;
1895
0
  }
1896
1897
  /*
1898
   * Search for slash and other special characters in the view and
1899
   * zone names.  Add a null terminator so we can use strpbrk(), then
1900
   * remove it.
1901
   */
1902
0
  isc_buffer_putuint8(tbuf, 0);
1903
0
  if (strpbrk(isc_buffer_base(tbuf), "\\/:") != NULL) {
1904
0
    special = true;
1905
0
  }
1906
0
  isc_buffer_subtract(tbuf, 1);
1907
1908
  /* __catz__<digest>.db */
1909
0
  rlen = (isc_md_type_get_size(ISC_MD_SHA256) * 2 + 1) + 12;
1910
1911
  /* optionally prepend with <zonedir>/ */
1912
0
  if (entry->opts.zonedir != NULL) {
1913
0
    rlen += strlen(entry->opts.zonedir) + 1;
1914
0
  }
1915
1916
0
  result = isc_buffer_reserve(*buffer, (unsigned int)rlen);
1917
0
  if (result != ISC_R_SUCCESS) {
1918
0
    goto cleanup;
1919
0
  }
1920
1921
0
  if (entry->opts.zonedir != NULL) {
1922
0
    isc_buffer_putstr(*buffer, entry->opts.zonedir);
1923
0
    isc_buffer_putstr(*buffer, "/");
1924
0
  }
1925
1926
0
  isc_buffer_usedregion(tbuf, &r);
1927
0
  isc_buffer_putstr(*buffer, "__catz__");
1928
0
  if (special || tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) {
1929
0
    unsigned char digest[ISC_MAX_MD_SIZE];
1930
0
    unsigned int digestlen;
1931
1932
    /* we can do that because digest string < 2 * DNS_NAME */
1933
0
    result = isc_md(ISC_MD_SHA256, r.base, r.length, digest,
1934
0
        &digestlen);
1935
0
    if (result != ISC_R_SUCCESS) {
1936
0
      goto cleanup;
1937
0
    }
1938
0
    result = digest2hex(digest, digestlen, (char *)r.base,
1939
0
            ISC_SHA256_DIGESTLENGTH * 2 + 1);
1940
0
    if (result != ISC_R_SUCCESS) {
1941
0
      goto cleanup;
1942
0
    }
1943
0
    isc_buffer_putstr(*buffer, (char *)r.base);
1944
0
  } else {
1945
0
    isc_buffer_copyregion(*buffer, &r);
1946
0
  }
1947
1948
0
  isc_buffer_putstr(*buffer, ".db");
1949
0
  result = ISC_R_SUCCESS;
1950
1951
0
cleanup:
1952
0
  isc_buffer_free(&tbuf);
1953
0
  return result;
1954
0
}
1955
1956
/*
1957
 * We have to generate a text buffer with regular zone config:
1958
 * zone "foo.bar" {
1959
 *  type secondary;
1960
 *  primaries { ip1 port port1; ip2 port port2; };
1961
 * }
1962
 */
1963
isc_result_t
1964
dns_catz_generate_zonecfg(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
1965
0
        isc_buffer_t **buf) {
1966
0
  isc_buffer_t *buffer = NULL;
1967
0
  isc_region_t region;
1968
0
  isc_result_t result;
1969
0
  uint32_t i;
1970
0
  isc_netaddr_t netaddr;
1971
0
  char pbuf[sizeof("65535")]; /* used for port number */
1972
0
  char zname[DNS_NAME_FORMATSIZE];
1973
1974
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1975
0
  REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1976
0
  REQUIRE(buf != NULL && *buf == NULL);
1977
1978
  /*
1979
   * The buffer will be reallocated if something won't fit,
1980
   * ISC_BUFFER_INCR seems like a good start.
1981
   */
1982
0
  isc_buffer_allocate(catz->catzs->mctx, &buffer, ISC_BUFFER_INCR);
1983
1984
0
  isc_buffer_putstr(buffer, "zone \"");
1985
0
  dns_name_totext(&entry->name, DNS_NAME_OMITFINALDOT, buffer);
1986
0
  isc_buffer_putstr(buffer, "\" { type secondary; primaries");
1987
1988
0
  isc_buffer_putstr(buffer, " { ");
1989
0
  for (i = 0; i < entry->opts.masters.count; i++) {
1990
    /*
1991
     * Every primary must have an IP address assigned.
1992
     */
1993
0
    switch (entry->opts.masters.addrs[i].type.sa.sa_family) {
1994
0
    case AF_INET:
1995
0
    case AF_INET6:
1996
0
      break;
1997
0
    default:
1998
0
      dns_name_format(&entry->name, zname,
1999
0
          DNS_NAME_FORMATSIZE);
2000
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2001
0
              DNS_LOGMODULE_CATZ, ISC_LOG_ERROR,
2002
0
              "catz: zone '%s' uses an invalid primary "
2003
0
              "(no IP address assigned)",
2004
0
              zname);
2005
0
      result = ISC_R_FAILURE;
2006
0
      goto cleanup;
2007
0
    }
2008
0
    isc_netaddr_fromsockaddr(&netaddr,
2009
0
           &entry->opts.masters.addrs[i]);
2010
0
    isc_buffer_reserve(buffer, INET6_ADDRSTRLEN);
2011
0
    result = isc_netaddr_totext(&netaddr, buffer);
2012
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
2013
2014
0
    isc_buffer_putstr(buffer, " port ");
2015
0
    snprintf(pbuf, sizeof(pbuf), "%u",
2016
0
       isc_sockaddr_getport(&entry->opts.masters.addrs[i]));
2017
0
    isc_buffer_putstr(buffer, pbuf);
2018
2019
0
    if (entry->opts.masters.keys[i] != NULL) {
2020
0
      isc_buffer_putstr(buffer, " key ");
2021
0
      result = dns_name_totext(entry->opts.masters.keys[i],
2022
0
             DNS_NAME_OMITFINALDOT, buffer);
2023
0
      if (result != ISC_R_SUCCESS) {
2024
0
        goto cleanup;
2025
0
      }
2026
0
    }
2027
2028
0
    if (entry->opts.masters.tlss[i] != NULL) {
2029
0
      isc_buffer_putstr(buffer, " tls ");
2030
0
      result = dns_name_totext(entry->opts.masters.tlss[i],
2031
0
             DNS_NAME_OMITFINALDOT, buffer);
2032
0
      if (result != ISC_R_SUCCESS) {
2033
0
        goto cleanup;
2034
0
      }
2035
0
    }
2036
0
    isc_buffer_putstr(buffer, "; ");
2037
0
  }
2038
0
  isc_buffer_putstr(buffer, "}; ");
2039
0
  if (!entry->opts.in_memory) {
2040
0
    isc_buffer_putstr(buffer, "file \"");
2041
0
    result = dns_catz_generate_masterfilename(catz, entry, &buffer);
2042
0
    if (result != ISC_R_SUCCESS) {
2043
0
      goto cleanup;
2044
0
    }
2045
0
    isc_buffer_putstr(buffer, "\"; ");
2046
0
  }
2047
0
  if (entry->opts.allow_query != NULL) {
2048
0
    isc_buffer_putstr(buffer, "allow-query { ");
2049
0
    isc_buffer_usedregion(entry->opts.allow_query, &region);
2050
0
    isc_buffer_copyregion(buffer, &region);
2051
0
    isc_buffer_putstr(buffer, "}; ");
2052
0
  }
2053
0
  if (entry->opts.allow_transfer != NULL) {
2054
0
    isc_buffer_putstr(buffer, "allow-transfer { ");
2055
0
    isc_buffer_usedregion(entry->opts.allow_transfer, &region);
2056
0
    isc_buffer_copyregion(buffer, &region);
2057
0
    isc_buffer_putstr(buffer, "}; ");
2058
0
  }
2059
2060
0
  isc_buffer_putstr(buffer, "};");
2061
0
  *buf = buffer;
2062
2063
0
  return ISC_R_SUCCESS;
2064
2065
0
cleanup:
2066
0
  isc_buffer_free(&buffer);
2067
0
  return result;
2068
0
}
2069
2070
static void
2071
0
dns__catz_timer_cb(void *arg) {
2072
0
  char domain[DNS_NAME_FORMATSIZE];
2073
0
  dns_catz_zone_t *catz = (dns_catz_zone_t *)arg;
2074
2075
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2076
2077
0
  if (atomic_load(&catz->catzs->shuttingdown)) {
2078
0
    return;
2079
0
  }
2080
2081
0
  LOCK(&catz->catzs->lock);
2082
2083
0
  INSIST(DNS_DB_VALID(catz->db));
2084
0
  INSIST(catz->dbversion != NULL);
2085
0
  INSIST(catz->updb == NULL);
2086
0
  INSIST(catz->updbversion == NULL);
2087
2088
0
  catz->updatepending = false;
2089
0
  catz->updaterunning = true;
2090
0
  catz->updateresult = ISC_R_UNSET;
2091
2092
0
  dns_name_format(&catz->name, domain, DNS_NAME_FORMATSIZE);
2093
2094
0
  if (!catz->active) {
2095
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2096
0
            ISC_LOG_INFO,
2097
0
            "catz: %s: no longer active, reload is canceled",
2098
0
            domain);
2099
0
    catz->updaterunning = false;
2100
0
    catz->updateresult = ISC_R_CANCELED;
2101
0
    goto exit;
2102
0
  }
2103
2104
0
  dns_db_attach(catz->db, &catz->updb);
2105
0
  catz->updbversion = catz->dbversion;
2106
0
  catz->dbversion = NULL;
2107
2108
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
2109
0
          "catz: %s: reload start", domain);
2110
2111
0
  dns_catz_zone_ref(catz);
2112
0
  isc_work_enqueue(catz->loop, dns__catz_update_cb, dns__catz_done_cb,
2113
0
       catz);
2114
2115
0
exit:
2116
0
  isc_timer_destroy(&catz->updatetimer);
2117
0
  catz->loop = NULL;
2118
2119
0
  catz->lastupdated = isc_time_now();
2120
2121
0
  UNLOCK(&catz->catzs->lock);
2122
0
}
2123
2124
isc_result_t
2125
0
dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
2126
0
  dns_catz_zones_t *catzs = NULL;
2127
0
  dns_catz_zone_t *catz = NULL;
2128
0
  isc_result_t result = ISC_R_SUCCESS;
2129
0
  isc_region_t r;
2130
2131
0
  REQUIRE(DNS_DB_VALID(db));
2132
0
  REQUIRE(DNS_CATZ_ZONES_VALID(fn_arg));
2133
0
  catzs = (dns_catz_zones_t *)fn_arg;
2134
2135
0
  if (atomic_load(&catzs->shuttingdown)) {
2136
0
    return ISC_R_SHUTTINGDOWN;
2137
0
  }
2138
2139
0
  dns_name_toregion(&db->origin, &r);
2140
2141
0
  LOCK(&catzs->lock);
2142
0
  if (catzs->zones == NULL) {
2143
0
    result = ISC_R_SHUTTINGDOWN;
2144
0
    goto cleanup;
2145
0
  }
2146
0
  result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&catz);
2147
0
  if (result != ISC_R_SUCCESS) {
2148
0
    goto cleanup;
2149
0
  }
2150
2151
  /* New zone came as AXFR */
2152
0
  if (catz->db != NULL && catz->db != db) {
2153
    /* Old db cleanup. */
2154
0
    if (catz->dbversion != NULL) {
2155
0
      dns_db_closeversion(catz->db, &catz->dbversion, false);
2156
0
    }
2157
0
    dns_db_updatenotify_unregister(
2158
0
      catz->db, dns_catz_dbupdate_callback, catz->catzs);
2159
0
    dns_db_detach(&catz->db);
2160
0
  }
2161
0
  if (catz->db == NULL) {
2162
    /* New db registration. */
2163
0
    dns_db_attach(db, &catz->db);
2164
0
    dns_db_updatenotify_register(db, dns_catz_dbupdate_callback,
2165
0
               catz->catzs);
2166
0
  }
2167
2168
0
  if (!catz->updatepending && !catz->updaterunning) {
2169
0
    catz->updatepending = true;
2170
0
    dns_db_currentversion(db, &catz->dbversion);
2171
0
    dns__catz_timer_start(catz);
2172
0
  } else {
2173
0
    char dname[DNS_NAME_FORMATSIZE];
2174
2175
0
    catz->updatepending = true;
2176
0
    dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
2177
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2178
0
            ISC_LOG_DEBUG(3),
2179
0
            "catz: %s: update already queued or running",
2180
0
            dname);
2181
0
    if (catz->dbversion != NULL) {
2182
0
      dns_db_closeversion(catz->db, &catz->dbversion, false);
2183
0
    }
2184
0
    dns_db_currentversion(catz->db, &catz->dbversion);
2185
0
  }
2186
2187
0
cleanup:
2188
0
  UNLOCK(&catzs->lock);
2189
2190
0
  return result;
2191
0
}
2192
2193
void
2194
0
dns_catz_dbupdate_unregister(dns_db_t *db, dns_catz_zones_t *catzs) {
2195
0
  REQUIRE(DNS_DB_VALID(db));
2196
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2197
2198
0
  dns_db_updatenotify_unregister(db, dns_catz_dbupdate_callback, catzs);
2199
0
}
2200
2201
void
2202
0
dns_catz_dbupdate_register(dns_db_t *db, dns_catz_zones_t *catzs) {
2203
0
  REQUIRE(DNS_DB_VALID(db));
2204
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2205
2206
0
  dns_db_updatenotify_register(db, dns_catz_dbupdate_callback, catzs);
2207
0
}
2208
2209
static bool
2210
0
catz_rdatatype_is_processable(const dns_rdatatype_t type) {
2211
0
  return !dns_rdatatype_isdnssec(type) && type != dns_rdatatype_cds &&
2212
0
         type != dns_rdatatype_cdnskey && type != dns_rdatatype_zonemd;
2213
0
}
2214
2215
/*
2216
 * Process an updated database for a catalog zone.
2217
 * It creates a new catz, iterates over database to fill it with content, and
2218
 * then merges new catz into old catz.
2219
 */
2220
static void
2221
0
dns__catz_update_cb(void *data) {
2222
0
  dns_catz_zone_t *catz = (dns_catz_zone_t *)data;
2223
0
  dns_db_t *updb = NULL;
2224
0
  dns_catz_zones_t *catzs = NULL;
2225
0
  dns_catz_zone_t *oldcatz = NULL, *newcatz = NULL;
2226
0
  isc_result_t result;
2227
0
  isc_region_t r;
2228
0
  dns_dbnode_t *node = NULL;
2229
0
  const dns_dbnode_t *vers_node = NULL;
2230
0
  dns_dbiterator_t *updbit = NULL;
2231
0
  dns_fixedname_t fixname;
2232
0
  dns_name_t *name = NULL;
2233
0
  dns_rdatasetiter_t *rdsiter = NULL;
2234
0
  dns_rdataset_t rdataset;
2235
0
  char bname[DNS_NAME_FORMATSIZE];
2236
0
  char cname[DNS_NAME_FORMATSIZE];
2237
0
  bool is_vers_processed = false;
2238
0
  bool is_active;
2239
0
  uint32_t vers;
2240
0
  uint32_t catz_vers;
2241
2242
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2243
0
  REQUIRE(DNS_DB_VALID(catz->updb));
2244
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catz->catzs));
2245
2246
0
  updb = catz->updb;
2247
0
  catzs = catz->catzs;
2248
2249
0
  if (atomic_load(&catzs->shuttingdown)) {
2250
0
    result = ISC_R_SHUTTINGDOWN;
2251
0
    goto exit;
2252
0
  }
2253
2254
0
  dns_name_format(&updb->origin, bname, DNS_NAME_FORMATSIZE);
2255
2256
  /*
2257
   * Create a new catz in the same context as current catz.
2258
   */
2259
0
  dns_name_toregion(&updb->origin, &r);
2260
0
  LOCK(&catzs->lock);
2261
0
  if (catzs->zones == NULL) {
2262
0
    UNLOCK(&catzs->lock);
2263
0
    result = ISC_R_SHUTTINGDOWN;
2264
0
    goto exit;
2265
0
  }
2266
0
  result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldcatz);
2267
0
  is_active = (result == ISC_R_SUCCESS && oldcatz->active);
2268
0
  UNLOCK(&catzs->lock);
2269
0
  if (result != ISC_R_SUCCESS) {
2270
    /* This can happen if we remove the zone in the meantime. */
2271
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2272
0
            ISC_LOG_ERROR, "catz: zone '%s' not in config",
2273
0
            bname);
2274
0
    goto exit;
2275
0
  }
2276
2277
0
  if (!is_active) {
2278
    /* This can happen during a reconfiguration. */
2279
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2280
0
            ISC_LOG_INFO,
2281
0
            "catz: zone '%s' is no longer active", bname);
2282
0
    result = ISC_R_CANCELED;
2283
0
    goto exit;
2284
0
  }
2285
2286
0
  result = dns_db_getsoaserial(updb, oldcatz->updbversion, &vers);
2287
0
  if (result != ISC_R_SUCCESS) {
2288
    /* A zone without SOA record?!? */
2289
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2290
0
            ISC_LOG_ERROR,
2291
0
            "catz: zone '%s' has no SOA record (%s)", bname,
2292
0
            isc_result_totext(result));
2293
0
    goto exit;
2294
0
  }
2295
2296
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
2297
0
          "catz: updating catalog zone '%s' with serial %" PRIu32,
2298
0
          bname, vers);
2299
2300
0
  result = dns_db_createiterator(updb, DNS_DB_NONSEC3, &updbit);
2301
0
  if (result != ISC_R_SUCCESS) {
2302
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2303
0
            ISC_LOG_ERROR,
2304
0
            "catz: failed to create DB iterator - %s",
2305
0
            isc_result_totext(result));
2306
0
    goto exit;
2307
0
  }
2308
2309
0
  name = dns_fixedname_initname(&fixname);
2310
2311
  /*
2312
   * Take the version record to process first, because the other
2313
   * records might be processed differently depending on the version of
2314
   * the catalog zone's schema.
2315
   */
2316
0
  result = dns_name_fromstring(name, "version", &updb->origin, 0, NULL);
2317
0
  if (result != ISC_R_SUCCESS) {
2318
0
    dns_dbiterator_destroy(&updbit);
2319
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2320
0
            ISC_LOG_ERROR,
2321
0
            "catz: failed to create name from string - %s",
2322
0
            isc_result_totext(result));
2323
0
    goto exit;
2324
0
  }
2325
2326
0
  result = dns_dbiterator_seek(updbit, name);
2327
0
  if (result != ISC_R_SUCCESS) {
2328
0
    dns_dbiterator_destroy(&updbit);
2329
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2330
0
            ISC_LOG_ERROR,
2331
0
            "catz: zone '%s' has no 'version' record (%s) "
2332
0
            "and will not be processed",
2333
0
            bname, isc_result_totext(result));
2334
0
    goto exit;
2335
0
  }
2336
2337
0
  newcatz = dns_catz_zone_new(catzs, &updb->origin);
2338
0
  name = dns_fixedname_initname(&fixname);
2339
2340
  /*
2341
   * Iterate over database to fill the new zone.
2342
   */
2343
0
  while (result == ISC_R_SUCCESS) {
2344
0
    if (atomic_load(&catzs->shuttingdown)) {
2345
0
      result = ISC_R_SHUTTINGDOWN;
2346
0
      break;
2347
0
    }
2348
2349
0
    result = dns_dbiterator_current(updbit, &node, name);
2350
0
    if (result != ISC_R_SUCCESS) {
2351
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2352
0
              DNS_LOGMODULE_CATZ, ISC_LOG_ERROR,
2353
0
              "catz: failed to get db iterator - %s",
2354
0
              isc_result_totext(result));
2355
0
      break;
2356
0
    }
2357
2358
0
    result = dns_dbiterator_pause(updbit);
2359
0
    RUNTIME_CHECK(result == ISC_R_SUCCESS);
2360
2361
0
    if (!is_vers_processed) {
2362
      /* Keep the version node to skip it later in the loop */
2363
0
      vers_node = node;
2364
0
    } else if (node == vers_node) {
2365
      /* Skip the already processed version node */
2366
0
      dns_db_detachnode(&node);
2367
0
      result = dns_dbiterator_next(updbit);
2368
0
      continue;
2369
0
    }
2370
2371
0
    result = dns_db_allrdatasets(updb, node, oldcatz->updbversion,
2372
0
               0, 0, &rdsiter);
2373
0
    if (result != ISC_R_SUCCESS) {
2374
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2375
0
              DNS_LOGMODULE_CATZ, ISC_LOG_ERROR,
2376
0
              "catz: failed to fetch rrdatasets - %s",
2377
0
              isc_result_totext(result));
2378
0
      dns_db_detachnode(&node);
2379
0
      break;
2380
0
    }
2381
2382
0
    dns_rdataset_init(&rdataset);
2383
0
    DNS_RDATASETITER_FOREACH(rdsiter) {
2384
0
      dns_rdatasetiter_current(rdsiter, &rdataset);
2385
2386
      /*
2387
       * Skip processing DNSSEC-related and ZONEMD types,
2388
       * because we are not interested in them in the context
2389
       * of a catalog zone, and processing them will fail
2390
       * and produce an unnecessary warning message.
2391
       */
2392
0
      if (!catz_rdatatype_is_processable(rdataset.type)) {
2393
0
        dns_rdataset_disassociate(&rdataset);
2394
0
        continue;
2395
0
      }
2396
2397
      /*
2398
       * Although newcatz->coos is accessed in
2399
       * catz_process_coo() in the call-chain below, we don't
2400
       * need to hold the newcatz->lock, because the newcatz
2401
       * is still local to this thread and function and
2402
       * newcatz->coos can't be accessed from the outside
2403
       * until dns__catz_zones_merge() has been called.
2404
       */
2405
0
      result = dns__catz_update_process(newcatz, name,
2406
0
                &rdataset);
2407
0
      if (result != ISC_R_SUCCESS) {
2408
0
        char typebuf[DNS_RDATATYPE_FORMATSIZE];
2409
0
        char classbuf[DNS_RDATACLASS_FORMATSIZE];
2410
2411
0
        dns_name_format(name, cname,
2412
0
            DNS_NAME_FORMATSIZE);
2413
0
        dns_rdataclass_format(rdataset.rdclass,
2414
0
                  classbuf,
2415
0
                  sizeof(classbuf));
2416
0
        dns_rdatatype_format(rdataset.type, typebuf,
2417
0
                 sizeof(typebuf));
2418
0
        isc_log_write(DNS_LOGCATEGORY_GENERAL,
2419
0
                DNS_LOGMODULE_CATZ,
2420
0
                ISC_LOG_WARNING,
2421
0
                "catz: invalid record in catalog "
2422
0
                "zone - %s %s %s (%s) - ignoring",
2423
0
                cname, classbuf, typebuf,
2424
0
                isc_result_totext(result));
2425
0
      }
2426
2427
0
      dns_rdataset_disassociate(&rdataset);
2428
0
    }
2429
2430
0
    dns_rdatasetiter_destroy(&rdsiter);
2431
2432
0
    dns_db_detachnode(&node);
2433
2434
0
    if (!is_vers_processed) {
2435
0
      is_vers_processed = true;
2436
0
      result = dns_dbiterator_first(updbit);
2437
0
    } else {
2438
0
      result = dns_dbiterator_next(updbit);
2439
0
    }
2440
0
  }
2441
2442
0
  dns_dbiterator_destroy(&updbit);
2443
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2444
0
          ISC_LOG_DEBUG(3),
2445
0
          "catz: update_from_db: iteration finished: %s",
2446
0
          isc_result_totext(result));
2447
2448
  /*
2449
   * Check catalog zone version compatibilites.
2450
   */
2451
0
  catz_vers = (newcatz->version == DNS_CATZ_VERSION_UNDEFINED)
2452
0
          ? oldcatz->version
2453
0
          : newcatz->version;
2454
0
  if (catz_vers == DNS_CATZ_VERSION_UNDEFINED) {
2455
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2456
0
            ISC_LOG_WARNING,
2457
0
            "catz: zone '%s' version is not set", bname);
2458
0
    newcatz->broken = true;
2459
0
  } else if (catz_vers != 1 && catz_vers != 2) {
2460
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2461
0
            ISC_LOG_WARNING,
2462
0
            "catz: zone '%s' unsupported version "
2463
0
            "'%" PRIu32 "'",
2464
0
            bname, catz_vers);
2465
0
    newcatz->broken = true;
2466
0
  } else {
2467
0
    oldcatz->version = catz_vers;
2468
0
  }
2469
2470
0
  if (newcatz->broken) {
2471
0
    dns_name_format(name, cname, DNS_NAME_FORMATSIZE);
2472
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2473
0
            ISC_LOG_ERROR,
2474
0
            "catz: new catalog zone '%s' is broken and "
2475
0
            "will not be processed",
2476
0
            bname);
2477
0
    dns_catz_zone_detach(&newcatz);
2478
0
    result = ISC_R_FAILURE;
2479
0
    goto exit;
2480
0
  }
2481
2482
  /*
2483
   * Finally merge new zone into old zone.
2484
   */
2485
0
  result = dns__catz_zones_merge(oldcatz, newcatz);
2486
0
  dns_catz_zone_detach(&newcatz);
2487
0
  if (result != ISC_R_SUCCESS) {
2488
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2489
0
            ISC_LOG_ERROR, "catz: failed merging zones: %s",
2490
0
            isc_result_totext(result));
2491
2492
0
    goto exit;
2493
0
  }
2494
2495
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ,
2496
0
          ISC_LOG_DEBUG(3),
2497
0
          "catz: update_from_db: new zone merged");
2498
2499
0
exit:
2500
0
  catz->updateresult = result;
2501
0
}
2502
2503
static void
2504
0
dns__catz_done_cb(void *data) {
2505
0
  dns_catz_zone_t *catz = (dns_catz_zone_t *)data;
2506
0
  char dname[DNS_NAME_FORMATSIZE];
2507
2508
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2509
2510
0
  LOCK(&catz->catzs->lock);
2511
0
  catz->updaterunning = false;
2512
2513
0
  dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
2514
2515
0
  if (catz->updatepending && !atomic_load(&catz->catzs->shuttingdown)) {
2516
    /* Restart the timer */
2517
0
    dns__catz_timer_start(catz);
2518
0
  }
2519
2520
0
  dns_db_closeversion(catz->updb, &catz->updbversion, false);
2521
0
  dns_db_detach(&catz->updb);
2522
2523
0
  UNLOCK(&catz->catzs->lock);
2524
2525
0
  isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CATZ, ISC_LOG_INFO,
2526
0
          "catz: %s: reload done: %s", dname,
2527
0
          isc_result_totext(catz->updateresult));
2528
2529
0
  dns_catz_zone_unref(catz);
2530
0
}
2531
2532
void
2533
0
dns_catz_prereconfig(dns_catz_zones_t *catzs) {
2534
0
  isc_result_t result;
2535
0
  isc_ht_iter_t *iter = NULL;
2536
2537
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2538
2539
0
  LOCK(&catzs->lock);
2540
0
  isc_ht_iter_create(catzs->zones, &iter);
2541
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
2542
0
       result = isc_ht_iter_next(iter))
2543
0
  {
2544
0
    dns_catz_zone_t *catz = NULL;
2545
0
    isc_ht_iter_current(iter, (void **)&catz);
2546
0
    catz->active = false;
2547
0
  }
2548
0
  UNLOCK(&catzs->lock);
2549
0
  INSIST(result == ISC_R_NOMORE);
2550
0
  isc_ht_iter_destroy(&iter);
2551
0
}
2552
2553
void
2554
0
dns_catz_postreconfig(dns_catz_zones_t *catzs) {
2555
0
  isc_result_t result;
2556
0
  dns_catz_zone_t *newcatz = NULL;
2557
0
  isc_ht_iter_t *iter = NULL;
2558
2559
0
  REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2560
2561
0
  LOCK(&catzs->lock);
2562
0
  isc_ht_iter_create(catzs->zones, &iter);
2563
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) {
2564
0
    dns_catz_zone_t *catz = NULL;
2565
2566
0
    isc_ht_iter_current(iter, (void **)&catz);
2567
0
    if (!catz->active) {
2568
0
      char cname[DNS_NAME_FORMATSIZE];
2569
0
      dns_name_format(&catz->name, cname,
2570
0
          DNS_NAME_FORMATSIZE);
2571
0
      isc_log_write(DNS_LOGCATEGORY_GENERAL,
2572
0
              DNS_LOGMODULE_CATZ, ISC_LOG_WARNING,
2573
0
              "catz: removing catalog zone %s", cname);
2574
2575
      /*
2576
       * Merge the old zone with an empty one to remove
2577
       * all members.
2578
       */
2579
0
      newcatz = dns_catz_zone_new(catzs, &catz->name);
2580
0
      dns__catz_zones_merge(catz, newcatz);
2581
0
      dns_catz_zone_detach(&newcatz);
2582
2583
      /* Make sure that we have an empty catalog zone. */
2584
0
      INSIST(isc_ht_count(catz->entries) == 0);
2585
0
      result = isc_ht_iter_delcurrent_next(iter);
2586
0
      dns_catz_zone_detach(&catz);
2587
0
    } else {
2588
0
      result = isc_ht_iter_next(iter);
2589
0
    }
2590
0
  }
2591
0
  UNLOCK(&catzs->lock);
2592
0
  RUNTIME_CHECK(result == ISC_R_NOMORE);
2593
0
  isc_ht_iter_destroy(&iter);
2594
0
}
2595
2596
void
2597
dns_catz_zone_for_each_entry2(dns_catz_zone_t *catz, dns_catz_entry_cb2 cb,
2598
0
            void *arg1, void *arg2) {
2599
0
  REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2600
2601
0
  isc_ht_iter_t *iter = NULL;
2602
0
  isc_result_t result;
2603
2604
0
  LOCK(&catz->catzs->lock);
2605
0
  isc_ht_iter_create(catz->entries, &iter);
2606
0
  for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
2607
0
       result = isc_ht_iter_next(iter))
2608
0
  {
2609
0
    dns_catz_entry_t *entry = NULL;
2610
2611
0
    isc_ht_iter_current(iter, (void **)&entry);
2612
0
    cb(entry, arg1, arg2);
2613
0
  }
2614
0
  isc_ht_iter_destroy(&iter);
2615
0
  UNLOCK(&catz->catzs->lock);
2616
0
}