Skip to content
Snippets Groups Projects
Forked from Knot projects / Knot DNS
12002 commits behind the upstream repository.
rrset.c 54.99 KiB
/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <config.h>
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include "libknot/consts.h"
#include "libknot/common.h"
#include "common/mempattern.h"
#include "libknot/rrset.h"
#include "libknot/rrset-dump.h"
#include "common/descriptor.h"
#include "libknot/util/debug.h"
#include "libknot/util/utils.h"
#include "libknot/packet/response.h"
#include "libknot/util/wire.h"
#include "libknot/dname.h"
#include "libknot/rdata.h"

static const unsigned int RR_CHANGE_TOO_BIG = 16;

static uint32_t rrset_rdata_offset(const knot_rrset_t *rrset, size_t pos)
{
	if (rrset == NULL || rrset->rdata_indices == NULL ||
	    pos >= rrset->rdata_count || pos == 0) {
		return 0;
	}

	assert(rrset->rdata_count >= 2);
	return rrset->rdata_indices[pos - 1];
}

uint8_t *rrset_rdata_pointer(const knot_rrset_t *rrset, size_t pos)
{
	if (rrset == NULL || rrset->rdata == NULL
	    || pos >= rrset->rdata_count) {
		return NULL;
	}

	return rrset->rdata + rrset_rdata_offset(rrset, pos);
}

static uint16_t rrset_rdata_naptr_bin_chunk_size(const knot_rrset_t *rrset,
                                               size_t pos)
{
	if (rrset == NULL || pos >= rrset->rdata_count) {
		return 0;
	}

	size_t size = 0;
	uint8_t *rdata = rrset_rdata_pointer(rrset, pos);
	assert(rdata);

	/* Two shorts at the beginning. */
	size += 4;
	/* 3 binary TXTs with length in the first byte. */
	for (int i = 0; i < 3; i++) {
		size += *(rdata + size) + 1;
	}

	/*
	 * Dname remaning, but we usually want to get to the DNAME, so
	 * there's no need to include it in the returned size.
	 */

	return size;
}

void knot_rrset_rdata_dump(const knot_rrset_t *rrset, size_t rdata_pos)
{
dbg_rrset_exec_detail(
	dbg_rrset_detail("      ------- RDATA pos=%zu -------\n", rdata_pos);
	if (rrset->rdata_count == 0) {
		dbg_rrset_detail("      There are no rdata in this RRset!\n");
		dbg_rrset_detail("      ------- RDATA -------\n");
		return;
	}
	const rdata_descriptor_t *desc =
		get_rdata_descriptor(knot_rrset_type(rrset));

	size_t offset = 0;
	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
		int item = desc->block_types[i];
		const uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos);
		if (descriptor_item_is_dname(item)) {
			const knot_dname_t *dname = rdata + offset;
			char *name = knot_dname_to_str(dname);
			dbg_rrset_detail("block=%d: (%p) DNAME=%s\n",
			        i, dname, name);
			free(name);
			offset += knot_dname_size(dname);
		} else if (descriptor_item_is_fixed(item)) {
			dbg_rrset_detail("block=%d Raw data (size=%d):\n",
			                 i, item);
			dbg_rrset_hex_detail((char *)(rdata + offset), item);
			                      offset += item;
		} else if (descriptor_item_is_remainder(item)) {
			dbg_rrset_detail("block=%d Remainder (size=%zu):\n",
			        i, rrset_rdata_item_size(rrset,
			                                 rdata_pos) - offset);
			dbg_rrset_hex_detail((char *)(rdata + offset),
			          rrset_rdata_item_size(rrset,
			                                rdata_pos) - offset);
		} else {
			assert(rrset->type == KNOT_RRTYPE_NAPTR);
			uint16_t naptr_chunk_size =
				rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos);
			dbg_rrset_detail("NAPTR, REGEXP block (size=%u):\n",
			        naptr_chunk_size);
			dbg_rrset_hex_detail((char *)(rdata + offset), naptr_chunk_size);
			offset += naptr_chunk_size;
		}
	}
);
}

static size_t rrset_rdata_remainder_size(const knot_rrset_t *rrset,
                                         size_t offset, size_t pos)
{
	assert(rrset_rdata_item_size(rrset, pos) != 0);

	size_t ret = rrset_rdata_item_size(rrset, pos) - offset;
	assert(ret <= rrset_rdata_size_total(rrset));
	return ret;
}

int rrset_rdata_compare_one(const knot_rrset_t *rrset1,
                            const knot_rrset_t *rrset2,
                            size_t pos1, size_t pos2)
{
	assert(rrset1 != NULL);
	assert(rrset2 != NULL);

	uint8_t *r1 = rrset_rdata_pointer(rrset1, pos1);
	uint8_t *r2 = rrset_rdata_pointer(rrset2, pos2);
	assert(rrset1->type == rrset2->type);
	const rdata_descriptor_t *desc = get_rdata_descriptor(rrset1->type);
	int cmp = 0;
	size_t offset = 0;

	// TODO: this can be much simpler: Get data for memcmp and sizes in ifs
	// compare only once
	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
		if (descriptor_item_is_dname(desc->block_types[i])) {
			const knot_dname_t *dname1 = r1 + offset;
			int size1 = knot_dname_size(dname1);
			const knot_dname_t *dname2 = r2 + offset;
			int size2 = knot_dname_size(dname2);
			cmp = memcmp(dname1, dname2,
			             size1 <= size2 ? size1 : size2);
			if (cmp == 0 && size1 != size2) {
				cmp = size1 < size2 ? -1 : 1;
			}
			offset += knot_dname_size(dname1);
		} else if (descriptor_item_is_fixed(desc->block_types[i])) {
			cmp = memcmp(r1 + offset, r2 + offset,
			             desc->block_types[i]);
			offset += desc->block_types[i];
		} else if (descriptor_item_is_remainder(desc->block_types[i])) {
			size_t size1 = rrset_rdata_remainder_size(rrset1, offset,
			                                          pos1);
			size_t size2 = rrset_rdata_remainder_size(rrset2, offset,
			                                          pos2);
			cmp = memcmp(r1 + offset, r2 + offset,
			             size1 <= size2 ? size1 : size2);
			if (cmp == 0 && size1 != size2) {
				cmp = size1 < size2 ? -1 : 1;
			}
			/* No need to move offset, this should be end anyway. */
			assert(desc->block_types[i + 1] == KNOT_RDATA_WF_END);
		} else {
			assert(rrset1->type == KNOT_RRTYPE_NAPTR);
			uint16_t naptr_chunk_size1 =
				rrset_rdata_naptr_bin_chunk_size(rrset1, pos1);
			uint16_t naptr_chunk_size2 =
				rrset_rdata_naptr_bin_chunk_size(rrset2, pos2);
			cmp = memcmp(r1, r2,
			             naptr_chunk_size1 <= naptr_chunk_size2 ?
			             naptr_chunk_size1 : naptr_chunk_size2);
			if (cmp == 0 && naptr_chunk_size1 == naptr_chunk_size2) {
				cmp = naptr_chunk_size1 < naptr_chunk_size2 ? -1 : 1;
			}

			/*
			 * It does not matter which one we assign. If the
			 * offsets were different, then cmp != 0, if yes,
			 * NAPTR DNAME will be on correct offset.
			 */
			offset += naptr_chunk_size1;
		}

		if (cmp != 0) {
			return cmp;
		}
	}

	assert(cmp == 0);
	return 0;
}

