"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.16.7/lib/dns/catz.c" (4 Sep 2020, 55723 Bytes) of package /linux/misc/dns/bind9/9.16.7/bind-9.16.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "catz.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 /*! \file */
   13 
   14 #include <inttypes.h>
   15 #include <stdbool.h>
   16 
   17 #include <isc/hex.h>
   18 #include <isc/md.h>
   19 #include <isc/mem.h>
   20 #include <isc/parseint.h>
   21 #include <isc/print.h>
   22 #include <isc/result.h>
   23 #include <isc/task.h>
   24 #include <isc/util.h>
   25 
   26 #include <dns/catz.h>
   27 #include <dns/dbiterator.h>
   28 #include <dns/events.h>
   29 #include <dns/rdatasetiter.h>
   30 #include <dns/view.h>
   31 #include <dns/zone.h>
   32 
   33 #define DNS_CATZ_ZONE_MAGIC  ISC_MAGIC('c', 'a', 't', 'z')
   34 #define DNS_CATZ_ZONES_MAGIC ISC_MAGIC('c', 'a', 't', 's')
   35 #define DNS_CATZ_ENTRY_MAGIC ISC_MAGIC('c', 'a', 't', 'e')
   36 
   37 #define DNS_CATZ_ZONE_VALID(catz)   ISC_MAGIC_VALID(catz, DNS_CATZ_ZONE_MAGIC)
   38 #define DNS_CATZ_ZONES_VALID(catzs) ISC_MAGIC_VALID(catzs, DNS_CATZ_ZONES_MAGIC)
   39 #define DNS_CATZ_ENTRY_VALID(entry) ISC_MAGIC_VALID(entry, DNS_CATZ_ENTRY_MAGIC)
   40 
   41 /*%
   42  * Single member zone in a catalog
   43  */
   44 struct dns_catz_entry {
   45     unsigned int magic;
   46     dns_name_t name;
   47     dns_catz_options_t opts;
   48     isc_refcount_t refs;
   49 };
   50 
   51 /*%
   52  * Catalog zone
   53  */
   54 struct dns_catz_zone {
   55     unsigned int magic;
   56     dns_name_t name;
   57     dns_catz_zones_t *catzs;
   58     dns_rdata_t soa;
   59     /* key in entries is 'mhash', not domain name! */
   60     isc_ht_t *entries;
   61     /*
   62      * defoptions are taken from named.conf
   63      * zoneoptions are global options from zone
   64      */
   65     dns_catz_options_t defoptions;
   66     dns_catz_options_t zoneoptions;
   67     isc_time_t lastupdated;
   68     bool updatepending;
   69     uint32_t version;
   70 
   71     dns_db_t *db;
   72     dns_dbversion_t *dbversion;
   73 
   74     isc_timer_t *updatetimer;
   75     isc_event_t updateevent;
   76 
   77     bool active;
   78     bool db_registered;
   79 
   80     isc_refcount_t refs;
   81 };
   82 
   83 static isc_result_t
   84 catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
   85              dns_label_t *mhash);
   86 static isc_result_t
   87 catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value,
   88                  dns_label_t *mhash, dns_name_t *name);
   89 
   90 /*%
   91  * Collection of catalog zones for a view
   92  */
   93 struct dns_catz_zones {
   94     unsigned int magic;
   95     isc_ht_t *zones;
   96     isc_mem_t *mctx;
   97     isc_refcount_t refs;
   98     isc_mutex_t lock;
   99     dns_catz_zonemodmethods_t *zmm;
  100     isc_taskmgr_t *taskmgr;
  101     isc_timermgr_t *timermgr;
  102     dns_view_t *view;
  103     isc_task_t *updater;
  104 };
  105 
  106 void
  107 dns_catz_options_init(dns_catz_options_t *options) {
  108     REQUIRE(options != NULL);
  109 
  110     dns_ipkeylist_init(&options->masters);
  111 
  112     options->allow_query = NULL;
  113     options->allow_transfer = NULL;
  114 
  115     options->allow_query = NULL;
  116     options->allow_transfer = NULL;
  117 
  118     options->in_memory = false;
  119     options->min_update_interval = 5;
  120     options->zonedir = NULL;
  121 }
  122 
  123 void
  124 dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx) {
  125     REQUIRE(options != NULL);
  126     REQUIRE(mctx != NULL);
  127 
  128     if (options->masters.count != 0) {
  129         dns_ipkeylist_clear(mctx, &options->masters);
  130     }
  131     if (options->zonedir != NULL) {
  132         isc_mem_free(mctx, options->zonedir);
  133         options->zonedir = NULL;
  134     }
  135     if (options->allow_query != NULL) {
  136         isc_buffer_free(&options->allow_query);
  137     }
  138     if (options->allow_transfer != NULL) {
  139         isc_buffer_free(&options->allow_transfer);
  140     }
  141 }
  142 
  143 isc_result_t
  144 dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src,
  145               dns_catz_options_t *dst) {
  146     REQUIRE(mctx != NULL);
  147     REQUIRE(src != NULL);
  148     REQUIRE(dst != NULL);
  149     REQUIRE(dst->masters.count == 0);
  150     REQUIRE(dst->allow_query == NULL);
  151     REQUIRE(dst->allow_transfer == NULL);
  152 
  153     if (src->masters.count != 0) {
  154         dns_ipkeylist_copy(mctx, &src->masters, &dst->masters);
  155     }
  156 
  157     if (dst->zonedir != NULL) {
  158         isc_mem_free(mctx, dst->zonedir);
  159         dst->zonedir = NULL;
  160     }
  161 
  162     if (src->zonedir != NULL) {
  163         dst->zonedir = isc_mem_strdup(mctx, src->zonedir);
  164     }
  165 
  166     if (src->allow_query != NULL) {
  167         isc_buffer_dup(mctx, &dst->allow_query, src->allow_query);
  168     }
  169 
  170     if (src->allow_transfer != NULL) {
  171         isc_buffer_dup(mctx, &dst->allow_transfer, src->allow_transfer);
  172     }
  173 
  174     return (ISC_R_SUCCESS);
  175 }
  176 
  177 isc_result_t
  178 dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults,
  179                 dns_catz_options_t *opts) {
  180     REQUIRE(mctx != NULL);
  181     REQUIRE(defaults != NULL);
  182     REQUIRE(opts != NULL);
  183 
  184     if (opts->masters.count == 0 && defaults->masters.count != 0) {
  185         dns_ipkeylist_copy(mctx, &defaults->masters, &opts->masters);
  186     }
  187 
  188     if (defaults->zonedir != NULL) {
  189         opts->zonedir = isc_mem_strdup(mctx, defaults->zonedir);
  190     }
  191 
  192     if (opts->allow_query == NULL && defaults->allow_query != NULL) {
  193         isc_buffer_dup(mctx, &opts->allow_query, defaults->allow_query);
  194     }
  195     if (opts->allow_transfer == NULL && defaults->allow_transfer != NULL) {
  196         isc_buffer_dup(mctx, &opts->allow_transfer,
  197                    defaults->allow_transfer);
  198     }
  199 
  200     /* This option is always taken from config, so it's always 'default' */
  201     opts->in_memory = defaults->in_memory;
  202     return (ISC_R_SUCCESS);
  203 }
  204 
  205 isc_result_t
  206 dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain,
  207            dns_catz_entry_t **nentryp) {
  208     dns_catz_entry_t *nentry;
  209 
  210     REQUIRE(mctx != NULL);
  211     REQUIRE(nentryp != NULL && *nentryp == NULL);
  212 
  213     nentry = isc_mem_get(mctx, sizeof(dns_catz_entry_t));
  214 
  215     dns_name_init(&nentry->name, NULL);
  216     if (domain != NULL) {
  217         dns_name_dup(domain, mctx, &nentry->name);
  218     }
  219 
  220     dns_catz_options_init(&nentry->opts);
  221     isc_refcount_init(&nentry->refs, 1);
  222     nentry->magic = DNS_CATZ_ENTRY_MAGIC;
  223     *nentryp = nentry;
  224     return (ISC_R_SUCCESS);
  225 }
  226 
  227 dns_name_t *
  228 dns_catz_entry_getname(dns_catz_entry_t *entry) {
  229     REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
  230     return (&entry->name);
  231 }
  232 
  233 isc_result_t
  234 dns_catz_entry_copy(dns_catz_zone_t *zone, const dns_catz_entry_t *entry,
  235             dns_catz_entry_t **nentryp) {
  236     isc_result_t result;
  237     dns_catz_entry_t *nentry = NULL;
  238 
  239     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
  240     REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
  241     REQUIRE(nentryp != NULL && *nentryp == NULL);
  242 
  243     result = dns_catz_entry_new(zone->catzs->mctx, &entry->name, &nentry);
  244     if (result != ISC_R_SUCCESS) {
  245         return (result);
  246     }
  247 
  248     result = dns_catz_options_copy(zone->catzs->mctx, &entry->opts,
  249                        &nentry->opts);
  250     if (result != ISC_R_SUCCESS) {
  251         dns_catz_entry_detach(zone, &nentry);
  252     }
  253 
  254     *nentryp = nentry;
  255     return (result);
  256 }
  257 
  258 void
  259 dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp) {
  260     REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
  261     REQUIRE(entryp != NULL && *entryp == NULL);
  262 
  263     isc_refcount_increment(&entry->refs);
  264     *entryp = entry;
  265 }
  266 
  267 void
  268 dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp) {
  269     dns_catz_entry_t *entry;
  270 
  271     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
  272     REQUIRE(entryp != NULL);
  273     entry = *entryp;
  274     *entryp = NULL;
  275     REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
  276 
  277     if (isc_refcount_decrement(&entry->refs) == 1) {
  278         isc_mem_t *mctx = zone->catzs->mctx;
  279         entry->magic = 0;
  280         isc_refcount_destroy(&entry->refs);
  281         dns_catz_options_free(&entry->opts, mctx);
  282         if (dns_name_dynamic(&entry->name)) {
  283             dns_name_free(&entry->name, mctx);
  284         }
  285         isc_mem_put(mctx, entry, sizeof(dns_catz_entry_t));
  286     }
  287 }
  288 
  289 bool
  290 dns_catz_entry_validate(const dns_catz_entry_t *entry) {
  291     REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
  292     UNUSED(entry);
  293 
  294     return (true);
  295 }
  296 
  297 bool
  298 dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) {
  299     isc_region_t ra, rb;
  300 
  301     REQUIRE(DNS_CATZ_ENTRY_VALID(ea));
  302     REQUIRE(DNS_CATZ_ENTRY_VALID(eb));
  303 
  304     if (ea == eb) {
  305         return (true);
  306     }
  307 
  308     if (ea->opts.masters.count != eb->opts.masters.count) {
  309         return (false);
  310     }
  311 
  312     if (memcmp(ea->opts.masters.addrs, eb->opts.masters.addrs,
  313            ea->opts.masters.count * sizeof(isc_sockaddr_t)))
  314     {
  315         return (false);
  316     }
  317 
  318     /* If one is NULL and the other isn't, the entries don't match */
  319     if ((ea->opts.allow_query == NULL) != (eb->opts.allow_query == NULL)) {
  320         return (false);
  321     }
  322 
  323     /* If one is non-NULL, then they both are */
  324     if (ea->opts.allow_query != NULL) {
  325         isc_buffer_usedregion(ea->opts.allow_query, &ra);
  326         isc_buffer_usedregion(eb->opts.allow_query, &rb);
  327         if (isc_region_compare(&ra, &rb)) {
  328             return (false);
  329         }
  330     }
  331 
  332     /* Repeat the above checks with allow_transfer */
  333     if ((ea->opts.allow_transfer == NULL) !=
  334         (eb->opts.allow_transfer == NULL)) {
  335         return (false);
  336     }
  337 
  338     if (ea->opts.allow_transfer != NULL) {
  339         isc_buffer_usedregion(ea->opts.allow_transfer, &ra);
  340         isc_buffer_usedregion(eb->opts.allow_transfer, &rb);
  341         if (isc_region_compare(&ra, &rb)) {
  342             return (false);
  343         }
  344     }
  345 
  346     /* xxxwpk TODO compare dscps/keys! */
  347     return (true);
  348 }
  349 
  350 dns_name_t *
  351 dns_catz_zone_getname(dns_catz_zone_t *zone) {
  352     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
  353 
  354     return (&zone->name);
  355 }
  356 
  357 dns_catz_options_t *
  358 dns_catz_zone_getdefoptions(dns_catz_zone_t *zone) {
  359     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
  360 
  361     return (&zone->defoptions);
  362 }
  363 
  364 void
  365 dns_catz_zone_resetdefoptions(dns_catz_zone_t *zone) {
  366     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
  367 
  368     dns_catz_options_free(&zone->defoptions, zone->catzs->mctx);
  369     dns_catz_options_init(&zone->defoptions);
  370 }
  371 
  372 isc_result_t
  373 dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
  374     isc_result_t result;
  375     isc_ht_iter_t *iter1 = NULL, *iter2 = NULL;
  376     isc_ht_iter_t *iteradd = NULL, *itermod = NULL;
  377     isc_ht_t *toadd = NULL, *tomod = NULL;
  378     bool delcur = false;
  379     char czname[DNS_NAME_FORMATSIZE];
  380     char zname[DNS_NAME_FORMATSIZE];
  381     dns_catz_zoneop_fn_t addzone, modzone, delzone;
  382 
  383     REQUIRE(DNS_CATZ_ZONE_VALID(newzone));
  384     REQUIRE(DNS_CATZ_ZONE_VALID(target));
  385 
  386     /* TODO verify the new zone first! */
  387 
  388     addzone = target->catzs->zmm->addzone;
  389     modzone = target->catzs->zmm->modzone;
  390     delzone = target->catzs->zmm->delzone;
  391 
  392     /* Copy zoneoptions from newzone into target. */
  393 
  394     dns_catz_options_free(&target->zoneoptions, target->catzs->mctx);
  395     dns_catz_options_copy(target->catzs->mctx, &newzone->zoneoptions,
  396                   &target->zoneoptions);
  397     dns_catz_options_setdefault(target->catzs->mctx, &target->defoptions,
  398                     &target->zoneoptions);
  399 
  400     dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE);
  401 
  402     result = isc_ht_init(&toadd, target->catzs->mctx, 16);
  403     if (result != ISC_R_SUCCESS) {
  404         goto cleanup;
  405     }
  406 
  407     result = isc_ht_init(&tomod, target->catzs->mctx, 16);
  408     if (result != ISC_R_SUCCESS) {
  409         goto cleanup;
  410     }
  411 
  412     result = isc_ht_iter_create(newzone->entries, &iter1);
  413     if (result != ISC_R_SUCCESS) {
  414         goto cleanup;
  415     }
  416 
  417     result = isc_ht_iter_create(target->entries, &iter2);
  418     if (result != ISC_R_SUCCESS) {
  419         goto cleanup;
  420     }
  421 
  422     /*
  423      * We can create those iterators now, even though toadd and tomod are
  424      * empty
  425      */
  426     result = isc_ht_iter_create(toadd, &iteradd);
  427     if (result != ISC_R_SUCCESS) {
  428         goto cleanup;
  429     }
  430 
  431     result = isc_ht_iter_create(tomod, &itermod);
  432     if (result != ISC_R_SUCCESS) {
  433         goto cleanup;
  434     }
  435 
  436     /*
  437      * First - walk the new zone and find all nodes that are not in the
  438      * old zone, or are in both zones and are modified.
  439      */
  440     for (result = isc_ht_iter_first(iter1); result == ISC_R_SUCCESS;
  441          result = delcur ? isc_ht_iter_delcurrent_next(iter1)
  442                  : isc_ht_iter_next(iter1))
  443     {
  444         dns_catz_entry_t *nentry = NULL;
  445         dns_catz_entry_t *oentry = NULL;
  446         unsigned char *key = NULL;
  447         size_t keysize;
  448         delcur = false;
  449 
  450         isc_ht_iter_current(iter1, (void **)&nentry);
  451         isc_ht_iter_currentkey(iter1, &key, &keysize);
  452 
  453         /*
  454          * Spurious record that came from suboption without main
  455          * record, removed.
  456          * xxxwpk: make it a separate verification phase?
  457          */
  458         if (dns_name_countlabels(&nentry->name) == 0) {
  459             dns_catz_entry_detach(newzone, &nentry);
  460             delcur = true;
  461             continue;
  462         }
  463 
  464         dns_name_format(&nentry->name, zname, DNS_NAME_FORMATSIZE);
  465 
  466         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
  467                   DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
  468                   "catz: iterating over '%s' from catalog '%s'",
  469                   zname, czname);
  470         dns_catz_options_setdefault(target->catzs->mctx,
  471                         &target->zoneoptions,
  472                         &nentry->opts);
  473 
  474         result = isc_ht_find(target->entries, key, (uint32_t)keysize,
  475                      (void **)&oentry);
  476         if (result != ISC_R_SUCCESS) {
  477             result = isc_ht_add(toadd, key, (uint32_t)keysize,
  478                         nentry);
  479             if (result != ISC_R_SUCCESS) {
  480                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
  481                           DNS_LOGMODULE_MASTER,
  482                           ISC_LOG_ERROR,
  483                           "catz: error adding zone '%s' "
  484                           "from catalog '%s' - %s",
  485                           zname, czname,
  486                           isc_result_totext(result));
  487             }
  488             continue;
  489         }
  490 
  491         if (dns_catz_entry_cmp(oentry, nentry) != true) {
  492             result = isc_ht_add(tomod, key, (uint32_t)keysize,
  493                         nentry);
  494             if (result != ISC_R_SUCCESS) {
  495                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
  496                           DNS_LOGMODULE_MASTER,
  497                           ISC_LOG_ERROR,
  498                           "catz: error modifying zone '%s' "
  499                           "from catalog '%s' - %s",
  500                           zname, czname,
  501                           isc_result_totext(result));
  502             }
  503         }
  504         dns_catz_entry_detach(target, &oentry);
  505         result = isc_ht_delete(target->entries, key, (uint32_t)keysize);
  506         RUNTIME_CHECK(result == ISC_R_SUCCESS);
  507     }
  508     RUNTIME_CHECK(result == ISC_R_NOMORE);
  509     isc_ht_iter_destroy(&iter1);
  510 
  511     /*
  512      * Then - walk the old zone; only deleted entries should remain.
  513      */
  514     for (result = isc_ht_iter_first(iter2); result == ISC_R_SUCCESS;
  515          result = isc_ht_iter_delcurrent_next(iter2))
  516     {
  517         dns_catz_entry_t *entry = NULL;
  518         isc_ht_iter_current(iter2, (void **)&entry);
  519 
  520         dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
  521         result = delzone(entry, target, target->catzs->view,
  522                  target->catzs->taskmgr,
  523                  target->catzs->zmm->udata);
  524         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
  525                   DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
  526                   "catz: deleting zone '%s' from catalog '%s' - %s",
  527                   zname, czname, isc_result_totext(result));
  528         dns_catz_entry_detach(target, &entry);
  529     }
  530     RUNTIME_CHECK(result == ISC_R_NOMORE);
  531     isc_ht_iter_destroy(&iter2);
  532     /* At this moment target->entries has to be be empty. */
  533     INSIST(isc_ht_count(target->entries) == 0);
  534     isc_ht_destroy(&target->entries);
  535 
  536     for (result = isc_ht_iter_first(iteradd); result == ISC_R_SUCCESS;
  537          result = isc_ht_iter_delcurrent_next(iteradd))
  538     {
  539         dns_catz_entry_t *entry = NULL;
  540         isc_ht_iter_current(iteradd, (void **)&entry);
  541 
  542         dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
  543         result = addzone(entry, target, target->catzs->view,
  544                  target->catzs->taskmgr,
  545                  target->catzs->zmm->udata);
  546         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
  547                   DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
  548                   "catz: adding zone '%s' from catalog "
  549                   "'%s' - %s",
  550                   zname, czname, isc_result_totext(result));
  551     }
  552 
  553     for (result = isc_ht_iter_first(itermod); result == ISC_R_SUCCESS;
  554          result = isc_ht_iter_delcurrent_next(itermod))
  555     {
  556         dns_catz_entry_t *entry = NULL;
  557         isc_ht_iter_current(itermod, (void **)&entry);
  558 
  559         dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
  560         result = modzone(entry, target, target->catzs->view,
  561                  target->catzs->taskmgr,
  562                  target->catzs->zmm->udata);
  563         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
  564                   DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
  565                   "catz: modifying zone '%s' from catalog "
  566                   "'%s' - %s",
  567                   zname, czname, isc_result_totext(result));
  568     }
  569 
  570     target->entries = newzone->entries;
  571     newzone->entries = NULL;
  572 
  573     result = ISC_R_SUCCESS;
  574 
  575 cleanup:
  576     if (iter1 != NULL) {
  577         isc_ht_iter_destroy(&iter1);
  578     }
  579     if (iter2 != NULL) {
  580         isc_ht_iter_destroy(&iter2);
  581     }
  582     if (iteradd != NULL) {
  583         isc_ht_iter_destroy(&iteradd);
  584     }
  585     if (itermod != NULL) {
  586         isc_ht_iter_destroy(&itermod);
  587     }
  588     if (toadd != NULL) {
  589         isc_ht_destroy(&toadd);
  590     }
  591     if (tomod != NULL) {
  592         isc_ht_destroy(&tomod);
  593     }
  594     return (result);
  595 }
  596 
  597 isc_result_t
  598 dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
  599            isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
  600            isc_timermgr_t *timermgr) {
  601     dns_catz_zones_t *new_zones;
  602     isc_result_t result;
  603 
  604     REQUIRE(catzsp != NULL && *catzsp == NULL);
  605     REQUIRE(zmm != NULL);
  606 
  607     new_zones = isc_mem_get(mctx, sizeof(*new_zones));
  608     memset(new_zones, 0, sizeof(*new_zones));
  609 
  610     isc_mutex_init(&new_zones->lock);
  611 
  612     isc_refcount_init(&new_zones->refs, 1);
  613 
  614     result = isc_ht_init(&new_zones->zones, mctx, 4);
  615     if (result != ISC_R_SUCCESS) {
  616         goto cleanup_refcount;
  617     }
  618 
  619     isc_mem_attach(mctx, &new_zones->mctx);
  620     new_zones->zmm = zmm;
  621     new_zones->timermgr = timermgr;
  622     new_zones->taskmgr = taskmgr;
  623 
  624     result = isc_task_create(taskmgr, 0, &new_zones->updater);
  625     if (result != ISC_R_SUCCESS) {
  626         goto cleanup_ht;
  627     }
  628     new_zones->magic = DNS_CATZ_ZONES_MAGIC;
  629 
  630     *catzsp = new_zones;
  631     return (ISC_R_SUCCESS);
  632 
  633 cleanup_ht:
  634     isc_ht_destroy(&new_zones->zones);
  635 cleanup_refcount:
  636     isc_refcount_destroy(&new_zones->refs);
  637     isc_mutex_destroy(&new_zones->lock);
  638     isc_mem_put(mctx, new_zones, sizeof(*new_zones));
  639 
  640     return (result);
  641 }
  642 
  643 void
  644 dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) {
  645     REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
  646     REQUIRE(view != NULL);
  647     /* Either it's a new one or it's being reconfigured. */
  648     REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name));
  649 
  650     catzs->view = view;
  651 }
  652 
  653 isc_result_t
  654 dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
  655           const dns_name_t *name) {
  656     isc_result_t result;
  657     dns_catz_zone_t *new_zone;
  658 
  659     REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
  660     REQUIRE(zonep != NULL && *zonep == NULL);
  661     REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
  662 
  663     new_zone = isc_mem_get(catzs->mctx, sizeof(*new_zone));
  664 
  665     memset(new_zone, 0, sizeof(*new_zone));
  666 
  667     dns_name_init(&new_zone->name, NULL);
  668     dns_name_dup(name, catzs->mctx, &new_zone->name);
  669 
  670     result = isc_ht_init(&new_zone->entries, catzs->mctx, 4);
  671     if (result != ISC_R_SUCCESS) {
  672         goto cleanup_name;
  673     }
  674 
  675     new_zone->updatetimer = NULL;
  676     result = isc_timer_create(catzs->timermgr, isc_timertype_inactive, NULL,
  677                   NULL, catzs->updater,
  678                   dns_catz_update_taskaction, new_zone,
  679                   &new_zone->updatetimer);
  680     if (result != ISC_R_SUCCESS) {
  681         goto cleanup_ht;
  682     }
  683 
  684     isc_time_settoepoch(&new_zone->lastupdated);
  685     new_zone->updatepending = false;
  686     new_zone->db = NULL;
  687     new_zone->dbversion = NULL;
  688     new_zone->catzs = catzs;
  689     dns_catz_options_init(&new_zone->defoptions);
  690     dns_catz_options_init(&new_zone->zoneoptions);
  691     new_zone->active = true;
  692     new_zone->db_registered = false;
  693     new_zone->version = (uint32_t)(-1);
  694     isc_refcount_init(&new_zone->refs, 1);
  695     new_zone->magic = DNS_CATZ_ZONE_MAGIC;
  696 
  697     *zonep = new_zone;
  698 
  699     return (ISC_R_SUCCESS);
  700 
  701 cleanup_ht:
  702     isc_ht_destroy(&new_zone->entries);
  703 cleanup_name:
  704     dns_name_free(&new_zone->name, catzs->mctx);
  705     isc_mem_put(catzs->mctx, new_zone, sizeof(*new_zone));
  706 
  707     return (result);
  708 }
  709 
  710 isc_result_t
  711 dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name,
  712           dns_catz_zone_t **zonep) {
  713     dns_catz_zone_t *new_zone = NULL;
  714     isc_result_t result, tresult;
  715     char zname[DNS_NAME_FORMATSIZE];
  716 
  717     REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
  718     REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
  719     REQUIRE(zonep != NULL && *zonep == NULL);
  720 
  721     dns_name_format(name, zname, DNS_NAME_FORMATSIZE);
  722     isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
  723               ISC_LOG_DEBUG(3), "catz: dns_catz_add_zone %s", zname);
  724 
  725     LOCK(&catzs->lock);
  726 
  727     result = dns_catz_new_zone(catzs, &new_zone, name);
  728     if (result != ISC_R_SUCCESS) {
  729         goto cleanup;
  730     }
  731 
  732     result = isc_ht_add(catzs->zones, new_zone->name.ndata,
  733                 new_zone->name.length, new_zone);
  734     if (result != ISC_R_SUCCESS) {
  735         dns_catz_zone_detach(&new_zone);
  736         if (result != ISC_R_EXISTS) {
  737             goto cleanup;
  738         }
  739     }
  740 
  741     if (result == ISC_R_EXISTS) {
  742         tresult = isc_ht_find(catzs->zones, name->ndata, name->length,
  743                       (void **)&new_zone);
  744         INSIST(tresult == ISC_R_SUCCESS && !new_zone->active);
  745         new_zone->active = true;
  746     }
  747 
  748     *zonep = new_zone;
  749 
  750 cleanup:
  751     UNLOCK(&catzs->lock);
  752 
  753     return (result);
  754 }
  755 
  756 dns_catz_zone_t *
  757 dns_catz_get_zone(dns_catz_zones_t *catzs, const dns_name_t *name) {
  758     isc_result_t result;
  759     dns_catz_zone_t *found = NULL;
  760 
  761     REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
  762     REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
  763 
  764     result = isc_ht_find(catzs->zones, name->ndata, name->length,
  765                  (void **)&found);
  766     if (result != ISC_R_SUCCESS) {
  767         return (NULL);
  768     }
  769 
  770     return (found);
  771 }
  772 
  773 void
  774 dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp) {
  775     REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
  776     REQUIRE(catzsp != NULL && *catzsp == NULL);
  777 
  778     isc_refcount_increment(&catzs->refs);
  779     *catzsp = catzs;
  780 }
  781 
  782 void
  783 dns_catz_zone_attach(dns_catz_zone_t *zone, dns_catz_zone_t **zonep) {
  784     REQUIRE(zonep != NULL && *zonep == NULL);
  785 
  786     isc_refcount_increment(&zone->refs);
  787     *zonep = zone;
  788 }
  789 
  790 void
  791 dns_catz_zone_detach(dns_catz_zone_t **zonep) {
  792     REQUIRE(zonep != NULL && *zonep != NULL);
  793     dns_catz_zone_t *zone = *zonep;
  794     *zonep = NULL;
  795 
  796     if (isc_refcount_decrement(&zone->refs) == 1) {
  797         isc_mem_t *mctx = zone->catzs->mctx;
  798         isc_refcount_destroy(&zone->refs);
  799         if (zone->entries != NULL) {
  800             isc_ht_iter_t *iter = NULL;
  801             isc_result_t result;
  802             result = isc_ht_iter_create(zone->entries, &iter);
  803             INSIST(result == ISC_R_SUCCESS);
  804             for (result = isc_ht_iter_first(iter);
  805                  result == ISC_R_SUCCESS;
  806                  result = isc_ht_iter_delcurrent_next(iter))
  807             {
  808                 dns_catz_entry_t *entry = NULL;
  809 
  810                 isc_ht_iter_current(iter, (void **)&entry);
  811                 dns_catz_entry_detach(zone, &entry);
  812             }
  813             INSIST(result == ISC_R_NOMORE);
  814             isc_ht_iter_destroy(&iter);
  815 
  816             /* The hashtable has to be empty now. */
  817             INSIST(isc_ht_count(zone->entries) == 0);
  818             isc_ht_destroy(&zone->entries);
  819         }
  820         zone->magic = 0;
  821         isc_timer_detach(&zone->updatetimer);
  822         if (zone->db_registered) {
  823             INSIST(dns_db_updatenotify_unregister(
  824                        zone->db, dns_catz_dbupdate_callback,
  825                        zone->catzs) == ISC_R_SUCCESS);
  826         }
  827         if (zone->dbversion) {
  828             dns_db_closeversion(zone->db, &zone->dbversion, false);
  829         }
  830         if (zone->db != NULL) {
  831             dns_db_detach(&zone->db);
  832         }
  833 
  834         dns_name_free(&zone->name, mctx);
  835         dns_catz_options_free(&zone->defoptions, mctx);
  836         dns_catz_options_free(&zone->zoneoptions, mctx);
  837 
  838         zone->catzs = NULL;
  839         isc_mem_put(mctx, zone, sizeof(dns_catz_zone_t));
  840     }
  841 }
  842 
  843 void
  844 dns_catz_catzs_detach(dns_catz_zones_t **catzsp) {
  845     dns_catz_zones_t *catzs;
  846 
  847     REQUIRE(catzsp != NULL && *catzsp != NULL);
  848 
  849     catzs = *catzsp;
  850     *catzsp = NULL;
  851 
  852     if (isc_refcount_decrement(&catzs->refs) == 1) {
  853         catzs->magic = 0;
  854         isc_task_destroy(&catzs->updater);
  855         isc_mutex_destroy(&catzs->lock);
  856         if (catzs->zones != NULL) {
  857             isc_ht_iter_t *iter = NULL;
  858             isc_result_t result;
  859             result = isc_ht_iter_create(catzs->zones, &iter);
  860             INSIST(result == ISC_R_SUCCESS);
  861             for (result = isc_ht_iter_first(iter);
  862                  result == ISC_R_SUCCESS;) {
  863                 dns_catz_zone_t *zone = NULL;
  864                 isc_ht_iter_current(iter, (void **)&zone);
  865                 result = isc_ht_iter_delcurrent_next(iter);
  866                 dns_catz_zone_detach(&zone);
  867             }
  868             INSIST(result == ISC_R_NOMORE);
  869             isc_ht_iter_destroy(&iter);
  870             INSIST(isc_ht_count(catzs->zones) == 0);
  871             isc_ht_destroy(&catzs->zones);
  872         }
  873         isc_refcount_destroy(&catzs->refs);
  874         isc_mem_putanddetach(&catzs->mctx, catzs, sizeof(*catzs));
  875     }
  876 }
  877 
  878 typedef enum {
  879     CATZ_OPT_NONE,
  880     CATZ_OPT_ZONES,
  881     CATZ_OPT_MASTERS,
  882     CATZ_OPT_ALLOW_QUERY,
  883     CATZ_OPT_ALLOW_TRANSFER,
  884     CATZ_OPT_VERSION,
  885 } catz_opt_t;
  886 
  887 static bool
  888 catz_opt_cmp(const dns_label_t *option, const char *opt) {
  889     unsigned int l = strlen(opt);
  890     if (option->length - 1 == l &&
  891         memcmp(opt, option->base + 1, l - 1) == 0) {
  892         return (true);
  893     } else {
  894         return (false);
  895     }
  896 }
  897 
  898 static catz_opt_t
  899 catz_get_option(const dns_label_t *option) {
  900     if (catz_opt_cmp(option, "zones")) {
  901         return (CATZ_OPT_ZONES);
  902     } else if (catz_opt_cmp(option, "masters")) {
  903         return (CATZ_OPT_MASTERS);
  904     } else if (catz_opt_cmp(option, "allow-query")) {
  905         return (CATZ_OPT_ALLOW_QUERY);
  906     } else if (catz_opt_cmp(option, "allow-transfer")) {
  907         return (CATZ_OPT_ALLOW_TRANSFER);
  908     } else if (catz_opt_cmp(option, "version")) {
  909         return (CATZ_OPT_VERSION);
  910     } else {
  911         return (CATZ_OPT_NONE);
  912     }
  913 }
  914 
  915 static isc_result_t
  916 catz_process_zones(dns_catz_zone_t *zone, dns_rdataset_t *value,
  917            dns_name_t *name) {
  918     dns_label_t mhash;
  919     dns_name_t opt;
  920 
  921     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
  922     REQUIRE(DNS_RDATASET_VALID(value));
  923     REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
  924 
  925     if (value->rdclass != dns_rdataclass_in) {
  926         return (ISC_R_FAILURE);
  927     }
  928 
  929     if (name->labels == 0) {
  930         return (ISC_R_FAILURE);
  931     }
  932 
  933     dns_name_getlabel(name, name->labels - 1, &mhash);
  934 
  935     if (name->labels == 1) {
  936         return (catz_process_zones_entry(zone, value, &mhash));
  937     } else {
  938         dns_name_init(&opt, NULL);
  939         dns_name_split(name, 1, &opt, NULL);
  940         return (catz_process_zones_suboption(zone, value, &mhash,
  941                              &opt));
  942     }
  943 }
  944 
  945 static isc_result_t
  946 catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
  947              dns_label_t *mhash) {
  948     isc_result_t result;
  949     dns_rdata_t rdata;
  950     dns_rdata_ptr_t ptr;
  951     dns_catz_entry_t *entry = NULL;
  952 
  953     /*
  954      * We only take -first- value, as mhash must be
  955      * different.
  956      */
  957     if (value->type != dns_rdatatype_ptr) {
  958         return (ISC_R_FAILURE);
  959     }
  960 
  961     result = dns_rdataset_first(value);
  962     if (result != ISC_R_SUCCESS) {
  963         return (ISC_R_FAILURE);
  964     }
  965 
  966     dns_rdata_init(&rdata);
  967     dns_rdataset_current(value, &rdata);
  968 
  969     result = dns_rdata_tostruct(&rdata, &ptr, NULL);
  970     RUNTIME_CHECK(result == ISC_R_SUCCESS);
  971 
  972     result = isc_ht_find(zone->entries, mhash->base, mhash->length,
  973                  (void **)&entry);
  974     if (result == ISC_R_SUCCESS) {
  975         if (dns_name_countlabels(&entry->name) != 0) {
  976             /* We have a duplicate. */
  977             dns_rdata_freestruct(&ptr);
  978             return (ISC_R_FAILURE);
  979         } else {
  980             dns_name_dup(&ptr.ptr, zone->catzs->mctx, &entry->name);
  981         }
  982     } else {
  983         result = dns_catz_entry_new(zone->catzs->mctx, &ptr.ptr,
  984                         &entry);
  985         if (result != ISC_R_SUCCESS) {
  986             dns_rdata_freestruct(&ptr);
  987             return (result);
  988         }
  989 
  990         result = isc_ht_add(zone->entries, mhash->base, mhash->length,
  991                     entry);
  992         if (result != ISC_R_SUCCESS) {
  993             dns_rdata_freestruct(&ptr);
  994             dns_catz_entry_detach(zone, &entry);
  995             return (result);
  996         }
  997     }
  998 
  999     dns_rdata_freestruct(&ptr);
 1000 
 1001     return (ISC_R_SUCCESS);
 1002 }
 1003 
 1004 static isc_result_t
 1005 catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) {
 1006     isc_result_t result;
 1007     dns_rdata_t rdata;
 1008     dns_rdata_txt_t rdatatxt;
 1009     dns_rdata_txt_string_t rdatastr;
 1010     uint32_t tversion;
 1011     char t[16];
 1012 
 1013     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
 1014     REQUIRE(DNS_RDATASET_VALID(value));
 1015 
 1016     if (value->rdclass != dns_rdataclass_in ||
 1017         value->type != dns_rdatatype_txt) {
 1018         return (ISC_R_FAILURE);
 1019     }
 1020 
 1021     result = dns_rdataset_first(value);
 1022     if (result != ISC_R_SUCCESS) {
 1023         return (result);
 1024     }
 1025 
 1026     dns_rdata_init(&rdata);
 1027     dns_rdataset_current(value, &rdata);
 1028 
 1029     result = dns_rdata_tostruct(&rdata, &rdatatxt, NULL);
 1030     RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1031 
 1032     result = dns_rdata_txt_first(&rdatatxt);
 1033     if (result != ISC_R_SUCCESS) {
 1034         goto cleanup;
 1035     }
 1036 
 1037     result = dns_rdata_txt_current(&rdatatxt, &rdatastr);
 1038     if (result != ISC_R_SUCCESS) {
 1039         goto cleanup;
 1040     }
 1041 
 1042     result = dns_rdata_txt_next(&rdatatxt);
 1043     if (result != ISC_R_NOMORE) {
 1044         result = ISC_R_FAILURE;
 1045         goto cleanup;
 1046     }
 1047     if (rdatastr.length > 15) {
 1048         result = ISC_R_BADNUMBER;
 1049         goto cleanup;
 1050     }
 1051     memmove(t, rdatastr.data, rdatastr.length);
 1052     t[rdatastr.length] = 0;
 1053     result = isc_parse_uint32(&tversion, t, 10);
 1054     if (result != ISC_R_SUCCESS) {
 1055         goto cleanup;
 1056     }
 1057     zone->version = tversion;
 1058     result = ISC_R_SUCCESS;
 1059 
 1060 cleanup:
 1061     dns_rdata_freestruct(&rdatatxt);
 1062     return (result);
 1063 }
 1064 
 1065 static isc_result_t
 1066 catz_process_masters(dns_catz_zone_t *zone, dns_ipkeylist_t *ipkl,
 1067              dns_rdataset_t *value, dns_name_t *name) {
 1068     isc_result_t result;
 1069     dns_rdata_t rdata;
 1070     dns_rdata_in_a_t rdata_a;
 1071     dns_rdata_in_aaaa_t rdata_aaaa;
 1072     dns_rdata_txt_t rdata_txt;
 1073     dns_rdata_txt_string_t rdatastr;
 1074     dns_name_t *keyname = NULL;
 1075     isc_mem_t *mctx;
 1076     char keycbuf[DNS_NAME_FORMATSIZE];
 1077     isc_buffer_t keybuf;
 1078     unsigned int rcount;
 1079     unsigned int i;
 1080 
 1081     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
 1082     REQUIRE(ipkl != NULL);
 1083     REQUIRE(DNS_RDATASET_VALID(value));
 1084     REQUIRE(dns_rdataset_isassociated(value));
 1085     REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
 1086 
 1087     mctx = zone->catzs->mctx;
 1088     memset(&rdata_a, 0, sizeof(rdata_a));
 1089     memset(&rdata_aaaa, 0, sizeof(rdata_aaaa));
 1090     memset(&rdata_txt, 0, sizeof(rdata_txt));
 1091     isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf));
 1092 
 1093     /*
 1094      * We have three possibilities here:
 1095      * - either empty name and IN A/IN AAAA record
 1096      * - label and IN A/IN AAAA
 1097      * - label and IN TXT - TSIG key name
 1098      */
 1099     if (value->rdclass != dns_rdataclass_in) {
 1100         return (ISC_R_FAILURE);
 1101     }
 1102 
 1103     if (name->labels > 0) {
 1104         isc_sockaddr_t sockaddr;
 1105 
 1106         /*
 1107          * We're pre-preparing the data once, we'll put it into
 1108          * the right spot in the masters array once we find it.
 1109          */
 1110         result = dns_rdataset_first(value);
 1111         RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1112         dns_rdata_init(&rdata);
 1113         dns_rdataset_current(value, &rdata);
 1114         switch (value->type) {
 1115         case dns_rdatatype_a:
 1116             result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
 1117             RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1118             isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0);
 1119             break;
 1120         case dns_rdatatype_aaaa:
 1121             result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
 1122             RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1123             isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr,
 1124                          0);
 1125             break;
 1126         case dns_rdatatype_txt:
 1127             result = dns_rdata_tostruct(&rdata, &rdata_txt, NULL);
 1128             RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1129 
 1130             result = dns_rdata_txt_first(&rdata_txt);
 1131             if (result != ISC_R_SUCCESS) {
 1132                 return (result);
 1133             }
 1134 
 1135             result = dns_rdata_txt_current(&rdata_txt, &rdatastr);
 1136             if (result != ISC_R_SUCCESS) {
 1137                 return (result);
 1138             }
 1139 
 1140             result = dns_rdata_txt_next(&rdata_txt);
 1141             if (result != ISC_R_NOMORE) {
 1142                 return (ISC_R_FAILURE);
 1143             }
 1144 
 1145             /* rdatastr.length < DNS_NAME_MAXTEXT */
 1146             keyname = isc_mem_get(mctx, sizeof(dns_name_t));
 1147             dns_name_init(keyname, 0);
 1148             memmove(keycbuf, rdatastr.data, rdatastr.length);
 1149             keycbuf[rdatastr.length] = 0;
 1150             result = dns_name_fromstring(keyname, keycbuf, 0, mctx);
 1151             if (result != ISC_R_SUCCESS) {
 1152                 dns_name_free(keyname, mctx);
 1153                 isc_mem_put(mctx, keyname, sizeof(dns_name_t));
 1154                 return (result);
 1155             }
 1156             break;
 1157         default:
 1158             return (ISC_R_FAILURE);
 1159         }
 1160 
 1161         /*
 1162          * We have to find the appropriate labeled record in masters
 1163          * if it exists.
 1164          * In common case we'll have no more than 3-4 records here so
 1165          * no optimization.
 1166          */
 1167         for (i = 0; i < ipkl->count; i++) {
 1168             if (ipkl->labels[i] != NULL &&
 1169                 !dns_name_compare(name, ipkl->labels[i])) {
 1170                 break;
 1171             }
 1172         }
 1173 
 1174         if (i < ipkl->count) { /* we have this record already */
 1175             if (value->type == dns_rdatatype_txt) {
 1176                 ipkl->keys[i] = keyname;
 1177             } else { /* A/AAAA */
 1178                 memmove(&ipkl->addrs[i], &sockaddr,
 1179                     sizeof(isc_sockaddr_t));
 1180             }
 1181         } else {
 1182             result = dns_ipkeylist_resize(mctx, ipkl, i + 1);
 1183             if (result != ISC_R_SUCCESS) {
 1184                 return (result);
 1185             }
 1186 
 1187             ipkl->labels[i] = isc_mem_get(mctx, sizeof(dns_name_t));
 1188             dns_name_init(ipkl->labels[i], NULL);
 1189             dns_name_dup(name, mctx, ipkl->labels[i]);
 1190 
 1191             if (value->type == dns_rdatatype_txt) {
 1192                 ipkl->keys[i] = keyname;
 1193             } else { /* A/AAAA */
 1194                 memmove(&ipkl->addrs[i], &sockaddr,
 1195                     sizeof(isc_sockaddr_t));
 1196             }
 1197             ipkl->count++;
 1198         }
 1199         return (ISC_R_SUCCESS);
 1200     }
 1201     /* else - 'simple' case - without labels */
 1202 
 1203     if (value->type != dns_rdatatype_a && value->type != dns_rdatatype_aaaa)
 1204     {
 1205         return (ISC_R_FAILURE);
 1206     }
 1207 
 1208     rcount = dns_rdataset_count(value) + ipkl->count;
 1209 
 1210     result = dns_ipkeylist_resize(mctx, ipkl, rcount);
 1211     if (result != ISC_R_SUCCESS) {
 1212         return (result);
 1213     }
 1214 
 1215     for (result = dns_rdataset_first(value); result == ISC_R_SUCCESS;
 1216          result = dns_rdataset_next(value))
 1217     {
 1218         dns_rdata_init(&rdata);
 1219         dns_rdataset_current(value, &rdata);
 1220         /*
 1221          * port 0 == take the default
 1222          */
 1223         if (value->type == dns_rdatatype_a) {
 1224             result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
 1225             RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1226             isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
 1227                         &rdata_a.in_addr, 0);
 1228         } else {
 1229             result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
 1230             RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1231             isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count],
 1232                          &rdata_aaaa.in6_addr, 0);
 1233         }
 1234         ipkl->keys[ipkl->count] = NULL;
 1235         ipkl->labels[ipkl->count] = NULL;
 1236         ipkl->count++;
 1237         dns_rdata_freestruct(&rdata_a);
 1238     }
 1239     return (ISC_R_SUCCESS);
 1240 }
 1241 
 1242 static isc_result_t
 1243 catz_process_apl(dns_catz_zone_t *zone, isc_buffer_t **aclbp,
 1244          dns_rdataset_t *value) {
 1245     isc_result_t result = ISC_R_SUCCESS;
 1246     dns_rdata_t rdata;
 1247     dns_rdata_in_apl_t rdata_apl;
 1248     dns_rdata_apl_ent_t apl_ent;
 1249     isc_netaddr_t addr;
 1250     isc_buffer_t *aclb = NULL;
 1251     unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */
 1252 
 1253     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
 1254     REQUIRE(aclbp != NULL);
 1255     REQUIRE(*aclbp == NULL);
 1256     REQUIRE(DNS_RDATASET_VALID(value));
 1257     REQUIRE(dns_rdataset_isassociated(value));
 1258 
 1259     if (value->rdclass != dns_rdataclass_in ||
 1260         value->type != dns_rdatatype_apl) {
 1261         return (ISC_R_FAILURE);
 1262     }
 1263 
 1264     if (dns_rdataset_count(value) > 1) {
 1265         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1266                   DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
 1267                   "catz: more than one APL entry for member zone, "
 1268                   "result is undefined");
 1269     }
 1270     result = dns_rdataset_first(value);
 1271     RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1272     dns_rdata_init(&rdata);
 1273     dns_rdataset_current(value, &rdata);
 1274     result = dns_rdata_tostruct(&rdata, &rdata_apl, zone->catzs->mctx);
 1275     if (result != ISC_R_SUCCESS) {
 1276         return (result);
 1277     }
 1278     isc_buffer_allocate(zone->catzs->mctx, &aclb, 16);
 1279     isc_buffer_setautorealloc(aclb, true);
 1280     for (result = dns_rdata_apl_first(&rdata_apl); result == ISC_R_SUCCESS;
 1281          result = dns_rdata_apl_next(&rdata_apl))
 1282     {
 1283         result = dns_rdata_apl_current(&rdata_apl, &apl_ent);
 1284         RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1285         memset(buf, 0, sizeof(buf));
 1286         if (apl_ent.data != NULL && apl_ent.length > 0) {
 1287             memmove(buf, apl_ent.data, apl_ent.length);
 1288         }
 1289         if (apl_ent.family == 1) {
 1290             isc_netaddr_fromin(&addr, (struct in_addr *)buf);
 1291         } else if (apl_ent.family == 2) {
 1292             isc_netaddr_fromin6(&addr, (struct in6_addr *)buf);
 1293         } else {
 1294             continue; /* xxxwpk log it or simply ignore? */
 1295         }
 1296         if (apl_ent.negative) {
 1297             isc_buffer_putuint8(aclb, '!');
 1298         }
 1299         isc_buffer_reserve(&aclb, INET6_ADDRSTRLEN);
 1300         result = isc_netaddr_totext(&addr, aclb);
 1301         RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1302         if ((apl_ent.family == 1 && apl_ent.prefix < 32) ||
 1303             (apl_ent.family == 2 && apl_ent.prefix < 128))
 1304         {
 1305             isc_buffer_putuint8(aclb, '/');
 1306             isc_buffer_putdecint(aclb, apl_ent.prefix);
 1307         }
 1308         isc_buffer_putstr(aclb, "; ");
 1309     }
 1310     if (result == ISC_R_NOMORE) {
 1311         result = ISC_R_SUCCESS;
 1312     } else {
 1313         goto cleanup;
 1314     }
 1315     *aclbp = aclb;
 1316     aclb = NULL;
 1317 cleanup:
 1318     if (aclb != NULL) {
 1319         isc_buffer_free(&aclb);
 1320     }
 1321     dns_rdata_freestruct(&rdata_apl);
 1322     return (result);
 1323 }
 1324 
 1325 static isc_result_t
 1326 catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value,
 1327                  dns_label_t *mhash, dns_name_t *name) {
 1328     isc_result_t result;
 1329     dns_catz_entry_t *entry = NULL;
 1330     dns_label_t option;
 1331     dns_name_t prefix;
 1332     catz_opt_t opt;
 1333 
 1334     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
 1335     REQUIRE(mhash != NULL);
 1336     REQUIRE(DNS_RDATASET_VALID(value));
 1337     REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
 1338 
 1339     if (name->labels == 0) {
 1340         return (ISC_R_FAILURE);
 1341     }
 1342     dns_name_getlabel(name, name->labels - 1, &option);
 1343     opt = catz_get_option(&option);
 1344 
 1345     /*
 1346      * We're adding this entry now, in case the option is invalid we'll get
 1347      * rid of it in verification phase.
 1348      */
 1349     result = isc_ht_find(zone->entries, mhash->base, mhash->length,
 1350                  (void **)&entry);
 1351     if (result != ISC_R_SUCCESS) {
 1352         result = dns_catz_entry_new(zone->catzs->mctx, NULL, &entry);
 1353         if (result != ISC_R_SUCCESS) {
 1354             return (result);
 1355         }
 1356         result = isc_ht_add(zone->entries, mhash->base, mhash->length,
 1357                     entry);
 1358         if (result != ISC_R_SUCCESS) {
 1359             dns_catz_entry_detach(zone, &entry);
 1360             return (result);
 1361         }
 1362     }
 1363 
 1364     dns_name_init(&prefix, NULL);
 1365     dns_name_split(name, 1, &prefix, NULL);
 1366     switch (opt) {
 1367     case CATZ_OPT_MASTERS:
 1368         return (catz_process_masters(zone, &entry->opts.masters, value,
 1369                          &prefix));
 1370     case CATZ_OPT_ALLOW_QUERY:
 1371         if (prefix.labels != 0) {
 1372             return (ISC_R_FAILURE);
 1373         }
 1374         return (catz_process_apl(zone, &entry->opts.allow_query,
 1375                      value));
 1376     case CATZ_OPT_ALLOW_TRANSFER:
 1377         if (prefix.labels != 0) {
 1378             return (ISC_R_FAILURE);
 1379         }
 1380         return (catz_process_apl(zone, &entry->opts.allow_transfer,
 1381                      value));
 1382     default:
 1383         return (ISC_R_FAILURE);
 1384     }
 1385 
 1386     return (ISC_R_FAILURE);
 1387 }
 1388 
 1389 static isc_result_t
 1390 catz_process_value(dns_catz_zone_t *zone, dns_name_t *name,
 1391            dns_rdataset_t *rdataset) {
 1392     dns_label_t option;
 1393     dns_name_t prefix;
 1394     catz_opt_t opt;
 1395 
 1396     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
 1397     REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
 1398     REQUIRE(DNS_RDATASET_VALID(rdataset));
 1399 
 1400     dns_name_getlabel(name, name->labels - 1, &option);
 1401     opt = catz_get_option(&option);
 1402     dns_name_init(&prefix, NULL);
 1403     dns_name_split(name, 1, &prefix, NULL);
 1404     switch (opt) {
 1405     case CATZ_OPT_ZONES:
 1406         return (catz_process_zones(zone, rdataset, &prefix));
 1407     case CATZ_OPT_MASTERS:
 1408         return (catz_process_masters(zone, &zone->zoneoptions.masters,
 1409                          rdataset, &prefix));
 1410     case CATZ_OPT_ALLOW_QUERY:
 1411         if (prefix.labels != 0) {
 1412             return (ISC_R_FAILURE);
 1413         }
 1414         return (catz_process_apl(zone, &zone->zoneoptions.allow_query,
 1415                      rdataset));
 1416     case CATZ_OPT_ALLOW_TRANSFER:
 1417         if (prefix.labels != 0) {
 1418             return (ISC_R_FAILURE);
 1419         }
 1420         return (catz_process_apl(
 1421             zone, &zone->zoneoptions.allow_transfer, rdataset));
 1422     case CATZ_OPT_VERSION:
 1423         if (prefix.labels != 0) {
 1424             return (ISC_R_FAILURE);
 1425         }
 1426         return (catz_process_version(zone, rdataset));
 1427     default:
 1428         return (ISC_R_FAILURE);
 1429     }
 1430 }
 1431 
 1432 isc_result_t
 1433 dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone,
 1434             const dns_name_t *src_name, dns_rdataset_t *rdataset) {
 1435     isc_result_t result;
 1436     int order;
 1437     unsigned int nlabels;
 1438     dns_namereln_t nrres;
 1439     dns_rdata_t rdata = DNS_RDATA_INIT;
 1440     dns_rdata_soa_t soa;
 1441     dns_name_t prefix;
 1442 
 1443     REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
 1444     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
 1445     REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC));
 1446 
 1447     nrres = dns_name_fullcompare(src_name, &zone->name, &order, &nlabels);
 1448     if (nrres == dns_namereln_equal) {
 1449         if (rdataset->type == dns_rdatatype_soa) {
 1450             result = dns_rdataset_first(rdataset);
 1451             if (result != ISC_R_SUCCESS) {
 1452                 return (result);
 1453             }
 1454 
 1455             dns_rdataset_current(rdataset, &rdata);
 1456             result = dns_rdata_tostruct(&rdata, &soa, NULL);
 1457             RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1458 
 1459             /*
 1460              * xxxwpk TODO do we want to save something from SOA?
 1461              */
 1462             return (result);
 1463         } else if (rdataset->type == dns_rdatatype_ns) {
 1464             return (ISC_R_SUCCESS);
 1465         } else {
 1466             return (ISC_R_UNEXPECTED);
 1467         }
 1468     } else if (nrres != dns_namereln_subdomain) {
 1469         return (ISC_R_UNEXPECTED);
 1470     }
 1471 
 1472     dns_name_init(&prefix, NULL);
 1473     dns_name_split(src_name, zone->name.labels, &prefix, NULL);
 1474     result = catz_process_value(zone, &prefix, rdataset);
 1475 
 1476     return (result);
 1477 }
 1478 
 1479 static isc_result_t
 1480 digest2hex(unsigned char *digest, unsigned int digestlen, char *hash,
 1481        size_t hashlen) {
 1482     unsigned int i;
 1483     int ret;
 1484     for (i = 0; i < digestlen; i++) {
 1485         size_t left = hashlen - i * 2;
 1486         ret = snprintf(hash + i * 2, left, "%02x", digest[i]);
 1487         if (ret < 0 || (size_t)ret >= left) {
 1488             return (ISC_R_NOSPACE);
 1489         }
 1490     }
 1491     return (ISC_R_SUCCESS);
 1492 }
 1493 
 1494 isc_result_t
 1495 dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
 1496                  isc_buffer_t **buffer) {
 1497     isc_buffer_t *tbuf = NULL;
 1498     isc_region_t r;
 1499     isc_result_t result;
 1500     size_t rlen;
 1501     bool special = false;
 1502 
 1503     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
 1504     REQUIRE(entry != NULL);
 1505     REQUIRE(buffer != NULL && *buffer != NULL);
 1506 
 1507     isc_buffer_allocate(zone->catzs->mctx, &tbuf,
 1508                 strlen(zone->catzs->view->name) +
 1509                     2 * DNS_NAME_FORMATSIZE + 2);
 1510 
 1511     isc_buffer_putstr(tbuf, zone->catzs->view->name);
 1512     isc_buffer_putstr(tbuf, "_");
 1513     result = dns_name_totext(&zone->name, true, tbuf);
 1514     if (result != ISC_R_SUCCESS) {
 1515         goto cleanup;
 1516     }
 1517 
 1518     isc_buffer_putstr(tbuf, "_");
 1519     result = dns_name_totext(&entry->name, true, tbuf);
 1520     if (result != ISC_R_SUCCESS) {
 1521         goto cleanup;
 1522     }
 1523 
 1524     /*
 1525      * Search for slash and other special characters in the view and
 1526      * zone names.  Add a null terminator so we can use strpbrk(), then
 1527      * remove it.
 1528      */
 1529     isc_buffer_putuint8(tbuf, 0);
 1530     if (strpbrk(isc_buffer_base(tbuf), "\\/:") != NULL) {
 1531         special = true;
 1532     }
 1533     isc_buffer_subtract(tbuf, 1);
 1534 
 1535     /* __catz__<digest>.db */
 1536     rlen = (isc_md_type_get_size(ISC_MD_SHA256) * 2 + 1) + 12;
 1537 
 1538     /* optionally prepend with <zonedir>/ */
 1539     if (entry->opts.zonedir != NULL) {
 1540         rlen += strlen(entry->opts.zonedir) + 1;
 1541     }
 1542 
 1543     result = isc_buffer_reserve(buffer, (unsigned int)rlen);
 1544     if (result != ISC_R_SUCCESS) {
 1545         goto cleanup;
 1546     }
 1547 
 1548     if (entry->opts.zonedir != NULL) {
 1549         isc_buffer_putstr(*buffer, entry->opts.zonedir);
 1550         isc_buffer_putstr(*buffer, "/");
 1551     }
 1552 
 1553     isc_buffer_usedregion(tbuf, &r);
 1554     isc_buffer_putstr(*buffer, "__catz__");
 1555     if (special || tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) {
 1556         unsigned char digest[ISC_MAX_MD_SIZE];
 1557         unsigned int digestlen;
 1558 
 1559         /* we can do that because digest string < 2 * DNS_NAME */
 1560         result = isc_md(ISC_MD_SHA256, r.base, r.length, digest,
 1561                 &digestlen);
 1562         if (result != ISC_R_SUCCESS) {
 1563             goto cleanup;
 1564         }
 1565         result = digest2hex(digest, digestlen, (char *)r.base,
 1566                     ISC_SHA256_DIGESTLENGTH * 2 + 1);
 1567         if (result != ISC_R_SUCCESS) {
 1568             goto cleanup;
 1569         }
 1570         isc_buffer_putstr(*buffer, (char *)r.base);
 1571     } else {
 1572         isc_buffer_copyregion(*buffer, &r);
 1573     }
 1574 
 1575     isc_buffer_putstr(*buffer, ".db");
 1576     result = ISC_R_SUCCESS;
 1577 
 1578 cleanup:
 1579     isc_buffer_free(&tbuf);
 1580     return (result);
 1581 }
 1582 
 1583 isc_result_t
 1584 dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
 1585               isc_buffer_t **buf) {
 1586     /*
 1587      * We have to generate a text buffer with regular zone config:
 1588      * zone "foo.bar" {
 1589      *  type slave;
 1590      *  masters [ dscp X ] { ip1 port port1; ip2 port port2; };
 1591      * }
 1592      */
 1593     isc_buffer_t *buffer = NULL;
 1594     isc_region_t region;
 1595     isc_result_t result;
 1596     uint32_t i;
 1597     isc_netaddr_t netaddr;
 1598     char pbuf[sizeof("65535")]; /* used both for port number and DSCP */
 1599     char zname[DNS_NAME_FORMATSIZE];
 1600 
 1601     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
 1602     REQUIRE(entry != NULL);
 1603     REQUIRE(buf != NULL && *buf == NULL);
 1604 
 1605     /*
 1606      * The buffer will be reallocated if something won't fit,
 1607      * ISC_BUFFER_INCR seems like a good start.
 1608      */
 1609     isc_buffer_allocate(zone->catzs->mctx, &buffer, ISC_BUFFER_INCR);
 1610 
 1611     isc_buffer_setautorealloc(buffer, true);
 1612     isc_buffer_putstr(buffer, "zone \"");
 1613     dns_name_totext(&entry->name, true, buffer);
 1614     isc_buffer_putstr(buffer, "\" { type slave; masters");
 1615 
 1616     /*
 1617      * DSCP value has no default, but when it is specified, it is identical
 1618      * for all masters and cannot be overridden for a specific master IP, so
 1619      * use the DSCP value set for the first master
 1620      */
 1621     if (entry->opts.masters.count > 0 && entry->opts.masters.dscps[0] >= 0)
 1622     {
 1623         isc_buffer_putstr(buffer, " dscp ");
 1624         snprintf(pbuf, sizeof(pbuf), "%hd",
 1625              entry->opts.masters.dscps[0]);
 1626         isc_buffer_putstr(buffer, pbuf);
 1627     }
 1628 
 1629     isc_buffer_putstr(buffer, " { ");
 1630     for (i = 0; i < entry->opts.masters.count; i++) {
 1631         /*
 1632          * Every master must have an IP address assigned.
 1633          */
 1634         switch (entry->opts.masters.addrs[i].type.sa.sa_family) {
 1635         case AF_INET:
 1636         case AF_INET6:
 1637             break;
 1638         default:
 1639             dns_name_format(&entry->name, zname,
 1640                     DNS_NAME_FORMATSIZE);
 1641             isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1642                       DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
 1643                       "catz: zone '%s' uses an invalid master "
 1644                       "(no IP address assigned)",
 1645                       zname);
 1646             result = ISC_R_FAILURE;
 1647             goto cleanup;
 1648         }
 1649         isc_netaddr_fromsockaddr(&netaddr,
 1650                      &entry->opts.masters.addrs[i]);
 1651         isc_buffer_reserve(&buffer, INET6_ADDRSTRLEN);
 1652         result = isc_netaddr_totext(&netaddr, buffer);
 1653         RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1654 
 1655         isc_buffer_putstr(buffer, " port ");
 1656         snprintf(pbuf, sizeof(pbuf), "%u",
 1657              isc_sockaddr_getport(&entry->opts.masters.addrs[i]));
 1658         isc_buffer_putstr(buffer, pbuf);
 1659 
 1660         if (entry->opts.masters.keys[i] != NULL) {
 1661             isc_buffer_putstr(buffer, " key ");
 1662             result = dns_name_totext(entry->opts.masters.keys[i],
 1663                          true, buffer);
 1664             if (result != ISC_R_SUCCESS) {
 1665                 goto cleanup;
 1666             }
 1667         }
 1668         isc_buffer_putstr(buffer, "; ");
 1669     }
 1670     isc_buffer_putstr(buffer, "}; ");
 1671     if (!entry->opts.in_memory) {
 1672         isc_buffer_putstr(buffer, "file \"");
 1673         result = dns_catz_generate_masterfilename(zone, entry, &buffer);
 1674         if (result != ISC_R_SUCCESS) {
 1675             goto cleanup;
 1676         }
 1677         isc_buffer_putstr(buffer, "\"; ");
 1678     }
 1679     if (entry->opts.allow_query != NULL) {
 1680         isc_buffer_putstr(buffer, "allow-query { ");
 1681         isc_buffer_usedregion(entry->opts.allow_query, &region);
 1682         isc_buffer_copyregion(buffer, &region);
 1683         isc_buffer_putstr(buffer, "}; ");
 1684     }
 1685     if (entry->opts.allow_transfer != NULL) {
 1686         isc_buffer_putstr(buffer, "allow-transfer { ");
 1687         isc_buffer_usedregion(entry->opts.allow_transfer, &region);
 1688         isc_buffer_copyregion(buffer, &region);
 1689         isc_buffer_putstr(buffer, "}; ");
 1690     }
 1691 
 1692     isc_buffer_putstr(buffer, "};");
 1693     *buf = buffer;
 1694 
 1695     return (ISC_R_SUCCESS);
 1696 
 1697 cleanup:
 1698     isc_buffer_free(&buffer);
 1699     return (result);
 1700 }
 1701 
 1702 void
 1703 dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event) {
 1704     isc_result_t result;
 1705     dns_catz_zone_t *zone;
 1706     (void)task;
 1707 
 1708     REQUIRE(event != NULL);
 1709     zone = event->ev_arg;
 1710     REQUIRE(DNS_CATZ_ZONE_VALID(zone));
 1711 
 1712     LOCK(&zone->catzs->lock);
 1713     zone->updatepending = false;
 1714     dns_catz_update_from_db(zone->db, zone->catzs);
 1715     result = isc_timer_reset(zone->updatetimer, isc_timertype_inactive,
 1716                  NULL, NULL, true);
 1717     RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1718     isc_event_free(&event);
 1719     result = isc_time_now(&zone->lastupdated);
 1720     RUNTIME_CHECK(result == ISC_R_SUCCESS);
 1721     UNLOCK(&zone->catzs->lock);
 1722 }
 1723 
 1724 isc_result_t
 1725 dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
 1726     dns_catz_zones_t *catzs;
 1727     dns_catz_zone_t *zone = NULL;
 1728     isc_time_t now;
 1729     uint64_t tdiff;
 1730     isc_result_t result = ISC_R_SUCCESS;
 1731     isc_region_t r;
 1732 
 1733     REQUIRE(DNS_DB_VALID(db));
 1734     REQUIRE(fn_arg != NULL);
 1735     catzs = (dns_catz_zones_t *)fn_arg;
 1736 
 1737     dns_name_toregion(&db->origin, &r);
 1738 
 1739     LOCK(&catzs->lock);
 1740     result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&zone);
 1741     if (result != ISC_R_SUCCESS) {
 1742         goto cleanup;
 1743     }
 1744 
 1745     /* New zone came as AXFR */
 1746     if (zone->db != NULL && zone->db != db) {
 1747         if (zone->dbversion != NULL) {
 1748             dns_db_closeversion(zone->db, &zone->dbversion, false);
 1749         }
 1750         dns_db_detach(&zone->db);
 1751         /*
 1752          * We're not registering db update callback, it will be
 1753          * registered at the end of update_from_db
 1754          */
 1755         zone->db_registered = false;
 1756     }
 1757     if (zone->db == NULL) {
 1758         dns_db_attach(db, &zone->db);
 1759     }
 1760 
 1761     if (!zone->updatepending) {
 1762         zone->updatepending = true;
 1763         isc_time_now(&now);
 1764         tdiff = isc_time_microdiff(&now, &zone->lastupdated) / 1000000;
 1765         if (tdiff < zone->defoptions.min_update_interval) {
 1766             isc_interval_t interval;
 1767             isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1768                       DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
 1769                       "catz: new zone version came too soon, "
 1770                       "deferring update");
 1771             isc_interval_set(&interval,
 1772                      zone->defoptions.min_update_interval -
 1773                          (unsigned int)tdiff,
 1774                      0);
 1775             dns_db_currentversion(db, &zone->dbversion);
 1776             result = isc_timer_reset(zone->updatetimer,
 1777                          isc_timertype_once, NULL,
 1778                          &interval, true);
 1779             if (result != ISC_R_SUCCESS) {
 1780                 goto cleanup;
 1781             }
 1782         } else {
 1783             isc_event_t *event;
 1784 
 1785             dns_db_currentversion(db, &zone->dbversion);
 1786             ISC_EVENT_INIT(&zone->updateevent,
 1787                        sizeof(zone->updateevent), 0, NULL,
 1788                        DNS_EVENT_CATZUPDATED,
 1789                        dns_catz_update_taskaction, zone, zone,
 1790                        NULL, NULL);
 1791             event = &zone->updateevent;
 1792             isc_task_send(catzs->updater, &event);
 1793         }
 1794     } else {
 1795         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1796                   DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
 1797                   "catz: update already queued");
 1798         if (zone->dbversion != NULL) {
 1799             dns_db_closeversion(zone->db, &zone->dbversion, false);
 1800         }
 1801         dns_db_currentversion(zone->db, &zone->dbversion);
 1802     }
 1803 
 1804 cleanup:
 1805     UNLOCK(&catzs->lock);
 1806 
 1807     return (result);
 1808 }
 1809 
 1810 void
 1811 dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
 1812     dns_catz_zone_t *oldzone = NULL, *newzone = NULL;
 1813     isc_result_t result;
 1814     isc_region_t r;
 1815     dns_dbnode_t *node = NULL;
 1816     dns_dbiterator_t *it = NULL;
 1817     dns_fixedname_t fixname;
 1818     dns_name_t *name;
 1819     dns_rdatasetiter_t *rdsiter = NULL;
 1820     dns_rdataset_t rdataset;
 1821     char bname[DNS_NAME_FORMATSIZE];
 1822     isc_buffer_t ibname;
 1823     uint32_t vers;
 1824 
 1825     REQUIRE(DNS_DB_VALID(db));
 1826     REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
 1827 
 1828     /*
 1829      * Create a new catz in the same context as current catz.
 1830      */
 1831     dns_name_toregion(&db->origin, &r);
 1832     result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldzone);
 1833     if (result != ISC_R_SUCCESS) {
 1834         /* This can happen if we remove the zone in the meantime. */
 1835         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1836                   DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
 1837                   "catz: zone '%s' not in config", bname);
 1838         return;
 1839     }
 1840 
 1841     isc_buffer_init(&ibname, bname, DNS_NAME_FORMATSIZE);
 1842     result = dns_name_totext(&db->origin, true, &ibname);
 1843     INSIST(result == ISC_R_SUCCESS);
 1844 
 1845     result = dns_db_getsoaserial(db, oldzone->dbversion, &vers);
 1846     if (result != ISC_R_SUCCESS) {
 1847         /* A zone without SOA record?!? */
 1848         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1849                   DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
 1850                   "catz: zone '%s' has no SOA record (%s)", bname,
 1851                   isc_result_totext(result));
 1852         return;
 1853     }
 1854 
 1855     isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
 1856               ISC_LOG_INFO,
 1857               "catz: updating catalog zone '%s' with serial %d", bname,
 1858               vers);
 1859 
 1860     result = dns_catz_new_zone(catzs, &newzone, &db->origin);
 1861     if (result != ISC_R_SUCCESS) {
 1862         dns_db_closeversion(db, &oldzone->dbversion, false);
 1863         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1864                   DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
 1865                   "catz: failed to create new zone - %s",
 1866                   isc_result_totext(result));
 1867         return;
 1868     }
 1869 
 1870     result = dns_db_createiterator(db, DNS_DB_NONSEC3, &it);
 1871     if (result != ISC_R_SUCCESS) {
 1872         dns_catz_zone_detach(&newzone);
 1873         dns_db_closeversion(db, &oldzone->dbversion, false);
 1874         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1875                   DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
 1876                   "catz: failed to create DB iterator - %s",
 1877                   isc_result_totext(result));
 1878         return;
 1879     }
 1880 
 1881     name = dns_fixedname_initname(&fixname);
 1882 
 1883     /*
 1884      * Iterate over database to fill the new zone.
 1885      */
 1886     result = dns_dbiterator_first(it);
 1887     if (result != ISC_R_SUCCESS) {
 1888         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1889                   DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
 1890                   "catz: failed to get db iterator - %s",
 1891                   isc_result_totext(result));
 1892     }
 1893 
 1894     while (result == ISC_R_SUCCESS) {
 1895         result = dns_dbiterator_current(it, &node, name);
 1896         if (result != ISC_R_SUCCESS) {
 1897             isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1898                       DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
 1899                       "catz: failed to get db iterator - %s",
 1900                       isc_result_totext(result));
 1901             break;
 1902         }
 1903 
 1904         result = dns_db_allrdatasets(db, node, oldzone->dbversion, 0,
 1905                          &rdsiter);
 1906         if (result != ISC_R_SUCCESS) {
 1907             isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1908                       DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
 1909                       "catz: failed to fetch rrdatasets - %s",
 1910                       isc_result_totext(result));
 1911             dns_db_detachnode(db, &node);
 1912             break;
 1913         }
 1914 
 1915         dns_rdataset_init(&rdataset);
 1916         result = dns_rdatasetiter_first(rdsiter);
 1917         while (result == ISC_R_SUCCESS) {
 1918             dns_rdatasetiter_current(rdsiter, &rdataset);
 1919             result = dns_catz_update_process(catzs, newzone, name,
 1920                              &rdataset);
 1921             if (result != ISC_R_SUCCESS) {
 1922                 char cname[DNS_NAME_FORMATSIZE];
 1923                 char typebuf[DNS_RDATATYPE_FORMATSIZE];
 1924                 char classbuf[DNS_RDATACLASS_FORMATSIZE];
 1925 
 1926                 dns_name_format(name, cname,
 1927                         DNS_NAME_FORMATSIZE);
 1928                 dns_rdataclass_format(rdataset.rdclass,
 1929                               classbuf,
 1930                               sizeof(classbuf));
 1931                 dns_rdatatype_format(rdataset.type, typebuf,
 1932                              sizeof(typebuf));
 1933                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1934                           DNS_LOGMODULE_MASTER,
 1935                           ISC_LOG_WARNING,
 1936                           "catz: unknown record in catalog "
 1937                           "zone - %s %s %s(%s) - ignoring",
 1938                           cname, classbuf, typebuf,
 1939                           isc_result_totext(result));
 1940             }
 1941             dns_rdataset_disassociate(&rdataset);
 1942             if (result != ISC_R_SUCCESS) {
 1943                 break;
 1944             }
 1945             result = dns_rdatasetiter_next(rdsiter);
 1946         }
 1947 
 1948         dns_rdatasetiter_destroy(&rdsiter);
 1949 
 1950         dns_db_detachnode(db, &node);
 1951         result = dns_dbiterator_next(it);
 1952     }
 1953 
 1954     dns_dbiterator_destroy(&it);
 1955     dns_db_closeversion(db, &oldzone->dbversion, false);
 1956     isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
 1957               ISC_LOG_DEBUG(3),
 1958               "catz: update_from_db: iteration finished");
 1959 
 1960     /*
 1961      * Finally merge new zone into old zone.
 1962      */
 1963     result = dns_catz_zones_merge(oldzone, newzone);
 1964     dns_catz_zone_detach(&newzone);
 1965     if (result != ISC_R_SUCCESS) {
 1966         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 1967                   DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
 1968                   "catz: failed merging zones: %s",
 1969                   isc_result_totext(result));
 1970 
 1971         return;
 1972     }
 1973 
 1974     isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
 1975               ISC_LOG_DEBUG(3),
 1976               "catz: update_from_db: new zone merged");
 1977 
 1978     /*
 1979      * When we're doing reconfig and setting a new catalog zone
 1980      * from an existing zone we won't have a chance to set up
 1981      * update callback in zone_startload or axfr_makedb, but we will
 1982      * call onupdate() artificially so we can register the callback here.
 1983      */
 1984     if (!oldzone->db_registered) {
 1985         result = dns_db_updatenotify_register(
 1986             db, dns_catz_dbupdate_callback, oldzone->catzs);
 1987         if (result == ISC_R_SUCCESS) {
 1988             oldzone->db_registered = true;
 1989         }
 1990     }
 1991 }
 1992 
 1993 void
 1994 dns_catz_prereconfig(dns_catz_zones_t *catzs) {
 1995     isc_result_t result;
 1996     isc_ht_iter_t *iter = NULL;
 1997 
 1998     REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
 1999 
 2000     result = isc_ht_iter_create(catzs->zones, &iter);
 2001     INSIST(result == ISC_R_SUCCESS);
 2002     for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
 2003          result = isc_ht_iter_next(iter))
 2004     {
 2005         dns_catz_zone_t *zone = NULL;
 2006         isc_ht_iter_current(iter, (void **)&zone);
 2007         zone->active = false;
 2008     }
 2009     INSIST(result == ISC_R_NOMORE);
 2010     isc_ht_iter_destroy(&iter);
 2011 }
 2012 
 2013 void
 2014 dns_catz_postreconfig(dns_catz_zones_t *catzs) {
 2015     isc_result_t result;
 2016     dns_catz_zone_t *newzone = NULL;
 2017     isc_ht_iter_t *iter = NULL;
 2018 
 2019     REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
 2020 
 2021     LOCK(&catzs->lock);
 2022     result = isc_ht_iter_create(catzs->zones, &iter);
 2023     INSIST(result == ISC_R_SUCCESS);
 2024     for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) {
 2025         dns_catz_zone_t *zone = NULL;
 2026 
 2027         isc_ht_iter_current(iter, (void **)&zone);
 2028         if (!zone->active) {
 2029             char cname[DNS_NAME_FORMATSIZE];
 2030             dns_name_format(&zone->name, cname,
 2031                     DNS_NAME_FORMATSIZE);
 2032             isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
 2033                       DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
 2034                       "catz: removing catalog zone %s", cname);
 2035 
 2036             /*
 2037              * Merge the old zone with an empty one to remove
 2038              * all members.
 2039              */
 2040             result = dns_catz_new_zone(catzs, &newzone,
 2041                            &zone->name);
 2042             INSIST(result == ISC_R_SUCCESS);
 2043             dns_catz_zones_merge(zone, newzone);
 2044             dns_catz_zone_detach(&newzone);
 2045 
 2046             /* Make sure that we have an empty catalog zone. */
 2047             INSIST(isc_ht_count(zone->entries) == 0);
 2048             result = isc_ht_iter_delcurrent_next(iter);
 2049             dns_catz_zone_detach(&zone);
 2050         } else {
 2051             result = isc_ht_iter_next(iter);
 2052         }
 2053     }
 2054     UNLOCK(&catzs->lock);
 2055     RUNTIME_CHECK(result == ISC_R_NOMORE);
 2056     isc_ht_iter_destroy(&iter);
 2057 }
 2058 
 2059 isc_result_t
 2060 dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp) {
 2061     REQUIRE(DNS_CATZ_ZONE_VALID(catz));
 2062     return (isc_ht_iter_create(catz->entries, itp));
 2063 }