static int knot_zone_diff_load_soas(const zone_contents_t *zone1, const zone_contents_t *zone2, changeset_t *changeset) { if (zone1 == NULL || zone2 == NULL || changeset == NULL) { return KNOT_EINVAL; } const zone_node_t *apex1 = zone1->apex; const zone_node_t *apex2 = zone2->apex; if (apex1 == NULL || apex2 == NULL) { return KNOT_EINVAL; } knot_rrset_t soa_rrset1 = node_rrset(apex1, KNOT_RRTYPE_SOA); knot_rrset_t soa_rrset2 = node_rrset(apex2, KNOT_RRTYPE_SOA); if (knot_rrset_empty(&soa_rrset1) || knot_rrset_empty(&soa_rrset2)) { return KNOT_EINVAL; } if (soa_rrset1.rrs.rr_count == 0 || soa_rrset2.rrs.rr_count == 0) { return KNOT_EINVAL; } int64_t soa_serial1 = knot_soa_serial(&soa_rrset1.rrs); int64_t soa_serial2 = knot_soa_serial(&soa_rrset2.rrs); if (serial_compare(soa_serial1, soa_serial2) == 0) { return KNOT_ENODIFF; } if (serial_compare(soa_serial1, soa_serial2) > 0) { return KNOT_ERANGE; } assert(changeset); changeset->soa_from = knot_rrset_copy(&soa_rrset1, NULL); if (changeset->soa_from == NULL) { return KNOT_ENOMEM; } changeset->soa_to = knot_rrset_copy(&soa_rrset2, NULL); if (changeset->soa_to == NULL) { knot_rrset_free(&changeset->soa_from, NULL); return KNOT_ENOMEM; } dbg_zonediff_verb("zone_diff: load_soas: SOAs diffed. " "(%"PRId64" -> %"PRId64")\n", soa_serial1, soa_serial2); return KNOT_EOK; }
static int insert_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n, bool nsec3) { if (z == NULL || knot_rrset_empty(rr) || n == NULL) { return KNOT_EINVAL; } // check if the RRSet belongs to the zone if (!knot_dname_is_sub(rr->owner, z->apex->owner) && !knot_dname_is_equal(rr->owner, z->apex->owner)) { return KNOT_EOUTOFZONE; } int ret = KNOT_EOK; if (*n == NULL) { *n = nsec3 ? zone_contents_get_nsec3_node(z, rr->owner) : zone_contents_get_node(z, rr->owner); if (*n == NULL) { // Create new, insert *n = node_new(rr->owner, NULL); if (*n == NULL) { return KNOT_ENOMEM; } ret = nsec3 ? zone_contents_add_nsec3_node(z, *n) : zone_contents_add_node(z, *n, true); if (ret != KNOT_EOK) { node_free(n, NULL); } } } return node_add_rrset(*n, rr, NULL); }
/*! * \brief Connect two nodes by adding a NSEC RR into the first node. * * Callback function, signature chain_iterate_cb. * * \param a First node. * \param b Second node (immediate follower of a). * \param data Pointer to nsec_chain_iterate_data_t holding parameters * including changeset. * * \return Error code, KNOT_EOK if successful. */ static int connect_nsec_nodes(zone_node_t *a, zone_node_t *b, nsec_chain_iterate_data_t *data) { assert(a); assert(b); assert(data); if (b->rrset_count == 0 || b->flags & NODE_FLAGS_NONAUTH) { return NSEC_NODE_SKIP; } int ret = 0; /*! * If the node has no other RRSets than NSEC (and possibly RRSIGs), * just remove the NSEC and its RRSIG, they are redundant */ if (node_rrtype_exists(b, KNOT_RRTYPE_NSEC) && knot_nsec_empty_nsec_and_rrsigs_in_node(b)) { ret = knot_nsec_changeset_remove(b, data->changeset); if (ret != KNOT_EOK) { return ret; } // Skip the 'b' node return NSEC_NODE_SKIP; } // create new NSEC knot_rrset_t *new_nsec = create_nsec_rrset(a, b, data->ttl); if (!new_nsec) { dbg_dnssec_detail("Failed to create new NSEC.\n"); return KNOT_ENOMEM; } knot_rrset_t old_nsec = node_rrset(a, KNOT_RRTYPE_NSEC); if (!knot_rrset_empty(&old_nsec)) { if (knot_rrset_equal(new_nsec, &old_nsec, KNOT_RRSET_COMPARE_WHOLE)) { // current NSEC is valid, do nothing dbg_dnssec_detail("NSECs equal.\n"); knot_rrset_free(&new_nsec, NULL); return KNOT_EOK; } dbg_dnssec_detail("NSECs not equal, replacing.\n"); // Mark the node so that we do not sign this NSEC a->flags |= NODE_FLAGS_REMOVED_NSEC; ret = knot_nsec_changeset_remove(a, data->changeset); if (ret != KNOT_EOK) { knot_rrset_free(&new_nsec, NULL); return ret; } } dbg_dnssec_detail("Adding new NSEC to changeset.\n"); // Add new NSEC to the changeset (no matter if old was removed) return knot_changeset_add_rrset(data->changeset, new_nsec, KNOT_CHANGESET_ADD); }
/*! * \brief Add entry for removed NSEC to the changeset. */ int knot_nsec_changeset_remove(const zone_node_t *n, knot_changeset_t *changeset) { if (changeset == NULL) { return KNOT_EINVAL; } int result = KNOT_EOK; knot_rrset_t *nsec = node_create_rrset(n, KNOT_RRTYPE_NSEC); if (nsec == NULL) { nsec = node_create_rrset(n, KNOT_RRTYPE_NSEC3); } if (nsec) { // update changeset result = knot_changeset_add_rrset(changeset, nsec, KNOT_CHANGESET_REMOVE); if (result != KNOT_EOK) { knot_rrset_free(&nsec, NULL); return result; } } knot_rrset_t rrsigs = node_rrset(n, KNOT_RRTYPE_RRSIG); if (!knot_rrset_empty(&rrsigs)) { knot_rrset_t *synth_rrsigs = knot_rrset_new(n->owner, KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, NULL); if (synth_rrsigs == NULL) { return KNOT_ENOMEM; } result = knot_synth_rrsig(KNOT_RRTYPE_NSEC, &rrsigs.rrs, &synth_rrsigs->rrs, NULL); if (result == KNOT_ENOENT) { // Try removing NSEC3 RRSIGs result = knot_synth_rrsig(KNOT_RRTYPE_NSEC3, &rrsigs.rrs, &synth_rrsigs->rrs, NULL); } if (result != KNOT_EOK) { knot_rrset_free(&synth_rrsigs, NULL); if (result != KNOT_ENOENT) { return result; } return KNOT_EOK; } // store RRSIG result = knot_changeset_add_rrset(changeset, synth_rrsigs, KNOT_CHANGESET_REMOVE); if (result != KNOT_EOK) { knot_rrset_free(&synth_rrsigs, NULL); return result; } } return KNOT_EOK; }
static int knot_zone_diff_rdata(const knot_rrset_t *rrset1, const knot_rrset_t *rrset2, changeset_t *changeset) { if ((changeset == NULL) || (rrset1 == NULL && rrset2 == NULL)) { return KNOT_EINVAL; } /* * The easiest solution is to remove all the RRs that had no match and * to add all RRs that had no match, but those from second RRSet. */ /* Get RRs to add to zone and to remove from zone. */ knot_rrset_t to_remove; knot_rrset_t to_add; if (rrset1 != NULL && rrset2 != NULL) { int ret = knot_zone_diff_rdata_return_changes(rrset1, rrset2, &to_remove); if (ret != KNOT_EOK) { return ret; } ret = knot_zone_diff_rdata_return_changes(rrset2, rrset1, &to_add); if (ret != KNOT_EOK) { return ret; } } if (!knot_rrset_empty(&to_remove)) { int ret = changeset_rem_rrset(changeset, &to_remove); knot_rdataset_clear(&to_remove.rrs, NULL); if (ret != KNOT_EOK) { knot_rdataset_clear(&to_add.rrs, NULL); return ret; } } if (!knot_rrset_empty(&to_add)) { int ret = changeset_add_rrset(changeset, &to_add); knot_rdataset_clear(&to_add.rrs, NULL); return ret; } return KNOT_EOK; }
static int answer_query(knot_pkt_t *pkt, pack_t *addr_set, struct kr_query *qry) { uint16_t rrtype = qry->stype; uint16_t rrclass = qry->sclass; if (rrtype != KNOT_RRTYPE_A && rrtype != KNOT_RRTYPE_AAAA) { return kr_error(ENOENT); } knot_dname_t *qname = knot_dname_copy(qry->sname, &pkt->mm); knot_rrset_t rr; knot_rrset_init(&rr, qname, rrtype, rrclass); int family_len = sizeof(struct in_addr); if (rr.type == KNOT_RRTYPE_AAAA) { family_len = sizeof(struct in6_addr); } /* Append address records from hints */ uint8_t *addr = pack_head(*addr_set); while (addr != pack_tail(*addr_set)) { size_t len = pack_obj_len(addr); void *addr_val = pack_obj_val(addr); if (len == family_len) { knot_rrset_add_rdata(&rr, addr_val, len, 0, &pkt->mm); } addr = pack_obj_next(addr); } int ret = kr_error(ENOENT); if (!knot_rrset_empty(&rr)) { /* Update packet question */ if (!knot_dname_is_equal(knot_pkt_qname(pkt), qname)) { KR_PKT_RECYCLE(pkt); knot_pkt_put_question(pkt, qname, rrclass, rrtype); } /* Append to packet */ ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, &rr, KNOT_PF_FREE); } /* Clear RR if failed */ if (ret != 0) { knot_rrset_clear(&rr, &pkt->mm); } return ret; }
/*! * \brief Marks all NSEC3 nodes in zone from which RRSets are to be removed. * * For each NSEC3 RRSet in the changeset finds its node and marks it with the * 'removed' flag. */ static int mark_removed_nsec3(changeset_t *out_ch, const zone_contents_t *zone) { if (zone_tree_is_empty(zone->nsec3_nodes)) { return KNOT_EOK; } changeset_iter_t itt; changeset_iter_rem(&itt, out_ch, false); knot_rrset_t rr = changeset_iter_next(&itt); while (!knot_rrset_empty(&rr)) { int ret = mark_nsec3(&rr, zone->nsec3_nodes); if (ret != KNOT_EOK) { changeset_iter_clear(&itt); return ret; } rr = changeset_iter_next(&itt); } changeset_iter_clear(&itt); return KNOT_EOK; }
/*!< \todo this could be generic function for adding / removing. */ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data) { if (node_ptr == NULL || *node_ptr == NULL || data == NULL) { return KNOT_EINVAL; } zone_node_t *node = *node_ptr; struct zone_diff_param *param = (struct zone_diff_param *)data; if (param->changeset == NULL) { return KNOT_EINVAL; } /* * First, we have to search the second tree to see if there's according * node, if not, the whole node has been removed. */ const zone_node_t *node_in_second_tree = NULL; const knot_dname_t *node_owner = node->owner; assert(node_owner); zone_tree_find(param->nodes, node_owner, &node_in_second_tree); if (node_in_second_tree == NULL) { return knot_zone_diff_remove_node(param->changeset, node); } assert(node_in_second_tree != node); /* The nodes are in both trees, we have to diff each RRSet. */ if (node->rrset_count == 0) { /* * If there are no RRs in the first tree, all of the RRs * in the second tree will have to be inserted to ADD section. */ return knot_zone_diff_add_node(node_in_second_tree, param->changeset); } for (unsigned i = 0; i < node->rrset_count; i++) { /* Search for the RRSet in the node from the second tree. */ knot_rrset_t rrset = node_rrset_at(node, i); /* SOAs are handled explicitely. */ if (rrset.type == KNOT_RRTYPE_SOA) { continue; } knot_rrset_t rrset_from_second_node = node_rrset(node_in_second_tree, rrset.type); if (knot_rrset_empty(&rrset_from_second_node)) { /* RRSet has been removed. Make a copy and remove. */ int ret = changeset_rem_rrset( param->changeset, &rrset); if (ret != KNOT_EOK) { return ret; } } else { /* Diff RRSets. */ int ret = knot_zone_diff_rrsets(&rrset, &rrset_from_second_node, param->changeset); if (ret != KNOT_EOK) { return ret; } } } for (unsigned i = 0; i < node_in_second_tree->rrset_count; i++) { /* Search for the RRSet in the node from the second tree. */ knot_rrset_t rrset = node_rrset_at(node_in_second_tree, i); /* SOAs are handled explicitely. */ if (rrset.type == KNOT_RRTYPE_SOA) { continue; } knot_rrset_t rrset_from_first_node = node_rrset(node, rrset.type); if (knot_rrset_empty(&rrset_from_first_node)) { /* RRSet has been added. Make a copy and add. */ int ret = changeset_add_rrset( param->changeset, &rrset); if (ret != KNOT_EOK) { return ret; } } } return KNOT_EOK; }
int main(int argc, char *argv[]) { plan(23); // Test with NULL changeset ok(changeset_size(NULL) == 0, "changeset: NULL size"); ok(changeset_empty(NULL), "changeset: NULL empty"); // Test creation. knot_dname_t *d = knot_dname_from_str_alloc("test."); assert(d); changeset_t *ch = changeset_new(d); knot_dname_free(&d, NULL); ok(ch != NULL, "changeset: new"); ok(changeset_empty(ch), "changeset: empty"); ch->soa_to = (knot_rrset_t *)0xdeadbeef; ok(!changeset_empty(ch), "changseset: empty SOA"); ch->soa_to = NULL; ok(changeset_size(ch) == 0, "changeset: empty size"); // Test additions. d = knot_dname_from_str_alloc("non.terminals.test."); assert(d); knot_rrset_t *apex_txt_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, NULL); assert(apex_txt_rr); uint8_t data[8] = "\7teststr"; knot_rrset_add_rdata(apex_txt_rr, data, sizeof(data), 3600, NULL); int ret = changeset_add_rrset(ch, apex_txt_rr); ok(ret == KNOT_EOK, "changeset: add RRSet"); ok(changeset_size(ch) == 1, "changeset: size add"); ret = changeset_rem_rrset(ch, apex_txt_rr); ok(ret == KNOT_EOK, "changeset: rem RRSet"); ok(changeset_size(ch) == 2, "changeset: size remove"); ok(!changeset_empty(ch), "changeset: not empty"); // Add another RR to node. knot_rrset_t *apex_spf_rr = knot_rrset_new(d, KNOT_RRTYPE_SPF, KNOT_CLASS_IN, NULL); assert(apex_spf_rr); knot_rrset_add_rdata(apex_spf_rr, data, sizeof(data), 3600, NULL); ret = changeset_add_rrset(ch, apex_spf_rr); ok(ret == KNOT_EOK, "changeset: add multiple"); // Add another node. knot_dname_free(&d, NULL); d = knot_dname_from_str_alloc("here.come.more.non.terminals.test"); assert(d); knot_rrset_t *other_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, NULL); assert(other_rr); knot_rrset_add_rdata(other_rr, data, sizeof(data), 3600, NULL); ret = changeset_add_rrset(ch, other_rr); ok(ret == KNOT_EOK, "changeset: remove multiple"); // Test add traversal. changeset_iter_t it; ret = changeset_iter_add(&it, ch, true); ok(ret == KNOT_EOK, "changeset: create iter add"); // Order: non.terminals.test. TXT, SPF, here.come.more.non.terminals.test. TXT. knot_rrset_t iter = changeset_iter_next(&it); bool trav_ok = knot_rrset_equal(&iter, apex_txt_rr, KNOT_RRSET_COMPARE_WHOLE); iter = changeset_iter_next(&it); trav_ok = trav_ok && knot_rrset_equal(&iter, apex_spf_rr, KNOT_RRSET_COMPARE_WHOLE); iter = changeset_iter_next(&it); trav_ok = trav_ok && knot_rrset_equal(&iter, other_rr, KNOT_RRSET_COMPARE_WHOLE); ok(trav_ok, "changeset: add traversal"); iter = changeset_iter_next(&it); changeset_iter_clear(&it); ok(knot_rrset_empty(&iter), "changeset: traversal: skip non-terminals"); // Test remove traversal. ret = changeset_iter_rem(&it, ch, false); ok(ret == KNOT_EOK, "changeset: create iter rem"); iter = changeset_iter_next(&it); ok(knot_rrset_equal(&iter, apex_txt_rr, KNOT_RRSET_COMPARE_WHOLE), "changeset: rem traversal"); changeset_iter_clear(&it); // Test all traversal - just count. ret = changeset_iter_all(&it, ch, false); ok(ret == KNOT_EOK, "changest: create iter all"); size_t size = 0; iter = changeset_iter_next(&it); while (!knot_rrset_empty(&iter)) { ++size; iter = changeset_iter_next(&it); } changeset_iter_clear(&it); ok(size == 4, "changeset: iter all"); // Create new changeset. knot_dname_free(&d, NULL); d = knot_dname_from_str_alloc("test."); assert(d); changeset_t *ch2 = changeset_new(d); knot_dname_free(&d, NULL); assert(ch2); // Add something to add section. knot_dname_free(&apex_txt_rr->owner, NULL); apex_txt_rr->owner = knot_dname_from_str_alloc("something.test."); assert(apex_txt_rr->owner); ret = changeset_add_rrset(ch2, apex_txt_rr); assert(ret == KNOT_EOK); // Add something to remove section. knot_dname_free(&apex_txt_rr->owner, NULL); apex_txt_rr->owner = knot_dname_from_str_alloc("and.now.for.something.completely.different.test."); assert(apex_txt_rr->owner); ret = changeset_rem_rrset(ch2, apex_txt_rr); assert(ret == KNOT_EOK); // Test merge. ret = changeset_merge(ch, ch2); ok(ret == KNOT_EOK && changeset_size(ch) == 6, "changeset: merge"); // Test cleanup. changeset_clear(ch); ok(changeset_empty(ch), "changeset: clear"); free(ch); list_t chgs; init_list(&chgs); add_head(&chgs, &ch2->n); changesets_clear(&chgs); ok(changeset_empty(ch2), "changeset: clear list"); free(ch2); knot_rrset_free(&apex_txt_rr, NULL); knot_rrset_free(&apex_spf_rr, NULL); knot_rrset_free(&other_rr, NULL); return 0; }