static int knot_rrset_header_to_wire(const knot_rrset_t *rrset,
                                     uint8_t **pos, size_t max_size,
                                     knot_compr_t *compr, size_t *size)
{
	// Common size of items: type, class and ttl.
	const size_t type_cls_ttl_len = 2 * sizeof(uint16_t) + sizeof(uint32_t);
	// Rdata length item size.
	const size_t rrlen_len = sizeof(uint16_t);

	if (rrset->owner == NULL) {
		return KNOT_EMALF;
	}

	const uint8_t *owner;
	uint8_t owner_len;

	// Use compressed owner.
	if (compr) {
		owner = compr->owner.wire + compr->owner.pos;
		owner_len = compr->owner.size;
	// Use rrset owner.
	} else {
		owner = rrset->owner;
		owner_len = knot_dname_size(owner);
	}

	dbg_response("Max size: %zu, compressed owner: %s, owner size: %u\n",
	             max_size, compr ? "yes" : "no", owner_len);

	// Check wire space for header.
	if (*size + owner_len + type_cls_ttl_len + rrlen_len > max_size) {
		dbg_rrset_detail("Header does not fit into wire.\n");
		return KNOT_ESPACE;
	}

	// Write owner, type, class and ttl to wire.
	*pos += knot_dname_to_wire(*pos, owner, KNOT_DNAME_MAXLEN);

	dbg_rrset_detail("  Type: %u\n", rrset->type);
	knot_wire_write_u16(*pos, rrset->type);
	*pos += sizeof(uint16_t);

	dbg_rrset_detail("  Class: %u\n", rrset->rclass);
	knot_wire_write_u16(*pos, rrset->rclass);
	*pos += sizeof(uint16_t);

	dbg_rrset_detail("  TTL: %u\n", rrset->ttl);
	knot_wire_write_u32(*pos, rrset->ttl);
	*pos += sizeof(uint32_t);

	*size += owner_len + type_cls_ttl_len;

	return KNOT_EOK;
}

/* [code-review] Split to more functions, this one's too long. */
static int knot_rrset_rdata_to_wire_one(const knot_rrset_t *rrset,
                                        uint16_t rdata_pos, uint8_t **pos,
                                        size_t max_size, size_t *rr_size,
                                        knot_compr_t *compr)
{
	assert(rrset);
	assert(pos);

	/* Put RR header to wire. */
	size_t size = 0;
	int ret = knot_rrset_header_to_wire(rrset, pos, max_size,
	                                    compr, &size);
	if (ret != KNOT_EOK) {
		dbg_response("Failed to convert RR header to wire (%s).\n",
		             knot_strerror(ret));
		return ret;
	}

	// save space for RDLENGTH
	uint8_t *rdlength_pos = *pos;
	*pos += 2;
	size += 2;

	if (compr) {
		compr->wire_pos += size;
	}

	/* Get pointer into RDATA array. */
	uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos);
	assert(rdata);
	/* Offset into one RDATA array. */
	size_t offset = 0;
	/* Actual RDLENGTH. */
	uint16_t rdlength = 0;

	const rdata_descriptor_t *desc = get_rdata_descriptor(rrset->type);

	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
		int item = desc->block_types[i];
		if (compr && descriptor_item_is_compr_dname(item)) {
			const knot_dname_t *dname = rdata + offset;
			int ret = knot_response_compress_dname(dname,
			            compr, *pos,
			            max_size - size - rdlength);
			if (ret < 0) {
				return KNOT_ESPACE;
			}
			assert(ret + size + rdlength <= max_size);
dbg_response_exec_detail(
			char *name = knot_dname_to_str(dname);
			dbg_response_detail("Compressed dname=%s size: %d\n",
			                    name, ret);
			free(name);
);
			*pos += ret;
			rdlength += ret;
			offset += knot_dname_size(dname);
			compr->wire_pos += ret;
		} else if (descriptor_item_is_dname(item)) {
			const knot_dname_t *dname = rdata + offset;
dbg_rrset_exec_detail(
			char *name = knot_dname_to_str(dname);
			dbg_rrset_detail("Saving this DNAME=%s\n", name);
			free(name);
);
			// save whole domain name
			size_t maxb = max_size - size - rdlength;
			int dname_size = knot_dname_to_wire(*pos, dname, maxb);
			if (dname_size < 0)
				return KNOT_ESPACE;
			dbg_rrset_detail("Uncompressed dname size: %d\n",
			                 dname_size);
			*pos += dname_size;
			rdlength += dname_size;
			offset += dname_size;
			if (compr) {
				compr->wire_pos += dname_size;
			}
		} else if (descriptor_item_is_fixed(item)) {
			dbg_rrset_detail("Saving static chunk, size=%d\n",
			                 item);
			/* Fixed length chunk. */
			if (size + rdlength + item > max_size) {
				return KNOT_ESPACE;
			}
			memcpy(*pos, rdata + offset, item);
			*pos += item;
			rdlength += item;
			offset += item;
			if (compr) {
				compr->wire_pos += item;
			}
		} else if (descriptor_item_is_remainder(item)) {
			/* Check that the remainder fits to stream. */
			size_t remainder_size =
				rrset_rdata_remainder_size(rrset, offset,
			                                   rdata_pos);
			dbg_rrset_detail("Saving remaining chunk, size=%zu, "
			                 "size with remainder=%zu\n",
			                 remainder_size,
			                 size + rdlength + remainder_size);
			if (size + rdlength + remainder_size > max_size) {
				dbg_rrset("rr: to_wire: Remainder does not fit "
				          "to wire.\n");
				return KNOT_ESPACE;
			}
			memcpy(*pos, rdata + offset, remainder_size);
			*pos += remainder_size;
			rdlength += remainder_size;
			offset += remainder_size;
			if (compr) {
				compr->wire_pos += remainder_size;
			}
		} else {
			assert(rrset->type == KNOT_RRTYPE_NAPTR);
			/* Store the binary chunk. */
			uint16_t chunk_size =
			    rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos);
			if (size + rdlength + chunk_size > max_size) {
				dbg_rrset("rr: to_wire: NAPTR chunk does not "
				          "fit to wire.\n");
				return KNOT_ESPACE;
			}
			memcpy(*pos, rdata + offset, chunk_size);
			*pos += chunk_size;
			rdlength += chunk_size;
			offset += chunk_size;
			if (compr) {
				compr->wire_pos += chunk_size;
			}
		}
	}

	knot_wire_write_u16(rdlength_pos, rdlength);
	size += rdlength;

	*rr_size = size;
	assert(size <= max_size);
	return KNOT_EOK;
}

static int knot_rrset_to_wire_aux(const knot_rrset_t *rrset, uint8_t **pos,
                                  size_t max_size, compression_param_t *comp)
{
	uint8_t wf_owner[256];
	size_t size = 0;

	assert(rrset != NULL);
	assert(rrset->owner != NULL);
	assert(pos != NULL);
	assert(*pos != NULL);

	dbg_rrset_detail("Max size: %zu, owner: %p, owner size: %d\n",
	                 max_size, rrset, knot_dname_size(rrset->owner));

	knot_compr_t compr_info;
	if (comp) {
		dbg_response_detail("Compressing RR owner: %s.\n",
		                    rrset->owner);
		compr_info.table = comp->compressed_dnames;
		compr_info.wire = comp->wire;
		compr_info.wire_pos = comp->wire_pos;
		int ret = knot_response_compress_dname(rrset->owner, &compr_info,
		                                       wf_owner, max_size);
		if (ret < 0) {
			return KNOT_ESPACE;
		}

		compr_info.owner.pos = 0;
		compr_info.owner.wire = wf_owner;
		compr_info.owner.size = ret;

		dbg_response_detail("Compressed owner has size=%d\n",
		                    compr_info.owner.size);
	}

	dbg_rrset_detail("Max size: %zu, size: %zu\n", max_size, size);

	// No RDATA, just save header and 0 RDLENGTH.
	if (rrset->rdata_count == 0) {
		size_t header_size = 0;
		int ret = knot_rrset_header_to_wire(rrset, pos, max_size,
		                                    comp ? &compr_info : NULL,
		                                    &header_size);
		if (ret != KNOT_EOK) {
			return ret;
		}

		// Save zero rdata length.
		knot_wire_write_u16(*pos, 0);
		*pos += sizeof(uint16_t);
		header_size += sizeof(uint16_t);

		return header_size;
	}

	// Save rrset records.
	for (uint16_t i = 0; i < rrset->rdata_count; ++i) {
		dbg_rrset_detail("rrset: to_wire: Current max_size=%zu\n",
			         max_size);
		size_t rr_size = 0;
		int ret = knot_rrset_rdata_to_wire_one(rrset, i, pos, max_size,
		                                       &rr_size,
		                                       comp ? &compr_info : NULL);
		if (ret != KNOT_EOK) {
			dbg_rrset("rrset: to_wire: Cannot convert RR. "
			          "Reason: %s.\n", knot_strerror(ret));
			return ret;
		}
		dbg_rrset_detail("Converted RR nr=%d, size=%zu\n", i, rr_size);
		/* Change size of whole RRSet. */
		size += rr_size;
		/* Change max size. */
		max_size -= rr_size;
	}
	dbg_rrset_detail("Max size: %zu, size: %zu\n", max_size, size);

	return size;
}

