diff --git a/Knot.files b/Knot.files
index 594ce9af338f35156cb67dbdb03fdb8ea4fe5c9a..2bd8f946b67bc7b37c8e751d093dfea888f63ebe 100644
--- a/Knot.files
+++ b/Knot.files
@@ -116,14 +116,14 @@ src/knot/server/dthreads.c
 src/knot/server/dthreads.h
 src/knot/server/journal.c
 src/knot/server/journal.h
+src/knot/server/net.c
+src/knot/server/net.h
 src/knot/server/notify.c
 src/knot/server/notify.h
 src/knot/server/rrl.c
 src/knot/server/rrl.h
 src/knot/server/server.c
 src/knot/server/server.h
-src/knot/server/socket.c
-src/knot/server/socket.h
 src/knot/server/tcp-handler.c
 src/knot/server/tcp-handler.h
 src/knot/server/udp-handler.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 6d851da7846a66176faef061982459b284a4cac6..44c073b1e479acc411353949a2fc4faece9d13f1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -232,8 +232,8 @@ libknotd_la_SOURCES =				\
 	knot/server/rrl.h			\
 	knot/server/server.c			\
 	knot/server/server.h			\
-	knot/server/socket.c			\
-	knot/server/socket.h			\
+	knot/server/net.c				\
+	knot/server/net.h				\
 	knot/server/tcp-handler.c		\
 	knot/server/tcp-handler.h		\
 	knot/server/udp-handler.c		\
diff --git a/src/common/sockaddr.c b/src/common/sockaddr.c
index 047d90a4bc32dd28ece156d0a001c4032a5d6d7e..fdf5273adfe38aba969ab07dc8505ee7b04a4fe5 100644
--- a/src/common/sockaddr.c
+++ b/src/common/sockaddr.c
@@ -18,7 +18,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <netdb.h>
-#include <unistd.h>
 
 #include "common/sockaddr.h"
 #include "common/errcode.h"
@@ -38,6 +37,15 @@ int sockaddr_len(const struct sockaddr_storage *ss)
 	}
 }
 
+int sockaddr_cmp(const struct sockaddr_storage *k1, const struct sockaddr_storage *k2)
+{
+	if (k1->ss_family != k2->ss_family) {
+		return (int)k1->ss_family - (int)k2->ss_family;
+	}
+
+	return memcmp(k1, k2, sockaddr_len(k1));
+}
+
 int sockaddr_set(struct sockaddr_storage *ss, int family, const char *straddr, int port)
 {
 	if (ss == NULL || straddr == NULL) {
@@ -118,7 +126,7 @@ int sockaddr_tostr(const struct sockaddr_storage *ss, char *buf, size_t maxlen)
 		sprintf(&buf[written], "%d", port);
 	}
 
-	return KNOT_ERROR;
+	return KNOT_EOK;
 }
 
 int sockaddr_port(const struct sockaddr_storage *ss)
diff --git a/src/common/sockaddr.h b/src/common/sockaddr.h
index fd25ae44fe020ed090b032b6e0c339ac2c264ab2..b7fa648426b82d638d20e3a7f036d7510e74c498 100644
--- a/src/common/sockaddr.h
+++ b/src/common/sockaddr.h
@@ -38,6 +38,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <stdint.h>
+#include <unistd.h>
 
 /* Subnet maximum prefix length. */
 #define IPV4_PREFIXLEN 32
@@ -56,6 +57,13 @@
  */
 int sockaddr_len(const struct sockaddr_storage *ss);
 
