"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.9.2/src/knot/dnssec/ds_query.c" (12 Dec 2019, 6380 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 "ds_query.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 "contrib/macros.h"
   20 #include "knot/common/log.h"
   21 #include "knot/conf/conf.h"
   22 #include "knot/dnssec/ds_query.h"
   23 #include "knot/dnssec/key-events.h"
   24 #include "knot/query/layer.h"
   25 #include "knot/query/query.h"
   26 #include "knot/query/requestor.h"
   27 
   28 static bool match_key_ds(zone_key_t *key, knot_rdata_t *ds)
   29 {
   30     assert(key);
   31     assert(ds);
   32 
   33     dnssec_binary_t ds_rdata = {
   34         .size = ds->len,
   35         .data = ds->data,
   36     };
   37 
   38     dnssec_binary_t cds_rdata = { 0 };
   39 
   40     int ret = zone_key_calculate_ds(key, &cds_rdata);
   41     if (ret != KNOT_EOK) {
   42         return false;
   43     }
   44 
   45     return (dnssec_binary_cmp(&cds_rdata, &ds_rdata) == 0);
   46 }
   47 
   48 struct ds_query_data {
   49     const knot_dname_t *zone_name;
   50     const struct sockaddr *remote;
   51 
   52     zone_key_t *key;
   53 
   54     uint16_t edns_max_payload;
   55 
   56     bool ds_ok;
   57     bool result_logged;
   58 
   59     uint32_t ttl;
   60 };
   61 
   62 static int ds_query_begin(knot_layer_t *layer, void *params)
   63 {
   64     layer->data = params;
   65 
   66     return KNOT_STATE_PRODUCE;
   67 }
   68 
   69 static int ds_query_produce(knot_layer_t *layer, knot_pkt_t *pkt)
   70 {
   71     struct ds_query_data *data = layer->data;
   72     struct query_edns_data edns = { .max_payload = data->edns_max_payload, .do_flag = true, };
   73 
   74     query_init_pkt(pkt);
   75 
   76     int r = knot_pkt_put_question(pkt, data->zone_name, KNOT_CLASS_IN, KNOT_RRTYPE_DS);
   77     if (r != KNOT_EOK) {
   78         return KNOT_STATE_FAIL;
   79     }
   80 
   81     r = query_put_edns(pkt, &edns);
   82     if (r != KNOT_EOK) {
   83         return KNOT_STATE_FAIL;
   84     }
   85 
   86     knot_wire_set_rd(pkt->wire);
   87 
   88     return KNOT_STATE_CONSUME;
   89 }
   90 
   91 static int ds_query_consume(knot_layer_t *layer, knot_pkt_t *pkt)
   92 {
   93     struct ds_query_data *data = layer->data;
   94     data->result_logged = true;
   95 
   96     uint16_t rcode = knot_pkt_ext_rcode(pkt);
   97     if (rcode != KNOT_RCODE_NOERROR) {
   98         ns_log((rcode == KNOT_RCODE_NXDOMAIN ? LOG_NOTICE : LOG_WARNING),
   99                data->zone_name, LOG_OPERATION_DS_CHECK,
  100                LOG_DIRECTION_OUT, data->remote, "failed (%s)", knot_pkt_ext_rcode_name(pkt));
  101         return KNOT_STATE_FAIL;
  102     }
  103 
  104     const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
  105 
  106     bool match = false;
  107 
  108     for (size_t j = 0; j < answer->count; j++) {
  109         const knot_rrset_t *rr = knot_pkt_rr(answer, j);
  110         switch ((rr && rr->rrs.count > 0) ? rr->type : 0) {
  111         case KNOT_RRTYPE_DS:
  112             if (match_key_ds(data->key, rr->rrs.rdata)) {
  113                 match = true;
  114                 if (data->ttl == 0) { // fallback: if there is no RRSIG
  115                     data->ttl = rr->ttl;
  116                 }
  117             }
  118             break;
  119         case KNOT_RRTYPE_RRSIG:
  120             data->ttl = knot_rrsig_original_ttl(rr->rrs.rdata);
  121             break;
  122         default:
  123             break;
  124         }
  125     }
  126 
  127     ns_log(LOG_INFO, data->zone_name, LOG_OPERATION_DS_CHECK,
  128            LOG_DIRECTION_OUT, data->remote, "KSK submission check: %s",
  129            (match ? "positive" : "negative"));
  130 
  131     if (match) {
  132         data->ds_ok = true;
  133     }
  134     return KNOT_STATE_DONE;
  135 }
  136 
  137 static const knot_layer_api_t ds_query_api = {
  138     .begin = ds_query_begin,
  139     .produce = ds_query_produce,
  140     .consume = ds_query_consume,
  141     .reset = NULL,
  142     .finish = NULL,
  143 };
  144 
  145 static int try_ds(const knot_dname_t *zone_name, const conf_remote_t *parent, zone_key_t *key,
  146                   size_t timeout, uint32_t *ds_ttl)
  147 {
  148     // TODO: Abstract interface to issue DNS queries. This is almost copy-pasted.
  149 
  150     assert(zone_name);
  151     assert(parent);
  152 
  153     struct ds_query_data data = {
  154         .zone_name = zone_name,
  155         .remote = (struct sockaddr *)&parent->addr,
  156         .key = key,
  157         .ds_ok = false,
  158         .result_logged = false,
  159         .ttl = 0,
  160     };
  161 
  162     knot_requestor_t requestor;
  163     knot_requestor_init(&requestor, &ds_query_api, &data, NULL);
  164 
  165     knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
  166     if (!pkt) {
  167         knot_requestor_clear(&requestor);
  168         return KNOT_ENOMEM;
  169     }
  170 
  171     const struct sockaddr_storage *dst = &parent->addr;
  172     const struct sockaddr_storage *src = &parent->via;
  173     knot_request_t *req = knot_request_make(NULL, dst, src, pkt, &parent->key, 0);
  174     if (!req) {
  175         knot_request_free(req, NULL);
  176         knot_requestor_clear(&requestor);
  177         return KNOT_ENOMEM;
  178     }
  179 
  180     data.edns_max_payload = dst->ss_family == AF_INET6 ?
  181                             conf()->cache.srv_udp_max_payload_ipv6 :
  182                             conf()->cache.srv_udp_max_payload_ipv4;
  183 
  184     int ret = knot_requestor_exec(&requestor, req, timeout);
  185     knot_request_free(req, NULL);
  186     knot_requestor_clear(&requestor);
  187 
  188     // alternative: we could put answer back through ctx instead of errcode
  189     if (ret == KNOT_EOK && !data.ds_ok) {
  190         ret = KNOT_ENORECORD;
  191     }
  192 
  193     if (ret != KNOT_EOK && !data.result_logged) {
  194         ns_log(LOG_WARNING, zone_name, LOG_OPERATION_DS_CHECK,
  195                LOG_DIRECTION_OUT, data.remote, "failed (%s)", knot_strerror(ret));
  196     }
  197 
  198     *ds_ttl = data.ttl;
  199 
  200     return ret;
  201 }
  202 
  203 static bool parents_have_ds(kdnssec_ctx_t *kctx, zone_key_t *key, size_t timeout,
  204                             uint32_t *max_ds_ttl)
  205 {
  206     bool success = false;
  207     dynarray_foreach(parent, knot_kasp_parent_t, i, kctx->policy->parents) {
  208         success = false;
  209         for (size_t j = 0; j < i->addrs; j++) {
  210             uint32_t ds_ttl = 0;
  211             int ret = try_ds(kctx->zone->dname, &i->addr[j], key, timeout, &ds_ttl);
  212             if (ret == KNOT_EOK) {
  213                 *max_ds_ttl = MAX(*max_ds_ttl, ds_ttl);
  214                 success = true;
  215                 break;
  216             } else if (ret == KNOT_ENORECORD) {
  217                 // parent was queried successfully, answer was negative
  218                 break;
  219             }
  220         }
  221         // Each parent must succeed.
  222         if (!success) {
  223             return false;
  224         }
  225     }
  226     return success;
  227 }
  228 
  229 int knot_parent_ds_query(kdnssec_ctx_t *kctx, zone_keyset_t *keyset, size_t timeout)
  230 {
  231     uint32_t max_ds_ttl = 0;
  232 
  233     for (size_t i = 0; i < keyset->count; i++) {
  234         zone_key_t *key = &keyset->keys[i];
  235         if (key->is_ready) {
  236             assert(key->is_ksk);
  237             if (parents_have_ds(kctx, key, timeout, &max_ds_ttl)) {
  238                 return knot_dnssec_ksk_sbm_confirm(kctx, max_ds_ttl);
  239             } else {
  240                 return KNOT_ENOENT;
  241             }
  242         }
  243     }
  244     return KNOT_ENOENT;
  245 }