"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.9.2/src/knot/zone/zone.c" (12 Dec 2019, 13868 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 "zone.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 <stdlib.h>
   18 #include <string.h>
   19 #include <sys/stat.h>
   20 #include <urcu.h>
   21 
   22 #include "knot/common/log.h"
   23 #include "knot/conf/module.h"
   24 #include "knot/dnssec/kasp/kasp_db.h"
   25 #include "knot/journal/journal_read.h"
   26 #include "knot/journal/journal_write.h"
   27 #include "knot/nameserver/process_query.h"
   28 #include "knot/query/requestor.h"
   29 #include "knot/updates/zone-update.h"
   30 #include "knot/zone/contents.h"
   31 #include "knot/zone/serial.h"
   32 #include "knot/zone/zone.h"
   33 #include "knot/zone/zonefile.h"
   34 #include "libknot/libknot.h"
   35 #include "contrib/sockaddr.h"
   36 #include "contrib/mempattern.h"
   37 #include "contrib/ucw/lists.h"
   38 #include "contrib/ucw/mempool.h"
   39 
   40 #define JOURNAL_LOCK_MUTEX (&zone->journal_lock)
   41 #define JOURNAL_LOCK_RW pthread_mutex_lock(JOURNAL_LOCK_MUTEX);
   42 #define JOURNAL_UNLOCK_RW pthread_mutex_unlock(JOURNAL_LOCK_MUTEX);
   43 
   44 static void free_ddns_queue(zone_t *zone)
   45 {
   46     ptrnode_t *node, *nxt;
   47     WALK_LIST_DELSAFE(node, nxt, zone->ddns_queue) {
   48         knot_request_free(node->d, NULL);
   49     }
   50     ptrlist_free(&zone->ddns_queue, NULL);
   51 }
   52 
   53 /*!
   54  * \param allow_empty_zone useful when need to flush journal but zone is not yet loaded
   55  * ...in this case we actually don't have to do anything because the zonefile is current,
   56  * but we must mark the journal as flushed
   57  */
   58 static int flush_journal(conf_t *conf, zone_t *zone, bool allow_empty_zone, bool verbose)
   59 {
   60     /*! @note Function expects nobody will change zone contents meanwile. */
   61 
   62     assert(zone);
   63 
   64     int ret = KNOT_EOK;
   65     zone_journal_t j = zone_journal(zone);
   66 
   67     bool force = zone->flags & ZONE_FORCE_FLUSH;
   68     zone->flags &= ~ZONE_FORCE_FLUSH;
   69 
   70     conf_val_t val = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name);
   71     int64_t sync_timeout = conf_int(&val);
   72 
   73     if (zone_contents_is_empty(zone->contents)) {
   74         if (allow_empty_zone && journal_is_existing(j)) {
   75             ret = journal_set_flushed(j);
   76         } else {
   77             ret = KNOT_EINVAL;
   78         }
   79         goto flush_journal_replan;
   80     }
   81 
   82     /* Check for disabled zonefile synchronization. */
   83     if (sync_timeout < 0 && !force) {
   84         if (verbose) {
   85             log_zone_warning(zone->name, "zonefile synchronization disabled, "
   86                                          "use force command to override it");
   87         }
   88         return KNOT_EOK;
   89     }
   90 
   91     /* Check for updated zone. */
   92     zone_contents_t *contents = zone->contents;
   93     uint32_t serial_to = zone_contents_serial(contents);
   94     if (!force && zone->zonefile.exists && zone->zonefile.serial == serial_to &&
   95         !zone->zonefile.retransfer && !zone->zonefile.resigned) {
   96         ret = KNOT_EOK; /* No differences. */
   97         goto flush_journal_replan;
   98     }
   99 
  100     char *zonefile = conf_zonefile(conf, zone->name);
  101 
  102     /* Synchronize journal. */
  103     ret = zonefile_write(zonefile, contents);
  104     if (ret != KNOT_EOK) {
  105         log_zone_warning(zone->name, "failed to update zone file (%s)",
  106                          knot_strerror(ret));
  107         free(zonefile);
  108         goto flush_journal_replan;
  109     }
  110 
  111     if (zone->zonefile.exists) {
  112         log_zone_info(zone->name, "zone file updated, serial %u -> %u",
  113                       zone->zonefile.serial, serial_to);
  114     } else {
  115         log_zone_info(zone->name, "zone file updated, serial %u",
  116                       serial_to);
  117     }
  118 
  119     /* Update zone version. */
  120     struct stat st;
  121     if (stat(zonefile, &st) < 0) {
  122         log_zone_warning(zone->name, "failed to update zone file (%s)",
  123                          knot_strerror(knot_map_errno()));
  124         free(zonefile);
  125         ret = KNOT_EACCES;
  126         goto flush_journal_replan;
  127     }
  128 
  129     free(zonefile);
  130 
  131     /* Update zone file attributes. */
  132     zone->zonefile.exists = true;
  133     zone->zonefile.mtime = st.st_mtim;
  134     zone->zonefile.serial = serial_to;
  135     zone->zonefile.resigned = false;
  136     zone->zonefile.retransfer = false;
  137 
  138     /* Flush journal. */
  139     if (journal_is_existing(j)) {
  140         ret = journal_set_flushed(j);
  141         if (ret != KNOT_EOK) {
  142             goto flush_journal_replan;
  143         }
  144     }
  145 
  146 flush_journal_replan:
  147     /* Plan next journal flush after proper period. */
  148     zone->timers.last_flush = time(NULL);
  149     if (sync_timeout > 0) {
  150         time_t next_flush = zone->timers.last_flush + sync_timeout;
  151         zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, 0,
  152                                       ZONE_EVENT_FLUSH, next_flush);
  153     }
  154 
  155     return ret;
  156 }
  157 
  158 zone_t* zone_new(const knot_dname_t *name)
  159 {
  160     zone_t *zone = malloc(sizeof(zone_t));
  161     if (zone == NULL) {
  162         return NULL;
  163     }
  164     memset(zone, 0, sizeof(zone_t));
  165 
  166     zone->name = knot_dname_copy(name, NULL);
  167     if (zone->name == NULL) {
  168         free(zone);
  169         return NULL;
  170     }
  171 
  172     // DDNS
  173     pthread_mutex_init(&zone->ddns_lock, NULL);
  174     zone->ddns_queue_size = 0;
  175     init_list(&zone->ddns_queue);
  176 
  177     knot_sem_init(&zone->cow_lock, 1);
  178 
  179     // Preferred master lock
  180     pthread_mutex_init(&zone->preferred_lock, NULL);
  181 
  182     // Initialize events
  183     zone_events_init(zone);
  184 
  185     // Initialize query modules list.
  186     init_list(&zone->query_modules);
  187 
  188     return zone;
  189 }
  190 
  191 void zone_control_clear(zone_t *zone)
  192 {
  193     if (zone == NULL) {
  194         return;
  195     }
  196 
  197     zone_update_clear(zone->control_update);
  198     free(zone->control_update);
  199     zone->control_update = NULL;
  200 }
  201 
  202 void zone_free(zone_t **zone_ptr)
  203 {
  204     if (zone_ptr == NULL || *zone_ptr == NULL) {
  205         return;
  206     }
  207 
  208     zone_t *zone = *zone_ptr;
  209 
  210     zone_events_deinit(zone);
  211 
  212     knot_dname_free(zone->name, NULL);
  213 
  214     free_ddns_queue(zone);
  215     pthread_mutex_destroy(&zone->ddns_lock);
  216 
  217     knot_sem_destroy(&zone->cow_lock);
  218 
  219     /* Control update. */
  220     zone_control_clear(zone);
  221 
  222     /* Free preferred master. */
  223     pthread_mutex_destroy(&zone->preferred_lock);
  224     free(zone->preferred_master);
  225 
  226     /* Free zone contents. */
  227     zone_contents_deep_free(zone->contents);
  228 
  229     conf_deactivate_modules(&zone->query_modules, &zone->query_plan);
  230 
  231     free(zone);
  232     *zone_ptr = NULL;
  233 }
  234 
  235 int zone_change_store(conf_t *conf, zone_t *zone, changeset_t *change, changeset_t *extra)
  236 {
  237     if (conf == NULL || zone == NULL || change == NULL) {
  238         return KNOT_EINVAL;
  239     }
  240 
  241     int ret = journal_insert(zone_journal(zone), change, extra);
  242     if (ret == KNOT_EBUSY) {
  243         log_zone_notice(zone->name, "journal is full, flushing");
  244 
  245         /* Transaction rolled back, journal released, we may flush. */
  246         ret = flush_journal(conf, zone, true, false);
  247         if (ret == KNOT_EOK) {
  248             ret = journal_insert(zone_journal(zone), change, extra);
  249         }
  250     }
  251 
  252     return ret;
  253 }
  254 
  255 int zone_changes_clear(conf_t *conf, zone_t *zone)
  256 {
  257     if (conf == NULL || zone == NULL) {
  258         return KNOT_EINVAL;
  259     }
  260 
  261     return journal_scrape_with_md(zone_journal(zone));
  262 }
  263 
  264 int zone_in_journal_store(conf_t *conf, zone_t *zone, zone_contents_t *new_contents)
  265 {
  266     if (conf == NULL || zone == NULL || new_contents == NULL) {
  267         return KNOT_EINVAL;
  268     }
  269 
  270     int ret = journal_insert_zone(zone_journal(zone), new_contents);
  271     if (ret == KNOT_EOK) {
  272         log_zone_info(zone->name, "zone stored to journal, serial %u",
  273                       zone_contents_serial(new_contents));
  274     }
  275 
  276     return ret;
  277 }
  278 
  279 int zone_flush_journal(conf_t *conf, zone_t *zone, bool verbose)
  280 {
  281     if (conf == NULL || zone == NULL) {
  282         return KNOT_EINVAL;
  283     }
  284 
  285     return flush_journal(conf, zone, false, verbose);
  286 }
  287 
  288 bool zone_journal_has_zij(zone_t *zone)
  289 {
  290     bool exists = false, zij = false;
  291     (void)journal_info(zone_journal(zone), &exists, NULL, &zij, NULL, NULL, NULL, NULL, NULL);
  292     return exists && zij;
  293 }
  294 
  295 zone_contents_t *zone_switch_contents(zone_t *zone, zone_contents_t *new_contents)
  296 {
  297     if (zone == NULL) {
  298         return NULL;
  299     }
  300 
  301     zone_contents_t *old_contents;
  302     zone_contents_t **current_contents = &zone->contents;
  303     old_contents = rcu_xchg_pointer(current_contents, new_contents);
  304 
  305     return old_contents;
  306 }
  307 
  308 bool zone_is_slave(conf_t *conf, const zone_t *zone)
  309 {
  310     if (conf == NULL || zone == NULL) {
  311         return false;
  312     }
  313 
  314     conf_val_t val = conf_zone_get(conf, C_MASTER, zone->name);
  315     return conf_val_count(&val) > 0 ? true : false;
  316 }
  317 
  318 void zone_set_preferred_master(zone_t *zone, const struct sockaddr_storage *addr)
  319 {
  320     if (zone == NULL || addr == NULL) {
  321         return;
  322     }
  323 
  324     pthread_mutex_lock(&zone->preferred_lock);
  325     free(zone->preferred_master);
  326     zone->preferred_master = malloc(sizeof(struct sockaddr_storage));
  327     *zone->preferred_master = *addr;
  328     pthread_mutex_unlock(&zone->preferred_lock);
  329 }
  330 
  331 void zone_clear_preferred_master(zone_t *zone)
  332 {
  333     if (zone == NULL) {
  334         return;
  335     }
  336 
  337     pthread_mutex_lock(&zone->preferred_lock);
  338     free(zone->preferred_master);
  339     zone->preferred_master = NULL;
  340     pthread_mutex_unlock(&zone->preferred_lock);
  341 }
  342 
  343 const knot_rdataset_t *zone_soa(const zone_t *zone)
  344 {
  345     if (!zone || zone_contents_is_empty(zone->contents)) {
  346         return NULL;
  347     }
  348 
  349     return node_rdataset(zone->contents->apex, KNOT_RRTYPE_SOA);
  350 }
  351 
  352 bool zone_expired(const zone_t *zone)
  353 {
  354     if (!zone) {
  355         return false;
  356     }
  357 
  358     const zone_timers_t *timers = &zone->timers;
  359 
  360     return timers->last_refresh > 0 && timers->soa_expire > 0 &&
  361            timers->last_refresh + timers->soa_expire <= time(NULL);
  362 }
  363 
  364 /*!
  365  * \brief Get preferred zone master while checking its existence.
  366  */
  367 int static preferred_master(conf_t *conf, zone_t *zone, conf_remote_t *master)
  368 {
  369     pthread_mutex_lock(&zone->preferred_lock);
  370 
  371     if (zone->preferred_master == NULL) {
  372         pthread_mutex_unlock(&zone->preferred_lock);
  373         return KNOT_ENOENT;
  374     }
  375 
  376     conf_val_t masters = conf_zone_get(conf, C_MASTER, zone->name);
  377     while (masters.code == KNOT_EOK) {
  378         conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, &masters);
  379         size_t addr_count = conf_val_count(&addr);
  380 
  381         for (size_t i = 0; i < addr_count; i++) {
  382             conf_remote_t remote = conf_remote(conf, &masters, i);
  383             if (sockaddr_net_match(&remote.addr, zone->preferred_master, -1)) {
  384                 *master = remote;
  385                 pthread_mutex_unlock(&zone->preferred_lock);
  386                 return KNOT_EOK;
  387             }
  388         }
  389 
  390         conf_val_next(&masters);
  391     }
  392 
  393     pthread_mutex_unlock(&zone->preferred_lock);
  394 
  395     return KNOT_ENOENT;
  396 }
  397 
  398 static void log_try_addr_error(const zone_t *zone, const char *remote_name,
  399                                const struct sockaddr_storage *remote_addr,
  400                                const char *err_str, int ret)
  401 {
  402     char addr_str[SOCKADDR_STRLEN] = { 0 };
  403     sockaddr_tostr(addr_str, sizeof(addr_str), remote_addr);
  404     log_zone_debug(zone->name, "%s%s%s, address %s, failed (%s)", err_str,
  405                    (remote_name != NULL ? ", remote " : ""),
  406                    (remote_name != NULL ? remote_name : ""),
  407                    addr_str, knot_strerror(ret));
  408 }
  409 
  410 int zone_master_try(conf_t *conf, zone_t *zone, zone_master_cb callback,
  411                     void *callback_data, const char *err_str)
  412 {
  413     if (conf == NULL || zone == NULL || callback == NULL || err_str == NULL) {
  414         return KNOT_EINVAL;
  415     }
  416 
  417     /* Try the preferred server. */
  418 
  419     conf_remote_t preferred = { { AF_UNSPEC } };
  420     if (preferred_master(conf, zone, &preferred) == KNOT_EOK) {
  421         int ret = callback(conf, zone, &preferred, callback_data);
  422         if (ret == KNOT_EOK) {
  423             return ret;
  424         }
  425 
  426         log_try_addr_error(zone, NULL, &preferred.addr, err_str, ret);
  427     }
  428 
  429     /* Try all the other servers. */
  430 
  431     bool success = false;
  432 
  433     conf_val_t masters = conf_zone_get(conf, C_MASTER, zone->name);
  434     while (masters.code == KNOT_EOK) {
  435         conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, &masters);
  436         size_t addr_count = conf_val_count(&addr);
  437 
  438         for (size_t i = 0; i < addr_count; i++) {
  439             conf_remote_t master = conf_remote(conf, &masters, i);
  440             if (preferred.addr.ss_family != AF_UNSPEC &&
  441                 sockaddr_net_match(&master.addr, &preferred.addr, -1)) {
  442                 preferred.addr.ss_family = AF_UNSPEC;
  443                 continue;
  444             }
  445 
  446             int ret = callback(conf, zone, &master, callback_data);
  447             if (ret == KNOT_EOK) {
  448                 success = true;
  449                 break;
  450             }
  451 
  452             log_try_addr_error(zone, conf_str(&masters), &master.addr,
  453                                err_str, ret);
  454         }
  455 
  456         if (!success) {
  457             log_zone_warning(zone->name, "%s, remote %s not usable",
  458                              err_str, conf_str(&masters));
  459         }
  460 
  461         conf_val_next(&masters);
  462     }
  463 
  464     return success ? KNOT_EOK : KNOT_ENOMASTER;
  465 }
  466 
  467 int zone_dump_to_dir(conf_t *conf, zone_t *zone, const char *dir)
  468 {
  469     if (zone == NULL || dir == NULL) {
  470         return KNOT_EINVAL;
  471     }
  472 
  473     size_t dir_len = strlen(dir);
  474     if (dir_len == 0) {
  475         return KNOT_EINVAL;
  476     }
  477 
  478     char *zonefile = conf_zonefile(conf, zone->name);
  479     char *zonefile_basename = strrchr(zonefile, '/');
  480     if (zonefile_basename == NULL) {
  481         zonefile_basename = zonefile;
  482     }
  483 
  484     size_t target_length = strlen(zonefile_basename) + dir_len + 2;
  485     char target[target_length];
  486     (void)snprintf(target, target_length, "%s/%s", dir, zonefile_basename);
  487     if (strcmp(target, zonefile) == 0) {
  488         free(zonefile);
  489         return KNOT_EDENIED;
  490     }
  491     free(zonefile);
  492 
  493     return zonefile_write(target, zone->contents);
  494 }
  495 
  496 int zone_set_master_serial(zone_t *zone, uint32_t serial)
  497 {
  498     return kasp_db_store_serial(zone->kaspdb, zone->name, KASPDB_SERIAL_MASTER, serial);
  499 }
  500 
  501 int zone_get_master_serial(zone_t *zone, uint32_t *serial)
  502 {
  503     return kasp_db_load_serial(zone->kaspdb, zone->name, KASPDB_SERIAL_MASTER, serial);
  504 }
  505 
  506 int zone_set_lastsigned_serial(zone_t *zone, uint32_t serial)
  507 {
  508     return kasp_db_store_serial(zone->kaspdb, zone->name, KASPDB_SERIAL_LASTSIGNED, serial);
  509 }
  510 
  511 int zone_get_lastsigned_serial(zone_t *zone, uint32_t *serial)
  512 {
  513     return kasp_db_load_serial(zone->kaspdb, zone->name, KASPDB_SERIAL_LASTSIGNED, serial);
  514 }
  515 
  516 int slave_zone_serial(zone_t *zone, conf_t *conf, uint32_t *serial)
  517 {
  518     int ret = KNOT_EOK;
  519     *serial = zone_contents_serial(zone->contents);
  520 
  521     conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name);
  522     if (conf_bool(&val)) {
  523         ret = zone_get_master_serial(zone, serial);
  524     }
  525 
  526     return ret;
  527 }