+/*!
+ * \brief Compare address storages.
+ *
+ * \return like memcmp(3)
+ */
+int sockaddr_cmp(const struct sockaddr_storage *k1, const struct sockaddr_storage *k2);
+
 /*!
  * \brief Set address and port.
  *
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
index b07196aca12d9ec93f21757e3ded4bba25fea24d..540044d93bee314ef8a817c7bddf0034dae79f2a 100644
--- a/src/knot/conf/cf-parse.y
+++ b/src/knot/conf/cf-parse.y
@@ -64,7 +64,7 @@ static conf_log_map_t *this_logmap = 0;
 #define SET_INT(out, in, name) SET_NUM(out, in, 0, INT_MAX, name);
 #define SET_SIZE(out, in, name) SET_NUM(out, in, 0, SIZE_MAX, name);
 
-static void conf_init_iface(void *scanner, char* ifname, int port)
+static void conf_init_iface(void *scanner, char* ifname)
 {
 	this_iface = malloc(sizeof(conf_iface_t));
 	if (this_iface == NULL) {
@@ -73,12 +73,11 @@ static void conf_init_iface(void *scanner, char* ifname, int port)
 	}
 	memset(this_iface, 0, sizeof(conf_iface_t));
 	this_iface->name = ifname;
-	this_iface->port = port;
 }
 
 static void conf_start_iface(void *scanner, char* ifname)
 {
-	conf_init_iface(scanner, ifname, -1);
+	conf_init_iface(scanner, ifname);
 	add_tail(&new_config->ifaces, &this_iface->n);
 	++new_config->ifaces_count;
 }
@@ -128,7 +127,7 @@ static void conf_remote_set_via(void *scanner, char *item) {
 	if (!found) {
 		cf_error(scanner, "interface '%s' is not defined", item);
 	} else {
-		sockaddr_set(&this_remote->via, found->family, found->address, 0);
+		memcpy(&this_remote->via, &found->addr, sizeof(struct sockaddr_storage));
 	}
 }
 
@@ -237,25 +236,26 @@ static bool set_remote_or_group(void *scanner, char *name,
 
 static void conf_acl_item_install(void *scanner, conf_iface_t *found)
 {
-	// silently skip duplicates
-
-	conf_remote_t *remote;
-	WALK_LIST (remote, *this_list) {
-		if (remote->remote == found) {
-			return;
-		}
-	}
 
 	// additional check for transfers
 
-	if ((this_list == &this_zone->acl.xfr_in ||
-	    this_list == &this_zone->acl.notify_out) && found->port == 0)
+	if ((this_list == &this_zone->acl.xfr_in || this_list == &this_zone->acl.notify_out)
+	    && sockaddr_port(&found->addr) == 0)
 	{
 		cf_error(scanner, "remote specified for XFR/IN or "
 		"NOTIFY/OUT needs to have valid port!");
 		return;
 	}
 
+	// silently skip duplicates
+
+	conf_remote_t *remote;
+	WALK_LIST (remote, *this_list) {
+		if (remote->remote == found) {
+			return;
+		}
+	}
+
 	// add into the list
 
 	remote = malloc(sizeof(conf_remote_t));
@@ -510,60 +510,34 @@ interface_start:
 
 interface:
  | interface PORT NUM ';' {
-     if (this_iface->port > 0) {
-       cf_error(scanner, "only one port definition is allowed in interface section\n");
+     if (this_iface->addr.ss_family == AF_UNSPEC) {
+       cf_error(scanner, "can't set port number before interface address\n");
      } else {
-       SET_UINT16(this_iface->port, $3.i, "port");
+       sockaddr_port_set(&this_iface->addr, $3.i);
      }
    }
  | interface ADDRESS IPA ';' {
-     if (this_iface->address != 0) {
-       cf_error(scanner, "only one address is allowed in interface section\n");
-     } else {
-       this_iface->address = $3.t;
-       this_iface->family = AF_INET;
-     }
+     sockaddr_set(&this_iface->addr, AF_INET, $3.t, CONFIG_DEFAULT_PORT);
+     free($3.t);
    }
  | interface ADDRESS IPA '@' NUM ';' {
-     if (this_iface->address != 0) {
-       cf_error(scanner, "only one address is allowed in interface section\n");
-     } else {
-       this_iface->address = $3.t;
-       this_iface->family = AF_INET;
-       if (this_iface->port > 0) {
-	 cf_error(scanner, "only one port definition is allowed in interface section\n");
-       } else {
-         SET_UINT16(this_iface->port, $5.i, "port");
-       }
-     }
+     sockaddr_set(&this_iface->addr, AF_INET, $3.t, $5.i);
+     free($3.t);
    }
  | interface ADDRESS IPA6 ';' {
-     if (this_iface->address != 0) {
-       cf_error(scanner, "only one address is allowed in interface section\n");
-     } else {
-       this_iface->address = $3.t;
-       this_iface->family = AF_INET6;
-     }
+     sockaddr_set(&this_iface->addr, AF_INET6, $3.t, CONFIG_DEFAULT_PORT);
+     free($3.t);
    }
  | interface ADDRESS IPA6 '@' NUM ';' {
-     if (this_iface->address != 0) {
-       cf_error(scanner, "only one address is allowed in interface section\n");
-     } else {
-       this_iface->address = $3.t;
-       this_iface->family = AF_INET6;
-       if (this_iface->port > 0) {
-          cf_error(scanner, "only one port definition is allowed in interface section\n");
-       } else {
-          SET_UINT16(this_iface->port, $5.i, "port");
-       }
-     }
+     sockaddr_set(&this_iface->addr, AF_INET, $3.t, $5.i);
+     free($3.t);
    }
  ;
 
 interfaces:
    INTERFACES '{'
  | interfaces interface_start '{' interface '}' {
-   if (this_iface->address == 0) {
+   if (this_iface->addr.ss_family == AF_UNSPEC) {
      cf_error(scanner, "interface '%s' has no defined address", this_iface->name);
    }
  }
@@ -713,75 +687,41 @@ remote_start:
 
 remote:
  | remote PORT NUM ';' {
-     if (this_remote->port != 0) {
-       cf_error(scanner, "only one port definition is allowed in remote section\n");
+     if (this_remote->addr.ss_family == AF_UNSPEC) {
+       cf_error(scanner, "can't set port number before interface address\n");
      } else {
-       SET_UINT16(this_remote->port, $3.i, "port");
+       sockaddr_port_set(&this_remote->addr, $3.i);
      }
    }
  | remote ADDRESS IPA ';' {
-     if (this_remote->address != 0) {
-       cf_error(scanner, "only one address is allowed in remote section\n");
-     } else {
-       this_remote->address = $3.t;
-       this_remote->prefix = IPV4_PREFIXLEN;
-       this_remote->family = AF_INET;
-     }
+     sockaddr_set(&this_remote->addr, AF_INET, $3.t, CONFIG_DEFAULT_PORT);
+     this_remote->prefix = IPV4_PREFIXLEN;
+     free($3.t);
+   }
+ | remote ADDRESS IPA '/' NUM ';' {
+     sockaddr_set(&this_remote->addr, AF_INET, $3.t, 0);
+     SET_NUM(this_remote->prefix, $5.i, 0, IPV4_PREFIXLEN, "prefix length");
+     free($3.t);
    }
-   | remote ADDRESS IPA '/' NUM ';' {
-       if (this_remote->address != 0) {
-         cf_error(scanner, "only one address is allowed in remote section\n");
-       } else {
-         this_remote->address = $3.t;
-         this_remote->family = AF_INET;
-         SET_NUM(this_remote->prefix, $5.i, 0, IPV4_PREFIXLEN, "prefix length");
-       }
-     }
  | remote ADDRESS IPA '@' NUM ';' {
-     if (this_remote->address != 0) {
-       cf_error(scanner, "only one address is allowed in remote section\n");
-     } else {
-       this_remote->address = $3.t;
-       this_remote->family = AF_INET;
-       this_remote->prefix = IPV4_PREFIXLEN;
-       if (this_remote->port != 0) {
-	 cf_error(scanner, "only one port definition is allowed in remote section\n");
-       } else {
-         SET_UINT16(this_remote->port, $5.i, "port");
-       }
-     }
+     sockaddr_set(&this_remote->addr, AF_INET, $3.t, $5.i);
+     this_remote->prefix = IPV4_PREFIXLEN;
+     free($3.t);
    }
  | remote ADDRESS IPA6 ';' {
-     if (this_remote->address != 0) {
-       cf_error(scanner, "only one address is allowed in remote section\n");
-     } else {
-       this_remote->address = $3.t;
-       this_remote->family = AF_INET6;
-       this_remote->prefix = IPV6_PREFIXLEN;
-     }
+     sockaddr_set(&this_remote->addr, AF_INET6, $3.t, CONFIG_DEFAULT_PORT);
+     this_remote->prefix = IPV6_PREFIXLEN;
+     free($3.t);
+   }
+ | remote ADDRESS IPA6 '/' NUM ';' {
+     sockaddr_set(&this_remote->addr, AF_INET6, $3.t, 0);
+     SET_NUM(this_remote->prefix, $5.i, 0, IPV6_PREFIXLEN, "prefix length");
+     free($3.t);
    }
-   | remote ADDRESS IPA6 '/' NUM ';' {
-       if (this_remote->address != 0) {
-         cf_error(scanner, "only one address is allowed in remote section\n");
-       } else {
-         this_remote->address = $3.t;
-         this_remote->family = AF_INET6;
-         SET_NUM(this_remote->prefix, $5.i, 0, IPV6_PREFIXLEN, "prefix length");
-       }
-     }
  | remote ADDRESS IPA6 '@' NUM ';' {
-     if (this_remote->address != 0) {
-       cf_error(scanner, "only one address is allowed in remote section\n");
-     } else {
-       this_remote->address = $3.t;
-       this_remote->family = AF_INET6;
-       this_remote->prefix = IPV6_PREFIXLEN;
-       if (this_remote->port != 0) {
-	 cf_error(scanner, "only one port definition is allowed in remote section\n");
-       } else {
-         SET_UINT16(this_remote->port, $5.i, "port");
-       }
-     }
+     sockaddr_set(&this_remote->addr, AF_INET6, $3.t, $5.i);
+     this_remote->prefix = IPV6_PREFIXLEN;
+     free($3.t);
    }
  | remote KEY TEXT ';' {
      if (this_remote->key != 0) {
@@ -808,7 +748,7 @@ remote:
 remotes:
    REMOTES '{'
  | remotes remote_start '{' remote '}' {
-     if (this_remote->address == 0) {
+     if (this_remote->addr.ss_family == AF_UNSPEC) {
        cf_error(scanner, "remote '%s' has no defined address", this_remote->name);
      }
    }
@@ -1081,7 +1021,7 @@ log: LOG { new_config->logs_count = 0; } '{' log_start log_end
  ;
 
 ctl_listen_start:
-  LISTEN_ON { conf_init_iface(scanner, NULL, -1); }
+  LISTEN_ON { conf_init_iface(scanner, NULL); }
   ;
 
 ctl_allow_start:
@@ -1093,17 +1033,16 @@ ctl_allow_start:
 control:
    CONTROL '{' { new_config->ctl.have = true; }
  | control ctl_listen_start '{' interface '}' {
-     if (this_iface->address == 0) {
+     if (this_iface->addr.ss_family == AF_UNSPEC) {
        cf_error(scanner, "control interface has no defined address");
      } else {
        new_config->ctl.iface = this_iface;
      }
  }
  | control ctl_listen_start TEXT ';' {
-     this_iface->address = $3.t;
-     this_iface->family = AF_UNIX;
-     this_iface->port = 0;
+     sockaddr_set(&this_iface->addr, AF_UNIX, $3.t, 0);
      new_config->ctl.iface = this_iface;
+     free($3.t);
  }
  | control ctl_allow_start '{' zone_acl '}'
  | control ctl_allow_start zone_acl_list
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index 0deb8e9140f5bfdc91086cddf4596e5231e4031e..66a2df816d48b335b2465d8df1f0175073a93b5e 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -217,36 +217,36 @@ static int conf_process(conf_t *conf)
 		conf->max_conn_reply = CONFIG_REPLY_WD;
 	}
 
-	// Postprocess interfaces
-	conf_iface_t *cfg_if = NULL;
-	WALK_LIST(cfg_if, conf->ifaces) {
-		if (cfg_if->port <= 0) {
-			cfg_if->port = CONFIG_DEFAULT_PORT;
-		}
-	}
-
 	/* Default interface. */
 	conf_iface_t *ctl_if = conf->ctl.iface;
 	if (!conf->ctl.have && ctl_if == NULL) {
 		ctl_if = malloc(sizeof(conf_iface_t));
 		memset(ctl_if, 0, sizeof(conf_iface_t));
-		ctl_if->family = AF_UNIX;
-		ctl_if->address = strdup("knot.sock");
+		sockaddr_set(&ctl_if->addr, AF_UNIX, "knot.sock", 0);
 		conf->ctl.iface = ctl_if;
 	}
 
 	/* Control interface. */
 	if (ctl_if) {
-		if (ctl_if->family == AF_UNIX) {
-			ctl_if->address = conf_abs_path(conf->rundir,
-			                                ctl_if->address);
+		if (ctl_if->addr.ss_family == AF_UNIX) {
+			char *full_path = malloc(SOCKADDR_STRLEN);
+			memset(full_path, 0, SOCKADDR_STRLEN);
+			sockaddr_tostr(&ctl_if->addr, full_path, SOCKADDR_STRLEN);
+
+			/* Convert to absolute path. */
+			full_path = conf_abs_path(conf->rundir, full_path);
+			if(full_path) {
+				sockaddr_set(&ctl_if->addr, AF_UNIX, full_path, 0);
+				free(full_path);
+			}
+
 			/* Check for ACL existence. */
 			if (!EMPTY_LIST(conf->ctl.allow)) {
 				log_server_warning("Control 'allow' statement "
 				                   "does not affect UNIX sockets.\n");
 			}
-		} else if (ctl_if->port <= 0) {
-			ctl_if->port = REMOTE_DPORT;
+		} else if (sockaddr_port(&ctl_if->addr) <= 0) {
+			sockaddr_port_set(&ctl_if->addr, REMOTE_DPORT);
 		}
 	}
 
@@ -437,9 +437,7 @@ static int conf_process(conf_t *conf)
 	conf_remote_t *r = NULL;
 	WALK_LIST(r, conf->ctl.allow) {
 		conf_iface_t *i = r->remote;
-		struct sockaddr_storage ss;
-		sockaddr_set(&ss, i->family, i->address, 0);
-		acl_insert(conf->ctl.acl, &ss, i->prefix, i->key);
+		acl_insert(conf->ctl.acl, &i->addr, i->prefix, i->key);
 	}
 
 	return ret;
@@ -463,9 +461,8 @@ void __attribute__ ((constructor)) conf_init()
 	/* Create default interface. */
 	conf_iface_t * iface = malloc(sizeof(conf_iface_t));
 	memset(iface, 0, sizeof(conf_iface_t));
+	sockaddr_set(&iface->addr, AF_INET, "127.0.0.1", CONFIG_DEFAULT_PORT);
 	iface->name = strdup("localhost");
-	iface->address = strdup("127.0.0.1");
-	iface->port = CONFIG_DEFAULT_PORT;
 	add_tail(&s_config->ifaces, &iface->n);
 	++s_config->ifaces_count;
 
@@ -1024,7 +1021,6 @@ void conf_free_iface(conf_iface_t *iface)
 	}
 
 	free(iface->name);
