From 09531e6df3491cebbb415722b2612545b30ec3b9 Mon Sep 17 00:00:00 2001
From: Lubos Slovak <lubos.slovak@nic.cz>
Date: Tue, 2 Aug 2011 11:19:12 +0200
Subject: [PATCH] Removed zones.h include from nameserver.

refs #1087 @5h
---
 src/knot/main.c               |   3 +-
 src/knot/other/debug.h        |   9 +
 src/knot/server/name-server.c | 864 +++++-----------------------------
 src/knot/server/name-server.h |  78 ++-
 src/knot/server/notify.c      | 144 +++++-
 src/knot/server/notify.h      |  41 +-
 src/knot/server/tcp-handler.c |   5 +-
 src/knot/server/udp-handler.c |  25 +-
 src/knot/server/xfr-handler.c |  81 +++-
 src/knot/server/xfr-in.c      |  14 +-
 src/knot/server/zones.c       | 356 ++++++++++++++
 src/knot/server/zones.h       |  33 ++
 12 files changed, 795 insertions(+), 858 deletions(-)

diff --git a/src/knot/main.c b/src/knot/main.c
index 640d2276b..fcfb12a4e 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -11,6 +11,7 @@
 #include "knot/conf/conf.h"
 #include "knot/conf/logconf.h"
 #include "common/evqueue.h"
+#include "knot/server/zones.h"
 
 /*----------------------------------------------------------------------------*/
 
@@ -115,7 +116,7 @@ int main(int argc, char **argv)
 	// Initialize configuration
 	conf_read_lock();
 	conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0);
-	conf_add_hook(conf(), CONF_LOG, dnslib_ns_conf_hook, server->nameserver);
+	conf_add_hook(conf(), CONF_LOG, zones_ns_conf_hook, server->nameserver);
 	conf_add_hook(conf(), CONF_LOG, server_conf_hook, server);
 	conf_read_unlock();
 
diff --git a/src/knot/other/debug.h b/src/knot/other/debug.h
index 43024a54a..cff353d09 100644
--- a/src/knot/other/debug.h
+++ b/src/knot/other/debug.h
@@ -23,6 +23,15 @@
 //#define NET_DEBUG
 //#define ZONES_DEBUG
 //#define XFR_DEBUG
+//#define NOTIFY_DEBUG
+
+#ifdef NOTIFY_DEBUG
+#define debug_notify(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define debug_notify_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define debug_notify(msg...)
+#define debug_notify_hex(data, len)
+#endif
 
 #ifdef SERVER_DEBUG
 #define debug_server(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
diff --git a/src/knot/server/name-server.c b/src/knot/server/name-server.c
index 05fd3eb41..6b9178867 100644
--- a/src/knot/server/name-server.c
+++ b/src/knot/server/name-server.c
@@ -11,7 +11,7 @@
 //#include "knot/stat/stat.h"
 //#include "knot/other/error.h"
 
-#include "knot/server/zones.h"
+//#include "knot/server/zones.h"
 
 #include "knot/server/name-server.h"
 #include "knot/server/notify.h"