static int knot_rrset_rdata_store_binary(uint8_t *rdata, size_t *offset,
                                         size_t packet_offset,
                                         const uint8_t *wire,
                                         size_t *pos,
                                         size_t rdlength,
                                         size_t size)
{
	assert(rdata);
	assert(wire);

	/* Check that size is OK. */
	if ((*pos - packet_offset) + size > rdlength) {
		dbg_rrset("rrset: rdata_store_binary: Read of size=%zu on "
		          "position %zu exceeded RDLENGTH by %zu octets.\n", size,
		          *pos, ((*pos - packet_offset) + size) - rdlength);
		return KNOT_ESPACE;
	}

	/* Store actual data. */
	memcpy(rdata + *offset, wire + *pos, size);
	*offset += size;
	*pos += size;

	return KNOT_EOK;
}

static size_t rrset_binary_size_one(const knot_rrset_t *rrset,
                                      size_t rdata_pos)
{
	const rdata_descriptor_t *desc =
		get_rdata_descriptor(knot_rrset_type(rrset));

	size_t offset = 0;
	size_t size = 0;
	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
		int item = desc->block_types[i];
		uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos);
		if (descriptor_item_is_dname(item)) {
			const knot_dname_t *dname = rdata + offset;
			int dname_size = knot_dname_size(dname);
			offset += dname_size;
			size += dname_size;
		} else if (descriptor_item_is_fixed(item)) {
			offset += item;
			size += item;
		} else if (descriptor_item_is_remainder(item)) {
			size += rrset_rdata_item_size(rrset, rdata_pos) -
			        offset;
		} else {
			assert(rrset->type == KNOT_RRTYPE_NAPTR);
			uint16_t naptr_chunk_size =
				rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos);
			/*
			 * Regular expressions in NAPTR are TXT's, so they
			 * can be upto 64k long.
			 */
			size += naptr_chunk_size + 2;
			offset += naptr_chunk_size;
		}
	}

	return size;
}

static void rrset_serialize_rr(const knot_rrset_t *rrset, size_t rdata_pos,
                               uint8_t *stream, size_t *size)
{
	const rdata_descriptor_t *desc =
		get_rdata_descriptor(knot_rrset_type(rrset));

	size_t offset = 0;
	*size = 0;
	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
		int item = desc->block_types[i];
		uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos);
		assert(rdata);
		if (descriptor_item_is_dname(item)) {
			const knot_dname_t *dname = rdata + offset;
			int dname_len = knot_dname_to_wire(stream + *size, dname,
			                                   KNOT_DNAME_MAXLEN);
			*size += dname_len;
			offset += dname_len;
		} else if (descriptor_item_is_fixed(item)) {
			memcpy(stream + *size, rdata + offset, item);
			offset += item;
			*size += item;
		} else if (descriptor_item_is_remainder(item)) {
			uint16_t remainder_size =
				rrset_rdata_item_size(rrset,
			                              rdata_pos) - offset;
			memcpy(stream + *size, rdata + offset, remainder_size);
			*size += remainder_size;
		} else {
			assert(rrset->type == KNOT_RRTYPE_NAPTR);
			/* Copy static chunk. */
			uint16_t naptr_chunk_size =
				rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos);
			/* We need a length. */
			memcpy(stream + *size, &naptr_chunk_size,
			       sizeof(uint16_t));
			*size += sizeof(uint16_t);
			/* Write data. */
			memcpy(stream + *size, rdata + offset, naptr_chunk_size);
		}
	}

	dbg_rrset_detail("RR nr=%zu serialized, size=%zu\n", rdata_pos, *size);
}

static int rrset_deserialize_rr(knot_rrset_t *rrset, size_t rdata_pos,
                                uint8_t *stream, uint32_t rdata_size,
                                size_t *read)
{
	const rdata_descriptor_t *desc =
		get_rdata_descriptor(knot_rrset_type(rrset));

	size_t stream_offset = 0;
	size_t rdata_offset = 0;
	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
		int item = desc->block_types[i];
		uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos);
		if (descriptor_item_is_dname(item)) {
			const knot_dname_t *dname = stream + stream_offset;
			int dname_size = knot_dname_size(dname);

			memcpy(rdata + rdata_offset, dname, dname_size);
			stream_offset += dname_size;
			rdata_offset += dname_size;
		} else if (descriptor_item_is_fixed(item)) {
			memcpy(rdata + rdata_offset, stream + stream_offset, item);
			rdata_offset += item;
			stream_offset += item;
		} else if (descriptor_item_is_remainder(item)) {
			size_t remainder_size = rdata_size - stream_offset;
			memcpy(rdata + rdata_offset, stream + stream_offset,
			       remainder_size);
			stream_offset += remainder_size;
		} else {
			assert(rrset->type == KNOT_RRTYPE_NAPTR);
			/* Read size. */
			uint16_t naptr_chunk_size;
			memcpy(&naptr_chunk_size, stream + stream_offset,
			       sizeof(uint16_t));
			stream_offset += sizeof(uint16_t);
			memcpy(rdata + rdata_offset, stream + stream_offset,
			       naptr_chunk_size);
			stream_offset += naptr_chunk_size;
			rdata_offset += rdata_offset;
		}
	}
	*read = stream_offset;
	return KNOT_EOK;
}

int knot_rrset_remove_rdata_pos(knot_rrset_t *rrset, size_t pos)
{
	if (rrset == NULL || pos >= rrset->rdata_count) {
		return KNOT_EINVAL;
	}

	/* Reorganize the actual RDATA array. */
	uint8_t *rdata_to_remove = rrset_rdata_pointer(rrset, pos);
	dbg_rrset_detail("rr: remove_rdata_pos: Removing data=%p on "
	                 "position=%zu\n", rdata_to_remove, pos);
	assert(rdata_to_remove);
	if (pos != rrset->rdata_count - 1) {
		/* Not the last item in array - we need to move the data. */
		uint8_t *next_rdata = rrset_rdata_pointer(rrset, pos + 1);
		assert(next_rdata);
		size_t remainder_size = rrset_rdata_size_total(rrset)
		                        - rrset_rdata_offset(rrset, pos + 1);
		/*
		 * Copy the all following RR data to where this item is.
		 * No need to worry about exceeding allocated space now.
		 */
		memmove(rdata_to_remove, next_rdata, remainder_size);
	}

	uint32_t removed_size = rrset_rdata_item_size(rrset, pos);
	uint32_t new_size = rrset_rdata_size_total(rrset) - removed_size;

	if (new_size == 0) {
		assert(rrset->rdata_count == 1);
		free(rrset->rdata);
		rrset->rdata = NULL;
		free(rrset->rdata_indices);
		rrset->rdata_indices = NULL;
	} else {
		/* Resize RDATA array, the change might be worth reallocation.*/
		rrset->rdata = xrealloc(rrset->rdata, new_size);
		/*
		 * Handle RDATA indices. All indices larger than the removed one
		 * have to be adjusted. Last index will be changed later.
		 */
		for (uint16_t i = pos; i < rrset->rdata_count - 1; ++i) {
			rrset->rdata_indices[i] = rrset->rdata_indices[i + 1] - removed_size;
		}

		/* Save size of the whole RDATA array. Note: probably not needed! */
		rrset->rdata_indices[rrset->rdata_count - 2] = new_size;

		/* Resize indices, not always needed, but we'll do it to be proper. */
		rrset->rdata_indices =
			xrealloc(rrset->rdata_indices,
		                 (rrset->rdata_count - 1) * sizeof(uint32_t));
	}

	--rrset->rdata_count;

	dbg_rrset_detail("rrset: remove rdata pos: RR after removal:\n");
	knot_rrset_dump(rrset);

	return KNOT_EOK;
}