-	free(iface->address);
 	free(iface);
 }
 
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
index 90209c53f05500be9f3136df69f6e86cfecf8daa..82de8707f78a38a6456dc0dcfe44319b840f741f 100644
--- a/src/knot/conf/conf.h
+++ b/src/knot/conf/conf.h
@@ -66,13 +66,11 @@
  */
 typedef struct conf_iface_t {
 	node_t n;
-	char *name;           /*!< Internal name for the interface. */
-	char *address;        /*!< IP (IPv4/v6) address for this interface */
-	unsigned prefix;      /*!< IP subnet prefix. */
-	int port;             /*!< Port number for this interface */
-	int family;           /*!< Address family. */
-	knot_tsig_key_t *key; /*!< TSIG key (only valid for remotes). */
-	struct sockaddr_storage via;      /*!< Used for remotes to specify qry endpoint.*/
+	char *name;                   /*!< Internal name for the interface. */
+	knot_tsig_key_t *key;         /*!< TSIG key (only applic for remotes). */
+	unsigned prefix;              /*!< IP subnet prefix (only applic for remotes). */
+	struct sockaddr_storage addr; /*!< Interface address. */
+	struct sockaddr_storage via;  /*!< Used for remotes to specify qry endpoint.*/
 } conf_iface_t;
 
 /*!
diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c
index ae6a82d6d4c15cf85d86e56d64e55967f58f04cd..627cd53b0264727e0fff81051b5455610d39d5a1 100644
--- a/src/knot/ctl/knotc_main.c
+++ b/src/knot/ctl/knotc_main.c
@@ -32,7 +32,6 @@
 #include "knot/ctl/remote.h"
 #include "knot/conf/conf.h"
 #include "knot/zone/zone-load.h"
-#include "knot/server/socket.h"
 #include "knot/server/tcp-handler.h"
 #include "libknot/packet/wire.h"
 #include "knot/server/zone-load.h"
@@ -184,7 +183,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[])
 
 	/* Check remote address. */
 	conf_iface_t *r = conf()->ctl.iface;