@@ -2131,17 +2131,17 @@ static int ns_ixfr_put_changeset(dnslib_ns_xfr_t *xfr, const xfrin_changeset_t *
 
 /*----------------------------------------------------------------------------*/
 
-static int ns_ixfr_from_zone(const dnslib_zone_t *zone, dnslib_ns_xfr_t *xfr)
+static int ns_ixfr_from_zone(dnslib_ns_xfr_t *xfr)
 {
-	assert(zone != NULL);
 	assert(xfr != NULL);
+	assert(xfr->zone != NULL);
 	assert(xfr->query != NULL);
 	assert(xfr->response != NULL);
 	assert(dnslib_packet_additional_rrset_count(xfr->query) > 0);
 
 	const dnslib_rrset_t *zone_soa =
 		dnslib_node_rrset(dnslib_zone_contents_apex(
-		                       dnslib_zone_contents(zone)),
+		                       dnslib_zone_contents(xfr->zone)),
 		                  DNSLIB_RRTYPE_SOA);
 	// retrieve origin (xfr) serial and target (zone) serial
 	uint32_t zone_serial = dnslib_rdata_soa_serial(
@@ -2152,7 +2152,8 @@ static int ns_ixfr_from_zone(const dnslib_zone_t *zone, dnslib_ns_xfr_t *xfr)
 	// 3) load changesets from journal
 	xfrin_changesets_t *chgsets = (xfrin_changesets_t *)
 	                               calloc(1, sizeof(xfrin_changesets_t));
-	int res = xfr_load_changesets(zone, chgsets, xfr_serial, zone_serial);
+	int res = xfr_load_changesets(xfr->zone, chgsets, xfr_serial, 
+	                              zone_serial);
 	if (res != DNSLIB_EOK) {
 		debug_dnslib_ns("IXFR query cannot be answered: %s.\n",
 		         dnslib_strerror(res));
@@ -2201,66 +2202,8 @@ static int ns_ixfr_from_zone(const dnslib_zone_t *zone, dnslib_ns_xfr_t *xfr)
 
 /*----------------------------------------------------------------------------*/
 
-static int ns_axfr(const dnslib_zonedb_t *zonedb, dnslib_ns_xfr_t *xfr)
-{
-	const dnslib_dname_t *qname = dnslib_packet_qname(xfr->response);
-
-	assert(dnslib_packet_qtype(xfr->response) == DNSLIB_RRTYPE_AXFR);
-
-DEBUG_DNSLIB_NS(
-	char *name_str = dnslib_dname_to_str(qname);
-	debug_dnslib_ns("Trying to find zone with name %s\n", name_str);
-	free(name_str);
-);
-	// find zone in which to search for the name
-	dnslib_zone_t *zone = dnslib_zonedb_find_zone(zonedb, qname);
-
-	// if no zone found, return NotAuth
-	if (zone == NULL) {
-		debug_dnslib_ns("No zone found.\n");
-		dnslib_response2_set_rcode(xfr->response, DNSLIB_RCODE_NOTAUTH);
-		ns_axfr_send_and_clear(xfr);
-		return 1;
-	}
-
-DEBUG_DNSLIB_NS(
-	char *name_str2 = dnslib_dname_to_str(zone->contents->apex->owner);
-	debug_dnslib_ns("Found zone for QNAME %s\n", name_str2);
-	free(name_str2);
-);
-	/* Check zone data. */
-	zonedata_t *zd = (zonedata_t *)zone->data;
-	if (zd == NULL) {
-		debug_dnslib_ns("Invalid zone data.\n");
-		dnslib_response2_set_rcode(xfr->response,
-		                           DNSLIB_RCODE_SERVFAIL);
-		ns_axfr_send_and_clear(xfr);
-		return 1;
-	}
-
-	// Check xfr-out ACL
-	if (acl_match(zd->xfr_out, &xfr->addr) == ACL_DENY) {
-		debug_dnslib_ns("Request for AXFR OUT is not authorized.\n");
-		dnslib_response2_set_rcode(xfr->response, DNSLIB_RCODE_REFUSED);
-		/*! \todo Probably rename the function. */
-		ns_axfr_send_and_clear(xfr);
-//		socket_close(xfr->session);
-		return 1;
-	} else {
-		debug_dnslib_ns("Authorized AXFR OUT request.\n");
-	}
-
-	// take the contents and answer from them
-	dnslib_zone_contents_t *contents = dnslib_zone_get_contents(zone);
-
-	return ns_axfr_from_zone(contents, xfr);
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int ns_ixfr(const dnslib_zonedb_t *zonedb, dnslib_ns_xfr_t *xfr)
+static int ns_ixfr(dnslib_ns_xfr_t *xfr)
 {
-	assert(zonedb != NULL);
 	assert(xfr != NULL);
 	assert(xfr->query != NULL);
 	assert(xfr->response != NULL);
@@ -2294,67 +2237,7 @@ static int ns_ixfr(const dnslib_zonedb_t *zonedb, dnslib_ns_xfr_t *xfr)
 		return 1;
 	}
 
-	// find zone
-DEBUG_DNSLIB_NS(
-	char *name_str = dnslib_dname_to_str(qname);
-	debug_dnslib_ns("Trying to find zone with name %s\n", name_str);
-	free(name_str);
-);
-	// find zone in which to search for the name
-	dnslib_zone_t *zone = dnslib_zonedb_find_zone(zonedb, qname);
-
-	// if no zone found, return NotAuth
-	if (zone == NULL) {
-		debug_dnslib_ns("No zone found.\n");
-		dnslib_response2_set_rcode(xfr->response, DNSLIB_RCODE_NOTAUTH);
-		ns_axfr_send_and_clear(xfr);
-		//socket_close(xfr->session);  /*! \todo Remove for UDP. */
-		return 1;
-	}
-
-DEBUG_DNSLIB_NS(
-	char *name_str = dnslib_dname_to_str(zone->contents->apex->owner);
-	debug_dnslib_ns("Found zone for QNAME %s\n", name_str);
-	free(name_str);
-);
-
-	/* Check zone data. */
-	zonedata_t *zd = (zonedata_t *)zone->data;
-	if (zd == NULL) {
-		debug_dnslib_ns("Invalid zone data.\n");
-		dnslib_response2_set_rcode(xfr->response,
-		                           DNSLIB_RCODE_SERVFAIL);
-		ns_axfr_send_and_clear(xfr);
-		//socket_close(xfr->session);  /*! \todo Remove for UDP. */
-		return 1;
-	}
-
-	// Check xfr-out ACL
-	if (acl_match(zd->xfr_out, &xfr->addr) == ACL_DENY) {
-		debug_dnslib_ns("Request for IXFR OUT is not authorized.\n");
-		dnslib_response2_set_rcode(xfr->response, DNSLIB_RCODE_REFUSED);
-		/*! \todo Probably rename the function. */
-		ns_axfr_send_and_clear(xfr);
-//		socket_close(xfr->session);  /*! \todo Remove for UDP. */
-		return 1;
-	} else {
-		debug_dnslib_ns("Authorized IXFR OUT request.\n");
-	}
-
-	// 3) call ns_ixfr_from_zone();
-	return ns_ixfr_from_zone(zone, xfr);
-}
-
-/*----------------------------------------------------------------------------*/
-
-/*!
- * \brief Wrapper for TCP send.
- * \todo Implement generic fd pool properly with callbacks.
- */
-#include "knot/server/tcp-handler.h"
-static int ns_send_cb(int fd, sockaddr_t *addr, uint8_t *msg, size_t msglen)
-{
-	return tcp_send(fd, msg, msglen);
+	return ns_ixfr_from_zone(xfr);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -2544,81 +2427,6 @@ void dnslib_ns_error_response(dnslib_nameserver_t *nameserver, uint16_t query_id
 
 /*----------------------------------------------------------------------------*/
 
-//int ns_answer_request(ns_nameserver_t *nameserver, const uint8_t *query_wire,
-//                      size_t qsize, uint8_t *response_wire, size_t *rsize)
-//{
-//	debug_dnslib_ns("ns_answer_request() called with query size %zu.\n", qsize);
-//	debug_dnslib_ns_hex((char *)query_wire, qsize);
-
-//	if (qsize < 2) {
-//		return DNSLIB_EMALF;
-//	}
-
-//	// 1) create empty response
-//	debug_dnslib_ns("Parsing query using new dnslib structure...\n");
-//	dnslib_response_t *resp = dnslib_response_new_empty(nameserver->opt_rr);
-
-//	if (resp == NULL) {
-//		log_answer_error("Error while creating response packet!\n");
-//		ns_error_response(nameserver, dnslib_wire_get_id(query_wire),
-//		                  DNSLIB_RCODE_SERVFAIL, response_wire, rsize);
-//		return DNSLIB_EOK;
-//	}
-
-//	int ret = 0;
-
-//	// 2) parse the query
-//	if ((ret = dnslib_response_parse_query(resp, query_wire, qsize)) != 0) {
-//		log_answer_info("Error while parsing query, "
-//		                "dnslib error '%s'.\n", dnslib_strerror(ret));
-//		ns_error_response(nameserver, dnslib_wire_get_id(query_wire),
-//		                  DNSLIB_RCODE_FORMERR, response_wire, rsize);
-//		dnslib_response_free(&resp);
-//		return DNSLIB_EOK;
-//	}
-
-//	// NSID
-//	if (NSID_ENABLED && dnslib_response_nsid_requested(resp)) {
-//		(void)dnslib_response_add_nsid(resp, NSID_DATA, NSID_LENGTH);
-//	}
-
-//	debug_dnslib_ns("Query parsed.\n");
-//	dnslib_response_dump(resp);
-
-//	// 3) get the answer for the query
-//	rcu_read_lock();
-//	dnslib_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
-
-//	ret = ns_answer(zonedb, resp);
-//	if (ret != 0) {
-//		// now only one type of error (SERVFAIL), later maybe more
-//		ns_error_response(nameserver, dnslib_wire_get_id(query_wire),
-//		                  DNSLIB_RCODE_SERVFAIL, response_wire, rsize);
-//	} else {
-//		debug_dnslib_ns("Created response packet.\n");
-//		dnslib_response_dump(resp);
-
-//		// 4) Transform the packet into wire format
-//		if (ns_response_to_wire(resp, response_wire, rsize) != 0) {
-//			// send back SERVFAIL (as this is our problem)
-//			ns_error_response(nameserver,
-//			                  dnslib_wire_get_id(query_wire),
-//			                  DNSLIB_RCODE_SERVFAIL, response_wire,
-//			                  rsize);
-//		}
-//	}
-
-//	dnslib_response_free(&resp);
-//	rcu_read_unlock();
-
-//	debug_dnslib_ns("Returning response with wire size %zu\n", *rsize);
-//	debug_dnslib_ns_hex((char *)response_wire, *rsize);
-
-//	return DNSLIB_EOK;
-//}
-
-/*----------------------------------------------------------------------------*/
-
 int dnslib_ns_answer_normal(dnslib_nameserver_t *nameserver, dnslib_packet_t *query,
                      uint8_t *response_wire, size_t *rsize)
 {
@@ -2725,75 +2533,9 @@ int dnslib_ns_answer_normal(dnslib_nameserver_t *nameserver, dnslib_packet_t *qu
 
 /*----------------------------------------------------------------------------*/
 
-int dnslib_ns_answer_notify(dnslib_nameserver_t *nameserver, dnslib_packet_t *query,
-                     sockaddr_t *from, uint8_t *response_wire, size_t *rsize)
-{
-	debug_dnslib_ns("ns_answer_notify()\n");
-
-	/* Find matching zone from qname. */
-	const dnslib_dname_t *zone_name = dnslib_packet_qname(query);
-	dnslib_zone_t *zone = dnslib_zonedb_find_zone(nameserver->zone_db,
-	                                              zone_name);
-	if (!zone) {
-		debug_dnslib_ns("notify: matching zone not found\n");
-		return DNSLIB_ENOZONE;
-	}
-	if (!dnslib_zone_data(zone)) {
-		debug_dnslib_ns("notify: invalid zone data\n");
-		return DNSLIB_ENOZONE;
-	}
-
-	/* Check ACL for notify-in. */
-	zonedata_t *zd = (zonedata_t *)dnslib_zone_data(zone);
-	if (from) {
-		if (acl_match(zd->notify_in, from) == ACL_DENY) {
-			/* rfc1996: Ignore request and report incident. */
-			char straddr[SOCKADDR_STRLEN];
-			sockaddr_tostr(from, straddr, sizeof(straddr));
-			debug_dnslib_ns("Unauthorized NOTIFY request "
-			                 "from %s:%d.\n",
-			                 straddr, sockaddr_portnum(from));
-			return DNSLIB_ERROR;
-		} else {
-			debug_dnslib_ns("notify: authorized NOTIFY query.\n");
-		}
-	}
-
-	/*! \todo Packet may contain updated RRs. */
-
-	/* Cancel EXPIRE timer. */
-	evsched_t *sched = nameserver->server->sched;
-	event_t *expire_ev = zd->xfr_in.expire;
-	if (expire_ev) {
-		debug_dnslib_ns("notify: canceling EXPIRE timer\n");
-		evsched_cancel(sched, expire_ev);
-		evsched_event_free(sched, expire_ev);
-		zd->xfr_in.expire = 0;
-	}
-
-	/* Cancel REFRESH/RETRY timer. */
-	event_t *refresh_ev = zd->xfr_in.timer;
-	if (refresh_ev) {
-		debug_dnslib_ns("notify: canceling REFRESH timer for XFRIN\n");
-		evsched_cancel(sched, refresh_ev);
-
-		/* Set REFRESH timer for now. */
-		evsched_schedule(sched, refresh_ev, 0);
-	}
-
-	/*! \todo Prepare response - copy query and set QR. */
-	/*! \todo It is safe to assume response_wire contains query wire? */
-	dnslib_wire_set_qr(response_wire);
-	*rsize = query->size;
-
-	return DNSLIB_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-int dnslib_ns_answer_axfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
+int dnslib_ns_init_xfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
 {
-	debug_dnslib_ns("ns_answer_axfr()\n");
+	debug_dnslib_ns("dnslib_ns_init_xfr()\n");
 
 	if (nameserver == NULL || xfr == NULL) {
 		return DNSLIB_EBADARG;
@@ -2811,8 +2553,10 @@ int dnslib_ns_answer_axfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
 		dnslib_ns_error_response(nameserver, xfr->query->header.id,
 				  DNSLIB_RCODE_SERVFAIL, xfr->wire,
 				  &xfr->wire_size);
-		/*! \todo Hm, maybe send the packet? ;-) */
-		return DNSLIB_EOK;
+		int res = xfr->send(xfr->session, &xfr->addr, xfr->wire, 
+		                    xfr->wire_size);
+		dnslib_packet_free(&response);
+		return res;
 	}
 
 	int ret = dnslib_packet_set_max_size(response, xfr->wire_size);
@@ -2821,11 +2565,12 @@ int dnslib_ns_answer_axfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
 		debug_dnslib_ns("Failed to init response structure.\n");
 		/*! \todo xfr->wire is not NULL, will fail on assert! */
 		dnslib_ns_error_response(nameserver, xfr->query->header.id,
-				  DNSLIB_RCODE_SERVFAIL, xfr->wire,
-				  &xfr->wire_size);
+		                         DNSLIB_RCODE_SERVFAIL, xfr->wire,
+		                         &xfr->wire_size);
+		int res = xfr->send(xfr->session, &xfr->addr, xfr->wire, 
+		                    xfr->wire_size);
 		dnslib_packet_free(&response);
-		/*! \todo Hm, maybe send the packet? ;-) */
-		return DNSLIB_EOK;
+		return res;
 	}
 
 	ret = dnslib_response2_init_from_query(response, xfr->query);
@@ -2834,28 +2579,71 @@ int dnslib_ns_answer_axfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
 		debug_dnslib_ns("Failed to init response structure.\n");
 		/*! \todo xfr->wire is not NULL, will fail on assert! */
 		dnslib_ns_error_response(nameserver, xfr->query->header.id,
-				  DNSLIB_RCODE_SERVFAIL, xfr->wire,
-				  &xfr->wire_size);
+		                         DNSLIB_RCODE_SERVFAIL, xfr->wire,
+		                         &xfr->wire_size);
+		int res = xfr->send(xfr->session, &xfr->addr, xfr->wire, 
+		                    xfr->wire_size);
 		dnslib_packet_free(&response);
-		/*! \todo Hm, maybe send the packet? ;-) */
-		return DNSLIB_EOK;
+		return res;
 	}
 
 	xfr->response = response;
+	
+	dnslib_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
+	
+	const dnslib_dname_t *qname = dnslib_packet_qname(xfr->response);
 
-	// set the OPT RR to the response
-	/*! \todo Only if client supports it! */
-	ret = dnslib_response2_add_opt(xfr->response, nameserver->opt_rr, 0);
-	if (ret != DNSLIB_EOK) {
-		debug_dnslib_ns("Failed to set OPT RR to the response: %s\n",
-		                  dnslib_strerror(ret));
+	assert(dnslib_packet_qtype(xfr->response) == DNSLIB_RRTYPE_AXFR);
+
+DEBUG_DNSLIB_NS(
+	char *name_str = dnslib_dname_to_str(qname);
+	debug_dnslib_ns("Trying to find zone with name %s\n", name_str);
+	free(name_str);
+);
+	// find zone in which to search for the name
+	dnslib_zone_t *zone = dnslib_zonedb_find_zone(zonedb, qname);
+
+	// if no zone found, return NotAuth
+	if (zone == NULL) {
+		debug_dnslib_ns("No zone found.\n");
+		dnslib_response2_set_rcode(xfr->response, DNSLIB_RCODE_NOTAUTH);
+		ns_axfr_send_and_clear(xfr);
+		return DNSLIB_ERROR;
 	}
 
-	// Get pointer to the zone database
+DEBUG_DNSLIB_NS(
+	char *name_str2 = dnslib_dname_to_str(zone->contents->apex->owner);
+	debug_dnslib_ns("Found zone for QNAME %s\n", name_str2);
+	free(name_str2);
+);
+	xfr->zone = zone;
+	
+	return DNSLIB_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_ns_xfr_send_error(dnslib_ns_xfr_t *xfr, dnslib_rcode_t rcode)
+{
+	dnslib_response2_set_rcode(xfr->response, rcode);
+	/*! \todo Probably rename the function. */
+	return ns_axfr_send_and_clear(xfr);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_ns_answer_axfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
+{
+	if (xfr == NULL || nameserver == NULL || xfr->zone == NULL) {
+		return DNSLIB_EBADARG;
+	}
+	
 	rcu_read_lock();
-	dnslib_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
+	
+	// take the contents and answer from them
+	dnslib_zone_contents_t *contents = dnslib_zone_get_contents(xfr->zone);
 
-	ret = ns_axfr(zonedb, xfr);
+	int ret = ns_axfr_from_zone(contents, xfr);
 
 	/*! \todo Somehow distinguish when it makes sense to send the SERVFAIL
 	 *        and when it does not. E.g. if there was problem in sending
@@ -2864,12 +2652,12 @@ int dnslib_ns_answer_axfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
 	if (ret < 0) {
 		debug_dnslib_ns("AXFR failed, sending SERVFAIL.\n");
 		// now only one type of error (SERVFAIL), later maybe more
-		size_t real_size;
 		/*! \todo xfr->wire is not NULL, will fail on assert! */
 		dnslib_ns_error_response(nameserver, xfr->query->header.id,
-				  DNSLIB_RCODE_SERVFAIL, xfr->wire,
-		                  &real_size);
-		ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, real_size);
+		                         DNSLIB_RCODE_SERVFAIL, xfr->wire,
+		                         &xfr->wire_size);
+		ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, 
+		                xfr->wire_size);
 	} else if (ret > 0) {
 		ret = DNSLIB_ERROR;
 	}
@@ -2885,95 +2673,35 @@ int dnslib_ns_answer_axfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
 
 int dnslib_ns_answer_ixfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
 {
-	debug_dnslib_ns("ns_answer_ixfr()\n");
-
-	if (nameserver == NULL || xfr == NULL) {
+	if (nameserver == NULL || xfr == NULL || xfr->zone == NULL
+	    || xfr->response == NULL) {
 		return DNSLIB_EBADARG;
 	}
-
-	debug_dnslib_ns("ns_answer_ixfr(): implement me\n");
-
-	// initialize response packet structure
-	dnslib_packet_t *response = dnslib_packet_new(
-	                               DNSLIB_PACKET_PREALLOC_RESPONSE);
-	if (response == NULL) {
-		debug_dnslib_ns("Failed to create packet structure.\n");
-		/*! \todo xfr->wire is not NULL, will fail on assert! */
-		dnslib_ns_error_response(nameserver, xfr->query->header.id,
-		                  DNSLIB_RCODE_SERVFAIL, xfr->wire,
-		                  &xfr->wire_size);
-		/*! \todo Hm, maybe send the packet? ;-) */
-		return DNSLIB_EOK;
-	}
-
-	int ret = dnslib_packet_set_max_size(response, xfr->wire_size);
-
-	if (ret != DNSLIB_EOK) {
-		debug_dnslib_ns("Failed to init response structure.\n");
-		/*! \todo xfr->wire is not NULL, will fail on assert! */
-		dnslib_ns_error_response(nameserver, xfr->query->header.id,
-		                  DNSLIB_RCODE_SERVFAIL, xfr->wire,
-		                  &xfr->wire_size);
-		/*! \todo Hm, maybe send the packet? ;-) */
-		dnslib_packet_free(&response);
-		return DNSLIB_EOK;
-	}
-
-	/*! \todo If TCP is used, more packets may be sent.
-	 *        If UDP is used, only one packet may be sent.
-	 */
-
-	ret = dnslib_response2_init_from_query(response, xfr->query);
-
-	if (ret != DNSLIB_EOK) {
-		debug_dnslib_ns("Failed to init response structure.\n");
-		/*! \todo xfr->wire is not NULL, will fail on assert! */
-		dnslib_ns_error_response(nameserver, xfr->query->header.id,
-		                  DNSLIB_RCODE_SERVFAIL, xfr->wire,
-		                  &xfr->wire_size);
-		/*! \todo Hm, maybe send the packet? ;-) */
-		dnslib_packet_free(&response);
-		return DNSLIB_EOK;
-	}
-
+	
 	// parse rest of the packet (we need the Authority record)
-	ret = dnslib_packet_parse_rest(xfr->query);
+	int ret = dnslib_packet_parse_rest(xfr->query);
 	if (ret != DNSLIB_EOK) {
 		debug_dnslib_ns("Failed to parse rest of the packet.\n");
 
 		/*! \todo Extract this to some function. */
-		dnslib_response2_set_rcode(response, DNSLIB_RCODE_FORMERR);
+		dnslib_response2_set_rcode(xfr->response, DNSLIB_RCODE_FORMERR);
 		uint8_t *wire = NULL;
 		size_t size = 0;
-		ret = dnslib_packet_to_wire(response, &wire, &size);
+		ret = dnslib_packet_to_wire(xfr->response, &wire, &size);
 		if (ret != DNSLIB_EOK) {
-			dnslib_ns_error_response(nameserver, xfr->query->header.id,
-			                  DNSLIB_RCODE_FORMERR, wire, &size);
+			dnslib_ns_error_response(nameserver, 
+			                         xfr->query->header.id,
+			                         DNSLIB_RCODE_FORMERR, wire, 
+			                         &size);
 		}
 
 		ret = xfr->send(xfr->session, &xfr->addr, wire, size);
 
-		dnslib_packet_free(&response);
+		dnslib_packet_free(&xfr->response);
 		return ret;
 	}
-
-	xfr->response = response;
-
-	// set the OPT RR to the response
-	if (dnslib_query_edns_supported(xfr->query)) {
-		ret = dnslib_response2_add_opt(xfr->response,
-		                               nameserver->opt_rr, 0);
-		if (ret != DNSLIB_EOK) {
-			debug_dnslib_ns("Failed to set OPT RR to the response"
-			                  ": %s\n", dnslib_strerror(ret));
-		}
-	}
-
-	// Get pointer to the zone database
-	rcu_read_lock();
-	dnslib_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
-
-	ret = ns_ixfr(zonedb, xfr);
+	
+	ret = ns_ixfr(xfr);
 
 	/*! \todo Somehow distinguish when it makes sense to send the SERVFAIL
 	 *        and when it does not. E.g. if there was problem in sending
@@ -2984,13 +2712,15 @@ int dnslib_ns_answer_ixfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
 		// now only one type of error (SERVFAIL), later maybe more
 
 		/*! \todo Extract this to some function. */
-		dnslib_response2_set_rcode(response, DNSLIB_RCODE_SERVFAIL);
+		dnslib_response2_set_rcode(xfr->response, DNSLIB_RCODE_SERVFAIL);
 		uint8_t *wire = NULL;
 		size_t size = 0;
-		ret = dnslib_packet_to_wire(response, &wire, &size);
+		ret = dnslib_packet_to_wire(xfr->response, &wire, &size);
 		if (ret != DNSLIB_EOK) {
-			dnslib_ns_error_response(nameserver, xfr->query->header.id,
-			                  DNSLIB_RCODE_SERVFAIL, wire, &size);
+			dnslib_ns_error_response(nameserver, 
+			                         xfr->query->header.id,
+			                         DNSLIB_RCODE_SERVFAIL, wire, 
+			                         &size);
 		}
 
 		ret = xfr->send(xfr->session, &xfr->addr, wire, size);
@@ -2998,339 +2728,11 @@ int dnslib_ns_answer_ixfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
 		ret = DNSLIB_ERROR;
 	}
 
-	rcu_read_unlock();
-
 	dnslib_packet_free(&xfr->response);
 
 	return ret;
 }
 
-int dnslib_ns_process_response(dnslib_nameserver_t *nameserver, sockaddr_t *from,
-                        dnslib_packet_t *packet, uint8_t *response_wire,
-                        size_t *rsize)
-{
-	if (!packet || !rsize) {
-		return DNSLIB_EBADARG;
-	}
-
-	/*! \todo Handle SOA query response, cancel EXPIRE timer
-	 *        and start AXFR transfer if needed.
-	 *        Reset REFRESH timer on finish.
-	 */
-	if (dnslib_packet_qtype(packet) == DNSLIB_RRTYPE_SOA) {
-
-		/* No response. */
-		*rsize = 0;
-
-		/* Find matching zone and ID. */
-		const dnslib_dname_t *zone_name = dnslib_packet_qname(packet);
-		dnslib_zone_t *zone = dnslib_zonedb_find_zone(
-					nameserver->zone_db,
-					zone_name);
-		if (!zone) {
-			return DNSLIB_ENOZONE;
-		}
-		if (!dnslib_zone_data(zone)) {
-			return DNSLIB_ENOZONE;
-		}
-
-		/* Match ID against awaited. */
-		zonedata_t *zd = (zonedata_t *)dnslib_zone_data(zone);
-		uint16_t pkt_id = dnslib_packet_id(packet);
-		if ((int)pkt_id != zd->xfr_in.next_id) {
-			return DNSLIB_ERROR;
-		}
-
-		/* Cancel EXPIRE timer. */
-		evsched_t *sched = nameserver->server->sched;
-		event_t *expire_ev = zd->xfr_in.expire;
-		if (expire_ev) {
-			evsched_cancel(sched, expire_ev);
-			evsched_event_free(sched, expire_ev);
-			zd->xfr_in.expire = 0;
-		}
-
-		/* Cancel REFRESH/RETRY timer. */
-		event_t *refresh_ev = zd->xfr_in.timer;
-		if (refresh_ev) {
-			debug_dnslib_ns("zone: canceling REFRESH timer\n");
-			evsched_cancel(sched, refresh_ev);
-		}
-
-		/* Get zone contents. */
-		rcu_read_lock();
-		const dnslib_zone_contents_t *contents =
-				dnslib_zone_contents(zone);
-
-		/* Check SOA SERIAL. */
-		if (xfrin_transfer_needed(contents, packet) < 1) {
-
-			/* Reinstall REFRESH timer. */
-			uint32_t ref_tmr = 0;
-
-			/* Retrieve SOA RDATA. */
-			const dnslib_rrset_t *soa_rrs = 0;
-			const dnslib_rdata_t *soa_rr = 0;
-			soa_rrs = dnslib_node_rrset(
-			             dnslib_zone_contents_apex(contents),
-			             DNSLIB_RRTYPE_SOA);
-			soa_rr = dnslib_rrset_rdata(soa_rrs);
-			ref_tmr = dnslib_rdata_soa_refresh(soa_rr);
-			ref_tmr *= 1000; /* Convert to miliseconds. */
-
-			debug_dnslib_ns("zone: reinstalling REFRESH timer (%u ms)\n",
-				ref_tmr);
-
-			evsched_schedule(sched, refresh_ev, ref_tmr);
-			rcu_read_unlock();
-			return DNSLIB_EOK;
-		}
-
-		/* Prepare XFR client transfer. */
-		dnslib_ns_xfr_t xfr_req;
-		memset(&xfr_req, 0, sizeof(dnslib_ns_xfr_t));
-		memcpy(&xfr_req.addr, from, sizeof(sockaddr_t));
-		xfr_req.data = (void *)zone;
-		xfr_req.send = ns_send_cb;
-
-		/* Select transfer method. */
-		xfr_req.type = dnslib_ns_transfer_to_use(nameserver, contents);
-
-		/* Unlock zone contents. */
-		rcu_read_unlock();
-
-		/* Enqueue XFR request. */
-		return xfr_request(nameserver->server->xfr_h, &xfr_req);
-	}
-
-
-	return DNSLIB_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-int dnslib_ns_process_notify(dnslib_nameserver_t *nameserver, sockaddr_t *from,
-                      dnslib_packet_t *packet, uint8_t *response_wire,
-                      size_t *rsize)
-{
-	if (!packet || !rsize) {
-		return DNSLIB_EBADARG;
-	}
-
-	/* Assert no response size. */
-	*rsize = 0;
-
-	/* Find matching zone. */
-	const dnslib_dname_t *zone_name = dnslib_packet_qname(packet);
-	dnslib_zone_t *zone = dnslib_zonedb_find_zone(nameserver->zone_db,
-	                                              zone_name);
-	if (!zone) {
-		return DNSLIB_ENOZONE;
-	}
-	if (!dnslib_zone_data(zone)) {
-		return DNSLIB_ENOZONE;
-	}
-
-	/* Match ID against awaited. */
-	zonedata_t *zd = (zonedata_t *)dnslib_zone_data(zone);
-	uint16_t pkt_id = dnslib_packet_id(packet);
-	notify_ev_t *ev = 0, *match = 0;
-	WALK_LIST(ev, zd->notify_pending) {
-		if ((int)pkt_id == ev->msgid) {
-			match = ev;
-			break;
-		}
-	}
-
-	/* Found waiting NOTIFY query? */
-	if (!match) {
-		debug_dnslib_ns("notify: no pending NOTIFY query found for ID=%u\n",
-			 pkt_id);
-		return DNSLIB_ERROR;
-	}
-
-	/* Cancel RETRY timer, NOTIFY is now finished. */
-	evsched_t *sched = nameserver->server->sched;
-	if (match->timer) {
-		evsched_cancel(sched, match->timer);
-		evsched_event_free(sched, match->timer);
-		match->timer = 0;
-		rem_node(&match->n);
-		free(match);
-	}
-
-	debug_dnslib_ns("notify: received response for pending NOTIFY query ID=%u\n",
-		 pkt_id);
-
-	return DNSLIB_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int ns_find_zone_for_xfr(dnslib_ns_xfr_t *xfr, const char **zonefile,
-                                const char **zonedb)
-{
-	// find the zone file name and zone db file name for the zone
-	conf_t *cnf = conf();
-	node *n = NULL;
-	WALK_LIST(n, cnf->zones) {
-		conf_zone_t *zone_conf = (conf_zone_t *)n;
-		dnslib_dname_t *zone_name = dnslib_dname_new_from_str(
-			zone_conf->name, strlen(zone_conf->name), NULL);
-		if (zone_name == NULL) {
-			return DNSLIB_ENOMEM;
-		}
-
-		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) {
-			// found the right zone
-			*zonefile = zone_conf->file;
-			*zonedb = zone_conf->db;
-			return DNSLIB_EOK;
-		}
-	}
-
-	char *name = dnslib_dname_to_str(dnslib_node_owner(
-	                 dnslib_zone_contents_apex(xfr->zone)));
-	debug_dnslib_ns("No zone found for the zone received by transfer "
-	                 "(%s).\n", name);
-	free(name);
-
-	return DNSLIB_ENOZONE;	/*! \todo OK error code? */
-}
-
-/*----------------------------------------------------------------------------*/
-
-static char *ns_find_free_filename(const char *old_name)
-{
-	// find zone name not present on the disk
-	int free_name = 0;
-	size_t name_size = strlen(old_name);
-
-	char *new_name = malloc(name_size + 3);
-	if (new_name == NULL) {
-		return NULL;
-	}
-	memcpy(new_name, old_name, name_size);
-	new_name[name_size] = '.';
-	new_name[name_size + 2] = 0;
-
-	debug_dnslib_ns("Finding free name for the zone file.\n");
-	int c = 48;
-	FILE *file;
-	while (!free_name && c < 58) {
-		new_name[name_size + 1] = c;
-		debug_dnslib_ns("Trying file name %s\n", new_name);
-		if ((file = fopen(new_name, "r")) != NULL) {
-			fclose(file);
-			++c;
-		} else {
-			free_name = 1;
-		}
-	}
-
-	if (free_name) {
-		return new_name;
-	} else {
-		free(new_name);
-		return NULL;
-	}
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int ns_dump_xfr_zone_text(dnslib_ns_xfr_t *xfr, const char *zonefile)
-{
-	assert(xfr != NULL && xfr->zone != NULL && zonefile != NULL);
-
-	char *new_zonefile = ns_find_free_filename(zonefile);
-
-	if (new_zonefile == NULL) {
-		debug_dnslib_ns("Failed to find free filename for temporary "
-		                 "storage of the zone text file.\n");
-		return DNSLIB_ERROR;	/*! \todo New error code? */
-	}
-
-	int rc = zone_dump_text(xfr->zone, new_zonefile);
-
-	if (rc != DNSLIB_EOK) {
-		debug_dnslib_ns("Failed to save the zone to text zone file %s."
-		                 "\n", new_zonefile);
-		free(new_zonefile);
-		return DNSLIB_ERROR;
-	}
-
-	// if successful, replace the old file with the new one
-	// TODO
-
-	free(new_zonefile);
-	return DNSLIB_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int ns_dump_xfr_zone_binary(dnslib_ns_xfr_t *xfr, const char *zonedb,
-                                   const char *zonefile)
-{
-	assert(xfr != NULL && xfr->zone != NULL && zonedb != NULL);
-
-	char *new_zonedb = ns_find_free_filename(zonedb);
-
-	if (new_zonedb == NULL) {
-		debug_dnslib_ns("Failed to find free filename for temporary "
-		                 "storage of the zone binary file.\n");
-		return DNSLIB_ERROR;	/*! \todo New error code? */
-	}
-
-	int rc = dnslib_zdump_binary(xfr->zone, new_zonedb, 0, zonefile);
-
-	if (rc != DNSLIB_EOK) {
-		debug_dnslib_ns("Failed to save the zone to binary zone db %s."
-		                 "\n", new_zonedb);
-		free(new_zonedb);
-		return DNSLIB_ERROR;
-	}
-
-	// if successful, replace the old file with the new one
-	// TODO
-
-	free(new_zonedb);
-	return DNSLIB_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int ns_save_zone(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
-{
-	assert(nameserver != NULL && xfr != NULL && xfr->zone != NULL
-	       && dnslib_zone_contents_apex(xfr->zone) != NULL);
-
-	const char *zonefile = NULL;
-	const char *zonedb = NULL;
-
-	int ret = ns_find_zone_for_xfr(xfr, &zonefile, &zonedb);
-	if (ret != DNSLIB_EOK) {
-		return ret;
-	}
-
-	assert(zonefile != NULL && zonedb != NULL);
-
-	// dump the zone into text zone file
-	ret = ns_dump_xfr_zone_text(xfr, zonefile);
-	if (ret != DNSLIB_EOK) {
-		return ret;
-	}
-	// dump the zone into binary db file
-	ret = ns_dump_xfr_zone_binary(xfr, zonedb, zonefile);
-
-	return ret;
-}
-
 /*----------------------------------------------------------------------------*/
 
 int dnslib_ns_process_axfrin(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
@@ -3344,7 +2746,7 @@ int dnslib_ns_process_axfrin(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *x
 	debug_dnslib_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_dnslib_ns("ns_process_axfrin: AXFR finished, zone created.\n");
@@ -3352,40 +2754,36 @@ int dnslib_ns_process_axfrin(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *x
 		 * Adjust zone so that node count is set properly and nodes are
 		 * marked authoritative / delegation point.
 		 */
-		xfr->zone = (dnslib_zone_contents_t *)xfr->data;
+		dnslib_zone_contents_t *zone = 
+				(dnslib_zone_contents_t *)xfr->data;
 
 		debug_dnslib_ns("ns_process_axfrin: adjusting zone.\n");
-		dnslib_zone_contents_adjust_dnames(xfr->zone);
+		dnslib_zone_contents_adjust_dnames(zone);
 
 		/* Create and fill hash table */
 		debug_dnslib_ns("ns_process_axfrin: filling hash table.\n");
-		int rc = dnslib_zone_contents_create_and_fill_hash_table(
-				xfr->zone);
+		int rc = dnslib_zone_contents_create_and_fill_hash_table(zone);
 		if (rc != DNSLIB_EOK) {
 			return DNSLIB_ERROR;	// TODO: change error code
 		}
 
-		dnslib_zone_contents_dump(xfr->zone, 0);
-		debug_dnslib_ns("AXFR finished. Saving to zone file.\n");
-
-		// save the zone to the disk
-		rc = ns_save_zone(nameserver, xfr);
-		if (rc != DNSLIB_EOK) {
-			debug_dnslib_ns("Freeing created zone: %p.\n", xfr->zone);
-			dnslib_zone_contents_deep_free(&xfr->zone);
-			debug_dnslib_ns("%p.\n", xfr->zone);
-			return rc;
-		}
-		return DNSLIB_EOK;
-	} else {
-		return ret;
+		dnslib_zone_contents_dump(zone, 0);
 	}
+	
+	return ret;
 }
 
 /*----------------------------------------------------------------------------*/
 
-int dnslib_ns_switch_zone(dnslib_nameserver_t *nameserver, dnslib_zone_contents_t *zone)
+int dnslib_ns_switch_zone(dnslib_nameserver_t *nameserver, 
+                          dnslib_ns_xfr_t *xfr)
 {
+	if (xfr == NULL || nameserver == NULL || xfr->data == NULL) {
+		return DNSLIB_EBADARG;
+	}
+	
+	dnslib_zone_contents_t *zone = (dnslib_zone_contents_t *)xfr->data;
+	
 	debug_dnslib_ns("Replacing zone by new one: %p\n", zone);
 
 	// find the zone in the zone db
@@ -3509,15 +2907,6 @@ int dnslib_ns_process_ixfrin(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *x
 
 /*----------------------------------------------------------------------------*/
 
-dnslib_ns_xfr_type_t dnslib_ns_transfer_to_use(dnslib_nameserver_t *nameserver,
-                                 const dnslib_zone_contents_t *zone)
-{
-	/*! \todo Implement. */
-	return NS_XFR_TYPE_AIN;
-}
-
-/*----------------------------------------------------------------------------*/
-
 void dnslib_ns_destroy(dnslib_nameserver_t **nameserver)
 {
 	synchronize_rcu();
@@ -3533,28 +2922,3 @@ void dnslib_ns_destroy(dnslib_nameserver_t **nameserver)
 	free(*nameserver);
 	*nameserver = NULL;
 }
-
-/*----------------------------------------------------------------------------*/
-
-int dnslib_ns_conf_hook(const struct conf_t *conf, void *data)
-{
-	dnslib_nameserver_t *ns = (dnslib_nameserver_t *)data;
-	debug_dnslib_ns("Event: reconfiguring name server.\n");
-
-	dnslib_zonedb_t *old_db = 0;
-
-	int ret = zones_update_db_from_config(conf, ns, &old_db);
-	if (ret != DNSLIB_EOK) {
-		return ret;
-	}
-	// Wait until all readers finish with reading the zones.
-	synchronize_rcu();
-
-	debug_dnslib_ns("Nameserver's zone db: %p, old db: %p\n", ns->zone_db, old_db);
-
-	// Delete all deprecated zones and delete the old database.
-	dnslib_zonedb_deep_free(&old_db);
-
-	return DNSLIB_EOK;
-}
-
diff --git a/src/knot/server/name-server.h b/src/knot/server/name-server.h
index 5589cb9f2..e8c79f568 100644
--- a/src/knot/server/name-server.h
+++ b/src/knot/server/name-server.h
@@ -49,6 +49,8 @@ typedef struct dnslib_nameserver {
 	uint8_t *err_response;    /*!< Prepared generic error response. */
 	size_t err_resp_size;     /*!< Size of the prepared error response. */
 	dnslib_opt_rr_t *opt_rr;  /*!< OPT RR with the server's EDNS0 info. */
+	
+	/*! \todo REMOVE */
 	struct server_t *server;  /*!< Pointer to server. */
 } dnslib_nameserver_t;
 
@@ -67,17 +69,9 @@ typedef struct dnslib_ns_xfr {
 	uint8_t *wire;
 	size_t wire_size;
 	void *data;
-	dnslib_zone_contents_t *zone;
+	dnslib_zone_t *zone;
 } dnslib_ns_xfr_t;
 
-/*! \todo Document me. */
-typedef enum dnslib_ns_xfr_type_t {
-	NS_XFR_TYPE_AIN,  /*!< AXFR-IN request (start transfer). */
-	NS_XFR_TYPE_AOUT, /*!< AXFR-OUT request (incoming transfer). */
-	NS_XFR_TYPE_IIN,  /*!< IXFR-IN request (start transfer). */
-	NS_XFR_TYPE_IOUT  /*!< IXFR-OUT request (incoming transfer). */
-} dnslib_ns_xfr_type_t;
-
 /*----------------------------------------------------------------------------*/
 /*!
  * \brief Allocates and initializes the name server structure.
@@ -188,8 +182,13 @@ int dnslib_ns_answer_normal(dnslib_nameserver_t *nameserver, dnslib_packet_t *qu
  * \retval KNOT_EACCES sender is not authorized to request NOTIFY.
  * \retval KNOT_EMALF if an error occured and the response is not valid.
  */
-int dnslib_ns_answer_notify(dnslib_nameserver_t *nameserver, dnslib_packet_t *query,
-		     sockaddr_t *from, uint8_t *response_wire, size_t *rsize);
+//int dnslib_ns_answer_notify(dnslib_nameserver_t *nameserver, 
+//                            dnslib_packet_t *query, uint8_t *response_wire,
+//                            size_t *rsize, const dnslib_zone_t **zone);
+
+int dnslib_ns_init_xfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr);
+
+int dnslib_ns_xfr_send_error(dnslib_ns_xfr_t *xfr, dnslib_rcode_t rcode);
 
 /*!
  * \brief Processes an AXFR query.
@@ -224,41 +223,15 @@ int dnslib_ns_answer_axfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr)
  */
 int dnslib_ns_answer_ixfr(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr);
 
-/*!
- * \brief Processes normal response packet.
- *
- * \param nameserver Name server structure to provide the needed data.
- * \param from Address of the response sender.
- * \param packet Parsed response packet.
- * \param response_wire Place for the response in wire format.
- * \param rsize Input: maximum acceptable size of the response. Output: real
- *              size of the response.
- *
- * \retval KNOT_EOK if a valid response was created.
- * \retval KNOT_EINVAL on invalid parameters or packet.
- * \retval KNOT_EMALF if an error occured and the response is not valid.
- */
-int dnslib_ns_process_response(dnslib_nameserver_t *nameserver, sockaddr_t *from,
-			dnslib_packet_t *packet, uint8_t *response_wire,
-			size_t *rsize);
 
-/*!
- * \brief Processes NOTIFY response packet.
- *
- * \param nameserver Name server structure to provide the needed data.
- * \param from Address of the response sender.
- * \param packet Parsed response packet.
- * \param response_wire Place for the response in wire format.
- * \param rsize Input: maximum acceptable size of the response. Output: real
- *              size of the response.
- *
- * \retval KNOT_EOK if a valid response was created.
- * \retval KNOT_EINVAL on invalid parameters or packet.
- * \retval KNOT_EMALF if an error occured and the response is not valid.
- */
-int dnslib_ns_process_notify(dnslib_nameserver_t *nameserver, sockaddr_t *from,
-		      dnslib_packet_t *packet, uint8_t *response_wire,
-		      size_t *rsize);
+//int dnslib_ns_process_response(dnslib_nameserver_t *nameserver, sockaddr_t *from,
+//			dnslib_packet_t *packet, uint8_t *response_wire,
+//			size_t *rsize);
+
+
+//int dnslib_ns_process_notify(dnslib_nameserver_t *nameserver, sockaddr_t *from,
+//		      dnslib_packet_t *packet, uint8_t *response_wire,
+//		      size_t *rsize);
 
 /*!
  * \brief Processes an AXFR-IN packet.
@@ -268,9 +241,11 @@ int dnslib_ns_process_notify(dnslib_nameserver_t *nameserver, sockaddr_t *from,
  *
  * \todo Document me.
  */
-int dnslib_ns_process_axfrin(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr);
+int dnslib_ns_process_axfrin(dnslib_nameserver_t *nameserver, 
+                             dnslib_ns_xfr_t *xfr);
 
-int dnslib_ns_switch_zone(dnslib_nameserver_t *nameserver, dnslib_zone_contents_t *zone);
+int dnslib_ns_switch_zone(dnslib_nameserver_t *nameserver, 
+                          dnslib_ns_xfr_t *xfr);
 
 /*!
  * \brief Processes an IXFR-IN packet.
@@ -280,7 +255,8 @@ int dnslib_ns_switch_zone(dnslib_nameserver_t *nameserver, dnslib_zone_contents_
  *
  * \todo Document me.
  */
-int dnslib_ns_process_ixfrin(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *xfr);
+int dnslib_ns_process_ixfrin(dnslib_nameserver_t *nameserver, 
+                             dnslib_ns_xfr_t *xfr);
 
 /*!
  * \brief Decides what type of transfer should be used to update the given zone.
@@ -290,8 +266,8 @@ int dnslib_ns_process_ixfrin(dnslib_nameserver_t *nameserver, dnslib_ns_xfr_t *x
  *
  * \retval
  */
-dnslib_ns_xfr_type_t dnslib_ns_transfer_to_use(dnslib_nameserver_t *nameserver,
-                                 const dnslib_zone_contents_t *zone);
+/*xfr_type_t dnslib_ns_transfer_to_use(dnslib_nameserver_t *nameserver,
+                                     const dnslib_zone_contents_t *zone);*/
 
 /*!
  * \brief Properly destroys the name server structure.
@@ -312,7 +288,7 @@ void dnslib_ns_destroy(dnslib_nameserver_t **nameserver);
  * \retval KNOT_EINVAL
  * \retval KNOT_ERROR
  */
-int dnslib_ns_conf_hook(const struct conf_t *conf, void *data);
+//int dnslib_ns_conf_hook(const struct conf_t *conf, void *data);
 
 
 #endif /* _KNOT_NAME_SERVER_H_ */
diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c
index febc91144..55ba30615 100644
--- a/src/knot/server/notify.c
+++ b/src/knot/server/notify.c
@@ -12,6 +12,11 @@
 #include "dnslib/zonedb.h"
 #include "dnslib/dnslib-common.h"
 #include "dnslib/error.h"
+#include "knot/server/zones.h"
+#include "common/acl.h"
+#include "common/evsched.h"
+#include "knot/other/debug.h"
+#include "knot/server/server.h"
 
 /*----------------------------------------------------------------------------*/
 /* Non-API functions                                                          */
@@ -143,23 +148,71 @@ int notify_create_request(const dnslib_zone_contents_t *zone, uint8_t *buffer,
 
 /*----------------------------------------------------------------------------*/
 
-int notify_process_request(dnslib_packet_t *notify,
-                           dnslib_zonedb_t *zonedb,
-                           const dnslib_zone_contents_t **zone,
+static int notify_check_and_schedule(const dnslib_nameserver_t *nameserver,
+                                     const dnslib_zone_t *zone,
+                                     sockaddr_t *from)
+{
+	if (zone == NULL || from == NULL || dnslib_zone_data(zone) == NULL) {
+		return KNOT_EINVAL;
+	}
+	
+	/* Check ACL for notify-in. */
+	zonedata_t *zd = (zonedata_t *)dnslib_zone_data(zone);
+	if (from) {
+		if (acl_match(zd->notify_in, from) == ACL_DENY) {
+			/* rfc1996: Ignore request and report incident. */
+			char straddr[SOCKADDR_STRLEN];
+			sockaddr_tostr(from, straddr, sizeof(straddr));
+			debug_notify("Unauthorized NOTIFY request "
+			                 "from %s:%d.\n",
+			                 straddr, sockaddr_portnum(from));
+			return DNSLIB_ERROR;
+		} else {
+			debug_notify("notify: authorized NOTIFY query.\n");
+		}
+	}
+
+	/*! \todo Packet may contain updated RRs. */
+
+	/* Cancel EXPIRE timer. */
+	evsched_t *sched = nameserver->server->sched;
+	event_t *expire_ev = zd->xfr_in.expire;
+	if (expire_ev) {
+		debug_notify("notify: canceling EXPIRE timer\n");
+		evsched_cancel(sched, expire_ev);
+		evsched_event_free(sched, expire_ev);
+		zd->xfr_in.expire = 0;
+	}
+
+	/* Cancel REFRESH/RETRY timer. */
+	event_t *refresh_ev = zd->xfr_in.timer;
+	if (refresh_ev) {
+		debug_notify("notify: canceling REFRESH timer for XFRIN\n");
+		evsched_cancel(sched, refresh_ev);
+
+		/* Set REFRESH timer for now. */
+		evsched_schedule(sched, refresh_ev, 0);
+	}
+	
+	return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int notify_process_request(const dnslib_nameserver_t *nameserver,
+                           dnslib_packet_t *notify,
+                           sockaddr_t *from,
                            uint8_t *buffer, size_t *size)
 {
 	/*! \todo Most of this function is identical to xfrin_transfer_needed()
 	 *        - it will be fine to merge the code somehow.
 	 */
 
-	if (notify == NULL || zone == NULL || buffer == NULL || size == NULL) {
+	if (notify == NULL || nameserver == NULL || buffer == NULL 
+	    || size == NULL || from == NULL) {
 		return KNOT_EINVAL;
 	}
 
-	*zone = NULL;
-
-	//debug_ns("Notify request - parsed: %zu, total wire size: %zu\n",
-	//         notify->parsed, notify->size);
 	int ret;
 
 	if (notify->parsed < notify->size) {
@@ -177,31 +230,74 @@ int notify_process_request(dnslib_packet_t *notify,
 
 	// find the zone
 	const dnslib_dname_t *qname = dnslib_packet_qname(notify);
-	const dnslib_zone_t *z = dnslib_zonedb_find_zone_for_name(zonedb, qname);
+	const dnslib_zone_t *z = dnslib_zonedb_find_zone_for_name(
+			nameserver->zone_db, qname);
 	if (z == NULL) {
 		return KNOT_ERROR;	/*! \todo Some other error. */
 	}
 
-	*zone = dnslib_zone_contents(z);
-	if (*zone == NULL) {
-		return KNOT_ERROR;	/*! \todo Some other error. */
-	}
-
-	/*! \todo Merge this with ns_answer_notify().
-	 *        According to RFC 1996, slave should
-	 *        behave as if the REFRESH timer has expired
-	 *        i.e. it should send SOA query to the master.
-	 *        No further processing after this comment is needed.
-	 */
-
+	notify_check_and_schedule(nameserver, z, from);
+	
 	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
 
-int notify_process_response(const dnslib_zone_contents_t *zone,
-                            dnslib_packet_t *notify)
+int notify_process_response(const dnslib_nameserver_t *nameserver,
+                            dnslib_packet_t *notify,
+                            sockaddr_t *from,
+                            uint8_t *buffer, size_t *size)
 {
-	return KNOT_ENOTSUP;
+	if (nameserver == NULL || notify == NULL || from == NULL 
+	    || buffer == NULL || size == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	/* Assert no response size. */
+	*size = 0;
+
+	/* Find matching zone. */
+	const dnslib_dname_t *zone_name = dnslib_packet_qname(notify);
+	dnslib_zone_t *zone = dnslib_zonedb_find_zone(nameserver->zone_db,
+	                                              zone_name);
+	if (!zone) {
+		return KNOT_ENOENT;
+	}
+	if (!dnslib_zone_data(zone)) {
+		return KNOT_ENOENT;
+	}
+
+	/* Match ID against awaited. */
+	zonedata_t *zd = (zonedata_t *)dnslib_zone_data(zone);
+	uint16_t pkt_id = dnslib_packet_id(notify);
+	notify_ev_t *ev = 0, *match = 0;
+	WALK_LIST(ev, zd->notify_pending) {
+		if ((int)pkt_id == ev->msgid) {
+			match = ev;
+			break;
+		}
+	}
+
+	/* Found waiting NOTIFY query? */
+	if (!match) {
+		debug_notify("notify: no pending NOTIFY query found for ID=%u\n",
+			 pkt_id);
+		return KNOT_ERROR;
+	}
+
+	/* Cancel RETRY timer, NOTIFY is now finished. */
+	evsched_t *sched = nameserver->server->sched;
+	if (match->timer) {
+		evsched_cancel(sched, match->timer);
+		evsched_event_free(sched, match->timer);
+		match->timer = 0;
+		rem_node(&match->n);
+		free(match);
+	}
+
+	debug_notify("notify: received response for pending NOTIFY query ID=%u\n",
+		 pkt_id);
+
+	return KNOT_EOK;
 }
 
diff --git a/src/knot/server/notify.h b/src/knot/server/notify.h
index 9b005264a..fd5b55dbc 100644
--- a/src/knot/server/notify.h
+++ b/src/knot/server/notify.h
@@ -20,6 +20,7 @@
 #include "dnslib/zonedb.h"
 #include "common/lists.h"
 #include "common/sockaddr.h"
+#include "knot/server/name-server.h"
 
 /*!
  * \brief Pending NOTIFY event.
@@ -50,6 +51,20 @@ typedef struct notify_ev_t {
 int notify_create_request(const dnslib_zone_contents_t *zone, uint8_t *buffer,
                           size_t *size);
 
+/*!
+ * \brief Processes normal response packet.
+ *
+ * \param nameserver Name server structure to provide the needed data.
+ * \param from Address of the response sender.
+ * \param packet Parsed response packet.
+ * \param response_wire Place for the response in wire format.
+ * \param rsize Input: maximum acceptable size of the response. Output: real
+ *              size of the response.
+ *
+ * \retval KNOT_EOK if a valid response was created.
+ * \retval KNOT_EINVAL on invalid parameters or packet.
+ * \retval KNOT_EMALF if an error occured and the response is not valid.
+ */
 /*!
  * \brief Evaluates incoming NOTIFY request and produces a reply.
  *
@@ -66,13 +81,29 @@ int notify_create_request(const dnslib_zone_contents_t *zone, uint8_t *buffer,
  * \retval KNOT_EMALF
  * \retval KNOT_ERROR
  */
-int notify_process_request(dnslib_packet_t *notify,
-                           dnslib_zonedb_t *zonedb,
-                           const dnslib_zone_contents_t **zone,
+int notify_process_request(const dnslib_nameserver_t *nameserver,
+                           dnslib_packet_t *notify,
+                           sockaddr_t *from,
                            uint8_t *buffer, size_t *size);
 
-int notify_process_response(const dnslib_zone_contents_t *zone,
-                            dnslib_packet_t *notify);
+/*!
+ * \brief Processes NOTIFY response packet.
+ *
+ * \param nameserver Name server structure to provide the needed data.
+ * \param from Address of the response sender.
+ * \param packet Parsed response packet.
+ * \param response_wire Place for the response in wire format.
+ * \param rsize Input: maximum acceptable size of the response. Output: real
+ *              size of the response.
+ *
+ * \retval KNOT_EOK if a valid response was created.
+ * \retval KNOT_EINVAL on invalid parameters or packet.
+ * \retval KNOT_EMALF if an error occured and the response is not valid.
+ */
+int notify_process_response(const dnslib_nameserver_t *nameserver,
+                            dnslib_packet_t *notify,
+                            sockaddr_t *from,
+                            uint8_t *buffer, size_t *size);
 
 #endif /* _KNOT_NOTIFY_H_ */
 
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index 45bffe7d8..6855e238d 100644
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -20,6 +20,7 @@
 #include "knot/other/error.h"
 #include "knot/stat/stat.h"
 #include "dnslib/wire.h"
+#include "knot/server/zones.h"
 
 /*! \brief TCP watcher. */
 typedef struct tcp_io_t {
@@ -160,7 +161,7 @@ static void tcp_handle(struct ev_loop *loop, ev_io *w, int revents)
 		break;
 	case DNSLIB_QUERY_AXFR:
 		memset(&xfr, 0, sizeof(dnslib_ns_xfr_t));
-		xfr.type = NS_XFR_TYPE_AOUT;
+		xfr.type = XFR_TYPE_AOUT;
 		xfr.query = packet;
 		xfr.send = xfr_send_cb;
 		xfr.session = w->fd;
@@ -170,7 +171,7 @@ static void tcp_handle(struct ev_loop *loop, ev_io *w, int revents)
 		return;
 	case DNSLIB_QUERY_IXFR:
 		memset(&xfr, 0, sizeof(dnslib_ns_xfr_t));
-		xfr.type = NS_XFR_TYPE_IOUT;
+		xfr.type = XFR_TYPE_IOUT;
 		xfr.query = packet; /* Will be freed after processing. */
 		xfr.send = xfr_send_cb;
 		xfr.session = w->fd;
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
index 6e252cf65..093a4dc1f 100644
--- a/src/knot/server/udp-handler.c
+++ b/src/knot/server/udp-handler.c
@@ -19,6 +19,8 @@
 #include "dnslib/wire.h"
 #include "dnslib/consts.h"
 #include "dnslib/packet.h"
+#include "knot/server/zones.h"
+#include "knot/server/notify.h"
 
 int udp_master(dthread_t *thread)
 {
@@ -141,27 +143,36 @@ int udp_master(dthread_t *thread)
 
 		/* Response types. */
 		case DNSLIB_RESPONSE_NORMAL:
-			res = dnslib_ns_process_response(ns, &addr, packet,
-						  qbuf, &resp_len);
+			res = zones_process_response(ns, &addr, packet,
+			                             qbuf, &resp_len);
+//			res = dnslib_ns_process_response();
 			break;
 		case DNSLIB_RESPONSE_AXFR:
 		case DNSLIB_RESPONSE_IXFR:
 		case DNSLIB_RESPONSE_NOTIFY:
-			res = dnslib_ns_process_notify(ns, &addr, packet,
-						qbuf, &resp_len);
+			res = notify_process_response(ns, packet, &addr,
+			                              qbuf, &resp_len);
 			break;
 
 		/* Query types. */
 		case DNSLIB_QUERY_NORMAL:
-			res = dnslib_ns_answer_normal(ns, packet, qbuf, &resp_len);
+			res = dnslib_ns_answer_normal(ns, packet, qbuf, 
+			                              &resp_len);
 			break;
 		case DNSLIB_QUERY_AXFR:
 		case DNSLIB_QUERY_IXFR:
 			/*! \todo Send error, not available on UDP. */
 			break;
 		case DNSLIB_QUERY_NOTIFY:
-			res = dnslib_ns_answer_notify(ns, packet, &addr,
-					       qbuf, &resp_len);
+			rcu_read_lock();
+//			const dnslib_zone_t *zone = NULL;
+//			res = dnslib_ns_answer_notify(ns, packet, qbuf, 
+//			                              &resp_len, &zone);
+			res = notify_process_request(ns, packet, &addr,
+			                             qbuf, &resp_len);
+//			if (res == DNSLIB_EOK) {
+//				res = zones_notify_schedule(zone, &addr);
+//			}
 			break;
 		case DNSLIB_QUERY_UPDATE:
 			/*! \todo Implement query notify/update. */
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index ba0073931..18382d3b8 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -19,6 +19,8 @@
 #include "knot/server/socket.h"
 #include "knot/server/tcp-handler.h"
 #include "knot/server/xfr-in.h"
+#include "knot/server/zones.h"
+#include "dnslib/error.h"
 
 /*! \brief XFR event wrapper for libev. */
 struct xfr_io_t
@@ -73,10 +75,10 @@ static inline void xfr_client_ev(struct ev_loop *loop, ev_io *w, int revents)
 
 	/* Process incoming packet. */
 	switch(request->type) {
-	case NS_XFR_TYPE_AIN:
+	case XFR_TYPE_AIN:
 		ret = dnslib_ns_process_axfrin(xfr_w->h->ns, request);
 		break;
-	case NS_XFR_TYPE_IIN:
+	case XFR_TYPE_IIN:
 		ret = dnslib_ns_process_ixfrin(xfr_w->h->ns, request);
 		break;
 	default:
@@ -85,15 +87,37 @@ static inline void xfr_client_ev(struct ev_loop *loop, ev_io *w, int revents)
 	}
 
 	/* Check return code for errors. */
-	if (ret != KNOT_EOK) {
+	if (ret < 0) {
+		/*! \todo Log error. */
 		return;
 	}
 
 	/* Check finished zone. */
-	if (request->zone) {
+	if (ret > 0) {
 
+		switch(request->type) {
+		case XFR_TYPE_AIN:
+			ret = zones_save_zone(request);
+			if (ret != KNOT_EOK) {
+				/*! \todo Log error. */
+				return;
+			}
+			ret = dnslib_ns_switch_zone(xfr_w->h->ns, request);
+			if (ret != KNOT_EOK) {
+				/*! \todo Log error. */
+				return;
+			}
+			break;
+		case XFR_TYPE_IIN:
+			/*! \todo save changesets */
+			/*! \todo udpate zone?? */
+			break;
+		default:
+			ret = KNOT_EINVAL;
+			break;
+		}
 		/* Save finished zone and reload. */
-		xfrin_zone_transferred(xfr_w->h->ns, request->zone);
+//		xfrin_zone_transferred(xfr_w->h->ns, request->zone);
 
 		/* Return error code to make TCP client disconnect. */
 		ev_io_stop(loop, (ev_io *)w);
@@ -166,10 +190,10 @@ static inline void xfr_bridge_ev(struct ev_loop *loop, ev_io *w, int revents)
 	ret = KNOT_ERROR;
 	size_t bufsize = req->wire_size;
 	switch(req->type) {
-	case NS_XFR_TYPE_AIN:
+	case XFR_TYPE_AIN:
 		ret = xfrin_create_axfr_query(contents, req->wire, &bufsize);
 		break;
-	case NS_XFR_TYPE_IIN:
+	case XFR_TYPE_IIN:
 		ret = xfrin_create_ixfr_query(contents, req->wire, &bufsize);
 		break;
 	default:
@@ -369,18 +393,53 @@ int xfr_master(dthread_t *thread)
 
 		/* Handle request. */
 		const char *req_type = "";
+		dnslib_rcode_t rcode;
+		
+		rcu_read_lock();
+		
 		switch(xfr.type) {
-		case NS_XFR_TYPE_AOUT:
+		case XFR_TYPE_AOUT:
 			req_type = "axfr-out";
+			
+			ret = dnslib_ns_init_xfr(xfrh->ns, &xfr);
+			if (ret != DNSLIB_EOK) {
+				debug_xfr("xfr_master: failed to init XFR: %s\n",
+				          dnslib_strerror(ret));
+				socket_close(xfr.session);
+			}
+			
+			ret = zones_xfr_check_zone(&xfr, &rcode);
+			if (ret != KNOT_EOK) {
+				dnslib_ns_xfr_send_error(&xfr, rcode);
+				socket_close(xfr.session);
+			}			
+			
 			ret = dnslib_ns_answer_axfr(xfrh->ns, &xfr);
 			dnslib_packet_free(&xfr.query); /* Free query. */
 			debug_xfr("xfr_master: ns_answer_axfr() = %d.\n", ret);
 			if (ret != KNOT_EOK) {
 				socket_close(xfr.session);
 			}
+			
+			rcu_read_unlock();
 			break;
-		case NS_XFR_TYPE_IOUT:
+		case XFR_TYPE_IOUT:
 			req_type = "ixfr-out";
+			
+			ret = dnslib_ns_init_xfr(xfrh->ns, &xfr);
+			if (ret != DNSLIB_EOK) {
+				debug_xfr("xfr_master: failed to init XFR: %s\n",
+				          dnslib_strerror(ret));
+				socket_close(xfr.session);
+			}
+			
+			ret = zones_xfr_check_zone(&xfr, &rcode);
+			if (ret != KNOT_EOK) {
+				dnslib_ns_xfr_send_error(&xfr, rcode);
+				socket_close(xfr.session);
+			}
+			
+			/*! \todo Init XFR, check zone, answer. */
 			ret = dnslib_ns_answer_ixfr(xfrh->ns, &xfr);
 			dnslib_packet_free(&xfr.query); /* Free query. */
 			debug_xfr("xfr_master: ns_answer_ixfr() = %d.\n", ret);
@@ -388,12 +447,12 @@ int xfr_master(dthread_t *thread)
 				socket_close(xfr.session);
 			}
 			break;
-		case NS_XFR_TYPE_AIN:
+		case XFR_TYPE_AIN:
 			req_type = "axfr-in";
 			evqueue_write(xfrh->cq, &xfr, sizeof(dnslib_ns_xfr_t));
 			ret = KNOT_EOK;
 			break;
-		case NS_XFR_TYPE_IIN:
+		case XFR_TYPE_IIN:
 			req_type = "ixfr-in";
 			evqueue_write(xfrh->cq, &xfr, sizeof(dnslib_ns_xfr_t));
 			ret = KNOT_EOK;
diff --git a/src/knot/server/xfr-in.c b/src/knot/server/xfr-in.c
index 2ea1a3f9d..2299b3550 100644
--- a/src/knot/server/xfr-in.c
+++ b/src/knot/server/xfr-in.c
@@ -259,13 +259,13 @@ int xfrin_create_ixfr_query(const dnslib_zone_contents_t *zone, uint8_t *buffer,
 
 /*----------------------------------------------------------------------------*/
 
-int xfrin_zone_transferred(dnslib_nameserver_t *nameserver,
-                           dnslib_zone_contents_t *zone)
-{
-	debug_xfr("Switching zone in nameserver.\n");
-	return dnslib_ns_switch_zone(nameserver, zone);
-	//return KNOT_ENOTSUP;
-}
+//int xfrin_zone_transferred(dnslib_nameserver_t *nameserver,
+//                           dnslib_zone_contents_t *zone)
+//{
+//	debug_xfr("Switching zone in nameserver.\n");
+//	return dnslib_ns_switch_zone(nameserver, zone);
+//	//return KNOT_ENOTSUP;
+//}
 
 /*----------------------------------------------------------------------------*/
 
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 8c360c3a4..65b348079 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -16,6 +16,9 @@
 #include "knot/server/server.h"
 #include "knot/server/xfr-in.h"
 #include "knot/server/zones.h"
+#include "dnslib/error.h"
+#include "dnslib/zone-dump.h"
+#include "knot/server/name-server.h"
 
 /*----------------------------------------------------------------------------*/
 
@@ -958,3 +961,356 @@ int zones_zonefile_sync(dnslib_zone_t *zone)
 
 	return KNOT_EOK;
 }
+
+/*----------------------------------------------------------------------------*/
+
+int zones_xfr_check_zone(dnslib_ns_xfr_t *xfr, dnslib_rcode_t *rcode)
+{
+	if (xfr == NULL || xfr->zone == NULL || rcode == NULL) {
+		*rcode = DNSLIB_RCODE_SERVFAIL;
+		return KNOT_EINVAL;
+	}
+	
+	/* Check zone data. */
+	zonedata_t *zd = (zonedata_t *)xfr->zone->data;
+	if (zd == NULL) {
+		debug_zones("Invalid zone data.\n");
+		*rcode = DNSLIB_RCODE_SERVFAIL;
+		return KNOT_ERROR;
+	}
+
+	// Check xfr-out ACL
+	if (acl_match(zd->xfr_out, &xfr->addr) == ACL_DENY) {
+		debug_zones("Request for AXFR OUT is not authorized.\n");
+		*rcode = DNSLIB_RCODE_REFUSED;
+		return KNOT_ERROR;
+	} else {
+		debug_zones("Authorized AXFR OUT request.\n");
+	}
+	return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Wrapper for TCP send.
+ * \todo Implement generic fd pool properly with callbacks.
+ */
+#include "knot/server/tcp-handler.h"
+static int zones_send_cb(int fd, sockaddr_t *addr, uint8_t *msg, size_t msglen)
+{
+	return tcp_send(fd, msg, msglen);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_process_response(dnslib_nameserver_t *nameserver, 
+                           sockaddr_t *from,
+                           dnslib_packet_t *packet, uint8_t *response_wire,
+                           size_t *rsize)
+{
+	if (!packet || !rsize || nameserver == NULL || from == NULL ||
+	    response_wire == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	/*! \todo Handle SOA query response, cancel EXPIRE timer
+	 *        and start AXFR transfer if needed.
+	 *        Reset REFRESH timer on finish.
+	 */
+	if (dnslib_packet_qtype(packet) == DNSLIB_RRTYPE_SOA) {
+
+		/* No response. */
+		*rsize = 0;
+
+		/* Find matching zone and ID. */
+		const dnslib_dname_t *zone_name = dnslib_packet_qname(packet);
+		/*! \todo Change the access to the zone db. */
+		dnslib_zone_t *zone = dnslib_zonedb_find_zone(
+		                        nameserver->zone_db,
+		                        zone_name);
+		if (!zone) {
+			return KNOT_EINVAL;
+		}
+		if (!dnslib_zone_data(zone)) {
+			return KNOT_EINVAL;
+		}
+
+		/* Match ID against awaited. */
+		zonedata_t *zd = (zonedata_t *)dnslib_zone_data(zone);
+		uint16_t pkt_id = dnslib_packet_id(packet);
+		if ((int)pkt_id != zd->xfr_in.next_id) {
+			return KNOT_ERROR;
+		}
+
+		/* Cancel EXPIRE timer. */
+		evsched_t *sched = nameserver->server->sched;
+		event_t *expire_ev = zd->xfr_in.expire;
+		if (expire_ev) {
+			evsched_cancel(sched, expire_ev);
+			evsched_event_free(sched, expire_ev);
+			zd->xfr_in.expire = 0;
+		}
+
+		/* Cancel REFRESH/RETRY timer. */
+		event_t *refresh_ev = zd->xfr_in.timer;
+		if (refresh_ev) {
+			debug_zones("zone: canceling REFRESH timer\n");
+			evsched_cancel(sched, refresh_ev);
+		}
+
+		/* Get zone contents. */
+		rcu_read_lock();
+		const dnslib_zone_contents_t *contents =
+				dnslib_zone_contents(zone);
+
+		/* Check SOA SERIAL. */
+		if (xfrin_transfer_needed(contents, packet) < 1) {
+
+			/* Reinstall REFRESH timer. */
+			uint32_t ref_tmr = 0;
+
+			/* Retrieve SOA RDATA. */
+			const dnslib_rrset_t *soa_rrs = 0;
+			const dnslib_rdata_t *soa_rr = 0;
+			soa_rrs = dnslib_node_rrset(
+			             dnslib_zone_contents_apex(contents),
+			             DNSLIB_RRTYPE_SOA);
+			soa_rr = dnslib_rrset_rdata(soa_rrs);
+			ref_tmr = dnslib_rdata_soa_refresh(soa_rr);
+			ref_tmr *= 1000; /* Convert to miliseconds. */
+
+			debug_zones("zone: reinstalling REFRESH timer (%u ms)\n",
+				ref_tmr);
+
+			evsched_schedule(sched, refresh_ev, ref_tmr);
+			rcu_read_unlock();
+			return KNOT_EOK;
+		}
+
+		/* Prepare XFR client transfer. */
+		dnslib_ns_xfr_t xfr_req;
+		memset(&xfr_req, 0, sizeof(dnslib_ns_xfr_t));
+		memcpy(&xfr_req.addr, from, sizeof(sockaddr_t));
+		xfr_req.data = (void *)zone;
+		xfr_req.send = zones_send_cb;
+
+		/* Select transfer method. */
+		xfr_req.type = zones_transfer_to_use(contents);
+
+		/* Unlock zone contents. */
+		rcu_read_unlock();
+
+		/* Enqueue XFR request. */
+		return xfr_request(nameserver->server->xfr_h, &xfr_req);
+	}
+
+
+	return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+xfr_type_t zones_transfer_to_use(const dnslib_zone_contents_t *zone)
+{
+	/*! \todo Implement. */
+	return XFR_TYPE_AIN;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_find_zone_for_xfr(const dnslib_zone_contents_t *zone, 
+                                   const char **zonefile, const char **zonedb)
+{
+	// find the zone file name and zone db file name for the zone
+	conf_t *cnf = conf();
+	node *n = NULL;
+	WALK_LIST(n, cnf->zones) {
+		conf_zone_t *zone_conf = (conf_zone_t *)n;
+		dnslib_dname_t *zone_name = dnslib_dname_new_from_str(
+			zone_conf->name, strlen(zone_conf->name), NULL);
+		if (zone_name == NULL) {
+			return KNOT_ENOMEM;
+		}
+
+		int r = dnslib_dname_compare(zone_name, dnslib_node_owner(
+		                              dnslib_zone_contents_apex(zone)));
+
+		/* Directly discard dname, won't be needed. */
+		dnslib_dname_free(&zone_name);
+
+		if (r == 0) {
+			// found the right zone
+			*zonefile = zone_conf->file;
+			*zonedb = zone_conf->db;
+			return KNOT_EOK;
+		}
+	}
+
+	char *name = dnslib_dname_to_str(dnslib_node_owner(
+	                 dnslib_zone_contents_apex(zone)));
+	debug_zones("No zone found for the zone received by transfer "
+	                 "(%s).\n", name);
+	free(name);
+
+	return KNOT_ENOENT;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static char *zones_find_free_filename(const char *old_name)
+{
+	// find zone name not present on the disk
+	int free_name = 0;
+	size_t name_size = strlen(old_name);
+
+	char *new_name = malloc(name_size + 3);
+	if (new_name == NULL) {
+		return NULL;
+	}
+	memcpy(new_name, old_name, name_size);
+	new_name[name_size] = '.';
+	new_name[name_size + 2] = 0;
+
+	debug_dnslib_ns("Finding free name for the zone file.\n");
+	int c = 48;
+	FILE *file;
+	while (!free_name && c < 58) {
+		new_name[name_size + 1] = c;
+		debug_dnslib_ns("Trying file name %s\n", new_name);
+		if ((file = fopen(new_name, "r")) != NULL) {
+			fclose(file);
+			++c;
+		} else {
+			free_name = 1;
+		}
+	}
+
+	if (free_name) {
+		return new_name;
+	} else {
+		free(new_name);
+		return NULL;
+	}
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_dump_xfr_zone_text(dnslib_zone_contents_t *zone, 
+                                    const char *zonefile)
+{
+	assert(zone != NULL && zonefile != NULL);
+
+	char *new_zonefile = zones_find_free_filename(zonefile);
+
+	if (new_zonefile == NULL) {
+		debug_zones("Failed to find free filename for temporary "
+		                 "storage of the zone text file.\n");
+		return KNOT_ERROR;	/*! \todo New error code? */
+	}
+
+	int rc = zone_dump_text(zone, new_zonefile);
+
+	if (rc != KNOT_EOK) {
+		debug_zones("Failed to save the zone to text zone file %s."
+		                 "\n", new_zonefile);
+		free(new_zonefile);
+		return KNOT_ERROR;
+	}
+
+	/*! \todo if successful, replace the old file with the new one */
+
+	free(new_zonefile);
+	return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_dump_xfr_zone_binary(dnslib_zone_contents_t *zone, 
+                                   const char *zonedb,
+                                   const char *zonefile)
+{
+	assert(zone != NULL && zonedb != NULL);
+
+	char *new_zonedb = zones_find_free_filename(zonedb);
+
+	if (new_zonedb == NULL) {
+		debug_zones("Failed to find free filename for temporary "
+		                 "storage of the zone binary file.\n");
+		return KNOT_ERROR;	/*! \todo New error code? */
+	}
+
+	int rc = dnslib_zdump_binary(zone, new_zonedb, 0, zonefile);
+
+	if (rc != DNSLIB_EOK) {
+		debug_zones("Failed to save the zone to binary zone db %s."
+		                 "\n", new_zonedb);
+		free(new_zonedb);
+		return KNOT_ERROR;
+	}
+
+	/*! \todo if successful, replace the old file with the new one */
+
+	free(new_zonedb);
+	return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_save_zone(const dnslib_ns_xfr_t *xfr)
+{
+	if (xfr == NULL || xfr->data == NULL) {
+		return KNOT_EINVAL;
+	}
+	
+	dnslib_zone_contents_t *zone = 
+		(dnslib_zone_contents_t *)xfr->data;
+	
+	const char *zonefile = NULL;
+	const char *zonedb = NULL;
+	
+	int ret = zones_find_zone_for_xfr(zone, &zonefile, &zonedb);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+	
+	assert(zonefile != NULL && zonedb != NULL);
+	
+	// dump the zone into text zone file
+	ret = zones_dump_xfr_zone_text(zone, zonefile);
+	if (ret != KNOT_EOK) {
+		return KNOT_ERROR;
+	}
+	// dump the zone into binary db file
+	ret = ns_dump_xfr_zone_binary(zone, zonedb, zonefile);
+	if (ret != KNOT_EOK) {
+		return KNOT_ERROR;
+	}
+	
+	return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_ns_conf_hook(const struct conf_t *conf, void *data)
+{
+	dnslib_nameserver_t *ns = (dnslib_nameserver_t *)data;
+	debug_zones("Event: reconfiguring name server.\n");
+
+	dnslib_zonedb_t *old_db = 0;
+
+	int ret = zones_update_db_from_config(conf, ns, &old_db);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+	// Wait until all readers finish with reading the zones.
+	synchronize_rcu();
+
+	debug_zones("Nameserver's zone db: %p, old db: %p\n", ns->zone_db,
+	            old_db);
+
+	// Delete all deprecated zones and delete the old database.
+	dnslib_zonedb_deep_free(&old_db);
+
+	return KNOT_EOK;
+}
diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h
index f7436e312..defd29574 100644
--- a/src/knot/server/zones.h
+++ b/src/knot/server/zones.h
@@ -56,6 +56,14 @@ typedef struct zonedata_t
 	uint32_t zonefile_serial;
 } zonedata_t;
 
+/*! \todo Document me. */
+typedef enum xfr_type_t {
+	XFR_TYPE_AIN,  /*!< AXFR-IN request (start transfer). */
+	XFR_TYPE_AOUT, /*!< AXFR-OUT request (incoming transfer). */
+	XFR_TYPE_IIN,  /*!< IXFR-IN request (start transfer). */
+	XFR_TYPE_IOUT  /*!< IXFR-OUT request (incoming transfer). */
+} xfr_type_t;
+
 /*!
  * \brief Update zone database according to configuration.
  *
@@ -97,6 +105,31 @@ int zones_update_db_from_config(const conf_t *conf, dnslib_nameserver_t *ns,
  */
 int zones_zonefile_sync(dnslib_zone_t *zone);
 
+int zones_xfr_check_zone(dnslib_ns_xfr_t *xfr, dnslib_rcode_t *rcode);
+
+int zones_process_response(dnslib_nameserver_t *nameserver, 
+                           sockaddr_t *from,
+                           dnslib_packet_t *packet, uint8_t *response_wire,
+                           size_t *rsize);
+
+xfr_type_t zones_transfer_to_use(const dnslib_zone_contents_t *zone);
+
+int zones_save_zone(const dnslib_ns_xfr_t *xfr);
+
+/*!
+ * \brief Name server config hook.
+ *
+ * Routine for dynamic name server reconfiguration.
+ *
+ * \param conf Current configuration.
+ * \param data Instance of the nameserver structure to update.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL
+ * \retval KNOT_ERROR
+ */
+int zones_ns_conf_hook(const struct conf_t *conf, void *data);
+
 #endif // _KNOT_ZONES_H_
 
 /*! @} */
-- 
GitLab