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