Exemple #1
0
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;
}
Exemple #2
0
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);
}
Exemple #3
0
/*!
 * \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);
}
Exemple #4
0
/*!
 * \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;
}
Exemple #5
0
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;
}
Exemple #6
0
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;
}
Exemple #7
0
/*!
 * \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;
}
Exemple #8
0
/*!< \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;
}
Exemple #9
0
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;
}