Exemple #1
0
/*!
 * \brief Compare suffixes and calculate score (number of matching labels).
 *
 * Update current best score.
 */
static bool knot_response_compr_score(uint8_t *n, uint8_t *p, uint8_t labels,
                                      uint8_t *wire, knot_compr_ptr_t *match)
{
	uint16_t score = 0;
	uint16_t off = 0;
	while (*n != '\0') {
		/* Can't exceed current best coverage. */
		if (score + labels <= match->lbcount)
			return false; /* Early cut. */
		/* Keep track of contiguous matches. */
		if (*n == *p && memcmp(n + 1, p + 1, *n) == 0) {
			if (score == 0)
				off = (p - wire);
			++score;
		} else {
			score = 0; /* Non-contiguous match. */
		}
		n = knot_wire_next_label(n, wire);
		p = knot_wire_next_label(p, wire);
		--labels;
	}

	/* New best score. */
	if (score > match->lbcount && off <= KNOT_WIRE_PTR_MAX) {
		match->lbcount = score;
		match->off = off;
		return true;
	}

	return false;
}
Exemple #2
0
/*----------------------------------------------------------------------------*/
_public_
int knot_dname_matched_labels(const knot_dname_t *d1, const knot_dname_t *d2)
{
	if (d1 == NULL || d2 == NULL)
		return KNOT_EINVAL;

	/* Count labels. */
	int l1 = knot_dname_labels(d1, NULL);
	int l2 = knot_dname_labels(d2, NULL);

	if (l1 < 0 || l2 < 0)
		return KNOT_EINVAL;

	assert(l1 >= 0 && l1 <= KNOT_DNAME_MAXLABELS);
	assert(l2 >= 0 && l2 <= KNOT_DNAME_MAXLABELS);

	/* Align end-to-end to common suffix. */
	int common = knot_dname_align(&d1, l1, &d2, l2, NULL);

	/* Count longest chain leading to root label. */
	int matched = 0;
	while (common > 0) {
		if (label_is_equal(d1, d2))
			++matched;
		else
			matched = 0; /* Broken chain. */

		/* Next label. */
		d1 = knot_wire_next_label(d1, NULL);
		d2 = knot_wire_next_label(d2, NULL);
		--common;
	}

	return matched;
}
Exemple #3
0
/*!
 * \brief Align name and reference to a common number of suffix labels.
 */
static uint8_t knot_response_compr_align(uint8_t **name, uint8_t nlabels,
                                         uint8_t **ref, uint8_t reflabels,
                                         uint8_t *wire)
{
	for (unsigned j = nlabels; j < reflabels; ++j)
		*ref = knot_wire_next_label(*ref, wire);

	for (unsigned j = reflabels; j < nlabels; ++j)
		*name = knot_wire_next_label(*name, wire);

	return (nlabels < reflabels) ? nlabels : reflabels;
}
Exemple #4
0
/*----------------------------------------------------------------------------*/
_public_
bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2)
{
	while(*d1 != '\0' || *d2 != '\0') {
		if (label_is_equal(d1, d2)) {
			d1 = knot_wire_next_label(d1, NULL);
			d2 = knot_wire_next_label(d2, NULL);
		} else {
			return false;
		}
	}

	return true;
}
Exemple #5
0
static int rosedb_query_txn(MDB_txn *txn, MDB_dbi dbi, knot_pkt_t *pkt, struct query_data *qdata)
{
	struct iter it;
	int ret = KNOT_EOK;

	/* Find suffix for QNAME. */
	const knot_dname_t *qname = knot_pkt_qname(qdata->query);
	const knot_dname_t *key = qname;
	while (key) {
		ret = cache_query_fetch(txn, dbi, &it, key);
		if (ret == 0) { /* Found */
			break;
		}

		if (*key == '\0') { /* Last label, not found. */
			return KNOT_ENOENT;
		}

		key = knot_wire_next_label(key, qdata->query->wire);
	}

	/* Synthetize record to response. */
	ret = rosedb_synth(pkt, key, &it, qdata);

	cache_iter_free(&it);
	return ret;
}
Exemple #6
0
/*----------------------------------------------------------------------------*/
_public_
int knot_dname_unpack(uint8_t* dst, const knot_dname_t *src,
                      size_t maxlen, const uint8_t *pkt)
{
	if (dst == NULL || src == NULL)
		return KNOT_EINVAL;

	/* Seek first real label occurence. */
	src = knot_wire_seek_label(src, pkt);

	/* Unpack rest of the labels. */
	int len = 0;
	while (*src != '\0') {
		uint8_t lblen = *src + 1;
		if (len + lblen > maxlen)
			return KNOT_ESPACE;
		memcpy(dst + len, src, lblen);
		len += lblen;
		src = knot_wire_next_label(src, pkt);
	}

	/* Terminal label */
	if (len + 1 > maxlen)
		return KNOT_EINVAL;

	*(dst + len) = '\0';
	return len + 1;
}
Exemple #7
0
/**
 * Name error response check (RFC4035 3.1.3.2; RFC4035 5.4, bullet 2).
 * @note Returned flags must be checked in order to prove denial.
 * @param flags Flags to be set according to check outcome.
 * @param nsec  NSEC RR.
 * @param name  Name to be checked.
 * @param pool
 * @return      0 or error code.
 */
