예제 #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;
}
예제 #2
0
파일: nsec-chain.c 프로젝트: dnstap/knot
/*!
 * \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);
}
예제 #3
0
파일: nsec-chain.c 프로젝트: dnstap/knot
/*!
 * \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;
}
예제 #4
0
파일: handlers.c 프로젝트: idtek/knot
/*! \brief Create zone query packet. */
static knot_pkt_t *zone_query(const zone_t *zone, uint16_t pkt_type, knot_mm_t *mm)
{
	/* Determine query type and opcode. */
	uint16_t query_type = KNOT_RRTYPE_SOA;
	uint16_t opcode = KNOT_OPCODE_QUERY;
	switch(pkt_type) {
	case KNOT_QUERY_AXFR: query_type = KNOT_RRTYPE_AXFR; break;
	case KNOT_QUERY_IXFR: query_type = KNOT_RRTYPE_IXFR; break;
	case KNOT_QUERY_NOTIFY: opcode = KNOT_OPCODE_NOTIFY; break;
	}

	knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, mm);
	if (pkt == NULL) {
		return NULL;
	}

	knot_wire_set_id(pkt->wire, dnssec_random_uint16_t());
	knot_wire_set_opcode(pkt->wire, opcode);
	if (pkt_type == KNOT_QUERY_NOTIFY) {
		knot_wire_set_aa(pkt->wire);
	}

	knot_pkt_put_question(pkt, zone->name, KNOT_CLASS_IN, query_type);

	/* Put current SOA (optional). */
	zone_contents_t *contents = zone->contents;
	if (pkt_type == KNOT_QUERY_IXFR) {  /* RFC1995, SOA in AUTHORITY. */
		knot_pkt_begin(pkt, KNOT_AUTHORITY);
		knot_rrset_t soa_rr = node_rrset(contents->apex, KNOT_RRTYPE_SOA);
		knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, &soa_rr, 0);
	} else if (pkt_type == KNOT_QUERY_NOTIFY) { /* RFC1996, SOA in ANSWER. */
		knot_pkt_begin(pkt, KNOT_ANSWER);
		knot_rrset_t soa_rr = node_rrset(contents->apex, KNOT_RRTYPE_SOA);
		knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, &soa_rr, 0);
	}

	return pkt;
}
예제 #5
0
int xfr_process_list(knot_pkt_t *pkt, xfr_put_cb process_item,
                     struct query_data *qdata)
{
	if (pkt == NULL || qdata == NULL || qdata->ext == NULL) {
		return KNOT_EINVAL;
	}

	int ret = KNOT_EOK;
	mm_ctx_t *mm = qdata->mm;
	struct xfr_proc *xfer = qdata->ext;

	zone_contents_t *zone = qdata->zone->contents;
	knot_rrset_t soa_rr = node_rrset(zone->apex, KNOT_RRTYPE_SOA);