-	if (!r || !r->address) {
+	if (!r || r->addr.ss_family == AF_UNSPEC) {
 		log_server_error("No remote address for '%s' configured.\n",
 		                 cmd);
 		return 1;
@@ -234,28 +233,39 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[])
 
 	dbg_server("%s: sending query size %zu\n", __func__, pkt->size);
 
-	/* Send query. */
-	int s = socket_create(r->family, SOCK_STREAM, 0);
-	int conn_state = socket_connect(s, r->family, r->address, r->port);
-	if (conn_state != KNOT_EOK || tcp_send(s, pkt->wire, pkt->size) <= 0) {
-		char portstr[32] = { '\0' };
-		if (r->family != AF_UNIX)
-			snprintf(portstr, sizeof(portstr), "@%d", r->port);
-		log_server_error("Couldn't connect to remote host "
-		                 "%s%s\n", r->address, portstr);
-		rc = 1;
+	/* Connect to remote. */
+	char addr_str[SOCKADDR_STRLEN] = {0};
+	sockaddr_tostr(&r->addr, addr_str, sizeof(addr_str));
+
+	int s = net_connected_socket(SOCK_STREAM, &r->addr, &r->via);
+	if (s < 0) {
+		log_server_error("Couldn't connect to remote host '%s'.\n", addr_str);
+		knot_pkt_free(&pkt);
+		return 1;
+	}
+
+	/* Wait for availability. */
+	struct pollfd pfd = { s, POLLOUT, 0 };
+	poll(&pfd, 1, conf()->max_conn_reply);
+
+	/* Send and free packet. */
+	int ret = tcp_send(s, pkt->wire, pkt->size);
+	knot_pkt_free(&pkt);
+
+	/* Evaluate and wait for reply. */
+	if (ret <= 0) {
+		log_server_error("Couldn't connect to remote host '%s'.\n", addr_str);
+		return 1;
 	}
 
 	/* Wait for reply. */
-	if (rc == 0) {
-		int ret = KNOT_EOK;
-		while (ret == KNOT_EOK) {
-			ret = cmd_remote_reply(s);
-			if (ret != KNOT_EOK && ret != KNOT_ECONN) {
-				log_server_warning("Remote command reply: %s\n",
-				                   knot_strerror(ret));
-				rc = 1;
-			}
+	ret = KNOT_EOK;
+	while (ret == KNOT_EOK) {
+		ret = cmd_remote_reply(s);
+		if (ret != KNOT_EOK && ret != KNOT_ECONN) {
+			log_server_warning("Remote command reply: %s\n",
+			                   knot_strerror(ret));
+			rc = 1;
 		}
 	}
 
@@ -265,8 +275,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[])
 	}
 
 	/* Close connection. */
-	socket_close(s);
-	knot_pkt_free(&pkt);
+	close(s);
 	return rc;
 }
 
@@ -530,25 +539,26 @@ int main(int argc, char **argv)
 
 	/* Override from command line. */
 	if (r_addr) {
-		free(ctl_if->address);
-		ctl_if->address = strdup(r_addr);
-		ctl_if->family = AF_INET;
-
 		/* Check for v6 address. */
-		if (strchr(r_addr, ':'))
-			ctl_if->family = AF_INET6;
+		int family = AF_INET;
+		if (strchr(r_addr, ':')) {
+			family = AF_INET6;
+		}
 
 		/* Is a valid UNIX socket or at least contains slash ? */
 		struct stat st;
 		bool has_slash = strchr(r_addr, '/') != NULL;
 		bool is_file = stat(r_addr, &st) == 0;
 		if (has_slash || (is_file && S_ISSOCK(st.st_mode))) {
-			ctl_if->family = AF_UNIX;
-			r_port = 0; /* Override. */
+			family = AF_UNIX;
 		}
 
+		sockaddr_set(&ctl_if->addr, family, r_addr, sockaddr_port(&ctl_if->addr));
+	}
+
+	if (r_port > 0) {
+		sockaddr_port_set(&ctl_if->addr, r_port);
 	}
-	if (r_port > -1) ctl_if->port = r_port;
 
 	/* Verbose mode. */
 	if (has_flag(flags, F_VERBOSE)) {
diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c
index 9c79cfb25bf2dd024ffdc29d3a21e605461bd0b5..ff2d2211f69bd41135ea8af76b86ff75e008c66a 100644
--- a/src/knot/ctl/remote.c
+++ b/src/knot/ctl/remote.c
@@ -21,7 +21,7 @@
 #include "common/fdset.h"
 #include "knot/knot.h"
 #include "knot/conf/conf.h"
-#include "knot/server/socket.h"
+#include "knot/server/net.h"
 #include "knot/server/tcp-handler.h"
 #include "knot/server/zones.h"
 #include "libknot/packet/wire.h"
@@ -399,71 +399,77 @@ static int remote_senderr(int c, uint8_t *qbuf, size_t buflen)
 
 int remote_bind(conf_iface_t *desc)
 {
-	if (!desc) {
-		return -1;
+	if (desc == NULL) {
+		return KNOT_EINVAL;
 	}
 
-	/* Create new socket. */
-	int s = socket_create(desc->family, SOCK_STREAM, 0);
-	if (s < 0) {
-		log_server_error("Couldn't create socket for remote "
-				 "control interface - %s", knot_strerror(s));
-		return KNOT_ERROR;
-	}
+	char addr_str[SOCKADDR_STRLEN] = {0};
+	sockaddr_tostr(&desc->addr, addr_str, sizeof(addr_str));
+	log_server_info("Binding remote control interface to '%s'.\n", addr_str);
 
-	/* Bind to interface and start listening. */
+	/* Create new socket. */
 	mode_t old_umask = umask(KNOT_CTL_SOCKET_UMASK);
-	int r = socket_bind(s, desc->family, desc->address, desc->port);
+	int sock = net_bound_socket(SOCK_STREAM, &desc->addr);
 	umask(old_umask);
-	if (r == KNOT_EOK) {
-		r = socket_listen(s, TCP_BACKLOG_SIZE);
+	if (sock < 0) {
+		return sock;
 	}
-	if (r != KNOT_EOK) {
-		log_server_error("Could not bind to remote control interface.\n");
-		socket_close(s);
-		return r;
+
+	/* Start listening. */
+	int ret = listen(sock, TCP_BACKLOG_SIZE);
+	if (ret < 0) {
+		log_server_error("Could not bind to '%s'.\n", addr_str);
+		close(sock);
+		return ret;
 	}
 
-	return s;
+	return sock;
 }
 
-int remote_unbind(int r)
+int remote_unbind(conf_iface_t *desc, int sock)
 {
-	if (r < 0) {
+	if (desc == NULL || sock < 0) {
 		return KNOT_EINVAL;
 	}
 
-	return socket_close(r);
+	/* Remove control socket file.  */
+	if (desc->addr.ss_family == AF_UNIX) {
+		char addr_str[SOCKADDR_STRLEN] = {0};
+		sockaddr_tostr(&desc->addr, addr_str, sizeof(addr_str));
+		unlink(addr_str);
+	}
+
+	return close(sock);
 }
 
-int remote_poll(int r)
+int remote_poll(int sock)
 {
 	/* Wait for events. */
 	fd_set rfds;
 	FD_ZERO(&rfds);
-	if (r > -1) {
-		FD_SET(r, &rfds);
+	if (sock > -1) {
+		FD_SET(sock, &rfds);
 	} else {
-		r = -1; /* Make sure n == r + 1 == 0 */
+		sock = -1; /* Make sure n == r + 1 == 0 */
 	}
 
-	return fdset_pselect(r + 1, &rfds, NULL, NULL, NULL, NULL);
+	return fdset_pselect(sock + 1, &rfds, NULL, NULL, NULL, NULL);
 }
 
-int remote_recv(int r, struct sockaddr *a, uint8_t* buf, size_t *buflen)
+int remote_recv(int sock, struct sockaddr *addr, uint8_t* buf, size_t *buflen)
 {
-	int c = tcp_accept(r);
+	int c = tcp_accept(sock);
 	if (c < 0) {
 		dbg_server("remote: couldn't accept incoming connection\n");
 		return c;
 	}
 
 	/* Receive data. */
-	int n = tcp_recv(c, buf, *buflen, a);
+	int n = tcp_recv(c, buf, *buflen, addr);
 	*buflen = n;
 	if (n <= 0) {
 		dbg_server("remote: failed to receive data\n");
-		socket_close(c);
+		close(c);
 		return KNOT_ECONNREFUSED;
 	}
 
@@ -518,9 +524,9 @@ failed:
 	return ret;
 }
 
-int remote_answer(int fd, server_t *s, knot_pkt_t *pkt)
+int remote_answer(int sock, server_t *s, knot_pkt_t *pkt)
 {
-	if (fd < 0 || s == NULL || pkt == NULL) {
+	if (sock < 0 || s == NULL || pkt == NULL) {
 		return KNOT_EINVAL;
 	}
 
@@ -583,12 +589,12 @@ int remote_answer(int fd, server_t *s, knot_pkt_t *pkt)
 	unsigned p = 0;
 	size_t chunk = 16384;
 	for (; p + chunk < args->rlen; p += chunk) {
-		remote_send_chunk(fd, pkt, args->resp + p, chunk);
+		remote_send_chunk(sock, pkt, args->resp + p, chunk);
 	}
 
 	unsigned r = args->rlen - p;
 	if (r > 0) {
-		remote_send_chunk(fd, pkt, args->resp + p, r);
+		remote_send_chunk(sock, pkt, args->resp + p, r);
 	}
 
 	free(args);
@@ -620,7 +626,7 @@ int remote_process(server_t *s, conf_iface_t *ctl_if, int sock,
 
 	/* Parse packet and answer if OK. */
 	int ret = remote_parse(pkt);
-	if (ret == KNOT_EOK && ctl_if->family != AF_UNIX) {
+	if (ret == KNOT_EOK && ctl_if->addr.ss_family != AF_UNIX) {
 
 		/* Check ACL list. */
 		char addr_str[SOCKADDR_STRLEN] = {0};
@@ -675,7 +681,7 @@ int remote_process(server_t *s, conf_iface_t *ctl_if, int sock,
 
 finish:
 	knot_pkt_free(&pkt);
-	socket_close(client);
+	close(client);
 	return ret;
 }
 
diff --git a/src/knot/ctl/remote.h b/src/knot/ctl/remote.h
index 01666de713aa8f898808266ceb7b472ad95d1fbd..6c8389aa5aa1a803f9434094769ce7bcfe645f6b 100644
--- a/src/knot/ctl/remote.h
+++ b/src/knot/ctl/remote.h
@@ -38,6 +38,7 @@
 
 /*!
  * \brief Bind RC interface according to configuration.
+ *
  * \param desc Interface descriptor (address, port).
  *
  * \retval socket if passed.
@@ -50,20 +51,22 @@ int remote_bind(conf_iface_t *desc);
  *
  * \note Breaks all pending connections.
  *
- * \param r RC interface socket
+ * \param desc Interface descriptor (address, port).
+ * \param socket Interface socket
  *
  * \retval KNOT_EOK on success.
  * \retval knot_error else.
  */
-int remote_unbind(int r);
+int remote_unbind(conf_iface_t *desc, int sock);
 
 /*!
  * \brief Poll new events on RC socket.
+ *
  * \param r RC interface socket.
  *
  * \return number of polled events or -1 on error.
  */
-int remote_poll(int r);
+int remote_poll(int sock);
 
 /*!
  * \brief Start a RC connection with remote.
@@ -76,7 +79,7 @@ int remote_poll(int r);
  * \return client TCP socket if success.
  * \return KNOT_ECONNREFUSED if fails to receive command.
  */
-int remote_recv(int r, struct sockaddr *a, uint8_t* buf, size_t *buflen);
+int remote_recv(int sock, struct sockaddr *addr, uint8_t* buf, size_t *buflen);
 
 /*!
  * \brief Parse a RC command.
@@ -100,7 +103,7 @@ int remote_parse(knot_pkt_t* pkt);
  * \retval KNOT_EOK on success.
  * \retval knot_error else.
  */
-int remote_answer(int fd, server_t *s, knot_pkt_t *pkt);
+int remote_answer(int sock, server_t *s, knot_pkt_t *pkt);
 
 /*!
  * \brief Accept new client, receive command, process it and send response.
diff --git a/src/knot/main.c b/src/knot/main.c
index 7ee129793342aeee34fad452fbeab4f1ab3cc14d..9dbae43e3171a0fef64a96cf66cf1c6b9f56e341 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -339,18 +339,7 @@ int main(int argc, char **argv)
 		/* Bind to control interface. */
 		uint8_t buf[KNOT_WIRE_MAX_PKTSIZE];
 		size_t buflen = sizeof(buf);
-		int remote = -1;
-		if (conf()->ctl.iface != NULL) {
-			conf_iface_t *ctl_if = conf()->ctl.iface;
-			memset(buf, 0, buflen);
-			if (ctl_if->port)
-				snprintf((char*)buf, buflen, "@%d", ctl_if->port);
-			/* Log control interface description. */
-			log_server_info("Binding remote control interface "
-					"to %s%s\n",
-					ctl_if->address, (char*)buf);
-			remote = remote_bind(ctl_if);
-		}
+		int remote = remote_bind(conf()->ctl.iface);
 
 		/* Run event loop. */
 		for(;;) {
@@ -385,14 +374,7 @@ int main(int argc, char **argv)
 		pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
 
 		/* Close remote control interface */
-		if (remote > -1) {
-			close(remote);
-
-			/* Remove control socket.  */
-			conf_iface_t *ctl_if = conf()->ctl.iface;
-			if (ctl_if && ctl_if->family == AF_UNIX)
-				unlink(conf()->ctl.iface->address);
-		}
+		remote_unbind(conf()->ctl.iface, remote);
 
 		if ((server_wait(&server)) != KNOT_EOK) {
 			log_server_error("An error occured while "
diff --git a/src/knot/server/net.c b/src/knot/server/net.c
new file mode 100644
index 0000000000000000000000000000000000000000..b8d47b616abcb9dbe2654790884ec9668b6a78cc
--- /dev/null
+++ b/src/knot/server/net.c
@@ -0,0 +1,144 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "knot/server/net.h"
+#include "knot/knot.h"
+
+static int socket_create(int family, int type, int proto)
+{
+	/* Create socket. */
+	int ret = socket(family, type, proto);
+	if (ret < 0) {
+		return knot_map_errno(EACCES, EINVAL, ENOMEM);
+	}
+
+	return ret;
+}
+
+int net_unbound_socket(int type, struct sockaddr_storage *ss)
+{
+	if (ss == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	/* Convert to string address format. */
+	char addr_str[SOCKADDR_STRLEN] = {0};
+	sockaddr_tostr(ss, addr_str, sizeof(addr_str));
+
+	/* Create socket. */
+	int socket = socket_create(ss->ss_family, type, 0);
+	if (socket < 0) {
+		log_server_error("Could not create socket for '%s': %s\n",
+		                 addr_str, knot_strerror(socket));
+		return socket;
+	}
+
+	/* Make the socket IPv6 only to allow 'any' for IPv4 and IPv6 at the same time. */
+	if (ss->ss_family == AF_INET6) {
+		/* Do not support mapping IPv4 in IPv6 sockets. */
+		int flag = 1;
+		(void) setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY,
+		                  &flag, sizeof(flag));
+	}
+
+	return socket;
+}
+
+int net_bound_socket(int type, struct sockaddr_storage *ss)
+{
+	/* Create socket. */
+	int socket = net_unbound_socket(type, ss);
+	if (socket < 0) {
+		return socket;
+	}
+
+	/* Convert to string address format. */
+	char addr_str[SOCKADDR_STRLEN] = {0};
+	sockaddr_tostr(ss, addr_str, sizeof(addr_str));
+
+	/* Reuse old address if taken. */
+	int flag = 1;
+	(void) setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
+
+	/* Unlink UNIX socket if exists. */
+	if (ss->ss_family == AF_UNIX) {
+		unlink(addr_str);
+	}
+
+	/* Bind to specified address. */
+	int ret = bind(socket, (struct sockaddr *)ss, sockaddr_len(ss));
+	if (ret < 0) {
+		ret = knot_map_errno(EADDRINUSE, EINVAL, EACCES, ENOMEM);
+		log_server_error("Cannot bind to address '%s': %s\n",
+		                 addr_str, knot_strerror(ret));
+		close(socket);
+		return ret;
+	}
+
+	return socket;
+}
+
+int net_connected_socket(int type, struct sockaddr_storage *dst_addr,
+                            struct sockaddr_storage *src_addr)
+{
+	if (dst_addr == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	int socket = -1;
+
+	/* Bind to specific source address - if set. */
+	if (src_addr != NULL && src_addr->ss_family != AF_UNSPEC) {
+		socket = net_bound_socket(type, src_addr);
+	} else {
+		socket = net_unbound_socket(type, dst_addr);
+	}
+	if (socket < 0) {
+		return socket;
+	}
+
+	/* Use non-blocking. */
+	if (fcntl(socket, F_SETFL, O_NONBLOCK) < 0)
+		; /* Go silently with blocking if it fails. */
+
+
+	/* Connect to destination. */
+	int ret = connect(socket, (struct sockaddr *)dst_addr, sockaddr_len(dst_addr));
+	if (ret != 0 && errno != EINPROGRESS) {
+		close(socket);
+		return knot_map_errno(EACCES, EADDRINUSE, EAGAIN,
+		                      ECONNREFUSED, EISCONN);
+	}
+
+	return socket;
+}
diff --git a/src/knot/server/net.h b/src/knot/server/net.h
new file mode 100644
index 0000000000000000000000000000000000000000..08c86e8403315b2bc476756bacabff492c3306e5
--- /dev/null
+++ b/src/knot/server/net.h
@@ -0,0 +1,70 @@
+/*  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 net.h
+ *
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief Generic sockets APIs.
+ *
+ * This file provides higher-level API for creating connections and listeners.
+ *
+ * \addtogroup network
+ * @{
+ */
+
+#ifndef _KNOTD_NET_H_
+#define _KNOTD_NET_H_
+
+/* POSIX only. */
+#include "common/sockaddr.h"
+
+/*!
+ * \brief Create unbound socket of given family and type.
+ *
+ * \param type  Socket transport type (SOCK_STREAM, SOCK_DGRAM).
+ * \param ss    Socket address storage.
+ *
+ * \return socket or error code
+ */
+int net_unbound_socket(int type, struct sockaddr_storage *ss);
+
+/*!
+ * \brief Create socket bound to given address.
+ *
+ * \param type  Socket transport type (SOCK_STREAM, SOCK_DGRAM).
+ * \param ss    Socket address storage.
+ *
+ * \return socket or error code
+ */
+int net_bound_socket(int type, struct sockaddr_storage *ss);
+
+/*!
+ * \brief Create socket connected (asynchronously) to destination address.
+ *
+ * \param type     Socket transport type (SOCK_STREAM, SOCK_DGRAM).
+ * \param dst_addr Destination address.
+ * \param src_addr Source address (can be NULL).
+ *
+ * \return socket or error code
+ */
+int net_connected_socket(int type, struct sockaddr_storage *dst_addr,
+                         struct sockaddr_storage *src_addr);
+
+
+#endif // _KNOTD_NET_H_
+
+/*! @} */
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index 26370962e8696b5a9d8482ac98fd08f13b8940ed..9d8ac367e236ba72d11ac0a7f097fa5ef818eb49 100644
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -94,7 +94,6 @@ static void server_remove_iface(iface_t *iface)
 	}
 
 	/* Free interface. */
-	free(iface->addr);
 	free(iface);
 }
 
@@ -113,98 +112,47 @@ static int server_init_iface(iface_t *new_if, conf_iface_t *cfg_if)
 {
 	/* Initialize interface. */
 	int ret = 0;
-	int sock = 0;
-	char errbuf[256] = {0};
 	memset(new_if, 0, sizeof(iface_t));
+	memcpy(&new_if->addr, &cfg_if->addr, sizeof(struct sockaddr_storage));
 
-	/* Create UDP socket. */
-	ret = socket_create(cfg_if->family, SOCK_DGRAM, IPPROTO_UDP);
-	if (ret < 0) {
-		if (strerror_r(errno, errbuf, sizeof(errbuf)) == 0) {
-			log_server_error("Could not create UDP socket: %s.\n",
-					 errbuf);
-		}
-		return ret;
-	} else {
-		sock = ret;
-	}
-
-	/* Set socket options. */
-	int flag = 1;
-	if (cfg_if->family == AF_INET6) {
-		/* Disable dual-stack for performance reasons. */
-		if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) {
-			dbg_net("udp: failed to set IPV6_V6ONLY to socket, using default config\n");
-		}
-	}
+	/* Convert to string address format. */
+	char addr_str[SOCKADDR_STRLEN] = {0};
+	sockaddr_tostr(&cfg_if->addr, addr_str, sizeof(addr_str));
 
-	ret = socket_bind(sock, cfg_if->family, cfg_if->address, cfg_if->port);
-	if (ret < 0) {
-		socket_close(sock);
-		log_server_error("Could not bind to "
-		                 "UDP interface %s port %d.\n",
-		                 cfg_if->address, cfg_if->port);
-		return ret;
+	/* Create bound UDP socket. */
+	int sock = net_bound_socket(SOCK_DGRAM, &cfg_if->addr);
+	if (sock < 0) {
+		return sock;
 	}
 
 	new_if->fd[IO_UDP] = sock;
-	new_if->type = cfg_if->family;
-	new_if->port = cfg_if->port;
-	new_if->addr = strdup(cfg_if->address);
 
-	/* Create TCP socket. */
-	ret = socket_create(cfg_if->family, SOCK_STREAM, IPPROTO_TCP);
-	if (ret < 0) {
-		socket_close(new_if->fd[IO_UDP]);
-		if (strerror_r(errno, errbuf, sizeof(errbuf)) == 0) {
-			log_server_error("Could not create TCP socket: %s.\n",
-					 errbuf);
-		}
-		return ret;
-	} else {
-		sock = ret;
+	/* Create bound TCP socket. */
+	sock = net_bound_socket(SOCK_STREAM, &cfg_if->addr);
+	if (sock < 0) {
+		close(new_if->fd[IO_UDP]);
+		return sock;
 	}
 
-	/* Set socket options. */
-	if (cfg_if->family == AF_INET6) {
-		if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) {
-			dbg_net("tcp: failed to set IPV6_V6ONLY to socket, using default config\n");
-		}
+	new_if->fd[IO_TCP] = sock;
+
+	/* Listen for incoming connections. */
+	ret = listen(sock, TCP_BACKLOG_SIZE);
+	if (ret < 0) {
+		close(new_if->fd[IO_UDP]);
+		close(new_if->fd[IO_TCP]);
+		log_server_error("Failed to listen on TCP interface '%s'.\n", addr_str);
+		return KNOT_ERROR;
 	}
 
 	/* accept() must not block */
 	if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
-		free(new_if->addr);
-		socket_close(new_if->fd[IO_UDP]);
-		socket_close(sock);
-		log_server_error("Failed to listen on %s@%d in non-blocking mode.\n",
-		                 cfg_if->address, cfg_if->port);
+		close(new_if->fd[IO_UDP]);
+		close(new_if->fd[IO_TCP]);
+		log_server_error("Failed to listen on '%s' in non-blocking mode.\n", addr_str);
 		return KNOT_ERROR;
 	}
 
-	ret = socket_bind(sock, cfg_if->family, cfg_if->address, cfg_if->port);
-	if (ret < 0) {
-		free(new_if->addr);
-		socket_close(new_if->fd[IO_UDP]);
-		socket_close(sock);
-		log_server_error("Could not bind to "
-		                 "TCP interface %s port %d.\n",
-		                 cfg_if->address, cfg_if->port);
-		return ret;
-	}
-
-	ret = socket_listen(sock, TCP_BACKLOG_SIZE);
-	if (ret < 0) {
-		free(new_if->addr);
-		socket_close(new_if->fd[IO_UDP]);
-		socket_close(sock);
-		log_server_error("Failed to listen on "
-		                 "TCP interface %s port %d.\n",
-		                 cfg_if->address, cfg_if->port);
-		return ret;
-	}
-
-	new_if->fd[IO_TCP] = sock;
 	return KNOT_EOK;
 }
 
@@ -213,10 +161,11 @@ static void remove_ifacelist(struct ref_t *p)
 	ifacelist_t *ifaces = (ifacelist_t *)p;
 
 	/* Remove deprecated interfaces. */
+	char addr_str[SOCKADDR_STRLEN] = {0};
 	iface_t *n = NULL, *m = NULL;
 	WALK_LIST_DELSAFE(n, m, ifaces->u) {
-		log_server_info("Removing interface %s port %d.\n",
-		                n->addr, n->port);
+		sockaddr_tostr(&n->addr, addr_str, sizeof(addr_str));
+		log_server_info("Removing interface '%s'.\n", addr_str);
 		server_remove_iface(n);
 	}
 	WALK_LIST_DELSAFE(n, m, ifaces->l) {
@@ -242,6 +191,7 @@ static int reconfigure_sockets(const struct conf_t *conf, server_t *s)
 	rcu_read_lock();
 
 	/* Prepare helper lists. */
+	char addr_str[SOCKADDR_STRLEN] = {0};
 	int bound = 0;
 	iface_t *m = 0;
 	ifacelist_t *newlist = malloc(sizeof(ifacelist_t));
@@ -266,11 +216,9 @@ static int reconfigure_sockets(const struct conf_t *conf, server_t *s)
 		if (s->ifaces) {
 			WALK_LIST(m, s->ifaces->u) {
 				/* Matching port and address. */
-				if (cfg_if->port == m->port) {
-					if (strcmp(cfg_if->address, m->addr) == 0) {
-						found_match = 1;
-						break;
-					}
+				if (sockaddr_cmp(&cfg_if->addr, &m->addr) == 0) {
+					found_match = 1;
+					break;
 				}
 			}
 		}
@@ -279,8 +227,8 @@ static int reconfigure_sockets(const struct conf_t *conf, server_t *s)
 		if (found_match) {
 			rem_node((node_t *)m);
 		} else {
-			log_server_info("Binding to interface %s port %d.\n",
-			                cfg_if->address, cfg_if->port);
+			sockaddr_tostr(&cfg_if->addr, addr_str, sizeof(addr_str));
+			log_server_info("Binding to interface %s.\n", addr_str);
 
 			/* Create new interface. */
 			m = malloc(sizeof(iface_t));
diff --git a/src/knot/server/server.h b/src/knot/server/server.h
index 3b8dff33058579e3d863c8f6ebcc9323599fb8be..f92d6b8d6f15b1650adf9f161076eb62431be5b2 100644
--- a/src/knot/server/server.h
+++ b/src/knot/server/server.h
@@ -40,7 +40,7 @@
 #include "common/lists.h"
 #include "knot/server/xfr-handler.h"
 #include "knot/server/dthreads.h"
-#include "knot/server/socket.h"
+#include "knot/server/net.h"
 #include "knot/server/rrl.h"
 #include "knot/zone/zonedb.h"
 
@@ -77,9 +77,7 @@ typedef enum {
 typedef struct iface_t {
 	struct node n;
 	int fd[2];
-	int type;
-	int port;    /*!< \brief Socket port. */
-	char* addr;  /*!< \brief Socket address. */
+	struct sockaddr_storage addr;
 } iface_t;
 
 /* Handler types. */
diff --git a/src/knot/server/socket.c b/src/knot/server/socket.c
deleted file mode 100644
index 0c6963b1efc2a50975da627bf1d3d6745e8f7c01..0000000000000000000000000000000000000000
--- a/src/knot/server/socket.c
+++ /dev/null
@@ -1,123 +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>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <netdb.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#ifdef HAVE_NETINET_IN_SYSTM_H
-#include <netinet/in_systm.h>
-#endif
-#include <sys/stat.h>
-#include <assert.h>
-
-#include "knot/knot.h"
-#include "knot/server/socket.h"
-
-int socket_create(int family, int type, int proto)
-{
-	/* Create socket. */
-	int ret = socket(family, type, proto);
-	if (ret < 0) {
-		return knot_map_errno(EACCES, EINVAL, ENOMEM);
-	}
-
-	return ret;
-}
-
-int socket_connect(int fd, int family, const char *addr, unsigned short port)
-{
-	struct sockaddr_storage ss;
-	int ret = sockaddr_set(&ss, family, addr, port);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
-	ret = connect(fd, (struct sockaddr *)&ss, sockaddr_len(&ss));
-	if (ret < 0) {
-		ret = knot_map_errno(EACCES, EADDRINUSE, EAGAIN,
-		                     ECONNREFUSED, EISCONN);
-	}
-
-	return ret;
-}
-
-int socket_bind(int socket, int family, const char *addr, unsigned short port)
-{
-	/* Check address family. */
-	int flag = 1;
-	struct sockaddr_storage ss;
-	int ret = sockaddr_set(&ss, family, addr, port);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
-	/* Make the socket IPv6 only to allow 'any' for IPv4 and IPv6 at the same time. */
-	if (family == AF_INET6) {
-		/* Do not support mapping IPv4 in IPv6 sockets. */
-		ret = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY,
-		                 &flag, sizeof(flag));
-		if (ret < 0) {
-			return KNOT_EINVAL;
-		}
-	}
-
-	/* Unlink UNIX socket if exists. */
-	if (family == AF_UNIX) {
-		unlink(addr);
-	}
-
-	/* Reuse old address if taken. */
-	ret = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
-	if (ret < 0) {
-		return KNOT_EINVAL;
-	}
-
-	/* Bind to specified address. */
-	ret = bind(socket, (struct sockaddr *)&ss, sockaddr_len(&ss));
-	if (ret < 0) {
-		ret = knot_map_errno(EADDRINUSE, EINVAL, EACCES, ENOMEM);
-		log_server_error("Cannot bind to socket: %s\n", knot_strerror(ret));
-	}
-
-	return ret;
-}
-
-int socket_listen(int socket, int backlog_size)
-{
-	int ret = listen(socket, backlog_size);
-	if (ret < 0) {
-		return knot_map_errno(EADDRINUSE);
-	}
-
-	return KNOT_EOK;
-}
-
-int socket_close(int socket)
-{
-	if (close(socket) < 0) {
-		return KNOT_ERROR;
-	}
-
-	return KNOT_EOK;
-}
diff --git a/src/knot/server/socket.h b/src/knot/server/socket.h
deleted file mode 100644
index 0e8f336310d542f09088e3b83c74c376429933fc..0000000000000000000000000000000000000000
--- a/src/knot/server/socket.h
+++ /dev/null
@@ -1,113 +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 socket.h
- *
- * \author Marek Vavrusa <marek.vavusa@nic.cz>
- *
- * \brief Generic sockets APIs.
- *
- * This file provides platform-independent sockets.
- * Functions work on sockets created via system socket(2) functions.
- *
- * You can use standard I/O functions send(), sendto(), recv(), recvfrom()
- * like you would with a normal sockets.
- *
- * \addtogroup network
- * @{
- */
-
-#ifndef _KNOTD_SOCKET_H_
-#define _KNOTD_SOCKET_H_
-
-/* POSIX only. */
-#include "common/sockaddr.h"
-
-/*!
- * \brief Create socket.
- *
- * \param family Socket family (PF_INET, PF_IPX, PF_PACKET, PF_UNIX).
- * \param type   Socket type (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW).
- *
- * \retval new socket filedescriptor on success.
- * \retval KNOT_EINVAL on invalid parameters.
- * \retval KNOT_ENOMEM out of memory error.
- * \retval KNOT_EACCES process does not have appropriate privileges.
- * \retval KNOT_ERROR unspecified error.
- */
-int socket_create(int family, int type, int proto);
-
-/*!
- * \brief Connect to remote host.
- *
- * \param fd     Socket filedescriptor.
- * \param family Socket family.
- * \param addr   Requested address.
- * \param port   Requested port.
- *
- * \retval KNOT_EOK on success.
- * \retval KNOT_EINVAL invalid parameters.
- * \retval KNOT_EACCES process does not have appropriate privileges.
- * \retval KNOT_EAGAIN lack of resources, try again.
- * \retval KNOT_EADDRINUSE address already in use.
- * \retval KNOT_ECONNREFUSED connection refused.
- * \retval KNOT_EISCONN already connected.
- * \retval KNOT_ERROR unspecified error.
- */
-int socket_connect(int fd, int family, const char *addr, unsigned short port);
-
-/*!
- * \brief Listen on given socket.
- *
- * \param fd     Socket filedescriptor.
- * \param family Socket family.
- * \param addr   Requested address.
- * \param port   Requested port.
- *
- * \retval KNOT_EOK on success.
- * \retval KNOT_EINVAL invalid parameters.
- * \retval KNOT_EACCES process does not have appropriate privileges.
- * \retval KNOT_EADDRINUSE address already in use.
- * \retval KNOT_ENOMEM out of memory error.
- * \retval KNOT_ERROR unspecified error.
- */
-int socket_bind(int fd, int family, const char *addr, unsigned short port);
-
-/*!
- * \brief Listen on given TCP socket.
- *
- * \param fd           Socket filedescriptor.
- * \param backlog_size Requested TCP backlog size.
- *
- * \retval KNOT_EOK on success.
- * \retval KNOT_EADDRINUSE address already in use.
- * \retval KNOT_ERROR unspecified error.
- */
-int socket_listen(int fd, int backlog_size);
-
-/*!
- * \brief Close and deinitialize socket.
- *
- * \param fd Socket filedescriptor.
- *
- * \retval KNOT_EOK on success.
- * \retval KNOT_EINVAL invalid parameters.
- */
-int socket_close(int fd);
-
-#endif // _KNOTD_SOCKET_H_
-
-/*! @} */
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index 82dbe17c4338865290493ffe9f2cd3451d4cf290..baa9f33e426cbf22068f24a69241f67fd266620a 100644
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -88,7 +88,7 @@ static enum fdset_sweep_state tcp_sweep(fdset_t *set, int i, void *data)
 	log_server_notice("Connection '%s' was terminated due to inactivity.\n",
 	                  addr_str);
 
-	socket_close(fd);
+	close(fd);
 	return FDSET_SWEEP;
 }
 
@@ -277,7 +277,7 @@ static int tcp_event_accept(tcp_context_t *tcp, unsigned i)
 		/* Assign to fdset. */
 		int next_id = fdset_add(&tcp->set, client, POLLIN, NULL);
 		if (next_id < 0) {
-			socket_close(client);
+			close(client);
 			return next_id; /* Contains errno. */
 		}
 
@@ -344,7 +344,7 @@ static int tcp_wait_for_events(tcp_context_t *tcp)
 
 		if (set->pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL)) {
 			fdset_remove(set, i);
-			socket_close(fd);
+			close(fd);
 			continue; /* Stay on the same index. */
 		}
 
@@ -401,7 +401,7 @@ int tcp_master(dthread_t *thread)
 
 			/* Cancel client connections. */
 			for (unsigned i = tcp.client_threshold; i < tcp.set.n; ++i) {
-				socket_close(tcp.set.pfd[i].fd);
+				close(tcp.set.pfd[i].fd);
 			}
 
 			ref_release(ref);
diff --git a/src/knot/server/tcp-handler.h b/src/knot/server/tcp-handler.h
index c67c7b8708afc2479b9211fcabbf594ce6024a7a..2265d60f8e4153e0b6b5b822e752d6c8fd0f6b06 100644
--- a/src/knot/server/tcp-handler.h
+++ b/src/knot/server/tcp-handler.h
@@ -33,7 +33,7 @@
 
 #include <stdint.h>
 
-#include "knot/server/socket.h"
+#include "knot/server/net.h"
 #include "knot/server/server.h"
 #include "knot/server/dthreads.h"
 
diff --git a/src/knot/server/udp-handler.h b/src/knot/server/udp-handler.h
index 11e7d6eab62fd97c517fbe0dc7f1f23d22a2833b..67ef81e3cb23cab52bb20b255bb400fccc611ca9 100644
--- a/src/knot/server/udp-handler.h
+++ b/src/knot/server/udp-handler.h
@@ -31,7 +31,7 @@
 #ifndef _KNOTD_UDPHANDLER_H_
 #define _KNOTD_UDPHANDLER_H_
 
-#include "knot/server/socket.h"
+#include "knot/server/net.h"
 #include "knot/server/server.h"
 #include "knot/server/dthreads.h"
 
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index 57386e9bca55ecdc8b151a5ee631bc8f16e17983..4253fe7fff56e1835f0297814922d10fac9b93f3 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -30,7 +30,7 @@
 
 #include "knot/server/xfr-handler.h"
 #include "knot/server/server.h"
-#include "knot/server/socket.h"
+#include "knot/server/net.h"
 #include "knot/server/udp-handler.h"
 #include "knot/server/tcp-handler.h"
 #include "knot/updates/xfr-in.h"
@@ -102,25 +102,13 @@ static int xfr_send_tcp(int fd, struct sockaddr *addr, uint8_t *msg, size_t msgl
 { return tcp_send(fd, msg, msglen); }
 
 static int xfr_send_udp(int fd, struct sockaddr *addr, uint8_t *msg, size_t msglen)
-{
-	struct sockaddr_storage *ss = (struct sockaddr_storage *)addr;
-	if (ss->ss_family == AF_INET) {
-		return sendto(fd, msg, msglen, 0, addr, sizeof(struct sockaddr_in));
-	}
-	if (ss->ss_family == AF_INET6) {
-		return sendto(fd, msg, msglen, 0, addr, sizeof(struct sockaddr_in6));
-	}
-	return KNOT_EINVAL;
-}
+{ return sendto(fd, msg, msglen, 0, addr, sockaddr_len((struct sockaddr_storage *)addr));  }
 
 static int xfr_recv_tcp(int fd, struct sockaddr *addr, uint8_t *buf, size_t buflen)
 { return tcp_recv(fd, buf, buflen, addr); }
 
 static int xfr_recv_udp(int fd, struct sockaddr *addr, uint8_t *buf, size_t buflen)
-{
-	socklen_t addrlen = sizeof(struct sockaddr_storage);
-	return recvfrom(fd, buf, buflen, 0, addr, &addrlen);
-}
+{ return recv(fd, buf, buflen, 0); }
 
 /*! \todo This should be obsoleted by the generic response parsing layer. */
 static int parse_packet(knot_pkt_t *packet, knot_pkt_type_t *type)
@@ -257,38 +245,16 @@ 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 ret = 0;
 	int stype = (rq->flags & XFR_FLAG_TCP) ? SOCK_STREAM : SOCK_DGRAM;
-	int fd = socket_create(rq->addr.ss_family, stype, 0);
-	if (fd < 0) {
-		return KNOT_ERROR;
-	}
-
-	/* Bind to specific address - if set. */
-	if (rq->saddr.ss_family != AF_UNSPEC) {
-		if (bind(fd, (struct sockaddr *)&rq->saddr, sizeof(rq->saddr)) < 0) {
-			socket_close(fd);
-			return KNOT_ERROR;
-		}
-	}
-
-	/* Connect if TCP. */
-	if (rq->flags & XFR_FLAG_TCP) {
-		if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
-			; /* Go silently with blocking if it fails. */
-
-		ret = connect(fd, (struct sockaddr *)&rq->addr, sizeof(rq->addr));
-		if (ret != 0 && errno != EINPROGRESS) {
-			socket_close(fd);
-			return KNOT_ECONNREFUSED;
-		}
+	int ret = net_connected_socket(stype, &rq->addr, &rq->saddr);
+	if (ret < 0) {
+		return ret;
 	}
 
 	/* Set up for UDP as well to trigger 'send query' event. */
+	rq->session = ret;
 	rq->flags |= XFR_FLAG_CONNECTING;
 
-	/* Store new socket descriptor. */
-	rq->session = fd;
 	return KNOT_EOK;
 }
 
@@ -945,7 +911,7 @@ static enum fdset_sweep_state xfr_sweep(fdset_t *set, int i, void *data)
 		if (xfr_task_is_transfer(rq))
 			xfr_pending_decr(xfr);
 		xfr_task_close(rq);
-		socket_close(set->pfd[i].fd);
+		close(set->pfd[i].fd);
 		return FDSET_SWEEP;
 	}
 
@@ -1063,7 +1029,7 @@ int xfr_worker(dthread_t *thread)
 				if (xfr_task_is_transfer(rq))
 					xfr_pending_decr(xfr);
 				xfr_task_close(rq);
-				socket_close(set.pfd[i].fd);
+				close(set.pfd[i].fd);
 				fdset_remove(&set, i);
 				continue; /* Stay on the same index. */
 			} else {
@@ -1093,7 +1059,7 @@ int xfr_worker(dthread_t *thread)
 	/* Cancel existing connections. */
 	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);
+		close(set.pfd[i].fd);
 		if (xfr_task_is_transfer(rq))
 			xfr_pending_decr(xfr);
 		xfr_task_free(rq);
diff --git a/src/knot/server/xfr-handler.h b/src/knot/server/xfr-handler.h
index 30f2cfe71732ff91dbe8850596adc0d8b2b64229..e79241535cb1775aacfdde77540edb05a6c35cc7 100644
--- a/src/knot/server/xfr-handler.h
+++ b/src/knot/server/xfr-handler.h
@@ -30,7 +30,7 @@
 #include "common/fdset.h"
 #include "common/evsched.h"
 #include "knot/server/dthreads.h"
-#include "knot/server/socket.h"
+#include "knot/server/net.h"
 #include "libknot/packet/pkt.h"
 #include "knot/zone/zone.h"
 
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index dda05a8b0a4b62ec555b25eac4a5ff48f4b0287d..9dc89ab8ceef956fdc33ea90222f3afd1bfd6c40 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -1547,9 +1547,7 @@ int zones_schedule_notify(zone_t *zone, server_t *server)
 		}
 
 		/* Parse server address. */
-		struct sockaddr_storage addr;
-		sockaddr_set(&addr, cfg_if->family, cfg_if->address, cfg_if->port);
-		xfr_task_setaddr(rq, &addr, &cfg_if->via);
+		xfr_task_setaddr(rq, &cfg_if->addr, &cfg_if->via);
 		rq->data = (void *)((long)cfg->notify_retries);
 		if (xfr_enqueue(server->xfr, rq) != KNOT_EOK) {
 			log_zone_error("Failed to enqueue NOTIFY for '%s'.\n",
diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c
index ebfa51e0da0d8dcd0501e8fa36984d383b0dadd5..137341aa5f341b21b93700ad106807c2c077d73c 100644
--- a/src/knot/zone/zone.c
+++ b/src/knot/zone/zone.c
@@ -159,12 +159,8 @@ static int set_acl(acl_t **acl, list_t* acl_list)
 	/* Load ACL rules. */
 	conf_remote_t *r = 0;
 	WALK_LIST(r, *acl_list) {
-		/* Initialize address. */
-		/*! Port matching disabled, port = 0. */
 		conf_iface_t *cfg_if = r->remote;
-		struct sockaddr_storage addr;
-		sockaddr_set(&addr, cfg_if->family, cfg_if->address, 0);
-		acl_insert(new_acl, &addr, cfg_if->prefix, cfg_if->key);
+		acl_insert(new_acl, &cfg_if->addr, cfg_if->prefix, cfg_if->key);
 	}
 
 	*acl = new_acl;
@@ -186,8 +182,7 @@ static void set_xfrin_parameters(zone_t *zone, conf_zone_t *conf)
 
 	conf_remote_t *master = HEAD(conf->acl.xfr_in);
 	conf_iface_t *master_if = master->remote;
-	sockaddr_set(&zone->xfr_in.master, master_if->family,
-	             master_if->address, master_if->port);
+	memcpy(&zone->xfr_in.master, &master_if->addr, sizeof(struct sockaddr_storage));
 	if (master_if->via.ss_family != AF_UNSPEC) {
 		memcpy(&zone->xfr_in.via, &master_if->via, sizeof(struct sockaddr_storage));
 	}
diff --git a/tests/conf.c b/tests/conf.c
index b6e22be2fa013136cf2e48d2f50e91b05e8a472a..ec8737b4709780372dae1f1fe61bdecaf0947e3c 100644
--- a/tests/conf.c
+++ b/tests/conf.c
@@ -27,7 +27,7 @@
  */
 int main(int argc, char *argv[])
 {
-	plan(21);
+	plan(19);
 
 	// Test 1: Allocate new config
 	const char *config_fn = "rc:/sample_conf";
@@ -48,15 +48,17 @@ int main(int argc, char *argv[])
 	// Test 4: Test interfaces (1-level depth)
 	ok(!EMPTY_LIST(conf->ifaces), "configured interfaces exist");
 
-	// Test 5,6,7,8: Interfaces content (2-level depth)
+	// Test 5,6: Interfaces content (2-level depth)
 	struct node *n = HEAD(conf->ifaces);
 	conf_iface_t *iface = (conf_iface_t*)n;
-	is_string("10.10.1.1", iface->address, "interface0 address check");
-	is_int(53531, iface->port, "interface0 port check");
+	struct sockaddr_storage addr_ref;
+	sockaddr_set(&addr_ref, AF_INET, "10.10.1.1", 53531);
+	is_int(0, sockaddr_cmp(&iface->addr, &addr_ref), "interface0 address check");
+
 	n = n->next;
 	iface = (conf_iface_t*)n;
-	is_string("::0", iface->address, "interface1 address check");
-	is_int(53, iface->port, "interface1 default port check");
+	sockaddr_set(&addr_ref, AF_INET6, "::0", 53);
+	is_int(0, sockaddr_cmp(&iface->addr, &addr_ref), "interface1 address check");
 
 	// Test 9,10: Check server key
 	if(conf->key_count <= 0) {