uint32_t rrset_rdata_size_total(const knot_rrset_t *rrset)
{
	if (rrset == NULL || rrset->rdata_indices == NULL ||
	    rrset->rdata_count == 0) {
		return 0;
	}

	// Last index denotes end of all RRs.
	return (rrset->rdata_indices[rrset->rdata_count - 1]);
}

knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
                             uint16_t rclass, uint32_t ttl)
{
	knot_rrset_t *ret = malloc(sizeof(knot_rrset_t));
	if (ret == NULL) {
		ERR_ALLOC_FAILED;
		return NULL;
	}

	ret->rdata = NULL;
	ret->rdata_count = 0;
	ret->rdata_indices = NULL;

	ret->owner = owner;
	ret->type = type;
	ret->rclass = rclass;
	ret->ttl = ttl;
	ret->rrsigs = NULL;

	return ret;
}

knot_rrset_t *knot_rrset_new_from(const knot_rrset_t *tpl)
{
	if (!tpl) {
		return NULL;
	}

	knot_dname_t *owner = knot_dname_copy(tpl->owner);
	if (!owner) {
		return NULL;
	}

	return knot_rrset_new(owner, tpl->type, tpl->rclass, tpl->ttl);
}

int knot_rrset_add_rdata(knot_rrset_t *rrset,
                         const uint8_t *rdata, uint16_t size)
{
	if (rrset == NULL || rdata == NULL || size == 0) {
		return KNOT_EINVAL;
	}

	uint8_t *p = knot_rrset_create_rdata(rrset, size);
	memcpy(p, rdata, size);

	return KNOT_EOK;
}
static uint8_t* knot_rrset_create_rdata_at_pos(knot_rrset_t *rrset,
                                               size_t pos, uint16_t size)
{
	if (rrset == NULL || size == 0 || pos > rrset->rdata_count) {
		return NULL;
	}
	if (pos == rrset->rdata_count) {
		return knot_rrset_create_rdata(rrset, size);
	}

	uint32_t total_size = rrset_rdata_size_total(rrset);

	// Realloc actual data.
	void *tmp = realloc(rrset->rdata, total_size + size);
	if (tmp) {
		rrset->rdata = tmp;
	} else {
		ERR_ALLOC_FAILED;
		return NULL;
	}

	/*
	 * Move already existing data to from position we want to add to.
	 * But only if we don't want to add new item after last item.
	 */
	uint8_t *old_pointer = rrset_rdata_pointer(rrset, pos);
	assert(old_pointer);
	memmove(old_pointer + size, old_pointer,
	        rrset_rdata_size_total(rrset) - rrset_rdata_offset(rrset, pos));
	
	// Realloc indices. We will allocate exact size to save space.
	tmp = realloc(rrset->rdata_indices,
	              (rrset->rdata_count + 1) * sizeof(uint32_t));
	if (tmp) {
		rrset->rdata_indices = tmp;
	} else {
		ERR_ALLOC_FAILED;
		return NULL;
	}
	// Move indices.
	memmove(rrset->rdata_indices + pos + 1, rrset->rdata_indices + pos,
	        (rrset->rdata_count - pos) * sizeof(uint32_t));
	// Init new index
	rrset->rdata_indices[pos] = pos ? rrset->rdata_indices[pos - 1] : 0;
	++rrset->rdata_count;
	// Adjust all following items
	for (uint16_t i = pos; i < rrset->rdata_count; ++i) {
		rrset->rdata_indices[i] += size;
	}

	// Return pointer from correct position (now contains bogus data).
	return old_pointer;
}

int knot_rrset_add_rdata_at_pos(knot_rrset_t *rrset, size_t pos,
                                const uint8_t *rdata, uint16_t size)
{
	if (rrset == NULL || rdata == NULL || size == 0) {
		return KNOT_EINVAL;
	}

	uint8_t *p = knot_rrset_create_rdata_at_pos(rrset, pos, size);
	if (p == NULL) {
		return KNOT_ERROR;
	}
	memcpy(p, rdata, size);

	return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/

uint8_t* knot_rrset_create_rdata(knot_rrset_t *rrset, uint16_t size)
{
	if (rrset == NULL || size == 0) {
		return NULL;
	}

	uint32_t total_size = rrset_rdata_size_total(rrset);

	/* Realloc indices. We will allocate exact size to save space. */
	rrset->rdata_indices = xrealloc(rrset->rdata_indices,
	                                (rrset->rdata_count + 1) * sizeof(uint32_t));

	/* Realloc actual data. */
	rrset->rdata = xrealloc(rrset->rdata, total_size + size);

	/* Pointer to new memory. */
	uint8_t *dst = rrset->rdata + total_size;

	/* Update indices. */
	if (rrset->rdata_count == 0) {
		rrset->rdata_indices[0] = size;
	} else {
		rrset->rdata_indices[rrset->rdata_count - 1] = total_size;
		rrset->rdata_indices[rrset->rdata_count] = total_size + size;
	}

	++rrset->rdata_count;

	return dst;
}

/*----------------------------------------------------------------------------*/


uint16_t rrset_rdata_item_size(const knot_rrset_t *rrset,
                               size_t pos)
{
	if (rrset == NULL || rrset->rdata_indices == NULL ||
	    rrset->rdata_count == 0) {
		//invalid case
		return 0;
	}

	if (rrset->rdata_count == 1 || pos == 0) {
		//first RR or only one RR (either size of first RR or total size)
		return rrset->rdata_indices[0];
	}

	assert(rrset->rdata_count >= 2 && pos != 0);
	return rrset->rdata_indices[pos] - rrset->rdata_indices[pos - 1];
}

int knot_rrset_set_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs)
{
	if (rrset == NULL) {
		return KNOT_EINVAL;
	}

	rrset->rrsigs = rrsigs;
	return KNOT_EOK;
}

int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
                          knot_rrset_dupl_handling_t dupl)
{
	if (rrset == NULL || rrsigs == NULL ||
	    !knot_dname_is_equal(rrset->owner, rrsigs->owner)) {
		return KNOT_EINVAL;
	}

	int rc;
	if (rrset->rrsigs != NULL) {
		if (dupl == KNOT_RRSET_DUPL_MERGE) {
			int merged, deleted_rrs;
			rc = knot_rrset_merge_sort(rrset->rrsigs, rrsigs,
			                           &merged, &deleted_rrs);
			if (rc != KNOT_EOK) {
				return rc;
			} else if (merged || deleted_rrs) {
				return 1;
			} else {
				return 0;
			}
		} else if (dupl == KNOT_RRSET_DUPL_SKIP) {
			return 2;
		} else if (dupl == KNOT_RRSET_DUPL_REPLACE) {
			rrset->rrsigs = rrsigs;
		}
	} else {
		if (rrset->ttl != rrsigs->ttl) {
			rrsigs->ttl = rrset->ttl;
		}
		rrset->rrsigs = rrsigs;
	}

	return KNOT_EOK;
}

const knot_dname_t *knot_rrset_owner(const knot_rrset_t *rrset)
{
	return rrset->owner;
}

knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset)
{
	return rrset->owner;
}

int knot_rrset_set_owner(knot_rrset_t *rrset, const knot_dname_t *owner)
{
	if (rrset == NULL) {
		return KNOT_EINVAL;
	}

	/* Copy the new owner. */
	knot_dname_t *owner_copy = NULL;
	if (owner) {
		owner_copy = knot_dname_copy(owner);
		if (owner_copy == NULL) {
			return KNOT_ENOMEM;
		}
	}

	/* Free old owner and assign. */
	knot_dname_free(&rrset->owner);
	rrset->owner = owner_copy;
	return KNOT_EOK;
}

void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl)
{
	if (rrset) {
		rrset->ttl = ttl;
	}
}

uint16_t knot_rrset_type(const knot_rrset_t *rrset)
{
	return rrset->type;
}

uint16_t knot_rrset_class(const knot_rrset_t *rrset)
{
	return rrset->rclass;
}

uint32_t knot_rrset_ttl(const knot_rrset_t *rrset)
{
	return rrset->ttl;
}

uint8_t *knot_rrset_get_rdata(const knot_rrset_t *rrset, size_t rdata_pos)
{
	return rrset_rdata_pointer(rrset, rdata_pos);
}

uint16_t knot_rrset_rdata_rr_count(const knot_rrset_t *rrset)
{
	if (rrset != NULL) {
		return rrset->rdata_count;
	} else {
		return 0;
	}
}

