diff --git a/src/common/hattrie/hat-trie.c b/src/common/hattrie/hat-trie.c index 2ec2309844ccf6aeb6449bf7c77cf5c5f0cbc8ea..881b4a47a3e43ec1fb18f2bd5373bfeb56b3e588 100644 --- a/src/common/hattrie/hat-trie.c +++ b/src/common/hattrie/hat-trie.c @@ -1093,3 +1093,12 @@ value_t* hattrie_iter_val(hattrie_iter_t* i) return ahtable_iter_val(i->i); } + +size_t hattrie_size(const hattrie_t *T) +{ + if (T == NULL) { + return 0; + } + + return T->m; +} diff --git a/src/common/hattrie/hat-trie.h b/src/common/hattrie/hat-trie.h index fa6e047a839f8913398367f73a795b56808ec980..5a9a5149099b1b16dae5ed48d054de7cbfaac854 100644 --- a/src/common/hattrie/hat-trie.h +++ b/src/common/hattrie/hat-trie.h @@ -82,6 +82,8 @@ int hattrie_find_lpr (hattrie_t*, const char* key, size_t len, value_t** dst); */ int hattrie_del(hattrie_t* T, const char* key, size_t len); +size_t hattrie_size(const hattrie_t *T); + typedef struct hattrie_iter_t_ hattrie_iter_t; hattrie_iter_t* hattrie_iter_begin (const hattrie_t*, bool sorted); diff --git a/src/libknot/dnssec/zone-nsec.c b/src/libknot/dnssec/zone-nsec.c index e0413fbe95a679ce829289e4651ad9c2eeea7b52..dec4763ae3cccfd6bcd47bfdc053e8ddf045352a 100644 --- a/src/libknot/dnssec/zone-nsec.c +++ b/src/libknot/dnssec/zone-nsec.c @@ -48,6 +48,7 @@ typedef struct { /* - NSEC chain iteration -------------------------------------------------- */ typedef int (*chain_iterate_cb)(knot_node_t *, knot_node_t *, void *); +typedef int (*chain_iterate_nsec_cb)(knot_dname_t *, knot_dname_t *, void *); /*! * \brief Call a function for each piece of the chain formed by sorted nodes. @@ -109,6 +110,62 @@ static int chain_iterate(knot_zone_tree_t *nodes, chain_iterate_cb callback, callback(current, first, data); } +/*! + * \brief Call a function for each piece of the chain formed by sorted nodes. + * + * \note If the callback function returns anything other than KNOT_EOK, the + * iteration is terminated and the error code is propagated. + * + * \param nodes Zone nodes. + * \param callback Callback function. + * \param data Custom data supplied to the callback function. + * + * \return Error code, KNOT_EOK if successful. + */ +static int chain_iterate_nsec(hattrie_t *nodes, chain_iterate_nsec_cb callback, + void *data) +{ + assert(nodes); + assert(callback); + + bool sorted = true; + hattrie_iter_t *it = hattrie_iter_begin(nodes, sorted); + + if (!it) { + return KNOT_ENOMEM; + } + + if (hattrie_iter_finished(it)) { + hattrie_iter_free(it); + return KNOT_EINVAL; + } + + knot_dname_t *first = (knot_dname_t *)*hattrie_iter_val(it); + knot_dname_t *previous = first; + knot_dname_t *current = first; + + hattrie_iter_next(it); + + int result = KNOT_EOK; + while (!hattrie_iter_finished(it)) { + current = (knot_dname_t *)*hattrie_iter_val(it); + + result = callback(previous, current, data); + if (result == KNOT_EOK) { + previous = current; + } else { + hattrie_iter_free(it); + return result; + } + hattrie_iter_next(it); + } + + hattrie_iter_free(it); + +// TODO do i need this? return callback(current, first, data); + return KNOT_EOK; +} + /*! * \brief Add entry for removed NSEC to the changeset. * @@ -891,7 +948,7 @@ static int walk_dname_and_store_empty_nonterminals(knot_dname_t *dname, uint8_t lf[KNOT_DNAME_MAXLEN]; knot_dname_lf(lf, n->owner, NULL); // Store into trie (duplicates 'rewritten') - *ahtable_get(t, (char *)lf+1, *lf) = (void *)0x1; + *ahtable_get(t, (char *)lf+1, *lf) = n->owner; } cut = knot_wire_next_label(dname, NULL); } @@ -940,7 +997,7 @@ static int update_changes_with_non_terminals(const knot_zone_contents_t *zone, hattrie_iter_free(itt); // Reinsert ahtable values into trie (dname already converted) - ahtable_iter_t ith = { '\0'}; + ahtable_iter_t ith = { '\0' }; ahtable_iter_begin(nterminal_t, &ith, sort); for (; !ahtable_iter_finished(&ith); ahtable_iter_next(&ith)) { // Store keys from table directly to trie @@ -951,37 +1008,163 @@ static int update_changes_with_non_terminals(const knot_zone_contents_t *zone, signed_info_t *info = malloc(sizeof(signed_info_t)); if (info == NULL) { ERR_ALLOC_FAILED; - ahtable_free(nterminal_t); ahtable_iter_free(&ith); + ahtable_free(nterminal_t); return KNOT_ENOMEM; } memset(info, 0, sizeof(signed_info_t)); + info->dname = + knot_dname_copy((knot_dname_t *)(*ahtable_iter_val(&ith))); + if (info->dname == NULL) { + ahtable_iter_free(&ith); + ahtable_free(nterminal_t); + return KNOT_ENOMEM; + } *hattrie_get(sorted_changes, k, key_size) = info; } ahtable_iter_free(&ith); + ahtable_free(nterminal_t); return KNOT_EOK; } -static int fix_nsec_chain_using_changes(const knot_zone_contents_t *zone, - const hattrie_t *sorted_changes, - knot_changeset_t *out_ch, - const knot_zone_keys_t *zone_keys, - const knot_dnssec_policy_t *policy) +static int create_nsec3_hashes_from_trie(const hattrie_t *sorted_changes, + const knot_zone_contents_t *zone, + hattrie_t **out) { + assert(sorted_changes); + assert(hattrie_size(sorted_changes) > 0); + *out = hattrie_create(); + if (*out == NULL) { + return KNOT_ENOMEM; + } + + const bool sort = false; + hattrie_iter_t *itt = hattrie_iter_begin(sorted_changes, sort); + if (itt == NULL) { + return KNOT_ERROR; + } + + for (; !hattrie_iter_finished(itt); hattrie_iter_next(itt)) { + knot_dname_t *nsec3_name = + create_nsec3_owner((knot_dname_t *)*hattrie_iter_val(itt), + zone->apex->owner, + &zone->nsec3_params); + if (nsec3_name == NULL) { + return KNOT_ERROR; + } + hattrie_insert_dname(*out, nsec3_name); + } + hattrie_iter_free(itt); return KNOT_EOK; } -static int fix_nsec3_chain_using_changes(const knot_zone_contents_t *zone, - const hattrie_t *sorted_changes, - knot_changeset_t *out_ch, - const knot_zone_keys_t *zone_keys, - const knot_dnssec_policy_t *policy) +static int update_nsec(const knot_node_t *from, const knot_node_t *to, + knot_changeset_t *out_ch, uint32_t soa_min, + bool is_apex) { + assert(from && to && out_ch); + const knot_rrset_t *nsec_rrset = knot_node_rrset(from, + KNOT_RRTYPE_NSEC); + // Create new NSEC + knot_rrset_t *new_nsec = + create_nsec_rrset(from, to, soa_min, is_apex); + if (new_nsec == NULL) { + return KNOT_ERROR; + } + // If node in zone has NSEC record, drop it if needed + if (nsec_rrset) { + if (!knot_rrset_equal(new_nsec, nsec_rrset, + KNOT_RRSET_COMPARE_WHOLE)) { + // Drop old + int ret = changeset_remove_nsec(nsec_rrset, + out_ch); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&new_nsec, 1); + return ret; + } + // Add new + ret = knot_changeset_add_rrset(out_ch, new_nsec, + KNOT_CHANGESET_ADD); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&new_nsec, 1); + return ret; + } + } else { + // All good, no need to update + knot_rrset_deep_free(&new_nsec, 1); + return KNOT_EOK; + } + } else { + int ret = knot_changeset_add_rrset(out_ch, new_nsec, + KNOT_CHANGESET_ADD); + if (ret != KNOT_EOK) { + knot_rrset_deep_free(&new_nsec, 1); + return ret; + } + } return KNOT_EOK; } +typedef struct chain_fix_data { + const knot_zone_contents_t *zone; + knot_changeset_t *out_ch; + const knot_zone_keys_t *zone_keys; + const knot_dnssec_policy_t *policy; +} chain_fix_data_t; + +static int fix_nsec_chain(knot_dname_t *a, knot_dname_t *b, void *d) +{ + chain_fix_data_t *fix_data = (chain_fix_data_t *)d; + assert(fix_data); + + // Get previous record from zone tree + bool nsec_node_found = false; + const knot_node_t *prev_zone_node = NULL; + while (!nsec_node_found) { + if (knot_dname_is_equal(b, prev_zone_node->owner)) { + if (fix_data->zone->apex == prev_zone_node) { + // Only one node in zone + + } else { + /*! + * Something is really wrong here - + * maybe the zone is unsigned? + */ + return KNOT_ERROR; + } + } + prev_zone_node = + knot_zone_contents_find_previous(fix_data->zone, b); + assert(prev_zone_node); + nsec_node_found = + knot_node_rrset(prev_zone_node, KNOT_RRTYPE_NSEC) ? + true : false; + } + + /*! + * Find out whether we should connect the 'b' node to node from zone + * or to node from changeset. + */ + int dname_cmp = knot_dname_cmp(prev_zone_node->owner, a); + assert(dname_cmp != 0); + const knot_node_t *prev_node = NULL; + if (dname_cmp < 0) { + // Node in zone is farther in tree, use changeset node + prev_node = knot_zone_contents_find_node(fix_data->zone, a); + } else { + prev_node = prev_zone_node; + } + + assert(prev_node); // NSEC has to be left in the node no matter what + int ret = update_nsec(prev_node, + knot_zone_contents_find_node(fix_data->zone, b), + fix_data->out_ch, + 3600, prev_node == fix_data->zone->apex); + return ret; +} + /* - public API ------------------------------------------------------------ */ /*! @@ -1060,22 +1243,41 @@ int knot_zone_fix_chain(const knot_zone_contents_t *zone, { if (zone == NULL || sorted_changes == NULL || zone_keys == NULL || policy == NULL) { + return KNOT_EINVAL; + } + + if (hattrie_size(sorted_changes) == 0) { + // no changes, no fixing return KNOT_EOK; } int ret = KNOT_EOK; + + // Prepare data for chain fixing functions + chain_fix_data_t fix_data = { .zone = zone, + .out_ch = out_ch, + .zone_keys = zone_keys, + .policy = policy }; if (is_nsec3_enabled(zone)) { ret = update_changes_with_non_terminals(zone, sorted_changes); if (ret != KNOT_EOK) { return ret; } - ret = fix_nsec3_chain_using_changes(zone, sorted_changes, - out_ch, zone_keys, policy); + + // Create and sort NSEC3 hashes + hattrie_t *nsec3_names = NULL; + ret = create_nsec3_hashes_from_trie(sorted_changes, + zone, + &nsec3_names); + assert(0); +// ret = chain_iterate(nsec3_names, fix_nsec3_chain, &fix_data); } else { - ret = fix_nsec_chain_using_changes(zone, sorted_changes, - out_ch, zone_keys, policy); + ret = chain_iterate_nsec(sorted_changes, fix_nsec_chain, + &fix_data); } + dbg_dnssec_verb("NSEC(3) chain fixed (%s)", knot_strerror(ret)); + return ret; } diff --git a/src/libknot/dnssec/zone-sign.c b/src/libknot/dnssec/zone-sign.c index 310b6cda9ce3e11781496e0d46de0e2691838980..c076d72ec8d50958a7ae29820b391ad1ab781d6b 100644 --- a/src/libknot/dnssec/zone-sign.c +++ b/src/libknot/dnssec/zone-sign.c @@ -1004,11 +1004,6 @@ static bool rr_already_signed(const knot_rrset_t *rrset, hattrie_t *t) knot_dname_lf(lf, rrset->owner, NULL); signed_info_t *info = (signed_info_t *)hattrie_tryget(t, (char *)lf+1, *lf); - assert(info); - if (rr_type_in_list(rrset, info->type_list)) { - return true; - } - if (info == NULL) { // Create new info struct info = malloc(sizeof(signed_info_t));