diff --git a/Knot.files b/Knot.files
index 8def07c81375a278fb1053476c0a0d15f6a039f9..552f82c4b59d30831b4ceef92837d62ffc056901 100644
--- a/Knot.files
+++ b/Knot.files
@@ -40,6 +40,8 @@ src/common/sockaddr.h
 src/common/sockaddr.c
 src/common/crc.c
 src/common/crc.h
+src/common/ref.c
+src/common/ref.h
 src/dnslib/dnslib-common.h
 src/dnslib/dname.h
 src/dnslib/dname.c
diff --git a/src/Makefile.am b/src/Makefile.am
index daa5632842c01007ae339010643556a204363fea..3b101663451fab612a736c781909fb5f224e241a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -153,6 +153,8 @@ libknot_la_SOURCES =				\
 	common/sockaddr.c				\
 	common/crc.h				\
 	common/crc.c				\
+	common/ref.h				\
+	common/ref.c				\
 	knot/stat/gatherer.c				\
 	knot/stat/stat.c				\
 	knot/stat/gatherer.h				\
diff --git a/src/common/ref.c b/src/common/ref.c
new file mode 100644
index 0000000000000000000000000000000000000000..88add37b60d14b5b6a23ebd5877bac0e92ac9dca
--- /dev/null
+++ b/src/common/ref.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+
+#include "ref.h"
+
+void ref_init(ref_t *p, ref_destructor_t dtor)
+{
+	if (p) {
+		p->count = 0;
+		p->dtor = dtor;
+	}
+}
+
+void ref_retain(ref_t *p)
+{
+	if (p) {
+		__sync_add_and_fetch(&p->count, 1);
+	}
+}
+
+void ref_release(ref_t *p)
+{
+	if (p) {
+		int rc = __sync_sub_and_fetch(&p->count, 1);
+		if (rc == 0) {
+			p->dtor(p);
+		}
+	}
+}
diff --git a/src/common/ref.h b/src/common/ref.h
new file mode 100644
index 0000000000000000000000000000000000000000..03bda19505992a39e86427c9d8e318d073fe4a04
--- /dev/null
+++ b/src/common/ref.h
@@ -0,0 +1,75 @@
+/*!
+ * \file ref.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Atomic reference counting structures.
+ *
+ * Reference counting allows implicit sharing of objects
+ * between threads with custom destructor functions.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOT_REF_H_
+#define _KNOT_REF_H_
+
+#include <stddef.h>
+
+struct ref_t;
+
+/*! \brief Prototype for object destructor callback. */
+typedef void (*ref_destructor_t)(struct ref_t * p);
+
+/*!
+ * \brief Structure for reference counting.
+ *
+ * Size equals to two sizes of pointer size.
+ * Structure may be embedded to the structures which
+ * we want to use for reference counting.
+ *
+ * \code
+ * struct mystruct {
+ *    ref_t ref;
+ *    int mydata;
+ *    char *mystr;
+ * }
+ * \endcode
+ */
+typedef struct ref_t {
+	size_t count;          /*! \brief Reference counter. */
+	ref_destructor_t dtor; /*! \brief Object destructor function. */
+} ref_t;
+
+/*!
+ * \brief Initialize reference counter.
+ *
+ * Set reference counter to 0 and initialize destructor callback.
+ *
+ * \param p Reference-counted object.
+ * \param dtor Destructor function.
+ */
+void ref_init(ref_t *p, ref_destructor_t dtor);
+
+/*!
+ * \brief Mark object as used by the caller.
+ *
+ * Reference counter will be incremented.
+ *
+ * \param p Reference-counted object.
+ */
+void ref_retain(ref_t *p);
+
+/*!
+ * \brief Marks object as unused by the caller.
+ *
+ * Reference counter will be decremented.
+ *
+ * \param p Reference-counted object.
+ */
+void ref_release(ref_t *p);
+
+#endif /* _KNOT_REF_H_ */
+
+/*! @} */
diff --git a/src/dnslib/dname-table.c b/src/dnslib/dname-table.c
index 6ae8c1b58eeeb158b17d40c6a1f9592175a87e90..72258d2f019019605f0c29357425ef5c12fbd36f 100644
--- a/src/dnslib/dname-table.c
+++ b/src/dnslib/dname-table.c
@@ -49,7 +49,7 @@ static int compare_dname_table_nodes(struct dname_table_node *n1,
 static void delete_dname_table_node(struct dname_table_node *node, void *data)
 {
 	if (data) {
-		dnslib_dname_free(&node->dname);
+		dnslib_dname_release(node->dname);
 	}
 
 	/*!< \todo it would be nice to set pointers to NULL. */
@@ -81,6 +81,7 @@ static int dnslib_dname_table_copy_node(const struct dname_table_node *from,
 	}
 
 	(*to)->dname = from->dname;
+	dnslib_dname_retain((*to)->dname);
 	(*to)->avl.avl_height = from->avl.avl_height;
 
 	int ret = dnslib_dname_table_copy_node(from->avl.avl_left,
@@ -135,6 +136,9 @@ dnslib_dname_t *dnslib_dname_table_find_dname(const dnslib_dname_table_t *table,
 	if (node == NULL) {
 		return NULL;
 	} else {
+		/* Increase reference counter. */
+		dnslib_dname_retain(node->dname);
+
 		return node->dname;
 	}
 }
@@ -160,6 +164,9 @@ int dnslib_dname_table_add_dname(dnslib_dname_table_t *table,
 //	printf("Inserted dname got id %d\n", node->dname->id);
 	assert(node->dname->id != 0);
 
+	/* Increase reference counter. */
+	dnslib_dname_retain(dname);
+
 	TREE_INSERT(table->tree, dname_table_node, avl, node);
 	return DNSLIB_EOK;
 }
@@ -168,7 +175,6 @@ int dnslib_dname_table_add_dname2(dnslib_dname_table_t *table,
                                   dnslib_dname_t **dname)
 {
 	dnslib_dname_t *found_dname = NULL;
-	int ret;
 
 	if (table == NULL || dname == NULL || *dname == NULL) {
 		return DNSLIB_EBADARG;
@@ -178,20 +184,31 @@ int dnslib_dname_table_add_dname2(dnslib_dname_table_t *table,
 //	printf("Inserting name %s to dname table.\n", name);
 //	free(name);
 
-	if ((!(found_dname =
-	      dnslib_dname_table_find_dname(table ,*dname))) &&
-	      (ret = dnslib_dname_table_add_dname(table, *dname))
-	      != DNSLIB_EOK) {
-//		printf("Error!\n");
-		return ret;
-	} else if (found_dname != NULL && found_dname != *dname) {
+	/* Fetch dname, need to release it later. */
+	found_dname = dnslib_dname_table_find_dname(table ,*dname);
+
+	if (!found_dname) {
+		/* Store reference in table. */
+		return dnslib_dname_table_add_dname(table, *dname);
+	} else {
 		/*! \todo Remove the check for equality. */
-//		name = dnslib_dname_to_str(found_dname);
-//		printf("Already there: %s (%p)\n", name, found_dname);
-//		free(name);
-		dnslib_dname_free(dname);
-		*dname = found_dname;
-		return 1;
+		if (found_dname != *dname) {
+			//name = dnslib_dname_to_str(found_dname);
+			//printf("Already there: %s (%p)\n", name, found_dname);
+			//free(name);
+
+			/* Replace dname with found. */
+			dnslib_dname_release(*dname);
+			*dname = found_dname;
+			return 1; /*! \todo Error code? */
+
+		} else {
+
+			/* If the dname is already in the table, there is already
+			 * a reference to it.
+			 */
+			dnslib_dname_release(found_dname);
+		}
 	}
 
 //	printf("Done.\n");
@@ -199,8 +216,8 @@ int dnslib_dname_table_add_dname2(dnslib_dname_table_t *table,
 	return DNSLIB_EOK;
 }
 
-int dnslib_dname_table_copy(dnslib_dname_table_t *from,
-                            dnslib_dname_table_t *to)
+int dnslib_dname_table_shallow_copy(dnslib_dname_table_t *from,
+                                    dnslib_dname_table_t *to)
 {
 	to->id_counter = from->id_counter;
 
diff --git a/src/dnslib/dname-table.h b/src/dnslib/dname-table.h
index d056317a1feb2cc0353ce3887763be15b4a5cfe1..ea2893371e607bd107a65802049cabc043351a52 100644
--- a/src/dnslib/dname-table.h
+++ b/src/dnslib/dname-table.h
@@ -55,6 +55,9 @@ dnslib_dname_table_t *dnslib_dname_table_new();
 /*!
  * \brief Finds name in the domain name table.
  *
+ * \note Reference count to dname will be incremented, caller is responsible
+ *       for releasing it.
+ *
  * \param table Domain name table to be searched.
  * \param dname Dname to be searched.
  *
@@ -105,8 +108,8 @@ int dnslib_dname_table_add_dname2(dnslib_dname_table_t *table,
  * \param from Original domain name table.
  * \param to Copy of the domain name table.
  */
-int dnslib_dname_table_copy(dnslib_dname_table_t *from,
-                            dnslib_dname_table_t *to);
+int dnslib_dname_table_shallow_copy(dnslib_dname_table_t *from,
+                                    dnslib_dname_table_t *to);
 
 /*!
  * \brief Frees dname table without its nodes. Sets pointer to NULL.
diff --git a/src/dnslib/dname.c b/src/dnslib/dname.c
index 736bc13f6ce60d691d20bcc797f503436960ef34..dbf405b4503addc06e96ce63ca62ef1066faa204 100644
--- a/src/dnslib/dname.c
+++ b/src/dnslib/dname.c
@@ -14,6 +14,10 @@
 #include "dnslib/utils.h"
 #include "dnslib/wire.h"
 
+/*! \todo dnames allocated from TLS cache will be discarded after thread
+ *        termination. This shouldn't happpen.
+ */
+#if 0
 /*
  * Memory cache.
  */
@@ -46,6 +50,7 @@ static void dnslib_dname_cache_init()
 	(void) pthread_key_create(&dname_ckey, dnslib_dname_cache_free);
 	atexit(dnslib_dname_cache_main_free); // Main thread cleanup
 }
+#endif
 
 /*!
  * \brief Allocate item from thread cache.
@@ -56,6 +61,10 @@ static dnslib_dname_t* dnslib_dname_alloc()
 {
 	return malloc(sizeof(dnslib_dname_t));
 
+	/*! \todo dnames allocated from TLS cache will be discarded after thread
+	 *        termination. This shouldn't happpen.
+	 */
+#if 0
 	/* Initialize dname cache TLS key. */
 	(void)pthread_once(&dname_once, dnslib_dname_cache_init);
 
@@ -73,6 +82,7 @@ static dnslib_dname_t* dnslib_dname_alloc()
 	}
 
 	return slab_cache_alloc(cache);
+#endif
 }
 
 /*----------------------------------------------------------------------------*/
@@ -326,6 +336,13 @@ DEBUG_DNSLIB_DNAME(
 	return 0;
 }
 
+/*! \brief Destructor for reference counter. */
+static void dnslib_dname_dtor(struct ref_t *p)
+{
+	dnslib_dname_t *dname = (dnslib_dname_t *)p;
+	dnslib_dname_free(&dname);
+}
+
 /*----------------------------------------------------------------------------*/
 /* API functions                                                              */
 /*----------------------------------------------------------------------------*/
@@ -340,6 +357,12 @@ dnslib_dname_t *dnslib_dname_new()
 	dname->label_count = -1;
 	dname->id = 0;
 
+	/* Initialize reference counting. */
+	ref_init(&dname->ref, dnslib_dname_dtor);
+
+	/* Set reference counter to 1, caller should release it after use. */
+	dnslib_dname_retain(dname);
+
 	return dname;
 }
 
@@ -411,7 +434,7 @@ dnslib_dname_t *dnslib_dname_new_from_wire(const uint8_t *name, uint size,
 		return NULL;
 	}
 
-	dnslib_dname_t *dname = dnslib_dname_alloc();
+	dnslib_dname_t *dname = dnslib_dname_new();
 
 	if (dname == NULL) {
 		ERR_ALLOC_FAILED;
@@ -490,7 +513,7 @@ dnslib_dname_t *dnslib_dname_parse_from_wire(const uint8_t *wire,
 		*pos += 1;
 	}
 
-	dnslib_dname_t *dname = dnslib_dname_alloc();
+	dnslib_dname_t *dname = dnslib_dname_new();
 
 	if (dname == NULL) {
 		ERR_ALLOC_FAILED;
@@ -541,7 +564,7 @@ int dnslib_dname_from_wire(const uint8_t *name, uint size,
 
 /*----------------------------------------------------------------------------*/
 
-dnslib_dname_t *dnslib_dname_copy(const dnslib_dname_t *dname)
+dnslib_dname_t *dnslib_dname_deep_copy(const dnslib_dname_t *dname)
 {
 	return dnslib_dname_new_from_wire(dname->name, dname->size,
 	                                  dname->node);
diff --git a/src/dnslib/dname.h b/src/dnslib/dname.h
index f711c6152e988a1996c1184e8aad9ae78a42b58d..df2d6feece3f49466cade28b64c38acbf811e6ac 100644
--- a/src/dnslib/dname.h
+++ b/src/dnslib/dname.h
@@ -14,6 +14,7 @@
 
 #include <stdint.h>
 #include <string.h>
+#include "common/ref.h"
 
 struct dnslib_node;
 
@@ -36,6 +37,7 @@ struct dnslib_dname {
 	unsigned short label_count;
 	struct dnslib_node *node; /*!< Zone node the domain name belongs to. */
 	unsigned int id; /*!< ID of domain name used in zone dumping. */
+	ref_t ref;     /*!< Reference counting. */
 };
 
 typedef struct dnslib_dname dnslib_dname_t;
@@ -45,6 +47,9 @@ typedef struct dnslib_dname dnslib_dname_t;
 /*!
  * \brief Creates empty dname structure (no name, no owner node).
  *
+ * \note Newly created dname is referenced, caller is responsible for releasing
+ *       it after use.
+ *
  * \return Newly allocated and initialized dname structure.
  *
  * \todo Possibly useless.
@@ -58,6 +63,9 @@ dnslib_dname_t *dnslib_dname_new();
  * The resulting domain name is stored in wire format, but it may not end with
  * root label (0).
  *
+ * \note Newly created dname is referenced, caller is responsible for releasing
+ *       it after use.
+ *
  * \param name Domain name in presentation format (labels separated by dots).
  * \param size Size of the domain name (count of characters with all dots).
  * \param node Zone node the domain name belongs to. Set to NULL if not
@@ -74,6 +82,8 @@ dnslib_dname_t *dnslib_dname_new_from_str(const char *name, unsigned int size,
  *
  * \note The name is copied into the structure.
  * \note If the given name is not a FQDN, the result will be neither.
+ * \note Newly created dname is referenced, caller is responsible for releasing
+ *       it after use.
  *
  * \param name Domain name in wire format.
  * \param size Size of the domain name in octets.
@@ -124,13 +134,16 @@ int dnslib_dname_from_wire(const uint8_t *name, unsigned int size,
                            struct dnslib_node *node, dnslib_dname_t *target);
 
 /*!
- * \brief Copies the given domain name.
+ * \brief Duplicates the given domain name.
+ *
+ * \note Copied dname referense count is reset to 1, caller is responsible
+ *       for releasing it after use.
  *
  * \param dname Domain name to be copied.
  *
  * \return New domain name which is an exact copy of \a dname.
  */
-dnslib_dname_t *dnslib_dname_copy(const dnslib_dname_t *dname);
+dnslib_dname_t *dnslib_dname_deep_copy(const dnslib_dname_t *dname);
 
 /*!
  * \brief Converts the given domain name to string representation.
@@ -204,6 +217,9 @@ int dnslib_dname_is_fqdn(const dnslib_dname_t *dname);
 /*!
  * \brief Creates new domain name by removing leftmost label from \a dname.
  *
+ * \note Newly created dname reference count is set to 1, caller is responsible
+ *        for releasing it after use.
+ *
  * \param dname Domain name to remove the first label from.
  *
  * \return New domain name with the same labels as \a dname, except for the
@@ -342,6 +358,45 @@ void dnslib_dname_set_id(dnslib_dname_t *dname, unsigned int id);
 
 unsigned int dnslib_dname_get_id(const dnslib_dname_t *dname);
 
+/*!
+ * \brief Increment reference counter for dname.
+ *
+ * Function makes shallow copy (reference).
+ *
+ * \param dname Referenced dname.
+ */
+static inline void dnslib_dname_retain(dnslib_dname_t *dname) {
+	if (dname) {
+		ref_retain(&dname->ref);
+	}
+}
+
+/*
+#define dnslib_dname_retain(d) \
+	dnslib_dname_retain_((d));\
+	if ((d))\
+	fprintf(stderr, "dname_retain: %s() at %s:%d, %p refcount=%zu\n",\
+	__func__, __FILE__, __LINE__, d, (d)->ref.count)
+*/
+
+/*!
+ * \brief Decrement reference counter for dname.
+ *
+ * \param dname Referenced dname.
+ */
+static inline void dnslib_dname_release(dnslib_dname_t *dname) {
+	if (dname) {
+		ref_release(&dname->ref);
+	}
+}
+/*
+#define dnslib_dname_release(d) \
+	if ((d))\
+	fprintf(stderr, "dname_release: %s() at %s:%d, %p refcount=%zu\n",\
+	__func__, __FILE__, __LINE__, d, (d)->ref.count-1);\
+	dnslib_dname_release_((d))
+*/
+
 #endif /* _KNOT_DNSLIB_DNAME_H_ */
 
 /*! @} */
diff --git a/src/dnslib/node.c b/src/dnslib/node.c
index 3e35ee50773b1f87004ee68ca1dfd3bb97f42ccc..54e9d6db9e94b28fac20619745cc4bbde1fb300f 100644
--- a/src/dnslib/node.c
+++ b/src/dnslib/node.c
@@ -170,6 +170,8 @@ dnslib_node_t *dnslib_node_new(dnslib_dname_t *owner, dnslib_node_t *parent,
 		return NULL;
 	}
 
+	/* Store reference to owner. */
+	dnslib_dname_retain(owner);
 	ret->owner = owner;
 	dnslib_node_set_parent(ret, parent);
 	ret->rrsets = skip_create_list(compare_rrset_types);
@@ -434,6 +436,18 @@ dnslib_dname_t *dnslib_node_get_owner(const dnslib_node_t *node)
 
 /*----------------------------------------------------------------------------*/
 
+void dnslib_node_set_owner(dnslib_node_t *node, dnslib_dname_t* owner)
+{
+	if (node) {
+		/* Retain new owner and release old owner. */
+		dnslib_dname_retain(owner);
+		dnslib_dname_release(node->owner);
+		node->owner = owner;
+	}
+}
+
+/*----------------------------------------------------------------------------*/
+
 const dnslib_node_t *dnslib_node_wildcard_child(const dnslib_node_t *node, 
                                                 int check_version)
 {
@@ -697,10 +711,12 @@ void dnslib_node_free(dnslib_node_t **node, int free_owner, int fix_refs)
 		debug_dnslib_node("Freeing RRSets.\n");
 		skip_destroy_list(&(*node)->rrsets, NULL, NULL);
 	}
-	if (free_owner) {
-		debug_dnslib_node("Freeing owner.\n");
-		dnslib_dname_free(&(*node)->owner);
-	}
+
+	/*! \todo Always release owner? */
+	//if (free_owner) {
+		debug_dnslib_node("Releasing owner.\n");
+		dnslib_dname_release((*node)->owner);
+	//}
 
 	// check nodes referencing this node and fix the references
 
@@ -757,7 +773,7 @@ int dnslib_node_compare(dnslib_node_t *node1, dnslib_node_t *node2)
 
 /*----------------------------------------------------------------------------*/
 
-int dnslib_node_deep_copy(const dnslib_node_t *from, dnslib_node_t **to)
+int dnslib_node_shallow_copy(const dnslib_node_t *from, dnslib_node_t **to)
 {
 	// create new node
 	*to = dnslib_node_new(from->owner, from->parent, from->flags);
@@ -765,17 +781,10 @@ int dnslib_node_deep_copy(const dnslib_node_t *from, dnslib_node_t **to)
 		return DNSLIB_ENOMEM;
 	}
 
-	// copy references
-	
+	// copy references	
 	// do not use the API function to set parent, so that children count 
 	// is not changed
-	(*to)->parent = from->parent;
-	(*to)->nsec3_node = from->nsec3_node;
-	(*to)->nsec3_referer = from->nsec3_referer;
-	(*to)->wildcard_child = from->wildcard_child;
-	(*to)->prev = from->prev;
-	(*to)->next = from->next;
-	(*to)->children = from->children;
+	memcpy(*to, from, sizeof(dnslib_node_t));
 
 	// copy RRSets
 	// copy the skip list with the old references
@@ -786,7 +795,5 @@ int dnslib_node_deep_copy(const dnslib_node_t *from, dnslib_node_t **to)
 		return DNSLIB_ENOMEM;
 	}
 
-	(*to)->rrset_count = from->rrset_count;
-
 	return DNSLIB_EOK;
 }
diff --git a/src/dnslib/node.h b/src/dnslib/node.h
index a0c1003fa2e4c509a184487a76febe980c30d3e1..95a8f741a97b09de81ec0d5e6d1c3dab4695c0e2 100644
--- a/src/dnslib/node.h
+++ b/src/dnslib/node.h
@@ -106,6 +106,8 @@ typedef enum {
 /*!
  * \brief Creates and initializes new node structure.
  *
+ * \todo Owner reference counter will be increased.
+ *
  * \param owner Owner of the created node.
  * \param parent Parent of the created node.
  * \param flags Document me.
@@ -272,8 +274,21 @@ void dnslib_node_set_nsec3_node(dnslib_node_t *node, dnslib_node_t *nsec3_node);
  */
 const dnslib_dname_t *dnslib_node_owner(const dnslib_node_t *node);
 
+/*!
+ * \todo Document me.
+ */
 dnslib_dname_t *dnslib_node_get_owner(const dnslib_node_t *node);
 
+/*!
+ * \brief Set node owner to specified dname.
+ *
+ * Previous owner will be replaced if exist.
+ *
+ * \param node Specified node.
+ * \param owner New owner dname.
+ */
+void dnslib_node_set_owner(dnslib_node_t *node, dnslib_dname_t* owner);
+
 /*!
  * \brief Returns the wildcard child of the node.
  *
@@ -396,7 +411,7 @@ void dnslib_node_free(dnslib_node_t **node, int free_owner, int fix_refs);
  */
 int dnslib_node_compare(dnslib_node_t *node1, dnslib_node_t *node2);
 
-int dnslib_node_deep_copy(const dnslib_node_t *from, dnslib_node_t **to);
+int dnslib_node_shallow_copy(const dnslib_node_t *from, dnslib_node_t **to);
 
 #endif /* _KNOT_DNSLIB_NODE_H_ */
 
diff --git a/src/dnslib/packet.c b/src/dnslib/packet.c
index 4fd10ffc96ac09cde7db1bed66f4a2656a48a93a..d7b5aa45bb7863d4cf8d15a6b6c2c9397260fc21 100644
--- a/src/dnslib/packet.c
+++ b/src/dnslib/packet.c
@@ -388,7 +388,7 @@ DEBUG_DNSLIB_PACKET(
 	if (size - *pos < 10) {
 		debug_dnslib_packet("Malformed RR: Not enough data to parse RR"
 		                    " header.\n");
-		dnslib_dname_free(&owner);
+		dnslib_dname_release(owner);
 		return NULL;
 	}
 
@@ -399,8 +399,12 @@ DEBUG_DNSLIB_PACKET(
 	uint32_t ttl = dnslib_wire_read_u32(wire + *pos + 4);
 
 	dnslib_rrset_t *rrset = dnslib_rrset_new(owner, type, rclass, ttl);
+
+	/* Owner is either referenced in rrset or rrset creation failed. */
+	dnslib_dname_release(owner);
+
+	/* Check rrset allocation. */
 	if (rrset == NULL) {
-		dnslib_dname_free(&owner);
 		return NULL;
 	}
 
@@ -415,7 +419,6 @@ DEBUG_DNSLIB_PACKET(
 	if (size - *pos < rdlength) {
 		debug_dnslib_packet("Malformed RR: Not enough data to parse RR"
 		                    " RDATA.\n");
-		//dnslib_dname_free(&rrset->owner);
 		dnslib_rrset_deep_free(&rrset, 1, 1, 0);
 //		free(rrset);
 		return NULL;
@@ -433,7 +436,6 @@ DEBUG_DNSLIB_PACKET(
 	                         dnslib_rrtype_descriptor_by_type(rrset->type));
 	if (rdata == NULL) {
 		debug_dnslib_packet("Malformed RR: Could not parse RDATA.\n");
-		//dnslib_dname_free(&rrset->owner);
 		dnslib_rrset_deep_free(&rrset, 1, 1, 0);
 //		free(rrset);
 		return NULL;
@@ -442,7 +444,6 @@ DEBUG_DNSLIB_PACKET(
 	if (dnslib_rrset_add_rdata(rrset, rdata) != DNSLIB_EOK) {
 		debug_dnslib_packet("Malformed RR: Could not add RDATA to RRSet"
 		                    ".\n");
-		//dnslib_dname_free(&rrset->owner);
 		dnslib_rdata_free(&rdata);
 		dnslib_rrset_deep_free(&rrset, 1, 1, 0);
 //		free(rrset);
@@ -596,7 +597,7 @@ static void dnslib_packet_free_allocated_space(dnslib_packet_t *pkt)
 	debug_dnslib_packet("Freeing additional space in packet.\n");
 	if (pkt->prealloc_type == DNSLIB_PACKET_PREALLOC_NONE) {
 		debug_dnslib_packet("Freeing QNAME.\n");
-		dnslib_dname_free(&pkt->question.qname);
+		dnslib_dname_release(pkt->question.qname);
 	}
 
 	if (pkt->max_an_rrsets > DEFAULT_RRSET_COUNT(ANCOUNT, pkt)) {
diff --git a/src/dnslib/rdata.c b/src/dnslib/rdata.c
index 3e7c678752e35d33a00058363c0965d2aa97760a..04d6dc1c7a060809cac2d27b8eae007be27c4946 100644
--- a/src/dnslib/rdata.c
+++ b/src/dnslib/rdata.c
@@ -201,6 +201,7 @@ int dnslib_rdata_from_wire(dnslib_rdata_t *rdata, const uint8_t *wire,
 			//*pos += dname->size;
 			parsed += pos2 - *pos;
 			*pos = pos2;
+			dname = 0;
 			break;
 		case DNSLIB_RDATA_WF_BYTE:
 //			printf("Next item - byte.\n");
@@ -266,6 +267,7 @@ int dnslib_rdata_from_wire(dnslib_rdata_t *rdata, const uint8_t *wire,
 				//*pos += dname->size;
 				parsed += pos2 - *pos;
 				*pos = pos2;
+				dname = 0;
 				break;
 			default:
 				assert(0);
@@ -317,6 +319,11 @@ int dnslib_rdata_set_item(dnslib_rdata_t *rdata, uint pos,
 	if (pos >= rdata->count) {
 		return DNSLIB_EBADARG;
 	}
+
+	/*! \todo As in set_items() we should increment refcounter for dnames,
+	 *        but we don't know the item type.
+	 */
+
 	rdata->items[pos] = item; // this should copy the union; or use memcpy?
 	return DNSLIB_EOK;
 }
@@ -348,6 +355,10 @@ int dnslib_rdata_set_items(dnslib_rdata_t *rdata,
 	memcpy(rdata->items, items, count * sizeof(dnslib_rdata_item_t));
 	rdata->count = count;
 
+	/*! \todo Cannot determine items type, so the dname
+	 *        refcounters should be increased in caller.
+	 */
+
 	return DNSLIB_EOK;
 }
 
@@ -384,6 +395,9 @@ int dnslib_rdata_item_set_dname(dnslib_rdata_t *rdata, uint pos,
 		return DNSLIB_EBADARG;
 	}
 
+	/* Retain dname. */
+	dnslib_dname_retain(dname);
+
 	rdata->items[pos].dname = dname;
 
 	return DNSLIB_EOK;
@@ -440,9 +454,17 @@ void dnslib_rdata_deep_free(dnslib_rdata_t **rdata, uint type,
 		if (desc->wireformat[i] == DNSLIB_RDATA_WF_COMPRESSED_DNAME
 		    || desc->wireformat[i] == DNSLIB_RDATA_WF_UNCOMPRESSED_DNAME
 		    || desc->wireformat[i] == DNSLIB_RDATA_WF_LITERAL_DNAME ) {
-			if (((*rdata)->items[i].dname != NULL) &&
-			    free_all_dnames) {
-				dnslib_dname_free(&(*rdata)->items[i].dname);
+			if (((*rdata)->items[i].dname != NULL)) {
+				/*! \todo This is hack to prevent memory errors,
+				 *        as the rdata_set_items() cannot determine
+				 *        items type and so cannot increment
+				 *        reference count in case of dname type.
+				 *        Free would then release dnames that
+				 *        aren't referenced by the rdata.
+				 */
+				if (free_all_dnames) {
+					dnslib_dname_release((*rdata)->items[i].dname);
+				}
 			}
 		} else {
 			free((*rdata)->items[i].raw_data);
@@ -578,7 +600,8 @@ void dnslib_rdata_deep_free(dnslib_rdata_t **rdata, uint type,
 
 /*----------------------------------------------------------------------------*/
 
-dnslib_rdata_t *dnslib_rdata_copy(const dnslib_rdata_t *rdata, uint16_t type)
+dnslib_rdata_t *dnslib_rdata_deep_copy(const dnslib_rdata_t *rdata, 
+                                       uint16_t type)
 {
 	dnslib_rdata_t *copy = dnslib_rdata_new();
 	CHECK_ALLOC_LOG(copy, NULL);
@@ -603,7 +626,7 @@ dnslib_rdata_t *dnslib_rdata_copy(const dnslib_rdata_t *rdata, uint16_t type)
 		    || d->wireformat[i] == DNSLIB_RDATA_WF_UNCOMPRESSED_DNAME
 		    || d->wireformat[i] == DNSLIB_RDATA_WF_LITERAL_DNAME) {
 			copy->items[i].dname =
-				dnslib_dname_copy(rdata->items[i].dname);
+				dnslib_dname_deep_copy(rdata->items[i].dname);
 		} else {
 			copy->items[i].raw_data = (uint16_t *)malloc(
 					rdata->items[i].raw_data[0] + 2);
diff --git a/src/dnslib/rdata.h b/src/dnslib/rdata.h
index 0b3051302aa839b70e14077c26f1ea5cb0eed8c2..a679dbc7a4ede18dfa40b5dfa790e1558fcabe5d 100644
--- a/src/dnslib/rdata.h
+++ b/src/dnslib/rdata.h
@@ -213,7 +213,8 @@ int dnslib_rdata_item_set_raw_data(dnslib_rdata_t *rdata, unsigned int pos,
  *
  * \return Copy of \a rdata.
  */
-dnslib_rdata_t *dnslib_rdata_copy(const dnslib_rdata_t *rdata, uint16_t type);
+dnslib_rdata_t *dnslib_rdata_deep_copy(const dnslib_rdata_t *rdata, 
+                                       uint16_t type);
 
 /*!
  * \brief Destroys the RDATA structure without deleting RDATA items.
diff --git a/src/dnslib/rrset.c b/src/dnslib/rrset.c
index 6ba6dfc33509367fa8566b36f1801c524f5bced6..0387b834ed1438dfe4ff3e93036bcf53de2a5cf2 100644
--- a/src/dnslib/rrset.c
+++ b/src/dnslib/rrset.c
@@ -54,6 +54,9 @@ dnslib_rrset_t *dnslib_rrset_new(dnslib_dname_t *owner, uint16_t type,
 
 	ret->rdata = NULL;
 
+	/* Retain reference to owner. */
+	dnslib_dname_retain(owner);
+
 	ret->owner = owner;
 	ret->type = type;
 	ret->rclass = rclass;
@@ -179,6 +182,18 @@ dnslib_dname_t *dnslib_rrset_get_owner(const dnslib_rrset_t *rrset)
 
 /*----------------------------------------------------------------------------*/
 
+void dnslib_rrset_set_owner(dnslib_rrset_t *rrset, dnslib_dname_t* owner)
+{
+	if (rrset) {
+		/* Retain new owner and release old owner. */
+		dnslib_dname_retain(owner);
+		dnslib_dname_release(rrset->owner);
+		rrset->owner = owner;
+	}
+}
+
+/*----------------------------------------------------------------------------*/
+
 uint16_t dnslib_rrset_type(const dnslib_rrset_t *rrset)
 {
 	return rrset->type;
@@ -294,10 +309,14 @@ int dnslib_rrset_compare(const dnslib_rrset_t *r1,
 
 /*----------------------------------------------------------------------------*/
 
-int dnslib_rrset_copy(const dnslib_rrset_t *from, dnslib_rrset_t **to)
+int dnslib_rrset_shallow_copy(const dnslib_rrset_t *from, dnslib_rrset_t **to)
 {
-	/*! \todo Implement (shallow copy). */
-	return DNSLIB_ERROR;
+	*to = (dnslib_rrset_t *)malloc(sizeof(dnslib_rrset_t));
+	CHECK_ALLOC_LOG(*to, DNSLIB_ENOMEM);
+	
+	memcpy(*to, from, sizeof(dnslib_rrset_t));
+	
+	return DNSLIB_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -308,6 +327,9 @@ void dnslib_rrset_free(dnslib_rrset_t **rrset)
 		return;
 	}
 
+	/*! \todo Shouldn't we always release owner reference? */
+	dnslib_dname_release((*rrset)->owner);
+
 	free(*rrset);
 	*rrset = NULL;
 }
@@ -345,9 +367,10 @@ void dnslib_rrset_deep_free(dnslib_rrset_t **rrset, int free_owner,
 		                       free_rdata_dnames);
 	}
 
-	if (free_owner) {
-		dnslib_dname_free(&(*rrset)->owner);
-	}
+	/*! \todo Release owner every time? */
+	//if (free_owner) {
+		dnslib_dname_release((*rrset)->owner);
+	//}
 
 	free(*rrset);
 	*rrset = NULL;
diff --git a/src/dnslib/rrset.h b/src/dnslib/rrset.h
index dc93455f81bf8d850dfce7f1a886ae4060df1bec..ec456d82ecb07869fbe09a8941295cd605108b55 100644
--- a/src/dnslib/rrset.h
+++ b/src/dnslib/rrset.h
@@ -116,8 +116,21 @@ int dnslib_rrset_add_rrsigs(dnslib_rrset_t *rrset, dnslib_rrset_t *rrsigs,
  */
 const dnslib_dname_t *dnslib_rrset_owner(const dnslib_rrset_t *rrset);
 
+/*!
+ * \todo Document me.
+ */
 dnslib_dname_t *dnslib_rrset_get_owner(const dnslib_rrset_t *rrset);
 
+/*!
+ * \brief Set rrset owner to specified dname.
+ *
+ * Previous owner will be replaced if exist.
+ *
+ * \param rrset Specified RRSet.
+ * \param owner New owner dname.
+ */
+void dnslib_rrset_set_owner(dnslib_rrset_t *rrset, dnslib_dname_t* owner);
+
 /*!
  * \brief Returns the TYPE of the RRSet.
  *
@@ -198,7 +211,7 @@ int dnslib_rrset_compare(const dnslib_rrset_t *r1,
                          const dnslib_rrset_t *r2,
                          dnslib_rrset_compare_type_t cmp);
 
-int dnslib_rrset_copy(const dnslib_rrset_t *from, dnslib_rrset_t **to);
+int dnslib_rrset_shallow_copy(const dnslib_rrset_t *from, dnslib_rrset_t **to);
 
 /*!
  * \brief Destroys the RRSet structure.
diff --git a/src/dnslib/tests/realdata/dnslib/response_tests_realdata.c b/src/dnslib/tests/realdata/dnslib/response_tests_realdata.c
index 5ba6c46361c4fd6a3c425793a451090f48b295f3..dfc982db1c8d339f68b9239c1d231c2a2f469f9f 100644
--- a/src/dnslib/tests/realdata/dnslib/response_tests_realdata.c
+++ b/src/dnslib/tests/realdata/dnslib/response_tests_realdata.c
@@ -919,8 +919,6 @@ int dnslib_response_tests_count(int argc, char *argv[])
 
 int dnslib_response_tests_run(int argc, char *argv[])
 {
-	int ret;
-
 	test_data_t *data = data_for_dnslib_tests;
 
 	ok(test_response_add_rrset_answer(data->rrset_list),
diff --git a/src/dnslib/zone-contents.c b/src/dnslib/zone-contents.c
index c51296ccb56f6ad8b940ea0a8bba3835a9846730..d72700173455b38365e77d95e83c9219fbdbcbec 100644
--- a/src/dnslib/zone-contents.c
+++ b/src/dnslib/zone-contents.c
@@ -562,6 +562,7 @@ DEBUG_DNSLIB_ZONE(
 
 	debug_dnslib_zone("Base32-encoded hash: %s\n", name_b32);
 
+	/* Will be returned to caller, make sure it is released after use. */
 	*nsec3_name = dnslib_dname_new_from_str(name_b32, size, NULL);
 
 	free(name_b32);
@@ -578,7 +579,7 @@ DEBUG_DNSLIB_ZONE(
 	if (ret == NULL) {
 		debug_dnslib_zone("Error while creating NSEC3 domain name for "
 		                  "hashed name.\n");
-		dnslib_dname_free(nsec3_name);
+		dnslib_dname_release(*nsec3_name);
 		return DNSLIB_ERROR;
 	}
 
@@ -722,8 +723,7 @@ static int dnslib_zone_contents_dnames_from_rrset_to_table(
 
 	if (replace_owner) {
 		// discard the old owner and replace it with the new
-		//dnslib_dname_free(&rrset->owner);
-		rrset->owner = owner;
+		dnslib_rrset_set_owner(rrset, owner);
 	}
 	debug_dnslib_zone("RRSet owner: %p\n", rrset->owner);
 
@@ -994,6 +994,7 @@ DEBUG_DNSLIB_ZONE(
 			debug_dnslib_zone("Creating new node.\n");
 			next_node = dnslib_node_new(chopped, NULL, flags);
 			if (next_node == NULL) {
+				/* Directly discard. */
 				dnslib_dname_free(&chopped);
 				return DNSLIB_ENOMEM;
 			}
@@ -1002,6 +1003,8 @@ DEBUG_DNSLIB_ZONE(
 				 dnslib_zone_contents_dnames_from_node_to_table(
 					zone->dname_table, next_node);
 				if (ret != DNSLIB_EOK) {
+					/*! \todo Will next_node leak? */
+					dnslib_dname_release(chopped);
 					return ret;
 				}
 			}
@@ -1024,7 +1027,8 @@ DEBUG_DNSLIB_ZONE(
 				debug_dnslib_zone("Failed to insert new node "
 				                  "to zone tree.\n");
 				/*! \todo Delete the node?? */
-				dnslib_dname_free(&chopped);
+				/* Directly discard. */
+				dnslib_dname_release(chopped);
 				return ret;
 			}
 
@@ -1046,7 +1050,8 @@ DEBUG_DNSLIB_ZONE(
 				debug_dnslib_zone("Error inserting node into "
 				                  "hash table!\n");
 				/*! \todo Delete the node?? */
-				dnslib_dname_free(&chopped);
+				/* Directly discard. */
+				dnslib_dname_release(chopped);
 				return DNSLIB_EHASH;
 			}
 
@@ -1064,7 +1069,14 @@ DEBUG_DNSLIB_ZONE(
 #endif
 			debug_dnslib_zone("Next parent.\n");
 			node = next_node;
+			dnslib_dname_t *chopped_last = chopped;
 			chopped = dnslib_dname_left_chop(chopped);
+
+			/* Release last chop, reference is already stored
+			 * in next_node.
+			 */
+			dnslib_dname_release(chopped_last);
+
 		}
 		// set the found parent (in the zone) as the parent of the last
 		// inserted node
@@ -1073,7 +1085,10 @@ DEBUG_DNSLIB_ZONE(
 
 		debug_dnslib_zone("Created all parents.\n");
 	}
-	dnslib_dname_free(&chopped);
+
+	/* Directly discard. */
+	/*! \todo This may be double-release. */
+	dnslib_dname_release(chopped);
 
 	return DNSLIB_EOK;
 }
@@ -1139,8 +1154,7 @@ int dnslib_zone_contents_add_rrset(dnslib_zone_contents_t *zone,
 	// table)
 	/*! \todo Do even if domain table is not used?? */
 	if (ret == DNSLIB_EOK && rrset->owner != (*node)->owner) {
-		dnslib_dname_free(&rrset->owner);
-		rrset->owner = (*node)->owner;
+		dnslib_rrset_set_owner(rrset, (*node)->owner);
 	}
 
 	debug_dnslib_zone("RRSet OK.\n");
@@ -1246,8 +1260,7 @@ int dnslib_zone_contents_add_rrsigs(dnslib_zone_contents_t *zone,
 	// replace RRSet's owner with the node's owner (that is already in the
 	// table)
 	if ((*rrset)->owner != (*rrset)->rrsigs->owner) {
-		dnslib_dname_free(&rrsigs->owner);
-		(*rrset)->rrsigs->owner = (*rrset)->owner;
+		dnslib_rrset_set_owner((*rrset)->rrsigs, (*rrset)->owner);
 	}
 
 	debug_dnslib_zone("RRSIGs OK\n");
@@ -1359,8 +1372,7 @@ int dnslib_zone_contents_add_nsec3_rrset(dnslib_zone_contents_t *zone,
 	// table)
 	/*! \todo Do even if domain table is not used? */
 	if (rrset->owner != (*node)->owner) {
-		dnslib_dname_free(&rrset->owner);
-		rrset->owner = (*node)->owner;
+		dnslib_rrset_set_owner(rrset, (*node)->owner);
 	}
 
 	debug_dnslib_zone("NSEC3 OK\n");
@@ -1725,7 +1737,8 @@ DEBUG_DNSLIB_ZONE(
 
 	// chop leftmost labels until some node is found
 	// copy the name for chopping
-	dnslib_dname_t *name_copy = dnslib_dname_copy(name);
+	/* Local allocation, will be discarded. */
+	dnslib_dname_t *name_copy = dnslib_dname_deep_copy(name);
 DEBUG_DNSLIB_ZONE(
 	char *n = dnslib_dname_to_str(name_copy);
 	debug_dnslib_zone("Finding closest encloser..\nStarting with: %s\n", n);
@@ -1749,6 +1762,7 @@ DEBUG_DNSLIB_ZONE(
 		                    name_copy->size);
 	}
 
+	/* Directly discard. */
 	dnslib_dname_free(&name_copy);
 
 	assert(item != NULL);
@@ -1797,7 +1811,7 @@ DEBUG_DNSLIB_ZONE(
 		zone->nsec3_nodes, nsec3_name, &found, &prev);
 	assert(exact_match >= 0);
 
-	dnslib_dname_free(&nsec3_name);
+	dnslib_dname_release(nsec3_name);
 
 DEBUG_DNSLIB_ZONE(
 	if (found) {
@@ -2142,7 +2156,7 @@ int dnslib_zone_contents_shallow_copy(const dnslib_zone_contents_t *from,
 			ret = DNSLIB_ENOMEM;
 			goto cleanup;
 		}
-		if ((ret = dnslib_dname_table_copy(from->dname_table,
+		if ((ret = dnslib_dname_table_shallow_copy(from->dname_table,
 		                        contents->dname_table)) != DNSLIB_EOK) {
 			goto cleanup;
 		}
@@ -2157,9 +2171,9 @@ int dnslib_zone_contents_shallow_copy(const dnslib_zone_contents_t *from,
 	memcpy(&contents->nsec3_params, &from->nsec3_params,
 	       sizeof(dnslib_nsec3_params_t));
 
-	if ((ret = dnslib_zone_tree_copy(from->nodes,
+	if ((ret = dnslib_zone_tree_shallow_copy(from->nodes,
 	                                 contents->nodes)) != DNSLIB_EOK
-	    || (ret = dnslib_zone_tree_copy(from->nsec3_nodes,
+	    || (ret = dnslib_zone_tree_shallow_copy(from->nsec3_nodes,
 	                                contents->nsec3_nodes)) != DNSLIB_EOK) {
 		goto cleanup;
 	}
@@ -2230,7 +2244,8 @@ void dnslib_zone_contents_deep_free(dnslib_zone_contents_t **contents)
 
 		dnslib_zone_tree_reverse_apply_postorder(
 			(*contents)->nsec3_nodes,
-			dnslib_zone_contents_destroy_node_rrsets_from_tree, 0);
+			dnslib_zone_contents_destroy_node_rrsets_from_tree,
+			(void*)1);
 
 		dnslib_zone_tree_reverse_apply_postorder(
 			(*contents)->nsec3_nodes,
@@ -2238,7 +2253,8 @@ void dnslib_zone_contents_deep_free(dnslib_zone_contents_t **contents)
 
 		dnslib_zone_tree_reverse_apply_postorder(
 			(*contents)->nodes,
-			dnslib_zone_contents_destroy_node_rrsets_from_tree, 0);
+			dnslib_zone_contents_destroy_node_rrsets_from_tree,
+			(void*)1);
 
 		dnslib_zone_tree_reverse_apply_postorder(
 			(*contents)->nodes,
diff --git a/src/dnslib/zone-dump.c b/src/dnslib/zone-dump.c
index 8b83ea8d3ad2171dc33f368847e3af35355326f5..6cc4fbc8ebc988bbb193a705aa103265a381fb61 100644
--- a/src/dnslib/zone-dump.c
+++ b/src/dnslib/zone-dump.c
@@ -909,6 +909,7 @@ static int check_nsec3_node_in_zone(dnslib_zone_contents_t *zone, dnslib_node_t
 	memmove(next_dname_decoded + 1, next_dname_decoded, real_size);
 	next_dname_decoded[0] = real_size;
 
+	/* Local allocation, will be discarded. */
 	dnslib_dname_t *next_dname =
 		dnslib_dname_new_from_wire(next_dname_decoded,
 					   real_size + 1, NULL);
@@ -927,6 +928,8 @@ static int check_nsec3_node_in_zone(dnslib_zone_contents_t *zone, dnslib_node_t
 		err_handler_handle_error(handler, node,
 					 ZC_ERR_NSEC3_RDATA_CHAIN);
 	}
+
+	/* Directly discard. */
 	dnslib_dname_free(&next_dname);
 
 	/* This is probably not sufficient, but again, it is covered in
@@ -1713,6 +1716,7 @@ static void log_cyclic_errors_in_zone(err_handler_t *handler,
 		memmove(next_dname_decoded + 1, next_dname_decoded, real_size);
 		next_dname_decoded[0] = real_size;
 
+		/* Local allocation, will be discarded. */
 		dnslib_dname_t *next_dname =
 			dnslib_dname_new_from_wire(next_dname_decoded,
 						   real_size + 1, NULL);
@@ -1725,6 +1729,7 @@ static void log_cyclic_errors_in_zone(err_handler_t *handler,
 
 		free(next_dname_decoded);
 
+		/*! \todo Free result and dname! */
 		if (dnslib_dname_cat(next_dname,
 			     dnslib_node_owner(dnslib_zone_contents_apex(zone))) ==
 		                NULL) {
@@ -1747,6 +1752,7 @@ static void log_cyclic_errors_in_zone(err_handler_t *handler,
 						 ZC_ERR_NSEC3_RDATA_CHAIN);
 		}
 
+		/* Directly discard. */
 		dnslib_dname_free(&next_dname);
 
 	} else if (do_checks == 2 ) {
diff --git a/src/dnslib/zone-load.c b/src/dnslib/zone-load.c
index 01393f008973b50cbb674455fa42b1165fdb64ed..faed45ba6e6149a75f6f20ee3c34c206439ee28f 100644
--- a/src/dnslib/zone-load.c
+++ b/src/dnslib/zone-load.c
@@ -117,8 +117,25 @@ static void load_rdata_purge(dnslib_rdata_t *rdata,
 			     int count,
 			     uint16_t type)
 {
+	/* Increase refcount manually, as the set_items() doesn't see the dname
+	 * type and thus is unable to increment refcounter.
+	 */
+	unsigned i = 0;
+	switch(type) {
+	case DNSLIB_RDATA_WF_COMPRESSED_DNAME:
+	case DNSLIB_RDATA_WF_UNCOMPRESSED_DNAME:
+	case DNSLIB_RDATA_WF_LITERAL_DNAME:
+		for (i = 0; i < count; ++i) {
+			dnslib_dname_retain(items[i].dname);
+		}
+		break;
+	default:
+		break;
+	}
+
+	/* Copy items to rdata and free the temporary rdata. */
 	dnslib_rdata_set_items(rdata, items, count);
-	dnslib_rdata_deep_free(&rdata, type, 0);
+	dnslib_rdata_deep_free(&rdata, type, 1);
 	free(items);
 }
 
@@ -130,7 +147,7 @@ static dnslib_dname_t *read_dname_with_id(FILE *f)
 	/* Read ID. */
 	uint32_t dname_id = 0;
 	if (!fread_wrapper(&dname_id, sizeof(dname_id), 1, f)) {
-		dnslib_dname_free(&ret);
+		dnslib_dname_release(ret);
 		return NULL;
 	}
 
@@ -139,7 +156,7 @@ static dnslib_dname_t *read_dname_with_id(FILE *f)
 	/* Read size of dname. */
 	uint32_t dname_size = 0;
 	if (!fread_wrapper(&dname_size, sizeof(dname_size), 1, f)) {
-		dnslib_dname_free(&ret);
+		dnslib_dname_release(ret);
 		return NULL;
 	}
 	ret->size = dname_size;
@@ -150,19 +167,19 @@ static dnslib_dname_t *read_dname_with_id(FILE *f)
 	ret->name = malloc(sizeof(uint8_t) * ret->size);
 	if (ret->name == NULL) {
 		ERR_ALLOC_FAILED;
-		dnslib_dname_free(&ret);
+		dnslib_dname_release(ret);
 		return NULL;
 	}
 
 	if (!fread_wrapper(ret->name, sizeof(uint8_t), ret->size, f)) {
-		dnslib_dname_free(&ret);
+		dnslib_dname_release(ret);
 		return NULL;
 	}
 
 	/* Read labels. */
 	uint16_t label_count = 0;
 	if (!fread_wrapper(&label_count, sizeof(label_count), 1, f)) {
-		dnslib_dname_free(&ret);
+		dnslib_dname_release(ret);
 		return NULL;
 	}
 
@@ -171,7 +188,7 @@ static dnslib_dname_t *read_dname_with_id(FILE *f)
 	ret->labels = malloc(sizeof(uint8_t) * ret->label_count);
 	if (ret->labels == NULL) {
 		ERR_ALLOC_FAILED;
-		dnslib_dname_free(&ret);
+		dnslib_dname_release(ret);
 		return NULL;
 	}
 
@@ -230,6 +247,9 @@ static dnslib_rdata_t *dnslib_load_rdata(uint16_t type, FILE *f,
 					load_rdata_purge(rdata, items, i, type);
 					return NULL;
 				}
+
+				/* Store reference do dname. */
+				dnslib_dname_retain(id_array[dname_id]);
 				items[i].dname = id_array[dname_id];
 			} else {
 				items[i].dname = read_dname_with_id(f);
@@ -284,6 +304,7 @@ static dnslib_rdata_t *dnslib_load_rdata(uint16_t type, FILE *f,
 		}
 	}
 
+	/* Each item has refcount already incremented for saving in rdata. */
 	if (dnslib_rdata_set_items(rdata, items, desc->length) != 0) {
 		fprintf(stderr, "zoneload: Could not set items "
 			"when loading rdata.\n");
@@ -398,6 +419,12 @@ static dnslib_rrset_t *dnslib_load_rrset(FILE *f, dnslib_dname_t **id_array,
 
 	rrset = dnslib_rrset_new(owner, rrset_type, rrset_class, rrset_ttl);
 
+	if (!use_ids) {
+		/* Directly release if allocated locally. */
+		dnslib_dname_release(owner);
+		owner = 0;
+	}
+
 	debug_dnslib_zload("RRSet type: %d\n", rrset->type);
 
 	dnslib_rdata_t *tmp_rdata;
@@ -418,11 +445,11 @@ static dnslib_rrset_t *dnslib_load_rrset(FILE *f, dnslib_dname_t **id_array,
 	if (rrsig_count) {
 		tmp_rrsig = dnslib_load_rrsig(f, id_array, use_ids);
 		if (!use_ids) {
-			tmp_rrsig->owner = rrset->owner;
+			dnslib_rrset_set_owner(tmp_rrsig, rrset->owner);
 		}
 	}
 
-	rrset->rrsigs = tmp_rrsig;
+	dnslib_rrset_set_rrsigs(rrset, tmp_rrsig);
 
 	return rrset;
 }
@@ -483,7 +510,9 @@ static dnslib_node_t *dnslib_load_node(FILE *f, dnslib_dname_t **id_array)
 		dnslib_node_set_nsec3_node(node, NULL);
 //		node->nsec3_node = NULL;
 	}
-	node->owner = owner;
+
+	/* Retain new owner while releasing replaced owner. */
+	dnslib_node_set_owner(node, owner);
 	node->flags = flags;
 
 	//XXX will have to be set already...canonical order should do it
@@ -504,9 +533,10 @@ static dnslib_node_t *dnslib_load_node(FILE *f, dnslib_dname_t **id_array)
 			fprintf(stderr, "zone: Could not load rrset.\n");
 			return NULL;
 		}
-		tmp_rrset->owner = node->owner;
+		/* Retain new owner while releasing replaced owner. */
+		dnslib_rrset_set_owner(tmp_rrset, node->owner);
 		if (tmp_rrset->rrsigs != NULL) {
-			tmp_rrset->rrsigs->owner = node->owner;
+			dnslib_rrset_set_owner(tmp_rrset->rrsigs, node->owner);
 		}
 		if (dnslib_node_add_rrset(node, tmp_rrset, 0) < 0) {
 			fprintf(stderr, "zone: Could not add rrset.\n");
@@ -538,6 +568,7 @@ static void find_and_set_wildcard_child(dnslib_zone_contents_t *zone,
 			dnslib_zone_contents_get_nsec3_node(zone, chopped);
 	}
 
+	/* Directly discard. */
 	dnslib_dname_free(&chopped);
 
 	assert(wildcard_parent); /* it *has* to be there */
@@ -744,7 +775,7 @@ static void cleanup_id_array(dnslib_dname_t **id_array,
 			     const uint from, const uint to)
 {
 	for (uint i = from; i < to; i++) {
-		dnslib_dname_free(&(id_array[i]));
+		dnslib_dname_release(id_array[i]);
 	}
 
 	free(id_array);
@@ -831,19 +862,30 @@ static dnslib_dname_t **create_dname_array(FILE *f, uint max_id)
 			cleanup_id_array(array, 0, i);
 			return NULL;
 		}
+
 		if (read_dname->id < max_id) {
+
+			/* Create new node from dname. */
 			read_dname->node = dnslib_node_new(read_dname, NULL, 0);
+
 			if (read_dname->node == NULL) {
 				ERR_ALLOC_FAILED;
+
+				/* Release read dname. */
+				dnslib_dname_release(read_dname);
 				cleanup_id_array(array, 0, i);
-				dnslib_dname_free(&read_dname);
 				return NULL;
 			}
+
+			/* Store reference to dname in array. */
 			array[read_dname->id] = read_dname;
 		} else {
+			/* Release read dname. */
+			dnslib_dname_release(read_dname);
 			cleanup_id_array(array, 0, i);
 			return NULL;
 		}
+
 //		assert(array[i]->id == i);
 	}
 
@@ -1027,6 +1069,10 @@ dnslib_zone_t *dnslib_zload_load(zloader_t *loader)
 	}
 
 	/* ID array is now useless */
+	for (uint i = 1; i < total_dnames; i++) {
+		/* Added to table, may discard now. */
+		dnslib_dname_release(id_array[i]);
+	}
 	free(id_array);
 
 
diff --git a/src/dnslib/zone-tree.c b/src/dnslib/zone-tree.c
index 28d80b6f47884bc4f886c80c247c37dfd4f013b1..2718634635cf2e29f1b85e4aa6d89e4d0acc748c 100644
--- a/src/dnslib/zone-tree.c
+++ b/src/dnslib/zone-tree.c
@@ -398,7 +398,8 @@ int dnslib_zone_tree_reverse_apply_postorder(dnslib_zone_tree_t *tree,
 
 /*----------------------------------------------------------------------------*/
 
-int dnslib_zone_tree_copy(dnslib_zone_tree_t *from, dnslib_zone_tree_t *to)
+int dnslib_zone_tree_shallow_copy(dnslib_zone_tree_t *from, 
+                                  dnslib_zone_tree_t *to)
 {
 	/*
 	 * This function will copy the tree by hand, so that the nodes
diff --git a/src/dnslib/zone-tree.h b/src/dnslib/zone-tree.h
index aaa8c93064484d397260f747a72b42fe8c4795b3..1dade6acc5b5b07c9a2919cc8628eb95b329d1f9 100644
--- a/src/dnslib/zone-tree.h
+++ b/src/dnslib/zone-tree.h
@@ -256,7 +256,8 @@ int dnslib_zone_tree_reverse_apply_postorder(dnslib_zone_tree_t *tree,
  * \retval DNSLIB_EOK
  * \retval DNSLIB_ENOMEM
  */
-int dnslib_zone_tree_copy(dnslib_zone_tree_t *from, dnslib_zone_tree_t *to);
+int dnslib_zone_tree_shallow_copy(dnslib_zone_tree_t *from, 
+                                  dnslib_zone_tree_t *to);
 
 /*!
  * \brief Destroys the zone tree, not touching the saved data.
diff --git a/src/dnslib/zone.c b/src/dnslib/zone.c
index eb37ae71a96748225080a6827fbf6f29422e9a18..c6ab358d26eaa250520d80fe3c275f721dd3d9ed 100644
--- a/src/dnslib/zone.c
+++ b/src/dnslib/zone.c
@@ -40,7 +40,7 @@ dnslib_zone_t *dnslib_zone_new(dnslib_node_t *apex, uint node_count,
 
 	// save the zone name
 	debug_dnslib_zone("Copying zone name.\n");
-	zone->name = dnslib_dname_copy(dnslib_node_owner(apex));
+	zone->name = dnslib_dname_deep_copy(dnslib_node_owner(apex));
 	if (zone->name == NULL) {
 		ERR_ALLOC_FAILED;
 		free(zone);
@@ -51,7 +51,7 @@ dnslib_zone_t *dnslib_zone_new(dnslib_node_t *apex, uint node_count,
 	zone->contents = dnslib_zone_contents_new(apex, node_count,
 	                                          use_domain_table, zone);
 	if (zone->contents == NULL) {
-		dnslib_dname_free(&zone->name);
+		dnslib_dname_release(zone->name);
 		free(zone);
 		return NULL;
 	}
@@ -331,7 +331,7 @@ void dnslib_zone_free(dnslib_zone_t **zone)
 		                  "update.\n");
 	}
 
-	dnslib_dname_free(&(*zone)->name);
+	dnslib_dname_release((*zone)->name);
 
 	/* Call zone data destructor if exists. */
 	if ((*zone)->dtor) {
@@ -365,7 +365,7 @@ DEBUG_DNSLIB_ZONE(
 	free(name);
 );
 
-	dnslib_dname_free(&(*zone)->name);
+	dnslib_dname_release((*zone)->name);
 
 	/* Call zone data destructor if exists. */
 	if ((*zone)->dtor) {
diff --git a/src/dnslib/zonedb.c b/src/dnslib/zonedb.c
index c20a1e166cbe5868fd00fdb2be1833adcea296ec..501ca07f15281ccea42e1b7525cac38412ac7603 100644
--- a/src/dnslib/zonedb.c
+++ b/src/dnslib/zonedb.c
@@ -210,23 +210,6 @@ DEBUG_DNSLIB_ZONEDB(
 
 /*----------------------------------------------------------------------------*/
 
-dnslib_zonedb_t *dnslib_zonedb_copy(const dnslib_zonedb_t *db)
-{
-	dnslib_zonedb_t *db_new =
-		(dnslib_zonedb_t *)malloc(sizeof(dnslib_zonedb_t));
-	CHECK_ALLOC_LOG(db_new, NULL);
-
-	db_new->zones = skip_copy_list(db->zones);
-	if (db_new->zones == NULL) {
-		free(db_new);
-		return NULL;
-	}
-
-	return db_new;
-}
-
-/*----------------------------------------------------------------------------*/
-
 void dnslib_zonedb_free(dnslib_zonedb_t **db)
 {
 	skip_destroy_list(&(*db)->zones, NULL, NULL);
diff --git a/src/dnslib/zonedb.h b/src/dnslib/zonedb.h
index fe374e55e496f554a8a4dfdb68ae3c6e48036671..45e811f45ba2d4c0f5147f4fee17ed82694ec476 100644
--- a/src/dnslib/zonedb.h
+++ b/src/dnslib/zonedb.h
@@ -97,16 +97,6 @@ dnslib_zone_t *dnslib_zonedb_find_zone(const dnslib_zonedb_t *db,
 const dnslib_zone_t *dnslib_zonedb_find_zone_for_name(dnslib_zonedb_t *db,
                                                    const dnslib_dname_t *dname);
 
-/*!
- * \brief Copies the zone database structure (but not the zones within).
- *
- * \param db Zone database to copy.
- *
- * \return A new zone database structure containing the same zones as \a db or
- *         NULL if an error occured.
- */
-dnslib_zonedb_t *dnslib_zonedb_copy(const dnslib_zonedb_t *db);
-
 /*!
  * \brief Destroys and deallocates the zone database structure (but not the
  *        zones within).
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
index f8a63fd3d7172d2d7018f09fa774744134a8fde7..adb8f551b743ebbc7d3fe9a5269be67654a706e2 100644
--- a/src/knot/conf/cf-parse.y
+++ b/src/knot/conf/cf-parse.y
@@ -267,7 +267,7 @@ zone_start: TEXT {
      strcat(this_zone->name, ".");
    }
 
-   // Check domain name
+   /* Check domain name. */
    dnslib_dname_t *dn = dnslib_dname_new_from_str(this_zone->name,
                                                   nlen + 1,
                                                   0);
@@ -276,6 +276,7 @@ zone_start: TEXT {
      free(this_zone);
      cf_error("invalid zone origin", scanner);
    } else {
+     /* Directly discard dname, won't be needed. */
      dnslib_dname_free(&dn);
      add_tail(&new_config->zones, &this_zone->n);
      ++new_config->zones_count;
diff --git a/src/knot/server/name-server.c b/src/knot/server/name-server.c
index edfd753e62649d08b6c80131948ea6e17a8509cf..2e4ab3a8b7f316831c320269bd3f226310524289 100644
--- a/src/knot/server/name-server.c
+++ b/src/knot/server/name-server.c
@@ -82,12 +82,10 @@ static const dnslib_zone_t *ns_get_zone_for_qname(dnslib_zonedb_t *zdb,
 	 * records are only present in a parent zone.
 	 */
 	if (qtype == DNSLIB_RRTYPE_DS) {
-		/*
-		 * TODO: optimize!!!
-		 *  1) do not copy the name!
-		 */
+		/*! \todo Optimize, do not deep copy dname. */
 		dnslib_dname_t *name = dnslib_dname_left_chop(qname);
 		zone = dnslib_zonedb_find_zone_for_name(zdb, name);
+		/* Directly discard. */
 		dnslib_dname_free(&name);
 	} else {
 		zone = dnslib_zonedb_find_zone_for_name(zdb, qname);
@@ -114,7 +112,7 @@ static dnslib_rrset_t *ns_synth_from_wildcard(
 {
 	debug_ns("Synthetizing RRSet from wildcard...\n");
 
-	dnslib_dname_t *owner = dnslib_dname_copy(qname);
+	dnslib_dname_t *owner = dnslib_dname_deep_copy(qname);
 //	printf("Copied owner ptr: %p\n", owner);
 
 	dnslib_rrset_t *synth_rrset = dnslib_rrset_new(
@@ -122,8 +120,10 @@ static dnslib_rrset_t *ns_synth_from_wildcard(
 			dnslib_rrset_class(wildcard_rrset),
 			dnslib_rrset_ttl(wildcard_rrset));
 
+	/* Release owner, as it's retained in rrset. */
+	dnslib_dname_release(owner);
+
 	if (synth_rrset == NULL) {
-		dnslib_dname_free(&owner);
 		return NULL;
 	}
 
@@ -136,7 +136,7 @@ static dnslib_rrset_t *ns_synth_from_wildcard(
 		// we could use the RDATA from the wildcard rrset
 		// but there is no way to distinguish it when deleting
 		// temporary RRSets
-		dnslib_rdata_t *rdata_copy = dnslib_rdata_copy(rdata,
+		dnslib_rdata_t *rdata_copy = dnslib_rdata_deep_copy(rdata,
 		                                dnslib_rrset_type(synth_rrset));
 		if (rdata_copy == NULL) {
 			dnslib_rrset_deep_free(&synth_rrset, 1, 1, 0);
@@ -634,7 +634,7 @@ static dnslib_dname_t *ns_next_closer(const dnslib_dname_t *closest_encloser,
 	       == ce_labels);
 
 	// chop some labels from the qname
-	dnslib_dname_t *next_closer = dnslib_dname_copy(name);
+	dnslib_dname_t *next_closer = dnslib_dname_deep_copy(name);
 	if (next_closer == NULL) {
 		return NULL;
 	}
@@ -812,7 +812,7 @@ DEBUG_NS(
 		ret = ns_put_covering_nsec3(zone, next_closer, resp);
 
 		// the cast is ugly, but no better way around it
-		dnslib_dname_free((dnslib_dname_t **)&next_closer);
+		dnslib_dname_release((dnslib_dname_t *)next_closer);
 	} else {
 		ret = ns_put_covering_nsec3(zone, next_closer, resp);
 	}
@@ -838,6 +838,7 @@ static dnslib_dname_t *ns_wildcard_child_name(const dnslib_dname_t *name)
 	}
 
 	if (dnslib_dname_cat(wildcard, name) == NULL) {
+		/* Directly discard dname. */
 		dnslib_dname_free(&wildcard);
 		return NULL;
 	}
@@ -879,6 +880,8 @@ static int ns_put_nsec3_no_wildcard_child(const dnslib_zone_contents_t *zone,
 		ret = NS_ERR_SERVFAIL;
 	} else {
 		ret = ns_put_covering_nsec3(zone, wildcard, resp);
+
+		/* Directly discard wildcard. */
 		dnslib_dname_free(&wildcard);
 	}
 
@@ -991,6 +994,7 @@ static int ns_put_nsec_nxdomain(const dnslib_dname_t *qname,
 	debug_ns("Previous node: %s\n",
 	    dnslib_dname_to_str(dnslib_node_owner(prev_new)));
 
+	/* Directly discard dname. */
 	dnslib_dname_free(&wildcard);
 
 	if (prev_new != previous) {
@@ -1123,8 +1127,9 @@ DEBUG_NS(
 );
 	int ret = ns_put_covering_nsec3(zone, next_closer, resp);
 
-	// the cast is ugly, but no better way around it
-	dnslib_dname_free(&next_closer);
+
+	/* Duplicate from ns_next_close(), safe to discard. */
+	dnslib_dname_release(next_closer);
 
 	return ret;
 }
@@ -1414,7 +1419,7 @@ static dnslib_rrset_t *ns_cname_from_dname(const dnslib_rrset_t *dname_rrset,
 
 	// create new CNAME RRSet
 
-	dnslib_dname_t *owner = dnslib_dname_copy(qname);
+	dnslib_dname_t *owner = dnslib_dname_deep_copy(qname);
 	if (owner == NULL) {
 		return NULL;
 	}
@@ -1422,8 +1427,10 @@ static dnslib_rrset_t *ns_cname_from_dname(const dnslib_rrset_t *dname_rrset,
 	dnslib_rrset_t *cname_rrset = dnslib_rrset_new(
 		owner, DNSLIB_RRTYPE_CNAME, DNSLIB_CLASS_IN, SYNTH_CNAME_TTL);
 
+	/* Release owner, as it's retained in rrset. */
+	dnslib_dname_release(owner);
+
 	if (cname_rrset == NULL) {
-		dnslib_dname_free(&owner);
 		return NULL;
 	}
 
@@ -3059,7 +3066,8 @@ int ns_process_response(ns_nameserver_t *nameserver, sockaddr_t *from,
 			evsched_cancel(sched, refresh_ev);
 		}
 
-		// get zone contents
+		/* Get zone contents. */
+		rcu_read_lock();
 		const dnslib_zone_contents_t *contents =
 				dnslib_zone_contents(zone);
 
@@ -3083,20 +3091,23 @@ int ns_process_response(ns_nameserver_t *nameserver, sockaddr_t *from,
 				ref_tmr);
 
 			evsched_schedule(sched, refresh_ev, ref_tmr);
+			rcu_read_unlock();
 			return KNOT_EOK;
 		}
 
-
 		/* Prepare XFR client transfer. */
 		ns_xfr_t xfr_req;
 		memset(&xfr_req, 0, sizeof(ns_xfr_t));
 		memcpy(&xfr_req.addr, from, sizeof(sockaddr_t));
-		xfr_req.data = (void *)contents;
+		xfr_req.data = (void *)zone;
 		xfr_req.send = ns_send_cb;
 
 		/* Select transfer method. */
 		xfr_req.type = ns_transfer_to_use(nameserver, contents);
 
+		/* Unlock zone contents. */
+		rcu_read_unlock();
+
 		/* Enqueue XFR request. */
 		return xfr_request(nameserver->server->xfr_h, &xfr_req);
 	}
@@ -3181,6 +3192,8 @@ static int ns_find_zone_for_xfr(ns_xfr_t *xfr, const char **zonefile,
 
 		int r = dnslib_dname_compare(zone_name, dnslib_node_owner(
 		                         dnslib_zone_contents_apex(xfr->zone)));
+
+		/* Directly discard dname, won't be needed. */
 		dnslib_dname_free(&zone_name);
 
 		if (r == 0) {
@@ -3340,7 +3353,7 @@ int ns_process_axfrin(ns_nameserver_t *nameserver, ns_xfr_t *xfr)
 	debug_ns("ns_process_axfrin: incoming packet\n");
 
 	int ret = xfrin_process_axfr_packet(xfr->wire, xfr->wire_size,
-	                               (dnslib_zone_contents_t **)(&xfr->data));
+				       (dnslib_zone_contents_t **)(&xfr->data));
 
 	if (ret > 0) { // transfer finished
 		debug_ns("ns_process_axfrin: AXFR finished, zone created.\n");
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index b227063410cb9ba7761c67939ac92617a0c107c6..9edd8889e87cc374980bdc67f52b0ad70fb22a3e 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -10,6 +10,8 @@
 #include <stdlib.h>
 #include <errno.h>
 
+#include <urcu.h>
+
 #include "knot/common.h"
 #include "knot/server/xfr-handler.h"
 #include "knot/server/name-server.h"
@@ -133,12 +135,10 @@ static inline void xfr_bridge_ev(struct ev_loop *loop, ev_io *w, int revents)
 	}
 
 	/* Fetch associated zone. */
-	dnslib_zone_contents_t *zone = (dnslib_zone_contents_t *)req->data;
+	dnslib_zone_t *zone = (dnslib_zone_t *)req->data;
 	if (!zone) {
 		return;
 	}
-	const dnslib_node_t *apex = dnslib_zone_contents_apex(zone);
-	const dnslib_dname_t *dname = dnslib_node_owner(apex);
 
 	/* Connect to remote. */
 	if (req->session <= 0) {
@@ -158,21 +158,28 @@ static inline void xfr_bridge_ev(struct ev_loop *loop, ev_io *w, int revents)
 		req->session = dup(req->session);
 	}
 
+	/* Fetch zone contents. */
+	rcu_read_lock();
+	const dnslib_zone_contents_t *contents = dnslib_zone_contents(zone);
+
 	/* Create XFR query. */
 	ret = KNOT_ERROR;
 	size_t bufsize = req->wire_size;
 	switch(req->type) {
 	case NS_XFR_TYPE_AIN:
-		ret = xfrin_create_axfr_query(dname, req->wire, &bufsize);
+		ret = xfrin_create_axfr_query(contents, req->wire, &bufsize);
 		break;
 	case NS_XFR_TYPE_IIN:
-		ret = xfrin_create_ixfr_query(dname, req->wire, &bufsize);
+		ret = xfrin_create_ixfr_query(contents, req->wire, &bufsize);
 		break;
 	default:
 		ret = KNOT_EINVAL;
 		break;
 	}
 
+	/* Unlock zone contents. */
+	rcu_read_unlock();
+
 	/* Handle errors. */
 	if (ret != KNOT_EOK) {
 		debug_xfr("xfr_in: failed to create XFR query type %d\n",
@@ -403,7 +410,7 @@ int xfr_master(dthread_t *thread)
 	}
 
 
-	// Stop whole unit
+	/* Stop whole unit. */
 	debug_xfr("xfr_master: finished.\n");
 	return KNOT_EOK;
 }
diff --git a/src/knot/server/xfr-in.c b/src/knot/server/xfr-in.c
index eaf732c7ffb25f1fe3fbf0b6708ed369b16bdc1a..13e09904d13387b61ade9e4380166bd3b1d3da9e 100644
--- a/src/knot/server/xfr-in.c
+++ b/src/knot/server/xfr-in.c
@@ -28,7 +28,7 @@ static const size_t XFRIN_CHANGESET_BINARY_STEP = 100;
 /* Non-API functions                                                          */
 /*----------------------------------------------------------------------------*/
 
-static int xfrin_create_query(const dnslib_dname_t *qname, uint16_t qtype,
+static int xfrin_create_query(const dnslib_zone_contents_t *zone, uint16_t qtype,
                               uint16_t qclass, uint8_t *buffer, size_t *size)
 {
 	dnslib_packet_t *pkt = dnslib_packet_new(DNSLIB_PACKET_PREALLOC_QUERY);
@@ -49,6 +49,12 @@ static int xfrin_create_query(const dnslib_dname_t *qname, uint16_t qtype,
 
 	dnslib_question_t question;
 
+	const dnslib_node_t *apex = dnslib_zone_contents_apex(zone);
+	dnslib_dname_t *qname = dnslib_node_get_owner(apex);
+
+	/* Retain qname until the question is freed. */
+	dnslib_dname_retain(qname);
+
 	// this is ugly!!
 	question.qname = (dnslib_dname_t *)qname;
 	question.qtype = qtype;
@@ -56,6 +62,7 @@ static int xfrin_create_query(const dnslib_dname_t *qname, uint16_t qtype,
 
 	rc = dnslib_query_set_question(pkt, &question);
 	if (rc != DNSLIB_EOK) {
+		dnslib_dname_release(question.qname);
 		dnslib_packet_free(&pkt);
 		return KNOT_ERROR;
 	}
@@ -68,6 +75,7 @@ static int xfrin_create_query(const dnslib_dname_t *qname, uint16_t qtype,
 	size_t wire_size = 0;
 	rc = dnslib_packet_to_wire(pkt, &wire, &wire_size);
 	if (rc != DNSLIB_EOK) {
+		dnslib_dname_release(question.qname);
 		dnslib_packet_free(&pkt);
 		return KNOT_ERROR;
 	}
@@ -87,6 +95,9 @@ static int xfrin_create_query(const dnslib_dname_t *qname, uint16_t qtype,
 
 	dnslib_packet_free(&pkt);
 
+	/* Release qname. */
+	dnslib_dname_release(question.qname);
+
 	return KNOT_EOK;
 }
 
@@ -160,10 +171,10 @@ static inline uint64_t ixfrdb_key_make(uint32_t from, uint32_t to)
 /* API functions                                                              */
 /*----------------------------------------------------------------------------*/
 
-int xfrin_create_soa_query(const dnslib_dname_t *zone_name, uint8_t *buffer,
+int xfrin_create_soa_query(const dnslib_zone_contents_t *zone, uint8_t *buffer,
                            size_t *size)
 {
-	return xfrin_create_query(zone_name, DNSLIB_RRTYPE_SOA,
+	return xfrin_create_query(zone, DNSLIB_RRTYPE_SOA,
 	                           DNSLIB_CLASS_IN, buffer, size);
 }
 
@@ -230,19 +241,19 @@ int xfrin_transfer_needed(const dnslib_zone_contents_t *zone,
 
 /*----------------------------------------------------------------------------*/
 
-int xfrin_create_axfr_query(const dnslib_dname_t *zone_name, uint8_t *buffer,
+int xfrin_create_axfr_query(const dnslib_zone_contents_t *zone, uint8_t *buffer,
                             size_t *size)
 {
-	return xfrin_create_query(zone_name, DNSLIB_RRTYPE_AXFR,
+	return xfrin_create_query(zone, DNSLIB_RRTYPE_AXFR,
 	                           DNSLIB_CLASS_IN, buffer, size);
 }
 
 /*----------------------------------------------------------------------------*/
 
-int xfrin_create_ixfr_query(const dnslib_dname_t *zone_name, uint8_t *buffer,
+int xfrin_create_ixfr_query(const dnslib_zone_contents_t *zone, uint8_t *buffer,
                             size_t *size)
 {
-	return xfrin_create_query(zone_name, DNSLIB_RRTYPE_IXFR,
+	return xfrin_create_query(zone, DNSLIB_RRTYPE_IXFR,
 	                           DNSLIB_CLASS_IN, buffer, size);
 }
 
@@ -259,7 +270,7 @@ int xfrin_zone_transferred(ns_nameserver_t *nameserver,
 /*----------------------------------------------------------------------------*/
 
 int xfrin_process_axfr_packet(const uint8_t *pkt, size_t size,
-                              dnslib_zone_contents_t **zone)
+			      dnslib_zone_contents_t **zone)
 {
 	if (pkt == NULL || zone == NULL) {
 		debug_xfr("Wrong parameters supported.\n");
@@ -1572,11 +1583,6 @@ static void xfrin_rollback_update(dnslib_zone_contents_t *contents,
 		dnslib_rrset_deep_free(&changes->new_rrsets[i], 0, 1, 0);
 	}
 
-//	// discard new dnames
-//	for (int i = 0; i < changes->new_dnames_count; ++i) {
-//		dnslib_dname_free(&changes->new_dnames[i]);
-//	}
-
 	// destroy the shallow copy of zone
 	xfrin_zone_contents_free(&contents);
 }
@@ -1611,7 +1617,7 @@ static int xfrin_get_node_copy(dnslib_node_t **node, xfrin_changes_t *changes)
 		dnslib_node_get_new_node(*node);
 	if (new_node == NULL) {
 		debug_xfr("Creating copy of node.\n");
-		int ret = dnslib_node_deep_copy(*node, &new_node);
+		int ret = dnslib_node_shallow_copy(*node, &new_node);
 		if (ret != DNSLIB_EOK) {
 			debug_xfr("Failed to create node copy.\n");
 			return KNOT_ENOMEM;
@@ -1659,7 +1665,7 @@ static int xfrin_copy_old_rrset(dnslib_rrset_t *old,
                                 dnslib_rrset_t **copy, xfrin_changes_t *changes)
 {
 	// create new RRSet by copying the old one
-	int ret = dnslib_rrset_copy(old, copy);
+	int ret = dnslib_rrset_shallow_copy(old, copy);
 	if (ret != DNSLIB_EOK) {
 		debug_xfr("Failed to create RRSet copy.\n");
 		return KNOT_ENOMEM;
diff --git a/src/knot/server/xfr-in.h b/src/knot/server/xfr-in.h
index 11ecd1486d728a24636aebab1fe8987cab59d7ca..f8ffb5c558749db99a32a18c6376822206e5e455 100644
--- a/src/knot/server/xfr-in.h
+++ b/src/knot/server/xfr-in.h
@@ -61,7 +61,7 @@ typedef struct {
 /*!
  * \brief Creates normal query for the given zone name and the SOA type.
  *
- * \param zone_name Name of the zone to ask for - the SOA owner.
+ * \param zone Zone to ask for - the SOA owner.
  * \param buffer Buffer to fill the message in.
  * \param size In: available space in the buffer. Out: actual size of the
  *             message in bytes.
@@ -70,7 +70,7 @@ typedef struct {
  * \retval KNOT_ESPACE
  * \retval KNOT_ERROR
  */
-int xfrin_create_soa_query(const dnslib_dname_t *zone_name, uint8_t *buffer,
+int xfrin_create_soa_query(const dnslib_zone_contents_t *zone, uint8_t *buffer,
                            size_t *size);
 
 /*!
@@ -90,7 +90,7 @@ int xfrin_transfer_needed(const dnslib_zone_contents_t *zone,
 /*!
  * \brief Creates normal query for the given zone name and the AXFR type.
  *
- * \param zone_name Name of the zone to ask for - the SOA owner.
+ * \param zone Zone to ask for - the SOA owner.
  * \param buffer Buffer to fill the message in.
  * \param size In: available space in the buffer. Out: actual size of the
  *             message in bytes.
@@ -99,13 +99,13 @@ int xfrin_transfer_needed(const dnslib_zone_contents_t *zone,
  * \retval KNOT_ESPACE
  * \retval KNOT_ERROR
  */
-int xfrin_create_axfr_query(const dnslib_dname_t *zone_name, uint8_t *buffer,
+int xfrin_create_axfr_query(const dnslib_zone_contents_t *zone, uint8_t *buffer,
                             size_t *size);
 
 /*!
  * \brief Creates normal query for the given zone name and the IXFR type.
  *
- * \param zone_name Name of the zone to ask for - the SOA owner.
+ * \param zone Zone to ask for - the SOA owner.
  * \param buffer Buffer to fill the message in.
  * \param size In: available space in the buffer. Out: actual size of the
  *             message in bytes.
@@ -114,7 +114,7 @@ int xfrin_create_axfr_query(const dnslib_dname_t *zone_name, uint8_t *buffer,
  * \retval KNOT_ESPACE
  * \retval KNOT_ERROR
  */
-int xfrin_create_ixfr_query(const dnslib_dname_t *zone_name, uint8_t *buffer,
+int xfrin_create_ixfr_query(const dnslib_zone_contents_t *zone, uint8_t *buffer,
                             size_t *size);
 
 /*!
@@ -143,7 +143,7 @@ int xfrin_zone_transferred(ns_nameserver_t *nameserver,
  * \todo Refactor!!!
  */
 int xfrin_process_axfr_packet(const uint8_t *pkt, size_t size,
-                              dnslib_zone_contents_t **zone);
+			      dnslib_zone_contents_t **zone);
 
 /*!
  * \brief Destroys the whole changesets structure.
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 9984195df3060ef73a18d7b07e5db87c078b4d6b..6da0539efd68e68a65b29a20b7f601ceb0161ddc 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -210,17 +210,12 @@ static int zones_axfrin_poll(event_t *e)
 	/* Cancel pending timers. */
 	zonedata_t *zd = (zonedata_t *)dnslib_zone_data(zone);
 
-	/* Get zone dname. */
-	const dnslib_node_t *apex = dnslib_zone_contents_apex(
-			dnslib_zone_contents(zone));
-	const dnslib_dname_t *dname = dnslib_node_owner(apex);
-
 	/* Prepare buffer for query. */
 	uint8_t qbuf[SOCKET_MTU_SZ];
 	size_t buflen = SOCKET_MTU_SZ;
 
 	/* Create query. */
-	int ret = xfrin_create_soa_query(dname, qbuf, &buflen);
+	int ret = xfrin_create_soa_query(dnslib_zone_contents(zone), qbuf, &buflen);
 	if (ret == KNOT_EOK && zd->xfr_in.ifaces) {
 
 		int sock = -1;
@@ -691,7 +686,9 @@ static int zones_insert_zones(ns_nameserver_t *ns,
 	// for all zones in the configuration
 	WALK_LIST(n, *zone_conf) {
 		conf_zone_t *z = (conf_zone_t *)n;
-		// convert the zone name into a domain name
+
+		/* Convert the zone name into a domain name. */
+		/* Local allocation, will be discarded. */
 		dnslib_dname_t *zone_name = dnslib_dname_new_from_str(z->name,
 		                                         strlen(z->name), NULL);
 		if (zone_name == NULL) {
@@ -790,6 +787,7 @@ static int zones_insert_zones(ns_nameserver_t *ns,
 
 		dnslib_zone_contents_dump(dnslib_zone_get_contents(zone), 1);
 
+		/* Directly discard zone. */
 		dnslib_dname_free(&zone_name);
 	}
 	return inserted;
@@ -814,7 +812,9 @@ static int zones_remove_zones(const list *zone_conf, dnslib_zonedb_t *db_old)
 	// for all zones in the configuration
 	WALK_LIST(n, *zone_conf) {
 		conf_zone_t *z = (conf_zone_t *)n;
-		// convert the zone name into a domain name
+
+		/* Convert the zone name into a domain name. */
+		/* Local allocation, will be discarded. */
 		dnslib_dname_t *zone_name = dnslib_dname_new_from_str(z->name,
 		                                         strlen(z->name), NULL);
 		if (zone_name == NULL) {
@@ -827,6 +827,7 @@ static int zones_remove_zones(const list *zone_conf, dnslib_zonedb_t *db_old)
 		// remove the zone from the old zone db, but do not delete it
 		dnslib_zonedb_remove_zone(db_old, zone_name, 0);
 
+		/* Directly discard. */
 		dnslib_dname_free(&zone_name);
 	}
 	return KNOT_EOK;
diff --git a/src/zcompile/zcompile.c b/src/zcompile/zcompile.c
index 0764f0e51cc531df1c4fceaa4e747e7ee4e9197f..11bb49d22febe8b83f4e6bcb5a0760183772b3b1 100644
--- a/src/zcompile/zcompile.c
+++ b/src/zcompile/zcompile.c
@@ -300,6 +300,9 @@ static ssize_t rdata_wireformat_to_rdata_atoms(const uint16_t *wireformat,
 			dname = dnslib_dname_new_from_str((char *)wireformat,
 							  length,
 							  NULL);
+			/*! \todo implement refcounting correctly. */
+			ref_init(&dname->ref, 0); /* disable dtor */
+			ref_retain(&dname->ref);
 
 			if (dname == NULL) {
 				dbg_rdata("malformed dname!\n");
@@ -1824,6 +1827,9 @@ int zone_read(const char *name, const char *zonefile, const char *outfile,
 
 	dnslib_dname_t *dname =
 		dnslib_dname_new_from_str(name, strlen(name), NULL);
+	/*! \todo implement refcounting correctly. */
+	ref_init(&dname->ref, 0); /* disable dtor */
+	ref_retain(&dname->ref);
 
 	dnslib_node_t *origin_node = dnslib_node_new(dname, NULL, 0);
 
diff --git a/src/zcompile/zlexer.l b/src/zcompile/zlexer.l
index 43d8bff937b8000d20e5393f642694b12f89ecb7..84ab56d1f4bbdb0a433d10741c1914453cef19af 100644
--- a/src/zcompile/zlexer.l
+++ b/src/zcompile/zlexer.l
@@ -194,10 +194,13 @@ ANY     [^\"\n\\]|\\.
 			/*! \todo dnslib_dname_new_from_wire() (dname.h)
 			 *        which dnslib_node to pass as node?
 			 */
-			const dnslib_dname_t *dname;
+			dnslib_dname_t *dname;
 			dname = dnslib_dname_new_from_wire((uint8_t*)tmp + 1,
 							   strlen(tmp + 1),
 							   NULL);
+			/*! \todo implement refcounting correctly. */
+			ref_init(&dname->ref, 0); /* disable dtor */
+			ref_retain(&dname->ref);
 			if (!dname) {
 				zc_error("incorrect include origin '%s'",
 					 tmp + 1);
diff --git a/src/zcompile/zparser.y b/src/zcompile/zparser.y
index abbf12e7af77b8b906c9b3b8145c0705510dddfe..17ef150d0d97dddb55c1809a3128105b470c5695 100644
--- a/src/zcompile/zparser.y
+++ b/src/zcompile/zparser.y
@@ -282,14 +282,14 @@ owner:	dname sp
 //	printf("Totally new dname: %p %s\n", $1,
 //	dnslib_dname_to_str($1));
 	dnslib_dname_free(&parser->prev_dname);
-	    parser->prev_dname = dnslib_dname_copy($1);
+	    parser->prev_dname = dnslib_dname_deep_copy($1);
 	    $$ = $1;
     }
     |	PREV
     {
 //	    printf("Name from prev_dname!: %p %s\n", parser->prev_dname,
 //	    dnslib_dname_to_str(parser->prev_dname));
-	    $$ = dnslib_dname_copy(parser->prev_dname);
+	    $$ = dnslib_dname_deep_copy(parser->prev_dname);
     }
     ;
 
@@ -344,7 +344,7 @@ abs_dname:	'.'
     {
 	    /*! \todo Get root domain from db. */
 		//$$ = parser->db->domains->root;
-	    $$ = dnslib_dname_copy(parser->root_domain);
+	    $$ = dnslib_dname_deep_copy(parser->root_domain);
     }
     |	'@'
     {
@@ -370,6 +370,9 @@ label:	STR
 		    $$ = error_dname;
 	    } else {
 		    $$ = dnslib_dname_new_from_str($1.str, $1.len, NULL);
+		    /*! \todo implement refcounting correctly. */
+		    //ref_init(&$$->ref, 0); /* disable dtor */
+		    //ref_retain(&dname->ref);
 	//printf("new: %p %s\n", $$, dnslib_dname_to_str($$));
 	    }
 
@@ -1213,6 +1216,9 @@ rdata_rrsig:	STR sp STR sp STR sp STR sp STR sp STR
 							 $15.len));*/
 	    dnslib_dname_t *dname =
 		dnslib_dname_new_from_wire((uint8_t *)$15.str, $15.len, NULL);
+	    /*! \todo implement refcounting correctly. */
+	    ref_init(&dname->ref, 0); /* disable dtor */
+	    ref_retain(&dname->ref);
 
 	    dnslib_dname_cat(dname, parser->root_domain);
 
@@ -1240,6 +1246,10 @@ rdata_nsec:	wire_dname nsec_seq
 
 	    dnslib_dname_t *dname =
 		dnslib_dname_new_from_wire((uint8_t *)$1.str, $1.len, NULL);
+	    /*! \todo implement refcounting correctly. */
+	    ref_init(&dname->ref, 0); /* disable dtor */
+	    ref_retain(&dname->ref);
+
 	    free($1.str);
 
 	    dnslib_dname_cat(dname, parser->root_domain);
@@ -1312,7 +1322,7 @@ rdata_dnskey:	STR sp STR sp STR sp str_sp_seq trail
 
 rdata_ipsec_base: STR sp STR sp STR sp dotted_str
     {
-	    const dnslib_dname_t* name = 0;
+	    dnslib_dname_t* name = 0;
 	    zadd_rdata_wireformat(zparser_conv_byte($1.str)); /* precedence */
 	    zadd_rdata_wireformat(zparser_conv_byte($3.str));
 	    /* gateway type */
@@ -1335,6 +1345,9 @@ rdata_ipsec_base: STR sp STR sp STR sp dotted_str
 			name = dnslib_dname_new_from_wire((uint8_t*)$7.str + 1,
 							  strlen($7.str + 1),
 							  NULL);
+			/*! \todo implement refcounting correctly. */
+			ref_init(&name->ref, 0); /* disable dtor */
+			ref_retain(&name->ref);
 
 			if(!name) {
 				zc_error_prev_line("IPSECKEY bad gateway"
@@ -1345,8 +1358,14 @@ rdata_ipsec_base: STR sp STR sp STR sp dotted_str
 			    tmpd = dnslib_dname_new_from_wire(name->name,
 							      name->size,
 							      NULL);
+			    /*! \todo implement refcounting correctly. */
+			    ref_init(&tmpd->ref, 0); /* disable dtor */
+			    ref_retain(&tmpd->ref);
 			    name = dnslib_dname_cat(tmpd,
 				    dnslib_node_parent(parser->origin, 0)->owner);
+			    /*! \todo implement refcounting correctly. */
+			    ref_init(&name->ref, 0); /* disable dtor */
+			    ref_retain(&name->ref);
 			}
 
 			free($1.str);
@@ -1444,7 +1463,7 @@ zparser_init(const char *filename, uint32_t ttl, uint16_t rclass,
 	parser->default_class = rclass;
 
 	parser->origin = origin;
-	parser->prev_dname = dnslib_dname_copy(parser->origin->owner);
+	parser->prev_dname = dnslib_dname_deep_copy(parser->origin->owner);
 
 	parser->default_apex = origin;
 	parser->error_occurred = 0;
@@ -1455,6 +1474,9 @@ zparser_init(const char *filename, uint32_t ttl, uint16_t rclass,
 
 	parser->last_node = origin;
 	parser->root_domain = dnslib_dname_new_from_str(".", 1, NULL);
+	/*! \todo implement refcounting correctly. */
+	ref_init(&parser->root_domain->ref, 0); /* disable dtor */
+	ref_retain(&parser->root_domain->ref);
 
 	/* Create zone */
 	parser->current_zone = dnslib_zone_new(origin, 0, 1);