const knot_rrset_t *knot_rrset_rrsigs(const knot_rrset_t *rrset)
{
	if (rrset == NULL) {
		return NULL;
	} else {
		return rrset->rrsigs;
	}
}

knot_rrset_t *knot_rrset_get_rrsigs(knot_rrset_t *rrset)
{
	if (rrset == NULL) {
		return NULL;
	} else {
		return rrset->rrsigs;
	}
}

/*!
 * \brief Compare two RR sets, order of RDATA is not significant.
 */
int knot_rrset_rdata_equal(const knot_rrset_t *r1, const knot_rrset_t *r2)
{
	if (r1 == NULL || r2 == NULL || (r1->type != r2->type) ||
	    r1->rdata_count == 0 || r2->rdata_count == 0) {
		return KNOT_EINVAL;
	}

	if (r1->rdata_count != r2->rdata_count) {
		return 0;
	}

	for (uint16_t i = 0; i < r1->rdata_count; i++) {
		bool found = false;
		for (uint16_t j = 0; j < r2->rdata_count; j++) {
			if (rrset_rdata_compare_one(r1, r2, i, j) == 0) {
				found = true;
				break;
			}
		}

		if (!found) {
			return 0;
		}
	}

	return 1;
}

int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
                       size_t max_size, uint16_t *rr_count, void *data)
{
	if (rrset == NULL || wire == NULL || size == NULL || rr_count == NULL) {
		return KNOT_EINVAL;
	}

	compression_param_t *comp_data = (compression_param_t *)data;
	uint8_t *pos = wire;

dbg_rrset_exec_detail(
	dbg_rrset_detail("Converting following RRSet:\n");
	knot_rrset_dump(rrset);
);

	int ret = knot_rrset_to_wire_aux(rrset, &pos, max_size, comp_data);
	if (ret < 0) {
		// some RR didn't fit in, so no RRs should be used
		// TODO: remove last entries from compression table
		dbg_rrset_verb("Some RR didn't fit in.\n");
		return KNOT_ESPACE;
	}

	// Check if the whole RRSet fit into packet.
	assert(ret <= max_size);
	assert(pos - wire == ret);

	*size = ret;

	dbg_rrset_detail("Size after: %zu\n", *size);

	// If the rrset is empty set record counter to 1.
	*rr_count = rrset->rdata_count > 0 ? rrset->rdata_count : 1;

	return KNOT_EOK;
}

int knot_rrset_to_wire_one(const knot_rrset_t *rrset, uint16_t rr_number,
                           uint8_t *wire, size_t max_size, size_t *outsize,
                           void *compr)
{
	if (!rrset || !wire || !outsize)
		return KNOT_EINVAL;

	uint8_t *pos = wire;
	return knot_rrset_rdata_to_wire_one(rrset, rr_number, &pos, max_size,
					    outsize, (knot_compr_t *)compr);
}

int knot_rrset_rdata_from_wire_one(knot_rrset_t *rrset,
                                   const uint8_t *wire, size_t *pos,
                                   size_t total_size, size_t rdlength)
{
	if (rrset == NULL || wire == NULL || pos == NULL) {
		return KNOT_EINVAL;
	}

	if (rdlength == 0) {
		/* Nothing to parse, */
		return KNOT_EOK;
	}

	dbg_rrset_detail("rr: parse_rdata_wire: Parsing RDATA of size=%zu,"
	                 " wire_size=%zu, type=%d.\n", rdlength, total_size,
	                 rrset->type);

	const rdata_descriptor_t *desc = get_rdata_descriptor(rrset->type);

	/* Check for obsolete record. */
	if (desc->type_name == NULL) {
		desc = get_obsolete_rdata_descriptor(rrset->type);
	}

	/*! \todo This estimate is very rough - just to have enough space for
	 *        possible unpacked dname. Should be later replaced by exact
	 *        size counting.
	 */
	uint8_t rdata_buffer[rdlength + KNOT_DNAME_MAXLEN];
	memset(rdata_buffer, 0, rdlength + KNOT_DNAME_MAXLEN);

	size_t offset = 0; // offset within in-memory RDATA
	size_t parsed = 0; // actual count of parsed octets
	const size_t packet_offset = *pos;

	/*! \todo [RRSet refactor]
	 *        This could be A LOT simpler - copy it as a whole,
	 *        unpack dnames and just do some format checks if necessary.
	 *        But it's questionable, if copying the memory when unpacking
	 *        dnames, wouldn't be too expensive.
	 */

	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END &&
	     parsed < rdlength; ++i) {
		const int item = desc->block_types[i];
		if (descriptor_item_is_dname(item)) {
			int wire_size = knot_dname_wire_check(wire + *pos,
			                                      wire + *pos + rdlength,
			                                      wire);
			if (wire_size <= 0) {
				return KNOT_EMALF;
			}
			int unpacked_size = knot_dname_unpack(
				rdata_buffer + offset, wire + *pos,
				KNOT_DNAME_MAXLEN, wire);
			if (unpacked_size <= 0) {
				return KNOT_EMALF;
			}

			parsed += wire_size;

dbg_rrset_exec_detail(
			dbg_rrset_detail("rr: parse_rdata_wire: Parsed DNAME, "
			                 "length=%d.\n", wire_size);
			char *name = knot_dname_to_str(rdata_buffer + offset);
			dbg_rrset_detail("rr: parse_rdata_wire: Parsed "
			                 "DNAME=%s\n", name);
			free(name);
);
			*pos += wire_size;
			offset += unpacked_size;
		} else if (descriptor_item_is_fixed(item)) {
			dbg_rrset_detail("rr: parse_rdata_wire: Saving static "
			                 "chunk of size=%u\n", item);
			int ret = knot_rrset_rdata_store_binary(rdata_buffer,
			                                        &offset,
			                                        packet_offset,
			                                        wire,
			                                        pos,
			                                        rdlength,
			                                        item);
			if (ret != KNOT_EOK) {
				dbg_rrset("rrset: rdata_from_wire: "
				          "Cannot store fixed RDATA chunk. "
				          "Reason: %s.\n", knot_strerror(ret));
				return ret;
			}
			parsed += item;
		} else if (descriptor_item_is_remainder(item)) {
			/* Item size has to be calculated. */
			size_t remainder_size = rdlength - parsed;
			dbg_rrset_detail("rr: parse_rdata_wire: Saving remaining "
			                 "chunk of size=%zu\n", remainder_size);
			int ret = knot_rrset_rdata_store_binary(rdata_buffer,
			                                        &offset,
			                                        packet_offset,
			                                        wire,
			                                        pos,
			                                        rdlength,
			                                        remainder_size);
			if (ret != KNOT_EOK) {
				dbg_rrset("rrset: rdata_from_wire: "
				          "Cannot store RDATA remainder of "
				          "size=%zu, RDLENGTH=%zu. "
				          "Reason: %s.\n", remainder_size,
				          rdlength, knot_strerror(ret));
				return ret;
			}
			parsed += remainder_size;
		} else {
			assert(rrset->type == KNOT_RRTYPE_NAPTR);
			/* Read fixed part - 2 shorts. */
			const size_t naptr_fixed_part_size = 4;
			int ret = knot_rrset_rdata_store_binary(rdata_buffer,
			                                        &offset,
			                                        packet_offset,
			                                        wire,
			                                        pos,
			                                        rdlength,
			                                        naptr_fixed_part_size);
			if (ret != KNOT_EOK) {
				dbg_rrset("rrset: rdata_from_wire: "
				          "Cannot store NAPTR fixed part. "
				          "Reason: %s.\n", knot_strerror(ret));
				return ret;
			}
			parsed += naptr_fixed_part_size;
			for (int j = 0; j < 3; ++j) {
				/* Read sizes of TXT's - one byte. */
				uint8_t txt_size = *(wire + (*pos)) + 1;
				dbg_rrset_detail("rrset: rdata_from_wire: "
				                 "Read TXT nr=%d size=%d\n", j,
				                 txt_size);
				int ret = knot_rrset_rdata_store_binary(rdata_buffer,
				                                        &offset,
				                                        packet_offset,
				                                        wire,
				                                        pos,
				                                        rdlength,
				                                        txt_size);
				if (ret != KNOT_EOK) {
					dbg_rrset("rrset: rdata_from_wire: "
					          "Cannot store NAPTR TXTs. "
					          "Reason: %s.\n", knot_strerror(ret));
					return ret;
				}
				parsed += txt_size;
			}
		}
	}

	uint8_t *rdata = knot_rrset_create_rdata(rrset, offset);
	if (rdata == NULL) {
		return KNOT_ENOMEM;
	}

	memcpy(rdata, rdata_buffer, offset);

	return KNOT_EOK;
}