	/* Prepend SOA on first packet. */
	if (xfer->npkts == 0) {
		ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC);
		if (ret != KNOT_EOK) {
			return ret;
		}
	}

	/* Process all items in the list. */
	while (!EMPTY_LIST(xfer->nodes)) {
		ptrnode_t *head = HEAD(xfer->nodes);
		ret = process_item(pkt, head->d, xfer);
		if (ret == KNOT_EOK) { /* Finished. */
			/* Complete change set. */
			rem_node((node_t *)head);
			mm_free(mm, head);
		} else { /* Packet full or other error. */
			break;
		}
	}

	/* Append SOA on last packet. */
	if (ret == KNOT_EOK) {
		ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC);
	}

	/* Update counters. */
	xfer->npkts  += 1;
	xfer->nbytes += pkt->size;

	return ret;
}
예제 #6
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;
}
예제 #7
0
파일: process_query.c 프로젝트: dnstap/knot
int main(int argc, char *argv[])
{
	plan(8*6 + 3); /* exec_query = 6 TAP tests */

	/* Create processing context. */
	knot_process_t query_ctx;
	memset(&query_ctx, 0, sizeof(knot_process_t));
	mm_ctx_mempool(&query_ctx.mm, sizeof(knot_pkt_t));

	/* Create name server. */
	server_t server;
	server_init(&server);
	server.opt_rr = knot_edns_new();
	knot_edns_set_version(server.opt_rr, EDNS_VERSION);
	knot_edns_set_payload(server.opt_rr, 4096);
	conf()->identity = strdup("bogus.ns");
	conf()->version = strdup("0.11");

	/* Insert root zone. */
	create_root_zone(&server, &query_ctx.mm);
	zone_t *zone = knot_zonedb_find(server.zone_db, ROOT_DNAME);

	/* Prepare. */
	int state = NS_PROC_FAIL;
	uint8_t query_wire[KNOT_WIRE_MAX_PKTSIZE];
	uint16_t query_len = KNOT_WIRE_MAX_PKTSIZE;
	knot_pkt_t *query = knot_pkt_new(query_wire, query_len, &query_ctx.mm);

	/* Create query processing parameter. */
	struct sockaddr_storage ss;
	memset(&ss, 0, sizeof(struct sockaddr_storage));
	sockaddr_set(&ss, AF_INET, "127.0.0.1", 53);
	struct process_query_param param = {0};
	param.query_source = &ss;
	param.server = &server;

	/* Query processor (CH zone) */
	state = knot_process_begin(&query_ctx, &param, NS_PROC_QUERY);
	const uint8_t chaos_dname[] = "\2""id""\6""server"; /* id.server */
	knot_pkt_clear(query);
	knot_pkt_put_question(query, chaos_dname, KNOT_CLASS_CH, KNOT_RRTYPE_TXT);
	exec_query(&query_ctx, "CH TXT", query->wire, query->size, KNOT_RCODE_NOERROR);

	/* Query processor (valid input). */
	state = knot_process_reset(&query_ctx);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
	exec_query(&query_ctx, "IN/root", query->wire, query->size, KNOT_RCODE_NOERROR);

	/* Query processor (-1 bytes, not enough data). */
	state = knot_process_reset(&query_ctx);
	exec_query(&query_ctx, "IN/few-data", query->wire, query->size - 1, KNOT_RCODE_FORMERR);

	/* Query processor (+1 bytes trailing). */
	state = knot_process_reset(&query_ctx);
	query->wire[query->size] = '\1'; /* Initialize the "garbage" value. */
	exec_query(&query_ctx, "IN/trail-garbage", query->wire, query->size + 1, KNOT_RCODE_FORMERR);

	/* Forge NOTIFY query from SOA query. */
	state = knot_process_reset(&query_ctx);
	knot_wire_set_opcode(query->wire, KNOT_OPCODE_NOTIFY);
	exec_query(&query_ctx, "IN/notify", query->wire, query->size, KNOT_RCODE_NOTAUTH);

	/* Forge AXFR query. */
	knot_process_reset(&query_ctx);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_AXFR);
	exec_query(&query_ctx, "IN/axfr", query->wire, query->size, KNOT_RCODE_NOTAUTH);

	/* Forge IXFR query (badly formed, no SOA in AUTHORITY section). */
	knot_process_reset(&query_ctx);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR);
	exec_query(&query_ctx, "IN/ixfr-formerr", query->wire, query->size, KNOT_RCODE_FORMERR);

	/* Forge IXFR query (well formed). */
	knot_process_reset(&query_ctx);
	/* Append SOA RR. */
	knot_rrset_t soa_rr = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
	knot_pkt_begin(query, KNOT_AUTHORITY);
	knot_pkt_put(query, COMPR_HINT_NONE, &soa_rr, 0);
	exec_query(&query_ctx, "IN/ixfr", query->wire, query->size, KNOT_RCODE_NOTAUTH);

	/* \note Tests below are not possible without proper zone and zone data. */
	/* #189 Process UPDATE query. */
	/* #189 Process AXFR client. */
	/* #189 Process IXFR client. */

	/* Query processor (smaller than DNS header, ignore). */
	state = knot_process_reset(&query_ctx);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
	state = knot_process_in(query->wire, KNOT_WIRE_HEADER_SIZE - 1, &query_ctx);
	ok(state == NS_PROC_NOOP, "ns: IN/less-than-header query ignored");

	/* Query processor (response, ignore). */
	state = knot_process_reset(&query_ctx);
	knot_wire_set_qr(query->wire);
	state = knot_process_in(query->wire, query->size, &query_ctx);
	ok(state == NS_PROC_NOOP, "ns: IN/less-than-header query ignored");

	/* Finish. */
	state = knot_process_finish(&query_ctx);
	ok(state == NS_PROC_NOOP, "ns: processing end" );

	/* Cleanup. */
	mp_delete((struct mempool *)query_ctx.mm.ctx);
	server_deinit(&server);

	return 0;
}
예제 #8
0
파일: process_query.c 프로젝트: idtek/knot
int main(int argc, char *argv[])
{
	plan(8*6 + 4); /* exec_query = 6 TAP tests */

	knot_mm_t mm;
	mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);

	/* Create processing context. */
	knot_layer_t proc;
	memset(&proc, 0, sizeof(knot_layer_t));
	proc.mm = &mm;

	/* Create fake server environment. */
	server_t server;
	int ret = create_fake_server(&server, proc.mm);
	ok(ret == KNOT_EOK, "ns: fake server initialization");

	zone_t *zone = knot_zonedb_find(server.zone_db, ROOT_DNAME);

	/* Prepare. */
	knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, proc.mm);

	/* Create query processing parameter. */
	struct sockaddr_storage ss;
	memset(&ss, 0, sizeof(struct sockaddr_storage));
	sockaddr_set(&ss, AF_INET, "127.0.0.1", 53);
	struct process_query_param param = {0};
	param.remote = &ss;
	param.server = &server;

	/* Query processor (CH zone) */
	knot_layer_begin(&proc, NS_PROC_QUERY, &param);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, IDSERVER_DNAME, KNOT_CLASS_CH, KNOT_RRTYPE_TXT);
	exec_query(&proc, "CH TXT", query, KNOT_RCODE_NOERROR);

	/* Query processor (valid input). */
	knot_layer_reset(&proc);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
	exec_query(&proc, "IN/root", query, KNOT_RCODE_NOERROR);

	/* Query processor (-1 bytes, not enough data). */
	knot_layer_reset(&proc);
	query->size -= 1;
	exec_query(&proc, "IN/few-data", query, KNOT_RCODE_FORMERR);
	query->size += 1;

	/* Query processor (+1 bytes trailing). */
	knot_layer_reset(&proc);
	query->wire[query->size] = '\1'; /* Initialize the "garbage" value. */
	query->size += 1;
	exec_query(&proc, "IN/trail-garbage", query, KNOT_RCODE_FORMERR);
	query->size -= 1;

	/* Forge NOTIFY query from SOA query. */
	knot_layer_reset(&proc);
	knot_wire_set_opcode(query->wire, KNOT_OPCODE_NOTIFY);
	exec_query(&proc, "IN/notify", query, KNOT_RCODE_NOTAUTH);

	/* Forge AXFR query. */
	knot_layer_reset(&proc);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_AXFR);
	exec_query(&proc, "IN/axfr", query, KNOT_RCODE_NOTAUTH);

	/* Forge IXFR query (badly formed, no SOA in AUTHORITY section). */
	knot_layer_reset(&proc);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR);
	exec_query(&proc, "IN/ixfr-formerr", query, KNOT_RCODE_FORMERR);

	/* Forge IXFR query (well formed). */
	knot_layer_reset(&proc);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR);
	/* Append SOA RR. */
	knot_rrset_t soa_rr = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
	knot_pkt_begin(query, KNOT_AUTHORITY);
	knot_pkt_put(query, KNOT_COMPR_HINT_NONE, &soa_rr, 0);
	exec_query(&proc, "IN/ixfr", query, KNOT_RCODE_NOTAUTH);

	/* \note Tests below are not possible without proper zone and zone data. */
	/* #189 Process UPDATE query. */
	/* #189 Process AXFR client. */
	/* #189 Process IXFR client. */

	/* Query processor (smaller than DNS header, ignore). */
	knot_layer_reset(&proc);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
	size_t orig_query_size = query->size;
	query->size = KNOT_WIRE_HEADER_SIZE - 1;
	int state = knot_layer_consume(&proc, query);
	ok(state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");
	query->size = orig_query_size;

	/* Query processor (response, ignore). */
	knot_layer_reset(&proc);
	knot_wire_set_qr(query->wire);
	state = knot_layer_consume(&proc, query);
	ok(state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");

	/* Finish. */
	state = knot_layer_finish(&proc);
	ok(state == KNOT_STATE_NOOP, "ns: processing end" );

	/* Cleanup. */
	mp_delete((struct mempool *)mm.ctx);
	server_deinit(&server);
	conf_free(conf());

	return 0;
}