"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.9.2/src/knot/updates/changesets.c" (12 Dec 2019, 15016 Bytes) of package /linux/misc/dns/knot-2.9.2.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 "changesets.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.1_vs_2.9.2.

    1 /*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
    2 
    3     This program is free software: you can redistribute it and/or modify
    4     it under the terms of the GNU General Public License as published by
    5     the Free Software Foundation, either version 3 of the License, or
    6     (at your option) any later version.
    7 
    8     This program is distributed in the hope that it will be useful,
    9     but WITHOUT ANY WARRANTY; without even the implied warranty of
   10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11     GNU General Public License for more details.
   12 
   13     You should have received a copy of the GNU General Public License
   14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
   15  */
   16 
   17 #include <assert.h>
   18 #include <stdlib.h>
   19 #include <stdarg.h>
   20 
   21 #include "knot/updates/changesets.h"
   22 #include "knot/updates/apply.h"
   23 #include "libknot/libknot.h"
   24 #include "knot/zone/zone-dump.h"
   25 #include "contrib/macros.h"
   26 #include "contrib/mempattern.h"
   27 
   28 static int handle_soa(knot_rrset_t **soa, const knot_rrset_t *rrset)
   29 {
   30     assert(soa);
   31     assert(rrset);
   32 
   33     if (*soa != NULL) {
   34         knot_rrset_free(*soa, NULL);
   35     }
   36 
   37     *soa = knot_rrset_copy(rrset, NULL);
   38     if (*soa == NULL) {
   39         return KNOT_ENOMEM;
   40     }
   41 
   42     return KNOT_EOK;
   43 }
   44 
   45 /*! \brief Adds RRSet to given zone. */
   46 static int add_rr_to_contents(zone_contents_t *z, const knot_rrset_t *rrset)
   47 {
   48     zone_node_t *n = NULL;
   49     int ret = zone_contents_add_rr(z, rrset, &n);
   50     UNUSED(n);
   51 
   52     // We don't care of TTLs.
   53     return ret == KNOT_ETTL ? KNOT_EOK : ret;
   54 }
   55 
   56 /*! \brief Inits changeset iterator with given tries. */
   57 static int changeset_iter_init(changeset_iter_t *ch_it, size_t tries, ...)
   58 {
   59     memset(ch_it, 0, sizeof(*ch_it));
   60 
   61     va_list args;
   62     va_start(args, tries);
   63 
   64     assert(tries <= sizeof(ch_it->trees) / sizeof(*ch_it->trees));
   65     for (size_t i = 0; i < tries; ++i) {
   66         zone_tree_t *t = va_arg(args, zone_tree_t *);
   67         if (t == NULL) {
   68             continue;
   69         }
   70 
   71         ch_it->trees[ch_it->n_trees++] = t;
   72     }
   73 
   74     va_end(args);
   75 
   76     assert(ch_it->n_trees);
   77     return zone_tree_it_begin(ch_it->trees[0], &ch_it->it);
   78 }
   79 
   80 // removes from counterpart what is in rr.
   81 // fixed_rr is an output parameter, holding a copy of rr without what has been removed from counterpart
   82 static void check_redundancy(zone_contents_t *counterpart, const knot_rrset_t *rr, knot_rrset_t **fixed_rr)
   83 {
   84     if (fixed_rr != NULL) {
   85         *fixed_rr = knot_rrset_copy(rr, NULL);
   86     }
   87 
   88     zone_node_t *node = zone_contents_find_node_for_rr(counterpart, rr);
   89     if (node == NULL) {
   90         return;
   91     }
   92 
   93     if (!node_rrtype_exists(node, rr->type)) {
   94         return;
   95     }
   96 
   97     uint32_t rrs_ttl = node_rrset(node, rr->type).ttl;
   98 
   99     if (fixed_rr != NULL && *fixed_rr != NULL &&
  100         ((*fixed_rr)->ttl == rrs_ttl || rr->type == KNOT_RRTYPE_RRSIG)) {
  101         int ret = knot_rdataset_subtract(&(*fixed_rr)->rrs, node_rdataset(node, rr->type), NULL);
  102         if (ret != KNOT_EOK) {
  103             return;
  104         }
  105     }
  106 
  107     // TTL of RRSIGs is better determined by original_ttl field, which is compared as part of rdata anyway
  108     if (rr->ttl == rrs_ttl || rr->type == KNOT_RRTYPE_RRSIG) {
  109         int ret = node_remove_rrset(node, rr, NULL);
  110         if (ret != KNOT_EOK) {
  111             return;
  112         }
  113     }
  114 
  115     if (node->rrset_count == 0 && node->children == 0 && node != counterpart->apex) {
  116         zone_tree_t *t = knot_rrset_is_nsec3rel(rr) ?
  117                  counterpart->nsec3_nodes : counterpart->nodes;
  118         zone_tree_del_node(t, node, true);
  119     }
  120 
  121     return;
  122 }
  123 
  124 int changeset_init(changeset_t *ch, const knot_dname_t *apex)
  125 {
  126     memset(ch, 0, sizeof(changeset_t));
  127 
  128     // Init local changes
  129     ch->add = zone_contents_new(apex, false);
  130     if (ch->add == NULL) {
  131         return KNOT_ENOMEM;
  132     }
  133     ch->remove = zone_contents_new(apex, false);
  134     if (ch->remove == NULL) {
  135         zone_contents_free(ch->add);
  136         return KNOT_ENOMEM;
  137     }
  138 
  139     return KNOT_EOK;
  140 }
  141 
  142 changeset_t *changeset_new(const knot_dname_t *apex)
  143 {
  144     changeset_t *ret = malloc(sizeof(changeset_t));
  145     if (ret == NULL) {
  146         return NULL;
  147     }
  148 
  149     if (changeset_init(ret, apex) == KNOT_EOK) {
  150         return ret;
  151     } else {
  152         free(ret);
  153         return NULL;
  154     }
  155 }
  156 
  157 bool changeset_empty(const changeset_t *ch)
  158 {
  159     if (ch == NULL) {
  160         return true;
  161     }
  162 
  163     if (zone_contents_is_empty(ch->remove) &&
  164         zone_contents_is_empty(ch->add)) {
  165         if (ch->soa_to == NULL) {
  166             return true;
  167         }
  168         if (ch->soa_from != NULL && ch->soa_to != NULL &&
  169             knot_rrset_equal(ch->soa_from, ch->soa_to, false)) {
  170             return true;
  171         }
  172     }
  173 
  174     return false;
  175 }
  176 
  177 size_t changeset_size(const changeset_t *ch)
  178 {
  179     if (ch == NULL) {
  180         return 0;
  181     }
  182 
  183     changeset_iter_t itt;
  184     changeset_iter_all(&itt, ch);
  185 
  186     size_t size = 0;
  187     knot_rrset_t rr = changeset_iter_next(&itt);
  188     while(!knot_rrset_empty(&rr)) {
  189         ++size;
  190         rr = changeset_iter_next(&itt);
  191     }
  192     changeset_iter_clear(&itt);
  193 
  194     if (!knot_rrset_empty(ch->soa_from)) {
  195         size += 1;
  196     }
  197     if (!knot_rrset_empty(ch->soa_to)) {
  198         size += 1;
  199     }
  200 
  201     return size;
  202 }
  203 
  204 int changeset_add_addition(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags)
  205 {
  206     if (!ch || !rrset) {
  207         return KNOT_EINVAL;
  208     }
  209 
  210     if (rrset->type == KNOT_RRTYPE_SOA) {
  211         /* Do not add SOAs into actual contents. */
  212         return handle_soa(&ch->soa_to, rrset);
  213     }
  214 
  215     knot_rrset_t *rrset_cancelout = NULL;
  216 
  217     /* Check if there's any removal and remove that, then add this
  218      * addition anyway. Required to change TTLs. */
  219     if (flags & CHANGESET_CHECK) {
  220         /* If we delete the rrset, we need to hold a copy to add it later */
  221         rrset = knot_rrset_copy(rrset, NULL);
  222         if (rrset == NULL) {
  223             return KNOT_ENOMEM;
  224         }
  225 
  226         check_redundancy(ch->remove, rrset, &rrset_cancelout);
  227     }
  228 
  229     const knot_rrset_t *to_add = (rrset_cancelout == NULL ? rrset : rrset_cancelout);
  230     int ret = knot_rrset_empty(to_add) ? KNOT_EOK : add_rr_to_contents(ch->add, to_add);
  231 
  232     if (flags & CHANGESET_CHECK) {
  233         knot_rrset_free((knot_rrset_t *)rrset, NULL);
  234     }
  235     knot_rrset_free(rrset_cancelout, NULL);
  236 
  237     return ret;
  238 }
  239 
  240 int changeset_add_removal(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags)
  241 {
  242     if (!ch || !rrset) {
  243         return KNOT_EINVAL;
  244     }
  245 
  246     if (rrset->type == KNOT_RRTYPE_SOA) {
  247         /* Do not add SOAs into actual contents. */
  248         return handle_soa(&ch->soa_from, rrset);
  249     }
  250 
  251     knot_rrset_t *rrset_cancelout = NULL;
  252 
  253     /* Check if there's any addition and remove that, then add this
  254      * removal anyway. */
  255     if (flags & CHANGESET_CHECK) {
  256         /* If we delete the rrset, we need to hold a copy to add it later */
  257         rrset = knot_rrset_copy(rrset, NULL);
  258         if (rrset == NULL) {
  259             return KNOT_ENOMEM;
  260         }
  261 
  262         check_redundancy(ch->add, rrset, &rrset_cancelout);
  263     }
  264 
  265     const knot_rrset_t *to_remove = (rrset_cancelout == NULL ? rrset : rrset_cancelout);
  266     int ret = (knot_rrset_empty(to_remove) || ch->remove == NULL) ? KNOT_EOK : add_rr_to_contents(ch->remove, to_remove);
  267 
  268     if (flags & CHANGESET_CHECK) {
  269         knot_rrset_free((knot_rrset_t *)rrset, NULL);
  270     }
  271     knot_rrset_free(rrset_cancelout, NULL);
  272 
  273     return ret;
  274 }
  275 
  276 int changeset_remove_addition(changeset_t *ch, const knot_rrset_t *rrset)
  277 {
  278     if (rrset->type == KNOT_RRTYPE_SOA) {
  279         /* Do not add SOAs into actual contents. */
  280         if (ch->soa_to != NULL) {
  281             knot_rrset_free(ch->soa_to, NULL);
  282             ch->soa_to = NULL;
  283         }
  284         return KNOT_EOK;
  285     }
  286 
  287     zone_node_t *n = NULL;
  288     return zone_contents_remove_rr(ch->add, rrset, &n);
  289 }
  290 
  291 int changeset_remove_removal(changeset_t *ch, const knot_rrset_t *rrset)
  292 {
  293     if (rrset->type == KNOT_RRTYPE_SOA) {
  294         /* Do not add SOAs into actual contents. */
  295         if (ch->soa_from != NULL) {
  296             knot_rrset_free(ch->soa_from, NULL);
  297             ch->soa_from = NULL;
  298         }
  299         return KNOT_EOK;
  300     }
  301 
  302     zone_node_t *n = NULL;
  303     return zone_contents_remove_rr(ch->remove, rrset, &n);
  304 }
  305 
  306 int changeset_merge(changeset_t *ch1, const changeset_t *ch2, int flags)
  307 {
  308     changeset_iter_t itt;
  309     changeset_iter_rem(&itt, ch2);
  310 
  311     knot_rrset_t rrset = changeset_iter_next(&itt);
  312     while (!knot_rrset_empty(&rrset)) {
  313         int ret = changeset_add_removal(ch1, &rrset, CHANGESET_CHECK | flags);
  314         if (ret != KNOT_EOK) {
  315             changeset_iter_clear(&itt);
  316             return ret;
  317         }
  318         rrset = changeset_iter_next(&itt);
  319     }
  320     changeset_iter_clear(&itt);
  321 
  322     changeset_iter_add(&itt, ch2);
  323 
  324     rrset = changeset_iter_next(&itt);
  325     while (!knot_rrset_empty(&rrset)) {
  326         int ret = changeset_add_addition(ch1, &rrset, CHANGESET_CHECK | flags);
  327         if (ret != KNOT_EOK) {
  328             changeset_iter_clear(&itt);
  329             return ret;
  330         }
  331         rrset = changeset_iter_next(&itt);
  332     }
  333     changeset_iter_clear(&itt);
  334 
  335     // Use soa_to and serial from the second changeset
  336     // soa_to from the first changeset is redundant, delete it
  337     if (ch2->soa_to == NULL && ch2->soa_from == NULL) {
  338         // but not if ch2 has no soa change
  339         return KNOT_EOK;
  340     }
  341     knot_rrset_t *soa_copy = knot_rrset_copy(ch2->soa_to, NULL);
  342     if (soa_copy == NULL && ch2->soa_to) {
  343         return KNOT_ENOMEM;
  344     }
  345     knot_rrset_free(ch1->soa_to, NULL);
  346     ch1->soa_to = soa_copy;
  347 
  348     return KNOT_EOK;
  349 }
  350 
  351 uint32_t changeset_from(const changeset_t *ch)
  352 {
  353     return ch->soa_from == NULL ? 0 : knot_soa_serial(ch->soa_from->rrs.rdata);
  354 }
  355 
  356 uint32_t changeset_to(const changeset_t *ch)
  357 {
  358     return ch->soa_to == NULL ? 0 : knot_soa_serial(ch->soa_to->rrs.rdata);
  359 }
  360 
  361 bool changeset_differs_just_serial(const changeset_t *ch)
  362 {
  363     if (ch == NULL || ch->soa_from == NULL || ch->soa_to == NULL) {
  364         return false;
  365     }
  366 
  367     knot_rrset_t *soa_to_cpy = knot_rrset_copy(ch->soa_to, NULL);
  368     knot_soa_serial_set(soa_to_cpy->rrs.rdata, knot_soa_serial(ch->soa_from->rrs.rdata));
  369 
  370     bool ret = knot_rrset_equal(ch->soa_from, soa_to_cpy, true);
  371     knot_rrset_free(soa_to_cpy, NULL);
  372 
  373     changeset_iter_t itt;
  374     changeset_iter_all(&itt, ch);
  375 
  376     knot_rrset_t rrset = changeset_iter_next(&itt);
  377     while (!knot_rrset_empty(&rrset) && ret) {
  378         if (rrset.type != KNOT_RRTYPE_RRSIG || rrset.rrs.count != 1 ||
  379             knot_rrsig_type_covered(rrset.rrs.rdata) != KNOT_RRTYPE_SOA) {
  380             ret = false;
  381         }
  382         rrset = changeset_iter_next(&itt);
  383     }
  384     changeset_iter_clear(&itt);
  385 
  386     return ret;
  387 }
  388 
  389 void changesets_clear(list_t *chgs)
  390 {
  391     if (chgs) {
  392         changeset_t *chg, *nxt;
  393         WALK_LIST_DELSAFE(chg, nxt, *chgs) {
  394             changeset_clear(chg);
  395             rem_node(&chg->n);
  396         }
  397         init_list(chgs);
  398     }
  399 }
  400 
  401 void changesets_free(list_t *chgs)
  402 {
  403     if (chgs) {
  404         changeset_t *chg, *nxt;
  405         WALK_LIST_DELSAFE(chg, nxt, *chgs) {
  406             rem_node(&chg->n);
  407             changeset_free(chg);
  408         }
  409         init_list(chgs);
  410     }
  411 }
  412 
  413 void changeset_clear(changeset_t *ch)
  414 {
  415     if (ch == NULL) {
  416         return;
  417     }
  418 
  419     // Delete RRSets in lists, in case there are any left
  420     zone_contents_deep_free(ch->add);
  421     zone_contents_deep_free(ch->remove);
  422     ch->add = NULL;
  423     ch->remove = NULL;
  424 
  425     knot_rrset_free(ch->soa_from, NULL);
  426     knot_rrset_free(ch->soa_to, NULL);
  427     ch->soa_from = NULL;
  428     ch->soa_to = NULL;
  429 
  430     // Delete binary data
  431     free(ch->data);
  432 }
  433 
  434 changeset_t *changeset_clone(const changeset_t *ch)
  435 {
  436     if (ch == NULL) {
  437         return NULL;
  438     }
  439 
  440     changeset_t *res = changeset_new(ch->add->apex->owner);
  441     if (res == NULL) {
  442         return NULL;
  443     }
  444 
  445     res->soa_from = knot_rrset_copy(ch->soa_from, NULL);
  446     res->soa_to = knot_rrset_copy(ch->soa_to, NULL);
  447 
  448     int ret = KNOT_EOK;
  449     changeset_iter_t itt;
  450 
  451     changeset_iter_rem(&itt, ch);
  452     knot_rrset_t rr = changeset_iter_next(&itt);
  453     while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) {
  454         ret = changeset_add_removal(res, &rr, 0);
  455         rr = changeset_iter_next(&itt);
  456     }
  457     changeset_iter_clear(&itt);
  458 
  459     changeset_iter_add(&itt, ch);
  460     rr = changeset_iter_next(&itt);
  461     while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) {
  462         ret = changeset_add_addition(res, &rr, 0);
  463         rr = changeset_iter_next(&itt);
  464     }
  465     changeset_iter_clear(&itt);
  466 
  467     if ((ch->soa_from != NULL && res->soa_from == NULL) ||
  468         (ch->soa_to != NULL && res->soa_to == NULL) ||
  469         ret != KNOT_EOK) {
  470         changeset_free(res);
  471         return NULL;
  472     }
  473 
  474     return res;
  475 }
  476 
  477 void changeset_free(changeset_t *ch)
  478 {
  479     changeset_clear(ch);
  480     free(ch);
  481 }
  482 
  483 int changeset_iter_add(changeset_iter_t *itt, const changeset_t *ch)
  484 {
  485     return changeset_iter_init(itt, 2, ch->add->nodes, ch->add->nsec3_nodes);
  486 }
  487 
  488 int changeset_iter_rem(changeset_iter_t *itt, const changeset_t *ch)
  489 {
  490     return changeset_iter_init(itt, 2, ch->remove->nodes, ch->remove->nsec3_nodes);
  491 }
  492 
  493 int changeset_iter_all(changeset_iter_t *itt, const changeset_t *ch)
  494 {
  495     return changeset_iter_init(itt, 4, ch->add->nodes, ch->add->nsec3_nodes,
  496                                ch->remove->nodes, ch->remove->nsec3_nodes);
  497 }
  498 
  499 knot_rrset_t changeset_iter_next(changeset_iter_t *it)
  500 {
  501     assert(it);
  502 
  503     knot_rrset_t rr;
  504     while (it->node == NULL || it->node_pos >= it->node->rrset_count) {
  505         if (it->node != NULL) {
  506             zone_tree_it_next(&it->it);
  507         }
  508         while (zone_tree_it_finished(&it->it)) {
  509             zone_tree_it_free(&it->it);
  510             if (--it->n_trees > 0) {
  511                 for (size_t i = 0; i < it->n_trees; i++) {
  512                     it->trees[i] = it->trees[i + 1];
  513                 }
  514                 (void)zone_tree_it_begin(it->trees[0], &it->it);
  515             } else {
  516                 knot_rrset_init_empty(&rr);
  517                 return rr;
  518             }
  519         }
  520         it->node = zone_tree_it_val(&it->it);
  521         it->node_pos = 0;
  522     }
  523     rr = node_rrset_at(it->node, it->node_pos++);
  524     assert(!knot_rrset_empty(&rr));
  525     return rr;
  526 }
  527 
  528 void changeset_iter_clear(changeset_iter_t *it)
  529 {
  530     if (it) {
  531         zone_tree_it_free(&it->it);
  532         it->node = NULL;
  533         it->node_pos = 0;
  534     }
  535 }
  536 
  537 int changeset_walk(const changeset_t *changeset, changeset_walk_callback callback, void *ctx)
  538 {
  539     changeset_iter_t it;
  540     int ret = changeset_iter_rem(&it, changeset);
  541     if (ret != KNOT_EOK) {
  542         return ret;
  543     }
  544 
  545     knot_rrset_t rrset = changeset_iter_next(&it);
  546     while (!knot_rrset_empty(&rrset)) {
  547         ret = callback(&rrset, false, ctx);
  548         if (ret != KNOT_EOK) {
  549             changeset_iter_clear(&it);
  550             return ret;
  551         }
  552         rrset = changeset_iter_next(&it);
  553     }
  554     changeset_iter_clear(&it);
  555 
  556     if (changeset->soa_from != NULL) {
  557         ret = callback(changeset->soa_from, false, ctx);
  558         if (ret != KNOT_EOK) {
  559             return ret;
  560         }
  561     }
  562 
  563     ret = changeset_iter_add(&it, changeset);
  564     if (ret != KNOT_EOK) {
  565         return ret;
  566     }
  567 
  568     rrset = changeset_iter_next(&it);
  569     while (!knot_rrset_empty(&rrset)) {
  570         ret = callback(&rrset, true, ctx);
  571         if (ret != KNOT_EOK) {
  572             changeset_iter_clear(&it);
  573             return ret;
  574         }
  575         rrset = changeset_iter_next(&it);
  576     }
  577     changeset_iter_clear(&it);
  578 
  579     if (changeset->soa_to != NULL) {
  580         ret = callback(changeset->soa_to, true, ctx);
  581         if (ret != KNOT_EOK) {
  582             return ret;
  583         }
  584     }
  585 
  586     return KNOT_EOK;
  587 }
  588 
  589 void changeset_print(const changeset_t *changeset, FILE *outfile, bool color)
  590 {
  591     const char * RED = "\x1B[31m", * GRN = "\x1B[32m", * RESET = "\x1B[0m";
  592     size_t buflen = 1024;
  593     char *buff = malloc(buflen);
  594 
  595     if (changeset->soa_from != NULL || !zone_contents_is_empty(changeset->remove)) {
  596         fprintf(outfile, "%s;;Removed\n", color ? RED : "");
  597     }
  598     if (changeset->soa_from != NULL && buff != NULL) {
  599         (void)knot_rrset_txt_dump(changeset->soa_from, &buff, &buflen, &KNOT_DUMP_STYLE_DEFAULT);
  600         fprintf(outfile, "%s", buff);
  601     }
  602     (void)zone_dump_text(changeset->remove, outfile, false);
  603 
  604     if (changeset->soa_to != NULL || !zone_contents_is_empty(changeset->add)) {
  605         fprintf(outfile, "%s;;Added\n", color ? GRN : "");
  606     }
  607     if (changeset->soa_to != NULL && buff != NULL) {
  608         (void)knot_rrset_txt_dump(changeset->soa_to, &buff, &buflen, &KNOT_DUMP_STYLE_DEFAULT);
  609         fprintf(outfile, "%s", buff);
  610     }
  611     (void)zone_dump_text(changeset->add, outfile, false);
  612 
  613     if (color) {
  614         printf("%s", RESET);
  615     }
  616     free(buff);
  617 }