int knot_rrset_equal(const knot_rrset_t *r1,
                     const knot_rrset_t *r2,
                     knot_rrset_compare_type_t cmp)
{
	if (cmp == KNOT_RRSET_COMPARE_PTR) {
		return r1 == r2;
	}


	if (!knot_dname_is_equal(r1->owner, r2->owner))
		return false;

	if (r1->rclass != r2->rclass || r1->type != r2->type)
		return false;

	if (cmp == KNOT_RRSET_COMPARE_WHOLE)
		return knot_rrset_rdata_equal(r1, r2);

	return true;
}

int knot_rrset_deep_copy_no_sig(const knot_rrset_t *from, knot_rrset_t **to)
{
	if (from == NULL || to == NULL) {
		return KNOT_EINVAL;
	}

	dbg_rrset_detail("rr: deep_copy: Copying RRs of type %d\n",
	                 from->type);

	*to = xmalloc(sizeof(knot_rrset_t));

	(*to)->owner = knot_dname_copy(from->owner);
	if ((*to)->owner == NULL) {
		free(*to);
		*to = NULL;
		return KNOT_ENOMEM;
	}

	(*to)->rclass = from->rclass;
	(*to)->ttl = from->ttl;
	(*to)->type = from->type;
	(*to)->rdata_count = from->rdata_count;
	(*to)->rrsigs = NULL;

	/* Just copy arrays - actual data + indices. */
	(*to)->rdata = xmalloc(rrset_rdata_size_total(from));
	memcpy((*to)->rdata, from->rdata, rrset_rdata_size_total(from));

	(*to)->rdata_indices = xmalloc(sizeof(uint32_t) * from->rdata_count);
	memcpy((*to)->rdata_indices, from->rdata_indices,
	       sizeof(uint32_t) * from->rdata_count);

	return KNOT_EOK;
}

int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to)
{
	int result = knot_rrset_deep_copy_no_sig(from, to);

	if (result == KNOT_EOK && from->rrsigs != NULL) {
		result = knot_rrset_deep_copy_no_sig(from->rrsigs,
		                                     &(*to)->rrsigs);
		if (result != KNOT_EOK) {
			knot_rrset_deep_free(to, 1);
		}
	}

	return result;
}

/*----------------------------------------------------------------------------*/

int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to)
{
	if (!from || !to) {
		return KNOT_EINVAL;
	}

	knot_rrset_t *result = (knot_rrset_t *)malloc(sizeof(knot_rrset_t));
	if (!result) {
		return KNOT_ENOMEM;
	}

	memcpy(result, from, sizeof(knot_rrset_t));
	result->owner = knot_dname_copy(result->owner);
	if (!result->owner) {
		free(result);
		return KNOT_ENOMEM;
	}

	*to = result;

	return KNOT_EOK;
}

/*----------------------------------------------------------------------------*/

void knot_rrset_rotate(knot_rrset_t *rrset)
{
	/*! \todo Maybe implement properly one day. */
	//rrset->rdata = rrset->rdata->next;
}

/*----------------------------------------------------------------------------*/

void knot_rrset_free(knot_rrset_t **rrset)
{
	if (rrset == NULL || *rrset == NULL) {
		return;
	}

	knot_dname_free(&(*rrset)->owner);

	free(*rrset);
	*rrset = NULL;
}

static void rrset_deep_free_content(knot_rrset_t *rrset, int free_owner)
{
	assert(rrset);

	if (rrset->rrsigs) {
		assert(rrset->rrsigs->rrsigs == NULL);
		rrset_deep_free_content(rrset->rrsigs, free_owner);
		free(rrset->rrsigs);
	}

	free(rrset->rdata);
	free(rrset->rdata_indices);
	if (free_owner) {
		knot_dname_free(&rrset->owner);
	}
}

void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner)
{
	/*! \bug The number of different frees in rrset is too damn high! */
	if (rrset == NULL || *rrset == NULL) {
		return;
	}

	rrset_deep_free_content(*rrset, free_owner);

	free(*rrset);
	*rrset = NULL;
}

void knot_rrset_deep_free_no_sig(knot_rrset_t **rrset, int free_owner)
{
	if (rrset == NULL || *rrset == NULL) {
		return;
	}

	free((*rrset)->rdata);
	free((*rrset)->rdata_indices);

	if (free_owner) {
		knot_dname_free(&(*rrset)->owner);
	}

	free(*rrset);
	*rrset = NULL;
}

static int knot_rrset_add_rr_n(knot_rrset_t *rrset, const knot_rrset_t *rr,
                               size_t pos)
{
	if (rrset == NULL || rr == NULL) {
		return KNOT_EINVAL;
	}
	if (!knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_HEADER)) {
		// Adding to a different header
		return KNOT_EINVAL;
	}

	uint8_t *new_rdata =
		knot_rrset_create_rdata(rrset,
	                                rrset_rdata_item_size(rr, pos));
	if (new_rdata == NULL) {
		return KNOT_ERROR;
	}

	memcpy(new_rdata, rrset_rdata_pointer(rr, pos),
	       rrset_rdata_item_size(rr, pos));

	return KNOT_EOK;
}

int knot_rrset_merge(knot_rrset_t *rrset1, const knot_rrset_t *rrset2)
{
	for (uint16_t i = 0; i < rrset2->rdata_count; ++i) {
		int ret = knot_rrset_add_rr_n(rrset1, rrset2, i);
		if (ret != KNOT_EOK) {
			return ret;
		}
	}

	return KNOT_EOK;
}

static int knot_rrset_add_rr_sort_n(knot_rrset_t *rrset, const knot_rrset_t *rr,
                                    int *merged, int *deleted, size_t pos)
{
	if (rrset == NULL || rr == NULL) {
		dbg_rrset("rrset: add_rr_sort: NULL arguments.");
		return KNOT_EINVAL;
	}

dbg_rrset_exec_detail(
	char *name = knot_dname_to_str(rrset->owner);
	dbg_rrset_detail("rrset: add_rr_sort: Merging %s.\n", name);
	free(name);
);

	if ((!knot_dname_is_equal(rrset->owner, rr->owner))
	    || rrset->rclass != rr->rclass
	    || rrset->type != rr->type) {
		dbg_rrset("rrset: add_rr_sort: Trying to merge "
		          "different RRs.\n");
		return KNOT_EINVAL;
	}

	int found = 0;
	int duplicated = 0;
	// Compare RR with all RRs in the first RRSet.
	size_t insert_to = 0;
	for (uint16_t j = 0; j < rrset->rdata_count && (!duplicated && !found); ++j) {
		int cmp = rrset_rdata_compare_one(rrset, rr, j, pos);
		if (cmp == 0) {
			// Duplication - no need to merge this RR
			duplicated = 1;
		} else if (cmp > 0) {
			// Found position to insert
			found = 1;
		} else {
			// Not yet - it might be next position
			insert_to = j + 1;
		}
	}

	if (!duplicated) {
		*merged += 1; // = need to shallow free rrset2
		// Insert RR to RRSet
		int ret = knot_rrset_add_rdata_at_pos(rrset, insert_to,
			                  rrset_rdata_pointer(rr, pos),
			                  rrset_rdata_item_size(rr, pos));
		if (ret != KNOT_EOK) {
			dbg_rrset("rrset: add_rr: Could not "
			          "add RDATA to RRSet. (%s)\n",
			          knot_strerror(ret));
			return ret;
		}
	} else {
		assert(!found);
		*deleted += 1; // = need to shallow free rr
	}

	return KNOT_EOK;
}

