"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.9.2/src/knot/events/handlers/ds_push.c" (12 Dec 2019, 7224 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_push.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/query/query.h"
   22 #include "knot/query/requestor.h"
   23 #include "knot/zone/zone.h"
   24 #include "libknot/errcode.h"
   25 
   26 struct ds_push_data {
   27     const knot_dname_t *zone;
   28     knot_dname_t *parent_soa;
   29     knot_rrset_t del_old_ds;
   30     knot_rrset_t new_ds;
   31     const struct sockaddr *remote;
   32     struct query_edns_data edns;
   33 };
   34 
   35 #define DS_PUSH_RETRY   600
   36 
   37 #define DS_PUSH_LOG(priority, zone, remote, fmt, ...) \
   38     ns_log(priority, zone, LOG_OPERATION_DS_PUSH, LOG_DIRECTION_OUT, remote, \
   39            fmt, ## __VA_ARGS__)
   40 
   41 static const knot_rdata_t remove_cds = { 5, { 0, 0, 0, 0, 0 } };
   42 
   43 static int ds_push_begin(knot_layer_t *layer, void *params)
   44 {
   45     layer->data = params;
   46 
   47     return KNOT_STATE_PRODUCE;
   48 }
   49 
   50 static int parent_soa_produce(struct ds_push_data *data, knot_pkt_t *pkt)
   51 {
   52     const knot_dname_t *query_name = knot_wire_next_label(data->zone, NULL);
   53 
   54     int ret = knot_pkt_put_question(pkt, query_name, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
   55     if (ret != KNOT_EOK) {
   56         return KNOT_STATE_FAIL;
   57     }
   58 
   59     ret = query_put_edns(pkt, &data->edns);
   60     if (ret != KNOT_EOK) {
   61         return KNOT_STATE_FAIL;
   62     }
   63 
   64     return KNOT_STATE_CONSUME;
   65 }
   66 
   67 static int ds_push_produce(knot_layer_t *layer, knot_pkt_t *pkt)
   68 {
   69     struct ds_push_data *data = layer->data;
   70 
   71     query_init_pkt(pkt);
   72 
   73     if (data->parent_soa == NULL) {
   74         return parent_soa_produce(data, pkt);
   75     }
   76 
   77     knot_wire_set_opcode(pkt->wire, KNOT_OPCODE_UPDATE);
   78     int ret = knot_pkt_put_question(pkt, data->parent_soa, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
   79     if (ret != KNOT_EOK) {
   80         return KNOT_STATE_FAIL;
   81     }
   82 
   83     knot_pkt_begin(pkt, KNOT_AUTHORITY);
   84 
   85     assert(data->del_old_ds.type == KNOT_RRTYPE_DS);
   86     ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, &data->del_old_ds, 0);
   87     if (ret != KNOT_EOK) {
   88         return KNOT_STATE_FAIL;
   89     }
   90 
   91     assert(data->new_ds.type == KNOT_RRTYPE_DS);
   92     assert(!knot_rrset_empty(&data->new_ds));
   93     if (knot_rdata_cmp(data->new_ds.rrs.rdata, &remove_cds) != 0) {
   94         // Otherwise only remove DS - it was a special "remove CDS".
   95         ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, &data->new_ds, 0);
   96         if (ret != KNOT_EOK) {
   97             return KNOT_STATE_FAIL;
   98         }
   99     }
  100 
  101     query_put_edns(pkt, &data->edns);
  102 
  103     return KNOT_STATE_CONSUME;
  104 }
  105 
  106 static const knot_rrset_t *sect_soa(const knot_pkt_t *pkt, knot_section_t sect)
  107 {
  108     const knot_pktsection_t *s = knot_pkt_section(pkt, sect);
  109     const knot_rrset_t *rr = s->count > 0 ? knot_pkt_rr(s, 0) : NULL;
  110     if (rr == NULL || rr->type != KNOT_RRTYPE_SOA || rr->rrs.count != 1) {
  111         return NULL;
  112     }
  113     return rr;
  114 }
  115 
  116 static int ds_push_consume(knot_layer_t *layer, knot_pkt_t *pkt)
  117 {
  118     struct ds_push_data *data = layer->data;
  119 
  120     if (data->parent_soa != NULL) {
  121         // DS push has already been sent, just finish the action.
  122         return KNOT_STATE_DONE;
  123     }
  124 
  125     const knot_rrset_t *parent_soa = sect_soa(pkt, KNOT_ANSWER);
  126     if (parent_soa == NULL) {
  127         parent_soa = sect_soa(pkt, KNOT_AUTHORITY);
  128     }
  129     if (parent_soa == NULL) {
  130         DS_PUSH_LOG(LOG_WARNING, data->zone, data->remote,
  131                     "malformed message");
  132         return KNOT_STATE_FAIL;
  133     }
  134 
  135     data->parent_soa = knot_dname_copy(parent_soa->owner, NULL);
  136 
  137     return KNOT_STATE_RESET;
  138 }
  139 
  140 static int ds_push_reset(knot_layer_t *layer)
  141 {
  142     (void)layer;
  143     return KNOT_STATE_PRODUCE;
  144 }
  145 
  146 static int ds_push_finish(knot_layer_t *layer)
  147 {
  148     struct ds_push_data *data = layer->data;
  149     free(data->parent_soa);
  150     data->parent_soa = NULL;
  151     return layer->state;
  152 }
  153 
  154 static const knot_layer_api_t DS_PUSH_API = {
  155     .begin = ds_push_begin,
  156     .produce = ds_push_produce,
  157     .reset = ds_push_reset,
  158     .consume = ds_push_consume,
  159     .finish = ds_push_finish,
  160 };
  161 
  162 static int send_ds_push(conf_t *conf, zone_t *zone,
  163                         const conf_remote_t *parent, int timeout)
  164 {
  165     knot_rrset_t zone_cds = node_rrset(zone->contents->apex, KNOT_RRTYPE_CDS);
  166     if (knot_rrset_empty(&zone_cds)) {
  167         return KNOT_EOK; // No CDS, do nothing.
  168     }
  169     zone_cds.type = KNOT_RRTYPE_DS;
  170 
  171     struct ds_push_data data = {
  172         .zone = zone->name,
  173         .new_ds = zone_cds,
  174         .remote = (struct sockaddr *)&parent->addr,
  175     };
  176 
  177     knot_rrset_init(&data.del_old_ds, zone->name, KNOT_RRTYPE_DS, KNOT_CLASS_ANY, 0);
  178     int ret = knot_rrset_add_rdata(&data.del_old_ds, NULL, 0, NULL);
  179     if (ret != KNOT_EOK) {
  180         return ret;
  181     }
  182     query_edns_data_init(&data.edns, conf, zone->name, parent->addr.ss_family);
  183 
  184     knot_requestor_t requestor;
  185     knot_requestor_init(&requestor, &DS_PUSH_API, &data, NULL);
  186 
  187     knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
  188     if (pkt == NULL) {
  189         knot_rdataset_clear(&data.del_old_ds.rrs, NULL);
  190         knot_requestor_clear(&requestor);
  191         return KNOT_ENOMEM;
  192     }
  193 
  194     const struct sockaddr_storage *dst = &parent->addr;
  195     const struct sockaddr_storage *src = &parent->via;
  196     knot_request_t *req = knot_request_make(NULL, dst, src, pkt, &parent->key, 0);
  197     if (req == NULL) {
  198         knot_rdataset_clear(&data.del_old_ds.rrs, NULL);
  199         knot_request_free(req, NULL);
  200         knot_requestor_clear(&requestor);
  201         return KNOT_ENOMEM;
  202     }
  203 
  204     ret = knot_requestor_exec(&requestor, req, timeout);
  205 
  206     if (ret == KNOT_EOK && knot_pkt_ext_rcode(req->resp) == 0) {
  207         DS_PUSH_LOG(LOG_INFO, zone->name, dst, "success");
  208     } else if (knot_pkt_ext_rcode(req->resp) == 0) {
  209         DS_PUSH_LOG(LOG_WARNING, zone->name, dst,
  210                     "failed (%s)", knot_strerror(ret));
  211     } else {
  212         DS_PUSH_LOG(LOG_WARNING, zone->name, dst,
  213                     "server responded with error '%s'",
  214                     knot_pkt_ext_rcode_name(req->resp));
  215     }
  216 
  217     knot_rdataset_clear(&data.del_old_ds.rrs, NULL);
  218     knot_request_free(req, NULL);
  219     knot_requestor_clear(&requestor);
  220 
  221     return ret;
  222 }
  223 
  224 int event_ds_push(conf_t *conf, zone_t *zone)
  225 {
  226     assert(zone);
  227 
  228     if (zone_contents_is_empty(zone->contents)) {
  229         return KNOT_EOK;
  230     }
  231 
  232     int timeout = conf->cache.srv_tcp_remote_io_timeout;
  233 
  234     conf_val_t policy_id = conf_zone_get(conf, C_DNSSEC_POLICY, zone->name);
  235     conf_id_fix_default(&policy_id);
  236     conf_val_t ds_push = conf_id_get(conf, C_POLICY, C_DS_PUSH, &policy_id);
  237     while (ds_push.code == KNOT_EOK) {
  238         conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, &ds_push);
  239         size_t addr_count = conf_val_count(&addr);
  240 
  241         int ret = KNOT_EOK;
  242         for (int i = 0; i < addr_count; i++) {
  243             conf_remote_t parent = conf_remote(conf, &ds_push, i);
  244             ret = send_ds_push(conf, zone, &parent, timeout);
  245             if (ret == KNOT_EOK) {
  246                 break;
  247             }
  248         }
  249 
  250         if (ret != KNOT_EOK) {
  251             time_t next_push = time(NULL) + DS_PUSH_RETRY;
  252             zone_events_schedule_at(zone, ZONE_EVENT_DS_PUSH, next_push);
  253             zone->timers.next_ds_push = next_push;
  254         }
  255 
  256         conf_val_next(&ds_push);
  257     }
  258 
  259     return KNOT_EOK;
  260 }