diff --git a/Knot.files b/Knot.files
index bffc2aa25d6474addc68342434d34105d181c91c..01457e00ce720c3aadff295b06e7f88253fb6c56 100644
--- a/Knot.files
+++ b/Knot.files
@@ -44,12 +44,6 @@ src/common/evsched.c
 src/common/evsched.h
 src/common/fdset.c
 src/common/fdset.h
-src/common/fdset_epoll.c
-src/common/fdset_epoll.h
-src/common/fdset_kqueue.c
-src/common/fdset_kqueue.h
-src/common/fdset_poll.c
-src/common/fdset_poll.h
 src/common/getline.c
 src/common/getline.h
 src/common/hattrie/ahtable.c
diff --git a/configure.ac b/configure.ac
index 9ae64978453ecdaa7d66c867afce058a795f6e06..419e27e816d6061f2bb7c2cbe972708aa91c60ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -302,7 +302,7 @@ AC_TYPE_SSIZE_T
 AC_DEFINE([DSFMT_MEXP], [521], [DSFMT parameters])
 
 # Checks for library functions.
-AC_CHECK_FUNCS([clock_gettime gettimeofday epoll_wait fgetln getline kqueue madvise poll posix_memalign pselect pthread_setaffinity_np regcomp select sendmmsg setgroups])
+AC_CHECK_FUNCS([clock_gettime gettimeofday fgetln getline madvise poll posix_memalign pselect pthread_setaffinity_np regcomp select sendmmsg setgroups])
 
 # Check for cpu_set_t/cpuset_t compatibility
 AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[cpu_set_t set; CPU_ZERO(&set);]])],
diff --git a/src/Makefile.am b/src/Makefile.am
index 191e6cccc5ddd8a55f607f01d89205f2fb7cd803..46fe2928b141eb2f926c31fc9293e8475313f945 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -218,12 +218,6 @@ libknots_la_SOURCES =				\
 	common/prng.c				\
 	common/fdset.h				\
 	common/fdset.c				\
-	common/fdset_poll.h			\
-	common/fdset_poll.c			\
-	common/fdset_kqueue.h			\
-	common/fdset_kqueue.c			\
-	common/fdset_epoll.h			\
-	common/fdset_epoll.c			\
 	common/getline.h			\
 	common/getline.c			\
 	common/log.c				\
diff --git a/src/common/fdset.c b/src/common/fdset.c
index 3b4b75aa73ab770e23eb332e7747cd951fb58cdc..5f93d278ff31992b01c8546592c7a8bf2d23092b 100644
--- a/src/common/fdset.c
+++ b/src/common/fdset.c
@@ -15,12 +15,11 @@
  */
 
 #include <config.h>
-#include <dlfcn.h>
+#include <stdlib.h>
 #include <string.h>
-#include <stdio.h>
 #include <time.h>
-#include <stdlib.h>
 #include "common/fdset.h"
+#include "common.h"
 
 /* Workarounds for clock_gettime() not available on some platforms. */
 #ifdef HAVE_CLOCK_GETTIME
@@ -34,171 +33,137 @@ typedef struct timeval timev_t;
 #error Neither clock_gettime() nor gettimeofday() found. At least one is required.
 #endif
 
-struct fdset_backend_t _fdset_backend = {
-	NULL
-};
+/* Realloc memory or return error (part of fdset_resize). */
+#define MEM_RESIZE(tmp, p, n) \
+	if ((tmp = realloc((p), (n))) == NULL) \
+		return KNOT_ENOMEM; \
+	(p) = tmp;
 