int knot_rrset_merge_sort(knot_rrset_t *rrset1, const knot_rrset_t *rrset2,
                          int *merged_rrs, int *deleted_rrs)
{
	int result = KNOT_EOK;
	int merged = 0;
	int deleted = 0;

	for (uint16_t i = 0; i < rrset2->rdata_count; ++i) {
		result = knot_rrset_add_rr_sort_n(rrset1, rrset2, &merged,
		                                   &deleted, i);
		if (result != KNOT_EOK) {
			break;
		}
	}

	if (merged_rrs) {
		*merged_rrs = merged;
	}

	if (deleted_rrs) {
		*deleted_rrs = deleted;
	}

	return result;
}

/*!
 * \todo Not optimal, rewrite!
 */
int knot_rrset_sort_rdata(knot_rrset_t *rrset)
{
	if (!rrset) {
		return KNOT_EINVAL;
	}

	// 1. create temporary rrset
	// 2. sort-merge given rrset into temporary rrset
	// 3. swap the contents, free the temporary

	knot_rrset_t *sorted = knot_rrset_new(rrset->owner, rrset->type,
	                                      rrset->rclass, rrset->ttl);
	if (!sorted) {
		return KNOT_ENOMEM;
	}

	int result = knot_rrset_merge_sort(sorted, rrset, NULL, NULL);
	if (result != KNOT_EOK) {
		knot_rrset_deep_free(&sorted, 1);
		return result;
	}

	rrset_deep_free_content(rrset, 0);
	*rrset = *sorted;
	free(sorted);

	return KNOT_EOK;
}

bool knot_rrset_is_nsec3rel(const knot_rrset_t *rr)
{
	assert(rr != NULL);

	/* Is NSEC3 or non-empty RRSIG covering NSEC3. */
	return ((knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3)
	        || (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG
	            && knot_rdata_rrsig_type_covered(rr, 0)
	            == KNOT_RRTYPE_NSEC3));
}

/*----------------------------------------------------------------------------*/

void knot_rrset_dump(const knot_rrset_t *rrset)
{
dbg_rrset_exec_detail(
	if (rrset == NULL) {
		return;
	}

	dbg_rrset_detail("      ------- RRSET -------\n");

	char *name = knot_dname_to_str(rrset->owner);
	dbg_rrset_detail("  owner: %s\n", name);
	free(name);
	dbg_rrset_detail("  type: %u\n", rrset->type);
	dbg_rrset_detail("  class: %d\n",  rrset->rclass);
	dbg_rrset_detail("  ttl: %d\n", rrset->ttl);
	dbg_rrset_detail("  RDATA count: %d\n", rrset->rdata_count);

	dbg_rrset_detail("  RRSIGs:\n");
	if (rrset->rrsigs != NULL) {
	        knot_rrset_dump(rrset->rrsigs);
	} else {
	        dbg_rrset_detail("  none\n");
	}

	dbg_rrset_detail("RDATA indices (total=%d):\n",
	        rrset_rdata_size_total(rrset));

	for (uint16_t i = 0; i < rrset->rdata_count; i++) {
		dbg_rrset_detail("%d=%d\n", i, rrset_rdata_offset(rrset, i));
	}

	if (knot_rrset_rdata_rr_count(rrset) == 0) {
		dbg_rrset_detail("NO RDATA\n");
	}

	for (uint16_t i = 0; i < knot_rrset_rdata_rr_count(rrset); i++) {
		knot_rrset_rdata_dump(rrset, i);
	}
);
}

uint64_t rrset_binary_size(const knot_rrset_t *rrset)
{
	if (rrset == NULL || rrset->rdata_count == 0) {
		return 0;
	}
	uint64_t size = sizeof(uint64_t) + // size at the beginning
	              knot_dname_size(knot_rrset_owner(rrset)) + // owner data
	              sizeof(uint16_t) + // type
	              sizeof(uint16_t) + // class
	              sizeof(uint32_t) + // ttl
	              sizeof(uint16_t) +  //RR count
	              sizeof(uint32_t) * rrset->rdata_count; // RR indices
	for (uint16_t i = 0; i < rrset->rdata_count; i++) {
		/* Space to store length of one RR. */
		size += sizeof(uint32_t);
		/* Actual data. */
		size += rrset_binary_size_one(rrset, i);
	}

	return size;
}

int rrset_serialize(const knot_rrset_t *rrset, uint8_t *stream, size_t *size)
{
	if (rrset == NULL || rrset->rdata_count == 0) {
		return KNOT_EINVAL;
	}

	uint64_t rrset_length = rrset_binary_size(rrset);
	dbg_rrset_detail("rr: serialize: Binary size=%"PRIu64"\n", rrset_length);
	memcpy(stream, &rrset_length, sizeof(uint64_t));

	size_t offset = sizeof(uint64_t);
	/* Save RR indices. Size first. */
	memcpy(stream + offset, &rrset->rdata_count, sizeof(uint16_t));
	offset += sizeof(uint16_t);
	memcpy(stream + offset, rrset->rdata_indices,
	       rrset->rdata_count * sizeof(uint32_t));
	offset += sizeof(uint32_t) * rrset->rdata_count;
	/* Save owner. */
	offset += knot_dname_to_wire(stream + offset, rrset->owner, rrset_length - offset);

	/* Save static data. */
	memcpy(stream + offset, &rrset->type, sizeof(uint16_t));
	offset += sizeof(uint16_t);
	memcpy(stream + offset, &rrset->rclass, sizeof(uint16_t));
	offset += sizeof(uint16_t);
	memcpy(stream + offset, &rrset->ttl, sizeof(uint32_t));
	offset += sizeof(uint32_t);

	/* Copy RDATA. */
	for (uint16_t i = 0; i < rrset->rdata_count; i++) {
		size_t size_one = 0;
		/* This cannot fail, if it does, RDATA are malformed. TODO */
		/* TODO this can be written later. */
		uint32_t rr_size = rrset_binary_size_one(rrset, i);
		dbg_rrset_detail("rr: serialize: RR index=%d size=%d\n",
		                 i, rr_size);
		memcpy(stream + offset, &rr_size, sizeof(uint32_t));
		offset += sizeof(uint32_t);
		rrset_serialize_rr(rrset, i, stream + offset, &size_one);
		assert(size_one == rr_size);
		offset += size_one;
	}

	*size = offset;
	assert(*size == rrset_length);
	dbg_rrset_detail("rr: serialize: RRSet serialized, size=%zu\n", *size);
	return KNOT_EOK;
}

int rrset_serialize_alloc(const knot_rrset_t *rrset, uint8_t **stream,
                          size_t *size)
{
	/* Get the binary size. */
	*size = rrset_binary_size(rrset);
	if (*size == 0) {
		/* Nothing to serialize. */
		dbg_rrset("rrset: serialize alloc: No data to serialize.\n");
		return KNOT_EINVAL;
	}

	/* Prepare memory. */
	*stream = malloc(*size);
	if (*stream == NULL) {
		return KNOT_ENOMEM;
	}

	return rrset_serialize(rrset, *stream, size);
}

