diff options
Diffstat (limited to 'src/libknot')
27 files changed, 3577 insertions, 818 deletions
diff --git a/src/libknot/consts.h b/src/libknot/consts.h index 4249763..3431bc3 100644 --- a/src/libknot/consts.h +++ b/src/libknot/consts.h @@ -57,7 +57,8 @@ typedef enum knot_packet_type { KNOT_RESPONSE_NORMAL, /*!< Normal response. */ KNOT_RESPONSE_AXFR, /*!< AXFR transfer response. */ KNOT_RESPONSE_IXFR, /*!< IXFR transfer response. */ - KNOT_RESPONSE_NOTIFY /*!< NOTIFY response. */ + KNOT_RESPONSE_NOTIFY, /*!< NOTIFY response. */ + KNOT_RESPONSE_UPDATE /*!< Dynamic update response. */ } knot_packet_type_t; /* diff --git a/src/libknot/dname.c b/src/libknot/dname.c index eed2fd6..5c7e961 100644 --- a/src/libknot/dname.c +++ b/src/libknot/dname.c @@ -178,8 +178,13 @@ static int knot_dname_str_to_wire(const char *name, uint size, while (ch - (const uint8_t *)name < size) { assert(w - wire - 1 == ch - (const uint8_t *)name); - + if (*ch == '.') { + /* Zero-length label inside a dname - invalid. */ + if (label_length == 0) { + free(wire); + return -1; + } dbg_dname_detail("Position %zd (%p): " "label length: %u\n", label_start - wire, @@ -262,6 +267,11 @@ static int knot_dname_find_labels(knot_dname_t *dname, int alloc) short label_count = 0; while (pos - name < size && *pos != '\0' && label_count < KNOT_MAX_DNAME_LABELS ) { + if (*pos > 63) { /* Check label lengths. */ + dbg_dname("Wrong wire format of domain name!\n"); + dbg_dname("Label %d exceeds 63 bytes.\n", label_count); + return -1; + } labels[label_count++] = pos - name; pos += *pos + 1; } @@ -471,8 +481,9 @@ knot_dname_t *knot_dname_new_from_wire(const uint8_t *name, uint size, /*----------------------------------------------------------------------------*/ knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire, - size_t *pos, size_t size, - knot_node_t *node) + size_t *pos, size_t size, + knot_node_t *node, + knot_dname_t *dname) { uint8_t name[KNOT_MAX_DNAME_LENGTH]; uint8_t labels[KNOT_MAX_DNAME_LABELS]; @@ -539,34 +550,30 @@ knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire, *pos += 1; } - knot_dname_t *dname = knot_dname_new(); + /* Allocate if NULL. */ + if (dname == NULL) { + dname = knot_dname_new(); + if (dname) { + dname->name = (uint8_t *)malloc((i + 1) * sizeof(uint8_t)); + dname->labels = (uint8_t *)malloc((l + 1) * sizeof(uint8_t)); + } + } if (dname == NULL) { ERR_ALLOC_FAILED; return NULL; } - dname->name = (uint8_t *)malloc((i + 1) * sizeof(uint8_t)); - if (dname->name == NULL) { + if (dname->name == NULL || dname->labels == NULL) { ERR_ALLOC_FAILED; knot_dname_free(&dname); return NULL; } memcpy(dname->name, name, i + 1); - dname->size = i + 1; - - /*! \todo Why l + 1 ?? */ - dname->labels = (uint8_t *)malloc((l + 1) * sizeof(uint8_t)); - if (dname->labels == NULL) { - ERR_ALLOC_FAILED; - knot_dname_free(&dname); - return NULL; - } memcpy(dname->labels, labels, l + 1); - + dname->size = i + 1; dname->label_count = l; - dname->node = node; return dname; diff --git a/src/libknot/dname.h b/src/libknot/dname.h index 347e699..6f649ce 100644 --- a/src/libknot/dname.h +++ b/src/libknot/dname.h @@ -124,9 +124,22 @@ knot_dname_t *knot_dname_new_from_wire(const uint8_t *name, unsigned int size, struct knot_node *node); +/*! + * \brief Parse dname from wire. + * + * \param wire Message in wire format. + * \param pos Position of the domain name on wire. + * \param size Domain name length. + * \param node Zone node the domain name belongs to. Set to NULL if not + * applicable. + * \param dname Destination dname (will allocate new when NULL). + * + * \return parsed domain name or NULL. + */ knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire, - size_t *pos, size_t size, - struct knot_node *node); + size_t *pos, size_t size, + struct knot_node *node, + knot_dname_t *dname); /*! * \brief Initializes domain name by the name given in wire format. diff --git a/src/libknot/hash/cuckoo-hash-table.c b/src/libknot/hash/cuckoo-hash-table.c index 7358e14..7d454a9 100644 --- a/src/libknot/hash/cuckoo-hash-table.c +++ b/src/libknot/hash/cuckoo-hash-table.c @@ -1434,7 +1434,7 @@ int ck_deep_copy(ck_hash_table_t *from, ck_hash_table_t **to) return -2; } - dbg_ck_detail("Copying stash item: %p with item %p, ", si, + dbg_ck_detail("Copying stash item: %p with item %p.\n", si, si->item); if (si->item == NULL) { @@ -1473,9 +1473,10 @@ dbg_ck_exec_detail( (si_new) ? si_new->item : NULL); assert(si_new != NULL); - assert(si_new->item != NULL); - dbg_ck_detail("key: %.*s\n", (int)si_new->item->key_length, - si_new->item->key); + if (si_new->item != NULL) { + dbg_ck_detail("key: %.*s\n", (int)si_new->item->key_length, + si_new->item->key); + } ); } diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c index 0a7a298..8238d7e 100644 --- a/src/libknot/nameserver/name-server.c +++ b/src/libknot/nameserver/name-server.c @@ -2950,7 +2950,7 @@ static int ns_ixfr(knot_ns_xfr_t *xfr) /*----------------------------------------------------------------------------*/ static int knot_ns_prepare_response(knot_packet_t *query, knot_packet_t **resp, - size_t max_size) + size_t max_size, int copy_question) { assert(max_size >= 500); @@ -2969,7 +2969,7 @@ static int knot_ns_prepare_response(knot_packet_t *query, knot_packet_t **resp, return ret; } - ret = knot_response_init_from_query(*resp, query); + ret = knot_response_init_from_query(*resp, query, copy_question); if (ret != KNOT_EOK) { dbg_ns("Failed to init response structure.\n"); @@ -3207,7 +3207,7 @@ int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize, if(knot_packet_is_query(packet)) { *type = KNOT_QUERY_UPDATE; } else { - return KNOT_RCODE_FORMERR; + *type = KNOT_RESPONSE_UPDATE; } break; default: @@ -3437,7 +3437,7 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, resp_max_size = MAX_UDP_PAYLOAD; } - ret = knot_ns_prepare_response(query, resp, resp_max_size); + ret = knot_ns_prepare_response(query, resp, resp_max_size, 1); if (ret != KNOT_EOK) { return KNOT_ERROR; } @@ -3487,6 +3487,143 @@ dbg_ns_exec_verb( /*----------------------------------------------------------------------------*/ +int knot_ns_prep_update_response(knot_nameserver_t *nameserver, + knot_packet_t *query, knot_packet_t **resp, + knot_zone_t **zone, size_t max_size) +{ + dbg_ns_verb("knot_ns_prep_update_response()\n"); + + if (nameserver == NULL || query == NULL || resp == NULL + || zone == NULL) { + return KNOT_EINVAL; + } + + // first, parse the rest of the packet + assert(knot_packet_is_query(query)); + dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n", + knot_packet_parsed(query), knot_packet_size(query)); + int ret; + + ret = knot_packet_parse_rest(query); + if (ret != KNOT_EOK) { + dbg_ns("Failed to parse rest of the query: %s.\n", + knot_strerror(ret)); + return ret; + } + + /* + * Semantic checks + * + * Check the QDCOUNT and in case of anything but 1 send back + * FORMERR + */ + if (knot_packet_qdcount(query) != 1) { + dbg_ns("QDCOUNT != 1. Reply FORMERR.\n"); + return KNOT_EMALF; + } + + /* + * Check what is in the Additional section. Only OPT and TSIG are + * allowed. TSIG must be the last record if present. + */ + /*! \todo Put to separate function - used in prep_normal_response(). */ + if (knot_packet_arcount(query) > 0) { + int ok = 0; + const knot_rrset_t *add1 = + knot_packet_additional_rrset(query, 0); + if (knot_packet_additional_rrset_count(query) == 1 + && (knot_rrset_type(add1) == KNOT_RRTYPE_OPT + || knot_rrset_type(add1) == KNOT_RRTYPE_TSIG)) { + ok = 1; + } else if (knot_packet_additional_rrset_count(query) == 2) { + const knot_rrset_t *add2 = + knot_packet_additional_rrset(query, 1); + if (knot_rrset_type(add1) == KNOT_RRTYPE_OPT + && knot_rrset_type(add2) == KNOT_RRTYPE_TSIG) { + ok = 1; + } + } + + if (!ok) { + dbg_ns("Additional section malformed. Reply FORMERR\n"); + return KNOT_EMALF; + } + } + + size_t resp_max_size = 0; + + knot_packet_dump(query); + + /*! \todo Put to separate function - used in prep_normal_response(). */ + if (max_size > 0) { + // if TCP is used, buffer size is the only constraint + assert(max_size > 0); + resp_max_size = max_size; + } else if (knot_query_edns_supported(query)) { + assert(max_size == 0); + if (knot_edns_get_payload(&query->opt_rr) < + knot_edns_get_payload(nameserver->opt_rr)) { + resp_max_size = knot_edns_get_payload(&query->opt_rr); + } else { + resp_max_size = knot_edns_get_payload( + nameserver->opt_rr); + } + } + + if (resp_max_size < MAX_UDP_PAYLOAD) { + resp_max_size = MAX_UDP_PAYLOAD; + } + + ret = knot_ns_prepare_response(query, resp, resp_max_size, 0); + if (ret != KNOT_EOK) { + return KNOT_ERROR; + } + + dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n", + query->parsed, query->size); + dbg_ns_detail("Opt RR: version: %d, payload: %d\n", + query->opt_rr.version, query->opt_rr.payload); + + // get the answer for the query + knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db); + + dbg_ns_detail("EDNS supported in query: %d\n", + knot_query_edns_supported(query)); + + // set the OPT RR to the response + if (knot_query_edns_supported(query)) { + ret = knot_response_add_opt(*resp, nameserver->opt_rr, 1, + knot_query_nsid_requested(query)); + if (ret != KNOT_EOK) { + dbg_ns("Failed to set OPT RR to the response" + ": %s\n", knot_strerror(ret)); + } else { + // copy the DO bit from the query + if (knot_query_dnssec_requested(query)) { + knot_edns_set_do(&(*resp)->opt_rr); + } + } + } + + dbg_ns_verb("Response max size: %zu\n", (*resp)->max_size); + + const knot_dname_t *qname = knot_packet_qname(knot_packet_query(*resp)); + assert(qname != NULL); + +// uint16_t qtype = knot_packet_qtype(*resp); +dbg_ns_exec_verb( + char *name_str = knot_dname_to_str(qname); + dbg_ns_verb("Trying to find zone %s\n", name_str); + free(name_str); +); + // find zone + *zone = knot_zonedb_find_zone(zonedb, qname); + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + int knot_ns_answer_normal(knot_nameserver_t *nameserver, const knot_zone_t *zone, knot_packet_t *resp, uint8_t *response_wire, size_t *rsize, int check_any) @@ -3619,7 +3756,7 @@ dbg_ns_exec_verb( response->wireformat = xfr->wire; response->max_size = xfr->wire_size; - ret = knot_response_init_from_query(response, xfr->query); + ret = knot_response_init_from_query(response, xfr->query, 1); if (ret != KNOT_EOK) { dbg_ns("Failed to init response structure.\n"); @@ -3796,8 +3933,9 @@ int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr) if (ret < 0 && ret != KNOT_ECONN) { dbg_ns("AXFR failed, sending SERVFAIL.\n"); // now only one type of error (SERVFAIL), later maybe more - /*! \todo xfr->wire is not NULL, will fail on assert! */ - knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL); + knot_ns_error_response_from_query(nameserver, xfr->query, + KNOT_RCODE_SERVFAIL, + xfr->wire, &xfr->wire_size); ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, xfr->wire_size); } else if (ret > 0) { @@ -4076,143 +4214,140 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver, } /*----------------------------------------------------------------------------*/ - -int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query, - uint8_t *response_wire, size_t *rsize, - knot_zone_t **zone, knot_changeset_t **changeset) +/* + * The given query is already fully parsed. But the parameter contains an + * already prepared response structure. + * + * This function should process the contents, prepare prerequisities, prepare + * changeset and return to the caller. + */ +int knot_ns_process_update(const knot_packet_t *query, + const knot_zone_contents_t *zone, + knot_changeset_t *changeset, knot_rcode_t *rcode) { - // 1) Parse the rest of the packet assert(knot_packet_is_query(query)); - knot_packet_t *response; - assert(*rsize >= MAX_UDP_PAYLOAD); - int ret = knot_ns_prepare_response(query, &response, MAX_UDP_PAYLOAD); - if (ret != KNOT_EOK) { - knot_ns_error_response_from_query(nameserver, query, - KNOT_RCODE_SERVFAIL, - response_wire, rsize); - return KNOT_EOK; - } - - assert(response != NULL); + dbg_ns("Processing Dynamic Update.\n"); - dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n", - query->parsed, query->size); - - if (knot_packet_parsed(query) < knot_packet_size(query)) { - ret = knot_packet_parse_rest(query); - if (ret != KNOT_EOK) { - dbg_ns("Failed to parse rest of the query: " - "%s.\n", knot_strerror(ret)); - knot_ns_error_response_full(nameserver, response, - (ret == KNOT_EMALF) - ? KNOT_RCODE_FORMERR - : KNOT_RCODE_SERVFAIL, - response_wire, rsize); - knot_packet_free(&response); - return KNOT_EOK; - } + *rcode = KNOT_RCODE_NOERROR; + + // 1) Check zone + // Already done +// dbg_ns_verb("Checking zone for DDNS.\n"); +// int ret = knot_ddns_check_zone(zone, query, rcode); +// if (ret != KNOT_EOK) { +// dbg_ns("Failed to check zone for update: " +// "%s.\n", knot_strerror(ret)); +// return ret; +// } + + // 2) Convert prerequisities + // Already done +// dbg_ns_verb("Processing prerequisities.\n"); +// knot_ddns_prereq_t *prereqs = NULL; +// int ret = knot_ddns_process_prereqs(query, &prereqs, rcode); +// if (ret != KNOT_EOK) { +// dbg_ns("Failed to check zone for update: " +// "%s.\n", knot_strerror(ret)); +// return ret; +// } + +// assert(prereqs != NULL); + + // 3) Check prerequisities + /*! \todo Somehow ensure the zone will not be changed until the update + * is finished. + */ + // Already done +// dbg_ns_verb("Checking prerequisities.\n"); +// ret = knot_ddns_check_prereqs(zone, &prereqs, rcode); +// if (ret != KNOT_EOK) { +// knot_ddns_prereqs_free(&prereqs); +// dbg_ns("Failed to check zone for update: " +// "%s.\n", knot_strerror(ret)); +// return ret; +// } + + // 4) Convert update to changeset + dbg_ns_verb("Converting UPDATE packet to changeset.\n"); + int ret = knot_ddns_process_update(zone, query, changeset, rcode); + if (ret != KNOT_EOK) { + dbg_ns("Failed to check zone for update: " + "%s.\n", knot_strerror(ret)); + return ret; } - dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n", - knot_packet_parsed(query), knot_packet_size(query)); + assert(changeset != NULL); - /*! \todo API for EDNS values. */ - dbg_ns_verb("Opt RR: version: %d, payload: %d\n", - query->opt_rr.version, query->opt_rr.payload); - - // 2) Find zone for the query - // we do not check if there is only one entry in the Question section - // because the packet structure does not allow it - if (knot_packet_qtype(query) != KNOT_RRTYPE_SOA) { - dbg_ns("Question is not of type SOA.\n"); - knot_ns_error_response_full(nameserver, response, - KNOT_RCODE_FORMERR, - response_wire, rsize); - knot_packet_free(&response); - return KNOT_EOK; - } + // Done in zones.c +// knot_ddns_prereqs_free(&prereqs); + return ret; +} - *zone = knot_zonedb_find_zone(nameserver->zone_db, - knot_packet_qname(query)); - if (*zone == NULL) { - dbg_ns("Zone not found for the update.\n"); - knot_ns_error_response_full(nameserver, response, - KNOT_RCODE_NOTAUTH, - response_wire, rsize); - knot_packet_free(&response); - return KNOT_EOK; +/*----------------------------------------------------------------------------*/ +/* + * This function should: + * 1) Create zone shallow copy and the changes structure. + * 2) Call knot_ddns_process_update2(). + * - If something went bad, call xfrin_rollback_update() and return an error. + * - If everything went OK, continue. + * 3) Finalize the updated zone. + * + * NOTE: Mostly copied from xfrin_apply_changesets(). Should be refactored in + * order to get rid of duplicate code. + */ +int knot_ns_process_update2(const knot_packet_t *query, + knot_zone_contents_t *old_contents, + knot_zone_contents_t **new_contents, + knot_changesets_t *chgs, knot_rcode_t *rcode) +{ + /*! \todo Implement. */ + if (query == NULL || old_contents == NULL || chgs == NULL || + chgs->sets == NULL || new_contents == NULL || rcode == NULL) { + return KNOT_EINVAL; } - uint8_t rcode = 0; - // 3) Check zone - ret = knot_ddns_check_zone(*zone, query, &rcode); - if (ret == KNOT_EBADZONE) { - // zone is slave, forward the request - /*! \todo Implement forwarding. */ - return KNOT_EBADZONE; - } else if (ret != KNOT_EOK) { - dbg_ns("Failed to check zone for update: " - "%s.\n", knot_strerror(ret)); - knot_ns_error_response_full(nameserver, response, rcode, - response_wire, rsize); - knot_packet_free(&response); - return KNOT_EOK; - } + dbg_ns("Applying UPDATE to zone...\n"); - // 4) Convert prerequisities - knot_ddns_prereq_t *prereqs = NULL; - ret = knot_ddns_process_prereqs(query, &prereqs, &rcode); + /* 1) Create zone shallow copy. */ + dbg_ns_verb("Creating shallow copy of the zone...\n"); + knot_zone_contents_t *contents_copy = NULL; + knot_changes_t *changes = NULL; + int ret = xfrin_prepare_zone_copy(old_contents, &contents_copy, + &changes); if (ret != KNOT_EOK) { - dbg_ns("Failed to check zone for update: " - "%s.\n", knot_strerror(ret)); - knot_ns_error_response_full(nameserver, response, rcode, - response_wire, rsize); - knot_packet_free(&response); - return KNOT_EOK; + dbg_ns("Failed to prepare zone copy: %s\n", + knot_strerror(ret)); + *rcode = KNOT_RCODE_SERVFAIL; + return ret; } - - assert(prereqs != NULL); - - // 5) Check prerequisities - /*! \todo Somehow ensure the zone will not be changed until the update - * is finished. - */ - ret = knot_ddns_check_prereqs(knot_zone_contents(*zone), &prereqs, - &rcode); + + /* 2) Apply the UPDATE and create changesets. */ + dbg_ns_verb("Applying the UPDATE and creating changeset...\n"); + ret = knot_ddns_process_update2(contents_copy, query, &chgs->sets[0], + changes, rcode); if (ret != KNOT_EOK) { - dbg_ns("Failed to check zone for update: " - "%s.\n", knot_strerror(ret)); - knot_ns_error_response_full(nameserver, response, rcode, - response_wire, rsize); - knot_ddns_prereqs_free(&prereqs); - knot_packet_free(&response); - return KNOT_EOK; + dbg_ns("Failed to apply UPDATE to the zone copy or no update" + " made: %s\n", (ret < 0) ? knot_strerror(ret) + : "No change made."); + xfrin_rollback_update(old_contents, &contents_copy, &changes); + return ret; } - - // 6) Convert update to changeset - ret = knot_ddns_process_update(query, changeset, &rcode); + + dbg_ns_verb("Finalizing updated zone...\n"); + ret = xfrin_finalize_updated_zone(contents_copy, changes, old_contents); if (ret != KNOT_EOK) { - dbg_ns("Failed to check zone for update: " - "%s.\n", knot_strerror(ret)); - knot_ns_error_response_full(nameserver, response, rcode, - response_wire, rsize); - knot_ddns_prereqs_free(&prereqs); - knot_packet_free(&response); - return KNOT_EOK; + dbg_ns("Failed to finalize updated zone: %s\n", + knot_strerror(ret)); + xfrin_rollback_update(old_contents, &contents_copy, &changes); + *rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR + : KNOT_RCODE_SERVFAIL; + return ret; } - assert(changeset != NULL); - - // 7) Create response - dbg_ns("Update converted successfuly.\n"); - - /*! \todo No response yet. Distinguish somehow in the caller. - * Maybe only this case will be EOK, other cases some error. - */ - - knot_ddns_prereqs_free(&prereqs); - knot_packet_free(&response); + chgs->changes = changes; + *new_contents = contents_copy; + return KNOT_EOK; } @@ -4221,7 +4356,10 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query, int knot_ns_create_forward_query(const knot_packet_t *query, uint8_t *query_wire, size_t *size) { - // just copy the wireformat of the query and set a new random ID to it + /* Forward UPDATE query: + * assign a new packet id + */ + int ret = KNOT_EOK; if (knot_packet_size(query) > *size) { return KNOT_ESPACE; } @@ -4229,10 +4367,9 @@ int knot_ns_create_forward_query(const knot_packet_t *query, memcpy(query_wire, knot_packet_wireformat(query), knot_packet_size(query)); *size = knot_packet_size(query); - knot_wire_set_id(query_wire, knot_random_id()); - return KNOT_EOK; + return ret; } /*----------------------------------------------------------------------------*/ diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h index 3fe1210..6bb7a86 100644 --- a/src/libknot/nameserver/name-server.h +++ b/src/libknot/nameserver/name-server.h @@ -121,6 +121,9 @@ typedef struct knot_ns_xfr { uint8_t *digest; /*!< Buffer for counting digest. */ size_t digest_size; /*!< Size of the digest. */ size_t digest_max_size; /*!< Size of the buffer. */ + + /*! \note [DDNS] Update forwarding fields. */ + int fwd_src_fd; /*!< Query originator fd. */ uint16_t tsig_rcode; uint64_t tsig_prev_time_signed; @@ -158,12 +161,14 @@ typedef enum knot_ns_transport { */ typedef enum knot_ns_xfr_type_t { /* DNS events. */ - XFR_TYPE_AIN = 1 << 0, /*!< AXFR-IN request (start transfer). */ - XFR_TYPE_AOUT= 1 << 1, /*!< AXFR-OUT request (incoming transfer). */ - XFR_TYPE_IIN = 1 << 2, /*!< IXFR-IN request (start transfer). */ - XFR_TYPE_IOUT = 1 << 3, /*!< IXFR-OUT request (incoming transfer). */ - XFR_TYPE_SOA = 1 << 4, /*!< Pending SOA request. */ - XFR_TYPE_NOTIFY = 1 << 5 /*!< Pending NOTIFY query. */ + XFR_TYPE_AIN = 1 << 0, /*!< AXFR-IN request (start transfer). */ + XFR_TYPE_AOUT= 1 << 1, /*!< AXFR-OUT request (incoming transfer). */ + XFR_TYPE_IIN = 1 << 2, /*!< IXFR-IN request (start transfer). */ + XFR_TYPE_IOUT = 1 << 3, /*!< IXFR-OUT request (incoming transfer). */ + XFR_TYPE_SOA = 1 << 4, /*!< Pending SOA request. */ + XFR_TYPE_NOTIFY = 1 << 5, /*!< Pending NOTIFY query. */ + XFR_TYPE_UPDATE = 1 << 6, /*!< UPDATE request (incoming UPDATE). */ + XFR_TYPE_FORWARD = 1 << 7 /*!< UPDATE forward request. */ } knot_ns_xfr_type_t; /*----------------------------------------------------------------------------*/ @@ -227,6 +232,10 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, knot_packet_t *query, knot_packet_t **resp, const knot_zone_t **zone, size_t max_size); +int knot_ns_prep_update_response(knot_nameserver_t *nameserver, + knot_packet_t *query, knot_packet_t **resp, + knot_zone_t **zone, size_t max_size); + /*! * \brief Creates a response for the given normal query using the data of the * nameserver. @@ -339,9 +348,14 @@ int knot_ns_switch_zone(knot_nameserver_t *nameserver, int knot_ns_process_ixfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr); -int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query, - uint8_t *response_wire, size_t *rsize, - knot_zone_t **zone, knot_changeset_t **changeset); +int knot_ns_process_update(const knot_packet_t *query, + const knot_zone_contents_t *zone, + knot_changeset_t *changeset, knot_rcode_t *rcode); + +int knot_ns_process_update2(const knot_packet_t *query, + knot_zone_contents_t *old_contents, + knot_zone_contents_t **new_contents, + knot_changesets_t *chgs, knot_rcode_t *rcode); int knot_ns_create_forward_query(const knot_packet_t *query, uint8_t *query_wire, size_t *size); diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c index 6a047fb..9b7e7c7 100644 --- a/src/libknot/packet/packet.c +++ b/src/libknot/packet/packet.c @@ -291,22 +291,25 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos, dbg_packet_verb("Parsing dname starting on position %zu and " "%zu bytes long.\n", *pos, i - *pos + 1); dbg_packet_verb("Alloc: %d\n", alloc); + size_t bp = *pos; if (alloc) { - question->qname = knot_dname_new_from_wire( - wire + *pos, i - *pos + 1, NULL); + question->qname = knot_dname_parse_from_wire(wire, pos, + i + 1, + NULL, NULL); if (question->qname == NULL) { return KNOT_ENOMEM; } } else { - int res = knot_dname_from_wire(wire + *pos, i - *pos + 1, - NULL, question->qname); - if (res != KNOT_EOK) { - assert(res != KNOT_EINVAL); - return res; + void *parsed = knot_dname_parse_from_wire(wire, pos, + i + 1, + NULL, question->qname); + if (!parsed) { + return KNOT_EMALF; } } - - *pos = i + 1; + if (*pos != i + 1) { + dbg_packet("Parsed dname expected len=%zu, parsed=%zu.\n", i+1-bp, *pos-bp); + } question->qtype = knot_wire_read_u16(wire + i + 1); question->qclass = knot_wire_read_u16(wire + i + 3); *pos += 4; @@ -386,7 +389,7 @@ static knot_rrset_t *knot_packet_parse_rr(const uint8_t *wire, size_t *pos, *pos, size); knot_dname_t *owner = knot_dname_parse_from_wire(wire, pos, size, - NULL); + NULL, NULL); dbg_packet_detail("Created owner: %p, actual position: %zu\n", owner, *pos); if (owner == NULL) { @@ -512,7 +515,7 @@ dbg_packet_exec_detail( free(name); ); - if (knot_rrset_compare((*rrsets)[i], rrset, + if (knot_rrset_match((*rrsets)[i], rrset, KNOT_RRSET_COMPARE_HEADER)) { /*! \todo Test this!!! */ // no duplicate checking here, the packet should @@ -1257,7 +1260,7 @@ const knot_rrset_t *knot_packet_answer_rrset( /*----------------------------------------------------------------------------*/ const knot_rrset_t *knot_packet_authority_rrset( - knot_packet_t *packet, short pos) + const knot_packet_t *packet, short pos) { if (packet == NULL || pos > packet->ns_rrsets) { return NULL; @@ -1269,7 +1272,7 @@ const knot_rrset_t *knot_packet_authority_rrset( /*----------------------------------------------------------------------------*/ const knot_rrset_t *knot_packet_additional_rrset( - knot_packet_t *packet, short pos) + const knot_packet_t *packet, short pos) { if (packet == NULL || pos > packet->ar_rrsets) { return NULL; @@ -1289,19 +1292,19 @@ int knot_packet_contains(const knot_packet_t *packet, } for (int i = 0; i < packet->an_rrsets; ++i) { - if (knot_rrset_compare(packet->answer[i], rrset, cmp)) { + if (knot_rrset_match(packet->answer[i], rrset, cmp)) { return 1; } } for (int i = 0; i < packet->ns_rrsets; ++i) { - if (knot_rrset_compare(packet->authority[i], rrset, cmp)) { + if (knot_rrset_match(packet->authority[i], rrset, cmp)) { return 1; } } for (int i = 0; i < packet->ar_rrsets; ++i) { - if (knot_rrset_compare(packet->additional[i], rrset, cmp)) { + if (knot_rrset_match(packet->additional[i], rrset, cmp)) { return 1; } } @@ -1390,7 +1393,7 @@ void knot_packet_header_to_wire(const knot_header_t *header, int knot_packet_question_to_wire(knot_packet_t *packet) { - if (packet == NULL) { + if (packet == NULL || packet->question.qname == NULL) { return KNOT_EINVAL; } @@ -1551,6 +1554,10 @@ void knot_packet_dump(const knot_packet_t *packet) knot_wire_flags_get_ra(packet->header.flags2) ? "ra" : "", knot_wire_flags_get_ad(packet->header.flags2) ? "ad" : "", knot_wire_flags_get_cd(packet->header.flags2) ? "cd" : ""); + dbg_packet(" RCODE: %u\n", knot_wire_flags_get_rcode( + packet->header.flags2)); + dbg_packet(" OPCODE: %u\n", knot_wire_flags_get_opcode( + packet->header.flags1)); dbg_packet(" QDCOUNT: %u\n", packet->header.qdcount); dbg_packet(" ANCOUNT: %u\n", packet->header.ancount); dbg_packet(" NSCOUNT: %u\n", packet->header.nscount); diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h index 522ae8e..5a95bae 100644 --- a/src/libknot/packet/packet.h +++ b/src/libknot/packet/packet.h @@ -451,7 +451,7 @@ const knot_rrset_t *knot_packet_answer_rrset( * or NULL if there is no such RRSet. */ const knot_rrset_t *knot_packet_authority_rrset( - knot_packet_t *packet, short pos); + const knot_packet_t *packet, short pos); /*! * \brief Returns the requested Additional RRset. @@ -465,7 +465,7 @@ const knot_rrset_t *knot_packet_authority_rrset( * or NULL if there is no such RRSet. */ const knot_rrset_t *knot_packet_additional_rrset( - knot_packet_t *packet, short pos); + const knot_packet_t *packet, short pos); /*! * \brief Checks if the packet already contains the given RRSet. diff --git a/src/libknot/packet/query.h b/src/libknot/packet/query.h index cda72b9..3ca4fd3 100644 --- a/src/libknot/packet/query.h +++ b/src/libknot/packet/query.h @@ -81,6 +81,8 @@ int knot_query_set_opcode(knot_packet_t *query, uint8_t opcode); int knot_query_add_rrset_authority(knot_packet_t *query, const knot_rrset_t *rrset); +int knot_query_rr_to_wire(const knot_rrset_t *rrset, const knot_rdata_t *rdata, + uint8_t **wire, uint8_t *endp); #endif /* _KNOT_QUERY_H_ */ diff --git a/src/libknot/packet/response.c b/src/libknot/packet/response.c index c6a1a09..476c6b3 100644 --- a/src/libknot/packet/response.c +++ b/src/libknot/packet/response.c @@ -903,7 +903,8 @@ int knot_response_init(knot_packet_t *response) /*----------------------------------------------------------------------------*/ int knot_response_init_from_query(knot_packet_t *response, - knot_packet_t *query) + knot_packet_t *query, + int copy_question) { if (response == NULL || query == NULL) { @@ -913,21 +914,29 @@ int knot_response_init_from_query(knot_packet_t *response, // copy the header from the query memcpy(&response->header, &query->header, sizeof(knot_header_t)); - // copy the Question section (but do not copy the QNAME) - memcpy(&response->question, &query->question, - sizeof(knot_question_t)); - int err = 0; - // put the qname into the compression table - // TODO: get rid of the numeric constants - if ((err = knot_response_store_dname_pos(&response->compression, - response->question.qname, 0, 12, 12, 0)) != KNOT_EOK) { - return err; - } + /*! \todo Constant. */ + size_t to_copy = 12; + + if (copy_question) { + // copy the Question section (but do not copy the QNAME) + memcpy(&response->question, &query->question, + sizeof(knot_question_t)); + + // put the qname into the compression table + // TODO: get rid of the numeric constants + if ((err = knot_response_store_dname_pos(&response->compression, + response->question.qname, 0, 12, 12, 0)) + != KNOT_EOK) { + return err; + } - // copy the wireformat of Header and Question from the query - // TODO: get rid of the numeric constants - size_t to_copy = 12 + 4 + knot_dname_size(response->question.qname); + /*! \todo Constant. */ + to_copy += 4 + knot_dname_size(response->question.qname); + } else { + response->header.qdcount = 0; + knot_wire_set_qdcount(response->wireformat, 0); + } assert(response->max_size >= to_copy); memcpy(response->wireformat, query->wireformat, to_copy); diff --git a/src/libknot/packet/response.h b/src/libknot/packet/response.h index beb1a59..277b1aa 100644 --- a/src/libknot/packet/response.h +++ b/src/libknot/packet/response.h @@ -62,7 +62,8 @@ int knot_response_init(knot_packet_t *response); * \retval KNOT_EOK */ int knot_response_init_from_query(knot_packet_t *response, - knot_packet_t *query); + knot_packet_t *query, + int copy_question); /*! * \brief Clears the response structure for reuse. diff --git a/src/libknot/rdata.c b/src/libknot/rdata.c index 9bcdbe5..352bb6c 100644 --- a/src/libknot/rdata.c +++ b/src/libknot/rdata.c @@ -242,7 +242,7 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire, case KNOT_RDATA_WF_LITERAL_DNAME: pos2 = *pos; dname = knot_dname_parse_from_wire( - wire, &pos2, total_size, NULL); + wire, &pos2, total_size, NULL, NULL); if (dname == NULL) { free(items); return KNOT_ERROR; @@ -308,7 +308,7 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire, case 3: pos2 = *pos; dname = knot_dname_parse_from_wire( - wire, &pos2, total_size, NULL); + wire, &pos2, total_size, NULL, NULL); if (dname == NULL) { knot_rdata_free_items(items, i, desc->type, 1); @@ -698,6 +698,18 @@ int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata) } /*---------------------------------------------------------------------------*/ +void knot_rdata_soa_serial_set(knot_rdata_t *rdata, uint32_t serial) +{ + if (!rdata || rdata->count < 3) { + return; + } + + // the number is in network byte order, transform it + knot_wire_write_u32((uint8_t *)(rdata->items[2].raw_data + 1), + serial); +} + +/*---------------------------------------------------------------------------*/ uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata) { @@ -816,3 +828,43 @@ const uint8_t *knot_rdata_nsec3_salt(const knot_rdata_t *rdata) return ((uint8_t *)(rdata->items[3].raw_data + 1)) + 1; } + +/*----------------------------------------------------------------------------*/ + +uint8_t knot_rdata_ds_digest_type(const knot_rdata_t *rdata) +{ + if (rdata->count < 3) { + return 0; + } + + return *((uint8_t *)(rdata->items[2].raw_data + 1)); +} + +/*----------------------------------------------------------------------------*/ + +uint16_t knot_rdata_ds_digest_len(const knot_rdata_t *rdata) +{ + if (rdata->count < 4) { + return 0; + } + + return *(rdata->items[3].raw_data); +} + +/*----------------------------------------------------------------------------*/ + +int knot_rdata_ds_check(const knot_rdata_t *rdata) +{ + // Check if the legth of the digest corresponds to the proper size of + // the digest according to the given algorithm + uint16_t len = knot_rdata_ds_digest_len(rdata); + uint8_t type = knot_rdata_ds_digest_type(rdata); + + if (type == 0 || len == 0) { + return KNOT_EINVAL; + } else if (len != knot_ds_digest_length(type)) { + return KNOT_EDSDIGESTLEN; + } else { + return KNOT_EOK; + } +} diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h index 57517bd..eb7bd55 100644 --- a/src/libknot/rdata.h +++ b/src/libknot/rdata.h @@ -329,6 +329,7 @@ const knot_dname_t *knot_rdata_get_name(const knot_rdata_t *rdata, uint16_t type); int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata); +void knot_rdata_soa_serial_set(knot_rdata_t *rdata, uint32_t serial); uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata); uint32_t knot_rdata_soa_retry(const knot_rdata_t *rdata); @@ -342,6 +343,10 @@ uint16_t knot_rdata_nsec3_iterations(const knot_rdata_t *rdata); uint8_t knot_rdata_nsec3_salt_length(const knot_rdata_t *rdata); const uint8_t *knot_rdata_nsec3_salt(const knot_rdata_t *rdata); +uint8_t knot_rdata_ds_digest_type(const knot_rdata_t *rdata); +uint16_t knot_rdata_ds_digest_len(const knot_rdata_t *rdata); +int knot_rdata_ds_check(const knot_rdata_t *rdata); + #endif /* _KNOT_RDATA_H */ /*! @} */ diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c index d8b10ce..84d5075 100644 --- a/src/libknot/rrset.c +++ b/src/libknot/rrset.c @@ -215,7 +215,7 @@ int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs, if (dupl == KNOT_RRSET_DUPL_MERGE) { rc = knot_rrset_merge_no_dupl((void **)&rrset->rrsigs, (void **)&rrsigs); - if (rc != KNOT_EOK) { + if (rc < 0) { return rc; } else { return 1; @@ -280,6 +280,15 @@ void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl) /*----------------------------------------------------------------------------*/ +void knot_rrset_set_class(knot_rrset_t *rrset, uint16_t rclass) +{ + if (rrset) { + rrset->rclass = rclass; + } +} + +/*----------------------------------------------------------------------------*/ + uint16_t knot_rrset_type(const knot_rrset_t *rrset) { return rrset->type; @@ -604,7 +613,7 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size, /*----------------------------------------------------------------------------*/ -int knot_rrset_compare(const knot_rrset_t *r1, +int knot_rrset_match(const knot_rrset_t *r1, const knot_rrset_t *r2, knot_rrset_compare_type_t cmp) { @@ -838,6 +847,7 @@ dbg_rrset_exec_detail( } knot_rdata_t *walk2 = rrset2->rdata; + int deleted = 0; // no RDATA in RRSet 1 if (rrset1->rdata == NULL && rrset2->rdata != NULL) { @@ -934,6 +944,7 @@ dbg_rrset_exec_detail( knot_rdata_deep_free(&tmp, rrset1->type, 1); assert(tmp == NULL); /* Maybe caller should be warned about this. */ + ++deleted; } } @@ -954,5 +965,5 @@ dbg_rrset_exec_detail( */ rrset2->rdata = NULL; - return KNOT_EOK; + return deleted; } diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h index b5b62db..077b067 100644 --- a/src/libknot/rrset.h +++ b/src/libknot/rrset.h @@ -163,6 +163,8 @@ void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner); void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl); +void knot_rrset_set_class(knot_rrset_t *rrset, uint16_t rclass); + /*! * \brief Returns the TYPE of the RRSet. * @@ -259,7 +261,7 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size, * \retval <> 0 If RRSets are equal. * \retval 0 if RRSets are not equal. */ -int knot_rrset_compare(const knot_rrset_t *r1, +int knot_rrset_match(const knot_rrset_t *r1, const knot_rrset_t *r2, knot_rrset_compare_type_t cmp); @@ -344,6 +346,8 @@ int knot_rrset_merge(void **r1, void **r2); * \retval KNOT_EOK * \retval KNOT_EINVAL if the RRSets could not be merged, because their * Owner, Type, Class or TTL does not match. + * \retval >0 if some RDATA have been removed because they were duplicate. The + * return value indicates number of such RDATA. */ int knot_rrset_merge_no_dupl(void **r1, void **r2); diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c index cb280ab..55316a9 100644 --- a/src/libknot/tsig-op.c +++ b/src/libknot/tsig-op.c @@ -108,20 +108,16 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len, char decoded_key[B64BUFSIZE]; memset(decoded_key, 0, sizeof(decoded_key)); - size_t decoded_key_size = B64BUFSIZE; - int ret = base64_decode(key->secret, strlen(key->secret), - decoded_key, - &decoded_key_size); - if (ret != 1) { - dbg_tsig("TSIG: New decode function failed! (%d)\n", ret); - return KNOT_ERROR; - } - - if (decoded_key_size < 0) { + int32_t ret = base64_decode((uint8_t *)key->secret, strlen(key->secret), + (uint8_t *)decoded_key, B64BUFSIZE); + + if (ret < 0) { dbg_tsig("TSIG: Could not decode Base64\n"); return KNOT_ERROR; } + size_t decoded_key_size = ret; + dbg_tsig_detail("TSIG: decoded key size: %d\n", decoded_key_size); dbg_tsig_detail("TSIG: decoded key:\n"); dbg_tsig_hex_detail(decoded_key, decoded_key_size); @@ -221,14 +217,14 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire, /* Copy class. */ knot_wire_write_u16(wire + offset, knot_rrset_class(tsig_rr)); - dbg_tsig_verb("TSIG: write variables: written CLASS: %u - ", + dbg_tsig_verb("TSIG: write variables: written CLASS: %u - \n", knot_rrset_class(tsig_rr)); dbg_tsig_hex_detail(wire + offset, sizeof(uint16_t)); offset += sizeof(uint16_t); /* Copy TTL - always 0. */ knot_wire_write_u32(wire + offset, knot_rrset_ttl(tsig_rr)); - dbg_tsig_verb("TSIG: write variables: written TTL: %u - ", + dbg_tsig_verb("TSIG: write variables: written TTL: %u - \n", knot_rrset_ttl(tsig_rr)); dbg_tsig_hex_detail(wire + offset, sizeof(uint32_t)); offset += sizeof(uint32_t); @@ -816,6 +812,9 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, memset(wire_to_sign, 0, sizeof(uint8_t) * size); memcpy(wire_to_sign, wire, size); + + /* Restore message id. */ + knot_wire_set_id(wire_to_sign, tsig_rdata_orig_id(tsig_rr)); /* Decrease arcount. */ knot_wire_set_arcount(wire_to_sign, @@ -864,10 +863,10 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, return KNOT_TSIG_EBADSIG; } - dbg_tsig_verb("TSIG: calc digest : "); + dbg_tsig_verb("TSIG: calc digest :\n"); dbg_tsig_hex_verb(digest_tmp, digest_tmp_len); - dbg_tsig_verb("TSIG: given digest: "); + dbg_tsig_verb("TSIG: given digest:\n"); dbg_tsig_hex_verb(tsig_mac, mac_length); if (strncasecmp((char *)(tsig_mac), (char *)digest_tmp, diff --git a/src/libknot/updates/changesets.c b/src/libknot/updates/changesets.c index ab83b07..5405726 100644 --- a/src/libknot/updates/changesets.c +++ b/src/libknot/updates/changesets.c @@ -66,7 +66,7 @@ static int knot_changeset_check_count(knot_rrset_t ***rrsets, size_t count, static int knot_changeset_rrsets_match(const knot_rrset_t *rrset1, const knot_rrset_t *rrset2) { - return knot_rrset_compare(rrset1, rrset2, KNOT_RRSET_COMPARE_HEADER) + return knot_rrset_match(rrset1, rrset2, KNOT_RRSET_COMPARE_HEADER) && (knot_rrset_type(rrset1) != KNOT_RRTYPE_RRSIG || knot_rdata_rrsig_type_covered( knot_rrset_rdata(rrset1)) @@ -76,7 +76,8 @@ static int knot_changeset_rrsets_match(const knot_rrset_t *rrset1, /*----------------------------------------------------------------------------*/ -int knot_changeset_allocate(knot_changesets_t **changesets) +int knot_changeset_allocate(knot_changesets_t **changesets, + uint32_t flags) { // create new changesets *changesets = (knot_changesets_t *)(malloc(sizeof(knot_changesets_t))); @@ -85,8 +86,15 @@ int knot_changeset_allocate(knot_changesets_t **changesets) } memset(*changesets, 0, sizeof(knot_changesets_t)); + (*changesets)->flags = flags; - return knot_changesets_check_size(*changesets); + if (knot_changesets_check_size(*changesets) != KNOT_EOK) { + free(*changesets); + *changesets = NULL; + return KNOT_ENOMEM; + } + + return KNOT_EOK; } /*----------------------------------------------------------------------------*/ @@ -138,19 +146,19 @@ int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count, int knot_changeset_add_new_rr(knot_changeset_t *changeset, knot_rrset_t *rrset, - xfrin_changeset_part_t part) + knot_changeset_part_t part) { knot_rrset_t ***rrsets = NULL; size_t *count = NULL; size_t *allocated = NULL; switch (part) { - case XFRIN_CHANGESET_ADD: + case KNOT_CHANGESET_ADD: rrsets = &changeset->add; count = &changeset->add_count; allocated = &changeset->add_allocated; break; - case XFRIN_CHANGESET_REMOVE: + case KNOT_CHANGESET_REMOVE: rrsets = &changeset->remove; count = &changeset->remove_count; allocated = &changeset->remove_allocated; @@ -173,6 +181,29 @@ int knot_changeset_add_new_rr(knot_changeset_t *changeset, /*----------------------------------------------------------------------------*/ +knot_rrset_t *knot_changeset_remove_rr(knot_rrset_t **rrsets, size_t *count, + int pos) +{ + if (pos >= *count || *count == 0) { + return NULL; + } + + knot_rrset_t *removed = rrsets[pos]; + + // shift all RRSets from pos+1 one cell to the left + for (int i = pos; i < *count - 1; ++i) { + rrsets[i] = rrsets[i + 1]; + } + + // just to be sure, set the last previously occupied position to NULL + rrsets[*count - 1] = NULL; + *count -= 1; + + return removed; +} + +/*----------------------------------------------------------------------------*/ + void knot_changeset_store_soa(knot_rrset_t **chg_soa, uint32_t *chg_serial, knot_rrset_t *soa) { @@ -183,14 +214,14 @@ void knot_changeset_store_soa(knot_rrset_t **chg_soa, /*----------------------------------------------------------------------------*/ int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa, - xfrin_changeset_part_t part) + knot_changeset_part_t part) { switch (part) { - case XFRIN_CHANGESET_ADD: + case KNOT_CHANGESET_ADD: knot_changeset_store_soa(&changeset->soa_to, &changeset->serial_to, soa); break; - case XFRIN_CHANGESET_REMOVE: + case KNOT_CHANGESET_REMOVE: knot_changeset_store_soa(&changeset->soa_from, &changeset->serial_from, soa); break; @@ -212,7 +243,8 @@ int knot_changesets_check_size(knot_changesets_t *changesets) } /* How many steps is needed to content count? */ - size_t extra = (changesets->count - changesets->allocated) % KNOT_CHANGESET_STEP; + size_t extra = (changesets->count - changesets->allocated) + % KNOT_CHANGESET_STEP; extra = (extra + 1) * KNOT_CHANGESET_STEP; /* Allocate new memory block. */ @@ -223,10 +255,15 @@ int knot_changesets_check_size(knot_changesets_t *changesets) return KNOT_ENOMEM; } - /* Clear old memory block and copy old data. */ + /* Clear new memory block and copy old data. */ memset(sets, 0, new_count * item_len); memcpy(sets, changesets->sets, changesets->allocated * item_len); + /* Set type to all newly allocated changesets. */ + for (int i = changesets->allocated; i < new_count; ++i) { + sets[i].flags = changesets->flags; + } + /* Replace old changesets. */ free(changesets->sets); changesets->sets = sets; @@ -237,6 +274,32 @@ int knot_changesets_check_size(knot_changesets_t *changesets) /*----------------------------------------------------------------------------*/ +void knot_changeset_set_flags(knot_changeset_t *changeset, + uint32_t flags) +{ + changeset->flags = flags; +} + +/*----------------------------------------------------------------------------*/ + +uint32_t knot_changeset_flags(knot_changeset_t *changeset) +{ + return changeset->flags; +} + +/*----------------------------------------------------------------------------*/ + +int knot_changeset_is_empty(const knot_changeset_t *changeset) +{ + if (changeset == NULL) { + return 0; + } + + return (changeset->add_count == 0 && changeset->remove_count == 0); +} + +/*----------------------------------------------------------------------------*/ + void knot_free_changeset(knot_changeset_t **changeset) { assert((*changeset)->add_allocated >= (*changeset)->add_count); @@ -287,3 +350,274 @@ void knot_free_changesets(knot_changesets_t **changesets) free(*changesets); *changesets = NULL; } + +/*----------------------------------------------------------------------------*/ +/* knot_changes_t manipulation */ +/*----------------------------------------------------------------------------*/ + +int knot_changes_rrsets_reserve(knot_rrset_t ***rrsets, + int *count, int *allocated, int to_add) +{ + if (rrsets == NULL || count == NULL || allocated == NULL) { + return KNOT_EINVAL; + } + + if (*count + to_add <= *allocated) { + return KNOT_EOK; + } + + int new_count = (*allocated == 0) ? 2 : *allocated * 2; + while (new_count < *count + to_add) { + new_count *= 2; + } + + /* Allocate new memory block. */ + knot_rrset_t **rrsets_new = malloc(new_count * sizeof(knot_rrset_t *)); + if (rrsets_new == NULL) { + return KNOT_ENOMEM; + } + + /* Initialize new memory and copy old data. */ + memset(rrsets_new, 0, new_count * sizeof(knot_rrset_t *)); + memcpy(rrsets_new, *rrsets, (*allocated) * sizeof(knot_rrset_t *)); + + /* Free old nodes and switch pointers. */ + free(*rrsets); + *rrsets = rrsets_new; + *allocated = new_count; + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +int knot_changes_nodes_reserve(knot_node_t ***nodes, + int *count, int *allocated) +{ + if (nodes == NULL || count == NULL || allocated == NULL) { + return KNOT_EINVAL; + } + + if (*count + 2 <= *allocated) { + return KNOT_EOK; + } + + int new_count = (*allocated == 0) ? 2 : *allocated * 2; + + /* Allocate new memory block. */ + const size_t node_len = sizeof(knot_node_t *); + knot_node_t **nodes_new = malloc(new_count * node_len); + if (nodes_new == NULL) { + return KNOT_ENOMEM; + } + + /* Clear memory block and copy old data. */ + memset(nodes_new, 0, new_count * node_len); + memcpy(nodes_new, *nodes, (*allocated) * node_len); + + /* Free old nodes and switch pointers. */ + free(*nodes); + *nodes = nodes_new; + *allocated = new_count; + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +int knot_changes_rdata_reserve(knot_rdata_t ***rdatas, uint16_t **types, + int count, int *allocated, int to_add) +{ + if (rdatas == NULL || types == NULL || allocated == NULL) { + return KNOT_EINVAL; + } + + if (count + to_add <= *allocated) { + return KNOT_EOK; + } + + int new_count = (*allocated == 0) ? 2 : *allocated * 2; + while (new_count < count + to_add) { + new_count *= 2; + } + + /* Allocate new memory block. */ + knot_rdata_t **rdatas_new = malloc(new_count * sizeof(knot_rdata_t *)); + if (rdatas_new == NULL) { + return KNOT_ENOMEM; + } + + uint16_t *types_new = malloc(new_count * sizeof(uint)); + if (types_new == NULL) { + free(rdatas_new); + return KNOT_ENOMEM; + } + + /* Initialize new memory and copy old data. */ + memset(rdatas_new, 0, new_count * sizeof(knot_rdata_t *)); + memcpy(rdatas_new, *rdatas, (*allocated) * sizeof(knot_rdata_t *)); + + memset(types_new, 0, new_count * sizeof(uint)); + memcpy(types_new, *types, (*allocated) * sizeof(uint)); + + /* Free old rdatas and switch pointers. */ + free(*rdatas); + free(*types); + *rdatas = rdatas_new; + *types = types_new; + *allocated = new_count; + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +void knot_changes_add_rdata(knot_rdata_t **rdatas, uint16_t *types, + int *count, knot_rdata_t *rdata, uint16_t type) +{ + if (rdatas == NULL || types == NULL || count == NULL || rdata == NULL) { + return; + } + + // Add all RDATAs from the chain!! + + knot_rdata_t *r = rdata; + do { +// dbg_xfrin_detail("Adding RDATA to RDATA list: %p\n", r); + rdatas[*count] = r; + types[*count] = type; + ++*count; + + r = r->next; + } while (r != NULL && r != rdata); +} + +/*----------------------------------------------------------------------------*/ + +int knot_changes_add_old_rrsets(knot_rrset_t **rrsets, int count, + knot_changes_t *changes, int add_rdata) +{ + if (rrsets == NULL || changes == NULL) { + return KNOT_EINVAL; + } + + if (count == 0) { + return KNOT_EOK; + } + + /* Reserve twice the space, to have enough space for RRSIGs if + * there are some. + */ + int ret = knot_changes_rrsets_reserve(&changes->old_rrsets, + &changes->old_rrsets_count, + &changes->old_rrsets_allocated, + 2 * count); + if (ret != KNOT_EOK) { +// dbg_xfrin("Failed to reserve changes rrsets.\n"); + return ret; + } + + /* Mark RRsets and RDATA for removal. */ + for (unsigned i = 0; i < count; ++i) { + if (rrsets[i] == NULL) { + continue; + } + + knot_rrset_t *rrsigs = knot_rrset_get_rrsigs(rrsets[i]); + + if (add_rdata) { + + /* RDATA count in the RRSet. */ + int rdata_count = knot_rrset_rdata_rr_count(rrsets[i]); + + if (rrsigs != NULL) { + /* Increment the RDATA count by the count of + * RRSIGs. */ + rdata_count += knot_rrset_rdata_rr_count( + rrsigs); + } + + /* Remove old RDATA. */ + + ret = knot_changes_rdata_reserve(&changes->old_rdata, + &changes->old_rdata_types, + changes->old_rdata_count, + &changes->old_rdata_allocated, + rdata_count); + if (ret != KNOT_EOK) { +// dbg_xfrin("Failed to reserve changes rdata.\n"); + return ret; + } + + knot_changes_add_rdata(changes->old_rdata, + changes->old_rdata_types, + &changes->old_rdata_count, + knot_rrset_get_rdata(rrsets[i]), + knot_rrset_type(rrsets[i])); + + knot_changes_add_rdata(changes->old_rdata, + changes->old_rdata_types, + &changes->old_rdata_count, + knot_rrset_get_rdata(rrsigs), + KNOT_RRTYPE_RRSIG); + } + + /* Disconnect RRsigs from rrset. */ + knot_rrset_set_rrsigs(rrsets[i], NULL); + changes->old_rrsets[changes->old_rrsets_count++] = rrsets[i]; + changes->old_rrsets[changes->old_rrsets_count++] = rrsigs; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +int knot_changes_add_new_rrsets(knot_rrset_t **rrsets, int count, + knot_changes_t *changes, int add_rdata) +{ + if (rrsets == NULL || changes == NULL) { + return KNOT_EINVAL; + } + + if (count == 0) { + return KNOT_EOK; + } + + int ret = knot_changes_rrsets_reserve(&changes->new_rrsets, + &changes->new_rrsets_count, + &changes->new_rrsets_allocated, + count); + if (ret != KNOT_EOK) { + return ret; + } + + /* Mark RRsets and RDATA for removal. */ + for (unsigned i = 0; i < count; ++i) { + if (rrsets[i] == NULL) { + continue; + } + + if (add_rdata) { + int rdata_count = knot_rrset_rdata_rr_count(rrsets[i]); + ret = knot_changes_rdata_reserve(&changes->new_rdata, + &changes->new_rdata_types, + changes->new_rdata_count, + &changes->new_rdata_allocated, + rdata_count); + if (ret != KNOT_EOK) { + return ret; + } + + knot_changes_add_rdata(changes->new_rdata, + changes->new_rdata_types, + &changes->new_rdata_count, + knot_rrset_get_rdata(rrsets[i]), + knot_rrset_type(rrsets[i])); + } + + changes->new_rrsets[changes->new_rrsets_count++] = rrsets[i]; + } + + return KNOT_EOK; +} diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h index 642b155..4585abc 100644 --- a/src/libknot/updates/changesets.h +++ b/src/libknot/updates/changesets.h @@ -30,6 +30,14 @@ #include "rrset.h" #include "zone/node.h" +/*----------------------------------------------------------------------------*/ + +/*! \brief Changeset flags, stored as first 4 bytes in serialized changeset. */ +typedef enum { + KNOT_CHANGESET_TYPE_IXFR = 1 << 0, + KNOT_CHANGESET_TYPE_DDNS = 1 << 1 +} knot_changeset_flag_t; + /*! \todo Changeset must be serializable/deserializable, so * all data and pointers have to be changeset-exclusive, * or more advanced structure serialization scheme has to be @@ -53,6 +61,8 @@ typedef struct { size_t allocated; uint32_t serial_from; uint32_t serial_to; + + uint32_t flags; /*!< DDNS / IXFR */ } knot_changeset_t; /*----------------------------------------------------------------------------*/ @@ -69,7 +79,7 @@ typedef struct { * Deleted after successful update. */ knot_rdata_t **old_rdata; - unsigned *old_rdata_types; + uint16_t *old_rdata_types; int old_rdata_count; int old_rdata_allocated; @@ -86,7 +96,7 @@ typedef struct { * Deleted after failed update. */ knot_rdata_t **new_rdata; - unsigned *new_rdata_types; + uint16_t *new_rdata_types; int new_rdata_count; int new_rdata_allocated; @@ -109,19 +119,21 @@ typedef struct { size_t count; size_t allocated; knot_rrset_t *first_soa; + uint32_t flags; knot_changes_t *changes; } knot_changesets_t; /*----------------------------------------------------------------------------*/ typedef enum { - XFRIN_CHANGESET_ADD, - XFRIN_CHANGESET_REMOVE -} xfrin_changeset_part_t; + KNOT_CHANGESET_ADD, + KNOT_CHANGESET_REMOVE +} knot_changeset_part_t; /*----------------------------------------------------------------------------*/ -int knot_changeset_allocate(knot_changesets_t **changesets); +int knot_changeset_allocate(knot_changesets_t **changesets, + uint32_t flags); int knot_changeset_add_rrset(knot_rrset_t ***rrsets, size_t *count, size_t *allocated, @@ -132,20 +144,51 @@ int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count, int knot_changeset_add_new_rr(knot_changeset_t *changeset, knot_rrset_t *rrset, - xfrin_changeset_part_t part); + knot_changeset_part_t part); + +knot_rrset_t *knot_changeset_remove_rr(knot_rrset_t **rrsets, size_t *count, + int pos); void knot_changeset_store_soa(knot_rrset_t **chg_soa, uint32_t *chg_serial, knot_rrset_t *soa); int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa, - xfrin_changeset_part_t part); + knot_changeset_part_t part); int knot_changesets_check_size(knot_changesets_t *changesets); +void knot_changeset_set_flags(knot_changeset_t *changeset, + uint32_t flags); + +uint32_t knot_changeset_flags(knot_changeset_t *changeset); + +int knot_changeset_is_empty(const knot_changeset_t *changeset); + void knot_free_changeset(knot_changeset_t **changeset); void knot_free_changesets(knot_changesets_t **changesets); +int knot_changes_rrsets_reserve(knot_rrset_t ***rrsets, + int *count, int *allocated, int to_add); + +int knot_changes_nodes_reserve(knot_node_t ***nodes, + int *count, int *allocated); + +int knot_changes_rdata_reserve(knot_rdata_t ***rdatas, uint16_t **types, + int count, int *allocated, int to_add); + +void knot_changes_add_rdata(knot_rdata_t **rdatas, uint16_t *types, + int *count, knot_rdata_t *rdata, uint16_t type); + +/*! + * \note Also processes RRSIGs. May be switched by a parameter later, if needed. + */ +int knot_changes_add_old_rrsets(knot_rrset_t **rrsets, int count, + knot_changes_t *changes, int add_rdata); + +int knot_changes_add_new_rrsets(knot_rrset_t **rrsets, int count, + knot_changes_t *changes, int add_rdata); + #endif /* _KNOT_CHANGESETS_H_ */ /*! @} */ diff --git a/src/libknot/updates/ddns.c b/src/libknot/updates/ddns.c index 72a1be9..90a578f 100644 --- a/src/libknot/updates/ddns.c +++ b/src/libknot/updates/ddns.c @@ -21,26 +21,24 @@ #include "util/debug.h" #include "packet/packet.h" #include "consts.h" +#include "common/mempattern.h" +#include "nameserver/name-server.h" // ns_serial_compare() - TODO: extract +#include "updates/xfr-in.h" /*----------------------------------------------------------------------------*/ // Copied from XFR - maybe extract somewhere else static int knot_ddns_prereq_check_rrsets(knot_rrset_t ***rrsets, size_t *count, size_t *allocated) { - int new_count = 0; - if (*count == *allocated) { - new_count = *allocated * 2; - } - - knot_rrset_t **rrsets_new = - (knot_rrset_t **)calloc(new_count, sizeof(knot_rrset_t *)); - if (rrsets_new == NULL) { + /* This is really confusing, it's ptr -> array of "knot_rrset_t*" */ + char *arr = (char*)*rrsets; + int ret = 0; + ret = mreserve(&arr, sizeof(knot_rrset_t*), *count + 1, 0, allocated); + if (ret < 0) { return KNOT_ENOMEM; } - - memcpy(rrsets_new, *rrsets, (*count) * sizeof(knot_rrset_t *)); - *rrsets = rrsets_new; - *allocated = new_count; + + *rrsets = (knot_rrset_t**)arr; return KNOT_EOK; } @@ -50,20 +48,15 @@ static int knot_ddns_prereq_check_rrsets(knot_rrset_t ***rrsets, static int knot_ddns_prereq_check_dnames(knot_dname_t ***dnames, size_t *count, size_t *allocated) { - int new_count = 0; - if (*count == *allocated) { - new_count = *allocated * 2; - } - - knot_dname_t **dnames_new = - (knot_dname_t **)calloc(new_count, sizeof(knot_dname_t *)); - if (dnames_new == NULL) { + /* This is really confusing, it's ptr -> array of "knot_dname_t*" */ + char *arr = (char*)*dnames; + int ret = 0; + ret = mreserve(&arr, sizeof(knot_dname_t*), *count + 1, 0, allocated); + if (ret < 0) { return KNOT_ENOMEM; } - - memcpy(dnames_new, *dnames, (*count) * sizeof(knot_dname_t *)); - *dnames = dnames_new; - *allocated = new_count; + + *dnames = (knot_dname_t**)arr; return KNOT_EOK; } @@ -77,8 +70,8 @@ static int knot_ddns_add_prereq_rrset(const knot_rrset_t *rrset, // check if such RRSet is not already there and merge if needed int ret; for (int i = 0; i < *count; ++i) { - if (knot_rrset_compare(rrset, (*rrsets)[i], - KNOT_RRSET_COMPARE_HEADER) == 0) { + if (knot_rrset_match(rrset, (*rrsets)[i], + KNOT_RRSET_COMPARE_HEADER) == 1) { ret = knot_rrset_merge((void **)&((*rrsets)[i]), (void **)&rrset); if (ret != KNOT_EOK) { @@ -188,39 +181,119 @@ static int knot_ddns_add_prereq(knot_ddns_prereq_t *prereqs, /*----------------------------------------------------------------------------*/ +static int knot_ddns_check_remove_rr(knot_changeset_t *changeset, + const knot_rrset_t *rr) +{ + dbg_ddns_verb("Removing possible redundant RRs from changeset.\n"); + for (int i = 0; i < changeset->add_count; ++i) { + // Removing RR(s) from this owner + if (knot_dname_compare(knot_rrset_owner(rr), + knot_rrset_owner(changeset->add[i])) == 0) { + // Removing one or all RRSets + if (knot_rrset_class(rr) == KNOT_CLASS_ANY) { + dbg_ddns_detail("Removing one or all " + "RRSets\n"); + if (knot_rrset_type(rr) + == knot_rrset_type(changeset->add[i]) + || knot_rrset_type(rr) == KNOT_RRTYPE_ANY) { + knot_rrset_t *remove = + knot_changeset_remove_rr( + changeset->add, + &changeset->add_count, i); + dbg_ddns_detail("Removed RRSet from " + "chgset:\n"); + knot_rrset_dump(remove, 0); + knot_rrset_deep_free(&remove, 1, 1, 1); + } + } else if (knot_rrset_type(rr) + == knot_rrset_type(changeset->add[i])){ + /* All other classes are checked in + * knot_ddns_check_update(). + */ + assert(knot_rrset_class(rr) == KNOT_CLASS_NONE); + + // Removing specific RR from a RRSet + knot_rdata_t *rdata = knot_rrset_remove_rdata( + changeset->add[i], + knot_rrset_rdata(rr)); + + dbg_ddns_detail("Removed RR from chgset: \n"); + knot_rdata_dump(rdata, knot_rrset_type(rr), 0); + + knot_rdata_deep_free(&rdata, + knot_rrset_type(changeset->add[i]), 1); + // if the RRSet is empty, remove from changeset + if (knot_rrset_rdata_rr_count(changeset->add[i]) + == 0) { + knot_rrset_t *remove = + knot_changeset_remove_rr( + changeset->add, + &changeset->add_count, i); + dbg_ddns_detail("RRSet empty, removing." + "\n"); + knot_rrset_deep_free(&remove, 1, 1, 1); + } + } + } + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + static int knot_ddns_add_update(knot_changeset_t *changeset, - const knot_rrset_t *rrset, uint16_t qclass) + const knot_rrset_t *rrset, uint16_t qclass, + knot_rrset_t **rrset_copy) { assert(changeset != NULL); assert(rrset != NULL); + assert(rrset_copy != NULL); int ret; // create a copy of the RRSet - /*! \todo If the packet was not parsed all at once, we could save this + /*! \todo ref #937 If the packet was not parsed all at once, we could save this * copy. */ - knot_rrset_t *rrset_copy; - ret = knot_rrset_deep_copy(rrset, &rrset_copy, 0); + *rrset_copy = NULL; + ret = knot_rrset_deep_copy(rrset, rrset_copy, 0); if (ret != KNOT_EOK) { return ret; } - /*! \todo What about the SOAs? */ - if (knot_rrset_class(rrset) == qclass) { // this RRSet should be added to the zone + dbg_ddns_detail(" * adding RR %p\n", *rrset_copy); ret = knot_changeset_add_rr(&changeset->add, &changeset->add_count, &changeset->add_allocated, - rrset_copy); + *rrset_copy); } else { // this RRSet marks removal of something from zone - // what should be removed is distinguished when applying + + /* To imitate in-order processing of UPDATE RRs, we must check + * If this REMOVE RR does not affect any of the previous + * ADD RRs in this update. If yes, they must be removed from + * the changeset. + * + * See https://git.nic.cz/redmine/issues/937#note-14 and below. + */ + + // TODO: finish, disabled for now + + dbg_ddns_detail(" * removing RR %p\n", *rrset_copy); + + ret = knot_ddns_check_remove_rr(changeset, *rrset_copy); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(rrset_copy, 1, 1, 1); + return ret; + } + ret = knot_changeset_add_rr(&changeset->remove, &changeset->remove_count, &changeset->remove_allocated, - rrset_copy); + *rrset_copy); } return ret; @@ -229,7 +302,7 @@ static int knot_ddns_add_update(knot_changeset_t *changeset, /*----------------------------------------------------------------------------*/ static int knot_ddns_check_exist(const knot_zone_contents_t *zone, - const knot_rrset_t *rrset, uint8_t *rcode) + const knot_rrset_t *rrset, knot_rcode_t *rcode) { assert(zone != NULL); assert(rrset != NULL); @@ -261,7 +334,8 @@ static int knot_ddns_check_exist(const knot_zone_contents_t *zone, /*----------------------------------------------------------------------------*/ static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone, - const knot_rrset_t *rrset, uint8_t *rcode) + const knot_rrset_t *rrset, + knot_rcode_t *rcode) { assert(zone != NULL); assert(rrset != NULL); @@ -305,7 +379,8 @@ static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone, /*----------------------------------------------------------------------------*/ static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone, - const knot_rrset_t *rrset, uint8_t *rcode) + const knot_rrset_t *rrset, + knot_rcode_t *rcode) { assert(zone != NULL); assert(rrset != NULL); @@ -322,23 +397,15 @@ static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone, } const knot_node_t *node; - const knot_rrset_t *found; node = knot_zone_contents_find_node(zone, knot_rrset_owner(rrset)); if (node == NULL) { return KNOT_EOK; - } else if ((found = knot_node_rrset(node, knot_rrset_type(rrset))) - == NULL) { + } else if (knot_node_rrset(node, knot_rrset_type(rrset)) == NULL) { return KNOT_EOK; - } else { - // do not have to compare the header, it is already done - assert(knot_rrset_type(found) == knot_rrset_type(rrset)); - assert(knot_dname_compare(knot_rrset_owner(found), - knot_rrset_owner(rrset)) == 0); - if (knot_rrset_compare_rdata(found, rrset) <= 0) { - return KNOT_EOK; - } } + + /* RDATA is always empty for simple RRset checks. */ *rcode = KNOT_RCODE_YXRRSET; return KNOT_EPREREQ; @@ -347,7 +414,8 @@ static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone, /*----------------------------------------------------------------------------*/ static int knot_ddns_check_in_use(const knot_zone_contents_t *zone, - const knot_dname_t *dname, uint8_t *rcode) + const knot_dname_t *dname, + knot_rcode_t *rcode) { assert(zone != NULL); assert(dname != NULL); @@ -376,7 +444,8 @@ static int knot_ddns_check_in_use(const knot_zone_contents_t *zone, /*----------------------------------------------------------------------------*/ static int knot_ddns_check_not_in_use(const knot_zone_contents_t *zone, - const knot_dname_t *dname, uint8_t *rcode) + const knot_dname_t *dname, + knot_rcode_t *rcode) { assert(zone != NULL); assert(dname != NULL); @@ -405,10 +474,13 @@ static int knot_ddns_check_not_in_use(const knot_zone_contents_t *zone, /* API functions */ /*----------------------------------------------------------------------------*/ -int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query, - uint8_t *rcode) +int knot_ddns_check_zone(const knot_zone_contents_t *zone, + const knot_packet_t *query, knot_rcode_t *rcode) { if (zone == NULL || query == NULL || rcode == NULL) { + if (rcode != NULL) { + *rcode = KNOT_RCODE_SERVFAIL; + } return KNOT_EINVAL; } @@ -417,18 +489,8 @@ int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query, return KNOT_EMALF; } - if(!knot_zone_contents(zone)) { - *rcode = KNOT_RCODE_REFUSED; - return KNOT_ENOZONE; - } - - // 1) check if the zone is master or slave - if (!knot_zone_is_master(zone)) { - return KNOT_EBADZONE; - } - - // 2) check zone CLASS - if (knot_zone_contents_class(knot_zone_contents(zone)) != + // check zone CLASS + if (knot_zone_contents_class(zone) != knot_packet_qclass(query)) { *rcode = KNOT_RCODE_NOTAUTH; return KNOT_ENOZONE; @@ -439,8 +501,8 @@ int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query, /*----------------------------------------------------------------------------*/ -int knot_ddns_process_prereqs(knot_packet_t *query, - knot_ddns_prereq_t **prereqs, uint8_t *rcode) +int knot_ddns_process_prereqs(const knot_packet_t *query, + knot_ddns_prereq_t **prereqs, knot_rcode_t *rcode) { /*! \todo Consider not parsing the whole packet at once, but * parsing one RR at a time - could save some memory and time. @@ -449,6 +511,8 @@ int knot_ddns_process_prereqs(knot_packet_t *query, if (query == NULL || prereqs == NULL || rcode == NULL) { return KNOT_EINVAL; } + + dbg_ddns("Processing prerequisities.\n"); // allocate space for the prerequisities *prereqs = (knot_ddns_prereq_t *)calloc(1, sizeof(knot_ddns_prereq_t)); @@ -478,9 +542,11 @@ int knot_ddns_process_prereqs(knot_packet_t *query, /*----------------------------------------------------------------------------*/ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone, - knot_ddns_prereq_t **prereqs, uint8_t *rcode) + knot_ddns_prereq_t **prereqs, knot_rcode_t *rcode) { int i, ret; + + dbg_ddns("Checking 'exist' prerequisities.\n"); for (i = 0; i < (*prereqs)->exist_count; ++i) { ret = knot_ddns_check_exist(zone, (*prereqs)->exist[i], rcode); @@ -489,6 +555,7 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone, } } + dbg_ddns("Checking 'exist full' prerequisities.\n"); for (i = 0; i < (*prereqs)->exist_full_count; ++i) { ret = knot_ddns_check_exist_full(zone, (*prereqs)->exist_full[i], @@ -498,6 +565,7 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone, } } + dbg_ddns("Checking 'not exist' prerequisities.\n"); for (i = 0; i < (*prereqs)->not_exist_count; ++i) { ret = knot_ddns_check_not_exist(zone, (*prereqs)->not_exist[i], rcode); @@ -506,6 +574,7 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone, } } + dbg_ddns("Checking 'in use' prerequisities.\n"); for (i = 0; i < (*prereqs)->in_use_count; ++i) { ret = knot_ddns_check_in_use(zone, (*prereqs)->in_use[i], rcode); @@ -514,6 +583,7 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone, } } + dbg_ddns("Checking 'not in use' prerequisities.\n"); for (i = 0; i < (*prereqs)->not_in_use_count; ++i) { ret = knot_ddns_check_not_in_use(zone, (*prereqs)->not_in_use[i], @@ -529,10 +599,15 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone, /*----------------------------------------------------------------------------*/ static int knot_ddns_check_update(const knot_rrset_t *rrset, - const knot_packet_t *query, uint8_t *rcode) + const knot_packet_t *query, + knot_rcode_t *rcode) { - if (!knot_dname_is_subdomain(knot_rrset_owner(rrset), - knot_packet_qname(query))) { + /* Accept both subdomain and dname match. */ + dbg_ddns("Checking UPDATE packet.\n"); + const knot_dname_t *owner = knot_rrset_owner(rrset); + const knot_dname_t *qname = knot_packet_qname(query); + int is_sub = knot_dname_is_subdomain(owner, qname); + if (!is_sub && knot_dname_compare(owner, qname) != 0) { *rcode = KNOT_RCODE_NOTZONE; return KNOT_EBADZONE; } @@ -565,8 +640,9 @@ static int knot_ddns_check_update(const knot_rrset_t *rrset, /*----------------------------------------------------------------------------*/ -int knot_ddns_process_update(knot_packet_t *query, - knot_changeset_t **changeset, uint8_t *rcode) +int knot_ddns_process_update(const knot_zone_contents_t *zone, + const knot_packet_t *query, + knot_changeset_t *changeset, knot_rcode_t *rcode) { // just put all RRSets from query's Authority section // it will be distinguished when applying to the zone @@ -575,63 +651,1739 @@ int knot_ddns_process_update(knot_packet_t *query, return KNOT_EINVAL; } - *changeset = (knot_changeset_t *)calloc(1, sizeof(knot_changeset_t)); - CHECK_ALLOC_LOG(*changeset, KNOT_ENOMEM); + int ret = KNOT_EOK; + + /* Copy base SOA query. */ + const knot_rrset_t *soa = knot_node_rrset(knot_zone_contents_apex(zone), + KNOT_RRTYPE_SOA); + knot_rrset_t *soa_begin = NULL; + knot_rrset_t *soa_end = NULL; + ret = knot_rrset_deep_copy(soa, &soa_begin, 0); + if (ret == KNOT_EOK) { + knot_changeset_store_soa(&changeset->soa_from, + &changeset->serial_from, soa_begin); + } else { + *rcode = KNOT_RCODE_SERVFAIL; + return ret; + } - int ret; + /* Current SERIAL */ + int64_t sn = knot_rdata_soa_serial(knot_rrset_rdata(soa_begin)); + int64_t sn_new; + /* Incremented SERIAL + * We must set it now to be able to compare SERIAL from SOAs in the + * UPDATE to it. Although we do not have the new SOA yet. + */ + if (sn > -1) { + sn_new = (uint32_t)sn + 1; + } else { + *rcode = KNOT_RCODE_SERVFAIL; + return ret; + } + + const knot_rrset_t *rrset = NULL; + knot_rrset_t *rrset_copy = NULL; + dbg_ddns("Processing UPDATE section.\n"); for (int i = 0; i < knot_packet_authority_rrset_count(query); ++i) { - const knot_rrset_t *rrset = - knot_packet_authority_rrset(query, i); + rrset = knot_packet_authority_rrset(query, i); ret = knot_ddns_check_update(rrset, query, rcode); if (ret != KNOT_EOK) { + dbg_ddns("Failed to check update RRSet:%s\n", + knot_strerror(ret)); return ret; } - ret = knot_ddns_add_update(*changeset, rrset, - knot_packet_qclass(query)); + ret = knot_ddns_add_update(changeset, rrset, + knot_packet_qclass(query), + &rrset_copy); if (ret != KNOT_EOK) { dbg_ddns("Failed to add update RRSet:%s\n", knot_strerror(ret)); *rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR : KNOT_RCODE_SERVFAIL; - knot_free_changeset(changeset); return ret; } + + /* Check if the added record is SOA. If yes, check the SERIAL. + * If this record should cause the SOA to be replaced in the + * zone, use it as the ending SOA. + * + * Also handle cases where there are multiple SOAs to be added + * in the same UPDATE. The one with the largest SERIAL should + * be used. + * + * TODO: If there are more SOAs in the UPDATE one after another, + * the ddns_add_update() function will merge them into a + * RRSet. This should be handled somehow. + */ + if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA + && ns_serial_compare(knot_rdata_soa_serial( + knot_rrset_rdata(rrset)), + sn_new) > 0) { + sn_new = knot_rdata_soa_serial(knot_rrset_rdata(rrset)); + soa_end = (knot_rrset_t *)rrset_copy; + } } - return KNOT_EOK; + /* Ending SOA */ + if (soa_end == NULL) { + /* If not set */ + assert(sn_new == (uint32_t)sn + 1); + ret = knot_rrset_deep_copy(soa, &soa_end, 1); + knot_rdata_t *rd = knot_rrset_get_rdata(soa_end); + knot_rdata_soa_serial_set(rd, sn_new); + } + + knot_changeset_store_soa(&changeset->soa_to, + &changeset->serial_to, + soa_end); + + return ret; } /*----------------------------------------------------------------------------*/ void knot_ddns_prereqs_free(knot_ddns_prereq_t **prereq) { + dbg_ddns("Freeing prerequisities.\n"); + int i; for (i = 0; i < (*prereq)->exist_count; ++i) { knot_rrset_deep_free(&(*prereq)->exist[i], 1, 1, 1); } + free((*prereq)->exist); for (i = 0; i < (*prereq)->exist_full_count; ++i) { knot_rrset_deep_free(&(*prereq)->exist_full[i], 1, 1, 1); } + free((*prereq)->exist_full); for (i = 0; i < (*prereq)->not_exist_count; ++i) { knot_rrset_deep_free(&(*prereq)->not_exist[i], 1, 1, 1); } + free((*prereq)->not_exist); for (i = 0; i < (*prereq)->in_use_count; ++i) { knot_dname_free(&(*prereq)->in_use[i]); } + free((*prereq)->in_use); for (i = 0; i < (*prereq)->not_in_use_count; ++i) { knot_dname_free(&(*prereq)->not_in_use[i]); } + free((*prereq)->not_in_use); free(*prereq); *prereq = NULL; } + +/*----------------------------------------------------------------------------*/ +/* New DDNS processing - */ +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_check_remove_rr2(knot_changeset_t *changeset, + const knot_dname_t *owner, + uint16_t type, const knot_rdata_t *rdata, + knot_rrset_t ***removed, + size_t *removed_count) +{ + assert(changeset != NULL); + assert(removed != NULL); + assert(removed_count != NULL); + + *removed_count = 0; + *removed = (knot_rrset_t **)malloc(changeset->add_count + * sizeof(knot_rrset_t *)); + if (*removed == NULL) { + return KNOT_ENOMEM; + } + + knot_rrset_t *remove = NULL; + + /* + * We assume that each RR in the ADD section of the changeset is in its + * own RRSet. It should be as this is how they are stored there by the + * ddns_process_add() function. + */ + + dbg_ddns_verb("Removing possible redundant RRs from changeset.\n"); + for (int i = 0; i < changeset->add_count; ++i) { + // Removing RR(s) from this owner + if (knot_dname_compare(knot_rrset_owner(changeset->add[i]), + owner) == 0) { + // Removing one or all RRSets + if (rdata == NULL + && (type == knot_rrset_type(changeset->add[i]) + || type == KNOT_RRTYPE_ANY)) { + dbg_ddns_detail("Removing one or all RRSets\n"); + remove = knot_changeset_remove_rr( + changeset->add, + &changeset->add_count, i); + } else if (type == knot_rrset_type(changeset->add[i])) { + // Removing specific RR + assert(rdata != NULL); + + knot_rrtype_descriptor_t *desc = + knot_rrtype_descriptor_by_type(type); + + // We must check if the RDATA match + if (knot_rdata_compare(rdata, + knot_rrset_rdata(changeset->add[i]), + desc->wireformat)) { + remove = knot_changeset_remove_rr( + changeset->add, + &changeset->add_count, i); + } + } + + dbg_ddns_detail("Removed RRSet from chgset:\n"); + knot_rrset_dump(remove, 0); + (*removed)[(*removed_count)++] = remove; + } + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static void knot_ddns_check_add_rr(knot_changeset_t *changeset, + const knot_rrset_t *rr, + knot_rrset_t **removed) +{ + assert(changeset != NULL); + assert(rr != NULL); + assert(removed != NULL); + + *removed = NULL; + + dbg_ddns_verb("Removing possible redundant RRs from changeset.\n"); + for (int i = 0; i < changeset->remove_count; ++i) { + /* Just check exact match, the changeset contains only + * whole RRs that have been removed. + */ + if (knot_rrset_match(rr, changeset->remove[i], + KNOT_RRSET_COMPARE_WHOLE) == 1) { + *removed = knot_changeset_remove_rr( + changeset->remove, + &changeset->remove_count, i); + dbg_ddns_detail("Removed RRSet from chgset:\n"); + knot_rrset_dump(*removed, 0); + break; + } + } +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_rr_is_nsec3(const knot_rrset_t *rr) +{ + assert(rr != NULL); + + if ((knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3) + || (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG + && knot_rrset_rdata(rr) + && knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr)) + == KNOT_RRTYPE_NSEC3)) + { + dbg_ddns_detail("This is NSEC3-related RRSet.\n"); + return 1; + } else { + return 0; + } +} + +/*----------------------------------------------------------------------------*/ +/*! \note Copied from xfrin_add_new_node(). */ +static knot_node_t *knot_ddns_add_new_node(knot_zone_contents_t *zone, + knot_dname_t *owner, int is_nsec3) +{ + assert(zone != NULL); + assert(owner != NULL); + + knot_node_t *node = knot_node_new(owner, NULL, 0); + if (node == NULL) { + dbg_xfrin("Failed to create a new node.\n"); + return NULL; + } + + int ret = 0; + + // insert the node into zone structures and create parents if + // necessary + if (is_nsec3) { + ret = knot_zone_contents_add_nsec3_node(zone, node, 1, 0, 1); + } else { + ret = knot_zone_contents_add_node(zone, node, 1, 0, 1); + } + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to add new node to zone contents.\n"); + knot_node_free(&node); + return NULL; + } + + /*! + * \note It is not needed to set the previous node, we will do this + * in adjusting after the transfer. + */ + assert(zone->zone != NULL); + //knot_node_set_zone(node, zone->zone); + assert(node->zone == zone->zone); + + return node; +} + +/*----------------------------------------------------------------------------*/ + +static knot_node_t *knot_ddns_get_node(knot_zone_contents_t *zone, + const knot_rrset_t *rr) +{ + assert(zone != NULL); + assert(rr != NULL); + + knot_node_t *node = NULL; + knot_dname_t *owner = knot_rrset_get_owner(rr); + + dbg_ddns_detail("Searching for node...\n"); + if (knot_ddns_rr_is_nsec3(rr)) { + node = knot_zone_contents_get_nsec3_node(zone, owner); + } else { + node = knot_zone_contents_get_node(zone, owner); + } + + return node; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_process_add_cname(knot_node_t *node, + const knot_rrset_t *rr, + knot_changeset_t *changeset, + knot_changes_t *changes) +{ + assert(node != NULL); + assert(rr != NULL); + assert(changeset != NULL); + assert(changes != NULL); + + dbg_ddns_detail("Adding CNAME RR.\n"); + + int ret = 0; + + /* Get the current CNAME RR from the node. */ + knot_rrset_t *removed = knot_node_get_rrset(node, KNOT_RRTYPE_CNAME); + + if (removed != NULL) { + /* If they are identical, ignore. */ + if (knot_rrset_match(removed, rr, KNOT_RRSET_COMPARE_WHOLE) + == 1) { + dbg_ddns_verb("CNAME identical to one in the node.\n"); + return 1; + } + + /*! \note + * Together with the removed CNAME we remove also its RRSIGs as + * they would not be valid for the new CNAME anyway. + * + * \todo Document!! + */ + + /* b) Store it to 'changes', together with its RRSIGs. */ + ret = knot_changes_add_old_rrsets(&removed, 1, changes, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add removed RRSet to " + "'changes': %s\n", knot_strerror(ret)); + return ret; + } + + /* c) And remove it from the node. */ + (void)knot_node_remove_rrset(node, KNOT_RRTYPE_CNAME); + + /* d) Check if this CNAME was not previously added by + * the UPDATE. If yes, remove it from the ADD + * section and do not add it to the REMOVE section. + */ + knot_rrset_t **from_chgset = NULL; + size_t from_chgset_count = 0; + ret = knot_ddns_check_remove_rr2( + changeset, knot_rrset_owner(removed), + KNOT_RRTYPE_CNAME, knot_rrset_rdata(removed), + &from_chgset, &from_chgset_count); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to remove possible redundant " + "RRs from ADD section: %s.\n", + knot_strerror(ret)); + free(from_chgset); + return ret; + } + + assert(from_chgset_count <= 1); + + if (from_chgset_count == 1) { + /* Just delete the RRSet. */ + knot_rrset_deep_free(&(from_chgset[0]), 1, 1, 1); + /* Okay, &(from_chgset[0]) is basically equal to just + * from_chgset, but it's more clear this way that we are + * deleting the first RRSet in the array ;-) + */ + } else { + /* Otherwise copy the removed CNAME and add it + * to the REMOVE section. + */ + knot_rrset_t *removed_copy; + ret = knot_rrset_deep_copy(removed, + &removed_copy, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy removed RRSet:" + " %s\n", knot_strerror(ret)); + free(from_chgset); + return ret; + } + + ret = knot_changeset_add_rrset( + &changeset->remove, + &changeset->remove_count, + &changeset->remove_allocated, + removed_copy); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&removed_copy, + 1, 1, 1); + dbg_ddns("Failed to add removed CNAME " + "to changeset: %s\n", + knot_strerror(ret)); + free(from_chgset); + return ret; + } + } + free(from_chgset); + } else if (knot_node_rrset_count(node) != 0) { + /* 2) Other occupied node => ignore. */ + return 1; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_process_add_soa(knot_node_t *node, + const knot_rrset_t *rr, + knot_changes_t *changes) +{ + assert(node != NULL); + assert(rr != NULL); + assert(changes != NULL); + + dbg_ddns_detail("Adding SOA RR.\n"); + + int ret = 0; + + /* + * Just remove the SOA from the node, together with its RRSIGs. + * Adding the RR is done in the caller function. Note that only SOA + * with larger SERIAL than the current one will get to these functions, + * so we don't have to check the SERIALS again. But an assert won't + * hurt. + */ + + /* Get the current SOA RR from the node. */ + knot_rrset_t *removed = knot_node_get_rrset(node, KNOT_RRTYPE_SOA); + + if (removed != NULL) { + dbg_ddns_detail("Found SOA in the node.\n"); + /* If they are identical, ignore. */ + if (knot_rrset_match(removed, rr, KNOT_RRSET_COMPARE_WHOLE) + == 1) { + dbg_ddns_detail("Old and new SOA identical.\n"); + return 1; + } + + /* Check that the serial is indeed larger than the current one*/ + assert(ns_serial_compare(knot_rdata_soa_serial( + knot_rrset_rdata(removed)), + knot_rdata_soa_serial( + knot_rrset_rdata(rr))) < 0); + + /* 1) Store it to 'changes', together with its RRSIGs. */ + ret = knot_changes_add_old_rrsets( + &removed, 1, changes, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add removed RRSet to " + "'changes': %s\n", knot_strerror(ret)); + return ret; + } + + /* 2) And remove it from the node. */ + (void)knot_node_remove_rrset(node, KNOT_RRTYPE_SOA); + + /* No changeset processing needed in this case. */ + } else { + dbg_ddns_detail("No SOA in node, ignoring.\n"); + /* If there is no SOA in the node, ignore. */ + return 1; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_add_rr_new_normal(knot_node_t *node, knot_rrset_t *rr_copy, + knot_changes_t *changes) +{ + assert(node != NULL); + assert(rr_copy != NULL); + assert(changes != NULL); + + dbg_ddns_verb("Adding normal RR.\n"); + + /* Add the RRSet to 'changes'. */ + int ret = knot_changes_add_new_rrsets(&rr_copy, 1, changes, 1); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&rr_copy, 1, 1, 1); + dbg_ddns("Failed to store copy of the added RR: " + "%s\n", knot_strerror(ret)); + return ret; + } + + /* Add the RRSet to the node. */ + ret = knot_node_add_rrset(node, rr_copy, 0); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add RR to node: %s\n", knot_strerror(ret)); + return ret; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_add_rr_new_rrsig(knot_node_t *node, knot_rrset_t *rr_copy, + knot_changes_t *changes, + uint16_t type_covered) +{ + assert(node != NULL); + assert(rr_copy != NULL); + assert(changes != NULL); + + dbg_ddns_verb("Adding RRSIG RR.\n"); + + /* Create RRSet to be covered by the RRSIG. */ + knot_rrset_t *covered_rrset = knot_rrset_new( + knot_rrset_get_owner(rr_copy), type_covered, + knot_rrset_class(rr_copy), + knot_rrset_ttl(rr_copy)); + if (covered_rrset == NULL) { + dbg_ddns("Failed to create RRSet to be covered" + " by the UPDATE RRSIG RR.\n"); + knot_rrset_deep_free(&rr_copy, 1, 1, 1); + return KNOT_ENOMEM; + } + + /* Add the RRSet to the node. */ + int ret = knot_node_add_rrset(node, covered_rrset, 0); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add the RRSet to be covered to the node: %s" + ".\n", knot_strerror(ret)); + knot_rrset_deep_free(&rr_copy, 1, 1, 1); + knot_rrset_deep_free(&covered_rrset, 1, 1, 1); + return KNOT_ENOMEM; + } + + /* Add the RRSet to 'changes'. */ + ret = knot_changes_add_new_rrsets(&covered_rrset, 1, changes, 0); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to add new RRSet (covered) to list: %s.\n", + knot_strerror(ret)); + knot_rrset_deep_free(&rr_copy, 1, 1, 1); + knot_rrset_deep_free(&covered_rrset, 1, 1, 1); + return ret; + } + + /* Add the RRSIG RRSet to 'changes'. */ + ret = knot_changes_add_new_rrsets(&rr_copy, 1, changes, 1); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&rr_copy, 1, 1, 1); + dbg_ddns("Failed to store copy of the added RRSIG: %s\n", + knot_strerror(ret)); + return ret; + } + + /* Add the RRSIG RRSet to the covered RRSet. */ + ret = knot_rrset_add_rrsigs(covered_rrset, rr_copy, + KNOT_RRSET_DUPL_SKIP); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add RRSIG RR to the covered RRSet.\n"); + return ret; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_add_rr_merge_normal(knot_rrset_t *node_rrset_copy, + knot_rrset_t **rr_copy) +{ + assert(node_rrset_copy != NULL); + assert(rr_copy != NULL); + assert(*rr_copy != NULL); + + dbg_ddns_verb("Merging normal RR to existing RRSet.\n"); + + /* In case the RRSet is empty (and only remained there because + * of the RRSIGs) it may happen that the TTL may be different + * than that of he new RRs. Update the TTL according to the + * first RR. + */ + if (knot_rrset_rdata(node_rrset_copy) == NULL + && knot_rrset_ttl(node_rrset_copy) + != knot_rrset_ttl(*rr_copy)) { + knot_rrset_set_ttl(node_rrset_copy, + knot_rrset_ttl(*rr_copy)); + } + + int rdata_in_copy = knot_rrset_rdata_rr_count(*rr_copy); + int ret = knot_rrset_merge_no_dupl((void **)&node_rrset_copy, + (void **)rr_copy); + dbg_ddns_detail("Merge returned: %d\n", ret); + + if (ret < 0) { + dbg_ddns("Failed to merge UPDATE RR to node RRSet: %s." + "\n", knot_strerror(ret)); + return ret; + } + + knot_rrset_free(rr_copy); + + if (rdata_in_copy == ret) { + /* All RDATA have been removed, because they were duplicates + * or there were none (0). In general this means, that no + * change was made. + */ + return 1; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_add_rr_merge_rrsig(knot_rrset_t *node_rrset_copy, + knot_rrset_t **rr_copy, + knot_changes_t *changes) +{ + assert(node_rrset_copy != NULL); + assert(rr_copy != NULL); + assert(*rr_copy != NULL); + assert(changes != NULL); + + dbg_ddns_verb("Adding RRSIG RR to existing RRSet.\n"); + + knot_rrset_t *rrsigs_old = knot_rrset_get_rrsigs(node_rrset_copy); + int ret = 0; + + if (rrsigs_old != NULL) { + /* If there is an RRSIG RRSet already, copy it too. */ + knot_rrset_t *rrsigs_copy = NULL; + ret = xfrin_copy_old_rrset(rrsigs_old, &rrsigs_copy, + changes, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy RRSIG RRSet: " + "%s\n", knot_strerror(ret)); + return ret; + } + + /* Replace the RRSIGs by the copy. */ + ret = knot_rrset_set_rrsigs(node_rrset_copy, rrsigs_copy); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to replace RRSIGs in " + "the RRSet: %s\n", + knot_strerror(ret)); + return ret; + } + + /* Merge the UPDATE RR to the copied RRSIG + * RRSet. + */ + dbg_ddns_detail("Merging RRSIG to the one in the RRSet.\n"); + + int rdata_in_copy = knot_rrset_rdata_rr_count(*rr_copy); + ret = knot_rrset_merge_no_dupl( + (void **)&rrsigs_copy, (void **)rr_copy); + if (ret < 0) { + dbg_xfrin("Failed to merge UPDATE RRSIG to copy: %s.\n", + knot_strerror(ret)); + return KNOT_ERROR; + } + + knot_rrset_free(rr_copy); + + if (rdata_in_copy == ret) { + /* All RDATA have been removed, because they were + * duplicates or there were none (0). In general this + * means, that no change was made. + */ + return 1; + } + } else { + /* If there is no RRSIG RRSet yet, just add the + * UPDATE RR to the copied covered RRSet. + */ + /* Add the RRSet to 'changes'. */ + ret = knot_changes_add_new_rrsets(rr_copy, 1, changes, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to store copy of the added RR: %s\n", + knot_strerror(ret)); + return ret; + } + + /* Add the RRSet to the covered RRSet. */ + ret = knot_rrset_add_rrsigs(node_rrset_copy, *rr_copy, + KNOT_RRSET_DUPL_SKIP); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add RRSIG RR to the" + " covered RRSet.\n"); + return ret; + } + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ +/*! + * \todo We should check, how it's possible that IXFR is not leaking due to the + * same issue with merge. Or maybe it is, we should try it!! + */ + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_add_rr(knot_node_t *node, const knot_rrset_t *rr, + knot_changes_t *changes, knot_rrset_t **rr_copy) +{ + assert(node != NULL); + assert(rr != NULL); + assert(changes != NULL); + assert(rr_copy != NULL); + + /* Copy the RRSet from the packet. */ + //knot_rrset_t *rr_copy; + int ret = knot_rrset_deep_copy(rr, rr_copy, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy RR: %s\n", knot_strerror(ret)); + return ret; + } + + uint16_t type = knot_rrset_type(rr); + uint16_t type_covered = (type == KNOT_RRTYPE_RRSIG) + ? knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr)) + : type; + + /* If the RR belongs to a RRSet already present in the node, we must + * take this RRSet from the node, copy it, and merge this RR into it. + * + * This code is more or less copied from xfr-in.c. + */ + knot_rrset_t *node_rrset_copy = NULL; + ret = xfrin_copy_rrset(node, type_covered, &node_rrset_copy, changes, + 0); + + if (node_rrset_copy == NULL) { + /* No such RRSet in the node. Add the whole UPDATE RRSet. */ + dbg_ddns_detail("Adding whole UPDATE RR to the zone.\n"); + if (type_covered != type) { + /* Adding RRSIG. */ + ret = knot_ddns_add_rr_new_rrsig(node, *rr_copy, + changes, type_covered); + } else { + ret = knot_ddns_add_rr_new_normal(node, *rr_copy, + changes); + } + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add new RR to node.\n"); + return ret; + } + } else { + /* We have copied the RRSet from the node. */ +dbg_ddns_exec_detail( + dbg_ddns_detail("Merging RR to an existing RRSet.\n"); + knot_rrset_dump(node_rrset_copy, 1); + dbg_ddns_detail("New RR:\n"); + knot_rrset_dump(*rr_copy, 0); +); + + if (type_covered != type) { + /* Adding RRSIG. */ + ret = knot_ddns_add_rr_merge_rrsig(node_rrset_copy, + rr_copy, changes); + } else { + ret = knot_ddns_add_rr_merge_normal(node_rrset_copy, + rr_copy); + } + +dbg_ddns_exec_detail( + dbg_ddns_detail("After merge:\n"); + knot_rrset_dump(node_rrset_copy, 1); +); + + if (ret < KNOT_EOK) { + dbg_ddns("Failed to merge UPDATE RR to node RRSet.\n"); + knot_rrset_deep_free(rr_copy, 1, 1, 1); + knot_rrset_deep_free(&node_rrset_copy, 1, 1, 1); + return ret; + } + + // save the new RRSet together with the new RDATA to 'changes' + // do not overwrite 'ret', it have to be returned + int r = knot_changes_add_new_rrsets(&node_rrset_copy, 1, + changes, 1); + if (r != KNOT_EOK) { + dbg_ddns("Failed to store RRSet copy to 'changes'\n"); + knot_rrset_deep_free(&node_rrset_copy, 1, 1, 1); + return r; + } + } + + assert(ret >= 0); + return ret; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_final_soa_to_chgset(const knot_rrset_t *soa, + knot_changeset_t *changeset) +{ + assert(soa != NULL); + assert(changeset != NULL); + + knot_rrset_t *soa_copy = NULL; + int ret = knot_rrset_deep_copy(soa, &soa_copy, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy SOA RR to the changeset: " + "%s\n", knot_strerror(ret)); + return ret; + } + + knot_changeset_store_soa(&changeset->soa_to, + &changeset->serial_to, + soa_copy); + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_add_rr_to_chgset(const knot_rrset_t *rr, + knot_changeset_t *changeset) +{ + assert(rr != NULL); + assert(changeset != NULL); + + int ret = 0; + knot_rrset_t *chgset_rr = NULL; + knot_ddns_check_add_rr(changeset, rr, &chgset_rr); + if (chgset_rr == NULL) { + ret = knot_rrset_deep_copy(rr, &chgset_rr, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy RR to the changeset: " + "%s\n", knot_strerror(ret)); + return ret; + } + /* No such RR in the changeset, add it. */ + ret = knot_changeset_add_rrset(&changeset->add, + &changeset->add_count, + &changeset->add_allocated, + chgset_rr); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&chgset_rr, 1, 1, 1); + dbg_ddns("Failed to add RR to changeset: %s.\n", + knot_strerror(ret)); + return ret; + } + } else { + knot_rrset_deep_free(&chgset_rr, 1, 1, 1); + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_process_add(const knot_rrset_t *rr, + knot_node_t *node, + knot_zone_contents_t *zone, + knot_changeset_t *changeset, + knot_changes_t *changes, + knot_rrset_t **rr_copy) +{ + assert(rr != NULL); + assert(zone != NULL); + assert(changeset != NULL); + assert(changes != NULL); + assert(rr_copy != NULL); + + dbg_ddns_verb("Adding RR.\n"); + + if (node == NULL) { + // create new node, connect it properly to the + // zone nodes + dbg_ddns_detail("Node not found. Creating new.\n"); + node = knot_ddns_add_new_node(zone, knot_rrset_get_owner(rr), + knot_ddns_rr_is_nsec3(rr)); + if (node == NULL) { + dbg_xfrin("Failed to create new node in zone.\n"); + return KNOT_ERROR; + } + } + + uint16_t type = knot_rrset_type(rr); + *rr_copy = NULL; + int ret = 0; + + /* + * First, rule out special cases: CNAME, SOA and adding to CNAME node. + */ + if (type == KNOT_RRTYPE_CNAME) { + /* 1) CNAME */ + ret = knot_ddns_process_add_cname(node, rr, changeset, changes); + } else if (type == KNOT_RRTYPE_SOA) { + /* 2) SOA */ + ret = knot_ddns_process_add_soa(node, rr, changes); + } else if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) { + /* + * Adding RR to CNAME node. Ignore the UPDATE RR. + * + * TODO: This may or may not be according to the RFC, it's quite + * unclear (see 3.4.2.2) + */ + return KNOT_EOK; + } + + if (ret == 1) { + dbg_ddns_detail("Ignoring the added RR.\n"); + // Ignore + return KNOT_EOK; + } else if (ret != KNOT_EOK) { + dbg_ddns_detail("Adding RR failed.\n"); + return ret; + } + + /* + * In all other cases, the RR should just be added to the node. + */ + + /* Add the RRSet to the node (RRSIGs handled in the function). */ + dbg_ddns_detail("Adding RR to the node.\n"); + ret = knot_ddns_add_rr(node, rr, changes, rr_copy); + if (ret < 0) { + dbg_ddns("Failed to add RR to the node.\n"); + return ret; + } + + /* + * If adding SOA, it should not be stored in the changeset. + * (This is done in the calling function, and the SOA is stored in the + * soa_final field.) + */ + if (type == KNOT_RRTYPE_SOA) { + return KNOT_EOK; + } + + /* Add the RR to ADD section of the changeset. */ + /* If the RR was previously removed, do not add it to the + * changeset, and remove the entry from the REMOVE section. + * + * If there was no change (i.e. all RDATA were duplicates), do not add + * the RR to the changeset. + */ + if (ret == KNOT_EOK) { + dbg_ddns_detail("Adding RR to the changeset.\n"); + ret = knot_ddns_add_rr_to_chgset(rr, changeset); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add the UPDATE RR to the changeset." + "\n"); + return ret; + } + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ +/*! + * \todo Geez, this is soooooo long even I don't exactly know what it does... + * Refactor! + */ +static int knot_ddns_process_rem_rr(const knot_rrset_t *rr, + knot_node_t *node, + knot_zone_contents_t *zone, + knot_changeset_t *changeset, + knot_changes_t *changes, uint16_t qclass) +{ + assert(rr != NULL); + assert(node != NULL); + assert(zone != NULL); + assert(changeset != NULL); + assert(changes != NULL); + + uint16_t type = knot_rrset_type(rr); + dbg_ddns_verb("Removing one RR.\n"); + + /* + * When doing changes to RRSets, we must: + * 1) Copy the RRSet (same as in IXFR changeset applying, maybe the + * function xfrin_copy_rrset() may be used for this). + * 2) Remove the RDATA (in this case only one). Check if it is not the + * last NS RR in the zone. + * 3) Store the removed RDATA in 'changes'. + * 4) If the RRSet is empty, remove it and store in 'changes'. + * 5) Check redundant RRs in changeset. + * 6) Store the RRSet containing the one RDATA in the changeset. We may + * use the RRSet from the packet for this - copy it, set CLASS + * and TTL. + * + * Special handling of RRSIGs is required in that the RRSet containing + * them must be copied as well. However, copying of RRSet copies also + * the RRSIGs, so copying the base RRSet is enough for both cases! + */ + + assert(type != KNOT_RRTYPE_SOA); + int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL; + + /* If removing NS from an apex and there is only one NS left, ignore + * this removal right away. We do not have to check if the RRs match: + * - if they don't match, the removal will be ignored + * - if they match, the last NS cannot be removed anyway. + */ + if (is_apex && type == KNOT_RRTYPE_NS + && knot_rrset_rdata_rr_count(knot_node_rrset(node, type)) == 1) { + return KNOT_EOK; + } + + /* + * 1) Copy the RRSet. + */ + uint16_t type_to_copy = (type != KNOT_RRTYPE_RRSIG) ? type + : knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr)); + knot_rrset_t *rrset_copy = NULL; + int ret = xfrin_copy_rrset(node, type_to_copy, &rrset_copy, changes, 1); + if (ret < 0) { + dbg_ddns("Failed to copy RRSet for removal: %s\n", + knot_strerror(ret)); + return ret; + } + + if (rrset_copy == NULL) { + dbg_ddns_verb("RRSet not found.\n"); + return KNOT_EOK; + } + + /* + * Set some variables needed, according to the modified RR type. + */ + + int rdata_count; + knot_rrset_t *to_modify; + if (type == KNOT_RRTYPE_RRSIG) { + rdata_count = knot_rrset_rdata_rr_count( + knot_rrset_rrsigs(rrset_copy)); + to_modify = knot_rrset_get_rrsigs(rrset_copy); + } else { + rdata_count = knot_rrset_rdata_rr_count(rrset_copy); + to_modify = rrset_copy; + } + + /* + * 1.5) Prepare place for the removed RDATA. + * We don't know if there are some, but if this fails, at least we + * haven't removed them yet. + */ + ret = knot_changes_rdata_reserve(&changes->old_rdata, + &changes->old_rdata_types, + changes->old_rdata_count, + &changes->old_rdata_allocated, + rdata_count); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to reserve place for RDATA.\n"); + return ret; + } + + /* + * 2) Remove the proper RDATA from the RRSet copy, or its RRSIGs. + */ + knot_rdata_t *removed = knot_rrset_remove_rdata(to_modify, + knot_rrset_rdata(rr)); + + /* No such RR in the RRSet. */ + if (removed == NULL) { + dbg_ddns_detail("No such RR found to be removed.\n"); + return KNOT_EOK; + } + + /* If we removed NS from apex, there should be at least one more. */ + assert(!is_apex || type != KNOT_RRTYPE_NS + || knot_rrset_rdata(rrset_copy) != NULL); + + /* + * 3) Store the removed RDATA in 'changes'. + */ + knot_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, + &changes->old_rdata_count, removed, type); + + /* + * 4) If the RRSet is empty, remove it and store in 'changes'. + * Do this also if the RRSIGs are empty. + * And if both are empty, remove both. + */ + if (type == KNOT_RRTYPE_RRSIG + && knot_rrset_rdata(to_modify) == NULL) { + /* Empty RRSIGs, remove the RRSIG RRSet */ + ret = knot_changes_rrsets_reserve(&changes->old_rrsets, + &changes->old_rrsets_count, + &changes->old_rrsets_allocated, + 1); + if (ret == KNOT_EOK) { + knot_rrset_t *rrsig = knot_rrset_get_rrsigs(rrset_copy); + dbg_xfrin_detail("Removed RRSIG RRSet (%p).\n", rrsig); + + assert(rrsig == to_modify); + + // add the removed RRSet to list of old RRSets + changes->old_rrsets[changes->old_rrsets_count++] + = rrsig; + + // remove it from the RRSet + knot_rrset_set_rrsigs(rrset_copy, NULL); + } else { + dbg_ddns("Failed to reserve space for empty RRSet.\n"); + } + } + + /*! \note Copied from xfr-in.c - maybe extract to some function. */ + /*! \note This is not needed as rrset is already on the old_rrsets */ +// if (knot_rrset_rdata(rrset_copy) == NULL +// && knot_rrset_rrsigs(rrset_copy) == NULL) { +// // The RRSet should not be empty if we were removing NSs from +// // apex in case of DDNS +// assert(!is_apex); + +// ret = knot_changes_rrsets_reserve(&changes->old_rrsets, +// &changes->old_rrsets_count, +// &changes->old_rrsets_allocated, +// 1); +// if (ret == KNOT_EOK) { +// knot_rrset_t *tmp = knot_node_remove_rrset(node, type); +// dbg_xfrin_detail("Removed whole RRSet (%p).\n", tmp); + +// assert(tmp == rrset_copy); + +// // add the removed RRSet to list of old RRSets +// changes->old_rrsets[changes->old_rrsets_count++] +// = rrset_copy; +// } else { +// dbg_ddns("Failed to reserve space for empty RRSet.\n"); +// } +// } + + /* + * 5) Check if the RR is not in the ADD section. If yes, remove it + * from there and do not add it to the REMOVE section. + */ + knot_rrset_t **from_chgset = NULL; + size_t from_chgset_count = 0; + ret = knot_ddns_check_remove_rr2(changeset, knot_node_owner(node), + type, knot_rrset_rdata(rr), + &from_chgset, &from_chgset_count); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to remove possible redundant RRs from ADD " + "section: %s.\n", knot_strerror(ret)); + free(from_chgset); + return ret; + } + + assert(from_chgset_count <= 1); + + if (from_chgset_count == 1) { + /* Just delete the RRSet. */ + knot_rrset_deep_free(&(from_chgset[0]), 1, 1, 1); + + /* Finish processing, no adding to changeset. */ + free(from_chgset); + return KNOT_EOK; + } + + free(from_chgset); + + /* + * 6) Store the RRSet containing the one RDATA in the changeset. We may + * use the RRSet from the packet for this - copy it, set CLASS + * and TTL. + */ + knot_rrset_t *to_chgset = NULL; + ret = knot_rrset_deep_copy(rr, &to_chgset, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy RRSet from packet to changeset.\n"); + return ret; + } + knot_rrset_set_class(to_chgset, qclass); + knot_rrset_set_ttl(to_chgset, knot_rrset_ttl(to_modify)); + + ret = knot_changeset_add_rrset(&changeset->remove, + &changeset->remove_count, + &changeset->remove_allocated, + to_chgset); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to store the RRSet copy to changeset: %s.\n", + knot_strerror(ret)); + knot_rrset_deep_free(&to_chgset, 1, 1, 1); + return ret; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_process_rem_rrsig(knot_node_t *node, + knot_rrset_t *rrset, + knot_changes_t *changes, + knot_rrset_t **rrsig) +{ + assert(node != NULL); + assert(rrset != NULL); + assert(changes != NULL); + + knot_rrset_t *rrset_copy = NULL; + + /* Copy RRSet. */ + int ret = xfrin_copy_old_rrset(rrset, &rrset_copy, changes, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy RRSet from node: %s.\n", + knot_strerror(ret)); + return ret; + } + + /* Remove RRSIGs from the copy. */ + *rrsig = knot_rrset_get_rrsigs(rrset_copy); + if (*rrsig != NULL) { + knot_rrset_set_rrsigs(rrset_copy, NULL); + } + + /* Put the copy to the node. */ + ret = knot_node_add_rrset(node, rrset_copy, 0); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add RRSet copy to the node: %s\n", + knot_strerror(ret)); + knot_rrset_deep_free(&rrset_copy, 1, 1, 1); + return ret; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_process_rem_rrsigs(knot_node_t *node, + knot_changes_t *changes, + knot_rrset_t ***removed, + size_t *removed_count) +{ + assert(node != NULL); + assert(removed != NULL); + assert(removed_count != NULL); + assert(changes != NULL); + + /* If removing RRSIGs, we must remove them from all RRSets in + * the node. This means to copy all RRSets and then remove the + * RRSIGs from them. + */ + dbg_ddns_verb("Removing all RRSIGs from node.\n"); + + knot_rrset_t **rrsets = knot_node_get_rrsets(node); + if (rrsets == NULL) { + // No RRSets in the node, nothing to remove + return KNOT_EOK; + } + + /* Allocate space for the removed RRSIGs. There may be as many as there + * are RRSets. + */ + short rrset_count = knot_node_rrset_count(node); + + *removed = malloc(rrset_count * sizeof(knot_rrset_t *)); + CHECK_ALLOC_LOG(*removed, KNOT_ENOMEM); + *removed_count = 0; + + /* Remove all the RRSets from the node, so that we may insert the copies + * right away. + */ + knot_node_remove_all_rrsets(node); + + knot_rrset_t *rrsig = NULL; + int ret = 0; + for (int i = 0; i < rrset_count; ++i) { + ret = knot_ddns_process_rem_rrsig(node, rrsets[i], changes, + &rrsig); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to remove RRSIG.\n"); + return ret; + } + /* Store the RRSIGs to the array of removed RRSets. */ + (*removed)[(*removed_count)++] = rrsig; + } + + free(rrsets); + + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_process_rem_rrset(uint16_t type, + knot_node_t *node, + knot_changeset_t *changeset, + knot_changes_t *changes) +{ + assert(node != NULL); + assert(changeset != NULL); + assert(changes != NULL); + + /*! \note + * We decided to automatically remove RRSIGs together with the removed + * RRSet as they are no longer valid or required anyway. + * + * Also refer to RFC3007, section 4.3: + * 'When the contents of an RRset are updated, the server MAY delete + * all associated SIG records, since they will no longer be valid.' + * + * (Although we are compliant with this RFC only selectively. The next + * section says: 'If any changes are made, the server MUST, if + * necessary, generate a new SOA record and new NXT records, and sign + * these with the appropriate zone keys.' and we are definitely not + * doing this... + * + * \todo Document!! + */ + + // this should be ruled out before + assert(type != KNOT_RRTYPE_SOA); + + if (knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL + && type == KNOT_RRTYPE_NS) { + // if removing NS from apex, ignore + return KNOT_EOK; + } + + knot_rrset_t **removed = NULL; + size_t removed_count = 0; + int ret = 0; + + if (type == KNOT_RRTYPE_RRSIG) { + /* Remove all RRSIGs from the node. */ + ret = knot_ddns_process_rem_rrsigs(node, changes, &removed, + &removed_count); + } else { + /* Remove the RRSet from the node. */ + removed = malloc(sizeof(knot_rrset_t *)); + if (!removed) { + ERR_ALLOC_FAILED; + return KNOT_ENOMEM; + } + + dbg_ddns_detail("Removing RRSet of type: %d\n", type); + + *removed = knot_node_remove_rrset(node, type); + removed_count = 1; + } + + dbg_ddns_detail("Removed: %p (first item: %p), removed count: %d\n", + removed, (removed == NULL) ? "none" : *removed, + removed_count); + + // no such RR + if (removed_count == 0 || removed == NULL) { + // ignore + return KNOT_EOK; + } + + /* 2) Store them to 'changes' for later deallocation, together with + * their RRSIGs. + */ + ret = knot_changes_add_old_rrsets(removed, removed_count, changes, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to add removed RRSet to 'changes': %s.\n", + knot_strerror(ret)); + free(removed); + return ret; + } + + /* 3) Copy the RRSets, so that they can be stored to the changeset. */ + knot_rrset_t **to_chgset = malloc(removed_count + * sizeof(knot_rrset_t *)); + if (to_chgset == NULL) { + dbg_ddns("Failed to allocate space for RRSets going to " + "changeset.\n"); + free(removed); + return KNOT_ENOMEM; + } + + for (int i = 0; i < removed_count; ++i) { + ret = knot_rrset_deep_copy(removed[i], &to_chgset[i], 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy the removed RRSet: %s.\n", + knot_strerror(ret)); + for (int j = 0; j < i; ++j) { + knot_rrset_deep_free(&to_chgset[j], 1, 1, 1); + } + free(to_chgset); + free(removed); + return ret; + } + } + + free(removed); + + /* 4) But we must check if some of the RRs were not previously added + * by the same UPDATE. If yes, these must be removed from the ADD + * section of the changeset and also from this RRSet copy (so they + * are neither stored in the REMOVE section of the changeset). + */ + knot_rrset_t **from_chgset = NULL; + size_t from_chgset_count = 0; + + /* 4 a) Remove redundant RRs from the ADD section of the changeset. */ + ret = knot_ddns_check_remove_rr2(changeset, knot_node_owner(node), type, + NULL, &from_chgset, + &from_chgset_count); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to remove possible redundant RRs from ADD " + "section: %s.\n", knot_strerror(ret)); + for (int i = 0; i < removed_count; ++i) { + knot_rrset_deep_free(&to_chgset[i], 1, 1, 1); + } + free(from_chgset); + free(to_chgset); + return ret; + } + + /* 4 b) Remove these RRs from the copy of the RRSets removed from zone*/ + knot_rdata_t *rem = NULL; + for (int j = 0; j < removed_count; ++j) { + /* In each RRSet removed from the node (each can have more + * RDATAs) ... + */ + for (int i = 0; i < from_chgset_count; ++i) { + /* ...try to remove redundant RDATA. Each RRSet in + * 'from_chgset' contains only one RDATA. + */ + rem = knot_rrset_remove_rdata(to_chgset[j], + knot_rrset_rdata( + from_chgset[i])); + /* And delete it right away, no use for that. */ + knot_rdata_deep_free(&rem, knot_rrset_type( + from_chgset[i]), 1); + } + } + + /* The array is cleared, we may delete the redundant RRs. */ + for (int i = 0; i < from_chgset_count; ++i) { + knot_rrset_deep_free(&from_chgset[i], 1, 1, 1); + } + free(from_chgset); + + /* 5) Store the remaining RRSet to the changeset. Do not try to merge + * to some previous RRSet, there should be none. + */ + for (int i = 0; i < removed_count; ++i) { + ret = knot_changeset_add_rrset(&changeset->remove, + &changeset->remove_count, + &changeset->remove_allocated, + to_chgset[i]); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to store the RRSet copy to changeset: " + "%s.\n", knot_strerror(ret)); + for (int j = i; j < removed_count; ++j) { + knot_rrset_deep_free(&to_chgset[j], 1, 1, 1); + } + free(to_chgset); + return ret; + } + } + + free(to_chgset); + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_process_rem_all(knot_node_t *node, + knot_changeset_t *changeset, + knot_changes_t *changes) +{ + assert(node != NULL); + assert(changeset != NULL); + assert(changes != NULL); + + /*! \note + * This basically means to call knot_ddns_process_rem_rrset() for every + * type present in the node. + * + * In case of SOA and NS in apex, the RRSets should not be removed, but + * what about their RRSIGs?? + * + * If the zone has to remain properly signed, the UPDATE will have to + * contain at least new SOA and RRSIGs for it (as the auto-incremented + * SOA would not be signed). So it should not matter if we leave the + * RRSIGs there or not. But in case of the NSs it's not that clear. + * + * For now, we will leave the RRSIGs there. It's easier to implement. + * + * \todo Should document this!! + */ + int ret = 0; + knot_rrset_t **rrsets = knot_node_get_rrsets(node); + int count = knot_node_rrset_count(node); + + if (rrsets == NULL && count != 0) { + dbg_ddns("Failed to fetch RRSets from node.\n"); + return KNOT_ENOMEM; + } + + int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL; + + dbg_ddns_verb("Removing all RRSets (count: %d).\n", count); + for (int i = 0; i < count; ++i) { + // If the node is apex, skip NS and SOA + if (is_apex && + (knot_rrset_type(rrsets[i]) == KNOT_RRTYPE_SOA + || knot_rrset_type(rrsets[i]) == KNOT_RRTYPE_NS)) { + /* Do not remove these RRSets, nor their RRSIGs. */ + continue; + } + + ret = knot_ddns_process_rem_rrset(knot_rrset_type(rrsets[i]), + node, changeset, changes); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to remove RRSet: %s\n", + knot_strerror(ret)); + free(rrsets); + return ret; + } + } + + free(rrsets); + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int knot_ddns_process_rr(const knot_rrset_t *rr, + knot_zone_contents_t *zone, + knot_changeset_t *changeset, + knot_changes_t *changes, uint16_t qclass, + knot_rrset_t **rr_copy) +{ + assert(rr != NULL); + assert(zone != NULL); + assert(changeset != NULL); + assert(changes != NULL); + assert(rr_copy != NULL); + + /* 1) Find node that will be affected. */ + knot_node_t *node = knot_ddns_get_node(zone, rr); + + /* 2) Decide what to do. */ + + if (knot_rrset_class(rr) == knot_zone_contents_class(zone)) { + return knot_ddns_process_add(rr, node, zone, changeset, + changes, rr_copy); + } else if (knot_rrset_class(rr) == KNOT_CLASS_NONE) { + return knot_ddns_process_rem_rr(rr, node, zone, changeset, + changes, qclass); + } else if (knot_rrset_class(rr) == KNOT_CLASS_ANY) { + if (knot_rrset_type(rr) == KNOT_RRTYPE_ANY) { + return knot_ddns_process_rem_all(node, changeset, + changes); + } else { + return knot_ddns_process_rem_rrset(knot_rrset_type(rr), + node, changeset, + changes); + } + } else { + assert(0); + return KNOT_ERROR; + } +} + +/*----------------------------------------------------------------------------*/ +/* + * NOTES: + * - 'zone' must be a copy of the current zone. + * - changeset must be allocated + * - changes must be allocated + * + * All this is done in the first parts of xfrin_apply_changesets() - extract + * to separate function, if possible. + * + * If anything fails, rollback must be done. The xfrin_rollback_update() may + * be good for this. + */ +int knot_ddns_process_update2(knot_zone_contents_t *zone, + const knot_packet_t *query, + knot_changeset_t *changeset, + knot_changes_t *changes, + knot_rcode_t *rcode) +{ + if (zone == NULL || query == NULL || changeset == NULL || rcode == NULL + || changes == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + + /* Copy base SOA RR. */ + const knot_rrset_t *soa = knot_node_rrset(knot_zone_contents_apex(zone), + KNOT_RRTYPE_SOA); + knot_rrset_t *soa_begin = NULL; + knot_rrset_t *soa_end = NULL; + ret = knot_rrset_deep_copy(soa, &soa_begin, 0); + if (ret == KNOT_EOK) { + knot_changeset_store_soa(&changeset->soa_from, + &changeset->serial_from, soa_begin); + } else { + *rcode = KNOT_RCODE_SERVFAIL; + return ret; + } + + /* Current SERIAL */ + int64_t sn = knot_rdata_soa_serial(knot_rrset_rdata(soa_begin)); + int64_t sn_new; + + /* Incremented SERIAL + * We must set it now to be able to compare SERIAL from SOAs in the + * UPDATE to it. Although we do not have the new SOA yet. + */ + if (sn > -1) { + sn_new = (uint32_t)sn + 1; + } else { + *rcode = KNOT_RCODE_SERVFAIL; + return ret; + } + + /* Process all RRs the Authority (Update) section. */ + + const knot_rrset_t *rr = NULL; + knot_rrset_t *rr_copy = NULL; + + dbg_ddns("Processing UPDATE section.\n"); + for (int i = 0; i < knot_packet_authority_rrset_count(query); ++i) { + + rr = knot_packet_authority_rrset(query, i); + + /* Check if the entry is correct. */ + ret = knot_ddns_check_update(rr, query, rcode); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to check update RRSet:%s\n", + knot_strerror(ret)); + return ret; + } + + /* Check if the record is SOA. If yes, check the SERIAL. + * If this record should cause the SOA to be replaced in the + * zone, use it as the ending SOA. + * + * Also handle cases where there are multiple SOAs to be added + * in the same UPDATE. The one with the largest SERIAL should + * be used. + * + * TODO: If there are more SOAs in the UPDATE one after another, + * the ddns_add_update() function will merge them into a + * RRSet. This should be handled somehow. + * + * If the serial is not larger than the current zone serial, + * ignore the record and continue. This will ensure that the + * RR processing function receives only SOA RRs that should be + * added to the zone (replacing the old one). + */ + if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA + && (knot_rrset_class(rr) == KNOT_CLASS_NONE + || knot_rrset_class(rr) == KNOT_CLASS_ANY + || ns_serial_compare(knot_rdata_soa_serial( + knot_rrset_rdata(rr)), sn_new) < 0)) { + // This ignores also SOA removals + dbg_ddns_verb("Ignoring SOA...\n"); + continue; + } + + ret = knot_ddns_process_rr(rr, zone, changeset, changes, + knot_packet_qclass(query), + &rr_copy); + + if (ret != KNOT_EOK) { + dbg_ddns("Failed to process update RR:%s\n", + knot_strerror(ret)); + *rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR + : KNOT_RCODE_SERVFAIL; + return ret; + } + + // we need the RR copy, that's why this code is here + if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) { + int64_t sn_rr = knot_rdata_soa_serial( + knot_rrset_rdata(rr)); + dbg_ddns_verb("Replacing SOA. Old serial: %d, new " + "serial: %d\n", sn_new, sn_rr); + assert(ns_serial_compare(sn_rr, sn_new) >= 0); + assert(rr_copy != NULL); + sn_new = sn_rr; + soa_end = (knot_rrset_t *)rr_copy; + } + } + + /* Ending SOA (not in the UPDATE) */ + if (soa_end == NULL) { + /* If the changeset is empty, do not process anything further + * and indicate this to the caller, so that the changeset is not + * saved and zone is not switched. + */ + if (knot_changeset_is_empty(changeset)) { + return 1; + } + + /* If not set, create new SOA. */ + assert(sn_new == (uint32_t)sn + 1); + ret = knot_rrset_deep_copy(soa, &soa_end, 1); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy ending SOA: %s\n", + knot_strerror(ret)); + *rcode = KNOT_RCODE_SERVFAIL; + return ret; + } + knot_rdata_t *rd = knot_rrset_get_rdata(soa_end); + knot_rdata_soa_serial_set(rd, sn_new); + + /* And replace it in the zone. */ + ret = xfrin_replace_rrset_in_node( + knot_zone_contents_get_apex(zone), + soa_end, changes, zone); + if (ret != KNOT_EOK) { + dbg_ddns("Failed to copy replace SOA in zone: %s\n", + knot_strerror(ret)); + *rcode = KNOT_RCODE_SERVFAIL; + return ret; + } + } + + ret = knot_ddns_final_soa_to_chgset(soa_end, changeset); + + return ret; +} diff --git a/src/libknot/updates/ddns.h b/src/libknot/updates/ddns.h index 35dfcb7..d3cb359 100644 --- a/src/libknot/updates/ddns.h +++ b/src/libknot/updates/ddns.h @@ -32,6 +32,8 @@ #include "packet/packet.h" #include "rrset.h" #include "dname.h" +#include "consts.h" +#include "common/lists.h" typedef struct knot_ddns_prereq_t { knot_rrset_t **exist; @@ -55,17 +57,24 @@ typedef struct knot_ddns_prereq_t { size_t not_in_use_allocd; } knot_ddns_prereq_t; -int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query, - uint8_t *rcode); +int knot_ddns_check_zone(const knot_zone_contents_t *zone, + const knot_packet_t *query, knot_rcode_t *rcode); -int knot_ddns_process_prereqs(knot_packet_t *query, - knot_ddns_prereq_t **prereqs, uint8_t *rcode); +int knot_ddns_process_prereqs(const knot_packet_t *query, + knot_ddns_prereq_t **prereqs, knot_rcode_t *rcode); int knot_ddns_check_prereqs(const knot_zone_contents_t *zone, - knot_ddns_prereq_t **prereqs, uint8_t *rcode); + knot_ddns_prereq_t **prereqs, knot_rcode_t *rcode); -int knot_ddns_process_update(knot_packet_t *query, - knot_changeset_t **changeset, uint8_t *rcode); +int knot_ddns_process_update(const knot_zone_contents_t *zone, + const knot_packet_t *query, + knot_changeset_t *changeset, knot_rcode_t *rcode); + +int knot_ddns_process_update2(knot_zone_contents_t *zone, + const knot_packet_t *query, + knot_changeset_t *changeset, + knot_changes_t *changes, + knot_rcode_t *rcode); void knot_ddns_prereqs_free(knot_ddns_prereq_t **prereq); diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c index 7d3ffdf..98d4df6 100644 --- a/src/libknot/updates/xfr-in.c +++ b/src/libknot/updates/xfr-in.c @@ -267,14 +267,14 @@ static int xfrin_add_orphan_rrsig(xfrin_orphan_rrsig_t **rrsigs, while (last != NULL) { // check if the RRSIG is not similar to the one we want to add assert(last->rrsig != NULL); - if (knot_rrset_compare(last->rrsig, rr, + if (knot_rrset_match(last->rrsig, rr, KNOT_RRSET_COMPARE_HEADER) == 1 && knot_rdata_rrsig_type_covered(knot_rrset_rdata( last->rrsig)) == knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr))) { ret = knot_rrset_merge_no_dupl((void **)&last->rrsig, (void **)&rr); - if (ret != KNOT_EOK) { + if (ret < 0) { return ret; } else { return 1; @@ -955,13 +955,13 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr) if (*chs == NULL) { dbg_xfrin_verb("Changesets empty, creating new.\n"); - ret = knot_changeset_allocate(chs); + ret = knot_changeset_allocate(chs, KNOT_CHANGESET_TYPE_IXFR); if (ret != KNOT_EOK) { knot_rrset_deep_free(&rr, 1, 1, 1); knot_packet_free(&packet); return ret; } - + // the first RR must be a SOA if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) { dbg_xfrin("First RR is not a SOA RR!\n"); @@ -1069,9 +1069,9 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr) assert((*chs)->sets[(*chs)->count - 1].soa_from != NULL); if ((*chs)->sets[(*chs)->count - 1].soa_to == NULL) { - state = XFRIN_CHANGESET_REMOVE; + state = KNOT_CHANGESET_REMOVE; } else { - state = XFRIN_CHANGESET_ADD; + state = KNOT_CHANGESET_ADD; } } @@ -1134,17 +1134,17 @@ dbg_xfrin_exec_verb( ret = knot_changeset_add_soa( &(*chs)->sets[(*chs)->count - 1], rr, - XFRIN_CHANGESET_REMOVE); + KNOT_CHANGESET_REMOVE); if (ret != KNOT_EOK) { knot_rrset_deep_free(&rr, 1, 1, 1); goto cleanup; } // change state to REMOVE - state = XFRIN_CHANGESET_REMOVE; + state = KNOT_CHANGESET_REMOVE; } break; - case XFRIN_CHANGESET_REMOVE: + case KNOT_CHANGESET_REMOVE: // if the next RR is SOA, store it and change state to // ADD if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) { @@ -1154,25 +1154,25 @@ dbg_xfrin_exec_verb( ret = knot_changeset_add_soa( &(*chs)->sets[(*chs)->count - 1], rr, - XFRIN_CHANGESET_ADD); + KNOT_CHANGESET_ADD); if (ret != KNOT_EOK) { knot_rrset_deep_free(&rr, 1, 1, 1); goto cleanup; } - state = XFRIN_CHANGESET_ADD; + state = KNOT_CHANGESET_ADD; } else { // just add the RR to the REMOVE part and // continue if ((ret = knot_changeset_add_new_rr( &(*chs)->sets[(*chs)->count - 1], rr, - XFRIN_CHANGESET_REMOVE)) != KNOT_EOK) { + KNOT_CHANGESET_REMOVE)) != KNOT_EOK) { knot_rrset_deep_free(&rr, 1, 1, 1); goto cleanup; } } break; - case XFRIN_CHANGESET_ADD: + case KNOT_CHANGESET_ADD: // if the next RR is SOA change to state -1 and do not // parse next RR if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) { @@ -1183,7 +1183,7 @@ dbg_xfrin_exec_verb( // just add the RR to the ADD part and continue if ((ret = knot_changeset_add_new_rr( &(*chs)->sets[(*chs)->count - 1], rr, - XFRIN_CHANGESET_ADD)) != KNOT_EOK) { + KNOT_CHANGESET_ADD)) != KNOT_EOK) { knot_rrset_deep_free(&rr, 1, 1, 1); goto cleanup; } @@ -1230,137 +1230,6 @@ cleanup: /* Applying changesets to zone */ /*----------------------------------------------------------------------------*/ -static int xfrin_changes_check_rrsets(knot_rrset_t ***rrsets, - int *count, int *allocated, int to_add) -{ - if (*count + to_add <= *allocated) { - return KNOT_EOK; - } - - int new_count = (*allocated == 0) ? 2 : *allocated * 2; - while (new_count < *count + to_add) { - new_count *= 2; - } - - /* Allocate new memory block. */ - knot_rrset_t **rrsets_new = malloc(new_count * sizeof(knot_rrset_t *)); - if (rrsets_new == NULL) { - return KNOT_ENOMEM; - } - - /* Initialize new memory and copy old data. */ - memset(rrsets_new, 0, new_count * sizeof(knot_rrset_t *)); - memcpy(rrsets_new, *rrsets, (*allocated) * sizeof(knot_rrset_t *)); - - /* Free old nodes and switch pointers. */ - free(*rrsets); - *rrsets = rrsets_new; - *allocated = new_count; - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -static int xfrin_changes_check_nodes(knot_node_t ***nodes, - int *count, int *allocated) -{ - assert(nodes != NULL); - assert(count != NULL); - assert(allocated != 0); - - if (*count + 2 <= *allocated) { - return KNOT_EOK; - } - - int new_count = (*allocated == 0) ? 2 : *allocated * 2; - - /* Allocate new memory block. */ - const size_t node_len = sizeof(knot_node_t *); - knot_node_t **nodes_new = malloc(new_count * node_len); - if (nodes_new == NULL) { - return KNOT_ENOMEM; - } - - /* Clear memory block and copy old data. */ - memset(nodes_new, 0, new_count * node_len); - memcpy(nodes_new, *nodes, (*allocated) * node_len); - - /* Free old nodes and switch pointers. */ - free(*nodes); - *nodes = nodes_new; - *allocated = new_count; - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -static int xfrin_changes_check_rdata(knot_rdata_t ***rdatas, uint **types, - int count, int *allocated, int to_add) -{ - if (count + to_add <= *allocated) { - return KNOT_EOK; - } - - int new_count = (*allocated == 0) ? 2 : *allocated * 2; - while (new_count < count + to_add) { - new_count *= 2; - } - - /* Allocate new memory block. */ - knot_rdata_t **rdatas_new = malloc(new_count * sizeof(knot_rdata_t *)); - if (rdatas_new == NULL) { - return KNOT_ENOMEM; - } - - uint *types_new = malloc(new_count * sizeof(uint)); - if (types_new == NULL) { - free(rdatas_new); - return KNOT_ENOMEM; - } - - /* Initialize new memory and copy old data. */ - memset(rdatas_new, 0, new_count * sizeof(knot_rdata_t *)); - memcpy(rdatas_new, *rdatas, (*allocated) * sizeof(knot_rdata_t *)); - - memset(types_new, 0, new_count * sizeof(uint)); - memcpy(types_new, *types, (*allocated) * sizeof(uint)); - - /* Free old rdatas and switch pointers. */ - free(*rdatas); - free(*types); - *rdatas = rdatas_new; - *types = types_new; - *allocated = new_count; - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -static void xfrin_changes_add_rdata(knot_rdata_t **rdatas, uint *types, - int *count, knot_rdata_t *rdata, uint type) -{ - if (rdata == NULL) { - return; - } - - // Add all RDATAs from the chain!! - - knot_rdata_t *r = rdata; - do { - dbg_xfrin_detail("Adding RDATA to RDATA list: %p\n", r); - rdatas[*count] = r; - types[*count] = type; - ++*count; - - r = r->next; - } while (r != NULL && r != rdata); -} - -/*----------------------------------------------------------------------------*/ - static void xfrin_zone_contents_free(knot_zone_contents_t **contents) { /*! \todo This should be all in some API!! */ @@ -1386,7 +1255,8 @@ static void xfrin_zone_contents_free(knot_zone_contents_t **contents) /*----------------------------------------------------------------------------*/ static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from, - const knot_rrset_t *what) + const knot_rrset_t *what, + int ddns_check) { knot_rdata_t *old = NULL; knot_rdata_t *old_actual = NULL; @@ -1396,6 +1266,20 @@ static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from, while (rdata != NULL) { // rdata - the RDATA to be removed // old_actual - removed RDATA + + /* + * DDNS special handling - last apex NS should remain in the + * zone. + * + * TODO: this is not correct, the last NS from the 'what' RRSet + * may not even be in the zone. + */ + if (ddns_check + && knot_rrset_rdata_next(what, rdata) == NULL) { + assert(knot_rrset_type(from) == KNOT_RRTYPE_NS); + return old; + } + old_actual = knot_rrset_remove_rdata(from, rdata); if (old_actual != NULL) { old_actual->next = old; @@ -1410,8 +1294,8 @@ static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from, /*----------------------------------------------------------------------------*/ -static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, - knot_changes_t *changes) +int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, + knot_changes_t *changes, int save_new) { dbg_xfrin_detail("Copying old RRSet: %p\n", old); // create new RRSet by copying the old one @@ -1420,57 +1304,65 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, dbg_xfrin("Failed to create RRSet copy.\n"); return KNOT_ENOMEM; } + + int count = 0; // add the RRSet to the list of new RRSets // create place also for RRSIGs - ret = xfrin_changes_check_rrsets(&changes->new_rrsets, - &changes->new_rrsets_count, - &changes->new_rrsets_allocated, 2); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to add new RRSet to list.\n"); - knot_rrset_deep_free(copy, 1, 1, 1); - return ret; - } - - int count = knot_rrset_rdata_rr_count(*copy); - count += knot_rrset_rdata_rr_count((*copy)->rrsigs); - - // add the copied RDATA to the list of new RDATA - ret = xfrin_changes_check_rdata(&changes->new_rdata, - &changes->new_rdata_types, - changes->new_rdata_count, - &changes->new_rdata_allocated, count); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to add new RRSet to list.\n"); - knot_rrset_deep_free(copy, 1, 1, 1); - return ret; - } - - changes->new_rrsets[changes->new_rrsets_count++] = *copy; + if (save_new) { + ret = knot_changes_rrsets_reserve(&changes->new_rrsets, + &changes->new_rrsets_count, + &changes->new_rrsets_allocated, + 2); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to add new RRSet to list.\n"); + knot_rrset_deep_free(copy, 1, 1, 1); + return ret; + } - dbg_xfrin_detail("Adding RDATA from the RRSet copy to new RDATA list." - "\n"); - xfrin_changes_add_rdata(changes->new_rdata, changes->new_rdata_types, - &changes->new_rdata_count, - knot_rrset_get_rdata(*copy), - knot_rrset_type(*copy)); + count = knot_rrset_rdata_rr_count(*copy); + count += knot_rrset_rdata_rr_count((*copy)->rrsigs); + + // add the copied RDATA to the list of new RDATA + ret = knot_changes_rdata_reserve(&changes->new_rdata, + &changes->new_rdata_types, + changes->new_rdata_count, + &changes->new_rdata_allocated, + count); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to add new RRSet to list.\n"); + knot_rrset_deep_free(copy, 1, 1, 1); + return ret; + } + + changes->new_rrsets[changes->new_rrsets_count++] = *copy; + + dbg_xfrin_detail("Adding RDATA from the RRSet copy to new RDATA list." + "\n"); + knot_changes_add_rdata(changes->new_rdata, + changes->new_rdata_types, + &changes->new_rdata_count, + knot_rrset_get_rdata(*copy), + knot_rrset_type(*copy)); - if ((*copy)->rrsigs != NULL) { - assert(old->rrsigs != NULL); - changes->new_rrsets[changes->new_rrsets_count++] = - (*copy)->rrsigs; - - dbg_xfrin_detail("Adding RDATA from RRSIG of the RRSet copy to " - "new RDATA list.\n"); - xfrin_changes_add_rdata(changes->new_rdata, - changes->new_rdata_types, - &changes->new_rdata_count, - knot_rrset_get_rdata((*copy)->rrsigs), - KNOT_RRTYPE_RRSIG); + if ((*copy)->rrsigs != NULL) { + assert(old->rrsigs != NULL); + changes->new_rrsets[changes->new_rrsets_count++] = + (*copy)->rrsigs; + + dbg_xfrin_detail("Adding RDATA from RRSIG of the RRSet copy to " + "new RDATA list.\n"); + knot_changes_add_rdata(changes->new_rdata, + changes->new_rdata_types, + &changes->new_rdata_count, + knot_rrset_get_rdata( + (*copy)->rrsigs), + KNOT_RRTYPE_RRSIG); + } } // add the old RRSet to the list of old RRSets - ret = xfrin_changes_check_rrsets(&changes->old_rrsets, + ret = knot_changes_rrsets_reserve(&changes->old_rrsets, &changes->old_rrsets_count, &changes->old_rrsets_allocated, 2); if (ret != KNOT_EOK) { @@ -1482,7 +1374,7 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, count += knot_rrset_rdata_rr_count(old->rrsigs); // and old RDATA to the list of old RDATA - ret = xfrin_changes_check_rdata(&changes->old_rdata, + ret = knot_changes_rdata_reserve(&changes->old_rdata, &changes->old_rdata_types, changes->old_rdata_count, &changes->old_rdata_allocated, count); @@ -1494,7 +1386,7 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, changes->old_rrsets[changes->old_rrsets_count++] = old; dbg_xfrin_detail("Adding RDATA from old RRSet to old RDATA list.\n"); - xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, + knot_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, &changes->old_rdata_count, old->rdata, knot_rrset_type(old)); @@ -1504,7 +1396,7 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, dbg_xfrin_detail("Adding RDATA from RRSIG of the old RRSet to " "old RDATA list.\n"); - xfrin_changes_add_rdata(changes->old_rdata, + knot_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, &changes->old_rdata_count, old->rrsigs->rdata, @@ -1516,8 +1408,9 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, /*----------------------------------------------------------------------------*/ -static int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type, - knot_rrset_t **rrset, knot_changes_t *changes) +int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type, + knot_rrset_t **rrset, knot_changes_t *changes, + int save_new) { dbg_xfrin_exec_detail( char *name = knot_dname_to_str(knot_node_owner(node)); @@ -1536,7 +1429,7 @@ dbg_xfrin_exec_detail( return 1; } - int ret = xfrin_copy_old_rrset(old, rrset, changes); + int ret = xfrin_copy_old_rrset(old, rrset, changes, save_new); if (ret != KNOT_EOK) { return ret; } @@ -1591,7 +1484,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, // copy the rrset dbg_xfrin_detail("Copying RRSet that carries the RRSIGs.\n"); - ret = xfrin_copy_rrset(node, type, rrset, changes); + ret = xfrin_copy_rrset(node, type, rrset, changes, 1); if (ret != KNOT_EOK) { dbg_xfrin("Failed to copy rrset from changeset.\n"); return ret; @@ -1616,7 +1509,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, dbg_xfrin_verb("Using RRSIG from previous iteration\n"); rrsigs = *rrsigs_old; } else { - ret = xfrin_copy_old_rrset(old, &rrsigs, changes); + ret = xfrin_copy_old_rrset(old, &rrsigs, changes, 1); if (ret != KNOT_EOK) { return ret; } @@ -1640,7 +1533,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, // now in '*rrset' we have a copy of the RRSet which holds the RRSIGs // and in 'rrsigs' we have the copy of the RRSIGs - knot_rdata_t *rdata = xfrin_remove_rdata(rrsigs, remove); + knot_rdata_t *rdata = xfrin_remove_rdata(rrsigs, remove, 0); if (rdata == NULL) { dbg_xfrin("Failed to remove RDATA from RRSet: %s.\n", knot_strerror(ret)); @@ -1650,7 +1543,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, int count = knot_rdata_count(rdata); // connect the RDATA to the list of old RDATA - ret = xfrin_changes_check_rdata(&changes->old_rdata, + ret = knot_changes_rdata_reserve(&changes->old_rdata, &changes->old_rdata_types, changes->old_rdata_count, &changes->old_rdata_allocated, count); @@ -1658,7 +1551,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, return ret; } - xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, + knot_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, &changes->old_rdata_count, rdata, knot_rrset_type(remove)); @@ -1670,7 +1563,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, knot_rrset_set_rrsigs(*rrset, NULL); // add RRSet to the list of old RRSets - ret = xfrin_changes_check_rrsets(&changes->old_rrsets, + ret = knot_changes_rrsets_reserve(&changes->old_rrsets, &changes->old_rrsets_count, &changes->old_rrsets_allocated, 1); @@ -1695,7 +1588,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, knot_rrset_type(*rrset)); assert(tmp == *rrset); - ret = xfrin_changes_check_rrsets(&changes->old_rrsets, + ret = knot_changes_rrsets_reserve(&changes->old_rrsets, &changes->old_rrsets_count, &changes->old_rrsets_allocated, 1); @@ -1720,7 +1613,8 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes, static int xfrin_apply_remove_normal(knot_changes_t *changes, const knot_rrset_t *remove, knot_node_t *node, - knot_rrset_t **rrset) + knot_rrset_t **rrset, + uint32_t chflags) { assert(changes != NULL); assert(remove != NULL); @@ -1731,7 +1625,18 @@ static int xfrin_apply_remove_normal(knot_changes_t *changes, dbg_xfrin_detail("Removing RRSet: \n"); knot_rrset_dump(remove, 0); - + + int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL; + + /* + * First handle the special case of DDNS - do not remove SOA from apex. + */ + if ((chflags & KNOT_CHANGESET_TYPE_DDNS) && is_apex + && knot_rrset_type(remove) == KNOT_RRTYPE_SOA) { + dbg_xfrin_verb("Ignoring SOA removal in UPDATE.\n"); + return KNOT_EOK; + } + // now we have the copy of the node, so lets get the right RRSet // check if we do not already have it if (*rrset @@ -1748,7 +1653,7 @@ static int xfrin_apply_remove_normal(knot_changes_t *changes, * probably not cause problems. TEST!! */ ret = xfrin_copy_rrset(node, - knot_rrset_type(remove), rrset, changes); + knot_rrset_type(remove), rrset, changes, 1); if (ret != KNOT_EOK) { return ret; } @@ -1769,8 +1674,12 @@ dbg_xfrin_exec_detail( ); // remove the specified RRs from the RRSet (de facto difference of sets) - knot_rdata_t *rdata = xfrin_remove_rdata(*rrset, remove); - if (rdata == NULL) { + int ddns_remove_ns_from_apex = + ((chflags & KNOT_CHANGESET_TYPE_DDNS) && is_apex + && knot_rrset_type(*rrset) == KNOT_RRTYPE_NS); + knot_rdata_t *rdata = xfrin_remove_rdata(*rrset, remove, + ddns_remove_ns_from_apex); + if (rdata == NULL && !ddns_remove_ns_from_apex) { dbg_xfrin_verb("Failed to remove RDATA from RRSet\n"); // In this case, the RDATA was not found in the RRSet return 1; @@ -1788,25 +1697,32 @@ dbg_xfrin_exec_detail( } ); - int count = knot_rdata_count(rdata); - // connect the RDATA to the list of old RDATA - ret = xfrin_changes_check_rdata(&changes->old_rdata, - &changes->old_rdata_types, - changes->old_rdata_count, - &changes->old_rdata_allocated, count); - if (ret != KNOT_EOK) { - return ret; - } + if (rdata != NULL) { + int count = knot_rdata_count(rdata); + // connect the RDATA to the list of old RDATA + ret = knot_changes_rdata_reserve(&changes->old_rdata, + &changes->old_rdata_types, + changes->old_rdata_count, + &changes->old_rdata_allocated, + count); + if (ret != KNOT_EOK) { + return ret; + } - xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, - &changes->old_rdata_count, rdata, - knot_rrset_type(remove)); + knot_changes_add_rdata(changes->old_rdata, + changes->old_rdata_types, + &changes->old_rdata_count, rdata, + knot_rrset_type(remove)); + } // if the RRSet is empty, remove from node and add to old RRSets // check if there is no RRSIGs; if there are, leave the RRSet // there; it may be eventually removed when the RRSIGs are removed if (knot_rrset_rdata(*rrset) == NULL && knot_rrset_rrsigs(*rrset) == NULL) { + // The RRSet should not be empty if we were removing NSs from + // apex in case of DDNS + assert(!ddns_remove_ns_from_apex); knot_rrset_t *tmp = knot_node_remove_rrset(node, knot_rrset_type(*rrset)); @@ -1815,7 +1731,7 @@ dbg_xfrin_exec_detail( // add the removed RRSet to list of old RRSets assert(tmp == *rrset); - ret = xfrin_changes_check_rrsets(&changes->old_rrsets, + ret = knot_changes_rrsets_reserve(&changes->old_rrsets, &changes->old_rrsets_count, &changes->old_rrsets_allocated, 1); @@ -1836,43 +1752,124 @@ dbg_xfrin_exec_detail( /*----------------------------------------------------------------------------*/ /*! \todo Needs review - RRs may not be merged into RRSets. */ static int xfrin_apply_remove_all_rrsets(knot_changes_t *changes, - knot_node_t *node, uint16_t type) + knot_node_t *node, uint16_t type, + uint32_t chflags) { - int ret; + int ret = KNOT_EOK; + knot_rrset_t **rrsets = NULL; + unsigned rrsets_count = 0; + int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL; - if (type == KNOT_RRTYPE_ANY) { - // put all the RRSets to the changes structure - ret = xfrin_changes_check_rrsets(&changes->old_rrsets, - &changes->old_rrsets_count, - &changes->old_rrsets_allocated, - knot_node_rrset_count(node)); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to check changeset rrsets.\n"); - return ret; - } +dbg_xfrin_exec_verb( + char *name = knot_dname_to_str(knot_node_owner(node)); + dbg_xfrin_verb("Removing all RRSets from node %s of type %s. " + "Is apex: %d, changeset flags: %u\n", + name, knot_rrtype_to_string(type), is_apex, chflags); + free(name); +); - knot_rrset_t **rrsets = knot_node_get_rrsets(node); - knot_rrset_t **place = changes->old_rrsets - + changes->old_rrsets_count; - /*! \todo Test this!!! */ - memcpy(place, rrsets, knot_node_rrset_count(node) - * sizeof(knot_rrset_t *)); + /*! \todo ref #937 is it OK to modify nodes at this point? + * shouldn't it be after the zones are switched? */ + + /* Assemble RRSets to remove. */ + if (type == KNOT_RRTYPE_ANY) { + /* Remove all RRSets from the node. */ + /* If removing from zone apex in an UPDATE, NS and SOA records + * should be left unchanged. + * We might either remove all RRSets and then return SOA and + * NS RRSets to the node. Or find all existing types in the node + * and remove all except NS and SOA. The first approach is + * IMHO faster. + */ - // remove all RRSets from the node + rrsets = knot_node_get_rrsets(node); + short rr_count = knot_node_rrset_count(node); + if (rr_count > 0) { + rrsets_count = (unsigned)rr_count; + } knot_node_remove_all_rrsets(node); + + /* + * If apex, return SOA and NS RRSets to the node and remove + * them from the list (so they are not deleted later). + * + * This function is called only when processing DDNS, but one + * never knows, so we'll rather check it + */ + if (is_apex && (chflags & KNOT_CHANGESET_TYPE_DDNS)) { + dbg_xfrin_detail("DDNS: returning SOA and NS to the " + "node.\n"); + for (unsigned i = 0; i < rrsets_count; ++i) { + if (knot_rrset_type(rrsets[i]) + == KNOT_RRTYPE_SOA + || knot_rrset_type(rrsets[i]) + == KNOT_RRTYPE_NS) { + dbg_xfrin_detail("Returning...\n"); + knot_node_add_rrset(node, rrsets[i], 0); + rrsets[i] = NULL; + } + } + } } else { - ret = xfrin_changes_check_rrsets(&changes->old_rrsets, - &changes->old_rrsets_count, - &changes->old_rrsets_allocated, - 1); + /* Remove only the RRSet with given type. */ + /* First we must check if we're not removing NS or SOA from + * apex. This change should be ignored. + * + * This function is called only when processing DDNS, but one + * never knows, so we'll rather check it + */ + if (is_apex && (chflags & KNOT_CHANGESET_TYPE_DDNS) + && (type == KNOT_RRTYPE_SOA || type == KNOT_RRTYPE_NS)) { + dbg_xfrin_detail("DDNS: ignoring SOA or NS removal.\n"); + return KNOT_EOK; + } + + rrsets = malloc(sizeof(knot_rrset_t*)); + if (rrsets) { + *rrsets = knot_node_remove_rrset(node, type); + rrsets_count = 1; + } + } + + ret = knot_changes_rrsets_reserve(&changes->old_rrsets, + &changes->old_rrsets_count, + &changes->old_rrsets_allocated, + rrsets_count); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to reserve changes rrsets.\n"); + free(rrsets); + return ret; + } + + /* Mark RRsets and RDATA for removal. */ + for (unsigned i = 0; i < rrsets_count; ++i) { + if (rrsets[i] == NULL) { + continue; + } + + changes->old_rrsets[changes->old_rrsets_count++] = rrsets[i]; + + /* Remove old RDATA. */ + int rdata_count = knot_rrset_rdata_rr_count(rrsets[i]); + ret = knot_changes_rdata_reserve(&changes->old_rdata, + &changes->old_rdata_types, + changes->old_rdata_count, + &changes->old_rdata_allocated, + rdata_count); if (ret != KNOT_EOK) { - dbg_xfrin("Failed to check changeset rrsets.\n"); + dbg_xfrin("Failed to reserve changes rdata.\n"); + free(rrsets); return ret; } - // remove only RRSet with the given type - knot_rrset_t *rrset = knot_node_remove_rrset(node, type); - changes->old_rrsets[changes->old_rrsets_count++] = rrset; + + knot_changes_add_rdata(changes->old_rdata, + changes->old_rdata_types, + &changes->old_rdata_count, + knot_rrset_get_rdata(rrsets[i]), + knot_rrset_type(rrsets[i])); } + + free(rrsets); return KNOT_EOK; } @@ -1917,11 +1914,179 @@ static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents, /*----------------------------------------------------------------------------*/ +int xfrin_replace_rrset_in_node(knot_node_t *node, + knot_rrset_t *rrset_new, + knot_changes_t *changes, + knot_zone_contents_t *contents) +{ + knot_rr_type_t type = knot_rrset_type(rrset_new); + // remove RRSet of the proper type from the node + dbg_xfrin_verb("Removing RRSet of type: %s.\n", + knot_rrtype_to_string(type)); + knot_rrset_t *rrset_old = knot_node_remove_rrset(node, type); + assert(rrset_old != NULL); + + // add the old RRSet to the list of old RRSets + int ret = knot_changes_rrsets_reserve(&changes->old_rrsets, + &changes->old_rrsets_count, + &changes->old_rrsets_allocated, 1); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to add old RRSet to list.\n"); + return ret; + } + + // save also the RDATA, because RDATA are not deleted with the RRSet + // The count should be 1, but just to be sure.... + int count = knot_rrset_rdata_rr_count(rrset_old); + ret = knot_changes_rdata_reserve(&changes->old_rdata, + &changes->old_rdata_types, + changes->old_rdata_count, + &changes->old_rdata_allocated, count); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to add old RDATA to list.\n"); + return ret; + } + + // save the new RRSet to the new RRSet, so that it is deleted if the + // apply fails + ret = knot_changes_rrsets_reserve(&changes->new_rrsets, + &changes->new_rrsets_count, + &changes->new_rrsets_allocated, 1); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to add new RRSet to list.\n"); + return ret; + } + + // The count should be 1, but just to be sure.... + count = knot_rrset_rdata_rr_count(rrset_new); + // save the new RDATA + ret = knot_changes_rdata_reserve(&changes->new_rdata, + &changes->new_rdata_types, + changes->new_rdata_count, + &changes->new_rdata_allocated, count); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to add new RDATA to list.\n"); + return ret; + } + + changes->old_rrsets[changes->old_rrsets_count++] = rrset_old; + + dbg_xfrin_verb("Adding RDATA from old RRSet to the list of old RDATA." + "\n"); + knot_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, + &changes->old_rdata_count, + knot_rrset_get_rdata(rrset_old), type); + + // store RRSIGs from the old RRSet to the new + knot_rrset_set_rrsigs(rrset_new, knot_rrset_get_rrsigs(rrset_old)); + + // insert the new RRSet to the node + dbg_xfrin_verb("Adding new RRSet.\n"); + ret = knot_zone_contents_add_rrset(contents, rrset_new, &node, + KNOT_RRSET_DUPL_SKIP, 1); + + if (ret < 0) { + dbg_xfrin("Failed to add RRSet to node.\n"); + return KNOT_ERROR; + } + assert(ret == 0); + + changes->new_rrsets[changes->new_rrsets_count++] = rrset_new; + + dbg_xfrin_verb("Adding RDATA from new RRSet to the list of new RDATA." + "\n"); + knot_changes_add_rdata(changes->new_rdata, changes->new_rdata_types, + &changes->new_rdata_count, + knot_rrset_get_rdata(rrset_new), type); + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +static int xfrin_apply_add_normal_ddns(knot_changes_t *changes, + knot_rrset_t *add, knot_node_t *node, + knot_zone_contents_t *contents) +{ + int ret; + + /* 1) Adding SOA. */ + if (knot_rrset_type(add) == KNOT_RRTYPE_SOA) { + /* a) If trying to add SOA to non-apex node, or the + * serial is less than the current serial, ignore. + */ + if (knot_node_rrset(node, KNOT_RRTYPE_SOA) == NULL + || ns_serial_compare(knot_rdata_soa_serial( + knot_rrset_rdata( + knot_node_rrset(node, KNOT_RRTYPE_SOA))), + knot_rdata_soa_serial(knot_rrset_rdata(add) + )) > 0 + ) { + dbg_ddns_verb("DDNS: Ignoring SOA.\n"); + return KNOT_EOK; + } else { + dbg_ddns_verb("DDNS: replacing SOA (old serial: %u," + " new serial: %u.\n", + knot_rdata_soa_serial(knot_rrset_rdata( + knot_node_rrset(node, + KNOT_RRTYPE_SOA))), + knot_rdata_soa_serial(knot_rrset_rdata( + add))); + /* b) Otherwise, replace the current SOA. */ + ret = xfrin_replace_rrset_in_node(node, add, + changes, + contents); + /* In this case we must however remove the ADD RRSet + * from the changeset, so that it is not deleted + * afterwards. + */ + if (ret == KNOT_EOK) { + return 3; + } else { + return ret; + } + } + } else if (knot_rrset_type(add) == KNOT_RRTYPE_CNAME) { + /* 2) Adding CNAME... */ + if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) { + dbg_ddns_verb("DDNS: replacing CNAME.\n"); + /* a) ... to a CNAME node => replace. */ + ret = xfrin_replace_rrset_in_node(node, add, changes, + contents); + /* In this case we must however remove the ADD RRSet + * from the changeset, so that it is not deleted + * afterwards. + */ + if (ret == KNOT_EOK) { + return 3; + } else { + return ret; + } + } else if (knot_node_rrset_count(node) > 0) { + dbg_ddns_verb("DDNS: ignoring CNAME (non-empty node)\n"); + /* b) ... to a non-empty node => ignore. */ + return KNOT_EOK; + } + /* c) ... to an empty node => process normally. */ + } else if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) { + /* 3) Adding other RRSets to CNAME node => ignore. */ + dbg_ddns_verb("DDNS: ignoring RRSet (CNAME node)\n"); + // handled in previous case + assert(knot_rrset_type(add) != KNOT_RRTYPE_CNAME); + return KNOT_EOK; + } + + return 1; // Continue normal processing +} + +/*----------------------------------------------------------------------------*/ + static int xfrin_apply_add_normal(knot_changes_t *changes, knot_rrset_t *add, knot_node_t *node, knot_rrset_t **rrset, - knot_zone_contents_t *contents) + knot_zone_contents_t *contents, + uint32_t chflags) { assert(changes != NULL); assert(add != NULL); @@ -1935,6 +2100,15 @@ dbg_xfrin_exec_detail( dbg_xfrin_detail("applying rrset:\n"); knot_rrset_dump(add, 0); ); + + /* DDNS special cases. */ + if (chflags & KNOT_CHANGESET_TYPE_DDNS) { + ret = xfrin_apply_add_normal_ddns(changes, add, node, contents); + /* Continue only if return value is 1. */ + if (ret != 1) { + return ret; + } + } int copied = 0; /*! \note Reusing RRSet from previous function caused it not to be @@ -1955,7 +2129,7 @@ dbg_xfrin_exec_detail( knot_rrset_t *old = *rrset; if (*rrset != NULL) { - ret = xfrin_copy_old_rrset(old, rrset, changes); + ret = xfrin_copy_old_rrset(old, rrset, changes, 1); if (ret != KNOT_EOK) { return ret; } @@ -1995,7 +2169,7 @@ dbg_xfrin_exec_detail( if (ret < 0) { dbg_xfrin("Failed to add RRSet to node.\n"); - return KNOT_ERROR; + return ret; } assert(ret == 0); @@ -2021,6 +2195,7 @@ dbg_xfrin_exec_detail( * * TODO: add the 'add' rrset to list of old RRSets? */ + dbg_xfrin_detail("Merging RRSets with owners: %s, %s types: %s, %s\n", (*rrset)->owner->name, add->owner->name, knot_rrtype_to_string((*rrset)->type), @@ -2039,7 +2214,7 @@ dbg_xfrin_exec_detail( } ret = knot_rrset_merge_no_dupl((void **)rrset, (void **)&add); - if (ret != KNOT_EOK) { + if (ret < 0) { dbg_xfrin("Failed to merge changeset RRSet.\n"); return ret; } @@ -2103,7 +2278,7 @@ dbg_xfrin_exec_verb( dbg_xfrin_verb("Using RRSet from previous iteration.\n"); } else { // copy the rrset - ret = xfrin_copy_rrset(node, type, rrset, changes); + ret = xfrin_copy_rrset(node, type, rrset, changes, 1); if (ret < 0) { return ret; } else if (ret != KNOT_EOK) { @@ -2128,7 +2303,7 @@ dbg_xfrin_exec_verb( dbg_xfrin_detail("Created new RRSet for RRSIG: %p.\n", *rrset); // add the RRset to the list of new RRsets - ret = xfrin_changes_check_rrsets( + ret = knot_changes_rrsets_reserve( &changes->new_rrsets, &changes->new_rrsets_count, &changes->new_rrsets_allocated, 1); @@ -2183,7 +2358,8 @@ dbg_xfrin_exec_detail( dbg_xfrin_verb("Using RRSIG from previous iteration\n"); rrsig = *rrsigs_old; } else { - ret = xfrin_copy_old_rrset(old, &rrsig, changes); + ret = xfrin_copy_old_rrset(old, &rrsig, changes, + 1); if (ret != KNOT_EOK) { return ret; } @@ -2202,7 +2378,7 @@ dbg_xfrin_exec_detail( // merge the changeset RRSet to the copy dbg_xfrin_detail("Merging RRSIG to the one in the RRSet.\n"); ret = knot_rrset_merge_no_dupl((void **)&rrsig, (void **)&add); - if (ret != KNOT_EOK) { + if (ret < 0) { dbg_xfrin("Failed to merge changeset RRSIG to copy: %s" ".\n", knot_strerror(ret)); return KNOT_ERROR; @@ -2535,10 +2711,14 @@ dbg_xfrin_exec_detail( knot_rrset_rdata(chset->remove[i])) == KNOT_RRTYPE_NSEC3)) { + dbg_xfrin_verb("Removed RRSet belongs to NSEC3 tree.\n"); is_nsec3 = 1; } // check if the old node is not the one we should use + dbg_xfrin_verb("Node:%p Owner: %p Node owner: %p\n", + node, knot_rrset_owner(chset->remove[i]), + knot_node_owner(node)); if (!node || knot_rrset_owner(chset->remove[i]) != knot_node_owner(node)) { if (is_nsec3) { @@ -2559,10 +2739,12 @@ dbg_xfrin_exec_detail( assert(node != NULL); // first check if all RRSets should be removed + dbg_xfrin_verb("RRSet class to be removed=%u\n", + knot_rrset_class(chset->remove[i])); if (knot_rrset_class(chset->remove[i]) == KNOT_CLASS_ANY) { ret = xfrin_apply_remove_all_rrsets( changes, node, - knot_rrset_type(chset->remove[i])); + knot_rrset_type(chset->remove[i]), chset->flags); } else if (knot_rrset_type(chset->remove[i]) == KNOT_RRTYPE_RRSIG) { // this should work also for UPDATE @@ -2573,7 +2755,8 @@ dbg_xfrin_exec_detail( // this should work also for UPDATE ret = xfrin_apply_remove_normal(changes, chset->remove[i], - node, &rrset); + node, &rrset, + chset->flags); } dbg_xfrin_detail("xfrin_apply_remove() ret = %d\n", ret); @@ -2594,7 +2777,7 @@ static int xfrin_apply_add(knot_zone_contents_t *contents, knot_changeset_t *chset, knot_changes_t *changes) { - int ret = 0; + int ret = KNOT_EOK; knot_node_t *node = NULL; knot_rrset_t *rrset = NULL; knot_rrset_t *rrsigs = NULL; @@ -2661,22 +2844,27 @@ dbg_xfrin_exec_detail( ret = xfrin_apply_add_rrsig(changes, chset->add[i], node, &rrset, &rrsigs, contents); + assert(ret != KNOT_EOK); } else { ret = xfrin_apply_add_normal(changes, chset->add[i], - node, &rrset, contents); + node, &rrset, contents, + chset->flags); + assert(ret <= 3); } - assert(ret != KNOT_EOK); + // Not correct anymore, add_normal() returns KNOT_EOK if the + // changeset RR should be removed + //assert(ret != KNOT_EOK); - dbg_xfrin_detail("xfrin_apply_..() returned %s, rrset: %p\n", - knot_strerror(ret), rrset); + dbg_xfrin_detail("xfrin_apply_..() returned %d, rrset: %p\n", + ret, rrset); if (ret > 0) { if (ret == 1) { // the ADD RRSet was used, i.e. it should be // removed from the changeset and saved in the // list of new RRSets - ret = xfrin_changes_check_rrsets( + ret = knot_changes_rrsets_reserve( &changes->new_rrsets, &changes->new_rrsets_count, &changes->new_rrsets_allocated, 1); @@ -2694,7 +2882,7 @@ dbg_xfrin_exec_detail( chset->add[i]); // connect the RDATA to the list of new RDATA - int res = xfrin_changes_check_rdata( + int res = knot_changes_rdata_reserve( &changes->new_rdata, &changes->new_rdata_types, changes->new_rdata_count, @@ -2703,7 +2891,7 @@ dbg_xfrin_exec_detail( return res; } - xfrin_changes_add_rdata(changes->new_rdata, + knot_changes_add_rdata(changes->new_rdata, changes->new_rdata_types, &changes->new_rdata_count, knot_rrset_get_rdata(chset->add[i]), @@ -2721,6 +2909,11 @@ dbg_xfrin_exec_detail( // stored in the list of new RDATA, because // it is joined to the copy of RDATA, that is // already stored there + } else if (ret == 3) { + // the RRSet was used and both RRSet and RDATA + // were properly stored. Just clear the place + // in the changeset + chset->add[i] = NULL; } else { assert(0); } @@ -2743,97 +2936,18 @@ static int xfrin_apply_replace_soa(knot_zone_contents_t *contents, knot_node_t *node = knot_zone_contents_get_apex(contents); assert(node != NULL); - int ret = 0; - assert(node != NULL); - // set the node copy as the apex of the contents - contents->apex = node; - - // remove the SOA RRSet from the apex - dbg_xfrin_verb("Removing SOA.\n"); - knot_rrset_t *rrset = knot_node_remove_rrset(node, KNOT_RRTYPE_SOA); - assert(rrset != NULL); - - // add the old RRSet to the list of old RRSets - ret = xfrin_changes_check_rrsets(&changes->old_rrsets, - &changes->old_rrsets_count, - &changes->old_rrsets_allocated, 1); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to add old RRSet to list.\n"); - return ret; - } - - // save also the SOA RDATA, because RDATA are not deleted with the - // RRSet - ret = xfrin_changes_check_rdata(&changes->old_rdata, - &changes->old_rdata_types, - changes->old_rdata_count, - &changes->old_rdata_allocated, 1); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to add old RDATA to list.\n"); - return ret; - } - // save the SOA to the new RRSet, so that it is deleted if the - // apply fails - ret = xfrin_changes_check_rrsets(&changes->new_rrsets, - &changes->new_rrsets_count, - &changes->new_rrsets_allocated, 1); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to add new RRSet to list.\n"); - return ret; + int ret = xfrin_replace_rrset_in_node(node, chset->soa_to, changes, + contents); + if (ret == KNOT_EOK) { + // remove the SOA from the changeset, so it will not be deleted + // after successful apply + chset->soa_to = NULL; } - int count = knot_rrset_rdata_rr_count(rrset); - count += knot_rrset_rdata_rr_count(chset->soa_to); - // save the new SOA RDATA - ret = xfrin_changes_check_rdata(&changes->new_rdata, - &changes->new_rdata_types, - changes->new_rdata_count, - &changes->new_rdata_allocated, count); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to add new RDATA to list.\n"); - return ret; - } - - changes->old_rrsets[changes->old_rrsets_count++] = rrset; - - /*! \todo Maybe check if the SOA does not have more RDATA? */ - dbg_xfrin_verb("Adding RDATA from old SOA to the list of old RDATA.\n"); - xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types, - &changes->old_rdata_count, rrset->rdata, - KNOT_RRTYPE_SOA); - - // store RRSIGs from the old SOA to the new SOA - knot_rrset_set_rrsigs(chset->soa_to, knot_rrset_get_rrsigs(rrset)); - - // insert the new SOA RRSet to the node - dbg_xfrin_verb("Adding SOA.\n"); - //ret = knot_node_add_rrset(node, chset->soa_to, 0); - ret = knot_zone_contents_add_rrset(contents, chset->soa_to, &node, - KNOT_RRSET_DUPL_SKIP, 1); - - if (ret < 0) { - dbg_xfrin("Failed to add RRSet to node.\n"); - return KNOT_ERROR; - } - assert(ret == 0); - - changes->new_rrsets[changes->new_rrsets_count++] = chset->soa_to; - - dbg_xfrin_verb("Adding RDATA from new SOA to the list of new RDATA.\n"); - xfrin_changes_add_rdata(changes->new_rdata, - changes->new_rdata_types, - &changes->new_rdata_count, - knot_rrset_get_rdata(chset->soa_to), - knot_rrset_type(chset->soa_to)); - - // remove the SOA from the changeset, so it will not be deleted after - // successful apply - chset->soa_to = NULL; - - return KNOT_EOK; + return ret; } /*----------------------------------------------------------------------------*/ @@ -2885,7 +2999,7 @@ static void xfrin_mark_empty(knot_node_t *node, void *data) if (knot_node_rrset_count(node) == 0 && knot_node_children(node) == 0) { - int ret = xfrin_changes_check_nodes(&changes->old_nodes, + int ret = knot_changes_nodes_reserve(&changes->old_nodes, &changes->old_nodes_count, &changes->old_nodes_allocated); if (ret != KNOT_EOK) { @@ -2919,7 +3033,7 @@ static void xfrin_mark_empty_nsec3(knot_node_t *node, void *data) if (knot_node_rrset_count(node) == 0 && knot_node_children(node) == 0) { - int ret = xfrin_changes_check_nodes(&changes->old_nsec3, + int ret = knot_changes_nodes_reserve(&changes->old_nsec3, &changes->old_nsec3_count, &changes->old_nsec3_allocated); if (ret != KNOT_EOK) { @@ -3069,22 +3183,15 @@ static int xfrin_check_contents_copy(knot_zone_contents_t *old_contents) /*----------------------------------------------------------------------------*/ -int xfrin_apply_changesets(knot_zone_t *zone, - knot_changesets_t *chsets, - knot_zone_contents_t **new_contents) +int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents, + knot_zone_contents_t **new_contents, + knot_changes_t **changes) { - if (zone == NULL || chsets == NULL || chsets->count == 0 - || new_contents == NULL) { + if (old_contents == NULL || new_contents == NULL || changes == NULL) { return KNOT_EINVAL; } - knot_zone_contents_t *old_contents = knot_zone_get_contents(zone); - if (!old_contents) { - dbg_xfrin("Cannot apply changesets to empty zone.\n"); - return KNOT_EINVAL; - } - - dbg_xfrin("Applying changesets to zone...\n"); + dbg_xfrin("Preparing zone copy...\n"); /* * Ensure that the zone generation is set to 0. @@ -3116,15 +3223,15 @@ int xfrin_apply_changesets(knot_zone_t *zone, return ret; } - knot_changes_t *changes = (knot_changes_t *)malloc( + knot_changes_t *chgs = (knot_changes_t *)malloc( sizeof(knot_changes_t)); - if (changes == NULL) { + if (chgs == NULL) { dbg_xfrin("Failed to allocate structure for changes!\n"); - xfrin_rollback_update(old_contents, &contents_copy, &changes); + xfrin_rollback_update(old_contents, &contents_copy, &chgs); return KNOT_ENOMEM; } - memset(changes, 0, sizeof(knot_changes_t)); + memset(chgs, 0, sizeof(knot_changes_t)); /*! * \todo Check if all nodes have their copy. @@ -3132,7 +3239,7 @@ int xfrin_apply_changesets(knot_zone_t *zone, ret = xfrin_check_contents_copy(old_contents); if (ret != KNOT_EOK) { dbg_xfrin("Contents copy check failed!\n"); - xfrin_rollback_update(old_contents, &contents_copy, &changes); + xfrin_rollback_update(old_contents, &contents_copy, &chgs); return ret; } @@ -3145,30 +3252,23 @@ int xfrin_apply_changesets(knot_zone_t *zone, dbg_xfrin("Switching ptrs pointing to old nodes to the new nodes.\n"); ret = xfrin_switch_nodes(contents_copy); assert(knot_zone_contents_apex(contents_copy) != NULL); + + *new_contents = contents_copy; + *changes = chgs; + + return KNOT_EOK; +} - /* - * Apply the changesets. - */ - dbg_xfrin("Applying changesets.\n"); - dbg_xfrin_verb("Old contents apex: %p, new apex: %p\n", - old_contents->apex, contents_copy->apex); - for (int i = 0; i < chsets->count; ++i) { - if ((ret = xfrin_apply_changeset(contents_copy, changes, - &chsets->sets[i])) - != KNOT_EOK) { - xfrin_rollback_update(old_contents, - &contents_copy, &changes); - dbg_xfrin("Failed to apply changesets to zone: " - "%s\n", knot_strerror(ret)); - return ret; - } - } - assert(knot_zone_contents_apex(contents_copy) != NULL); - - /*! - * \todo Test failure of IXFR. - */ +/*----------------------------------------------------------------------------*/ +int xfrin_finalize_updated_zone(knot_zone_contents_t *contents_copy, + knot_changes_t *changes, + knot_zone_contents_t *old_contents) +{ + if (contents_copy == NULL || changes == NULL || old_contents == NULL) { + return KNOT_EINVAL; + } + /* * Finalize the new zone contents: * - delete empty nodes @@ -3181,11 +3281,11 @@ int xfrin_apply_changesets(knot_zone_t *zone, * Select and remove empty nodes from zone trees. Do not free them right * away as they may be referenced by some domain names. */ - ret = xfrin_remove_empty_nodes(contents_copy, changes); + int ret = xfrin_remove_empty_nodes(contents_copy, changes); if (ret != KNOT_EOK) { dbg_xfrin("Failed to remove empty nodes: %s\n", knot_strerror(ret)); - xfrin_rollback_update(old_contents, &contents_copy, &changes); +// xfrin_rollback_update(old_contents, &contents_copy, &changes); return ret; } @@ -3196,7 +3296,7 @@ int xfrin_apply_changesets(knot_zone_t *zone, if (ret != KNOT_EOK) { dbg_xfrin("Failed to finalize zone contents: %s\n", knot_strerror(ret)); - xfrin_rollback_update(old_contents, &contents_copy, &changes); +// xfrin_rollback_update(old_contents, &contents_copy, &changes); return ret; } assert(knot_zone_contents_apex(contents_copy) != NULL); @@ -3205,6 +3305,71 @@ int xfrin_apply_changesets(knot_zone_t *zone, ret = knot_zone_contents_check_loops(contents_copy); if (ret != KNOT_EOK) { dbg_xfrin("CNAME loop check failed: %s\n", knot_strerror(ret)); +// xfrin_rollback_update(old_contents, &contents_copy, &changes); + return ret; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +int xfrin_apply_changesets(knot_zone_t *zone, + knot_changesets_t *chsets, + knot_zone_contents_t **new_contents) +{ + if (zone == NULL || chsets == NULL || chsets->count == 0 + || new_contents == NULL) { + return KNOT_EINVAL; + } + + knot_zone_contents_t *old_contents = knot_zone_get_contents(zone); + if (!old_contents) { + dbg_xfrin("Cannot apply changesets to empty zone.\n"); + return KNOT_EINVAL; + } + + dbg_xfrin("Applying changesets to zone...\n"); + + dbg_xfrin_verb("Creating shallow copy of the zone...\n"); + knot_zone_contents_t *contents_copy = NULL; + knot_changes_t *changes = NULL; + int ret = xfrin_prepare_zone_copy(old_contents, &contents_copy, + &changes); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to prepare zone copy: %s\n", + knot_strerror(ret)); + return ret; + } + + /* + * Apply the changesets. + */ + dbg_xfrin("Applying changesets.\n"); + dbg_xfrin_verb("Old contents apex: %p, new apex: %p\n", + old_contents->apex, contents_copy->apex); + for (int i = 0; i < chsets->count; ++i) { + if ((ret = xfrin_apply_changeset(contents_copy, changes, + &chsets->sets[i])) + != KNOT_EOK) { + xfrin_rollback_update(old_contents, + &contents_copy, &changes); + dbg_xfrin("Failed to apply changesets to zone: " + "%s\n", knot_strerror(ret)); + return ret; + } + } + assert(knot_zone_contents_apex(contents_copy) != NULL); + + /*! + * \todo Test failure of IXFR. + */ + + dbg_xfrin_verb("Finalizing updated zone...\n"); + ret = xfrin_finalize_updated_zone(contents_copy, changes, old_contents); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to finalize updated zone: %s\n", + knot_strerror(ret)); xfrin_rollback_update(old_contents, &contents_copy, &changes); return ret; } @@ -3237,7 +3402,7 @@ int xfrin_switch_zone(knot_zone_t *zone, old, (old) ? old->apex : NULL, new_contents->apex); // switch pointers in domain names, now only the new zone is used - if (transfer_type == XFR_TYPE_IIN) { + if (transfer_type == XFR_TYPE_IIN || transfer_type == XFR_TYPE_UPDATE) { // Traverse also the dname table and change the node pointers // in dnames int ret = knot_zone_contents_dname_table_apply( diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h index a762b81..c65e708 100644 --- a/src/libknot/updates/xfr-in.h +++ b/src/libknot/updates/xfr-in.h @@ -185,6 +185,14 @@ int xfrin_apply_changesets(knot_zone_t *zone, knot_changesets_t *chsets, knot_zone_contents_t **new_contents); +int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents, + knot_zone_contents_t **new_contents, + knot_changes_t **changes); + +int xfrin_finalize_updated_zone(knot_zone_contents_t *contents_copy, + knot_changes_t *changes, + knot_zone_contents_t *old_contents); + int xfrin_switch_zone(knot_zone_t *zone, knot_zone_contents_t *new_contents, int deep_free); @@ -195,6 +203,18 @@ void xfrin_rollback_update(knot_zone_contents_t *old_contents, knot_zone_contents_t **new_contents, knot_changes_t **changes); +int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type, + knot_rrset_t **rrset, knot_changes_t *changes, + int save_new); + +int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy, + knot_changes_t *changes, int save_new); + +int xfrin_replace_rrset_in_node(knot_node_t *node, + knot_rrset_t *rrset_new, + knot_changes_t *changes, + knot_zone_contents_t *contents); + #endif /* _KNOTXFR_IN_H_ */ /*! @} */ diff --git a/src/libknot/util/debug.h b/src/libknot/util/debug.h index 731fed8..abe335b 100644 --- a/src/libknot/util/debug.h +++ b/src/libknot/util/debug.h @@ -68,6 +68,7 @@ #ifdef KNOT_XFR_DEBUG #define KNOT_XFRIN_DEBUG #define KNOT_TSIG_DEBUG + #define KNOT_DDNS_DEBUG #endif /* KNOT_DNAME_DEBUG -- in configure.ac */ @@ -758,27 +759,33 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone); #ifdef DEBUG_ENABLE_BRIEF #define dbg_ddns(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg) #define dbg_ddns_hex(data, len) hex_log(LOG_ZONE, (data), (len)) +#define dbg_ddns_exec(cmds) do { cmds } while (0) #else #define dbg_ddns(msg...) #define dbg_ddns_hex(data, len) +#define dbg_ddns_exec(cmds) #endif /* Verbose messages. */ #ifdef DEBUG_ENABLE_VERBOSE #define dbg_ddns_verb(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg) #define dbg_ddns_hex_verb(data, len) hex_log(LOG_ZONE, (data), (len)) +#define dbg_ddns_exec_verb(cmds) do { cmds } while (0) #else #define dbg_ddns_verb(msg...) #define dbg_ddns_hex_verb(data, len) +#define dbg_ddns_exec_verb(cmds) #endif /* Detail messages. */ #ifdef DEBUG_ENABLE_DETAILS #define dbg_ddns_detail(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg) #define dbg_ddns_hex_detail(data, len) hex_log(LOG_ZONE, (data), (len)) +#define dbg_ddns_exec_detail(cmds) do { cmds } while (0) #else #define dbg_ddns_detail(msg...) #define dbg_ddns_hex_detail(data, len) +#define dbg_ddns_exec_detail(cmds) #endif /* No messages. */ @@ -786,9 +793,12 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone); #define dbg_ddns(msg...) #define dbg_ddns_hex(data, len) #define dbg_ddns_verb(msg...) +#define dbg_ddns_exec(cmds) #define dbg_ddns_hex_verb(data, len) +#define dbg_ddns_exec_verb(cmds) #define dbg_ddns_detail(msg...) #define dbg_ddns_hex_detail(data, len) +#define dbg_ddns_exec_detail(cmds) #endif #ifdef KNOT_TSIG_DEBUG diff --git a/src/libknot/util/descriptor.c b/src/libknot/util/descriptor.c index 1588a2e..8cab6b4 100644 --- a/src/libknot/util/descriptor.c +++ b/src/libknot/util/descriptor.c @@ -68,6 +68,15 @@ static knot_lookup_table_t dns_rrclasses[] = { { 0, NULL } }; +/*! \brief DS digest lengths. */ +enum knot_ds_algorithm_len +{ + KNOT_DS_DIGEST_LEN_SHA1 = 20, /* 20B - RFC 3658 */ + KNOT_DS_DIGEST_LEN_SHA256 = 32, /* 32B - RFC 4509 */ + KNOT_DS_DIGEST_LEN_GOST = 32, /* 32B - RFC 5933 */ + KNOT_DS_DIGEST_LEN_SHA384 = 48 /* 48B - RFC 6605 */ +}; + /*! \brief RR type descriptors. */ static knot_rrtype_descriptor_t knot_rrtype_descriptors[KNOT_RRTYPE_DESCRIPTORS_LENGTH] = { @@ -572,5 +581,21 @@ int knot_rrtype_is_metatype(uint16_t type) || type == KNOT_RRTYPE_OPT); } +size_t knot_ds_digest_length(uint8_t algorithm) +{ + switch (algorithm) { + case KNOT_DS_ALG_SHA1: + return KNOT_DS_DIGEST_LEN_SHA1; + case KNOT_DS_ALG_SHA256: + return KNOT_DS_DIGEST_LEN_SHA256; + case KNOT_DS_ALG_GOST: + return KNOT_DS_DIGEST_LEN_GOST; + case KNOT_DS_ALG_SHA384: + return KNOT_DS_DIGEST_LEN_SHA384; + default: + return 0; + } +} + /*! @} */ diff --git a/src/libknot/util/descriptor.h b/src/libknot/util/descriptor.h index 6364e5b..23b5d13 100644 --- a/src/libknot/util/descriptor.h +++ b/src/libknot/util/descriptor.h @@ -237,6 +237,41 @@ typedef enum knot_rdata_zoneformat knot_rdata_zoneformat_t; /*! \brief Enum containing wireformat codes. */ typedef enum knot_rdatawireformat knot_rdata_wireformat_t; +/*! \brief Constants for DNSSEC algorithm types. + * + * Source: http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml + */ +enum knot_dnssec_algorithm +{ + KNOT_DNSSEC_ALG_RSAMD5 = 1, + KNOT_DNSSEC_ALG_DH = 2, + KNOT_DNSSEC_ALG_DSA = 3, + KNOT_DNSSEC_ALG_RSASHA1 = 5, + KNOT_DNSSEC_ALG_DSA_NSEC3_SHA1 = 6, + KNOT_DNSSEC_ALG_RSASHA1_NSEC3_SHA1 = 7, + KNOT_DNSSEC_ALG_RSASHA256 = 8, + KNOT_DNSSEC_ALG_RSASHA512 = 10, + KNOT_DNSSEC_ALG_ECC_GOST = 12, + KNOT_DNSSEC_ALG_ECDSAP256SHA256 = 13, + KNOT_DNSSEC_ALG_ECDSAP384SHA384 = 14 +}; + +typedef enum knot_dnssec_algorithm knot_dnssec_algorithm_t; + +/*! \brief Constants for DNSSEC algorithm types. + * + * Source: http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xml + */ +enum knot_ds_algorithm +{ + KNOT_DS_ALG_SHA1 = 1, /* 20B - RFC 3658 */ + KNOT_DS_ALG_SHA256 = 2, /* 32B - RFC 4509 */ + KNOT_DS_ALG_GOST = 3, /* 32B - RFC 5933 */ + KNOT_DS_ALG_SHA384 = 4 /* 48B - RFC 6605 */ +}; + +typedef enum knot_ds_algorithm knot_ds_algorithm_t; + /*! \brief Structure holding RR descriptor. */ struct knot_rrtype_descriptor { uint16_t type; /*!< RR type */ @@ -328,6 +363,8 @@ size_t knot_wireformat_size(unsigned int wire_type); int knot_rrtype_is_metatype(uint16_t type); +size_t knot_ds_digest_length(uint8_t algorithm); + #endif /* _KNOT_DESCRIPTOR_H_ */ /*! @} */ diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c index 61e9e51..bdc268b 100644 --- a/src/libknot/zone/zone-contents.c +++ b/src/libknot/zone/zone-contents.c @@ -408,6 +408,40 @@ static void knot_zone_contents_adjust_rdata_item(knot_rdata_t *rdata, } /*----------------------------------------------------------------------------*/ + +static int knot_zone_contents_adjust_rdata(knot_rdata_t *rdata, + knot_rrtype_descriptor_t *desc, + knot_zone_contents_t *zone, + knot_node_t *node) +{ + for (int i = 0; i < rdata->count; ++i) { + if (desc->wireformat[i] + == KNOT_RDATA_WF_COMPRESSED_DNAME + || desc->wireformat[i] + == KNOT_RDATA_WF_UNCOMPRESSED_DNAME + || desc->wireformat[i] + == KNOT_RDATA_WF_LITERAL_DNAME) { + dbg_zone_detail("Adjusting domain name at position %d" + " of the RDATA\n", i); + knot_zone_contents_adjust_rdata_item(rdata, zone, node, + i); + } + } + + /* + * DS digest length check (#2050). + */ + int ret; + if (desc->type == KNOT_RRTYPE_DS + && (ret = knot_rdata_ds_check(rdata)) != KNOT_EOK) { + dbg_zone("DS RDATA check failed: %s\n", knot_strerror(ret)); + return KNOT_EMALF; + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ /*! * \brief Adjusts all RDATA in the given RRSet by replacing domain names by ones * present in the zone. @@ -419,9 +453,9 @@ static void knot_zone_contents_adjust_rdata_item(knot_rdata_t *rdata, * \param rrset RRSet to adjust RDATA in. * \param zone Zone to which the RRSet belongs. */ -static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset, - knot_zone_contents_t *zone, - knot_node_t *node) +static int knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset, + knot_zone_contents_t *zone, + knot_node_t *node) { uint16_t type = knot_rrset_type(rrset); @@ -433,49 +467,37 @@ static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset, knot_rdata_t *rdata = rdata_first; if (rdata == NULL) { - return; + return KNOT_EOK; } + int i = 1; + int ret; while (rdata->next != rdata_first) { - for (int i = 0; i < rdata->count; ++i) { - if (desc->wireformat[i] - == KNOT_RDATA_WF_COMPRESSED_DNAME - || desc->wireformat[i] - == KNOT_RDATA_WF_UNCOMPRESSED_DNAME - || desc->wireformat[i] - == KNOT_RDATA_WF_LITERAL_DNAME) { - dbg_zone("Adjusting domain name at " - "position %d of RDATA of record with owner " - "%s and type %s.\n", - i, rrset->owner->name, - knot_rrtype_to_string(type)); - - knot_zone_contents_adjust_rdata_item(rdata, - zone, node, - i); - } +dbg_zone_exec_detail( + char *name = knot_dname_to_str(knot_rrset_owner(rrset)); + dbg_zone_detail("Adjusting domain name at %d. RDATA of RRSet " + "with owner %s and type %s.\n", i, name, + knot_rrtype_to_string(type)); + free(name); +); + ret = knot_zone_contents_adjust_rdata(rdata, desc, + zone, node); + if (ret != KNOT_EOK) { + return ret; } - rdata = rdata->next; - } - for (int i = 0; i < rdata->count; ++i) { - if (desc->wireformat[i] - == KNOT_RDATA_WF_COMPRESSED_DNAME - || desc->wireformat[i] - == KNOT_RDATA_WF_UNCOMPRESSED_DNAME - || desc->wireformat[i] - == KNOT_RDATA_WF_LITERAL_DNAME) { - dbg_zone("Adjusting domain name at " - "position %d of RDATA of record with owner " - "%s and type %s.\n", - i, rrset->owner->name, - knot_rrtype_to_string(type)); - - knot_zone_contents_adjust_rdata_item(rdata, zone, node, - i); - } + rdata = rdata->next; + ++i; } +dbg_zone_exec_detail( + char *name = knot_dname_to_str(knot_rrset_owner(rrset)); + dbg_zone_detail("Adjusting domain name at %d. RDATA of RRSet " + "with owner %s and type %s.\n", i, name, + knot_rrtype_to_string(type)); + free(name); +); + return knot_zone_contents_adjust_rdata(rdata, desc, zone, node); } /*----------------------------------------------------------------------------*/ @@ -489,28 +511,37 @@ static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset, * \param node Zone node to adjust the RRSets in. * \param zone Zone to which the node belongs. */ -static void knot_zone_contents_adjust_rrsets(knot_node_t *node, - knot_zone_contents_t *zone) +static int knot_zone_contents_adjust_rrsets(knot_node_t *node, + knot_zone_contents_t *zone) { knot_rrset_t **rrsets = knot_node_get_rrsets(node); short count = knot_node_rrset_count(node); assert(count == 0 || rrsets != NULL); + int ret = KNOT_EOK; for (int r = 0; r < count; ++r) { assert(rrsets[r] != NULL); dbg_zone("Adjusting next RRSet.\n"); - knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone, - node); + ret = knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone, + node); + knot_rrset_t *rrsigs = rrsets[r]->rrsigs; - if (rrsigs != NULL) { + if (ret == KNOT_EOK && rrsigs != NULL) { dbg_zone("Adjusting next RRSIGs.\n"); - knot_zone_contents_adjust_rdata_in_rrset(rrsigs, zone, - node); + ret = knot_zone_contents_adjust_rdata_in_rrset(rrsigs, + zone, + node); + } + + if (ret != KNOT_EOK) { + break; } } free(rrsets); + + return ret; } /*----------------------------------------------------------------------------*/ /*! @@ -529,12 +560,15 @@ static void knot_zone_contents_adjust_rrsets(knot_node_t *node, * in the inserting function, though that may not be always used (e.g. * old changeset processing). */ -static void knot_zone_contents_adjust_node(knot_node_t *node, - knot_zone_contents_t *zone) +static int knot_zone_contents_adjust_node(knot_node_t *node, + knot_zone_contents_t *zone) { // adjust domain names in RDATA /*! \note Enabled again after a LONG time. Should test thoroughly. */ - knot_zone_contents_adjust_rrsets(node, zone); + int ret = knot_zone_contents_adjust_rrsets(node, zone); + if (ret != KNOT_EOK) { + return ret; + } // assure that owner has proper node if (knot_dname_node(knot_node_owner(node)) == NULL) { @@ -566,6 +600,8 @@ static void knot_zone_contents_adjust_node(knot_node_t *node, knot_node_is_deleg_point(node) ? "yes" : "no"); dbg_zone_detail("Non-authoritative: %s\n", knot_node_is_non_auth(node) ? "yes" : "no"); + + return KNOT_EOK; } /*----------------------------------------------------------------------------*/ @@ -621,7 +657,12 @@ dbg_zone_exec_verb( * 2) Do other adjusting (flags, closest enclosers, wildcard children, * etc.). */ - knot_zone_contents_adjust_node(node, zone); + ret = knot_zone_contents_adjust_node(node, zone); + if (ret != KNOT_EOK) { + dbg_xfrin("Failed to adjust node: %s\n", knot_strerror(ret)); + args->err = ret; + return; + } } /*----------------------------------------------------------------------------*/ @@ -816,10 +857,10 @@ dbg_zone_exec_verb( dbg_zone("\n"); char *name_b32 = NULL; - size_t size = base32hex_encode_alloc((char *)hashed_name, hash_size, - &name_b32); + int32_t b32_ret = base32hex_encode_alloc(hashed_name, hash_size, + (uint8_t **)(&name_b32)); - if (size == 0) { + if (b32_ret <= 0) { char *n = knot_dname_to_str(name); dbg_zone("Error while encoding hashed name %s to base32.\n", n); free(n); @@ -829,6 +870,8 @@ dbg_zone_exec_verb( return KNOT_ECRYPTO; } + size_t size = b32_ret; + assert(name_b32 != NULL); free(hashed_name); @@ -1316,22 +1359,21 @@ dbg_zone_exec_detail( return ret; } - ret = knot_zone_tree_insert(zone->nodes, node); - if (ret != KNOT_EOK) { - dbg_zone("Failed to insert node into zone tree.\n"); - return ret; - } - if (use_domain_table) { ret = knot_zone_contents_dnames_from_node_to_table( zone->dname_table, node); if (ret != KNOT_EOK) { - /*! \todo Remove node from the tree and hash table.*/ dbg_zone("Failed to add dnames into table.\n"); return ret; } } + ret = knot_zone_tree_insert(zone->nodes, node); + if (ret != KNOT_EOK) { + dbg_zone("Failed to insert node into zone tree.\n"); + return ret; + } + #ifdef USE_HASH_TABLE // add the node also to the hash table if authoritative, or deleg. point if (zone->table != NULL @@ -1339,7 +1381,10 @@ dbg_zone_exec_detail( (const char *)node->owner->name, node->owner->size, (void *)node) != 0) { dbg_zone("Error inserting node into hash table!\n"); - /*! \todo Remove the node from the tree. */ + knot_zone_tree_node_t *removed; + (void)knot_zone_tree_remove(zone->nodes, knot_node_owner(node), + &removed); + assert(removed->node == node); return KNOT_EHASH; } #endif @@ -2119,7 +2164,7 @@ const knot_node_t *knot_zone_contents_find_previous_nsec3( static void knot_zone_contents_left_chop(char *name, size_t *size) { - short label_size = name[0]; + short label_size = (unsigned char)name[0]; memmove(name, name + label_size + 1, *size -label_size - 1); *size = *size - label_size - 1; diff --git a/src/libknot/zone/zone-diff.c b/src/libknot/zone/zone-diff.c index 734c7c2..a4efff9 100644 --- a/src/libknot/zone/zone-diff.c +++ b/src/libknot/zone/zone-diff.c @@ -163,11 +163,11 @@ static int knot_zone_diff_changeset_add_rrset(knot_changeset_t *changeset, } if (rrset_copy->rrsigs != NULL) { knot_rrset_deep_free(&rrset_copy->rrsigs, 1, 1, 1); - } + } assert(knot_rrset_rrsigs(rrset_copy) == NULL); ret = knot_changeset_add_new_rr(changeset, rrset_copy, - XFRIN_CHANGESET_ADD); + KNOT_CHANGESET_ADD); if (ret != KNOT_EOK) { /* We have to free the copy now! */ knot_rrset_deep_free(&rrset_copy, 1, 1, 1); @@ -193,6 +193,7 @@ static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset, } if (knot_rrset_rdata_rr_count(rrset) == 0) { + /* RDATA are the same, however*/ dbg_zonediff_detail("zone_diff: Nothing to remove.\n"); return KNOT_EOK; } @@ -209,11 +210,12 @@ static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset, } if (rrset_copy->rrsigs != NULL) { knot_rrset_deep_free(&rrset_copy->rrsigs, 1, 1, 1); - } + } + assert(knot_rrset_rrsigs(rrset_copy) == NULL); ret = knot_changeset_add_new_rr(changeset, rrset_copy, - XFRIN_CHANGESET_REMOVE); + KNOT_CHANGESET_REMOVE); if (ret != KNOT_EOK) { /* We have to free the copy now. */ knot_rrset_deep_free(&rrset_copy, 1, 1, 1); @@ -251,6 +253,19 @@ static int knot_zone_diff_add_node(const knot_node_t *node, free(rrsets); return ret; } + + if (knot_rrset_rrsigs(rrsets[i])) { + /* Add RRSIGs of the new node. */ + ret = knot_zone_diff_changeset_add_rrset(changeset, + knot_rrset_rrsigs(rrsets[i])); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: add_node: Cannot " + "add RRSIG (%s).\n", + knot_strerror(ret)); + free(rrsets); + return ret; + } + } } free(rrsets); @@ -292,6 +307,18 @@ dbg_zonediff_exec_detail( free(rrsets); return ret; } + if (knot_rrset_rrsigs(rrsets[i])) { + /* Remove RRSIGs of the old node. */ + ret = knot_zone_diff_changeset_remove_rrset(changeset, + knot_rrset_rrsigs(rrsets[i])); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: remove_node: Cannot " + "remove RRSIG (%s).\n", + knot_strerror(ret)); + free(rrsets); + return ret; + } + } } free(rrsets); @@ -356,8 +383,8 @@ static int knot_zone_diff_rdata_return_changes(const knot_rrset_t *rrset1, * because the list was traversed - there's no match. */ dbg_zonediff("zone_diff: diff_rdata: " - "No match for RR (type=%s owner=%s).\n", - knot_rrtype_to_string(knot_rrset_type(rrset1)), + "No match for RR (type=%u owner=%s).\n", + knot_rrset_type(rrset1), knot_dname_to_str(rrset1->owner)); /* Make a copy of tmp_rdata. */ knot_rdata_t *tmp_rdata_copy = @@ -457,7 +484,7 @@ static int knot_zone_diff_rdata(const knot_rrset_t *rrset1, } int ret = knot_zone_diff_changeset_remove_rrset(changeset, - to_remove); + to_remove); if (ret != KNOT_EOK) { knot_rrset_deep_free(&to_remove, 1, 1, 1); dbg_zonediff("zone_diff: diff_rdata: Could not remove RRs. " @@ -587,12 +614,13 @@ static int knot_zone_diff_rrsets(const knot_rrset_t *rrset1, int ret = knot_zone_diff_rdata(knot_rrset_rrsigs(rrset1), knot_rrset_rrsigs(rrset2), changeset); if (ret != KNOT_EOK) { - dbg_zonediff("zone_diff: diff_rrsets (%s:%s): Failed to diff RRSIGs. " - "They were: %p %p. (%s).\n", - knot_dname_to_str(rrset1->owner), - knot_rrtype_to_string(rrset1->type), - rrset1->rrsigs, - rrset2->rrsigs, knot_strerror(ret)); + dbg_zonediff("zone_diff: diff_rrsets (%s:%s): " + "Failed to diff RRSIGs. " + "They were: %p %p. (%s).\n", + knot_dname_to_str(rrset1->owner), + knot_rrtype_to_string(rrset1->type), + rrset1->rrsigs, + rrset2->rrsigs, knot_strerror(ret)); } /* RRs (=rdata) have to be cross-compared, unfortunalely. */ @@ -706,6 +734,20 @@ static void knot_zone_diff_node(knot_node_t *node, void *data) free(rrsets); return; } + + /* Remove RRSet's RRSIGs as well. */ + if (knot_rrset_rrsigs(rrset)) { + ret = knot_zone_diff_changeset_remove_rrset( + param->changeset, + knot_rrset_rrsigs(rrset)); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_node+: " + "Failed to remove RRSIGs.\n"); + param->ret = ret; + free(rrsets); + return; + } + } } else { dbg_zonediff("zone_diff: diff_node: There is a counterpart " "for RRSet of type %s in second tree.\n", @@ -790,6 +832,18 @@ static void knot_zone_diff_node(knot_node_t *node, void *data) free(rrsets); return; } + if (knot_rrset_rrsigs(rrset)) { + int ret = knot_zone_diff_changeset_add_rrset( + param->changeset, + knot_rrset_rrsigs(rrset)); + if (ret != KNOT_EOK) { + dbg_zonediff("zone_diff: diff_node: " + "Failed to add RRSIGs.\n"); + param->ret = ret; + free(rrsets); + return; + } + } } else { /* Already handled. */ ; @@ -989,7 +1043,10 @@ int knot_zone_diff_create_changesets(const knot_zone_contents_t *z1, return KNOT_EINVAL; } /* Create changesets. */ - int ret = knot_changeset_allocate(changesets); + /* Setting type to IXFR - that's the default, DDNS triggers special + * processing when applied. See #2110 and #2111. + */ + int ret = knot_changeset_allocate(changesets, KNOT_CHANGESET_TYPE_IXFR); if (ret != KNOT_EOK) { dbg_zonediff("zone_diff: create_changesets: " "Could not allocate changesets." @@ -1014,14 +1071,13 @@ int knot_zone_diff_create_changesets(const knot_zone_contents_t *z1, dbg_zonediff_exec_detail( knot_zone_diff_dump_changeset((*changesets)->sets); ); - return KNOT_EOK; } -/* Mostly just for testing. We only shall diff zones in memory later. */ +///* Mostly just for testing. We only shall diff zones in memory later. */ //int knot_zone_diff_zones(const char *zonefile1, const char *zonefile2) //{ - /* Compile test zones. */ +// /* Compile test zones. */ // int ret = zone_read("example.com.", "/home/jan/test/testzone1", "tmpzone1.db", 0); // assert(ret == KNOT_EOK); // ret = zone_read("example.com.", "/home/jan/test/testzone2", "tmpzone2.db", 0); @@ -1040,29 +1096,29 @@ dbg_zonediff_exec_detail( // assert(knot_zone_contents_diff(z1->contents, z2->contents, // changeset) == KNOT_EOK); // dbg_zonediff("Changeset created: From=%d to=%d.\n", changeset.serial_from, -// changeset.serial_to); -//// knot_changesets_t chngsets; -//// chngsets->sets = malloc(sizeof(knot_changeset_t)); -//// chngsets->sets[0] = changeset; -//// chngsets->count = 1; -//// chngsets->allocated = 1; -//// knot_zone_contents_t *new_zone = NULL; -//// ret = xfrin_apply_changesets(z1, chngsets, &new_zone); -//// if (ret != KNOT_EOK) { -//// dbg_zonediff("Application of changesets failed. (%s)\n", -//// knot_strerror(ret)); -//// } +// changeset.serial_to); +// // knot_changesets_t chngsets; +// // chngsets->sets = malloc(sizeof(knot_changeset_t)); +// // chngsets->sets[0] = changeset; +// // chngsets->count = 1; +// // chngsets->allocated = 1; +// // knot_zone_contents_t *new_zone = NULL; +// // ret = xfrin_apply_changesets(z1, chngsets, &new_zone); +// // if (ret != KNOT_EOK) { +// // dbg_zonediff("Application of changesets failed. (%s)\n", +// // knot_strerror(ret)); +// // } -//// assert(new_zone); +// // assert(new_zone); // /* Dump creted zone. */ -//// FILE *f = fopen("testovani", "w"); -//// zone_dump_text(new_zone, f); +// // FILE *f = fopen("testovani", "w"); +// // zone_dump_text(new_zone, f); // knot_zone_deep_free(&z2, 0); // knot_zone_deep_free(&z1, 0); -//// knot_zone_contents_deep_free(&new_zone, 1); -//// knot_zone_free(&z1); +// // knot_zone_contents_deep_free(&new_zone, 1); +// // knot_zone_free(&z1); // knot_free_changeset(&changeset); // exit(0); |