-/*! \brief Set backend implementation. */
-static void fdset_set_backend(struct fdset_backend_t *backend) {
-	memcpy(&_fdset_backend, backend, sizeof(struct fdset_backend_t));
+static int fdset_resize(fdset_t *set, unsigned size)
+{
+	void *tmp = NULL;
+	MEM_RESIZE(tmp, set->ctx, size * sizeof(void*));
+	MEM_RESIZE(tmp, set->pfd, size * sizeof(struct pollfd));
+	MEM_RESIZE(tmp, set->timeout, size * sizeof(timev_t));
+	set->size = size;
+	return KNOT_EOK;
 }
 
-/* Linux epoll API. */
-#ifdef HAVE_EPOLL_WAIT
-  #include "common/fdset_epoll.h"
-#endif /* HAVE_EPOLL_WAIT */
-
-/* BSD kqueue API */
-#ifdef HAVE_KQUEUE
-  #include "common/fdset_kqueue.h"
-#endif /* HAVE_KQUEUE */
-
-/* POSIX poll API */
-#ifdef HAVE_POLL
-  #include "common/fdset_poll.h"
-#endif /* HAVE_POLL */
-
-/*! \brief Bootstrap polling subsystem (it is called automatically). */
-void __attribute__ ((constructor)) fdset_init()
+int fdset_init(fdset_t *set, unsigned size)
 {
-	/* Preference: epoll */
-#ifdef HAVE_EPOLL_WAIT
-	if (dlsym(RTLD_DEFAULT, "epoll_wait") != 0) {
-		fdset_set_backend(&FDSET_EPOLL);
-		return;
+	if (set == NULL) {
+		return KNOT_EINVAL;
 	}
-#endif
 
-	/* Preference: kqueue */
-#ifdef HAVE_KQUEUE
-	if (dlsym(RTLD_DEFAULT, "kqueue") != 0) {
-		fdset_set_backend(&FDSET_KQUEUE);
-		return;
-	}
-#endif
+	memset(set, 0, sizeof(fdset_t));
+	return fdset_resize(set, size);
+}
 
-	/* Fallback: poll */
-#ifdef HAVE_POLL
-	if (dlsym(RTLD_DEFAULT, "poll") != 0) {
-		fdset_set_backend(&FDSET_POLL);
-		return;
+int fdset_clear(fdset_t* set)
+{
+	if (set == NULL) {
+		return KNOT_EINVAL;
 	}
-#endif
 
-	/* This shouldn't happen. */
-	fprintf(stderr, "fdset: fatal error - no valid fdset backend found\n");
-	return;
+	free(set->ctx);
+	free(set->pfd);
+	free(set->timeout);
+	memset(set, 0, sizeof(fdset_t));
+	return KNOT_EOK;
 }
 
-/*!
- * \brief Compare file descriptors.
- *
- * \param a File descriptor.
- * \param b File descriptor.
- *
- * \retval -1 if a < b
- * \retval  0 if a == b
- * \retval  1 if a > b
- */
-static inline int fdset_compare(void *a, void *b)
+int fdset_add(fdset_t *set, int fd, unsigned events, void *ctx)
 {
-	if (a > b) return 1;
-	if (a < b) return -1;
-	return 0;
-}
-
-fdset_t *fdset_new() {
-	fdset_t* set = _fdset_backend.fdset_new();
-	fdset_base_t *base = (fdset_base_t*)set;
-	if (base != NULL) {
-		/* Create atimes list. */
-		base->atimes = skip_create_list(fdset_compare);
-		if (base->atimes == NULL) {
-			fdset_destroy(set);
-			set = NULL;
-		}
+	if (set == NULL || fd < 0) {
+		return KNOT_EINVAL;
 	}
-	return set;
+
+	/* Realloc needed. */
+	if (set->n == set->size && fdset_resize(set, set->size + FDSET_INIT_SIZE))
+		return KNOT_ENOMEM;
+
+	/* Initialize. */
+	int i = set->n++;
+	set->pfd[i].fd = fd;
+	set->pfd[i].events = events;
+	set->pfd[i].revents = 0;
+	set->ctx[i] = ctx;
+	set->timeout[i] = 0;
+
+	/* Return index to this descriptor. */
+	return i;
 }
 
-int fdset_destroy(fdset_t* fdset) {
-	fdset_base_t *base = (fdset_base_t*)fdset;
-	if (base != NULL && base->atimes != NULL) {
-		skip_destroy_list(&base->atimes, NULL, free);
+int fdset_remove(fdset_t *set, unsigned i)
+{
+	if (set == NULL || i >= set->n) {
+		return KNOT_EINVAL;
 	}
-	return _fdset_backend.fdset_destroy(fdset);
-}
 
-int fdset_remove(fdset_t *fdset, int fd) {
-	fdset_base_t *base = (fdset_base_t*)fdset;
-	if (base != NULL && base->atimes != NULL) {
-		skip_remove(base->atimes, (void*)((size_t)fd), NULL, free);
+	/* Decrement number of elms. */
+	--set->n;
+
+	/* Nothing else if it is the last one.
+	 * Move last -> i if some remain. */
+	unsigned last = set->n; /* Already decremented */
+	if (i < last) {
+		set->pfd[i] = set->pfd[last];
+		set->timeout[i] = set->timeout[last];
+		set->ctx[i] = set->ctx[last];
 	}
-	return _fdset_backend.fdset_remove(fdset, fd);
+
+	return KNOT_EOK;
 }
 
-int fdset_set_watchdog(fdset_t* fdset, int fd, int interval)
+int fdset_set_watchdog(fdset_t* set, int i, int interval)
 {
-	fdset_base_t *base = (fdset_base_t*)fdset;
-	if (base == NULL || base->atimes == NULL) {
-		return -1;
+	if (set == NULL || i >= set->n) {
+		return KNOT_EINVAL;
 	}
 
 	/* Lift watchdog if interval is negative. */
 	if (interval < 0) {
-		skip_remove(base->atimes, (void*)((size_t)fd), NULL, free);
-		return 0;
-	}
-
-	/* Find if exists. */
-	timev_t *ts = NULL;
-	ts = (timev_t*)skip_find(base->atimes, (void*)((size_t)fd));
-	if (ts == NULL) {
-		ts = malloc(sizeof(timev_t));
-		if (ts == NULL) {
-			return -1;
-		}
-		skip_insert(base->atimes, (void*)((size_t)fd), (void*)ts, NULL);
+		set->timeout[i] = 0;
+		return KNOT_EOK;
 	}
 
 	/* Update clock. */
-	if (time_now(ts) < 0) {
-		return -1;
-	}
+	timev_t now;
+	if (time_now(&now) < 0)
+		return KNOT_ERROR;
 
-	ts->tv_sec += interval; /* Only seconds precision. */
-	return 0;
+	set->timeout[i] = now.tv_sec + interval; /* Only seconds precision. */
+	return KNOT_EOK;
 }
 
-int fdset_sweep(fdset_t* fdset, void(*cb)(fdset_t*, int, void*), void *data)
+int fdset_sweep(fdset_t* set, fdset_sweep_cb_t cb, void *data)
 {
-	fdset_base_t *base = (fdset_base_t*)fdset;
-	if (base == NULL || base->atimes == NULL) {
-		return -1;
+	if (set == NULL || cb == NULL) {
+		return KNOT_EINVAL;
 	}
 
 	/* Get time threshold. */
 	timev_t now;
 	if (time_now(&now) < 0) {
-		return -1;
+		return KNOT_ERROR;
 	}
 
-	/* Inspect all nodes. */
-	int sweeped = 0;
-	const skip_node_t *n = skip_first(base->atimes);
-	while (n != NULL) {
-		const skip_node_t* pnext = skip_next(n);
-
-		/* Evaluate */
-		timev_t *ts = (timev_t*)n->value;
-		if (ts->tv_sec <= now.tv_sec) {
-			cb(fdset, (int)(((ssize_t)n->key)), data);
-			++sweeped;
+	unsigned i = 0;
+	while (i < set->n) {
+
+		/* Check sweep state, remove if requested. */
+		if (set->timeout[i] > 0 && set->timeout[i] <= now.tv_sec) {
+			if (cb(set, i, data) == FDSET_SWEEP) {
+				if (fdset_remove(set, i) == KNOT_EOK)
+					continue; /* Stay on the index. */
+			}
 		}
-		n = pnext;
+
+		/* Next descriptor. */
+		++i;
 	}
 
-	return sweeped;
+	return KNOT_EOK;
 }
 
 /* OpenBSD compatibility. */
diff --git a/src/common/fdset.h b/src/common/fdset.h
index e1facdb06e2ef64fb1fdd3fe68d94ac8e6bbbd77..f0ef84928ea2742831e8b4b075093b1275ab1b56 100644
--- a/src/common/fdset.h
+++ b/src/common/fdset.h
@@ -18,12 +18,7 @@
  *
  * \author Marek Vavrusa <marek.vavrusa@nic.cz>
  *
- * \brief Wrapper for native I/O multiplexing.
- *
- * Selects best implementation according to config.
- * - poll()
- * - epoll()
- * - kqueue()
+ * \brief I/O multiplexing with context and timeouts for each fd.
  *
  * \addtogroup common_lib
  * @{
@@ -32,88 +27,36 @@
 #ifndef _KNOTD_FDSET_H_
 #define _KNOTD_FDSET_H_
 
+#include <config.h>
 #include <stddef.h>
-#ifdef HAVE_SYS_SELECT_H
- #include <sys/select.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
- #include <sys/time.h>
-#endif
-#ifdef HAVE_SIGNAL_H
- #include <signal.h>
-#endif
-#include "skip-list.h"
-#include "mempattern.h"
-
-/*! \brief Waiting for completion constants. */
-enum fdset_wait_t {
-	OS_EV_FOREVER = -1, /*!< Wait forever. */
-	OS_EV_NOWAIT  =  0  /*!< Return immediately. */
+#include <poll.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#define FDSET_INIT_SIZE 256 /* Resize step. */
+
+/*! \brief Set of filedescriptors with associated context and timeouts. */
+typedef struct fdset {
+	unsigned n;          /*!< Active fds. */
+	unsigned size;       /*!< Array size (allocated). */
+	void* *ctx;          /*!< Context for each fd. */
+	struct pollfd *pfd;  /*!< poll state for each fd */
+	time_t *timeout;       /*!< Timeout for each fd (seconds precision). */
+} fdset_t;
+
+/*! \brief Mark-and-sweep state. */
+enum fdset_sweep_state {
+	FDSET_KEEP,
+	FDSET_SWEEP
 };
 
-/*! \brief Base for implementation-specific fdsets. */
-typedef struct fdset_base_t {
-	skip_list_t *atimes;
-} fdset_base_t;
+/*! \brief Sweep callback (set, index, data) */
+typedef enum fdset_sweep_state (*fdset_sweep_cb_t)(fdset_t*, int, void*);
 
 /*!
- * \brief Opaque pointer to implementation-specific fdset data.
- * \warning Implementation MUST have fdset_base_t member on the first place.
- * Example:
- * struct fdset_t {
- *    fdset_base_t base;
- *    ...other members...
- * }
+ * \brief Initialize fdset to given size.
  */
-typedef struct fdset_t fdset_t;
-
-/*! \brief Unified event types. */
-enum fdset_event_t {
-	OS_EV_READ  = 1 << 0, /*!< Readable event. */
-	OS_EV_WRITE = 1 << 1, /*!< Writeable event. */
-	OS_EV_ERROR = 1 << 2  /*!< Error event. */
-};
-
-/*! \brief File descriptor set iterator. */
-typedef struct fdset_it_t {
-	int fd;     /*!< Current file descriptor. */
-	int events; /*!< Returned events. */
-	size_t pos; /* Internal usage. */
-} fdset_it_t;
-
-/*!
- * \brief File descriptor set implementation backend.
- * \note Functions documentation following.
- * \internal
- */
-struct fdset_backend_t
-{
-	fdset_t* (*fdset_new)();
-	int (*fdset_destroy)(fdset_t*);
-	int (*fdset_add)(fdset_t*, int, int);
-	int (*fdset_remove)(fdset_t*, int);
-	int (*fdset_wait)(fdset_t*, int);
-	int (*fdset_begin)(fdset_t*, fdset_it_t*);
-	int (*fdset_end)(fdset_t*, fdset_it_t*);
-	int (*fdset_next)(fdset_t*, fdset_it_t*);
-	const char* (*fdset_method)();
-};
-
-/*!
- * \brief Selected backend.
- * \internal
- */
-extern struct fdset_backend_t _fdset_backend;
-
-/*!
- * \brief Create new fdset.
- *
- * FDSET implementation depends on system.
- *
- * \retval Pointer to initialized FDSET structure if successful.
- * \retval NULL on error.
- */
-fdset_t *fdset_new();
+int fdset_init(fdset_t *set, unsigned size);
 
 /*!
  * \brief Destroy FDSET.
@@ -121,125 +64,61 @@ fdset_t *fdset_new();
  * \retval 0 if successful.
  * \retval -1 on error.
  */
-int fdset_destroy(fdset_t* fdset);
+int fdset_clear(fdset_t* set);
 
 /*!
  * \brief Add file descriptor to watched set.
  *
- * \param fdset Target set.
+ * \param set Target set.
  * \param fd Added file descriptor.
  * \param events Mask of watched events.
+ * \param ctx Context (optional).
  *
- * \retval 0 if successful.
+ * \retval index of the added fd if successful.
  * \retval -1 on errors.
  */
-static inline int fdset_add(fdset_t *fdset, int fd, int events) {
-	return _fdset_backend.fdset_add(fdset, fd, events);
-}
-
+int fdset_add(fdset_t *set, int fd, unsigned events, void *ctx);
 
 /*!
  * \brief Remove file descriptor from watched set.
  *
- * \param fdset Target set.
- * \param fd File descriptor to be removed.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_remove(fdset_t *fdset, int fd);
-
-/*!
- * \brief Poll set for new events.
- *
- * \param fdset Target set.
- * \param timeout Timeout (OS_EV_FOREVER, OS_EV_NOWAIT or value in miliseconds).
- *
- * \retval Number of events if successful.
- * \retval -1 on errors.
- */
-static inline int fdset_wait(fdset_t *fdset, int timeout) {
-	return _fdset_backend.fdset_wait(fdset, timeout);
-}
-
-/*!
- * \brief Set event iterator to the beginning of last polled events.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-static inline int fdset_begin(fdset_t *fdset, fdset_it_t *it) {
-	return _fdset_backend.fdset_begin(fdset, it);
-}
-
-/*!
- * \brief Set event iterator to the end of last polled events.
- *
- * \param fdset Target set.
- * \param it Event iterator.
+ * \param set Target set.
+ * \param i Index of the removed fd.
  *
  * \retval 0 if successful.
  * \retval -1 on errors.
  */
-static inline int fdset_end(fdset_t *fdset, fdset_it_t *it) {
-	return _fdset_backend.fdset_end(fdset, it);
-}
-
-/*!
- * \brief Set event iterator to the next event.
- *
- * Event iterator fd will be set to -1 if next event doesn't exist.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-static inline int fdset_next(fdset_t *fdset, fdset_it_t *it) {
-	return _fdset_backend.fdset_next(fdset, it);
-}
-
-/*!
- * \brief Returned name of underlying poll method.
- *
- * \retval Name if successful.
- * \retval NULL if no method was loaded (shouldn't happen).
- */
-static inline const char* fdset_method() {
-	return _fdset_backend.fdset_method();
-}
+int fdset_remove(fdset_t *set, unsigned i);
 
 /*!
  * \brief Set file descriptor watchdog interval.
  *
- * Descriptors without activity in given interval
- * can be disposed with fdset_sweep().
+ * Set time (interval from now) after which the associated file descriptor
+ * should be sweeped (see fdset_sweep). Good example is setting a grace period
+ * of N seconds between socket activity. If socket is not active within
+ * <now, now + interval>, it is sweeped and potentially closed.
  *
- * \param fdset Target set.
- * \param fd File descriptor.
+ * \param set Target set.
+ * \param i Index for the file descriptor.
  * \param interval Allowed interval without activity (seconds).
- *                 <0 removes watchdog interval.
+ *                 -1 disables watchdog timer
  *
  * \retval 0 if successful.
  * \retval -1 on errors.
  */
-int fdset_set_watchdog(fdset_t* fdset, int fd, int interval);
+int fdset_set_watchdog(fdset_t* set, int i, int interval);
 
 /*!
  * \brief Sweep file descriptors with exceeding inactivity period.
  *
  * \param fdset Target set.
  * \param cb Callback for sweeped descriptors.
- * \param data Custom data for sweep operation.
+ * \param data Pointer to extra data.
  *
  * \retval number of sweeped descriptors.
  * \retval -1 on errors.
  */
-int fdset_sweep(fdset_t* fdset, void(*cb)(fdset_t*, int, void*), void *data);
+int fdset_sweep(fdset_t* set, fdset_sweep_cb_t cb, void *data);
 
 /*!
  * \brief pselect(2) compatibility wrapper.
diff --git a/src/common/fdset_epoll.c b/src/common/fdset_epoll.c
deleted file mode 100644
index f889dd33c557430fb884966e891f56125ecb086f..0000000000000000000000000000000000000000
--- a/src/common/fdset_epoll.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-
-#ifdef HAVE_EPOLL_WAIT
-
-#include <sys/epoll.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "fdset_epoll.h"
-#include "skip-list.h"
-
-#define OS_FDS_CHUNKSIZE 8   /*!< Number of pollfd structs in a chunk. */
-
-struct fdset_t {
-	fdset_base_t _base;
-	int epfd;
-	struct epoll_event *events;
-	size_t nfds;
-	size_t reserved;
-	size_t polled;
-};
-
-fdset_t *fdset_epoll_new()
-{
-	fdset_t *set = malloc(sizeof(fdset_t));
-	if (set) {
-		/* Blank memory. */
-		memset(set, 0, sizeof(fdset_t));
-
-		/* Create epoll fd. */
-		set->epfd = epoll_create(OS_FDS_CHUNKSIZE);
-	}
-
-	return set;
-}
-
-int fdset_epoll_destroy(fdset_t * fdset)
-{
-	if(fdset == NULL) {
-		return -1;
-	}
-
-	/* Teardown epoll. */
-	close(fdset->epfd);
-
-	/* OK if NULL. */
-	free(fdset->events);
-	free(fdset);
-	return 0;
-}
-
-int fdset_epoll_add(fdset_t *fdset, int fd, int events)
-{
-	if (fdset == NULL || fd < 0 || events <= 0) {
-		return -1;
-	}
-
-	/* Realloc needed. */
-	if (mreserve((char **)&fdset->events, sizeof(struct epoll_event),
-	             fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->reserved) < 0) {
-		return -1;
-	}
-
-	/* Add to epoll set. */
-	struct epoll_event ev;
-	memset(&ev, 0, sizeof(struct epoll_event));
-	ev.events = EPOLLIN;
-	ev.data.fd = fd;
-	if (epoll_ctl(fdset->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
-		return -1;
-	}
-
-	++fdset->nfds;
-	return 0;
-}
-
-int fdset_epoll_remove(fdset_t *fdset, int fd)
-{
-	if (fdset == NULL || fd < 0) {
-		return -1;
-	}
-
-	/* Attempt to remove from set. */
-	struct epoll_event ev;
-	memset(&ev, 0, sizeof(struct epoll_event));
-	if (epoll_ctl(fdset->epfd, EPOLL_CTL_DEL, fd, &ev) < 0) {
-		return -1;
-	}
-
-	/* Overwrite current item. */
-	--fdset->nfds;
-
-	/* Trim excessive memory if possible (retval is not interesting). */
-	mreserve((char **)&fdset->events, sizeof(struct epoll_event),
-	         fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->reserved);
-
-	return 0;
-}
-
-int fdset_epoll_wait(fdset_t *fdset, int timeout)
-{
-	if (fdset == NULL || fdset->nfds < 1 || fdset->events == NULL) {
-		return -1;
-	}
-
-	/* Poll new events. */
-	fdset->polled = 0;
-	int nfds = epoll_wait(fdset->epfd, fdset->events, fdset->nfds, timeout);
-
-	/* Check. */
-	if (nfds < 0) {
-		return -1;
-	}
-
-	/* Events array is ordered from 0 to nfds. */
-	fdset->polled = nfds;
-	return nfds;
-}
-
-int fdset_epoll_begin(fdset_t *fdset, fdset_it_t *it)
-{
-	if (fdset == NULL || it == NULL) {
-		return -1;
-	}
-
-	/* Find first. */
-	it->pos = 0;
-	return fdset_next(fdset, it);
-}
-
-int fdset_epoll_end(fdset_t *fdset, fdset_it_t *it)
-{
-	if (fdset == NULL || it == NULL || fdset->nfds < 1) {
-		return -1;
-	}
-
-	/* Check for polled events. */
-	if (fdset->polled < 1) {
-		it->fd = -1;
-		it->pos = 0;
-		return -1;
-	}
-
-	/* No end found, ends on the beginning. */
-	size_t nid = fdset->polled - 1;
-	it->fd = fdset->events[nid].data.fd;
-	it->pos = nid;
-	it->events = 0;
-	return -1;
-}
-
-int fdset_epoll_next(fdset_t *fdset, fdset_it_t *it)
-{
-	if (fdset == NULL || it == NULL || fdset->nfds < 1) {
-		return -1;
-	}
-
-	/* Check boundaries. */
-	if (it->pos >= fdset->polled || it->pos >= fdset->nfds) {
-		return -1;
-	}
-
-	/* Select next. */
-	size_t nid = it->pos++;
-	it->fd = fdset->events[nid].data.fd;
-	it->events = 0;
-	return 0;
-}
-
-const char* fdset_epoll_method()
-{
-	return "epoll";
-}
-
-/* Package APIs. */
-struct fdset_backend_t FDSET_EPOLL = {
-	.fdset_new = fdset_epoll_new,
-	.fdset_destroy = fdset_epoll_destroy,
-	.fdset_add = fdset_epoll_add,
-	.fdset_remove = fdset_epoll_remove,
-	.fdset_wait = fdset_epoll_wait,
-	.fdset_begin = fdset_epoll_begin,
-	.fdset_end = fdset_epoll_end,
-	.fdset_next = fdset_epoll_next,
-	.fdset_method = fdset_epoll_method
-};
-
-#endif
diff --git a/src/common/fdset_epoll.h b/src/common/fdset_epoll.h
deleted file mode 100644
index 58f25f81e7df57c51eadb6dbc52bb7a15b1c070a..0000000000000000000000000000000000000000
--- a/src/common/fdset_epoll.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/*!
- * \file fdset_epoll.h
- *
- * \author Marek Vavrusa <marek.vavrusa@nic.cz>
- *
- * \brief Wrapper for epoll I/O multiplexing.
- *
- * \addtogroup common_lib
- * @{
- */
-
-#ifndef _KNOTD_FDSET_EPOLL_H_
-#define _KNOTD_FDSET_EPOLL_H_
-
-#include "fdset.h"
-
-/*!
- * \brief Create new fdset.
- *
- * Linux epoll() backend.
- *
- * \retval Pointer to initialized FDSET structure if successful.
- * \retval NULL on error.
- */
-fdset_t *fdset_epoll_new();
-
-/*!
- * \brief Destroy FDSET.
- *
- * \retval 0 if successful.
- * \retval -1 on error.
- */
-int fdset_epoll_destroy(fdset_t * fdset);
-
-/*!
- * \brief Add file descriptor to watched set.
- *
- * \param fdset Target set.
- * \param fd Added file descriptor.
- * \param events Mask of watched events.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_epoll_add(fdset_t *fdset, int fd, int events);
-
-/*!
- * \brief Remove file descriptor from watched set.
- *
- * \param fdset Target set.
- * \param fd File descriptor to be removed.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_epoll_remove(fdset_t *fdset, int fd);
-
-/*!
- * \brief Poll set for new events.
- *
- * \param fdset Target set.
- * \param timeout Timeout (OS_EV_FOREVER, OS_EV_NOWAIT or value in miliseconds).
- *
- * \retval Number of events if successful.
- * \retval -1 on errors.
- */
-int fdset_epoll_wait(fdset_t *fdset, int timeout);
-
-/*!
- * \brief Set event iterator to the beginning of last polled events.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_epoll_begin(fdset_t *fdset, fdset_it_t *it);
-
-/*!
- * \brief Set event iterator to the end of last polled events.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_epoll_end(fdset_t *fdset, fdset_it_t *it);
-
-/*!
- * \brief Set event iterator to the next event.
- *
- * Event iterator fd will be set to -1 if next event doesn't exist.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_epoll_next(fdset_t *fdset, fdset_it_t *it);
-
-/*!
- * \brief Returned name of epoll method.
- *
- * \retval Name if successful.
- * \retval NULL if no method was loaded (shouldn't happen).
- */
-const char* fdset_epoll_method();
-
-/*! \brief Exported API. */
-extern struct fdset_backend_t FDSET_EPOLL;
-
-#endif /* _KNOTD_FDSET_EPOLL_H_ */
-
-/*! @} */
diff --git a/src/common/fdset_kqueue.c b/src/common/fdset_kqueue.c
deleted file mode 100644
index 7c52f71ba96cac4c82ccf5f7104573d2f96b5597..0000000000000000000000000000000000000000
--- a/src/common/fdset_kqueue.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-#ifdef HAVE_KQUEUE
-
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/event.h>
-#include <sys/time.h>
-#include <time.h>
-
-#include "fdset_kqueue.h"
-
-#define OS_FDS_CHUNKSIZE 8   /*!< Number of pollfd structs in a chunk. */
-#define OS_FDS_KEEPCHUNKS 32 /*!< Will attempt to free memory when reached. */
-
-struct fdset_t {
-	fdset_base_t _base;
-	int kq;
-	struct kevent *events;
-	struct kevent *revents;
-	size_t nfds;
-	size_t reserved;
-	size_t rreserved;
-	size_t polled;
-};
-
-fdset_t *fdset_kqueue_new()
-{
-	fdset_t *set = malloc(sizeof(fdset_t));
-	if (set) {
-		/* Blank memory. */
-		memset(set, 0, sizeof(fdset_t));
-
-		/* Create kqueue fd. */
-		set->kq = kqueue();
-		if (set->kq < 0) {
-			free(set);
-			set = 0;
-		}
-	}
-
-	return set;
-}
-
-int fdset_kqueue_destroy(fdset_t * fdset)
-{
-	if(fdset == NULL) {
-		return -1;
-	}
-
-	/* Teardown kqueue. */
-	close(fdset->kq);
-
-	/* OK if NULL. */
-	free(fdset->revents);
-	free(fdset->events);
-	free(fdset);
-	return 0;
-}
-
-int fdset_kqueue_add(fdset_t *fdset, int fd, int events)
-{
-	if (fdset == NULL || fd < 0 || events <= 0) {
-		return -1;
-	}
-
-	/* Realloc needed. */
-	int ret = 0;
-	ret += mreserve((char **)&fdset->events, sizeof(struct kevent),
-	                fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->reserved);
-	ret += mreserve((char **)&fdset->revents, sizeof(struct kevent),
-	                fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->rreserved);
-	if (ret != 0) {
-		return ret;
-	}
-
-	/* Add to kqueue set. */
-	int evfilt = EVFILT_READ;
-	EV_SET(&fdset->events[fdset->nfds], fd, evfilt,
-	       EV_ADD|EV_ENABLE, 0, 0, 0);
-	memset(fdset->revents + fdset->nfds, 0, sizeof(struct kevent));
-
-	++fdset->nfds;
-	return 0;
-}
-
-int fdset_kqueue_remove(fdset_t *fdset, int fd)
-{
-	if (fdset == NULL || fd < 0) {
-		return -1;
-	}
-
-	/* Find in set. */
-	int pos = -1;
-	for (int i = 0; i < fdset->nfds; ++i) {
-		if (fdset->events[i].ident == fd) {
-			pos = i;
-			break;
-		}
-	}
-
-	if (pos < 0) {
-		return -1;
-	}
-
-	/* Remove filters. */
-	EV_SET(&fdset->events[pos], fd, EVFILT_READ,
-	       EV_DISABLE|EV_DELETE, 0, 0, 0);
-
-	/* Attempt to remove from set. */
-	size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct kevent);
-	memmove(fdset->events + pos, fdset->events + (pos + 1), remaining);
-
-	/* Attempt to remove from revents set. */
-	pos = -1;
-	for (int i = 0; i < fdset->nfds; ++i) {
-		if (fdset->events[i].ident == fd) {
-			pos = i;
-			break;
-		}
-	}
-	if (pos >= 0) {
-		size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct kevent);
-		memmove(fdset->revents + pos, fdset->revents + (pos + 1), remaining);
-	}
-
-
-	/* Overwrite current item. */
-	--fdset->nfds;
-
-	/* Trim excessive memory if possible (retval is not interesting). */
-	mreserve((char **)&fdset->events, sizeof(struct kevent),
-	         fdset->nfds, OS_FDS_CHUNKSIZE, &fdset->reserved);
-	mreserve((char **)&fdset->revents, sizeof(struct kevent),
-	         fdset->nfds, OS_FDS_CHUNKSIZE, &fdset->rreserved);
-
-	return 0;
-}
-
-int fdset_kqueue_wait(fdset_t *fdset, int timeout)
-{
-	if (fdset == NULL || fdset->nfds < 1 || fdset->events == NULL) {
-		return -1;
-	}
-
-	/* Set timeout. */
-	struct timespec tmval;
-	struct timespec *tm = NULL;
-	if (timeout == 0) {
-		tmval.tv_sec = tmval.tv_nsec = 0;
-		tm = &tmval;
-	} else if (timeout > 0) {
-		tmval.tv_sec = timeout / 1000;     /* ms -> s */
-		timeout -= tmval.tv_sec * 1000;    /* Cut off */
-		tmval.tv_nsec = timeout * 1000000L; /* ms -> ns */
-		tm = &tmval;
-	}
-
-	/* Poll new events. */
-	fdset->polled = 0;
-	int nfds = kevent(fdset->kq, fdset->events, fdset->nfds,
-	                  fdset->revents, fdset->nfds, tm);
-
-	/* Check. */
-	if (nfds < 0) {
-		return -1;
-	}
-
-	/* Events array is ordered from 0 to nfds. */
-	fdset->polled = nfds;
-	return nfds;
-}
-
-int fdset_kqueue_begin(fdset_t *fdset, fdset_it_t *it)
-{
-	if (fdset == NULL || it == NULL) {
-		return -1;
-	}
-
-	/* Find first. */
-	it->pos = 0;
-	it->fd = -1;
-	return fdset_next(fdset, it);
-}
-
-int fdset_kqueue_end(fdset_t *fdset, fdset_it_t *it)
-{
-	if (fdset == NULL || it == NULL || fdset->nfds < 1) {
-		return -1;
-	}
-
-	/* Check for polled events. */
-	if (fdset->polled < 1) {
-		it->fd = -1;
-		it->pos = 0;
-		return -1;
-	}
-
-	/* No end found, ends on the beginning. */
-	size_t nid = fdset->polled - 1;
-	it->fd = fdset->revents[nid].ident;
-	it->pos = nid;
-	it->events = 0; /*! \todo Map events. */
-	return -1;
-}
-
-int fdset_kqueue_next(fdset_t *fdset, fdset_it_t *it)
-{
-	if (fdset == NULL || it == NULL || fdset->polled < 1) {
-		return -1;
-	}
-
-	/* Check boundaries. */
-	if (it->pos >= fdset->polled || it->pos >= fdset->nfds) {
-		return -1;
-	}
-
-	/* Select next. */
-	size_t nid = it->pos++;
-	it->fd = fdset->revents[nid].ident;
-	it->events = 0; /*! \todo Map events. */
-	return 0;
-}
-
-const char* fdset_kqueue_method()
-{
-	return "kqueue";
-}
-
-/* Package APIs. */
-struct fdset_backend_t FDSET_KQUEUE = {
-	.fdset_new = fdset_kqueue_new,
-	.fdset_destroy = fdset_kqueue_destroy,
-	.fdset_add = fdset_kqueue_add,
-	.fdset_remove = fdset_kqueue_remove,
-	.fdset_wait = fdset_kqueue_wait,
-	.fdset_begin = fdset_kqueue_begin,
-	.fdset_end = fdset_kqueue_end,
-	.fdset_next = fdset_kqueue_next,
-	.fdset_method = fdset_kqueue_method
-};
-
-#endif
diff --git a/src/common/fdset_kqueue.h b/src/common/fdset_kqueue.h
deleted file mode 100644
index 4b650a7b90a26389c162e200038884c5bc3d0d0c..0000000000000000000000000000000000000000
--- a/src/common/fdset_kqueue.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/*!
- * \file fdset_kqueue.h
- *
- * \author Marek Vavrusa <marek.vavrusa@nic.cz>
- *
- * \brief Wrapper for kqueue I/O multiplexing.
- *
- * \addtogroup common_lib
- * @{
- */
-
-#ifndef _KNOTD_FDSET_KQUEUE_H_
-#define _KNOTD_FDSET_KQUEUE_H_
-
-#include "fdset.h"
-
-/*!
- * \brief Create new fdset.
- *
- * BSD kqueue() backend.
- *
- * \retval Pointer to initialized FDSET structure if successful.
- * \retval NULL on error.
- */
-fdset_t *fdset_kqueue_new();
-
-/*!
- * \brief Destroy FDSET.
- *
- * \retval 0 if successful.
- * \retval -1 on error.
- */
-int fdset_kqueue_destroy(fdset_t * fdset);
-
-/*!
- * \brief Add file descriptor to watched set.
- *
- * \param fdset Target set.
- * \param fd Added file descriptor.
- * \param events Mask of watched events.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_kqueue_add(fdset_t *fdset, int fd, int events);
-
-/*!
- * \brief Remove file descriptor from watched set.
- *
- * \param fdset Target set.
- * \param fd File descriptor to be removed.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_kqueue_remove(fdset_t *fdset, int fd);
-
-/*!
- * \brief Poll set for new events.
- *
- * \param fdset Target set.
- * \param timeout Timeout (OS_EV_FOREVER, OS_EV_NOWAIT or value in miliseconds).
- *
- * \retval Number of events if successful.
- * \retval -1 on errors.
- */
-int fdset_kqueue_wait(fdset_t *fdset, int timeout);
-
-/*!
- * \brief Set event iterator to the beginning of last polled events.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_kqueue_begin(fdset_t *fdset, fdset_it_t *it);
-
-/*!
- * \brief Set event iterator to the end of last polled events.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_kqueue_end(fdset_t *fdset, fdset_it_t *it);
-
-/*!
- * \brief Set event iterator to the next event.
- *
- * Event iterator fd will be set to -1 if next event doesn't exist.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_kqueue_next(fdset_t *fdset, fdset_it_t *it);
-
-/*!
- * \brief Returned name of kqueue method.
- *
- * \retval Name if successful.
- * \retval NULL if no method was loaded (shouldn't happen).
- */
-const char* fdset_kqueue_method();
-
-/*! \brief Exported API. */
-extern struct fdset_backend_t FDSET_KQUEUE;
-
-#endif /* _KNOTD_FDSET_KQUEUE_H_ */
-
-/*! @} */
diff --git a/src/common/fdset_poll.c b/src/common/fdset_poll.c
deleted file mode 100644
index e16ae11e0bfd8ced9eef2b26bec7df8539324767..0000000000000000000000000000000000000000
--- a/src/common/fdset_poll.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-
-#ifdef HAVE_POLL
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/poll.h>
-#include <stddef.h>
-
-#include "common/fdset_poll.h"
-
-#define OS_FDS_CHUNKSIZE 8   /*!< Number of pollfd structs in a chunk. */
-
-struct fdset_t {
-	fdset_base_t _base;
-	struct pollfd *fds;
-	nfds_t nfds;
-	size_t reserved;
-	size_t polled;
-	size_t begin;
-};
-
-fdset_t *fdset_poll_new()
-{
-	fdset_t *set = malloc(sizeof(fdset_t));
-	if (set != NULL) {
-		memset(set, 0, sizeof(fdset_t));
-	}
-
-	return set;
-}
-
-int fdset_poll_destroy(fdset_t * fdset)
-{
-	if(fdset == NULL) {
-		return -1;
-	}
-
-	/* OK if NULL. */
-	free(fdset->fds);
-	free(fdset);
-	return 0;
-}
-
-int fdset_poll_add(fdset_t *fdset, int fd, int events)
-{
-	if (fdset == NULL || fd < 0 || events <= 0) {
-		return -1;
-	}
-
-	/* Realloc needed. */
-	if (mreserve((char **)&fdset->fds, sizeof(struct pollfd),
-	             fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->reserved) < 0) {
-		return -1;
-	}
-
-	/* Append. */
-	int nid = fdset->nfds++;
-	fdset->fds[nid].fd = fd;
-	fdset->fds[nid].events = POLLIN;
-	fdset->fds[nid].revents = 0;
-	return 0;
-}
-
-int fdset_poll_remove(fdset_t *fdset, int fd)
-{
-	if (fdset == NULL || fd < 0) {
-		return -1;
-	}
-
-	/* Find file descriptor. */
-	unsigned found = 0;
-	size_t pos = 0;
-	for (size_t i = 0; i < fdset->nfds; ++i) {
-		if (fdset->fds[i].fd == fd) {
-			found = 1;
-			pos = i;
-			break;
-		}
-	}
-
-	/* Check. */
-	if (!found) {
-		return -1;
-	}
-
-	/* Overwrite current item. */
-	size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct pollfd);
-	memmove(fdset->fds + pos, fdset->fds + (pos + 1), remaining);
-	--fdset->nfds;
-
-	/* Trim excessive memory if possible (retval is not interesting). */
-	mreserve((char **)&fdset->fds, sizeof(struct pollfd), fdset->nfds,
-	         OS_FDS_CHUNKSIZE, &fdset->reserved);
-
-	return 0;
-}
-
-int fdset_poll_wait(fdset_t *fdset, int timeout)
-{
-	if (fdset == NULL || fdset->nfds < 1 || fdset->fds == NULL) {
-		return -1;
-	}
-
-	/* Initialize pointers. */
-	fdset->polled = 0;
-	fdset->begin = 0;
-
-	/* Poll for events. */
-	int ret = poll(fdset->fds, fdset->nfds, timeout);
-	if (ret < 0) {
-		return -1;
-	}
-
-	/* Set pointers for iterating. */
-	fdset->polled = ret;
-	fdset->begin = 0;
-	return ret;
-}
-
-int fdset_poll_begin(fdset_t *fdset, fdset_it_t *it)
-{
-	if (fdset == NULL || it == NULL) {
-		return -1;
-	}
-
-	/* Find first. */
-	it->pos = 0;
-	return fdset_next(fdset, it);
-}
-
-int fdset_poll_end(fdset_t *fdset, fdset_it_t *it)
-{
-	if (fdset == NULL || it == NULL || fdset->nfds < 1) {
-		return -1;
-	}
-
-	/* Check for polled events. */
-	if (fdset->polled < 1) {
-		it->fd = -1;
-		it->pos = 0;
-		return -1;
-	}
-
-	/* Trace last matching item from the end. */
-	struct pollfd* pfd = fdset->fds + fdset->nfds - 1;
-	while (pfd != fdset->fds) {
-		if (pfd->events & pfd->revents) {
-			it->fd = pfd->fd;
-			it->pos = pfd - fdset->fds;
-			return 0;
-		}
-	}
-
-	/* No end found, ends on the beginning. */
-	it->fd = -1;
-	it->pos = 0;
-	return -1;
-}
-
-int fdset_poll_next(fdset_t *fdset, fdset_it_t *it)
-{
-	if (fdset == NULL || it == NULL || fdset->nfds < 1) {
-		return -1;
-	}
-
-	/* Find next with matching flags. */
-	for (; it->pos < fdset->nfds; ++it->pos) {
-		struct pollfd* pfd = fdset->fds + it->pos;
-		if (pfd->events & pfd->revents) {
-			it->fd = pfd->fd;
-			it->events = pfd->revents;
-			++it->pos; /* Next will start after current. */
-			return 0;
-		}
-	}
-
-	/* No matching event found. */
-	it->fd = -1;
-	it->pos = 0;
-	return -1;
-}
-
-const char* fdset_poll_method()
-{
-	return "poll";
-}
-
-/* Package APIs. */
-struct fdset_backend_t FDSET_POLL = {
-	.fdset_new = fdset_poll_new,
-	.fdset_destroy = fdset_poll_destroy,
-	.fdset_add = fdset_poll_add,
-	.fdset_remove = fdset_poll_remove,
-	.fdset_wait = fdset_poll_wait,
-	.fdset_begin = fdset_poll_begin,
-	.fdset_end = fdset_poll_end,
-	.fdset_next = fdset_poll_next,
-	.fdset_method = fdset_poll_method
-};
-
-#endif
diff --git a/src/common/fdset_poll.h b/src/common/fdset_poll.h
deleted file mode 100644
index 68e9e69097f339b1d0e8ce8e591f8b288d31cf68..0000000000000000000000000000000000000000
--- a/src/common/fdset_poll.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/*!
- * \file fdset_poll.h
- *
- * \author Marek Vavrusa <marek.vavrusa@nic.cz>
- *
- * \brief Wrapper for poll I/O multiplexing.
- *
- * \addtogroup common_lib
- * @{
- */
-
-#ifndef _KNOTD_FDSET_POLL_H_
-#define _KNOTD_FDSET_POLL_H_
-
-#include "fdset.h"
-
-/*!
- * \brief Create new fdset.
- *
- * POSIX poll() backend.
- *
- * \retval Pointer to initialized FDSET structure if successful.
- * \retval NULL on error.
- */
-fdset_t *fdset_poll_new();
-
-/*!
- * \brief Destroy FDSET.
- *
- * \retval 0 if successful.
- * \retval -1 on error.
- */
-int fdset_poll_destroy(fdset_t * fdset);
-
-/*!
- * \brief Add file descriptor to watched set.
- *
- * \param fdset Target set.
- * \param fd Added file descriptor.
- * \param events Mask of watched events.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_poll_add(fdset_t *fdset, int fd, int events);
-
-/*!
- * \brief Remove file descriptor from watched set.
- *
- * \param fdset Target set.
- * \param fd File descriptor to be removed.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_poll_remove(fdset_t *fdset, int fd);
-
-/*!
- * \brief Poll set for new events.
- *
- * \param fdset Target set.
- * \param timeout Timeout (OS_EV_FOREVER, OS_EV_NOWAIT or value in miliseconds).
- *
- * \retval Number of events if successful.
- * \retval -1 on errors.
- */
-int fdset_poll_wait(fdset_t *fdset, int timeout);
-
-/*!
- * \brief Set event iterator to the beginning of last polled events.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_poll_begin(fdset_t *fdset, fdset_it_t *it);
-
-/*!
- * \brief Set event iterator to the end of last polled events.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_poll_end(fdset_t *fdset, fdset_it_t *it);
-
-/*!
- * \brief Set event iterator to the next event.
- *
- * Event iterator fd will be set to -1 if next event doesn't exist.
- *
- * \param fdset Target set.
- * \param it Event iterator.
- *
- * \retval 0 if successful.
- * \retval -1 on errors.
- */
-int fdset_poll_next(fdset_t *fdset, fdset_it_t *it);
-
-/*!
- * \brief Returned name of poll method.
- *
- * \retval Name if successful.
- * \retval NULL if no method was loaded (shouldn't happen).
- */
-const char* fdset_poll_method();
-
-/*! \brief Exported API. */
-extern struct fdset_backend_t FDSET_POLL;
-
-#endif /* _KNOTD_FDSET_POLL_H_ */
-
-/*! @} */
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index 8cc0ec4a3ee32de8e95c82452ffbde6754dc83ed..9830c04a29223bdf36b8d86e29f3eeb2a1911af1 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -249,7 +249,8 @@ static int conf_process(conf_t *conf)
 	}
 
 	/* Default parallel transfers. */
-	if (conf->xfers <= 0) conf->xfers = CONFIG_XFERS;
+	if (conf->xfers <= 0)
+		conf->xfers = CONFIG_XFERS;
 
 	// Postprocess zones
 	int ret = KNOT_EOK;
diff --git a/src/knot/main.c b/src/knot/main.c
index 685d5f247658f5a795ca85e7349262c189d2372d..610b21f2a6edb34ae65a8b2b258eb44fa174d57e 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -327,7 +327,7 @@ int main(int argc, char **argv)
 		pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL);
 
 		/* Bind to control interface. */
-		uint8_t buf[65535]; /*! \todo #2035 should be on heap */
+		uint8_t buf[SOCKET_MTU_SZ];
 		size_t buflen = sizeof(buf);
 		int remote = -1;
 		if (conf()->ctl.iface != NULL) {
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index cef38012dc7decf124479344729f124f8af2bb4a..7244d3c019a66060c90098fb4fcd76ba99e0f82f 100644
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -629,18 +629,15 @@ int server_conf_hook(const struct conf_t *conf, void *data)
 	return ret;
 }
 
-ref_t *server_set_ifaces(server_t *s, fdset_t **fds, int *count, int type)
+ref_t *server_set_ifaces(server_t *s, fdset_t *fds, int type)
 {
 	iface_t *i = NULL;
-	*count = 0;
 
 	rcu_read_lock();
-	fdset_destroy(*fds);
-	*fds = fdset_new();
+	fdset_clear(fds);
 	if (s->ifaces) {
 		WALK_LIST(i, s->ifaces->l) {
-			fdset_add(*fds, i->fd[type], OS_EV_READ);
-			*count += 1;
+			fdset_add(fds, i->fd[type], POLLIN, NULL);
 		}
 
 	}
diff --git a/src/knot/server/server.h b/src/knot/server/server.h
index 84a32e3f531eae025be934f7deca62866610fdf4..a766b0b14a4362ef339ca8df4577b628e1d138d3 100644
--- a/src/knot/server/server.h
+++ b/src/knot/server/server.h
@@ -236,11 +236,10 @@ int server_conf_hook(const struct conf_t *conf, void *data);
  * \brief Update fdsets from current interfaces list.
  * \param s Server.
  * \param fds Filedescriptor set.
- * \param count Number of ifaces (will be set to N).
  * \param type I/O type (UDP/TCP).
  * \return new interface list
  */
-ref_t *server_set_ifaces(server_t *s, fdset_t **fds, int *count, int type);
+ref_t *server_set_ifaces(server_t *s, fdset_t *fds, int type);
 
 #endif // _KNOTD_SERVER_H_
 
diff --git a/src/knot/server/socket.h b/src/knot/server/socket.h
index 1849f1d9d8ddd2ca84851afff2fb1a6e510c7773..371977a3e8975cb47fb6a2b126fd2f2279bf43d5 100644
--- a/src/knot/server/socket.h
+++ b/src/knot/server/socket.h
@@ -39,9 +39,7 @@
 #include "common/sockaddr.h"
 
 /*! \brief Socket-related constants. */
-typedef enum {
-	SOCKET_MTU_SZ = 65535,  /*!< Maximum MTU size. */
-} socket_const_t;
+#define SOCKET_MTU_SZ 65535 /*!< Maximum MTU size. */
 
 /*!
  * \brief Create socket.
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index f8fe90bb4caa14c36d5f641e376b287aa8677465..92eb40209cae1b35f0f8a72d2309fe0f7e69e07c 100644
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -39,13 +39,10 @@
 #include "libknot/nameserver/name-server.h"
 #include "libknot/util/wire.h"
 
-/* Defines */
-#define TCP_BUFFER_SIZE 65535 /*! Do not change, as it is used for maximum DNS/TCP packet size. */
-
 /*! \brief TCP worker data. */
 typedef struct tcp_worker_t {
 	iohandler_t *ioh; /*!< Shortcut to I/O handler. */
-	fdset_t *fdset;   /*!< File descriptor set. */
+	fdset_t set;      /*!< File descriptor set. */
 	int pipe[2];      /*!< Master-worker signalization pipes. */
 } tcp_worker_t;
 
@@ -83,17 +80,19 @@ static int tcp_reply(int fd, uint8_t *qbuf, size_t resp_len)
 }
 
 /*! \brief Sweep TCP connection. */
-static void tcp_sweep(fdset_t *set, int fd, void* data)
+static enum fdset_sweep_state tcp_sweep(fdset_t *set, int i, void *data)
 {
 	UNUSED(data);
+	assert(set && i < set->n && i >= 0);
 
+	int fd = set->pfd[i].fd;
 	char r_addr[SOCKADDR_STRLEN] = { '\0' };
 	int r_port = 0;
 	struct sockaddr_storage addr;
 	socklen_t len = sizeof(addr);
 	if (getpeername(fd, (struct sockaddr*)&addr, &len) < 0) {
 		dbg_net("tcp: sweep getpeername() on invalid socket=%d\n", fd);
-		return;
+		return FDSET_SWEEP;
 	}
 
 	/* Translate */
@@ -111,8 +110,8 @@ static void tcp_sweep(fdset_t *set, int fd, void* data)
 
 	log_server_notice("Connection with '%s@%d' was terminated due to "
 	                  "inactivity.\n", r_addr, r_port);
-	fdset_remove(set, fd);
 	close(fd);
+	return FDSET_SWEEP;
 }
 
 /*!
@@ -327,10 +326,8 @@ int tcp_accept(int fd)
 tcp_worker_t* tcp_worker_create()
 {
 	tcp_worker_t *w = malloc(sizeof(tcp_worker_t));
-	if (w == NULL) {
-		dbg_net("tcp: out of memory when creating worker\n");
+	if (w == NULL)
 		return NULL;
-	}
 
 	/* Create signal pipes. */
 	memset(w, 0, sizeof(tcp_worker_t));
@@ -340,16 +337,14 @@ tcp_worker_t* tcp_worker_create()
 	}
 
 	/* Create fdset. */
-	w->fdset = fdset_new();
-	if (!w->fdset) {
+	if (fdset_init(&w->set, FDSET_INIT_SIZE) != KNOT_EOK) {
 		close(w->pipe[0]);
 		close(w->pipe[1]);
 		free(w);
 		return NULL;
 	}
 
-	fdset_add(w->fdset, w->pipe[0], OS_EV_READ);
-
+	fdset_add(&w->set, w->pipe[0], POLLIN, NULL);
 	return w;
 }
 
@@ -359,8 +354,8 @@ void tcp_worker_free(tcp_worker_t* w)
 		return;
 	}
 
-	/* Destroy fdset. */
-	fdset_destroy(w->fdset);
+	/* Clear fdset. */
+	fdset_clear(&w->set);
 
 	/* Close pipe write end and worker. */
 	close(w->pipe[0]);
@@ -394,15 +389,21 @@ int tcp_send(int fd, uint8_t *msg, size_t msglen)
 	int uncork = setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
 #endif
 
+	/* Flags. */
+	int flags = 0;
+#ifdef MSG_NOSIGNAL
+	flags |= MSG_NOSIGNAL;
+#endif
+
 	/* Send message size. */
 	unsigned short pktsize = htons(msglen);
-	int sent = send(fd, &pktsize, sizeof(pktsize), 0);
+	int sent = send(fd, &pktsize, sizeof(pktsize), flags);
 	if (sent < 0) {
 		return KNOT_ERROR;
 	}
 
 	/* Send message data. */
-	sent = send(fd, msg, msglen, 0);
+	sent = send(fd, msg, msglen, flags);
 	if (sent < 0) {
 		return KNOT_ERROR;
 	}
@@ -421,9 +422,15 @@ int tcp_send(int fd, uint8_t *msg, size_t msglen)
 
 int tcp_recv(int fd, uint8_t *buf, size_t len, sockaddr_t *addr)
 {
+	/* Flags. */
+	int flags = MSG_WAITALL;
+#ifdef MSG_NOSIGNAL
+	flags |= MSG_NOSIGNAL;
+#endif
+
 	/* Receive size. */
 	unsigned short pktsize = 0;
-	int n = recv(fd, &pktsize, sizeof(unsigned short), MSG_WAITALL);
+	int n = recv(fd, &pktsize, sizeof(unsigned short), flags);
 	if (n < 0) {
 		if (errno == EAGAIN) {
 			return KNOT_EAGAIN;
@@ -455,7 +462,7 @@ int tcp_recv(int fd, uint8_t *buf, size_t len, sockaddr_t *addr)
 	}
 
 	/* Receive payload. */
-	n = recv(fd, buf, pktsize, MSG_WAITALL);
+	n = recv(fd, buf, pktsize, flags);
 	if (n < 0) {
 		if (errno == EAGAIN) {
 			return KNOT_EAGAIN;
@@ -481,23 +488,22 @@ int tcp_loop_master(dthread_t *thread)
 	tcp_worker_t **workers = h->data;
 
 	/* Prepare structures for bound sockets. */
-	fdset_it_t it;
-	fdset_t *fds = NULL;
 	ref_t *ref = NULL;
-	int if_cnt = 0;
+	fdset_t set;
+	fdset_init(&set, conf()->ifaces_count);
 
 	/* Accept connections. */
-	int id = 0;
-	dbg_net("tcp: created 1 master with %d workers, backend is '%s' \n",
-	        unit->size - 1, fdset_method());
+	int id = 0, ret = 0;
+	dbg_net("tcp: created 1 master with %d workers\n", unit->size - 1);
 	for(;;) {
 
 		/* Check handler state. */
 		if (knot_unlikely(st->s & ServerReload)) {
 			st->s &= ~ServerReload;
 			ref_release(ref);
-			ref = server_set_ifaces(h->server, &fds, &if_cnt, IO_TCP);
-			if (if_cnt == 0) break;
+			ref = server_set_ifaces(h->server, &set, IO_TCP);
+			if (set.n == 0) /* Terminate on zero interfaces. */
+				break;
 		}
 
 		/* Check for cancellation. */
@@ -506,35 +512,34 @@ int tcp_loop_master(dthread_t *thread)
 		}
 
 		/* Wait for events. */
-		int nfds = fdset_wait(fds, OS_EV_FOREVER);
+		int nfds = poll(set.pfd, set.n, -1);
 		if (nfds <= 0) {
-			if (errno == EINTR) continue;
+			if (errno == EINTR)
+				continue;
 			break;
 		}
 
-		fdset_begin(fds, &it);
-		while(nfds > 0) {
-			/* Accept client. */
-			int client = tcp_accept(it.fd);
-			if (client > -1) {
-				/* Add to worker in RR fashion. */
-				if (write(workers[id]->pipe[1], &client, sizeof(int)) < 0) {
-					dbg_net("tcp: failed to register fd=%d to worker=%d\n",
-					        client, id);
-					close(client);
-					continue;
-				}
-				id = get_next_rr(id, unit->size - 1);
-			}
+		for (unsigned i = 0; nfds > 0 && i < set.n; ++i) {
+			/* Skip inactive. */
+			if (!(set.pfd[i].revents & POLLIN))
+				continue;
 
-			if (fdset_next(fds, &it) != 0) {
-				break;
-			}
+			/* Accept client. */
+			--nfds; /* One less active event. */
+			int client = tcp_accept(set.pfd[i].fd);
+			if (client < 0)
+				continue;
+
+			/* Add to worker in RR fashion. */
+			id = get_next_rr(id, unit->size - 1);
+			ret = write(workers[id]->pipe[1], &client, sizeof(int));
+			if (ret < 0)
+				close(client);
 		}
 	}
 
 	dbg_net("tcp: master thread finished\n");
-	fdset_destroy(fds);
+	fdset_clear(&set);
 	ref_release(ref);
 
 	return KNOT_EOK;
@@ -542,18 +547,6 @@ int tcp_loop_master(dthread_t *thread)
 
 int tcp_loop_worker(dthread_t *thread)
 {
-	tcp_worker_t *w = thread->data;
-	if (!w) {
-		return KNOT_EINVAL;
-	}
-
-	/* Allocate buffer for requests. */
-	uint8_t *qbuf = malloc(TCP_BUFFER_SIZE);
-	if (qbuf == NULL) {
-		dbg_net("tcp: failed to allocate buffers for TCP worker\n");
-		return KNOT_EINVAL;
-	}
-
 	/* Drop all capabilities on workers. */
 #ifdef HAVE_CAP_NG_H
 	if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) {
@@ -562,25 +555,29 @@ int tcp_loop_worker(dthread_t *thread)
 	}
 #endif /* HAVE_CAP_NG_H */
 
-	/* Next sweep time. */
+	uint8_t *qbuf = malloc(SOCKET_MTU_SZ);
+	tcp_worker_t *w = thread->data;
+	if (w == NULL || qbuf == NULL) {
+		free(qbuf);
+		return KNOT_EINVAL;
+	}
+
+	/* Accept clients. */
+	dbg_net("tcp: worker %p started\n", w);
+	fdset_t *set = &w->set;
 	timev_t next_sweep;
 	time_now(&next_sweep);
 	next_sweep.tv_sec += TCP_SWEEP_INTERVAL;
-
-	/* Accept clients. */
-	dbg_net_verb("tcp: worker %p started\n", w);
 	for (;;) {
 
 		/* Cancellation point. */
-		if (dt_is_cancelled(thread)) {
+		if (dt_is_cancelled(thread))
 			break;
-		}
 
 		/* Wait for events. */
-		int nfds = fdset_wait(w->fdset, (TCP_SWEEP_INTERVAL * 1000)/2);
-		if (nfds < 0) {
+		int nfds = poll(set->pfd, set->n, TCP_SWEEP_INTERVAL * 1000);
+		if (nfds < 0)
 			continue;
-		}
 
 		/* Establish timeouts. */
 		rcu_read_lock();
@@ -589,57 +586,49 @@ int tcp_loop_worker(dthread_t *thread)
 		rcu_read_unlock();
 
 		/* Process incoming events. */
-		dbg_net_verb("tcp: worker %p registered %d events\n",
-		             w, nfds);
-		fdset_it_t it;
-		fdset_begin(w->fdset, &it);
-		while(nfds > 0) {
-
-			/* Handle incoming clients. */
-			if (it.fd == w->pipe[0]) {
-				int client = 0;
-				if (read(it.fd, &client, sizeof(int)) < 0) {
-					continue;
-				}
+		unsigned i = 0;
+		while (nfds > 0 && i < set->n) {
+
+			if (!(set->pfd[i].revents & set->pfd[i].events)) {
+				/* Skip inactive. */
+				++i;
+				continue;
+			} else {
+				/* One less active event. */
+				--nfds;
+			}
 
-				dbg_net_verb("tcp: worker %p registered "
-				             "client %d\n",
-				             w, client);
-				fdset_add(w->fdset, client, OS_EV_READ);
-				fdset_set_watchdog(w->fdset, client, max_hs);
-				dbg_net("tcp: watchdog for fd=%d set to %ds\n",
-				        client, max_hs);
+			int fd = set->pfd[i].fd;
+			if (fd == w->pipe[0]) {
+				/* Register incoming TCP connection. */
+				int client, next_id;
+				if (read(fd, &client, sizeof(int)) == sizeof(int)) {
+					next_id = fdset_add(set, client, POLLIN, NULL);
+					fdset_set_watchdog(set, next_id, max_hs);
+				}
 			} else {
-				/* Handle other events. */
-				int ret = tcp_handle(w, it.fd, qbuf,
-				                     TCP_BUFFER_SIZE);
+				/* Process query over TCP. */
+				int ret = tcp_handle(w, fd, qbuf, SOCKET_MTU_SZ);
 				if (ret == KNOT_EOK) {
-					fdset_set_watchdog(w->fdset, it.fd,
-					                   max_idle);
-					dbg_net("tcp: watchdog for fd=%d "
-					        "set to %ds\n",
-					        it.fd, max_idle);
+					/* Update socket activity timer. */
+					fdset_set_watchdog(set, i, max_idle);
 				}
-				/*! \todo Refactor to allow erase on iterator.*/
 				if (ret == KNOT_ECONNREFUSED) {
-					fdset_remove(w->fdset, it.fd);
-					close(it.fd);
-					break;
+					fdset_remove(set, i);
+					close(fd);
+					continue; /* Stay on the same index. */
 				}
-
 			}
 
-			/* Check if next exists. */
-			if (fdset_next(w->fdset, &it) != 0) {
-				break;
-			}
+			/* Next active. */
+			++i;
 		}
 
 		/* Sweep inactive. */
 		timev_t now;
 		if (time_now(&now) == 0) {
 			if (now.tv_sec >= next_sweep.tv_sec) {
-				fdset_sweep(w->fdset, &tcp_sweep, NULL);
+				fdset_sweep(set, &tcp_sweep, NULL);
 				memcpy(&next_sweep, &now, sizeof(next_sweep));
 				next_sweep.tv_sec += TCP_SWEEP_INTERVAL;
 			}
@@ -648,7 +637,7 @@ int tcp_loop_worker(dthread_t *thread)
 
 	/* Stop whole unit. */
 	free(qbuf);
-	dbg_net_verb("tcp: worker %p finished\n", w);
+	dbg_net("tcp: worker %p finished\n", w);
 	return KNOT_EOK;
 }
 
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index c7c1abfcca05e5157395c61474f7eadc31177fde..39d012e9ad2a9488765513ca1c83189807dc9cbd 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -44,9 +44,9 @@
 #include "libknot/rrset.h"
 
 /* Constants */
-#define XFR_CHUNKLEN 3 /*! Number of requests assigned in a single pass. */
+#define XFR_MAX_TASKS 1024 /*! Maximum pending tasks. */
+#define XFR_CHUNKLEN 16 /*! Number of requests assigned in a single pass. */
 #define XFR_SWEEP_INTERVAL 2 /*! [seconds] between sweeps. */
-#define XFR_BUFFER_SIZE 65535 /*! Do not change this - maximum value for UDP packet length. */
 #define XFR_MSG_DLTTR 9 /*! Index of letter differentiating IXFR/AXFR in log msg. */
 
 /* Messages */
@@ -70,6 +70,29 @@ static knot_lookup_table_t xfr_result_table[] = {
         { XFR_TYPE_AIN, NULL }
 };
 
+/* Limits. */
+static bool xfr_pending_incr(xfrhandler_t *xfr)
+{
+	bool ret = false;
+	pthread_mutex_lock(&xfr->pending_mx);
+	rcu_read_lock();
+	if (xfr->pending < conf()->xfers) {
+		++xfr->pending;
+		ret = true;
+	}
+	rcu_read_unlock();
+	pthread_mutex_unlock(&xfr->pending_mx);
+
+	return ret;
+}
+
+static void xfr_pending_decr(xfrhandler_t *xfr)
+{
+	pthread_mutex_lock(&xfr->pending_mx);
+	--xfr->pending;
+	pthread_mutex_unlock(&xfr->pending_mx);
+}
+
 /* I/O wrappers */
 
 static int xfr_send_tcp(int fd, sockaddr_t *addr, uint8_t *msg, size_t msglen)
@@ -84,28 +107,6 @@ static int xfr_recv_tcp(int fd, sockaddr_t *addr, uint8_t *buf, size_t buflen)
 static int xfr_recv_udp(int fd, sockaddr_t *addr, uint8_t *buf, size_t buflen)
 { return recvfrom(fd, buf, buflen, 0, (struct sockaddr *)addr, &addr->len); }
 
-/* Context fetching. */
-
-static knot_ns_xfr_t* xfr_task_get(xfrworker_t *w, int fd)
-{
-	value_t *val = ahtable_tryget(w->pool.t, (const char*)&fd, sizeof(int));
-	if (!val) return NULL;
-	return *val;
-}
-
-static void xfr_task_set(xfrworker_t *w, int fd, knot_ns_xfr_t *rq)
-{
-	fdset_add(w->pool.fds, fd, OS_EV_READ);
-	value_t *val = ahtable_get(w->pool.t, (const char*)&fd, sizeof(int));
-	*val = rq;
-}
-
-static void xfr_task_clear(xfrworker_t *w, int fd)
-{
-	ahtable_del(w->pool.t, (const char*)&fd, sizeof(int));
-	fdset_remove(w->pool.fds, fd);
-}
-
 /*! \brief Wrapper function for answering AXFR/OUT. */
 static int xfr_answer_axfr(knot_nameserver_t *ns, knot_ns_xfr_t *xfr)
 {
@@ -241,10 +242,8 @@ static int xfr_task_setsig(knot_ns_xfr_t *rq, knot_tsig_key_t *key)
 static int xfr_task_connect(knot_ns_xfr_t *rq)
 {
 	/* Create socket by type. */
-	int stype = SOCK_DGRAM;
-	if (rq->flags & XFR_FLAG_TCP) {
-		stype = SOCK_STREAM;
-	}
+	int ret = 0;
+	int stype = (rq->flags & XFR_FLAG_TCP) ? SOCK_STREAM : SOCK_DGRAM;
 	int fd = socket_create(sockaddr_family(&rq->addr), stype, 0);
 	if (fd < 0) {
 		return KNOT_ERROR;
@@ -257,14 +256,22 @@ static int xfr_task_connect(knot_ns_xfr_t *rq)
 			return KNOT_ERROR;
 		}
 	}
+
 	/* Connect if TCP. */
 	if (rq->flags & XFR_FLAG_TCP) {
-		if (connect(fd, (struct sockaddr *)&rq->addr, rq->addr.len) < 0) {
+		if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
+			; /* Go silently with blocking if it fails. */
+
+		ret = connect(fd, (struct sockaddr *)&rq->addr, rq->addr.len);
+		if (ret != 0 && errno != EINPROGRESS) {
 			socket_close(fd);
 			return KNOT_ECONNREFUSED;
 		}
 	}
 
+	/* Set up for UDP as well to trigger 'send query' event. */
+	rq->flags |= XFR_FLAG_CONNECTING;
+
 	/* Store new socket descriptor. */
 	rq->session = fd;
 	return KNOT_EOK;
@@ -299,15 +306,12 @@ static void xfr_task_cleanup(knot_ns_xfr_t *rq)
 	hattrie_clear(rq->lookup_tree);
 }
 
-/*! \brief Close and free task. */
-static int xfr_task_close(xfrworker_t *w, int fd)
+/*! \brief End task properly and free it. */
+static int xfr_task_close(knot_ns_xfr_t *rq)
 {
-	knot_ns_xfr_t *rq = xfr_task_get(w, fd);
-	if (!rq) return KNOT_ENOENT;
-
 	/* Update xfer state. */
+	zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone);
 	if (rq->type == XFR_TYPE_AIN || rq->type == XFR_TYPE_IIN) {
-		zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone);
 		pthread_mutex_lock(&zd->lock);
 		if (zd->xfr_in.state == XFR_PENDING) {
 			zd->xfr_in.state = XFR_IDLE;
@@ -315,15 +319,25 @@ static int xfr_task_close(xfrworker_t *w, int fd)
 		pthread_mutex_unlock(&zd->lock);
 	}
 
+	/* Reschedule failed bootstrap. */
+	if (rq->type == XFR_TYPE_AIN && !knot_zone_contents(rq->zone)) {
+		int tmr_s = AXFR_BOOTSTRAP_RETRY * tls_rand();
+		event_t *ev = zd->xfr_in.timer;
+		if (ev) {
+			evsched_cancel(ev->parent, ev);
+			evsched_schedule(ev->parent, ev, tmr_s);
+		}
+		log_zone_notice("%s Bootstrap failed, next attempt in %d seconds.\n",
+		                rq->msg, tmr_s / 1000);
+	}
+
 	/* Close socket and free task. */
-	xfr_task_clear(w, fd);
 	xfr_task_free(rq);
-	socket_close(fd);
 	return KNOT_EOK;
 }
 
 /*! \brief Timeout handler. */
-static int xfr_task_expire(fdset_t *fds, knot_ns_xfr_t *rq)
+static int xfr_task_expire(fdset_t *set, int i, knot_ns_xfr_t *rq)
 {
 	/* Fetch related zone (refcounted, no RCU). */
 	knot_zone_t *zone = (knot_zone_t *)rq->zone;
@@ -335,7 +349,7 @@ static int xfr_task_expire(fdset_t *fds, knot_ns_xfr_t *rq)
 	case XFR_TYPE_NOTIFY:
 		if ((long)--rq->data > 0) { /* Retries */
 			notify_create_request(contents, rq->wire, &rq->wire_size);
-			fdset_set_watchdog(fds, rq->session, NOTIFY_TIMEOUT);
+			fdset_set_watchdog(set, i, NOTIFY_TIMEOUT);
 			rq->send(rq->session, &rq->addr, rq->wire, rq->wire_size);
 			log_zone_info("%s Query issued (serial %u).\n",
 			              rq->msg, knot_zone_serial(contents));
@@ -370,14 +384,6 @@ static int xfr_task_start(knot_ns_xfr_t *rq)
 		return KNOT_ECONNREFUSED;
 	}
 
-	/* Connect to remote. */
-	if (rq->session <= 0) {
-		ret = xfr_task_connect(rq);
-		if (ret != KNOT_EOK) {
-			return ret;
-		}
-	}
-
 	/* Prepare TSIG key if set. */
 	int add_tsig = 0;
 	if (rq->tsig_key) {
@@ -437,20 +443,13 @@ static int xfr_task_start(knot_ns_xfr_t *rq)
 	return KNOT_EOK;
 }
 
-static int xfr_task_process(xfrworker_t *w, knot_ns_xfr_t* rq, uint8_t *buf, size_t buflen)
+static int xfr_task_is_transfer(knot_ns_xfr_t *rq)
 {
-	/* Check if not already processing, zone is refcounted. */
-	zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone);
-
-	/* Check if the zone is not discarded. */
-	if (!zd || knot_zone_flags(rq->zone) & KNOT_ZONE_DISCARDED) {
-		dbg_xfr_verb("xfr: request on a discarded zone, ignoring\n");
-		return KNOT_EINVAL;
-	}
-
-	/* Update XFR message prefix. */
-	xfr_task_setmsg(rq, NULL);
+	return rq->type == XFR_TYPE_AIN || rq->type == XFR_TYPE_IIN;
+}
 
+static void xfr_async_setbuf(knot_ns_xfr_t *rq, uint8_t *buf, size_t buflen)
+{
 	/* Update request. */
 	rq->wire = buf;
 	rq->wire_size = buflen;
@@ -461,17 +460,78 @@ static int xfr_task_process(xfrworker_t *w, knot_ns_xfr_t* rq, uint8_t *buf, siz
 		rq->send = &xfr_send_tcp;
 		rq->recv = &xfr_recv_tcp;
 	}
+}
+
+static int xfr_async_start(fdset_t *set, knot_ns_xfr_t *rq)
+{
+	/* Update XFR message prefix. */
+	int ret = KNOT_EOK;
+	xfr_task_setmsg(rq, NULL);
+
+	/* Connect to remote. */
+	if (rq->session <= 0)
+		ret = xfr_task_connect(rq);
+
+	/* Add to set. */
+	if (ret == KNOT_EOK) {
+		unsigned flags = POLLIN;
+		if (rq->flags & XFR_FLAG_CONNECTING)
+			flags = POLLOUT;
+		int next_id = fdset_add(set, rq->session, flags, rq);
+		if (next_id >= 0) {
+			/* Set default connection timeout. */
+			rcu_read_lock();
+			fdset_set_watchdog(set, next_id, conf()->max_conn_reply);
+			rcu_read_unlock();
+		} else {
+			/* Or refuse if failed. */
+			ret = KNOT_ECONNREFUSED;
+		}
+	}
+
+	return ret;
+}
+
+static int xfr_async_state(knot_ns_xfr_t *rq)
+{
+	/* Check socket status. */
+	int err = EINVAL;
+	socklen_t len = sizeof(int);
+	if (getsockopt(rq->session, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+		return KNOT_ERROR;
+	if (err != 0)
+		return knot_map_errno(err);
+	return KNOT_EOK;
+}
+
+static int xfr_async_finish(fdset_t *set, unsigned id)
+{
+	/* Drop back to synchronous mode. */
+	int ret = KNOT_EOK;
+	knot_ns_xfr_t *rq = (knot_ns_xfr_t *)set->ctx[id];
+	if ((ret = xfr_async_state(rq)) == KNOT_EOK) {
+		rq->flags &= ~XFR_FLAG_CONNECTING;
+		set->pfd[id].events = POLLIN;
+		if (fcntl(set->pfd[id].fd, F_SETFL, 0) < 0)
+			;
+	}
+
+	/* Check if the zone is not discarded. */
+	zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone);
+	if (!zd || knot_zone_flags(rq->zone) & KNOT_ZONE_DISCARDED) {
+		dbg_xfr_verb("xfr: request on a discarded zone, ignoring\n");
+		return KNOT_EINVAL;
+	}
 
 	/* Handle request. */
 	dbg_xfr("%s processing request type '%d'\n", rq->msg, rq->type);
-	int ret = xfr_task_start(rq);
+	ret = xfr_task_start(rq);
 	const char *msg = knot_strerror(ret);
 	knot_lookup_table_t *xd = knot_lookup_by_id(xfr_result_table, rq->type);
 	if (xd && ret == KNOT_EOK) {
 		msg = xd->name;
 	}
 
-	int bootstrap_fail = 0;
 	switch(rq->type) {
 	case XFR_TYPE_AIN:
 	case XFR_TYPE_IIN:
@@ -480,34 +540,26 @@ static int xfr_task_process(xfrworker_t *w, knot_ns_xfr_t* rq, uint8_t *buf, siz
 			pthread_mutex_lock(&zd->lock);
 			zd->xfr_in.state = XFR_IDLE;
 			pthread_mutex_unlock(&zd->lock);
-			bootstrap_fail = !knot_zone_contents(rq->zone);
 		}
 		break;
-	case XFR_TYPE_NOTIFY: /* Send on first timeout <0,5>s. */
-		fdset_set_watchdog(w->pool.fds, rq->session, (int)(tls_rand() * 5));
-		xfr_task_set(w, rq->session, rq);
+	case XFR_TYPE_NOTIFY:
+		/* This is a bit of a hack to adapt NOTIFY lifetime tracking.
+		 * When NOTIFY event enters handler, it shouldn't be sent immediately.
+		 * To accomodate for this, <0, 5>s random delay is set on
+		 * event startup, so the first query fires when this timer
+		 * expires. */
+		fdset_set_watchdog(set, id, (int)(tls_rand() * 5));
 		return KNOT_EOK;
 	case XFR_TYPE_SOA:
 	case XFR_TYPE_FORWARD:
-		fdset_set_watchdog(w->pool.fds, rq->session, conf()->max_conn_reply);
+		fdset_set_watchdog(set, id, conf()->max_conn_reply);
 		break;
 	default:
 		break;
 	}
 
 	if (ret == KNOT_EOK) {
-		xfr_task_set(w, rq->session, rq);
 		log_server_info("%s %s.\n", rq->msg, msg);
-	} else if (bootstrap_fail){
-		int tmr_s = AXFR_BOOTSTRAP_RETRY * tls_rand();
-		event_t *ev = zd->xfr_in.timer;
-		if (ev) {
-			evsched_cancel(ev->parent, ev);
-			evsched_schedule(ev->parent, ev, tmr_s);
-		}
-		log_zone_notice("%s Bootstrap failed, next "
-		                "attempt in %d seconds.\n",
-		                rq->msg, tmr_s / 1000);
 	} else {
 		log_server_error("%s %s.\n", rq->msg, msg);
 	}
@@ -745,16 +797,11 @@ static int xfr_task_xfer(xfrworker_t *w, knot_ns_xfr_t *rq)
 	/* Only for successful xfers. */
 	if (ret > 0) {
 		ret = xfr_task_finalize(w, rq);
-		if (ret != KNOT_EOK && !knot_zone_contents(rq->zone)) {
-
-			/* AXFR bootstrap timeout. */
-			zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone);
-			int tmr_s = AXFR_BOOTSTRAP_RETRY * tls_rand();
-			zd->xfr_in.bootstrap_retry = tmr_s;
-			log_zone_info("%s Next attempt to bootstrap "
-			              "in %d seconds.\n",
-			              rq->msg, tmr_s / 1000);
-		} else if (ret == KNOT_EBUSY && rq->type == XFR_TYPE_IIN) {
+
+		/* EBUSY on incremental transfer has a special meaning and
+		 * is caused by a journal not able to free up space for incoming
+		 * transfer, thus forcing to start a new full zone transfer. */
+		if (ret == KNOT_EBUSY && rq->type == XFR_TYPE_IIN) {
 			return xfr_start_axfr(w, rq, diff_nospace_msg);
 		} else {
 
@@ -763,7 +810,7 @@ static int xfr_task_xfer(xfrworker_t *w, knot_ns_xfr_t *rq)
 		}
 
 		/* Update REFRESH/RETRY */
-		zones_schedule_refresh(rq->zone, 0);
+		zones_schedule_refresh(rq->zone, REFRESH_DEFAULT);
 		ret = KNOT_ECONNREFUSED; /* Disconnect */
 	}
 
@@ -801,37 +848,36 @@ static int xfr_process_event(xfrworker_t *w, knot_ns_xfr_t *rq)
 	}
 }
 
-/*! \brief Sweep non-replied connection. */
-static void xfr_sweep(fdset_t *set, int fd, void *data)
+/*! \brief Sweep inactive connection. */
+static enum fdset_sweep_state xfr_sweep(fdset_t *set, int i, void *data)
 {
-	dbg_xfr("xfr: sweeping fd=%d\n", fd);
-	if (!set || !data) {
-		dbg_xfr("xfr: invalid sweep operation on NULL worker or set\n");
-		return;
-	}
-	xfrworker_t *w = (xfrworker_t *)data;
-	knot_ns_xfr_t *rq = xfr_task_get(w, fd);
-	if (!rq) {
-		dbg_xfr("xfr: NULL data to sweep\n");
-		return;
-	}
+	assert(set && i < set->n && i >= 0);
 
-	/* Skip non-sweepable types. */
+	knot_ns_xfr_t *rq = set->ctx[i];
+	xfrhandler_t *xfr = (xfrhandler_t *)data;
+
+	/* Expire only UDP requests. */
 	int ret = KNOT_ECONNREFUSED;
 	switch(rq->type) {
 	case XFR_TYPE_SOA:
 	case XFR_TYPE_NOTIFY:
 	case XFR_TYPE_FORWARD:
-		ret = xfr_task_expire(set, rq);
+		ret = xfr_task_expire(set, i, rq);
 		break;
 	default:
 		break;
 	}
 
+	/* Close if not valid anymore. */
 	if (ret != KNOT_EOK) {
-		xfr_task_close(w, fd);
-		--w->pending;
+		if (xfr_task_is_transfer(rq))
+			xfr_pending_decr(xfr);
+		xfr_task_close(rq);
+		socket_close(set->pfd[i].fd);
+		return FDSET_SWEEP;
 	}
+
+	return FDSET_KEEP;
 }
 
 /*! \brief Check TSIG if exists. */
@@ -969,7 +1015,7 @@ int xfr_worker(dthread_t *thread)
 	xfrhandler_t *xfr = w->master;
 
 	/* Buffer for answering. */
-	size_t buflen = XFR_BUFFER_SIZE;
+	size_t buflen = SOCKET_MTU_SZ;
 	uint8_t* buf = malloc(buflen);
 	if (buf == NULL) {
 		dbg_xfr("xfr: failed to allocate buffer for XFR worker\n");
@@ -981,84 +1027,113 @@ int xfr_worker(dthread_t *thread)
 	time_now(&next_sweep);
 	next_sweep.tv_sec += XFR_SWEEP_INTERVAL;
 
-	int limit = XFR_CHUNKLEN * 3;
-	if (conf() && conf()->xfers > 0) {
-		limit = conf()->xfers;
+	/* Approximate thread capacity limits. */
+	unsigned threads = w->master->unit->size;
+	unsigned thread_capacity = XFR_MAX_TASKS / threads;
+
+	/* Set of connections. */
+	fdset_t set;
+	int ret = fdset_init(&set, thread_capacity);
+	if (ret != KNOT_EOK) {
+		free(buf);
+		return ret;
 	}
-	unsigned thread_capacity = limit / w->master->unit->size;
-	if (thread_capacity < 1) thread_capacity = 1;
-	w->pool.fds = fdset_new();
-	w->pool.t = ahtable_create();
-	w->pending = 0;
 
 	/* Accept requests. */
-	int ret = 0;
 	dbg_xfr_verb("xfr: worker=%p starting\n", w);
 	for (;;) {
 
 		/* Populate pool with new requests. */
-		if (w->pending <= thread_capacity) {
-			pthread_mutex_lock(&xfr->mx);
-			unsigned was_pending = w->pending;
-			while (!EMPTY_LIST(xfr->queue)) {
-				knot_ns_xfr_t *rq = HEAD(xfr->queue);
-				rem_node(&rq->n);
+		unsigned newconns = 0;
+		for (;;) {
+			/* Do not exceed thread capacity. */
+			if (set.n >= thread_capacity || newconns > XFR_CHUNKLEN)
+				break;
 
-				/* Unlock queue and process request. */
+			/* Tak first request. */
+			pthread_mutex_lock(&xfr->mx);
+			if (EMPTY_LIST(xfr->queue)) {
 				pthread_mutex_unlock(&xfr->mx);
-				ret = xfr_task_process(w, rq, buf, buflen);
-				if (ret == KNOT_EOK)  ++w->pending;
-				else                  xfr_task_free(rq);
-				pthread_mutex_lock(&xfr->mx);
+				break;
+			}
+
 
-				if (w->pending - was_pending > XFR_CHUNKLEN)
-					break;
+			/* Limit number of transfers. */
+			knot_ns_xfr_t *rq = HEAD(xfr->queue);
+			unsigned is_transfer = xfr_task_is_transfer(rq);
+			if (is_transfer && !xfr_pending_incr(xfr)) {
+				pthread_mutex_unlock(&xfr->mx);
+				break;
 			}
+
+			rem_node(&rq->n);
 			pthread_mutex_unlock(&xfr->mx);
+
+			/* Start asynchronous connect. */
+			xfr_async_setbuf(rq, buf, buflen);
+			if (xfr_async_start(&set, rq) != KNOT_EOK) {
+				if (is_transfer)
+					xfr_pending_decr(xfr);
+				xfr_task_close(rq);
+				break;
+			}
+
+			++newconns;
 		}
 
 		/* Check pending threads. */
-		if (dt_is_cancelled(thread) || w->pending == 0) {
+		if (dt_is_cancelled(thread) || set.n == 0) {
 			break;
 		}
 
 		/* Poll fdset. */
-		int nfds = fdset_wait(w->pool.fds, (XFR_SWEEP_INTERVAL/2) * 1000);
+		int nfds = poll(set.pfd, set.n, XFR_SWEEP_INTERVAL * 1000);
 		if (nfds < 0) {
-			if (errno == EINTR) continue;
+			if (errno == EINTR)
+				continue;
 			break;
 		}
 
 		/* Iterate fdset. */
-		fdset_it_t it;
-		fdset_begin(w->pool.fds, &it);
-		while(nfds > 0) {
-
-			/* Find data. */
-			knot_ns_xfr_t *rq = xfr_task_get(w, it.fd);
-			dbg_xfr_verb("xfr: worker=%p processing event on "
-			             "fd=%d data=%p.\n",
-			             w, it.fd, rq);
-			if (rq) {
+		unsigned i = 0;
+		while (nfds > 0 && i < set.n && !dt_is_cancelled(thread)) {
+
+			if (!(set.pfd[i].revents & set.pfd[i].events)) {
+				/* Skip inactive. */
+				++i;
+				continue;
+			} else {
+				/* One less active event. */
+				--nfds;
+			}
+
+			/* Process pending tasks. */
+			knot_ns_xfr_t *rq = (knot_ns_xfr_t *)set.ctx[i];
+			if (rq->flags & XFR_FLAG_CONNECTING) {
+				ret = xfr_async_finish(&set, i);
+			} else {
 				ret = xfr_process_event(w, rq);
-				if (ret != KNOT_EOK) {
-					xfr_task_close(w, it.fd);
-					--w->pending;
-					--it.pos; /* Reset iterator */
-				}
 			}
 
-			/* Next fd. */
-			if (fdset_next(w->pool.fds, &it) < 0) {
-				break;
+			/* Check task state. */
+			if (ret != KNOT_EOK) {
+				if (xfr_task_is_transfer(rq))
+					xfr_pending_decr(xfr);
+				xfr_task_close(rq);
+				socket_close(set.pfd[i].fd);
+				fdset_remove(&set, i);
+				continue; /* Stay on the same index. */
 			}
+
+			/* Next active. */
+			++i;
 		}
 
 		/* Sweep inactive. */
 		timev_t now;
 		if (time_now(&now) == 0) {
 			if (now.tv_sec >= next_sweep.tv_sec) {
-				fdset_sweep(w->pool.fds, &xfr_sweep, w);
+				fdset_sweep(&set, &xfr_sweep, xfr);
 				memcpy(&next_sweep, &now, sizeof(next_sweep));
 				next_sweep.tv_sec += XFR_SWEEP_INTERVAL;
 			}
@@ -1066,22 +1141,17 @@ int xfr_worker(dthread_t *thread)
 	}
 
 	/* Cancel existing connections. */
-	size_t keylen = 0;
-	ahtable_iter_t i;
-	ahtable_iter_begin(w->pool.t, &i, false);
-	while (!ahtable_iter_finished(&i)) {
-		int *key = (int *)ahtable_iter_key(&i, &keylen);
-		xfr_task_close(w, *key);
-		ahtable_iter_next(&i);
-	}
-	ahtable_iter_free(&i);
-
-	/* Destroy data structures. */
-	fdset_destroy(w->pool.fds);
-	ahtable_free(w->pool.t);
-	free(buf);
+	for (unsigned i = 0; i < set.n; ++i) {
+		knot_ns_xfr_t *rq = (knot_ns_xfr_t *)set.ctx[i];
+		socket_close(set.pfd[i].fd);
+		if (xfr_task_is_transfer(rq))
+			xfr_pending_decr(xfr);
+		xfr_task_free(rq);
+	}
 
 	dbg_xfr_verb("xfr: worker=%p finished.\n", w);
+	fdset_clear(&set);
+	free(buf);
 	return KNOT_EOK;
 }
 
@@ -1115,6 +1185,7 @@ xfrhandler_t *xfr_create(size_t thrcount, knot_nameserver_t *ns)
 
 	/* Create tasks structure and mutex. */
 	pthread_mutex_init(&xfr->mx, 0);
+	pthread_mutex_init(&xfr->pending_mx, 0);
 	init_list(&xfr->queue);
 
 	/* Assign worker threads. */
@@ -1133,6 +1204,7 @@ int xfr_free(xfrhandler_t *xfr)
 	}
 
 	/* Free RR mutex. */
+	pthread_mutex_destroy(&xfr->pending_mx);
 	pthread_mutex_destroy(&xfr->mx);
 
 	/* Free pending queue. */
diff --git a/src/knot/server/xfr-handler.h b/src/knot/server/xfr-handler.h
index 204260541ae4407e69b9c2233e7de0c0f8ffc245..f0b04875705c40605ff4d1f9abcdae0de6dd543a 100644
--- a/src/knot/server/xfr-handler.h
+++ b/src/knot/server/xfr-handler.h
@@ -48,11 +48,6 @@ enum xfrstate_t {
  */
 typedef struct xfrworker_t
 {
-	struct {
-		ahtable_t *t;
-		fdset_t   *fds;
-	} pool;
-	unsigned pending;
 	struct xfrhandler_t *master; /*! \brief Worker master. */
 } xfrworker_t;
 
@@ -62,6 +57,8 @@ typedef struct xfrworker_t
 typedef struct xfrhandler_t
 {
 	list queue;
+	unsigned pending; /*!< \brief Pending transfers. */
+	pthread_mutex_t pending_mx;
 	pthread_mutex_t mx; /*!< \brief Tasks synchronisation. */
 	knot_nameserver_t *ns;
 	dt_unit_t       *unit;  /*!< \brief Threading unit. */
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index d1a3bf694e4d07f9339d37e0636a52836f1e67cb..eba8fc516e5fb1f46d48a3e0e73ffbd6803904a0 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -2495,7 +2495,7 @@ int zones_process_response(knot_nameserver_t *nameserver,
 
 		/* No updates available. */
 		if (ret == 0) {
-			zones_schedule_refresh(zone, 0);
+			zones_schedule_refresh(zone, REFRESH_DEFAULT);
 			rcu_read_unlock();
 			return KNOT_EUPTODATE;
 		}
@@ -2698,8 +2698,7 @@ int zones_ns_conf_hook(const struct conf_t *conf, void *data)
 
 	/* REFRESH zones. */
 	for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) {
-		/* Refresh new slave zones (almost) immediately. */
-		zones_schedule_refresh(zones[i], tls_rand() * 500 + i/2);
+		zones_schedule_refresh(zones[i], 0); /* Now. */
 		zones_schedule_notify(zones[i]);
 	}
 
@@ -3177,7 +3176,7 @@ int zones_schedule_notify(knot_zone_t *zone)
 	return KNOT_EOK;
 }
 
-int zones_schedule_refresh(knot_zone_t *zone, unsigned time)
+int zones_schedule_refresh(knot_zone_t *zone, int64_t time)
 {
 	if (!zone || !zone->data) {
 		return KNOT_EINVAL;
@@ -3206,18 +3205,17 @@ int zones_schedule_refresh(knot_zone_t *zone, unsigned time)
 	if (zd->xfr_in.has_master) {
 
 		/* Schedule REFRESH timer. */
-		uint32_t refresh_tmr = time;
-		if (refresh_tmr == 0) {
-			if (knot_zone_contents(zone)) {
-				refresh_tmr = zones_jitter(zones_soa_refresh(zone));
-			} else {
-				refresh_tmr = zd->xfr_in.bootstrap_retry;
-			}
+		if (time < 0) {
+			if (knot_zone_contents(zone))
+				time = zones_jitter(zones_soa_refresh(zone));
+			else
+				time = zd->xfr_in.bootstrap_retry;
 		}
+
 		zd->xfr_in.timer = evsched_schedule_cb(sch, zones_refresh_ev,
-		                                       zone, refresh_tmr);
+		                                       zone, time);
 		dbg_zones("zone: REFRESH '%s' set to %u\n",
-		          zd->conf->name, refresh_tmr);
+		          zd->conf->name, time);
 		zd->xfr_in.state = XFR_SCHED;
 	}
 	rcu_read_unlock();
diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h
index 5045d50bd288108674ad93d05955c309747d8b87..2260e01a0819ae13f9db183cd41be66ecb0dc889 100644
--- a/src/knot/server/zones.h
+++ b/src/knot/server/zones.h
@@ -46,6 +46,10 @@
 #define IXFR_DBSYNC_TIMEOUT (60*1000) /*!< Database sync timeout = 60s. */
 #define AXFR_BOOTSTRAP_RETRY (30*1000) /*!< Interval between AXFR BS retries. */
 
+enum {
+	REFRESH_DEFAULT = -1 /* Use time value from zone structure. */
+};
+
 /*!
  * \brief Zone-related data.
  */
@@ -302,13 +306,13 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs,
  * REFRESH/RETRY/EXPIRE timers are updated according to SOA.
  *
  * \param zone Related zone.
- * \param time Specific time or 0 for default.
+ * \param time Specific time or REFRESH_DEFAULT for default.
  *
  * \retval KNOT_EOK
  * \retval KNOT_EINVAL
  * \retval KNOT_ERROR
  */
-int zones_schedule_refresh(knot_zone_t *zone, unsigned time);
+int zones_schedule_refresh(knot_zone_t *zone, int64_t time);
 
 /*!
  * \brief Schedule NOTIFY after zone update.
diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h
index 58ea212b9cc6a3722974c846a8c41eb3b444b658..6574539bbe5f8f5381641b0226a3a9959379d448 100644
--- a/src/libknot/nameserver/name-server.h
+++ b/src/libknot/nameserver/name-server.h
@@ -159,7 +159,8 @@ static const size_t KNOT_NS_TSIG_DATA_MAX_SIZE = 100 * 64 * 1024;
 enum knot_ns_xfr_flag_t {
 	XFR_FLAG_TCP = 1 << 0, /*!< XFR request is on TCP. */
 	XFR_FLAG_UDP = 1 << 1,  /*!< XFR request is on UDP. */
-	XFR_FLAG_AXFR_FINISHED = 1 << 2 /*!< Transfer is finished. */
+	XFR_FLAG_AXFR_FINISHED = 1 << 2, /*!< Transfer is finished. */
+	XFR_FLAG_CONNECTING = 1 << 3 /*!< In connecting phase. */
 };
 
 typedef enum knot_ns_transport {
@@ -173,8 +174,8 @@ typedef enum knot_ns_transport {
 typedef enum knot_ns_xfr_type_t {
 	/* DNS events. */
 	XFR_TYPE_AIN = 0, /*!< AXFR-IN request (start transfer). */
-	XFR_TYPE_AOUT,    /*!< AXFR-OUT request (incoming transfer). */
 	XFR_TYPE_IIN,     /*!< IXFR-IN request (start transfer). */
+	XFR_TYPE_AOUT,    /*!< AXFR-OUT request (incoming transfer). */
 	XFR_TYPE_IOUT,    /*!< IXFR-OUT request (incoming transfer). */
 	XFR_TYPE_SOA,     /*!< Pending SOA request. */
 	XFR_TYPE_NOTIFY,  /*!< Pending NOTIFY query. */
diff --git a/src/tests/common/fdset_tests.c b/src/tests/common/fdset_tests.c
index d7b29fa52887fd7837b7e99c1da4253b1f38de83..e8b9d73ee309f90f75d19cfd6d59413d585d699d 100644
--- a/src/tests/common/fdset_tests.c
+++ b/src/tests/common/fdset_tests.c
@@ -98,22 +98,21 @@ static int fdset_tests_count(int argc, char *argv[])
 
 static int fdset_tests_run(int argc, char *argv[])
 {
-	diag("fdset: implements '%s'", fdset_method());
-
 	/* 1. Create fdset. */
-	fdset_t *set = fdset_new();
-	ok(set != 0, "fdset: new");
+	fdset_t set;
+	int ret = fdset_init(&set, 32);
+	ok(ret == 0, "fdset: init");
 
 	/* 2. Create pipe. */
 	int fds[2], tmpfds[2];
-	int ret = pipe(fds);
+	ret = pipe(fds);
 	ok(ret >= 0, "fdset: pipe() works");
 	ret = pipe(tmpfds);
 
 	/* 3. Add fd to set. */
-	ret = fdset_add(set, fds[0], OS_EV_READ);
+	ret = fdset_add(&set, fds[0], POLLIN, NULL);
 	ok(ret == 0, "fdset: add to set works");
-	fdset_add(set, tmpfds[0], OS_EV_READ);
+	fdset_add(&set, tmpfds[0], POLLIN, NULL);
 
 	/* Schedule write. */
 	struct timeval ts, te;
@@ -122,54 +121,44 @@ static int fdset_tests_run(int argc, char *argv[])
 	pthread_create(&t, 0, thr_action, &fds[1]);
 
 	/* 4. Watch fdset. */
-	ret = fdset_wait(set, OS_EV_FOREVER);
+	int nfds = poll(set.pfd, set.n, 2000);
 	gettimeofday(&te, 0);
 	size_t diff = timeval_diff(&ts, &te);
 
-	ok(ret > 0 && diff > 99 && diff < 10000,
-	   "fdset: poll returned events in %zu ms", diff);
+	ok(nfds > 0 && diff > 99 && diff < 10000,
+	   "fdset: poll returned %d events in %zu ms", nfds, diff);
 
 	/* 5. Prepare event set. */
-	fdset_it_t it;
-	ret = fdset_begin(set, &it);
-	ok(ret == 0 && it.fd == fds[0], "fdset: begin is valid, ret=%d", ret);
+	ok(set.pfd[0].revents & POLLIN, "fdset: pipe is active");
 
 	/* 6. Receive data. */
 	char buf = 0x00;
-	ret = read(it.fd, &buf, WRITE_PATTERN_LEN);
-	ok(ret >= 0 && buf == WRITE_PATTERN, "fdset: contains valid data, fd=%d", it.fd);
-
-	/* 7. Iterate event set. */
-	ret = fdset_next(set, &it);
-	ok(ret < 0, "fdset: boundary check works");
+	ret = read(set.pfd[0].fd, &buf, WRITE_PATTERN_LEN);
+	ok(ret >= 0 && buf == WRITE_PATTERN, "fdset: contains valid data");
 
-	/* 8. Remove from event set. */
-	ret = fdset_remove(set, fds[0]);
+	/* 7-9. Remove from event set. */
+	ret = fdset_remove(&set, 0);
 	ok(ret == 0, "fdset: remove from fdset works");
 	close(fds[0]);
 	close(fds[1]);
-	ret = fdset_remove(set, tmpfds[0]);
+	ret = fdset_remove(&set, 0);
 	close(tmpfds[1]);
 	close(tmpfds[1]);
-
-	/* 9. Poll empty fdset. */
-	ret = fdset_wait(set, OS_EV_FOREVER);
-	ok(ret <= 0, "fdset: polling empty fdset returns -1 (ret=%d)", ret);
+	ok(ret == 0, "fdset: remove from fdset works (2)");
+	ret = fdset_remove(&set, 0);
+	ok(ret != 0, "fdset: removing nonexistent item");
 
 	/* 10. Crash test. */
 	lives_ok({
-		 fdset_destroy(0);
-		 fdset_add(0, -1, 0);
-		 fdset_remove(0, -1);
-		 fdset_wait(0, OS_EV_NOWAIT);
-		 fdset_begin(0, 0);
-		 fdset_end(0, 0);
-		 fdset_next(0, 0);
-		 fdset_method();
+		fdset_init(0, 0);
+		fdset_add(0, 1, 1, 0);
+		fdset_add(0, 0, 1, 0);
+		fdset_remove(0, 1);
+		fdset_remove(0, 0);
 	}, "fdset: crash test successful");
 
 	/* 11. Destroy fdset. */
-	ret = fdset_destroy(set);
+	ret = fdset_clear(&set);
 	ok(ret == 0, "fdset: destroyed");
 
 	/* Cleanup. */