int rrset_deserialize(uint8_t *stream, size_t *stream_size,
                      knot_rrset_t **rrset)
{
	if (sizeof(uint64_t) > *stream_size) {
		dbg_rrset("rr: deserialize: No space for length.\n");
		return KNOT_ESPACE;
	}
	uint64_t rrset_length = 0;
	memcpy(&rrset_length, stream, sizeof(uint64_t));
	if (rrset_length > *stream_size) {
		dbg_rrset("rr: deserialize: No space for whole RRSet. "
		          "(given=%zu needed=%"PRIu64")\n", *stream_size,
		          rrset_length);
		return KNOT_ESPACE;
	}

	size_t offset = sizeof(uint64_t);
	uint16_t rdata_count = 0;
	memcpy(&rdata_count, stream + offset, sizeof(uint16_t));
	offset += sizeof(uint16_t);
	uint32_t *rdata_indices = xmalloc(rdata_count * sizeof(uint32_t));
	memcpy(rdata_indices, stream + offset,
	       rdata_count * sizeof(uint32_t));
	offset += rdata_count * sizeof(uint32_t);
	/* Read owner from the stream. */
	unsigned owner_size = knot_dname_size(stream + offset);
	knot_dname_t *owner = knot_dname_copy_part(stream + offset, owner_size);
	assert(owner);
	offset += owner_size;
	/* Read type. */
	uint16_t type = 0;
	memcpy(&type, stream + offset, sizeof(uint16_t));
	offset += sizeof(uint16_t);
	/* Read class. */
	uint16_t rclass = 0;
	memcpy(&rclass, stream + offset, sizeof(uint16_t));
	offset += sizeof(uint16_t);
	/* Read TTL. */
	uint32_t ttl = 0;
	memcpy(&ttl, stream + offset, sizeof(uint32_t));
	offset += sizeof(uint32_t);

	/* Create new RRSet. */
	*rrset = knot_rrset_new(owner, type, rclass, ttl);
	if (*rrset == NULL) {
		knot_dname_free(&owner);
		free(rdata_indices);
		return KNOT_ENOMEM;
	}

	(*rrset)->rdata_indices = rdata_indices;
	(*rrset)->rdata_count = rdata_count;

	(*rrset)->rdata = xmalloc(rdata_indices[rdata_count - 1]);
	memset((*rrset)->rdata, 0, rdata_indices[rdata_count - 1]);
	/* Read RRs. */
	for (uint16_t i = 0; i < (*rrset)->rdata_count; i++) {
		/*
		 * There's always size of rdata in the beginning.
		 * Needed because of remainders.
		 */
		uint32_t rdata_size = 0;
		memcpy(&rdata_size, stream + offset, sizeof(uint32_t));
		offset += sizeof(uint32_t);
		size_t read = 0;
		int ret = rrset_deserialize_rr((*rrset), i, stream + offset,
		                               rdata_size, &read);
		if (ret != KNOT_EOK) {
			free((*rrset)->rdata);
			free(rdata_indices);
			return ret;
		}
		/* TODO handle malformations. */
		dbg_rrset_detail("rr: deserialaze: RR read size=%zu,"
		                 "actual=%"PRIu32"\n", read, rdata_size);
		assert(read == rdata_size);
		offset += read;
	}

dbg_rrset_exec_detail(
	dbg_rrset_detail("RRSet deserialized:\n");
	knot_rrset_dump(*rrset);
);
	*stream_size = *stream_size - offset;

	return KNOT_EOK;
}

int knot_rrset_find_rr_pos(const knot_rrset_t *rr_search_in,
                           const knot_rrset_t *rr_reference, size_t pos,
                           size_t *pos_out)
{
	int found = 0;
	for (uint16_t i = 0; i < rr_search_in->rdata_count && !found; ++i) {
		if (rrset_rdata_compare_one(rr_search_in,
		                            rr_reference, i, pos) == 0) {
			*pos_out = i;
			found = 1;
		}
	}

	return found ? KNOT_EOK : KNOT_ENOENT;
}

static int knot_rrset_remove_rr(knot_rrset_t *rrset,
                                const knot_rrset_t *rr_from, size_t rdata_pos)
{
	/*
	 * Position in first and second rrset can differ, we have
	 * to search for position first.
	 */
	size_t pos_to_remove = 0;
	int ret = knot_rrset_find_rr_pos(rrset, rr_from, rdata_pos,
	                                 &pos_to_remove);
	if (ret == KNOT_EOK) {
		/* Position found, can be removed. */
		dbg_rrset_detail("rr: remove_rr: Counter position found=%zu\n",
		                 pos_to_remove);
		assert(pos_to_remove < rrset->rdata_count);
		ret = knot_rrset_remove_rdata_pos(rrset, pos_to_remove);
		if (ret != KNOT_EOK) {
			dbg_rrset("Cannot remove RDATA from RRSet (%s).\n",
			          knot_strerror(ret));
			return ret;
		}
	} else {
		dbg_rrset_verb("rr: remove_rr: RDATA not found (%s).\n",
		               knot_strerror(ret));
		return ret;
	}

	return KNOT_EOK;
}

static int knot_rrset_rdata_reset(knot_rrset_t *rrset)
{
	if (rrset == NULL) {
		return KNOT_EINVAL;
	}

	rrset->rdata = NULL;
	rrset->rdata_indices = NULL;
	rrset->rdata_count = 0;

	return KNOT_EOK;
}

int knot_rrset_add_rr_from_rrset(knot_rrset_t *dest, const knot_rrset_t *source,
                                 size_t rdata_pos)
{
	if (dest == NULL || source == NULL ||
	    rdata_pos >= source->rdata_count) {
		return KNOT_EINVAL;
	}

	/* Get size of RDATA to be copied. */
	uint16_t item_size = rrset_rdata_item_size(source, rdata_pos);
	/* Reserve space in dest RRSet. */
	uint8_t *rdata = knot_rrset_create_rdata(dest, item_size);
	if (rdata == NULL) {
		dbg_rrset("rr: add_rr_from_rrset: Could not create RDATA.\n");
		return KNOT_ERROR;
	}

	/* Copy actual data. */
	memcpy(rdata, rrset_rdata_pointer(source, rdata_pos), item_size);

	return KNOT_EOK;
}

int knot_rrset_remove_rr_using_rrset(knot_rrset_t *from,
                                     const knot_rrset_t *what,
                                     knot_rrset_t **rr_deleted)
{
	if (from == NULL || what == NULL || rr_deleted == NULL) {
		return KNOT_EINVAL;
	}

	knot_rrset_t *return_rr = NULL;
	int ret = knot_rrset_shallow_copy(what, &return_rr);
	if (ret != KNOT_EOK) {
		dbg_rrset("remove_rr_using_rrset: Could not copy RRSet (%s).\n",
		          knot_strerror(ret));
		return ret;
	}
	/* Reset RDATA of returned RRSet. */
	knot_rrset_rdata_reset(return_rr);
	return_rr->rrsigs = NULL;

	for (uint16_t i = 0; i < what->rdata_count; ++i) {
		ret = knot_rrset_remove_rr(from, what, i);
		if (ret == KNOT_EOK) {
			/* RR was removed, can be added to 'return' RRSet. */
			ret = knot_rrset_add_rr_from_rrset(return_rr, what, i);
			if (ret != KNOT_EOK) {
				knot_rrset_deep_free(&return_rr, 0);
				dbg_xfrin("xfr: Could not add RR (%s).\n",
				          knot_strerror(ret));
				return ret;
			}
			dbg_rrset_detail("rrset: remove_rr_using_rrset: "
			                 "Successfuly removed and returned this RR:\n");
			knot_rrset_rdata_dump(return_rr, return_rr->rdata_count - 1);
		} else if (ret != KNOT_ENOENT) {
			/* NOENT is OK, but other errors are not. */
			dbg_rrset("rrset: remove_using_rrset: "
			          "RRSet removal failed (%s).\n",
			          knot_strerror(ret));
			knot_rrset_deep_free(&return_rr, 0);
			return ret;
		}
	}

	*rr_deleted = return_rr;
	return KNOT_EOK;
}

int knot_rrset_remove_rr_using_rrset_del(knot_rrset_t *from,
                                         const knot_rrset_t *what)
{
	knot_rrset_t *rr_removed = NULL;
	int ret = knot_rrset_remove_rr_using_rrset(from, what, &rr_removed);
	knot_rrset_deep_free(&rr_removed, 1);
	return ret;
}

void knot_rrset_set_class(knot_rrset_t *rrset, uint16_t rclass)
{
	if (!rrset) {
		return;
	}

	rrset->rclass = rclass;
}

int knot_rrset_ds_check(const knot_rrset_t *rrset)
{
	// Check if the legth of the digest corresponds to the proper size of
	// the digest according to the given algorithm
	for (uint16_t i = 0; i < rrset->rdata_count; ++i) {
		/* 4 bytes before actual digest. */
		if (rrset_rdata_item_size(rrset, i) < 4) {
			/* Not even keytag, alg and alg type. */
			return KNOT_EMALF;
		}
		uint16_t len = rrset_rdata_item_size(rrset, i) - 4;
		uint8_t type = rrset_rdata_pointer(rrset, i)[3];
		if (type == 0 || len == 0) {
			return KNOT_EINVAL;
		} else if (len != knot_ds_digest_length(type)) {
			return KNOT_EDSDIGESTLEN;
		}
	}
	return KNOT_EOK;
}