static int name_error_response_check_rr(int *flags, const knot_rrset_t *nsec,
                                        const knot_dname_t *name)
{
	assert(flags && nsec && name);

	if (nsec_nonamematch(nsec, name) == 0) {
		*flags |= FLG_NOEXIST_RRSET;
	}

	/* Try to find parent wildcard that is proved by this NSEC. */ 
	uint8_t namebuf[KNOT_DNAME_MAXLEN];
	int ret = knot_dname_to_wire(namebuf, name, sizeof(namebuf));
	if (ret < 0)
		return ret;
	knot_dname_t *ptr = namebuf;
	while (ptr[0]) {
		/* Remove leftmost label and replace it with '\1*'. */
		ptr = (uint8_t *) knot_wire_next_label(ptr, NULL);
		*(--ptr) = '*';
		*(--ptr) = 1;
		/* True if this wildcard provably doesn't exist. */
		if (nsec_nonamematch(nsec, ptr) == 0) {
			*flags |= FLG_NOEXIST_WILDCARD;
			break;
		}
		/* Remove added leftmost asterisk. */
		ptr += 2;
	}

	return kr_ok();
}
Exemple #8
0
/*----------------------------------------------------------------------------*/
_public_
int knot_dname_align(const uint8_t **d1, uint8_t d1_labels,
                     const uint8_t **d2, uint8_t d2_labels,
                     uint8_t *wire)
{
	if (d1 == NULL || d2 == NULL)
		return KNOT_EINVAL;

	for (unsigned j = d1_labels; j < d2_labels; ++j)
		*d2 = knot_wire_next_label(*d2, wire);

	for (unsigned j = d2_labels; j < d1_labels; ++j)
		*d1 = knot_wire_next_label(*d1, wire);

	return (d1_labels < d2_labels) ? d1_labels : d2_labels;
}
Exemple #9
0
/*----------------------------------------------------------------------------*/
_public_
int knot_dname_lf(uint8_t *dst, const knot_dname_t *src, const uint8_t *pkt)
{
	if (dst == NULL || src == NULL)
		return KNOT_EINVAL;

	uint8_t *len = dst++;
	*len = '\0';
	*dst = '\0';
	const uint8_t* l = src;
	/*! \todo This could be made as offsets to pkt? */
	const uint8_t* lstack[KNOT_DNAME_MAXLABELS];
	const uint8_t **sp = lstack;
	while(*l != 0) { /* build label stack */
		*sp++ = l;
		l = knot_wire_next_label(l, pkt);
	}
	while(sp != lstack) {          /* consume stack */
		l = *--sp; /* fetch rightmost label */
		memcpy(dst, l+1, *l);  /* write label */
		for (int i = 0; i < *l; ++i) {   /* convert to lowercase */
			dst[i] = knot_tolower(dst[i]);
		}
		dst += *l;
		*dst++ = '\0';         /* label separator */
		*len += *l + 1;
	}

	/* root label special case */
	if (*len == 0)
		*len = 1; /* \x00 */

	return KNOT_EOK;
}
Exemple #10
0
/*----------------------------------------------------------------------------*/
_public_
knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned labels,
					const knot_dname_t *suffix)
{
	if (name == NULL)
		return NULL;

	/* Calculate prefix and suffix lengths. */
	int dname_lbs = knot_dname_labels(name, NULL);
	assert(dname_lbs >= labels);
	unsigned prefix_lbs = dname_lbs - labels;

	/* Trim 1 octet from prefix, as it is measured as FQDN. */
	int prefix_len = knot_dname_prefixlen(name, prefix_lbs, NULL) - 1;
	int suffix_len = knot_dname_size(suffix);
	if (prefix_len < 0 || suffix_len < 0)
		return NULL;

	/* Create target name. */
	int new_len = prefix_len + suffix_len;
	knot_dname_t *out = malloc(new_len);
	if (out == NULL)
		return NULL;

	/* Copy prefix. */
	uint8_t *dst = out;
	while (prefix_lbs > 0) {
		memcpy(dst, name, *name + 1);
		dst += *name + 1;
		name = knot_wire_next_label(name, NULL);
		--prefix_lbs;
	}

	/* Copy suffix. */
	while (*suffix != '\0') {
		memcpy(dst, suffix, *suffix + 1);
		dst += *suffix + 1;
		suffix = knot_wire_next_label(suffix, NULL);
	}
	*dst = '\0';
	return out;
}
Exemple #11
0
/*----------------------------------------------------------------------------*/
_public_
int knot_dname_labels(const uint8_t *name, const uint8_t *pkt)
{
	if (name == NULL)
		return KNOT_EINVAL;

	uint8_t count = 0;
	while (*name != '\0') {
		++count;
		name = knot_wire_next_label((uint8_t *)name, (uint8_t *)pkt);
		if (!name)
			return KNOT_EMALF;
	}
	return count;
}
Exemple #12
0
/*----------------------------------------------------------------------------*/
_public_
bool knot_dname_is_sub(const knot_dname_t *sub, const knot_dname_t *domain)
{
	if (sub == domain)
		return false;

	/* Count labels. */
	assert(sub != NULL && domain != NULL);
	int sub_l = knot_dname_labels(sub, NULL);
	int domain_l = knot_dname_labels(domain, NULL);

	if (sub_l < 0 || domain_l < 0)
		return false;

	assert(sub_l >= 0 && sub_l <= KNOT_DNAME_MAXLABELS);
	assert(domain_l >= 0 && domain_l <= KNOT_DNAME_MAXLABELS);

	/* Subdomain must have more labels as parent. */
	if (sub_l <= domain_l)
		return false;

	/* Align end-to-end to common suffix. */
	int common = knot_dname_align(&sub, sub_l, &domain, domain_l, NULL);

	/* Compare common suffix. */
	while(common > 0) {
		/* Compare label. */
		if (!label_is_equal(sub, domain))
			return false;
		/* Next label. */
		sub = knot_wire_next_label(sub, NULL);
		domain = knot_wire_next_label(domain, NULL);
		--common;
	}
	return true;
}
Exemple #13
0
/*----------------------------------------------------------------------------*/
_public_
int knot_dname_to_lower(knot_dname_t *name)
{
	if (name == NULL)
		return KNOT_EINVAL;

	/*! \todo Faster with \xdfdf mask. */
	while (*name != '\0') {
		for (uint8_t i = 0; i < *name; ++i)
			name[1 + i] = knot_tolower(name[1 + i]);
		name = (uint8_t *)knot_wire_next_label(name, NULL);
		if (name == NULL) { /* Must not be used on compressed names. */
			return KNOT_EMALF;
		}
	}

	return KNOT_EOK;
}
Exemple #14
0
/*! \brief Parse address from reverse query QNAME and return address family. */
static int reverse_addr_parse(struct query_data *qdata, synth_template_t *tpl, char *addr_str)
{
	/* QNAME required format is [address].[subnet/zone]
	 * f.e.  [1.0...0].[h.g.f.e.0.0.0.0.d.c.b.a.ip6.arpa] represents
	 *       [abcd:0:efgh::1] */
	const knot_dname_t* label = qdata->name;
	const uint8_t *query_wire = qdata->query->wire;

	/* Push labels on stack for reverse walkthrough. */
	const uint8_t* label_stack[KNOT_DNAME_MAXLABELS];
	const uint8_t** sp = label_stack;
	int label_count = knot_dname_labels(label, query_wire);
	while(label_count > ARPA_ZONE_LABELS) {
		*sp++ = label;
		label = knot_wire_next_label(label, query_wire);
		--label_count;
	}

	/* Write formatted address string. */
	char sep = str_separator(tpl->subnet.ss.ss_family);
	int sep_frequency = 1;
	if (sep == ':') {
		sep_frequency = 4; /* Separator per 4 hexdigits. */
	}

	char *dst = addr_str;
	label_count = 0;
	while(sp != label_stack) {
		label = *--sp;
		/* Write separator for each Nth label. */
		if (label_count == sep_frequency) {
			*dst = sep;
			dst += 1;
			label_count = 0;
		}
		/* Write label */
		memcpy(dst, label + 1, label[0]);
		dst += label[0];
		label_count += 1;
	}

	return KNOT_EOK;
}
Exemple #15
0
/*----------------------------------------------------------------------------*/
_public_
int knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt)
{
	if (name == NULL)
		return KNOT_EINVAL;

	/* Zero labels means 1 octet \x00 */
	if (nlabels == 0)
		return 1;

	/* Seek first real label occurence. */
	name = knot_wire_seek_label(name, pkt);

	int len = 1; /* Terminal label */
	while (*name != '\0') {
		len += *name + 1;
		name = knot_wire_next_label(name, pkt);
		if (--nlabels == 0) /* Count N first labels only. */
			break;
	}

	return len;
}
Exemple #16
0
/*!
 * \brief Add covered RRs to signing context.
 *
 * Requires all DNAMEs in canonical form and all RRs ordered canonically.
 *
 * \param ctx      Signing context.
 * \param covered  Covered RRs.
 *
 * \return Error code, KNOT_EOK if successful.
 */
