diff --git a/CuteDNS.files b/CuteDNS.files
index 853e800d583ca247bcdbe8c9f83f36cbc4ec358d..cf56fc22248fd6773b97da78fa463ea75481e567 100644
--- a/CuteDNS.files
+++ b/CuteDNS.files
@@ -83,3 +83,5 @@ src/dnslib/packet.h
 src/dnslib/response.c
 src/dnslib/edns.h
 src/dnslib/utils.h
+src/dnslib/zonedb.h
+src/dnslib/zonedb.c
diff --git a/src/dnslib/zonedb.c b/src/dnslib/zonedb.c
new file mode 100644
index 0000000000000000000000000000000000000000..41a3de4353c0da0857fb61e5f33d802f79fb0a60
--- /dev/null
+++ b/src/dnslib/zonedb.c
@@ -0,0 +1,135 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include <urcu.h>
+
+#include "zonedb.h"
+#include "common.h"
+#include "skip-list.h"
+#include "zone.h"
+#include "dname.h"
+#include "node.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions                                                          */
+/*----------------------------------------------------------------------------*/
+
+static int dnslib_zonedb_compare_zone_names(void *d1, void *d2)
+{
+	const dnslib_dname_t *dname1 = (const dnslib_dname_t *)d1;
+	const dnslib_dname_t *dname2 = (const dnslib_dname_t *)d2;
+
+	return (dnslib_dname_compare(dname1, dname2));
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions                                                              */
+/*----------------------------------------------------------------------------*/
+
+dnslib_zonedb_t *dnslib_zonedb_new()
+{
+	dnslib_zonedb_t *db =
+		(dnslib_zonedb_t *)malloc(sizeof(dnslib_zonedb_t));
+	CHECK_ALLOC_LOG(db, NULL);
+
+	db->zones = skip_create_list(dnslib_zonedb_compare_zone_names);
+	if (db->zones == NULL) {
+		free(db);
+		return NULL;
+	}
+
+	return db;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_zonedb_add_zone(dnslib_zonedb_t *db, dnslib_zone_t *zone)
+{
+	int ret = skip_insert(db->zones, zone->apex->owner, zone, NULL);
+	assert(ret == 0 || ret == 1 || ret == -1);
+	return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_zonedb_remove_zone(dnslib_zonedb_t *db, dnslib_dname_t *zone_name)
+{
+	// add some lock to avoid multiple removals
+	dnslib_zone_t *z = (dnslib_zone_t *)skip_find(db->zones, zone_name);
+
+	if (z == NULL) {
+		return -1;
+	}
+
+	// remove the zone from the skip list, but do not destroy it
+	int ret = skip_remove(db->zones, zone_name, NULL, NULL);
+	assert(ret == 0);
+
+	// wait for all readers to finish
+	synchronize_rcu();
+
+	// properly destroy the zone and all its contents
+	dnslib_zone_deep_free(&z);
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const dnslib_zone_t *dnslib_zonedb_find_zone_for_name(dnslib_zonedb_t *db,
+                                                    const dnslib_dname_t *dname)
+{
+	rcu_read_lock();
+
+	dnslib_zone_t *zone = skip_find(db->zones, (void *)dname);
+
+	rcu_read_unlock();
+
+	return zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const dnslib_node_t *dnslib_zonedb_find_name_in_zone(const dnslib_zone_t *zone,
+                                                    const dnslib_dname_t *dname)
+{
+	assert(zone != NULL && dname != NULL);
+
+	// start of RCU reader critical section
+	rcu_read_lock();
+
+	const dnslib_node_t *n = dnslib_zonedb_find_name_in_zone(zone, dname);
+
+	// end of RCU reader critical section
+	rcu_read_unlock();
+
+	return n;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void dnslib_zonedb_deep_free(dnslib_zonedb_t **db)
+{
+	const skip_node_t *zn = skip_first((*db)->zones);
+	dnslib_zone_t *zone = NULL;
+
+	while (zn != NULL) {
+		zone = (dnslib_zone_t *)zn->value;
+		assert(zone != NULL);
+
+		// remove the zone from the database
+		skip_remove((*db)->zones, zn->key, NULL, NULL);
+		// wait for all readers to finish
+		synchronize_rcu();
+		// destroy the zone
+		dnslib_zone_deep_free(&zone);
+
+		zn = skip_first((*db)->zones);
+	}
+
+	assert(skip_is_empty((*db)->zones));
+
+	skip_destroy_list(&(*db)->zones, NULL, NULL);
+	free(*db);
+	*db = NULL;
+}
diff --git a/src/dnslib/zonedb.h b/src/dnslib/zonedb.h
new file mode 100644
index 0000000000000000000000000000000000000000..79987be55637470edc9252be478ce95b8df8dba2
--- /dev/null
+++ b/src/dnslib/zonedb.h
@@ -0,0 +1,109 @@
+/*!
+ * \file zonedb.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Zone database structure and API for manipulating it.
+ *
+ * Zone database groups several zones and provides functions for finding
+ * suitable zone for a domain name, for searching in a particular zone, etc.
+ *
+ * \addtogroup dnslib
+ * @{
+ */
+
+#ifndef _CUTEDNS_DNSLIB_ZONEDB_H_
+#define _CUTEDNS_DNSLIB_ZONEDB_H_
+
+#include "skip-list.h"
+#include "zone.h"
+#include "node.h"
+#include "dname.h"
+
+struct dnslib_zonedb {
+	skip_list_t *zones;
+};
+
+typedef struct dnslib_zonedb dnslib_zonedb_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Allocates and initializes the zone database structure.
+ *
+ * \return Pointer to the created zone database structure.
+ */
+dnslib_zonedb_t *dnslib_zonedb_new();
+
+/*!
+ * \brief Adds new zone to the database.
+ *
+ * \param database Zone database to store the zone.
+ * \param zone Parsed zone in ldns_zone format.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int dnslib_zonedb_add_zone(dnslib_zonedb_t *db, dnslib_zone_t *zone);
+
+/*!
+ * \brief Removes the given zone from the database if it exists.
+ *
+ * \param db Zone database to remove from.
+ * \param zone_name Name of the zone to be removed.
+ *
+ * The removal of a zone is synchronized using RCU mechanism, so the zone data
+ * will not be destroyed while some thread may be using it.
+ *
+ * \retval 0 On success.
+ * \retval -1 If the zone was not found.
+ */
+int dnslib_zonedb_remove_zone(dnslib_zonedb_t *db, dnslib_dname_t *zone_name);
+
+/*!
+ * \brief Finds zone the given domain name should belong to.
+ *
+ * \param database Zone database to search in.
+ * \param dname Domain name to find zone for.
+ *
+ * \retval Zone in which the domain name should be present.
+// *
+// * \note As the zones are ordered in reverse canonical order, a possible parent
+// *       of the returned zone may be retrieved easily as it is the next item
+// *       in the linked list (zdb_zone.next).
+ */
+const dnslib_zone_t *dnslib_zonedb_find_zone_for_name(dnslib_zonedb_t *db,
+                                                   const dnslib_dname_t *dname);
+
+/*!
+ * \brief Finds the given name in the zone database and returns corresponding
+ *        zone node.
+ *
+ * \param database Zone database to search in.
+ * \param dname Domain name to find.
+ *
+ * \return Proper zone node for the given name or NULL if not found.
+ */
+const dnslib_node_t *dnslib_zonedb_find_name_in_zone(const dnslib_zone_t *zone,
+                                                   const dnslib_dname_t *dname);
+
+/*!
+ * \brief Destroys and deallocates the whole zone database.
+ *
+ * \param database Pointer to pointer to the zone database to be destroyed.
+ *
+ * The zones are destroyed one-by-one and the process is synchronized using
+ * RCU mechanism, so the zone data will not be destroyed while some thread may
+ * be using it.
+// *
+// * \todo Destroy nodes which are not hashed into the table. Best will be to
+// *       destroy zone nodes from the list and tell zds_destroy() not to destroy
+// *       the stored items.
+ */
+void dnslib_zonedb_deep_free(dnslib_zonedb_t **db);
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* _CUTEDNS_DNSLIB_ZONEDB_H_ */
+
+/*! @} */