"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.16.7/lib/dns/zt.c" (4 Sep 2020, 14192 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 "zt.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.17.3_vs_9.17.4.

    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/atomic.h>
   18 #include <isc/file.h>
   19 #include <isc/magic.h>
   20 #include <isc/mem.h>
   21 #include <isc/string.h>
   22 #include <isc/task.h>
   23 #include <isc/util.h>
   24 
   25 #include <dns/log.h>
   26 #include <dns/name.h>
   27 #include <dns/rbt.h>
   28 #include <dns/rdataclass.h>
   29 #include <dns/result.h>
   30 #include <dns/view.h>
   31 #include <dns/zone.h>
   32 #include <dns/zt.h>
   33 
   34 struct zt_load_params {
   35     dns_zt_zoneloaded_t dl;
   36     bool newonly;
   37 };
   38 
   39 struct dns_zt {
   40     /* Unlocked. */
   41     unsigned int magic;
   42     isc_mem_t *mctx;
   43     dns_rdataclass_t rdclass;
   44     isc_rwlock_t rwlock;
   45     dns_zt_allloaded_t loaddone;
   46     void *loaddone_arg;
   47     struct zt_load_params *loadparams;
   48 
   49     /* Atomic */
   50     atomic_bool flush;
   51     isc_refcount_t references;
   52     isc_refcount_t loads_pending;
   53 
   54     /* Locked by lock. */
   55     dns_rbt_t *table;
   56 };
   57 
   58 #define ZTMAGIC      ISC_MAGIC('Z', 'T', 'b', 'l')
   59 #define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC)
   60 
   61 static void
   62 auto_detach(void *, void *);
   63 
   64 static isc_result_t
   65 load(dns_zone_t *zone, void *uap);
   66 
   67 static isc_result_t
   68 asyncload(dns_zone_t *zone, void *callback);
   69 
   70 static isc_result_t
   71 freezezones(dns_zone_t *zone, void *uap);
   72 
   73 static isc_result_t
   74 doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
   75 
   76 isc_result_t
   77 dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) {
   78     dns_zt_t *zt;
   79     isc_result_t result;
   80 
   81     REQUIRE(ztp != NULL && *ztp == NULL);
   82 
   83     zt = isc_mem_get(mctx, sizeof(*zt));
   84 
   85     zt->table = NULL;
   86     result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
   87     if (result != ISC_R_SUCCESS) {
   88         goto cleanup_zt;
   89     }
   90 
   91     result = isc_rwlock_init(&zt->rwlock, 0, 0);
   92     if (result != ISC_R_SUCCESS) {
   93         goto cleanup_rbt;
   94     }
   95 
   96     zt->mctx = NULL;
   97     isc_mem_attach(mctx, &zt->mctx);
   98     isc_refcount_init(&zt->references, 1);
   99     atomic_init(&zt->flush, false);
  100     zt->rdclass = rdclass;
  101     zt->magic = ZTMAGIC;
  102     zt->loaddone = NULL;
  103     zt->loaddone_arg = NULL;
  104     zt->loadparams = NULL;
  105     isc_refcount_init(&zt->loads_pending, 0);
  106     *ztp = zt;
  107 
  108     return (ISC_R_SUCCESS);
  109 
  110 cleanup_rbt:
  111     dns_rbt_destroy(&zt->table);
  112 
  113 cleanup_zt:
  114     isc_mem_put(mctx, zt, sizeof(*zt));
  115 
  116     return (result);
  117 }
  118 
  119 isc_result_t
  120 dns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) {
  121     isc_result_t result;
  122     dns_zone_t *dummy = NULL;
  123     dns_name_t *name;
  124 
  125     REQUIRE(VALID_ZT(zt));
  126 
  127     name = dns_zone_getorigin(zone);
  128 
  129     RWLOCK(&zt->rwlock, isc_rwlocktype_write);
  130 
  131     result = dns_rbt_addname(zt->table, name, zone);
  132     if (result == ISC_R_SUCCESS) {
  133         dns_zone_attach(zone, &dummy);
  134     }
  135 
  136     RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
  137 
  138     return (result);
  139 }
  140 
  141 isc_result_t
  142 dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
  143     isc_result_t result;
  144     dns_name_t *name;
  145 
  146     REQUIRE(VALID_ZT(zt));
  147 
  148     name = dns_zone_getorigin(zone);
  149 
  150     RWLOCK(&zt->rwlock, isc_rwlocktype_write);
  151 
  152     result = dns_rbt_deletename(zt->table, name, false);
  153 
  154     RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
  155 
  156     return (result);
  157 }
  158 
  159 isc_result_t
  160 dns_zt_find(dns_zt_t *zt, const dns_name_t *name, unsigned int options,
  161         dns_name_t *foundname, dns_zone_t **zonep) {
  162     isc_result_t result;
  163     dns_zone_t *dummy = NULL;
  164     unsigned int rbtoptions = 0;
  165 
  166     REQUIRE(VALID_ZT(zt));
  167 
  168     if ((options & DNS_ZTFIND_NOEXACT) != 0) {
  169         rbtoptions |= DNS_RBTFIND_NOEXACT;
  170     }
  171 
  172     RWLOCK(&zt->rwlock, isc_rwlocktype_read);
  173 
  174     result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
  175                   (void **)(void *)&dummy);
  176     if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
  177         /*
  178          * If DNS_ZTFIND_MIRROR is set and the zone which was
  179          * determined to be the deepest match for the supplied name is
  180          * a mirror zone which is expired or not yet loaded, treat it
  181          * as non-existent.  This will trigger a fallback to recursion
  182          * instead of returning a SERVFAIL.
  183          *
  184          * Note that currently only the deepest match in the zone table
  185          * is checked.  Consider a server configured with two mirror
  186          * zones: "bar" and its child, "foo.bar".  If zone data is
  187          * available for "bar" but not for "foo.bar", a query with
  188          * QNAME equal to or below "foo.bar" will cause ISC_R_NOTFOUND
  189          * to be returned, not DNS_R_PARTIALMATCH, despite zone data
  190          * being available for "bar".  This is considered to be an edge
  191          * case, handling which more appropriately is possible, but
  192          * arguably not worth the added complexity.
  193          */
  194         if ((options & DNS_ZTFIND_MIRROR) != 0 &&
  195             dns_zone_gettype(dummy) == dns_zone_mirror &&
  196             !dns_zone_isloaded(dummy))
  197         {
  198             result = ISC_R_NOTFOUND;
  199         } else {
  200             dns_zone_attach(dummy, zonep);
  201         }
  202     }
  203 
  204     RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
  205 
  206     return (result);
  207 }
  208 
  209 void
  210 dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) {
  211     REQUIRE(VALID_ZT(zt));
  212     REQUIRE(ztp != NULL && *ztp == NULL);
  213 
  214     isc_refcount_increment(&zt->references);
  215 
  216     *ztp = zt;
  217 }
  218 
  219 static isc_result_t
  220 flush(dns_zone_t *zone, void *uap) {
  221     UNUSED(uap);
  222     return (dns_zone_flush(zone));
  223 }
  224 
  225 static void
  226 zt_destroy(dns_zt_t *zt) {
  227     if (atomic_load_acquire(&zt->flush)) {
  228         (void)dns_zt_apply(zt, false, NULL, flush, NULL);
  229     }
  230     dns_rbt_destroy(&zt->table);
  231     isc_rwlock_destroy(&zt->rwlock);
  232     zt->magic = 0;
  233     isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
  234 }
  235 
  236 static void
  237 zt_flushanddetach(dns_zt_t **ztp, bool need_flush) {
  238     dns_zt_t *zt;
  239 
  240     REQUIRE(ztp != NULL && VALID_ZT(*ztp));
  241 
  242     zt = *ztp;
  243     *ztp = NULL;
  244 
  245     if (need_flush) {
  246         atomic_store_release(&zt->flush, true);
  247     }
  248 
  249     if (isc_refcount_decrement(&zt->references) == 1) {
  250         zt_destroy(zt);
  251     }
  252 }
  253 
  254 void
  255 dns_zt_flushanddetach(dns_zt_t **ztp) {
  256     zt_flushanddetach(ztp, true);
  257 }
  258 
  259 void
  260 dns_zt_detach(dns_zt_t **ztp) {
  261     zt_flushanddetach(ztp, false);
  262 }
  263 
  264 isc_result_t
  265 dns_zt_load(dns_zt_t *zt, bool stop, bool newonly) {
  266     isc_result_t result;
  267     struct zt_load_params params;
  268     REQUIRE(VALID_ZT(zt));
  269     params.newonly = newonly;
  270     RWLOCK(&zt->rwlock, isc_rwlocktype_read);
  271     result = dns_zt_apply(zt, stop, NULL, load, &params);
  272     RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
  273     return (result);
  274 }
  275 
  276 static isc_result_t
  277 load(dns_zone_t *zone, void *paramsv) {
  278     isc_result_t result;
  279     struct zt_load_params *params = (struct zt_load_params *)paramsv;
  280     result = dns_zone_load(zone, params->newonly);
  281     if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
  282         result == DNS_R_DYNAMIC)
  283     {
  284         result = ISC_R_SUCCESS;
  285     }
  286     return (result);
  287 }
  288 
  289 static void
  290 call_loaddone(dns_zt_t *zt) {
  291     dns_zt_allloaded_t loaddone = zt->loaddone;
  292     void *loaddone_arg = zt->loaddone_arg;
  293 
  294     /*
  295      * Set zt->loaddone, zt->loaddone_arg and zt->loadparams to NULL
  296      * before calling loaddone.
  297      */
  298     zt->loaddone = NULL;
  299     zt->loaddone_arg = NULL;
  300 
  301     isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
  302     zt->loadparams = NULL;
  303 
  304     /*
  305      * Call the callback last.
  306      */
  307     if (loaddone != NULL) {
  308         loaddone(loaddone_arg);
  309     }
  310 }
  311 
  312 isc_result_t
  313 dns_zt_asyncload(dns_zt_t *zt, bool newonly, dns_zt_allloaded_t alldone,
  314          void *arg) {
  315     isc_result_t result;
  316     uint_fast32_t loads_pending;
  317 
  318     REQUIRE(VALID_ZT(zt));
  319 
  320     /*
  321      * Obtain a reference to zt->loads_pending so that asyncload can
  322      * safely decrement both zt->references and zt->loads_pending
  323      * without going to zero.
  324      */
  325     loads_pending = isc_refcount_increment0(&zt->loads_pending);
  326     INSIST(loads_pending == 0);
  327 
  328     /*
  329      * Only one dns_zt_asyncload call at a time should be active so
  330      * these pointers should be NULL.  They are set back to NULL
  331      * before the zt->loaddone (alldone) is called in call_loaddone.
  332      */
  333     INSIST(zt->loadparams == NULL);
  334     INSIST(zt->loaddone == NULL);
  335     INSIST(zt->loaddone_arg == NULL);
  336 
  337     zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params));
  338     zt->loadparams->dl = doneloading;
  339     zt->loadparams->newonly = newonly;
  340     zt->loaddone = alldone;
  341     zt->loaddone_arg = arg;
  342 
  343     RWLOCK(&zt->rwlock, isc_rwlocktype_read);
  344     result = dns_zt_apply(zt, false, NULL, asyncload, zt);
  345     RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
  346 
  347     /*
  348      * Have all the loads completed?
  349      */
  350     if (isc_refcount_decrement(&zt->loads_pending) == 1) {
  351         call_loaddone(zt);
  352     }
  353 
  354     return (result);
  355 }
  356 
  357 /*
  358  * Initiates asynchronous loading of zone 'zone'.  'callback' is a
  359  * pointer to a function which will be used to inform the caller when
  360  * the zone loading is complete.
  361  */
  362 static isc_result_t
  363 asyncload(dns_zone_t *zone, void *zt_) {
  364     isc_result_t result;
  365     struct dns_zt *zt = (dns_zt_t *)zt_;
  366     REQUIRE(zone != NULL);
  367 
  368     isc_refcount_increment(&zt->references);
  369     isc_refcount_increment(&zt->loads_pending);
  370 
  371     result = dns_zone_asyncload(zone, zt->loadparams->newonly,
  372                     *zt->loadparams->dl, zt);
  373     if (result != ISC_R_SUCCESS) {
  374         /*
  375          * Caller is holding a reference to zt->loads_pending
  376          * and zt->references so these can't decrement to zero.
  377          */
  378         isc_refcount_decrement1(&zt->references);
  379         isc_refcount_decrement1(&zt->loads_pending);
  380     }
  381     return (ISC_R_SUCCESS);
  382 }
  383 
  384 isc_result_t
  385 dns_zt_freezezones(dns_zt_t *zt, bool freeze) {
  386     isc_result_t result, tresult;
  387 
  388     REQUIRE(VALID_ZT(zt));
  389 
  390     RWLOCK(&zt->rwlock, isc_rwlocktype_read);
  391     result = dns_zt_apply(zt, false, &tresult, freezezones, &freeze);
  392     RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
  393     if (tresult == ISC_R_NOTFOUND) {
  394         tresult = ISC_R_SUCCESS;
  395     }
  396     return ((result == ISC_R_SUCCESS) ? tresult : result);
  397 }
  398 
  399 static isc_result_t
  400 freezezones(dns_zone_t *zone, void *uap) {
  401     bool freeze = *(bool *)uap;
  402     bool frozen;
  403     isc_result_t result = ISC_R_SUCCESS;
  404     char classstr[DNS_RDATACLASS_FORMATSIZE];
  405     char zonename[DNS_NAME_FORMATSIZE];
  406     dns_zone_t *raw = NULL;
  407     dns_view_t *view;
  408     const char *vname;
  409     const char *sep;
  410     int level;
  411 
  412     dns_zone_getraw(zone, &raw);
  413     if (raw != NULL) {
  414         zone = raw;
  415     }
  416     if (dns_zone_gettype(zone) != dns_zone_master) {
  417         if (raw != NULL) {
  418             dns_zone_detach(&raw);
  419         }
  420         return (ISC_R_SUCCESS);
  421     }
  422     if (!dns_zone_isdynamic(zone, true)) {
  423         if (raw != NULL) {
  424             dns_zone_detach(&raw);
  425         }
  426         return (ISC_R_SUCCESS);
  427     }
  428 
  429     frozen = dns_zone_getupdatedisabled(zone);
  430     if (freeze) {
  431         if (frozen) {
  432             result = DNS_R_FROZEN;
  433         }
  434         if (result == ISC_R_SUCCESS) {
  435             result = dns_zone_flush(zone);
  436         }
  437         if (result == ISC_R_SUCCESS) {
  438             dns_zone_setupdatedisabled(zone, freeze);
  439         }
  440     } else {
  441         if (frozen) {
  442             result = dns_zone_loadandthaw(zone);
  443             if (result == DNS_R_CONTINUE ||
  444                 result == DNS_R_UPTODATE) {
  445                 result = ISC_R_SUCCESS;
  446             }
  447         }
  448     }
  449     view = dns_zone_getview(zone);
  450     if (strcmp(view->name, "_bind") == 0 || strcmp(view->name, "_defaul"
  451                                    "t") == 0)
  452     {
  453         vname = "";
  454         sep = "";
  455     } else {
  456         vname = view->name;
  457         sep = " ";
  458     }
  459     dns_rdataclass_format(dns_zone_getclass(zone), classstr,
  460                   sizeof(classstr));
  461     dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
  462     level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
  463     isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
  464               level, "%s zone '%s/%s'%s%s: %s",
  465               freeze ? "freezing" : "thawing", zonename, classstr, sep,
  466               vname, isc_result_totext(result));
  467     if (raw != NULL) {
  468         dns_zone_detach(&raw);
  469     }
  470     return (result);
  471 }
  472 
  473 void
  474 dns_zt_setviewcommit(dns_zt_t *zt) {
  475     dns_rbtnode_t *node;
  476     dns_rbtnodechain_t chain;
  477     isc_result_t result;
  478 
  479     REQUIRE(VALID_ZT(zt));
  480 
  481     dns_rbtnodechain_init(&chain);
  482 
  483     result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
  484     while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
  485         result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
  486         if (result == ISC_R_SUCCESS && node->data != NULL) {
  487             dns_zone_setviewcommit(node->data);
  488         }
  489 
  490         result = dns_rbtnodechain_next(&chain, NULL, NULL);
  491     }
  492 
  493     dns_rbtnodechain_invalidate(&chain);
  494 }
  495 
  496 void
  497 dns_zt_setviewrevert(dns_zt_t *zt) {
  498     dns_rbtnode_t *node;
  499     dns_rbtnodechain_t chain;
  500     isc_result_t result;
  501 
  502     REQUIRE(VALID_ZT(zt));
  503 
  504     dns_rbtnodechain_init(&chain);
  505 
  506     result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
  507     while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
  508         result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
  509         if (result == ISC_R_SUCCESS && node->data != NULL) {
  510             dns_zone_setviewrevert(node->data);
  511         }
  512 
  513         result = dns_rbtnodechain_next(&chain, NULL, NULL);
  514     }
  515 
  516     dns_rbtnodechain_invalidate(&chain);
  517 }
  518 
  519 isc_result_t
  520 dns_zt_apply(dns_zt_t *zt, bool stop, isc_result_t *sub,
  521          isc_result_t (*action)(dns_zone_t *, void *), void *uap) {
  522     dns_rbtnode_t *node;
  523     dns_rbtnodechain_t chain;
  524     isc_result_t result, tresult = ISC_R_SUCCESS;
  525     dns_zone_t *zone;
  526 
  527     REQUIRE(VALID_ZT(zt));
  528     REQUIRE(action != NULL);
  529 
  530     dns_rbtnodechain_init(&chain);
  531     result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
  532     if (result == ISC_R_NOTFOUND) {
  533         /*
  534          * The tree is empty.
  535          */
  536         tresult = result;
  537         result = ISC_R_NOMORE;
  538     }
  539     while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
  540         result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
  541         if (result == ISC_R_SUCCESS) {
  542             zone = node->data;
  543             if (zone != NULL) {
  544                 result = (action)(zone, uap);
  545             }
  546             if (result != ISC_R_SUCCESS && stop) {
  547                 tresult = result;
  548                 goto cleanup; /* don't break */
  549             } else if (result != ISC_R_SUCCESS &&
  550                    tresult == ISC_R_SUCCESS) {
  551                 tresult = result;
  552             }
  553         }
  554         result = dns_rbtnodechain_next(&chain, NULL, NULL);
  555     }
  556     if (result == ISC_R_NOMORE) {
  557         result = ISC_R_SUCCESS;
  558     }
  559 
  560 cleanup:
  561     dns_rbtnodechain_invalidate(&chain);
  562     if (sub != NULL) {
  563         *sub = tresult;
  564     }
  565 
  566     return (result);
  567 }
  568 
  569 /*
  570  * Decrement the loads_pending counter; when counter reaches
  571  * zero, call the loaddone callback that was initially set by
  572  * dns_zt_asyncload().
  573  */
  574 static isc_result_t
  575 doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
  576     UNUSED(zone);
  577     UNUSED(task);
  578 
  579     REQUIRE(VALID_ZT(zt));
  580 
  581     if (isc_refcount_decrement(&zt->loads_pending) == 1) {
  582         call_loaddone(zt);
  583     }
  584 
  585     if (isc_refcount_decrement(&zt->references) == 1) {
  586         zt_destroy(zt);
  587     }
  588 
  589     return (ISC_R_SUCCESS);
  590 }
  591 
  592 /***
  593  *** Private
  594  ***/
  595 
  596 static void
  597 auto_detach(void *data, void *arg) {
  598     dns_zone_t *zone = data;
  599 
  600     UNUSED(arg);
  601     dns_zone_detach(&zone);
  602 }