static int sign_ctx_add_records(dnssec_sign_ctx_t *ctx, const knot_rrset_t *covered,
                                uint32_t orig_ttl, int trim_labels)
{
	// huge block of rrsets can be optionally created
	static uint8_t wire_buffer[KNOT_WIRE_MAX_PKTSIZE];
	int written = knot_rrset_to_wire(covered, wire_buffer, sizeof(wire_buffer), NULL);
	if (written < 0) {
		return written;
	}

	/* Set original ttl. */
	int ret = adjust_wire_ttl(wire_buffer, written, orig_ttl);
	if (ret != 0) {
		return ret;
	}

	/* RFC4035 5.3.2
	 * Remove leftmost labels and replace them with '*.'.
	 */
	uint8_t *beginp = wire_buffer;
	if (trim_labels > 0) {
		for (int i = 0; i < trim_labels; ++i) {
			assert(beginp[0]);
			beginp = (uint8_t *) knot_wire_next_label(beginp, NULL);
		}
		*(--beginp) = '*';
		*(--beginp) = 1;
	}

	dnssec_binary_t wire_binary = {
		.size = written - (beginp - wire_buffer),
		.data = beginp
	};
	return dnssec_sign_add(ctx, &wire_binary);
}

/*!
 * \brief Add all data covered by signature into signing context.
 *
 * RFC 4034: The signature covers RRSIG RDATA field (excluding the signature)
 * and all matching RR records, which are ordered canonically.
 *
 * Requires all DNAMEs in canonical form and all RRs ordered canonically.
 *
 * \param ctx          Signing context.
 * \param rrsig_rdata  RRSIG RDATA with populated fields except signature.
 * \param covered      Covered RRs.
 *
 * \return Error code, KNOT_EOK if successful.
 */
