"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.9.2/src/knot/events/handlers/load.c" (12 Dec 2019, 10529 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 "load.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 
   19 #include "knot/common/log.h"
   20 #include "knot/conf/conf.h"
   21 #include "knot/dnssec/key-events.h"
   22 #include "knot/dnssec/zone-events.h"
   23 #include "knot/events/handlers.h"
   24 #include "knot/events/replan.h"
   25 #include "knot/zone/serial.h"
   26 #include "knot/zone/zone-diff.h"
   27 #include "knot/zone/zone-load.h"
   28 #include "knot/zone/zone.h"
   29 #include "knot/zone/zonefile.h"
   30 #include "knot/updates/acl.h"
   31 
   32 static bool dontcare_load_error(conf_t *conf, const zone_t *zone)
   33 {
   34     return (zone->contents == NULL && zone_load_can_bootstrap(conf, zone->name));
   35 }
   36 
   37 static bool allowed_xfr(conf_t *conf, const zone_t *zone)
   38 {
   39     conf_val_t acl = conf_zone_get(conf, C_ACL, zone->name);
   40     while (acl.code == KNOT_EOK) {
   41         conf_val_t action = conf_id_get(conf, C_ACL, C_ACTION, &acl);
   42         while (action.code == KNOT_EOK) {
   43             if (conf_opt(&action) == ACL_ACTION_TRANSFER) {
   44                 return true;
   45             }
   46             conf_val_next(&action);
   47         }
   48         conf_val_next(&acl);
   49     }
   50 
   51     return false;
   52 }
   53 
   54 int event_load(conf_t *conf, zone_t *zone)
   55 {
   56     zone_update_t up = { 0 };
   57     zone_contents_t *journal_conts = NULL, *zf_conts = NULL;
   58     bool old_contents_exist = (zone->contents != NULL);
   59 
   60     conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, zone->name);
   61     unsigned load_from = conf_opt(&val);
   62 
   63     val = conf_zone_get(conf, C_ZONEFILE_LOAD, zone->name);
   64     unsigned zf_from = conf_opt(&val);
   65 
   66     int ret = KNOT_EOK;
   67 
   68     // If configured, load journal contents.
   69     if (load_from == JOURNAL_CONTENT_ALL && !old_contents_exist && zf_from != ZONEFILE_LOAD_WHOLE) {
   70         ret = zone_load_from_journal(conf, zone, &journal_conts);
   71         if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
   72             goto cleanup;
   73         }
   74     }
   75 
   76     // If configured, attempt to load zonefile.
   77     if (zf_from != ZONEFILE_LOAD_NONE) {
   78         struct timespec mtime;
   79         char *filename = conf_zonefile(conf, zone->name);
   80         ret = zonefile_exists(filename, &mtime);
   81         bool zonefile_unchanged = (zone->zonefile.exists &&
   82                        zone->zonefile.mtime.tv_sec == mtime.tv_sec &&
   83                        zone->zonefile.mtime.tv_nsec == mtime.tv_nsec);
   84         free(filename);
   85         if (ret == KNOT_EOK) {
   86             ret = zone_load_contents(conf, zone->name, &zf_conts, false);
   87         }
   88         if (ret != KNOT_EOK) {
   89             zf_conts = NULL;
   90             if (dontcare_load_error(conf, zone)) {
   91                 log_zone_info(zone->name, "failed to parse zone file (%s)",
   92                           knot_strerror(ret));
   93             } else {
   94                 log_zone_error(zone->name, "failed to parse zone file (%s)",
   95                            knot_strerror(ret));
   96             }
   97             goto cleanup;
   98         }
   99 
  100         // Save zonefile information.
  101         zone->zonefile.serial = zone_contents_serial(zf_conts);
  102         zone->zonefile.exists = (zf_conts != NULL);
  103         zone->zonefile.mtime = mtime;
  104 
  105         // If configured and possible, fix the SOA serial of zonefile.
  106         zone_contents_t *relevant = (zone->contents != NULL ? zone->contents : journal_conts);
  107         if (zf_conts != NULL && zf_from == ZONEFILE_LOAD_DIFSE && relevant != NULL) {
  108             uint32_t serial = zone_contents_serial(relevant);
  109             conf_val_t policy = conf_zone_get(conf, C_SERIAL_POLICY, zone->name);
  110             uint32_t set = serial_next(serial, conf_opt(&policy));
  111             zone_contents_set_soa_serial(zf_conts, set);
  112             log_zone_info(zone->name, "zone file parsed, serial corrected %u -> %u",
  113                           zone->zonefile.serial, set);
  114             zone->zonefile.serial = set;
  115         } else {
  116             log_zone_info(zone->name, "zone file parsed, serial %u",
  117                           zone->zonefile.serial);
  118         }
  119 
  120         // If configured and appliable to zonefile, load journal changes.
  121         bool journal_load_configured1 = (load_from == JOURNAL_CONTENT_CHANGES);
  122         bool journal_load_configured2 = (load_from == JOURNAL_CONTENT_ALL);
  123 
  124         if ((journal_load_configured1 || journal_load_configured2) &&
  125             (!old_contents_exist || zonefile_unchanged)) {
  126             ret = zone_load_journal(conf, zone, zf_conts);
  127             if (ret != KNOT_EOK) {
  128                 zone_contents_deep_free(zf_conts);
  129                 zf_conts = NULL;
  130                 log_zone_warning(zone->name, "failed to load journal (%s)",
  131                                  knot_strerror(ret));
  132             }
  133         }
  134     }
  135 
  136     // If configured contents=all, but not present, store zonefile.
  137     if (load_from == JOURNAL_CONTENT_ALL &&
  138         journal_conts == NULL && zf_conts != NULL && !old_contents_exist) {
  139         ret = zone_in_journal_store(conf, zone, zf_conts);
  140         if (ret != KNOT_EOK) {
  141             log_zone_warning(zone->name, "failed to write zone-in-journal (%s)",
  142                              knot_strerror(ret));
  143         }
  144     }
  145 
  146     val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name);
  147     bool dnssec_enable = conf_bool(&val), zu_from_zf_conts = false;
  148     bool do_diff = (zf_from == ZONEFILE_LOAD_DIFF || zf_from == ZONEFILE_LOAD_DIFSE);
  149     bool ignore_dnssec = (do_diff && dnssec_enable);
  150 
  151     // Create zone_update structure according to current state.
  152     if (old_contents_exist) {
  153         if (zf_conts == NULL) {
  154             // nothing to be re-loaded
  155             ret = KNOT_EOK;
  156             goto cleanup;
  157         } else if (zf_from == ZONEFILE_LOAD_WHOLE) {
  158             // throw old zone contents and load new from ZF
  159             ret = zone_update_from_contents(&up, zone, zf_conts,
  160                                             (load_from == JOURNAL_CONTENT_NONE ?
  161                                              UPDATE_FULL : UPDATE_HYBRID));
  162             zu_from_zf_conts = true;
  163         } else {
  164             // compute ZF diff and if success, apply it
  165             ret = zone_update_from_differences(&up, zone, NULL, zf_conts, UPDATE_INCREMENTAL, ignore_dnssec);
  166         }
  167     } else {
  168         if (journal_conts != NULL && zf_from != ZONEFILE_LOAD_WHOLE) {
  169             if (zf_conts == NULL) {
  170                 // load zone-in-journal
  171                 ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_HYBRID);
  172             } else {
  173                 // load zone-in-journal, compute ZF diff and if success, apply it
  174                 ret = zone_update_from_differences(&up, zone, journal_conts, zf_conts,
  175                                                    UPDATE_HYBRID, ignore_dnssec);
  176                 if (ret == KNOT_ESEMCHECK || ret == KNOT_ERANGE) {
  177                     log_zone_warning(zone->name,
  178                                      "zone file changed with SOA serial %s, "
  179                                      "ignoring zone file and loading from journal",
  180                                      (ret == KNOT_ESEMCHECK ? "unupdated" : "decreased"));
  181                     ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_HYBRID);
  182                 }
  183             }
  184         } else {
  185             if (zf_conts == NULL) {
  186                 // nothing to be loaded
  187                 ret = KNOT_ENOENT;
  188             } else {
  189                 // load from ZF
  190                 ret = zone_update_from_contents(&up, zone, zf_conts,
  191                                                 (load_from == JOURNAL_CONTENT_NONE ?
  192                                                  UPDATE_FULL : UPDATE_HYBRID));
  193                 if (zf_from == ZONEFILE_LOAD_WHOLE) {
  194                     zu_from_zf_conts = true;
  195                 }
  196             }
  197         }
  198     }
  199     if (ret != KNOT_EOK) {
  200         switch (ret) {
  201         case KNOT_ENOENT:
  202             if (zone_load_can_bootstrap(conf, zone->name)) {
  203                 log_zone_info(zone->name, "zone will be bootstrapped");
  204             } else {
  205                 log_zone_info(zone->name, "zone not found");
  206             }
  207             break;
  208         case KNOT_ESEMCHECK:
  209             log_zone_warning(zone->name, "zone file changed without SOA serial update");
  210             break;
  211         case KNOT_ERANGE:
  212             log_zone_warning(zone->name, "zone file changed, but SOA serial decreased");
  213             break;
  214         }
  215         goto cleanup;
  216     }
  217 
  218     uint32_t middle_serial = zone_contents_serial(up.new_cont);
  219 
  220     if (do_diff && old_contents_exist && dnssec_enable &&
  221         zone->zonefile.serial != zone_contents_serial(zone->contents) &&
  222         !zone_journal_has_zij(zone)) {
  223         ret = zone_update_start_extra(&up);
  224         if (ret != KNOT_EOK) {
  225             goto cleanup;
  226         }
  227     }
  228 
  229     // The contents are already part of zone_update.
  230     zf_conts = NULL;
  231     journal_conts = NULL;
  232 
  233     // Sign zone using DNSSEC if configured.
  234     zone_sign_reschedule_t dnssec_refresh = { 0 };
  235     if (dnssec_enable) {
  236         ret = knot_dnssec_zone_sign(&up, 0, KEY_ROLL_ALLOW_ALL, &dnssec_refresh);
  237         if (ret != KNOT_EOK) {
  238             goto cleanup;
  239         }
  240         if (zu_from_zf_conts && (up.flags & UPDATE_HYBRID) && allowed_xfr(conf, zone)) {
  241             log_zone_warning(zone->name,
  242                              "with automatic DNSSEC signing and outgoing transfers enabled, "
  243                              "'zonefile-load: difference' should be set to avoid malformed "
  244                              "IXFR after manual zone file update");
  245         }
  246     }
  247 
  248     // If the change is only automatically incremented SOA serial, make it no change.
  249     if (zf_from == ZONEFILE_LOAD_DIFSE && (up.flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) &&
  250         changeset_differs_just_serial(&up.change)) {
  251         changeset_t *cpy = changeset_clone(&up.change);
  252         if (cpy == NULL) {
  253             ret = KNOT_ENOMEM;
  254             goto cleanup;
  255         }
  256         ret = zone_update_apply_changeset_reverse(&up, cpy);
  257         changeset_free(cpy);
  258         if (ret != KNOT_EOK) {
  259             goto cleanup;
  260         }
  261     }
  262 
  263     uint32_t old_serial = 0, new_serial = zone_contents_serial(up.new_cont);
  264     char old_serial_str[11] = "none", new_serial_str[15] = "";
  265     if (old_contents_exist) {
  266         old_serial = zone_contents_serial(zone->contents);
  267         (void)snprintf(old_serial_str, sizeof(old_serial_str), "%u", old_serial);
  268     }
  269     if (new_serial != middle_serial) {
  270         (void)snprintf(new_serial_str, sizeof(new_serial_str), " -> %u", new_serial);
  271     }
  272 
  273     // Commit zone_update back to zone (including journal update, rcu,...).
  274     ret = zone_update_commit(conf, &up);
  275     if (ret != KNOT_EOK) {
  276         goto cleanup;
  277     }
  278 
  279     log_zone_info(zone->name, "loaded, serial %s -> %u%s, %zu bytes",
  280                   old_serial_str, middle_serial, new_serial_str, zone->contents->size);
  281 
  282     // Schedule depedent events.
  283     const knot_rdataset_t *soa = zone_soa(zone);
  284     zone->timers.soa_expire = knot_soa_expire(soa->rdata);
  285 
  286     if (dnssec_enable) {
  287         event_dnssec_reschedule(conf, zone, &dnssec_refresh, false); // false since we handle NOTIFY below
  288     }
  289 
  290     replan_from_timers(conf, zone);
  291 
  292     if (!old_contents_exist || old_serial != new_serial) {
  293         zone_events_schedule_now(zone, ZONE_EVENT_NOTIFY);
  294     }
  295 
  296     return KNOT_EOK;
  297 
  298 cleanup:
  299     // Try to bootstrap the zone if local error.
  300     replan_from_timers(conf, zone);
  301 
  302     zone_update_clear(&up);
  303     zone_contents_deep_free(zf_conts);
  304     zone_contents_deep_free(journal_conts);
  305 
  306     return (dontcare_load_error(conf, zone) ? KNOT_EOK : ret);
  307 }