"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.9.2/src/knot/nameserver/ixfr.c" (12 Dec 2019, 9374 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 "ixfr.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 <urcu.h>
   18 
   19 #include "contrib/mempattern.h"
   20 #include "contrib/sockaddr.h"
   21 #include "knot/journal/journal_metadata.h"
   22 #include "knot/nameserver/axfr.h"
   23 #include "knot/nameserver/internet.h"
   24 #include "knot/nameserver/ixfr.h"
   25 #include "knot/nameserver/log.h"
   26 #include "knot/nameserver/xfr.h"
   27 #include "knot/zone/serial.h"
   28 #include "libknot/libknot.h"
   29 
   30 #define ZONE_NAME(qdata) knot_pkt_qname((qdata)->query)
   31 #define REMOTE(qdata) (struct sockaddr *)(qdata)->params->remote
   32 
   33 #define IXFROUT_LOG(priority, qdata, fmt...) \
   34     ns_log(priority, ZONE_NAME(qdata), LOG_OPERATION_IXFR, \
   35            LOG_DIRECTION_OUT, REMOTE(qdata), fmt)
   36 
   37 /*! \brief Helper macro for putting RRs into packet. */
   38 #define IXFR_SAFE_PUT(pkt, rr) \
   39     int ret = knot_pkt_put((pkt), 0, (rr), KNOT_PF_NOTRUNC | KNOT_PF_ORIGTTL); \
   40     if (ret != KNOT_EOK) { \
   41         return ret; \
   42     }
   43 
   44 /*! \brief Puts current RR into packet, stores state for retries. */
   45 static int ixfr_put_chg_part(knot_pkt_t *pkt, struct ixfr_proc *ixfr,
   46                              journal_read_t *read)
   47 {
   48     assert(pkt);
   49     assert(ixfr);
   50     assert(read);
   51 
   52     if (!knot_rrset_empty(&ixfr->cur_rr)) {
   53         IXFR_SAFE_PUT(pkt, &ixfr->cur_rr);
   54         journal_read_clear_rrset(&ixfr->cur_rr);
   55     }
   56 
   57     while (journal_read_rrset(read, &ixfr->cur_rr, true)) {
   58         if (ixfr->cur_rr.type == KNOT_RRTYPE_SOA &&
   59             !ixfr->in_remove_section &&
   60             knot_soa_serial(ixfr->cur_rr.rrs.rdata) == ixfr->soa_to) {
   61             break;
   62         }
   63         IXFR_SAFE_PUT(pkt, &ixfr->cur_rr);
   64         if (ixfr->cur_rr.type == KNOT_RRTYPE_SOA) {
   65             ixfr->in_remove_section = !ixfr->in_remove_section;
   66         }
   67         knot_rrset_clear(&ixfr->cur_rr, NULL);
   68     }
   69 
   70     return journal_read_get_error(read, KNOT_EOK);
   71 }
   72 
   73 /*!
   74  * \brief Process the changes from journal.
   75  * \note Keep in mind that this function must be able to resume processing,
   76  *       for example if it fills a packet and returns ESPACE, it is called again
   77  *       with next empty answer and it must resume the processing exactly where
   78  *       it's left off.
   79  */
   80 static int ixfr_process_journal(knot_pkt_t *pkt, const void *item,
   81                                 struct xfr_proc *xfer)
   82 {
   83     int ret = KNOT_EOK;
   84     struct ixfr_proc *ixfr = (struct ixfr_proc *)xfer;
   85     journal_read_t *read = (journal_read_t *)item;
   86 
   87     ret = ixfr_put_chg_part(pkt, ixfr, read);
   88 
   89     return ret;
   90 }
   91 
   92 #undef IXFR_SAFE_PUT
   93 
   94 static int ixfr_load_chsets(journal_read_t **journal_read, zone_t *zone,
   95                             const zone_contents_t *contents, const knot_rrset_t *their_soa)
   96 {
   97     assert(journal_read);
   98     assert(zone);
   99 
  100     /* Compare serials. */
  101     uint32_t serial_to = zone_contents_serial(contents), j_serial_to;
  102     uint32_t serial_from = knot_soa_serial(their_soa->rrs.rdata);
  103     if (serial_compare(serial_to, serial_from) & SERIAL_MASK_LEQ) { /* We have older/same age zone. */
  104         return KNOT_EUPTODATE;
  105     }
  106 
  107     zone_journal_t j = zone_journal(zone);
  108     bool j_exists = false;
  109     int ret = journal_info(j, &j_exists, NULL, NULL, &j_serial_to, NULL, NULL, NULL, NULL);
  110     if (ret != KNOT_EOK) {
  111         return ret;
  112     } else if (!j_exists) {
  113         return KNOT_ENOENT;
  114     }
  115 
  116     // please note that the journal serial_to might differ from zone SOA serial
  117     // it is beacuse RCU lock is made at different moment than LMDB txn begin
  118     return journal_read_begin(zone_journal(zone), false, serial_from, journal_read);
  119 }
  120 
  121 static int ixfr_query_check(knotd_qdata_t *qdata)
  122 {
  123     NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);
  124     NS_NEED_AUTH(qdata, ACL_ACTION_TRANSFER);
  125     NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL);
  126 
  127     /* Need SOA authority record. */
  128     const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
  129     const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0);
  130     if (authority->count < 1 || their_soa->type != KNOT_RRTYPE_SOA) {
  131         qdata->rcode = KNOT_RCODE_FORMERR;
  132         return KNOT_STATE_FAIL;
  133     }
  134     /* SOA needs to match QNAME. */
  135     NS_NEED_QNAME(qdata, their_soa->owner, KNOT_RCODE_FORMERR);
  136 
  137     return KNOT_STATE_DONE;
  138 }
  139 
  140 static void ixfr_answer_cleanup(knotd_qdata_t *qdata)
  141 {
  142     struct ixfr_proc *ixfr = (struct ixfr_proc *)qdata->extra->ext;
  143     knot_mm_t *mm = qdata->mm;
  144 
  145     knot_rrset_clear(&ixfr->cur_rr, NULL);
  146     ptrlist_free(&ixfr->proc.nodes, mm);
  147     journal_read_end(ixfr->journal_ctx);
  148     mm_free(mm, qdata->extra->ext);
  149 
  150     /* Allow zone changes (finished). */
  151     rcu_read_unlock();
  152 }
  153 
  154 static int ixfr_answer_init(knotd_qdata_t *qdata, uint32_t *serial_from)
  155 {
  156     assert(qdata);
  157 
  158     if (ixfr_query_check(qdata) == KNOT_STATE_FAIL) {
  159         if (qdata->rcode == KNOT_RCODE_FORMERR) {
  160             return KNOT_EMALF;
  161         } else {
  162             return KNOT_EDENIED;
  163         }
  164     }
  165 
  166     const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
  167     const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0);
  168     *serial_from = knot_soa_serial(their_soa->rrs.rdata);
  169 
  170     knot_mm_t *mm = qdata->mm;
  171     struct ixfr_proc *xfer = mm_alloc(mm, sizeof(struct ixfr_proc));
  172     if (xfer == NULL) {
  173         return KNOT_ENOMEM;
  174     }
  175     memset(xfer, 0, sizeof(struct ixfr_proc));
  176 
  177     int ret = ixfr_load_chsets(&xfer->journal_ctx, (zone_t *)qdata->extra->zone,
  178                                qdata->extra->contents, their_soa);
  179     if (ret != KNOT_EOK) {
  180         mm_free(mm, xfer);
  181         return ret;
  182     }
  183 
  184     xfr_stats_begin(&xfer->proc.stats);
  185     xfer->state = IXFR_SOA_DEL;
  186     init_list(&xfer->proc.nodes);
  187     knot_rrset_init_empty(&xfer->cur_rr);
  188     xfer->qdata = qdata;
  189 
  190     ptrlist_add(&xfer->proc.nodes, xfer->journal_ctx, mm);
  191 
  192     xfer->soa_from = knot_soa_serial(their_soa->rrs.rdata);
  193     xfer->soa_to = zone_contents_serial(qdata->extra->contents);
  194 
  195     qdata->extra->ext = xfer;
  196     qdata->extra->ext_cleanup = &ixfr_answer_cleanup;
  197 
  198     /* No zone changes during multipacket answer (unlocked in ixfr_answer_cleanup) */
  199     rcu_read_lock();
  200 
  201     return KNOT_EOK;
  202 }
  203 
  204 static int ixfr_answer_soa(knot_pkt_t *pkt, knotd_qdata_t *qdata)
  205 {
  206     assert(pkt);
  207     assert(qdata);
  208 
  209     /* Check query. */
  210     int state = ixfr_query_check(qdata);
  211     if (state == KNOT_STATE_FAIL) {
  212         return state; /* Malformed query. */
  213     }
  214 
  215     /* Reserve space for TSIG. */
  216     int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key));
  217     if (ret != KNOT_EOK) {
  218         return KNOT_STATE_FAIL;
  219     }
  220 
  221     /* Guaranteed to have zone contents. */
  222     const zone_node_t *apex = qdata->extra->contents->apex;
  223     knot_rrset_t soa_rr = node_rrset(apex, KNOT_RRTYPE_SOA);
  224     if (knot_rrset_empty(&soa_rr)) {
  225         return KNOT_STATE_FAIL;
  226     }
  227     ret = knot_pkt_put(pkt, 0, &soa_rr, 0);
  228     if (ret != KNOT_EOK) {
  229         qdata->rcode = KNOT_RCODE_SERVFAIL;
  230         return KNOT_STATE_FAIL;
  231     }
  232 
  233     return KNOT_STATE_DONE;
  234 }
  235 
  236 int ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
  237 {
  238     if (pkt == NULL || qdata == NULL) {
  239         return KNOT_STATE_FAIL;
  240     }
  241 
  242     /* If IXFR is disabled, respond with SOA. */
  243     if (qdata->params->flags & KNOTD_QUERY_FLAG_NO_IXFR) {
  244         return ixfr_answer_soa(pkt, qdata);
  245     }
  246 
  247     /* Initialize on first call. */
  248     struct ixfr_proc *ixfr = qdata->extra->ext;
  249     if (ixfr == NULL) {
  250         uint32_t soa_from = 0;
  251         int ret = ixfr_answer_init(qdata, &soa_from);
  252         ixfr = qdata->extra->ext;
  253         switch (ret) {
  254         case KNOT_EOK:       /* OK */
  255             IXFROUT_LOG(LOG_INFO, qdata, "started, serial %u -> %u",
  256                     ixfr->soa_from, ixfr->soa_to);
  257             break;
  258         case KNOT_EUPTODATE: /* Our zone is same age/older, send SOA. */
  259             IXFROUT_LOG(LOG_INFO, qdata, "zone is up-to-date, serial %u", soa_from);
  260             return ixfr_answer_soa(pkt, qdata);
  261         case KNOT_ERANGE:    /* No history -> AXFR. */
  262         case KNOT_ENOENT:
  263             IXFROUT_LOG(LOG_INFO, qdata, "incomplete history, serial %u, fallback to AXFR", soa_from);
  264             qdata->type = KNOTD_QUERY_TYPE_AXFR; /* Solve as AXFR. */
  265             return axfr_process_query(pkt, qdata);
  266         case KNOT_EDENIED:  /* Not authorized, already logged. */
  267             return KNOT_STATE_FAIL;
  268         case KNOT_EMALF:    /* Malformed query. */
  269             IXFROUT_LOG(LOG_DEBUG, qdata, "malformed query");
  270             return KNOT_STATE_FAIL;
  271         default:             /* Server errors. */
  272             IXFROUT_LOG(LOG_ERR, qdata, "failed to start (%s)",
  273                         knot_strerror(ret));
  274             return KNOT_STATE_FAIL;
  275         }
  276     }
  277 
  278     /* Reserve space for TSIG. */
  279     int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key));
  280     if (ret != KNOT_EOK) {
  281         return KNOT_STATE_FAIL;
  282     }
  283 
  284     /* Answer current packet (or continue). */
  285     ret = xfr_process_list(pkt, &ixfr_process_journal, qdata);
  286     switch (ret) {
  287     case KNOT_ESPACE: /* Couldn't write more, send packet and continue. */
  288         return KNOT_STATE_PRODUCE; /* Check for more. */
  289     case KNOT_EOK:    /* Last response. */
  290         xfr_stats_end(&ixfr->proc.stats);
  291         xfr_log_finished(ZONE_NAME(qdata), LOG_OPERATION_IXFR, LOG_DIRECTION_OUT,
  292                          REMOTE(qdata), &ixfr->proc.stats);
  293         return KNOT_STATE_DONE;
  294     default:          /* Generic error. */
  295         IXFROUT_LOG(LOG_ERR, qdata, "failed (%s)", knot_strerror(ret));
  296         return KNOT_STATE_FAIL;
  297     }
  298 }