/* TODO -- Taken from knot/src/knot/dnssec/rrset-sign.c. Re-write for better fit needed. */
static int sign_ctx_add_data(dnssec_sign_ctx_t *ctx, const uint8_t *rrsig_rdata,
                             const knot_rrset_t *covered, uint32_t orig_ttl, int trim_labels)
{
	int result = sign_ctx_add_self(ctx, rrsig_rdata);
	if (result != KNOT_EOK) {
		return result;
	}

	return sign_ctx_add_records(ctx, covered, orig_ttl, trim_labels);
}
Exemple #17
0
int knot_response_compress_dname(const knot_dname_t *dname, knot_compr_t *compr,
                                 uint8_t *dst, size_t max)
{
	if (!dname || !compr || !dst) {
		return KNOT_EINVAL;
	}

	/* Do not compress small dnames. */
	uint8_t *name = dname->name;
	if (dname->size <= 2) {
                if (dname->size > max)
                        return KNOT_ESPACE;
                memcpy(dst, name, dname->size);
                return dname->size;
	}

	/* Align and compare name and pointer in the compression table. */
	unsigned i = 0;
	unsigned lbcount = 0;
	unsigned match_id = 0;
	knot_compr_ptr_t match = { 0, 0 };
	for (; i < COMPR_MAXLEN && compr->table[i].off > 0; ++i) {
		uint8_t *name = dname->name;
		uint8_t *ref = compr->wire + compr->table[i].off;
		lbcount = knot_response_compr_align(&name, dname->label_count,
		                                    &ref, compr->table[i].lbcount,
		                                    compr->wire);

		if (knot_response_compr_score(name, ref, lbcount, compr->wire,
		                              &match)) {
			match_id = i;
			if (match.lbcount == dname->label_count)
				break; /* Best match, break. */
		}
	}

	/* Write non-matching prefix. */
	unsigned written = 0;
	for (unsigned j = match.lbcount; j < dname->label_count; ++j) {
		if (written + *name + 1 > max)
			return KNOT_ESPACE;
		memcpy(dst + written, name, *name + 1);
		written += *name + 1;
		name = knot_wire_next_label(name, compr->wire);
	}

	/* Write out pointer covering suffix. */
	if (*name != '\0') {
		if (written + sizeof(uint16_t) > max)
			return KNOT_ESPACE;
		knot_wire_put_pointer(dst + written, match.off);
		written += sizeof(uint16_t);
	} else {
		/* Not covered by compression table, write terminal. */
		if (written + 1 > max)
			return KNOT_ESPACE;
		*(dst + written) = '\0';
		written += 1;
	}

	/* Promote good matches. */
	if (match_id > 1) {
		match = compr->table[match_id];
		compr->table[match_id] = compr->table[match_id - 1];
		compr->table[match_id - 1] = match;
	}

	/* Do not insert if exceeds bounds or full match. */
	if (match.lbcount == dname->label_count ||
	    compr->wire_pos > KNOT_WIRE_PTR_MAX)
		return written;

	/* If table is full, elect name from the lower 1/4 of the table
	 * and replace it. */
	if (i == COMPR_MAXLEN - 1) {
		i = COMPR_FIXEDLEN + rand() % COMPR_VOLATILE;
		compr->table[i].off = 0;
	}

	/* Store in dname table. */
	if (compr->table[i].off == 0) {
		compr->table[i].off = (uint16_t)compr->wire_pos;
		compr->table[i].lbcount = dname->label_count;
	}

	return written;
}
Exemple #18
0
static int zone_contents_add_node(zone_contents_t *zone, zone_node_t *node,
                                  bool create_parents)
{
	if (zone == NULL || node == NULL) {
		return KNOT_EINVAL;
	}

	int ret = 0;
	if ((ret = zone_contents_check_node(zone, node)) != 0) {
		dbg_zone("Node check failed.\n");
		return ret;
	}

	ret = zone_tree_insert(zone->nodes, node);
	if (ret != KNOT_EOK) {
		dbg_zone("Failed to insert node into zone tree.\n");
		return ret;
	}

	if (!create_parents) {
		return KNOT_EOK;
	}

	dbg_zone_detail("Creating parents of the node.\n");

	/* No parents for root domain. */
	if (*node->owner == '\0')
		return KNOT_EOK;

	zone_node_t *next_node = NULL;
	const uint8_t *parent = knot_wire_next_label(node->owner, NULL);

	if (knot_dname_cmp(zone->apex->owner, parent) == 0) {
		dbg_zone_detail("Zone apex is the parent.\n");
		node_set_parent(node, zone->apex);

		// check if the node is not wildcard child of the parent
		if (knot_dname_is_wildcard(node->owner)) {
			zone->apex->flags |= NODE_FLAGS_WILDCARD_CHILD;
		}
	} else {
		while (parent != NULL &&
		       !(next_node = zone_contents_get_node(zone, parent))) {

			/* Create a new node. */
			dbg_zone_detail("Creating new node.\n");
			next_node = node_new(parent, NULL);
			if (next_node == NULL) {
				return KNOT_ENOMEM;
			}

			/* Insert node to a tree. */
			dbg_zone_detail("Inserting new node to zone tree.\n");
			ret = zone_tree_insert(zone->nodes, next_node);
			if (ret != KNOT_EOK) {
				node_free(&next_node, NULL);
				return ret;
			}

			/* Update node pointers. */
			node_set_parent(node, next_node);
			if (knot_dname_is_wildcard(node->owner)) {
				next_node->flags |= NODE_FLAGS_WILDCARD_CHILD;
			}

			dbg_zone_detail("Next parent.\n");
			node = next_node;
			parent = knot_wire_next_label(parent, NULL);
		}

		// set the found parent (in the zone) as the parent of the last
		// inserted node
		assert(node->parent == NULL);
		node_set_parent(node, next_node);

		dbg_zone_detail("Created all parents.\n");
	}

	return KNOT_EOK;
}