diff --git a/AUTHORS b/AUTHORS
index c032e473b483b4ac2acaea6bf049acfe1044ac3c..82bb742e4d281962dd54dd9f301287eb52cb76bc 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,5 +1,6 @@
 Ľuboš Slovák <lubos.slovak@nic.cz>
 Marek Vavruša <marek.vavrusa@nic.cz>
 Jan Kadlec <jan.kadlec@nic.cz>
+Daniel Salzman <daniel.salzman@nic.cz>
 Ondřej Surý <ondrej.sury@nic.cz>
 Ondřej Filip <ondrej.filip@nic.cz>
diff --git a/Knot.files b/Knot.files
index 56517c0314aba41d72beee8d7e8fd1c8d1ff0e4e..177609eeb3c941ca53ab0d28699bc0e8f1878fad 100644
--- a/Knot.files
+++ b/Knot.files
@@ -47,6 +47,7 @@ src/libknot/util/utils.h
 src/libknot/util/utils.c
 src/libknot/util/descriptor.h
 src/libknot/util/descriptor.c
+src/libknot/util/endian.h
 src/libknot/zone/zonedb.h
 src/libknot/zone/zonedb.c
 src/libknot/zone/node.h
@@ -191,13 +192,15 @@ src/tests/knot/server_tests.h
 src/tests/libknot/unittests_libknot.c
 src/tests/libknot/libknot/rrset_tests.c
 src/tests/libknot/libknot/rrset_tests.h
+src/tests/libknot/libknot/dname_tests.c
+src/tests/libknot/libknot/dname_tests.h
+src/tests/libknot/libknot/wire_tests.c
+src/tests/libknot/libknot/wire_tests.h
 samples/Makefile.am
 src/libknot/tsig.h
 src/libknot/tsig.c
 src/libknot/tsig-op.c
 src/libknot/tsig-op.h
-src/tests/libknot/libknot/tsig_tests.h
-src/tests/libknot/libknot/tsig_tests.c
 src/knot/zone/semantic-check.c
 src/knot/zone/semantic-check.h
 src/tests/xfr_tests.h
@@ -220,6 +223,7 @@ src/libknot/zone/zone-diff.h
 src/libknot/zone/zone-diff.c
 src/zscanner/file_loader.c
 src/zscanner/file_loader.h
+src/zscanner/scanner.rl
 src/zscanner/scanner.h
 src/zscanner/scanner_body.rl
 src/zscanner/scanner_functions.c
diff --git a/RELNOTES b/RELNOTES
index 78da0384b00f372b5063f4b687304bd0ac6b5cc9..3d4d6d762e048084b199aeb70ebc733ba86e84c6 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -1,3 +1,7 @@
+v1.2 - Jan 30, 2013
+-------------------
+
+
 v1.2-rc1 - Jan 4, 2013
 ------------------
 
diff --git a/configure.ac b/configure.ac
index 72d145f2ba0d125b8fe0011723b7c959c4b469f1..7b00e32e0ed49d32df2fa8893b31d096ee06662c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
 #                                               -*- Autoconf -*-
 
 AC_PREREQ([2.60])
-AC_INIT([knot], [1.2-rc1], [knot-dns@labs.nic.cz])
+AC_INIT([knot], [1.2], [knot-dns@labs.nic.cz])
 AM_INIT_AUTOMAKE([gnu -Wall -Werror])
 AC_CONFIG_SRCDIR([src/knot/main.c])
 AC_CONFIG_HEADERS([src/config.h])
diff --git a/src/Makefile.am b/src/Makefile.am
index 59aeccb13f33bbfb61498a4eb0f79e7cbf6cf955..56c80ddf1a8efcf90fa7519ec6b928482a09fbe4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,10 @@ unittests_SOURCES =				\
 	tests/knot/journal_tests.h		\
 	tests/knot/server_tests.c		\
 	tests/knot/server_tests.h		\
+	tests/libknot/dname_tests.c		\
+	tests/libknot/dname_tests.h		\
+	tests/libknot/wire_tests.h		\
+	tests/libknot/wire_tests.c		\
 	tests/unittests_main.c
 	
 unittests_libknot_SOURCES = 			\
@@ -74,6 +78,7 @@ libknot_la_SOURCES =				\
 	libknot/util/tolower.h			\
 	libknot/util/tolower.c			\
 	libknot/util/wire.h			\
+	libknot/util/endian.h			\
 	libknot/packet/query.c			\
 	libknot/packet/response.c		\
 	libknot/packet/packet.c			\
diff --git a/src/common/errcode.c b/src/common/errcode.c
index 2efedea87fd6ce47a5ef633eeb4e0040f21b2ee5..9065504dc9ad62a0be76b9ab603e046d64c83bf8 100644
--- a/src/common/errcode.c
+++ b/src/common/errcode.c
@@ -71,6 +71,7 @@ const error_table_t knot_error_msgs[] = {
 	{KNOT_ENODIFF, "Cannot create zone diff."},
 	{KNOT_EDSDIGESTLEN, "DS digest length does not match digest type."},
 	{KNOT_ENOTSIG, "expected a TSIG or SIG(0)" },
+	{KNOT_ESTOP, "Stop." },
 
 	/* Zone file loader errors. */
 	{FLOADER_EFSTAT, "Fstat error!"},
diff --git a/src/common/errcode.h b/src/common/errcode.h
index 5087983a7d0974f59aa42f1ffacae2fcd5075a71..fa4e53da9a080554202c9d8204ba7cde3fd58398 100644
--- a/src/common/errcode.h
+++ b/src/common/errcode.h
@@ -44,7 +44,7 @@ enum knot_error {
 	KNOT_EINVAL = -EINVAL,             /*!< Invalid parameter passed. */
 	KNOT_ENOTSUP = -ENOTSUP,           /*!< Parameter not supported. */
 	KNOT_EBUSY = -EBUSY,               /*!< Requested resource is busy. */
-	KNOT_EAGAIN = -EAGAIN,            /*!< OS lacked necessary resources. */
+	KNOT_EAGAIN = -EAGAIN,             /*!< OS lacked necessary resources. */
 	KNOT_EACCES = -EACCES,             /*!< Permission is denied. */
 	KNOT_ECONNREFUSED = -ECONNREFUSED, /*!< Connection is refused. */
 	KNOT_EISCONN = -EISCONN,           /*!< Already connected. */
@@ -84,7 +84,8 @@ enum knot_error {
 	KNOT_ECNAME,          /*!< CNAME loop found in zone. */
 	KNOT_ENODIFF,         /*!< No zone diff can be created. */
 	KNOT_EDSDIGESTLEN,    /*!< DS digest length does not match digest type. */
-	KNOT_ENOTSIG,         /*!< expected a TSIG or SIG(0) */
+	KNOT_ENOTSIG,         /*!< Expected a TSIG or SIG(0). */
+	KNOT_ESTOP,           /*!< Stop doing something. */
 
 	/* Zone file loader errors. */
 	FLOADER_EFSTAT,
diff --git a/src/common/lists.c b/src/common/lists.c
index a0f10b61fee10aab68c0b8efef3731e10b487772..9672d630c09e825be63165e1bc6ec85e24fadeb5 100644
--- a/src/common/lists.c
+++ b/src/common/lists.c
@@ -165,7 +165,7 @@ void list_dup(list *dst, list *src, size_t itemsz)
  *
  * This function counts nodes in list @l and returns this number.
  */
-size_t list_size(list *l)
+size_t list_size(const list *l)
 {
 	size_t count = 0;
 
diff --git a/src/common/lists.h b/src/common/lists.h
index ec0c15ee47200fc50dff946f1bf45d3bb36b4ff2..392f75dd73943548eaa4fa6dc1d369db07701d17 100644
--- a/src/common/lists.h
+++ b/src/common/lists.h
@@ -81,7 +81,7 @@ void add_tail_list(list *, list *);
 void init_list(list *);
 void insert_node(node *, node *);
 void list_dup(list *dst, list *src, size_t itemsz);
-size_t list_size(list *);
+size_t list_size(const list *);
 
 /*!
  * \brief List item for string lists.
diff --git a/src/knot.conf.5 b/src/knot.conf.5
index 0f6194f653adffc35b375c19803a22882b27a4d8..927ad44886f9e0680d616c068cdc81022b14ecba 100644
--- a/src/knot.conf.5
+++ b/src/knot.conf.5
@@ -1,4 +1,4 @@
-.TH "knot.conf" "5" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2-rc1"
+.TH "knot.conf" "5" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2"
 .SH "NAME"
 .LP
 .B knot.conf
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 79277d2f7b7cd1fcfe182fa1992b16e3f5e25170..b1057c0562ce4d9b841c5ca220f7328852ef8964 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -1849,29 +1849,6 @@ static int zones_check_tsig_query(const knot_zone_t *zone,
 	assert(rcode != NULL);
 	assert(tsig_key_zone != NULL);
 
-	const knot_rrset_t *tsig = knot_packet_tsig(query);
-
-	// not required, TSIG is already found
-//	if (knot_packet_additional_rrset_count(query) > 0) {
-//		/*! \todo warning */
-//		tsig = knot_packet_additional_rrset(query,
-//		                 knot_packet_additional_rrset_count(query) - 1);
-//		if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) {
-//			dbg_zones_verb("found TSIG in normal query\n");
-//		} else {
-//			tsig = NULL; /* Invalidate if not TSIG RRTYPE. */
-//		}
-//	}
-
-	if (tsig == NULL) {
-		// no TSIG, this is completely valid
-		/*! \note This function is (should be) called only in case of
-		          normal query, i.e. we do not have to check ACL.
-		 */
-		*tsig_rcode = 0;
-		return KNOT_EOK;
-	}
-
 	// if there is some TSIG in the query, find the TSIG associated with
 	// the zone
 	dbg_zones_verb("Checking zone and ACL.\n");
@@ -1889,10 +1866,14 @@ static int zones_check_tsig_query(const knot_zone_t *zone,
 			                              tsig_prev_time_signed);
 		} else {
 			dbg_zones_verb("No key configured for zone.\n");
-			// no key configured for zone, return BADKEY
-			*tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
-			*rcode = KNOT_RCODE_NOTAUTH;
-			ret = KNOT_TSIG_EBADKEY;
+			if (knot_packet_tsig(query)) {
+				// no key configured for zone, return BADKEY
+				dbg_zones_verb("TSIG used, but not configured "
+				               "for this zone, ret=BADKEY.\n");
+				*tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+				*rcode = KNOT_RCODE_NOTAUTH;
+				ret = KNOT_TSIG_EBADKEY;
+			}
 		}
 	}
 
@@ -2580,10 +2561,17 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
 			ret = KNOT_TSIG_EBADKEY;
 		} else {
 			dbg_zones_verb("Checking TSIG in query.\n");
-			ret = zones_check_tsig_query(zone, query, addr,
-			                             &rcode, &tsig_rcode,
-			                             &tsig_key_zone,
-			                             &tsig_prev_time_signed);
+			const knot_rrset_t *tsig = knot_packet_tsig(query);
+			if (tsig == NULL) {
+				// no TSIG, this is completely valid
+				tsig_rcode = 0;
+				ret = KNOT_EOK;
+			} else {
+				ret = zones_check_tsig_query(zone, query, addr,
+				                             &rcode, &tsig_rcode,
+				                             &tsig_key_zone,
+				                             &tsig_prev_time_signed);
+			}
 		}
 
 		if (ret == KNOT_EOK) {
@@ -2754,104 +2742,43 @@ int zones_process_update(knot_nameserver_t *nameserver,
 	knot_packet_t *resp = NULL;
 	knot_zone_t *zone = NULL;
 	knot_rcode_t rcode = KNOT_RCODE_NOERROR;
+	size_t rsize_max = *rsize;
+	knot_key_t *tsig_key_zone = NULL;
+	uint16_t tsig_rcode = 0;
+	uint64_t tsig_prev_time_signed = 0;
+	const knot_rrset_t *tsig_rr = NULL; 
 
 	// Parse rest of the query, prepare response, find zone
-	dbg_zones_verb("Preparing response structure.\n");
 	int ret = knot_ns_prep_update_response(nameserver, query, &resp, &zone,
 	                                       (transport == NS_TRANSPORT_TCP)
 	                                       ? *rsize : 0);
+	dbg_zones_verb("Preparing response structure = %s\n", knot_strerror(ret));
 	switch (ret) {
-	case KNOT_EOK:
-		rcode = KNOT_RCODE_NOERROR;
-		break;
-	case KNOT_EMALF:
-		// no TSIG signing in this case
+	case KNOT_EOK: break;
+	case KNOT_EMALF: /* No TSIG signing in this case. */
 		rcode = KNOT_RCODE_FORMERR;
 		break;
 	default:
-		// no TSIG signing in this case
-		/*! \todo ref #937 is SERVFAIL answer really unsigned? */
 		rcode = KNOT_RCODE_SERVFAIL;
 		break;
 	}
 
-	/* Check if zone is not discarded. */
-	if (zone && (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED)) {
-		rcode = KNOT_RCODE_SERVFAIL;
-	}
-
+	/* Check if zone is valid. */
 	const knot_zone_contents_t *contents = knot_zone_contents(zone);
-
-	/*
-	 * 1) DDNS Zone Section check (RFC2136, Section 3.1).
-	 * Do not have to check the return value, the RCODE is sufficient.
-	 */
-	(void)knot_ddns_check_zone(contents, query, &rcode);
-
-	/*
-	 * 2) DDNS Prerequisities Section processing (RFC2136, Section 3.2).
-	 *
-	 * Altough IMHO completely illogical, the prerequisities should be
-	 * checked BEFORE ACL & TSIG checks. Well, never mind, just follow the
-	 * RFC...
-	 */
-	// a) Convert prerequisities
-	dbg_zones_verb("Processing prerequisities.\n");
-	knot_ddns_prereq_t *prereqs = NULL;
-	ret = knot_ddns_process_prereqs(query, &prereqs, &rcode);
-	if (ret != KNOT_EOK) {
-		dbg_zones("Failed to check zone for update: "
-		       "%s.\n", knot_strerror(ret));
-		// RCODE should be set, checked later
-	} else {
-		assert(prereqs != NULL);
-
-		// b) Check prerequisities
-		dbg_zones_verb("Checking prerequisities.\n");
-		ret = knot_ddns_check_prereqs(contents, &prereqs, &rcode);
-		if (ret != KNOT_EOK) {
-			dbg_zones("Failed to check zone for update: "
-			       "%s.\n", knot_strerror(ret));
-			// RCODE should be set, checked later
-		}
-
-		// Not needed anymore
-		knot_ddns_prereqs_free(&prereqs);
-	}
-	
-	if (rcode != KNOT_RCODE_NOERROR) {
-		dbg_zones_verb("Failed preparing response structure: %s.\n",
-		               knot_strerror(rcode));
-
-		// Error response without Question section
-		/*! \todo Change to error_response_from_query() to retain the
-		 *        Question section if possible - see
-		 *        normal_query_answer().
-		 */
-		knot_ns_error_response_from_query_wire(nameserver,
-		                          knot_packet_wireformat(query),
-		                          knot_packet_size(query),
-		                          rcode, resp_wire, rsize);
-		knot_packet_free(&resp);
-		rcu_read_unlock();
-		return KNOT_EOK;
-	}
-
-
-
-	/* Now we have zone. Verify TSIG if it is in the packet. */
-	size_t rsize_max = *rsize;
-	knot_key_t *tsig_key_zone = NULL;
-	uint16_t tsig_rcode = 0;
-	uint64_t tsig_prev_time_signed = 0;
-	const knot_rrset_t *tsig_rr = knot_packet_tsig(query);
-	if (zone == NULL) {
-		/* Treat as BADKEY error. */
-		assert(knot_packet_tsig(query) != NULL);
+	if (zone && (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED)) {
+		rcode = KNOT_RCODE_SERVFAIL; /* It's ok, temporarily. */
+		tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+		ret = KNOT_ENOZONE;
+	} else if (!zone || !contents) {     /* Treat as BADKEY. */
 		rcode = KNOT_RCODE_NOTAUTH;
 		tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
 		ret = KNOT_TSIG_EBADKEY;
-	} else {
+		dbg_zones_verb("No zone or empty, refusing UPDATE.\n");
+	}
+
+	/* Verify TSIG if it is in the packet. */
+	tsig_rr = knot_packet_tsig(query);
+	if (ret == KNOT_EOK) { /* Have valid zone to check ACLs against. */
 		dbg_zones_verb("Checking TSIG in query.\n");
 		ret = zones_check_tsig_query(zone, query, addr,
 					     &rcode, &tsig_rcode,
@@ -2861,7 +2788,8 @@ int zones_process_update(knot_nameserver_t *nameserver,
 
 	/* Allow pass-through of an unknown TSIG in DDNS forwarding (must have zone). */
 	if (zone && (ret == KNOT_EOK || (ret == KNOT_TSIG_EBADKEY && !tsig_key_zone))) {
-		/* Message is authenticated and has primary master,
+		/* Transaction is authenticated (or unprotected)
+		 * and zone has primary master set,
 		 * proceed to forward the query to the next hop.
 		 */
 		zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
@@ -2874,42 +2802,57 @@ int zones_process_update(knot_nameserver_t *nameserver,
 			return ret;
 		}
 	}
+	
+	/*
+	 * 1) DDNS Zone Section check (RFC2136, Section 3.1).
+	 */
+	if (ret == KNOT_EOK) {
+		ret = knot_ddns_check_zone(contents, query, &rcode);
+		dbg_zones_verb("Checking zone = %s\n", knot_strerror(ret));
+	}
+
+	/*
+	 * 2) DDNS Prerequisities Section processing (RFC2136, Section 3.2).
+	 *
+	 * \note Permissions section means probably policies and fine grained
+	 *       access control, not transaction security.
+	 */
+	knot_ddns_prereq_t *prereqs = NULL;
+	if (ret == KNOT_EOK) {
+		ret = knot_ddns_process_prereqs(query, &prereqs, &rcode);
+		dbg_zones_verb("Processing prereq = %s\n", knot_strerror(ret));
+	}
+	if (ret == KNOT_EOK) {
+		assert(prereqs != NULL);
+		ret = knot_ddns_check_prereqs(contents, &prereqs, &rcode);
+		dbg_zones_verb("Checking prereq = %s\n", knot_strerror(ret));
+		knot_ddns_prereqs_free(&prereqs);
+	}
 
-	/* Process query. */
+	/*
+	 * 3) Process query.
+	 */
 	if (ret == KNOT_EOK) {
-		/* Lock zone for xfers. */
 		zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
 		pthread_mutex_lock(&zd->xfr_in.lock);
 
-		/* This function expects RCU locked. */
+		/*! \note This function expects RCU locked. */
 		ret = zones_process_update_auth(zone, resp, resp_wire, rsize,
 		                                &rcode, addr, tsig_key_zone);
-
+		dbg_zones_verb("Auth, update_proc = %s\n", knot_strerror(ret));
+		
 		pthread_mutex_unlock(&zd->xfr_in.lock);
-	} else {
-		if (tsig_rcode != KNOT_RCODE_NOERROR) {
-			dbg_zones_verb("Failed TSIG check: %s, TSIG err: %u.\n",
-				       knot_strerror(ret), tsig_rcode);
-			/* First, convert the response to wire format. */
-			dbg_zones_verb("Sending TSIG error.\n");
-			knot_response_set_rcode(resp, rcode);
-			ret = ns_response_to_wire(resp, resp_wire, rsize);
-			dbg_zones_detail("Packet to wire returned %d\n", ret);
-		}
 	}
 
-
-	
 	/* Create error query if processing failed. */
 	if (ret != KNOT_EOK) {
-		ret = knot_ns_error_response_from_query_wire(nameserver,
-							     knot_packet_wireformat(query),
-							     knot_packet_size(query),
-							     rcode, resp_wire, rsize);
+		ret = knot_ns_error_response_from_query(nameserver,
+		                                        query, rcode,
+		                                        resp_wire, rsize);
 	}
 	
-	/* Finish processing if no TSIG signing is required (or no response). */
-	if (*rsize == 0 || !tsig_key_zone) {
+	/* No response, no signing required or FORMERR. */
+	if (*rsize == 0 || !tsig_rr || rcode == KNOT_RCODE_FORMERR) {
 		knot_packet_free(&resp);
 		rcu_read_unlock();
 		return ret;
@@ -2917,10 +2860,10 @@ int zones_process_update(knot_nameserver_t *nameserver,
 	
 	/* Just add TSIG RR on most errors. */
 	if (tsig_rcode != 0 && tsig_rcode != KNOT_TSIG_RCODE_BADTIME) {
-		dbg_zones_verb("Adding TSIG to error.\n");
-		ret = knot_tsig_add(resp_wire, rsize, *rsize,
-				    tsig_rcode, tsig_rr);
-	} else {
+		ret = knot_tsig_add(resp_wire, rsize, rsize_max,
+		                    tsig_rcode, tsig_rr);
+		dbg_zones_verb("Adding TSIG = %s\n", knot_strerror(ret));
+	} else if (tsig_key_zone) {
 		dbg_zones_verb("Signing message with TSIG.\n");
 		size_t digest_len = tsig_alg_digest_length(tsig_key_zone->algorithm);
 		uint8_t *digest = (uint8_t *)malloc(digest_len);
@@ -3887,7 +3830,11 @@ int zones_verify_tsig_query(const knot_packet_t *query,
 	assert(tsig_rcode != NULL);
 	
 	const knot_rrset_t *tsig_rr = knot_packet_tsig(query);
-	assert(tsig_rr != NULL);
+	if (tsig_rr == NULL) {
+		dbg_zones("TSIG key required, but not in query - REFUSED.\n");
+		*rcode = KNOT_RCODE_REFUSED;
+		return KNOT_TSIG_EBADKEY;
+	}
 
 	/*
 	 * 1) Check if we support the requested algorithm.
diff --git a/src/knotc.8 b/src/knotc.8
index f0b33fa59c6ce1ca144ac90ad76810ceea806e48..94b6d6cb81af5ed27857a38f2878a4afabe6c164 100644
--- a/src/knotc.8
+++ b/src/knotc.8
@@ -1,4 +1,4 @@
-.TH knotc "8" "August 2012" "CZ.NIC Labs" "Knot DNS, version 1.2-rc1"
+.TH knotc "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2"
 .SH NAME
 .B knotc
 \- Knot DNS control utility
diff --git a/src/knotd.8 b/src/knotd.8
index 5df10aca0f72f49d3846f3b59f37b0dddcf9ba02..35d6d62f181da3b50b9c287898cfb0effaf53d55 100644
--- a/src/knotd.8
+++ b/src/knotd.8
@@ -1,4 +1,4 @@
-.TH "knotd" "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2-rc1"
+.TH "knotd" "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2"
 .SH NAME
 .B knotd
 \- Knot DNS daemon
diff --git a/src/libknot/dname.c b/src/libknot/dname.c
index 8a9f2d33b14ff6fbc52eb2657163f362b2552d61..5c1f143ba799d19af77198bc63f5b95e7e8f26a6 100644
--- a/src/libknot/dname.c
+++ b/src/libknot/dname.c
@@ -267,6 +267,11 @@ static int knot_dname_find_labels(knot_dname_t *dname, int alloc)
 	short label_count = 0;
 
 	while (pos - name < size && *pos != '\0' && label_count < KNOT_MAX_DNAME_LABELS ) {
+		if (*pos > 63) { /* Check label lengths. */
+			dbg_dname("Wrong wire format of domain name!\n");
+			dbg_dname("Label %d exceeds 63 bytes.\n", label_count);
+			return -1;
+		}
 		labels[label_count++] = pos - name;
 		pos += *pos + 1;
 	}
@@ -476,8 +481,9 @@ knot_dname_t *knot_dname_new_from_wire(const uint8_t *name, uint size,
 /*----------------------------------------------------------------------------*/
 
 knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
-                                             size_t *pos, size_t size,
-                                             knot_node_t *node)
+                                         size_t *pos, size_t size,
+                                         knot_node_t *node,
+                                         knot_dname_t *dname)
 {
 	uint8_t name[KNOT_MAX_DNAME_LENGTH];
 	uint8_t labels[KNOT_MAX_DNAME_LABELS];
@@ -544,34 +550,30 @@ knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
 		*pos += 1;
 	}
 
-	knot_dname_t *dname = knot_dname_new();
+	/* Allocate if NULL. */
+	if (dname == NULL) {
+		dname = knot_dname_new();
+		if (dname) {
+			dname->name = (uint8_t *)malloc((i + 1) * sizeof(uint8_t));
+			dname->labels = (uint8_t *)malloc((l + 1) * sizeof(uint8_t));
+		}
+	}
 
 	if (dname == NULL) {
 		ERR_ALLOC_FAILED;
 		return NULL;
 	}
 
-	dname->name = (uint8_t *)malloc((i + 1) * sizeof(uint8_t));
-	if (dname->name == NULL) {
+	if (dname->name == NULL || dname->labels == NULL) {
 		ERR_ALLOC_FAILED;
 		knot_dname_free(&dname);
 		return NULL;
 	}
 
 	memcpy(dname->name, name, i + 1);
-	dname->size = i + 1;
-
-	/*! \todo Why l + 1 ?? */
-	dname->labels = (uint8_t *)malloc((l + 1) * sizeof(uint8_t));
-	if (dname->labels == NULL) {
-		ERR_ALLOC_FAILED;
-		knot_dname_free(&dname);
-		return NULL;
-	}
 	memcpy(dname->labels, labels, l + 1);
-
+	dname->size = i + 1;
 	dname->label_count = l;
-
 	dname->node = node;
 
 	return dname;
diff --git a/src/libknot/dname.h b/src/libknot/dname.h
index 010f285adf1948f9b6d2d4ad2e0e7e78f6f0fc94..5827c988312e8090fda408311d1b58a4f10a2d65 100644
--- a/src/libknot/dname.h
+++ b/src/libknot/dname.h
@@ -124,9 +124,22 @@ knot_dname_t *knot_dname_new_from_wire(const uint8_t *name,
                                            unsigned int size,
                                            struct knot_node *node);
 
+/*!
+ * \brief Parse dname from wire.
+ *
+ * \param wire Message in wire format.
+ * \param pos Position of the domain name on wire.
+ * \param size Domain name length.
+ * \param node Zone node the domain name belongs to. Set to NULL if not
+ *             applicable.
+ * \param dname Destination dname (will allocate new when NULL).
+ *
+ * \return parsed domain name or NULL.
+ */
 knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
-                                             size_t *pos, size_t size,
-                                             struct knot_node *node);
+                                         size_t *pos, size_t size,
+                                         struct knot_node *node,
+                                         knot_dname_t *dname);
 
 /*!
  * \brief Initializes domain name by the name given in wire format.
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
index 86c289a00a2f3b942a91010c57a4eaf542ccdd7e..cb0b039e5c46592ec0c347b69915de3af0ffdfb4 100644
--- a/src/libknot/nameserver/name-server.c
+++ b/src/libknot/nameserver/name-server.c
@@ -2575,7 +2575,7 @@ dbg_ns_exec_verb(
 		assert(rrsets[i] != NULL);
 		rrset = rrsets[i];
 rrset:
-		dbg_ns_verb("  Type: %s\n",
+		dbg_ns_verb("  Type: %u\n",
 		            knot_rrtype_to_string(knot_rrset_type(rrset)));
 
 		// do not add SOA
diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c
index 04a48ec15808a213f0afe762548b05f9716540b4..ee06c7e236d8c4d4723cd31b2180f46ebe9f798d 100644
--- a/src/libknot/packet/packet.c
+++ b/src/libknot/packet/packet.c
@@ -283,22 +283,25 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
 	dbg_packet_verb("Parsing dname starting on position %zu and "
 	                      "%zu bytes long.\n", *pos, i - *pos + 1);
 	dbg_packet_verb("Alloc: %d\n", alloc);
+	size_t bp = *pos;
 	if (alloc) {
-		question->qname = knot_dname_new_from_wire(
-				wire + *pos, i - *pos + 1, NULL);
+		question->qname = knot_dname_parse_from_wire(wire, pos,
+		                                             i + 1,
+		                                             NULL, NULL);
 		if (question->qname == NULL) {
 			return KNOT_ENOMEM;
 		}
 	} else {
-		int res = knot_dname_from_wire(wire + *pos, i - *pos + 1,
-	                                         NULL, question->qname);
-		if (res != KNOT_EOK) {
-			assert(res != KNOT_EINVAL);
-			return res;
+		void *parsed = knot_dname_parse_from_wire(wire, pos,
+		                                     i + 1,
+	                                             NULL, question->qname);
+		if (!parsed) {
+			return KNOT_EMALF;
 		}
 	}
-
-	*pos = i + 1;
+	if (*pos != i + 1) {
+		dbg_packet("Parsed dname expected len=%zu, parsed=%zu.\n", i+1-bp, *pos-bp);
+	}
 	question->qtype = knot_wire_read_u16(wire + i + 1);
 	question->qclass = knot_wire_read_u16(wire + i + 3);
 	*pos += 4;
@@ -380,7 +383,7 @@ static int knot_packet_parse_rdata(knot_rrset_t *rr, const uint8_t *wire,
 		const int id = desc->block_types[i];
 		if (descriptor_item_is_dname(id)) {
 			knot_dname_t *dn = NULL;
-			dn = knot_dname_parse_from_wire(wire, pos, total_size, NULL);
+			dn = knot_dname_parse_from_wire(wire, pos, total_size, NULL, NULL);
 			if (dn == NULL) {
 				return KNOT_EMALF;
 			}
@@ -416,7 +419,7 @@ static knot_rrset_t *knot_packet_parse_rr(const uint8_t *wire, size_t *pos,
 	           *pos, size);
 
 	knot_dname_t *owner = knot_dname_parse_from_wire(wire, pos, size,
-	                                                     NULL);
+	                                                     NULL, NULL);
 	dbg_packet_detail("Created owner: %p, actual position: %zu\n", owner,
 	                  *pos);
 	if (owner == NULL) {
@@ -1098,6 +1101,14 @@ uint8_t knot_packet_opcode(const knot_packet_t *packet)
 
 /*----------------------------------------------------------------------------*/
 
+knot_question_t *knot_packet_question(knot_packet_t *packet)
+{
+	if (packet == NULL) return NULL;
+	return &packet->question;
+}
+
+/*----------------------------------------------------------------------------*/
+
 const knot_dname_t *knot_packet_qname(const knot_packet_t *packet)
 {
 	if (packet == NULL) {
diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h
index 8a9821fbfd60b2d3512dee02b3d2e9d33703627c..f151a63b2cb2c6af964cf2f66792427722a4a08f 100644
--- a/src/libknot/packet/packet.h
+++ b/src/libknot/packet/packet.h
@@ -357,6 +357,15 @@ void knot_packet_set_random_id(knot_packet_t *packet);
  */
 uint8_t knot_packet_opcode(const knot_packet_t *packet);
 
+/*!
+ * \brief Return question section from the packet.
+ *
+ * \param packet Packet instance.
+ *
+ * \return pointer to question section.
+ */
+knot_question_t *knot_packet_question(knot_packet_t *packet);
+
 /*!
  * \brief Returns the QNAME from the packet.
  *
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
index dd76fbfd2a13facafa669f9d5e89ba1a43f4400b..595b28eaea7bb5e166ec94ba18fbc98996df171d 100644
--- a/src/libknot/rrset.c
+++ b/src/libknot/rrset.c
@@ -932,7 +932,7 @@ int knot_rrset_rdata_from_wire_one(uint8_t **rdata, uint16_t type,
 			pos2 = *pos;
 			knot_dname_t *dname =
 				knot_dname_parse_from_wire(
-					wire, &pos2, total_size, NULL);
+					wire, &pos2, total_size, NULL, NULL);
 			if (dname == NULL) {
 				return KNOT_ERROR;
 			}
@@ -1009,7 +1009,7 @@ int knot_rrset_rdata_from_wire_one(uint8_t **rdata, uint16_t type,
 			/* Dname remaining. No need to note read size. */
 			knot_dname_t *dname =
 				knot_dname_parse_from_wire(
-					wire, pos, total_size, NULL);
+					wire, pos, total_size, NULL, NULL);
 			if (dname == NULL) {
 				return KNOT_ERROR;
 			}
diff --git a/src/libknot/updates/ddns.c b/src/libknot/updates/ddns.c
index ed4f79d8a13d58745ea053932d8961dd6b04abc5..c6e4ee382203ce942db564234bf3b4f25289a230 100644
--- a/src/libknot/updates/ddns.c
+++ b/src/libknot/updates/ddns.c
@@ -2325,6 +2325,7 @@ int knot_ddns_process_update2(knot_zone_contents_t *zone,
 			continue;
 		}
 
+		dbg_ddns_verb("Processing RR %p...\n", rr);
 		ret = knot_ddns_process_rr(rr, zone, changeset, changes,
 		                           knot_packet_qclass(query),
 		                           &rr_copy);
diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c
index daf5f2a965aa132c4c857f6c05ac612e2295c760..877e0c9a9144179c248074c462b2e1410708249d 100644
--- a/src/libknot/updates/xfr-in.c
+++ b/src/libknot/updates/xfr-in.c
@@ -1412,7 +1412,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
 	 *        one RRSet of each type and owner in the changeset.
 	 */
 	
-	int ret;
+	int ret = KNOT_EOK;
 
 	int copied = 0;
 
@@ -2587,7 +2587,7 @@ static int xfrin_apply_remove(knot_zone_contents_t *contents,
 dbg_xfrin_exec_verb(
 		char *name = knot_dname_to_str(
 			knot_rrset_owner(chset->remove[i]));
-		dbg_xfrin_verb("Removing RRSet: %s, type %s\n", name,
+		dbg_xfrin_verb("Removing RRSet: %s, type %u\n", name,
 		               knot_rrset_type(chset->remove[i]));
 		free(name);
 );
@@ -3286,10 +3286,9 @@ int xfrin_switch_zone(knot_zone_t *zone,
 	               old, (old) ? old->apex : NULL, new_contents->apex);
 
 	// switch pointers in domain names, now only the new zone is used
-	if (transfer_type == XFR_TYPE_IIN) {
+	if (transfer_type == XFR_TYPE_IIN || transfer_type == XFR_TYPE_UPDATE) {
 		// Traverse also the dname table and change the node pointers
 		// in dnames
-		assert(0);
 		//TODO still valid?
 //		int ret = knot_zone_contents_dname_table_apply(
 //		                        new_contents,
diff --git a/src/libknot/util/descriptor.c b/src/libknot/util/descriptor.c
index 90e4e93205c2e62a61273f41b56c638014d03a39..175b528899a66c77c37bc4b36452423bd24034d3 100644
--- a/src/libknot/util/descriptor.c
+++ b/src/libknot/util/descriptor.c
@@ -317,7 +317,7 @@ static knot_rrtype_descriptor_t
             KNOT_RDATA_ZF_APL, KNOT_RDATA_ZF_APL,
             KNOT_RDATA_ZF_APL, KNOT_RDATA_ZF_APL,
             KNOT_RDATA_ZF_APL, KNOT_RDATA_ZF_APL,
-            KNOT_RDATA_ZF_APL, KNOT_RDATA_ZF_APL }, 
+            KNOT_RDATA_ZF_APL, KNOT_RDATA_ZF_APL },
           false },
   	/* 43 */
   	{ KNOT_RRTYPE_DS, "DS", 4,
@@ -397,7 +397,7 @@ static knot_rrtype_descriptor_t
      /* it is indeed needed, in rrtype_from_string */
 
     /* There's a GNU extension that works like this: [first ... last] = value */
-        
+
         [53 ... 98] = { 0, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
   	/* 99 */
 	[99] = { KNOT_RRTYPE_SPF, "SPF", 1,
@@ -462,9 +462,9 @@ knot_rrtype_descriptor_t *knot_rrtype_descriptor_by_name(const char *name)
 	return NULL;
 }
 
-int32_t knot_rrtype_to_string(const uint16_t rrtype,
-			      char           *out,
-			      const uint32_t out_len)
+int knot_rrtype_to_string(const uint16_t rrtype,
+                          char           *out,
+                          const size_t   out_len)
 {
 	int ret;
 
@@ -484,7 +484,7 @@ int32_t knot_rrtype_to_string(const uint16_t rrtype,
 	}
 }
 
-uint16_t knot_rrtype_from_string(const char *name)
+int knot_rrtype_from_string(const char *name, uint16_t *num)
 {
 	char *end;
 	long rrtype;
@@ -492,36 +492,38 @@ uint16_t knot_rrtype_from_string(const char *name)
 
 	entry = knot_rrtype_descriptor_by_name(name);
 	if (entry) {
-		return entry->type;
+		*num = entry->type;
+		return 0;
 	}
 
 	if (strlen(name) < 5) {
-		return 0;
+		return -1;
 	}
 
 	if (strncasecmp(name, "TYPE", 4) != 0) {
-		return 0;
+		return -1;
 	}
 
 	if (!isdigit((int)name[4])) {
-		return 0;
+		return -1;
 	}
 
-	/* The rest from the string must be a number.  */
+	/* The rest from the string must be a number. */
 	rrtype = strtol(name + 4, &end, 10);
 	if (*end != '\0') {
-		return 0;
+		return -1;
 	}
 	if (rrtype < 0 || rrtype > 65535L) {
-		return 0;
+		return -1;
 	}
 
-	return (uint16_t) rrtype;
+	*num = rrtype;
+	return 0;
 }
 
-int32_t knot_rrclass_to_string(const uint16_t rrclass,
-			       char           *out,
-			       const uint32_t out_len)
+int knot_rrclass_to_string(const uint16_t rrclass,
+                           char           *out,
+                           const size_t   out_len)
 {
 	int ret;
 
@@ -541,7 +543,7 @@ int32_t knot_rrclass_to_string(const uint16_t rrclass,
 	}
 }
 
-uint16_t knot_rrclass_from_string(const char *name)
+int knot_rrclass_from_string(const char *name, uint16_t *num)
 {
 	char *end;
 	long rrclass;
@@ -549,31 +551,33 @@ uint16_t knot_rrclass_from_string(const char *name)
 
 	entry = knot_lookup_by_name(dns_rrclasses, name);
 	if (entry) {
-		return (uint16_t) entry->id;
+		*num = entry->id;
+		return 0;
 	}
 
 	if (strlen(name) < 6) {
-		return 0;
+		return -1;
 	}
 
 	if (strncasecmp(name, "CLASS", 5) != 0) {
-		return 0;
+		return -1;
 	}
 
 	if (!isdigit((int)name[5])) {
-		return 0;
+		return -1;
 	}
 
 	// The rest from the string must be a number.
 	rrclass = strtol(name + 5, &end, 10);
 	if (*end != '\0') {
-		return 0;
+		return -1;
 	}
 	if (rrclass < 0 || rrclass > 65535L) {
-		return 0;
+		return -1;
 	}
 
-	return (uint16_t) rrclass;
+	*num = rrclass;
+	return 0;
 }
 
 size_t knot_wireformat_size(unsigned int wire_type)
diff --git a/src/libknot/util/endian.h b/src/libknot/util/endian.h
new file mode 100644
index 0000000000000000000000000000000000000000..937d5ffc8a869fac003a339538667fb9080b1ede
--- /dev/null
+++ b/src/libknot/util/endian.h
@@ -0,0 +1,39 @@
+/*  Copyright (C) 2013 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/>.
+ */
+
+#ifndef _KNOT_ENDIAN_H
+#define _KNOT_ENDIAN_H
+
+#if defined(__linux__)
+#	include <endian.h>
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+#	include <sys/endian.h>
+#elif defined(__OpenBSD__)
+#	include <sys/types.h>
+#	define be16toh(x) betoh16(x)
+#	define be32toh(x) betoh32(x)
+#	define be64toh(x) betoh64(x)
+#elif defined(__APPLE__)
+#       include <libkern/OSByteOrder.h>
+#       define be16toh(x) OSSwapBigToHostInt16(x)
+#       define be32toh(x) OSSwapBigToHostInt32(x)
+#       define be64toh(x) OSSwapBigToHostInt64(x)
+#       define htobe16(x) OSSwapHostToBigInt16(x)
+#       define htobe32(x) OSSwapHostToBigInt32(x)
+#       define htobe64(x) OSSwapHostToBigInt64(x)
+#endif
+
+#endif /* _KNOT_ENDIAN_H_ */
diff --git a/src/libknot/util/utils.h b/src/libknot/util/utils.h
index fd275b3e83248062370245d42845b2edfe757e86..f7e58623a27a50d29e680422bd5aba47831da695 100644
--- a/src/libknot/util/utils.h
+++ b/src/libknot/util/utils.h
@@ -27,6 +27,7 @@
 #ifndef _KNOT_UTILS_H_
 #define _KNOT_UTILS_H_
 
+#include "util/endian.h"
 #include <string.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -89,13 +90,11 @@ size_t knot_strlcpy(char *dst, const char *src, size_t size);
  *
  * \param pos Data to read the 2 bytes from.
  *
- * \todo Wrong assumption of endianness (issue #1558).
- *
- * \return The 2 bytes read, in inverse endian.
+ * \return The 2 bytes read, in host byte order.
  */
 static inline uint16_t knot_wire_read_u16(const uint8_t *pos)
 {
-	return (pos[0] << 8) | pos[1];
+	return be16toh(*(uint16_t *)pos);
 }
 
 /*!
@@ -103,13 +102,11 @@ static inline uint16_t knot_wire_read_u16(const uint8_t *pos)
  *
  * \param pos Data to read the 4 bytes from.
  *
- * \todo Wrong assumption of endianness (issue #1558).
- *
- * \return The 4 bytes read, in inverse endian.
+ * \return The 4 bytes read, in host byte order.
  */
 static inline uint32_t knot_wire_read_u32(const uint8_t *pos)
 {
-	return (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3];
+	return be32toh(*(uint32_t *)pos);
 }
 
 /*!
@@ -117,69 +114,78 @@ static inline uint32_t knot_wire_read_u32(const uint8_t *pos)
  *
  * \param pos Data to read the 6 bytes from.
  *
- * \todo Wrong assumption of endianness (issue #1558).
- *
- * \return The 6 bytes read, in inverse endian.
+ * \return The 6 bytes read, in host byte order.
  */
 static inline uint64_t knot_wire_read_u48(const uint8_t *pos)
 {
-	return ((uint64_t)(pos[0]) << 40) | ((uint64_t)(pos[1]) << 32)
-	        | ((uint64_t)(pos[2]) << 24) | ((uint64_t)(pos[3]) << 16)
-	        | ((uint64_t)(pos[4]) << 8) | (uint64_t)pos[5];
+	uint64_t input = 0;
+	memcpy((void *)&input + 1, (void *)pos, 6);
+	return be64toh(input) >> 8;
 }
 
 /*!
- * \brief Writes 2 bytes in wireformat.
+ * \brief Read 8 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 8 bytes from.
  *
- * The endian of the data is inverted.
+ * \return The 8 bytes read, in host byte order.
+ */
+static inline uint64_t knot_wire_read_u64(const uint8_t *pos)
+{
+	return be64toh(*(uint64_t *)pos);
+}
+
+/*!
+ * \brief Writes 2 bytes in wireformat.
  *
- * \todo Wrong assumption of endianness (issue #1558).
+ * The data are stored in network byte order (big endian).
  *
  * \param pos Position where to put the 2 bytes.
  * \param data Data to put.
  */
 static inline void knot_wire_write_u16(uint8_t *pos, uint16_t data)
 {
-	*(pos++) = (uint8_t)((data >> 8) & 0xff);
-	*pos = (uint8_t)(data & 0xff);
+	*(uint16_t *)pos = htobe16(data);
 }
 
 /*!
  * \brief Writes 4 bytes in wireformat.
  *
- * The endian of the data is inverted.
- *
- * \todo Wrong assumption of endianness (issue #1558).
+ * The data are stored in network byte order (big endian).
  *
  * \param pos Position where to put the 4 bytes.
  * \param data Data to put.
  */
 static inline void knot_wire_write_u32(uint8_t *pos, uint32_t data)
 {
-	*(pos++) = (uint8_t)((data >> 24) & 0xff);
-	*(pos++) = (uint8_t)((data >> 16) & 0xff);
-	*(pos++) = (uint8_t)((data >> 8) & 0xff);
-	*pos = (uint8_t)(data & 0xff);
+	*(uint32_t *)pos = htobe32(data);
 }
 
 /*!
  * \brief Writes 6 bytes in wireformat.
  *
- * The endian of the data is inverted.
- *
- * \todo Wrong assumption of endianness (issue #1558).
+ * The data are stored in network byte order (big endian).
  *
  * \param pos Position where to put the 4 bytes.
  * \param data Data to put.
  */
 static inline void knot_wire_write_u48(uint8_t *pos, uint64_t data)
 {
-	*(pos++) = (uint8_t)((data >> 40) & 0xff);
-	*(pos++) = (uint8_t)((data >> 32) & 0xff);
-	*(pos++) = (uint8_t)((data >> 24) & 0xff);
-	*(pos++) = (uint8_t)((data >> 16) & 0xff);
-	*(pos++) = (uint8_t)((data >> 8) & 0xff);
-	*pos = (uint8_t)(data & 0xff);
+	uint64_t swapped = htobe64(data << 8);
+	memcpy((void *)pos, (uint8_t *)&swapped + 1, 6);
+}
+
+/*!
+ * \brief Writes 8 bytes in wireformat.
+ *
+ * The data are stored in network byte order (big endian).
+ *
+ * \param pos Position where to put the 8 bytes.
+ * \param data Data to put.
+ */
+static inline void knot_wire_write_u64(uint8_t *pos, uint64_t data)
+{
+	*(uint64_t *)pos = htobe64(data);
 }
 
 /*!
diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c
index c1027e2a0077393a6612d0c74d2f1a6a1231c551..26e4bfcb79227ba85fbb4bf82e9465516c6174c1 100644
--- a/src/libknot/zone/zone-contents.c
+++ b/src/libknot/zone/zone-contents.c
@@ -1694,7 +1694,7 @@ const knot_node_t *knot_zone_contents_find_previous_nsec3(
 
 static void knot_zone_contents_left_chop(char *name, size_t *size)
 {
-	short label_size = name[0];
+	short label_size = (unsigned char)name[0];
 	
 	memmove(name, name + label_size + 1, *size -label_size - 1);
 	*size = *size - label_size - 1;
diff --git a/src/libknot/zone/zone-diff.c b/src/libknot/zone/zone-diff.c
index 3c498123ba8e3c2ef67d21e7db33c4bd57e6e7c9..bed7be3cbcefacff9c65983815c701ba06a7cf2d 100644
--- a/src/libknot/zone/zone-diff.c
+++ b/src/libknot/zone/zone-diff.c
@@ -371,7 +371,7 @@ static int knot_zone_diff_rdata_return_changes(const knot_rrset_t *rrset1,
 		if (ret == KNOT_ENOENT) {
 			/* No such RR is present in 'rrset2'. */
 			dbg_zonediff("zone_diff: diff_rdata: "
-			       "No match for RR (type=%d owner=%s).\n",
+			       "No match for RR (type=%d owner=%u).\n",
 			       knot_rrset_type(rrset1),
 			       knot_dname_to_str(rrset1->owner));
 			/* We'll copy index 'i' into 'changes' RRSet. */
diff --git a/src/tests/libknot/dname_tests.c b/src/tests/libknot/dname_tests.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f669d79098edeb701c43103f98ba2869164b18f
--- /dev/null
+++ b/src/tests/libknot/dname_tests.c
@@ -0,0 +1,83 @@
+/*  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 "tests/libknot/dname_tests.h"
+#include "libknot/dname.h"
+
+/* Test dname_parse_from_wire */
+static int test_fw(size_t l, const char *w) {
+	size_t p = 0;
+	knot_dname_t *d = NULL;
+	d = knot_dname_parse_from_wire((const uint8_t*)w, &p, l, NULL, NULL);
+	int ret = (d != NULL);
+//	d = knot_dname_new_from_wire((const uint8_t*)w, l, 0);
+//	if (d) {
+//		for(unsigned i = 0; i < d->label_count; ++i) {
+//			diag("%d", knot_dname_label_size(d, i));
+//		}
+//	}
+	knot_dname_free(&d);
+	return ret;
+}
+
+static int dname_tests_count(int argc, char *argv[]);
+static int dname_tests_run(int argc, char *argv[]);
+
+unit_api dname_tests_api = {
+	"dname",
+	&dname_tests_count,
+	&dname_tests_run
+};
+
+static int dname_tests_count(int argc, char *argv[])
+{
+	return 8;
+}
+
+static int dname_tests_run(int argc, char *argv[])
+{
+	const char *w = NULL;
+	
+	/* 1. NULL wire */
+	ok(!test_fw(0, NULL), "parsing NULL dname");
+	
+	/* 2. empty label */
+	ok(test_fw(1, ""), "parsing empty dname");
+	
+	/* 3. incomplete dname */
+	ok(!test_fw(5, "\x08""dddd"), "parsing incomplete wire");
+
+	/* 4. non-fqdn */
+	ok(!test_fw(3, "\x02""ab"), "parsing non-fqdn name");
+	
+	/* 5. label > 63b */
+	w = "\x40""dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
+	ok(!test_fw(65, w), "parsing label > 63b");
+	
+	/* 6. label count > 127 */
+	w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
+	ok(!test_fw(257, w), "parsing label count > 127");
+	
+	/* 7. dname length > 255 */
+	w = "\xff""ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
+	ok(!test_fw(257, w), "parsing dname len > 255");
+	
+	/* 8. special case - invalid label */
+	w = "\x20\x68\x6d\x6e\x63\x62\x67\x61\x61\x61\x61\x65\x72\x6b\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x67\x6e\x69\x64\x68\x62\x61\x61\x61\x61\x65\x6c\x64\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x61\x63\x6f\x63\x64\x62\x61\x61\x61\x61\x65\x6b\x72\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x69\x62\x63\x6d\x6a\x6f\x61\x61\x61\x61\x65\x72\x6a\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x6f\x6c\x6e\x6c\x67\x68\x61\x61\x61\x61\x65\x73\x72\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x6a\x6b\x64\x66\x66\x67\x61\x61\x61\x61\x65\x6c\x68\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x67\x67\x6c\x70\x70\x61\x61\x61\x61\x61\x65\x73\x72\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x65\x6b\x6c\x67\x70\x66\x61\x61\x61\x61\x65\x6c\x68\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x0\x21\x42\x63\x84\xa5\xc6\xe7\x8\xa\xd\x11\x73\x3\x6e\x69\x63\x2\x43\x5a";
+	ok(!test_fw(277, w), "parsing invalid label (spec. case 1)");
+
+	return 0;
+}
diff --git a/src/utils/host/host_exec.h b/src/tests/libknot/dname_tests.h
similarity index 68%
rename from src/utils/host/host_exec.h
rename to src/tests/libknot/dname_tests.h
index e7c0df6619e8787e5c80fdb5e109660b0e471697..f3edff3675daf002755352d6a25d8932c3153675 100644
--- a/src/utils/host/host_exec.h
+++ b/src/tests/libknot/dname_tests.h
@@ -13,24 +13,12 @@
     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 host_exec.h
- *
- * \author Daniel Salzman <daniel.salzman@nic.cz>
- *
- * \brief host executives.
- *
- * \addtogroup knot_utils
- * @{
- */
-
-#ifndef _HOST__HOST_EXEC_H_
-#define _HOST__HOST_EXEC_H_
 
-#include "utils/common/params.h"	// params_t
+#ifndef _KNOT_DNAME_TESTS_
+#define _KNOT_DNAME_TESTS_
 
-int host_exec(const params_t *params);
+#include "common/libtap/tap_unit.h"
 
-#endif // _HOST__HOST_EXEC_H_
+unit_api dname_tests_api;
 
-/*! @} */
+#endif
diff --git a/src/tests/libknot/wire_tests.c b/src/tests/libknot/wire_tests.c
new file mode 100644
index 0000000000000000000000000000000000000000..a56d3f91d38fd352716968a23ffb61c9e7577c32
--- /dev/null
+++ b/src/tests/libknot/wire_tests.c
@@ -0,0 +1,112 @@
+/*  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 "tests/libknot/wire_tests.h"
+#include "libknot/util/utils.h"
+
+static int wire_tests_count(int argc, char *argv[]);
+static int wire_tests_run(int argc, char *argv[]);
+
+unit_api wire_tests_api = {
+	"Wire",
+	&wire_tests_count,
+	&wire_tests_run
+};
+
+static int wire_tests_count(int argc, char *argv[])
+{
+	return 8;
+}
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+	#define ENDIAN_MATCH(expression, match_little, match_big) \
+		((expression) == (match_little))
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+	#define ENDIAN_MATCH(expression, match_little, match_big) \
+		((expression) == (match_big))
+#else
+	#error Unsupported byte order.
+#endif
+
+static int wire_tests_run(int argc, char *argv[])
+{
+	// 1. - 16-bit read
+	{
+		uint16_t data = 0xAABB;
+		ok(ENDIAN_MATCH(knot_wire_read_u16((uint8_t *)&data),
+		   0xBBAA, 0xAABB), "16-bit read");
+	}
+
+	// 2. - 16-bit read
+	{
+		uint16_t data_in = 0xAABB;
+		uint64_t data_out = 0xFF0000;
+		knot_wire_write_u16((uint8_t *)&data_out, data_in);
+		ok(ENDIAN_MATCH(data_out,
+		   0xFFBBAA, 0xFFAABB), "16-bit write");
+	}
+
+	// 3. - 32-bit read
+	{
+		uint32_t data = 0xAABBCCDD;
+		ok(ENDIAN_MATCH(knot_wire_read_u32((uint8_t *)&data),
+		   0xDDCCBBAA, 0xAABBCCDD), "32-bit read");
+	}
+
+	// 4. - 32-bit write
+	{
+		uint32_t data_in = 0xAABBCCDD;
+		uint64_t data_out = 0xFF00000000;
+		knot_wire_write_u32((uint8_t *)&data_out, data_in);
+		ok(ENDIAN_MATCH(data_out,
+		   0xFFDDCCBBAA, 0xFFAABBCCDD), "32-bit write");
+
+	}
+
+	// 5. - 48-bit read
+	{
+		uint64_t data = 0x81AABBCCDDEEFF;
+		ok(ENDIAN_MATCH(knot_wire_read_u48((uint8_t *)&data),
+		   0xFFEEDDCCBBAA, 0xAABBCCDDEEFF), "48-bit read");
+	}
+
+	// 6. - 48-bit write
+	{
+		uint64_t data_in = 0x81AABBCCDDEEFF;
+		uint64_t data_out = 0xDD000000000000;
+		knot_wire_write_u48((uint8_t *)&data_out, data_in);
+		ok(ENDIAN_MATCH(data_out,
+		   0xDDFFEEDDCCBBAA, 0xDDAABBCCDDEEFF), "48-bit write");
+	}
+
+	// 7. - 64-bit read
+	{
+		uint64_t data = 0x8899AABBCCDDEEFF;
+		ok(ENDIAN_MATCH(knot_wire_read_u64((uint8_t *)&data),
+		   0xFFEEDDCCBBAA9988, 0x8899AABBCCDDEEFF), "64-bit read");
+	}
+
+	// 8. - 64-bit write
+	{
+		uint64_t data_in = 0x8899AABBCCDDEEFF;
+		uint64_t data_out = 0x0;
+		knot_wire_write_u64((uint8_t *)&data_out, data_in);
+		ok(ENDIAN_MATCH(data_out,
+		   0xFFEEDDCCBBAA9988, 0x8899AABBCCDDEEFF), "64-bit write");
+	}
+
+	return 0;
+}
diff --git a/src/utils/host/host_exec.c b/src/tests/libknot/wire_tests.h
similarity index 50%
rename from src/utils/host/host_exec.c
rename to src/tests/libknot/wire_tests.h
index f1958c5bd12f3d943ee6c504ee922b708dd6d839..37e038d45a5e973e64c34893471465f82ee36c47 100644
--- a/src/utils/host/host_exec.c
+++ b/src/tests/libknot/wire_tests.h
@@ -12,41 +12,13 @@
 
     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 "utils/host/host_exec.h"
+#ifndef _KNOTD_WIRE_TESTS_
+#define _KNOTD_WIRE_TESTS_
 
-#include "common/lists.h"		// list
-#include "common/errcode.h"		// KNOT_EOK
+#include "common/libtap/tap_unit.h"
 
-#include "utils/common/msg.h"		// WARN
-#include "utils/dig/dig_params.h"	// dig_params_t
-#include "utils/dig/dig_exec.h"		// process_query
+unit_api wire_tests_api;
 
-int host_exec(const params_t *params)
-{
-	node *query = NULL;
-
-	if (params == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	dig_params_t *ext_params = DIG_PARAM(params);
-
-	switch (params->operation) {
-	case OPERATION_QUERY:
-		// Loop over query list.
-		WALK_LIST(query, ext_params->queries) {
-			process_query(params, (query_t *)query);
-		}
-
-		break;
-	case OPERATION_LIST_SOA:
-		break;
-	default:
-		ERR("unsupported operation\n");
-		break;
-	}
-
-	return KNOT_EOK;
-}
+#endif
diff --git a/src/tests/unittests_main.c b/src/tests/unittests_main.c
index 17ea3b41d8d378ccf7a0ec56409bdf7591a47cfa..c4c220ffb341fb24a6be4c960104cee6b205f084 100644
--- a/src/tests/unittests_main.c
+++ b/src/tests/unittests_main.c
@@ -30,6 +30,8 @@
 #include "tests/knot/journal_tests.h"
 #include "tests/knot/server_tests.h"
 #include "tests/knot/conf_tests.h"
+#include "tests/libknot/wire_tests.h"
+#include "tests/libknot/dname_tests.h"
 
 // Run all loaded units
 int main(int argc, char *argv[])
@@ -42,21 +44,25 @@ int main(int argc, char *argv[])
 
 	// Build test set
 	unit_api *tests[] = {
-		/* Core data structures. */
-		&journal_tests_api,	//! Journal unit
-		&slab_tests_api,	//! SLAB allocator unit
-		&skiplist_tests_api,	//! Skip list unit
-		&dthreads_tests_api,	//! DThreads testing unit
-		&events_tests_api,	//! Events testing unit
-		&acl_tests_api,		//! ACLs
-		&fdset_tests_api,	//! FDSET polling wrapper
-		&base64_tests_api,	//! Base64 encoding
-		&base32hex_tests_api,	//! Base32hex encoding
+	        /* Core data structures. */
+	        &journal_tests_api,	//! Journal unit
+	        &slab_tests_api,	//! SLAB allocator unit
+	        &skiplist_tests_api,	//! Skip list unit
+	        &dthreads_tests_api,	//! DThreads testing unit
+	        &events_tests_api,	//! Events testing unit
+	        &acl_tests_api,		//! ACLs
+	        &fdset_tests_api,	//! FDSET polling wrapper
+	        &base64_tests_api,	//! Base64 encoding
+	        &base32hex_tests_api,	//! Base32hex encoding
 
-		/* Server parts. */
-		&conf_tests_api,	//! Configuration parser tests
-		&server_tests_api,	//! Server unit
-		NULL
+	        /* Library. */
+	        &wire_tests_api,
+	        &dname_tests_api,
+
+	        /* Server parts. */
+	        &conf_tests_api,	//! Configuration parser tests
+	        &server_tests_api,	//! Server unit
+	        NULL
 	};
 
 	// Plan number of tests
diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c
index f31e8373c521d76b2477db4b39d85ccb0af56dcf..d6e99b0ea9c4a2929033576ee2cd4939182b7612 100644
--- a/src/utils/common/exec.c
+++ b/src/utils/common/exec.c
@@ -18,18 +18,12 @@
 
 #include <stdlib.h>			// free
 #include <time.h>			// localtime_r
-#include <sys/time.h>			// gettimeofday
-
-#include <arpa/inet.h>			// inet_ntop
-#include <sys/socket.h>			// AF_INET
-#include <netinet/in.h>			// sockaddr_in (BSD)
 
 #include "common/lists.h"		// list
 #include "common/errcode.h"		// KNOT_EOK
 #include "libknot/consts.h"		// KNOT_RCODE_NOERROR
 #include "libknot/util/wire.h"		// knot_wire_set_rd
 #include "libknot/packet/query.h"	// knot_query_init
-
 #include "utils/common/msg.h"		// WARN
 #include "utils/common/params.h"	// params_t
 #include "utils/common/netio.h"		// send_msg
@@ -98,7 +92,7 @@ knot_packet_t* create_empty_packet(knot_packet_prealloc_type_t t, int max_size)
 	return packet;
 }
 
-static void print_header(const knot_packet_t *packet)
+static void print_header(const style_t *style, const knot_packet_t *packet)
 {
 	char    flags[64] = "";
 	uint8_t rcode_id, opcode_id;
@@ -138,33 +132,35 @@ static void print_header(const knot_packet_t *packet)
 	}
 
 	// Print formated info.
-	printf("\n;; ->>HEADER<<- opcode: %s, status: %s, id: %u\n"
-	       ";; Flags:%1s, "
-	       "QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n",
-	       opcode->name, rcode->name, knot_packet_id(packet),
-	       flags, packet->header.qdcount, packet->an_rrsets,
-	       packet->ns_rrsets, packet->ar_rrsets);
+	switch (style->format) {
+	case FORMAT_NSUPDATE:
+		printf("\n;; ->>HEADER<<- opcode: %s, status: %s, id: %u\n"
+		       ";; Flags:%1s, "
+		       "ZONE: %u, PREREQ: %u, UPDATE: %u, ADDITIONAL: %u\n",
+		       opcode->name, rcode->name, knot_packet_id(packet),
+		       flags, packet->header.qdcount, packet->an_rrsets,
+		       packet->ns_rrsets, packet->ar_rrsets);
+
+		break;
+	default:
+		printf("\n;; ->>HEADER<<- opcode: %s, status: %s, id: %u\n"
+		       ";; Flags:%1s, "
+		       "QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n",
+		       opcode->name, rcode->name, knot_packet_id(packet),
+		       flags, packet->header.qdcount, packet->an_rrsets,
+		       packet->ns_rrsets, packet->ar_rrsets);
+		break;
+	}
 }
 
-static void print_footer(const size_t total_len,
-                         const int    sockfd,
+static void print_footer(const net_t  *net,
                          const float  elapsed,
+                         const size_t total_len,
                          const size_t msg_count)
 {
 	struct tm tm;
 	char      date[64];
 
-	struct sockaddr_storage addr;
-	socklen_t addr_len;
-	socklen_t socktype_len;
-	int       socktype;
-	char      proto[8] = "NULL";
-	char      ip[INET6_ADDRSTRLEN] = "NULL";
-	int       port = -1;
-
-	addr_len = sizeof(addr);
-	socktype_len = sizeof(socktype);
-
 	// Get current timestamp.
 	time_t now = time(NULL);
 	localtime_r(&now, &tm);
@@ -172,37 +168,20 @@ static void print_footer(const size_t total_len,
 	// Create formated date-time string.
 	strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S %Z", &tm);
 
-	// Get connected address.
-	if (getpeername(sockfd, (struct sockaddr*)&addr, &addr_len) == 0) {
-		if (addr.ss_family == AF_INET) {
-			struct sockaddr_in *s = (struct sockaddr_in *)&addr;
-			port = ntohs(s->sin_port);
-			inet_ntop(AF_INET, &s->sin_addr, ip, sizeof(ip));
-		} else { // AF_INET6
-			struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
-			port = ntohs(s->sin6_port);
-			inet_ntop(AF_INET6, &s->sin6_addr, ip, sizeof(ip));
-		}
-	}
-
-	// Get connected socket type.
-	if (getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (char*)&socktype,
-	    &socktype_len) == 0) {
-		switch (socktype) {
-		case SOCK_STREAM:
-			strcpy(proto, "TCP");
-			break;
-		case SOCK_DGRAM:
-			strcpy(proto, "UDP");
-			break;
-		}
-	}
-
 	// Print formated info.
 	printf("\n;; Received %zu B (%zu messages)\n"
 	       ";; From %s#%i over %s in %.1f ms\n"
 	       ";; On %s\n",
-	       total_len, msg_count, ip, port, proto, elapsed, date);
+	       total_len, msg_count, net->addr, net->port, net->proto,
+	       elapsed, date);
+}
+
+static void print_opt_section(const knot_opt_rr_t *rr)
+{
+	printf("Version: %u, flags: %s, UDP size: %u B\n",
+	       knot_edns_get_version(rr),
+	       (knot_edns_do(rr) != 0) ? "DO" : "",
+	       knot_edns_get_payload(rr));
 }
 
 static void print_section_question(const knot_dname_t *owner,
@@ -280,7 +259,7 @@ static void print_section_dig(const knot_rrset_t **rrsets,
 }
 
 static void print_section_host(const knot_rrset_t **rrsets,
-                              const uint16_t     count)
+                               const uint16_t     count)
 {
 	size_t buflen = 8192;
 	char   *buf = malloc(buflen);
@@ -347,8 +326,12 @@ static void print_error_host(const uint8_t         code,
 	free(owner);
 }
 
-void print_header_xfr(const format_t format, const knot_rr_type_t type)
+void print_header_xfr(const style_t *style, const knot_rr_type_t type)
 {
+	if (style == NULL) {
+		return;
+	}
+
 	char name[16] = "";
 
 	switch (type) {
@@ -362,7 +345,7 @@ void print_header_xfr(const format_t format, const knot_rr_type_t type)
 		return;
 	}
 
-	switch (format) {
+	switch (style->format) {
 	case FORMAT_VERBOSE:
 	case FORMAT_MULTILINE:
 		printf(";; %s transfer\n\n", name);
@@ -374,14 +357,14 @@ void print_header_xfr(const format_t format, const knot_rr_type_t type)
 	}
 }
 
-void print_data_xfr(const format_t      format,
+void print_data_xfr(const style_t       *style,
                     const knot_packet_t *packet)
 {
-	if (packet == NULL) {
+	if (style == NULL || packet == NULL) {
 		return;
 	}
 
-	switch (format) {
+	switch (style->format) {
 	case FORMAT_DIG:
 		print_section_dig(packet->answer, packet->an_rrsets);
 		break;
@@ -397,16 +380,20 @@ void print_data_xfr(const format_t      format,
 	}
 }
 
-void print_footer_xfr(const format_t format,
-                      const size_t   total_len,
-                      const int      sockfd,
+void print_footer_xfr(const net_t    *net,
+                      const style_t  *style,
                       const float    elapsed,
+                      const size_t   total_len,
                       const size_t   msg_count)
 {
-	switch (format) {
+	if (net == NULL || style == NULL) {
+		return;
+	}
+
+	switch (style->format) {
 	case FORMAT_VERBOSE:
 	case FORMAT_MULTILINE:
-		print_footer(total_len, sockfd, elapsed, msg_count);
+		print_footer(net, elapsed, total_len, msg_count);
 		break;
 	case FORMAT_DIG:
 	case FORMAT_HOST:
@@ -415,18 +402,18 @@ void print_footer_xfr(const format_t format,
 	}
 }
 
-void print_packet(const format_t      format,
+void print_packet(const net_t         *net,
+                  const style_t       *style,
                   const knot_packet_t *packet,
-                  const size_t        total_len,
-                  const int           sockfd,
                   const float         elapsed,
+                  const size_t        total_len,
                   const size_t        msg_count)
 {
-	if (packet == NULL) {
+	if (style == NULL || packet == NULL) {
 		return;
 	}
 
-	switch (format) {
+	switch (style->format) {
 	case FORMAT_DIG:
 		if (packet->an_rrsets > 0) {
 			print_section_dig(packet->answer, packet->an_rrsets);
@@ -441,9 +428,42 @@ void print_packet(const format_t      format,
 		}
 		break;
 	case FORMAT_NSUPDATE:
+		print_header(style, packet);
+
+		if (packet->header.qdcount > 0) {
+			printf("\n;; ZONE SECTION:\n;; ");
+			print_section_question(packet->question.qname,
+			                       packet->question.qclass,
+			                       packet->question.qtype);
+		}
+
+		if (packet->an_rrsets > 0) {
+			printf("\n;; PREREQUISITE SECTION:\n");
+			print_section_verbose(packet->answer,
+			                      packet->an_rrsets);
+		}
+
+		if (packet->ns_rrsets > 0) {
+			printf("\n;; UPDATE SECTION:\n");
+			print_section_verbose(packet->authority,
+			                      packet->ns_rrsets);
+		}
+
+		if (packet->ar_rrsets > 0) {
+			printf("\n;; ADDITIONAL DATA:\n");
+			print_section_verbose(packet->additional,
+			                      packet->ar_rrsets);
+		}
+		break;
 	case FORMAT_VERBOSE:
 	case FORMAT_MULTILINE:
-		print_header(packet);
+		print_header(style, packet);
+
+		if (knot_edns_get_version(&packet->opt_rr)
+		    != EDNS_NOT_SUPPORTED) {
+			printf("\n;; EDNS PSEUDOSECTION:\n;; ");
+			print_opt_section(&packet->opt_rr);
+		}
 
 		if (packet->header.qdcount > 0) {
 			printf("\n;; QUESTION SECTION:\n;; ");
@@ -466,13 +486,22 @@ void print_packet(const format_t      format,
 
 		if (packet->ar_rrsets > 0) {
 			printf("\n;; ADDITIONAL SECTION:\n");
-			print_section_verbose(packet->additional,
-			                      packet->ar_rrsets);
+
+			if (knot_edns_get_version(&packet->opt_rr)
+			    != EDNS_NOT_SUPPORTED) {
+				print_section_verbose(packet->additional,
+				                      packet->ar_rrsets - 1);
+			} else {
+				print_section_verbose(packet->additional,
+				                      packet->ar_rrsets);
+			}
 		}
 
-		if (format != FORMAT_NSUPDATE) {
-			print_footer(total_len, sockfd, elapsed, msg_count);
+		if (net == NULL) {
+			break;
 		}
+
+		print_footer(net, elapsed, total_len, msg_count);
 		break;
 	default:
 		break;
diff --git a/src/utils/common/exec.h b/src/utils/common/exec.h
index 4e22f5685f9871b1f96d64bfd011a0a7d5e7f8a4..1401fe914c3b7cbd48d8f368931ea87d5656425b 100644
--- a/src/utils/common/exec.h
+++ b/src/utils/common/exec.h
@@ -28,7 +28,8 @@
 #define _UTILS__EXEC_H_
 
 #include "libknot/packet/packet.h"	// knot_packet_t
-#include "utils/common/params.h"	// format_t
+#include "utils/common/netio.h"		// net_t
+#include "utils/common/params.h"	// style_t
 
 extern knot_lookup_table_t opcodes[];
 extern knot_lookup_table_t rcodes[];
@@ -36,22 +37,22 @@ extern knot_lookup_table_t rtypes[];
 
 knot_packet_t* create_empty_packet(knot_packet_prealloc_type_t t, int max_size);
 
-void print_header_xfr(const format_t format, const knot_rr_type_t type);
+void print_header_xfr(const style_t *style, const knot_rr_type_t type);
 
-void print_data_xfr(const format_t      format,
+void print_data_xfr(const style_t       *style,
                     const knot_packet_t *packet);
 
-void print_footer_xfr(const format_t format,
-                      const size_t   total_len,
-                      const int      sockfd,
+void print_footer_xfr(const net_t    *net,
+                      const style_t  *style,
                       const float    elapsed,
+                      const size_t   total_len,
                       const size_t   msg_count);
 
-void print_packet(const format_t      format,
+void print_packet(const net_t         *net,
+                  const style_t       *style,
                   const knot_packet_t *packet,
-                  const size_t        wire_len,
-                  const int           sockfd,
                   const float         elapsed,
+                  const size_t        total_len,
                   const size_t        msg_count);
 
 #endif // _UTILS__EXEC_H_
diff --git a/src/utils/common/netio.c b/src/utils/common/netio.c
index 8e77fc2766abe3bf1aeb7519351f86833c915563..c600add7bee83eb08ecbfa495f7da1c4f69eb9a2 100644
--- a/src/utils/common/netio.c
+++ b/src/utils/common/netio.c
@@ -22,6 +22,8 @@
 #include <fcntl.h>			// fcntl
 #include <sys/socket.h>			// AF_INET (BSD)
 #include <netinet/in.h>			// ntohl (BSD)
+#include <arpa/inet.h>			// inet_ntop
+#include <unistd.h>			// close
 
 #include "utils/common/msg.h"		// WARN
 #include "libknot/util/descriptor.h"	// KNOT_CLASS_IN
@@ -60,13 +62,27 @@ void server_free(server_t *server)
 	free(server);
 }
 
-int get_socktype(const params_t *params, const uint16_t type)
+static void net_clean(net_t *net)
 {
-	if (params == NULL) {
-		return KNOT_EINVAL;
+	free(net->proto);
+	free(net->addr);
+}
+
+int get_iptype(const ip_t ip)
+{
+	switch (ip) {
+	case IP_4:
+		return AF_INET;
+	case IP_6:
+		return AF_INET6;
+	default:
+		return AF_UNSPEC;
 	}
+}
 
-	switch (params->protocol) {
+int get_socktype(const protocol_t proto, const uint16_t type)
+{
+	switch (proto) {
 	case PROTO_TCP:
 		return SOCK_STREAM;
 	case PROTO_UDP:
@@ -83,48 +99,76 @@ int get_socktype(const params_t *params, const uint16_t type)
 	}
 }
 
-int send_msg(const params_t *params,
-             const uint16_t  type,
-             const server_t *server,
-             const uint8_t  *buf,
-             const size_t   buf_len)
+static void net_info(net_t *net)
 {
-	struct addrinfo hints, *res;
-	struct pollfd pfd;
-	int sockfd;
+	struct sockaddr_storage ss;
+	socklen_t               ss_len = sizeof(ss);
+	char                    addr[INET6_ADDRSTRLEN] = "NULL";
+	int                     port = -1;
+
+	// Set connected socket type.
+	switch (net->socktype) {
+	case SOCK_STREAM:
+		net->proto = strdup("TCP");
+		break;
+	case SOCK_DGRAM:
+		net->proto = strdup("UDP");
+		break;
+	}
 
-	if (params == NULL || server == NULL || buf == NULL) {
-		return KNOT_EINVAL;
+	// Get connected address.
+	if (getpeername(net->sockfd, (struct sockaddr*)&ss, &ss_len) == 0) {
+		if (ss.ss_family == AF_INET) {
+			struct sockaddr_in *s = (struct sockaddr_in *)&ss;
+			port = ntohs(s->sin_port);
+			inet_ntop(AF_INET, &s->sin_addr, addr, sizeof(addr));
+		} else { // AF_INET6
+			struct sockaddr_in6 *s = (struct sockaddr_in6 *)&ss;
+			port = ntohs(s->sin6_port);
+			inet_ntop(AF_INET6, &s->sin6_addr, addr, sizeof(addr));
+		}
 	}
 
-	memset(&hints, 0, sizeof hints);
+	net->addr = strdup(addr);
+	net->port = port;
+}
 
-	// Set IP type.
-	if (params->ip == IP_4) {
-		hints.ai_family = AF_INET;
-	} else if (params->ip == IP_6) {
-		hints.ai_family = AF_INET6;
-	} else {
-		hints.ai_family = AF_UNSPEC;
+int net_connect(const server_t *local,
+                const server_t *remote,
+                const int      iptype,
+                const int      socktype,
+                const int      wait,
+                net_t          *net)
+{
+	struct addrinfo hints, *res;
+	struct pollfd   pfd;
+	int             sockfd, cs, err = 0;
+	socklen_t       err_len = sizeof(err);
+
+	if (remote == NULL || net == NULL) {
+		return KNOT_EINVAL;
 	}
 
-	// Set TCP or UDP.
-	hints.ai_socktype = get_socktype(params, type);
+	memset(&hints, 0, sizeof(hints));
+
+	// Fill in relevant hints.
+	hints.ai_family = iptype;
+	hints.ai_socktype = socktype;
 
 	// Get connection parameters.
-	if (getaddrinfo(server->name, server->service, &hints, &res) != 0) {
-		WARN("can't use nameserver %s port %s\n",
-		     server->name, server->service);
-		return -1;
+	if (getaddrinfo(remote->name, remote->service, &hints, &res) != 0) {
+		WARN("can't use server %s service %s\n",
+		     remote->name, remote->service);
+		return KNOT_ERROR;
 	}
 
 	// Create socket.
 	sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-
 	if (sockfd == -1) {
-		WARN("can't create socket for nameserver %s port %s\n",
-		     server->name, server->service);
-		return -1;
+		WARN("can't create socket for %s#%s\n",
+		     remote->name, remote->service);
+		freeaddrinfo(res);
+		return KNOT_ERROR;
 	}
 
 	// Initialize poll descriptor structure.
@@ -137,87 +181,129 @@ int send_msg(const params_t *params,
 		WARN("can't create non-blocking socket\n");
 	}
 
+	// Bind to address if specified.
+	if (local != NULL) {
+		struct addrinfo lhints, *lres;
+
+		memset(&lhints, 0, sizeof(lhints));
+
+		// Fill in relevant hints.
+		lhints.ai_family = iptype;
+		lhints.ai_socktype = socktype;
+
+		// Get connection parameters.
+		if (getaddrinfo(local->name, local->service, &lhints, &lres)
+		    != 0) {
+			WARN("can't use local %s service %s\n",
+			     local->name, local->service);
+		}
+
+		// Bind to the address.
+		if (bind(sockfd, lres->ai_addr, lres->ai_addrlen) == -1) {
+			WARN("can't bind to %s#%s\n",
+			     local->name, local->service);
+		}
+	}
+
 	// Connect using socket.
 	if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1 &&
 	    errno != EINPROGRESS) {
-		WARN("can't connect to nameserver %s port %s\n",
-		     server->name, server->service);
-		shutdown(sockfd, SHUT_RDWR);
-		return -1;
+		WARN("can't connect to %s#%s\n",
+		     remote->name, remote->service);
+		close(sockfd);
+		freeaddrinfo(res);
+		return KNOT_ERROR;
 	}
 
+	// Free getaddrr data.
+	freeaddrinfo(res);
+
 	// Check for connection timeout.
-	if (poll(&pfd, 1, 1000 * params->wait) != 1) {
-		WARN("can't wait for connection to nameserver %s port %s\n",
-		     server->name, server->service);
-		shutdown(sockfd, SHUT_RDWR);
-		return -1;
+	if (poll(&pfd, 1, 1000 * wait) != 1) {
+		WARN("can't wait for connection to %s#%s\n",
+		     remote->name, remote->service);
+		close(sockfd);
+		return KNOT_ERROR;
 	}
 	
-	// Check if socket is writeable (waited for NB connect)
-	int err = 0;
-	socklen_t elen = sizeof(err);
-	int cs = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &elen);
+	// Check if NB socket is writeable.
+	cs = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &err_len);
 	if (cs < 0 || err != 0) {
-		WARN("can't connect to nameserver %s port %s\n",
-		     server->name, server->service);
-		shutdown(sockfd, SHUT_RDWR);
-		return -1;
+		WARN("can't connect to %s#%s\n",
+		     remote->name, remote->service);
+		close(sockfd);
+		return KNOT_ERROR;
 	}
-	
+
+	// Fill in output.
+	net->sockfd = sockfd;
+	net->socktype = socktype;
+	net->wait = wait;
+
+	// Fill in additional information.
+	net_info(net);
+
+	return KNOT_EOK;
+}
+
+int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len)
+{
+	if (net == NULL || buf == NULL) {
+		return KNOT_EINVAL;
+	}
+
 	// For TCP add leading length bytes.
-	if (hints.ai_socktype == SOCK_STREAM) {
+	if (net->socktype == SOCK_STREAM) {
 		uint16_t pktsize = htons(buf_len);
 
-		if (send(sockfd, &pktsize, sizeof(pktsize), 0) !=
+		if (send(net->sockfd, &pktsize, sizeof(pktsize), 0) !=
 		    sizeof(pktsize)) {
-			WARN("TCP packet leading lenght\n");
+			WARN("can't send leading TCP bytes to %s#%i\n",
+			net->addr, net->port);
+			return KNOT_ERROR;
 		}
 	}
 
 	// Send data.
-	if (send(sockfd, buf, buf_len, 0) != buf_len) {
-		WARN("can't send query\n");
+	if (send(net->sockfd, buf, buf_len, 0) != buf_len) {
+		WARN("can't send query to %s#%i over %s\n",
+		     net->addr, net->port, net->proto);
+		return KNOT_ERROR;
 	}
 
-	// Free getaddrr data.
-	freeaddrinfo(res);
-
-	return sockfd;
+	return KNOT_EOK;
 }
 
-int receive_msg(const params_t *params,
-                const uint16_t  type,
-                int            sockfd,
-                uint8_t        *buf,
-                const size_t   buf_len)
+int net_receive(const net_t *net, uint8_t *buf, const size_t buf_len)
 {
 	ssize_t       ret;
 	struct pollfd pfd;
 
-	if (params == NULL || buf == NULL) {
+	if (net == NULL || buf == NULL) {
 		return KNOT_EINVAL;
 	}
 
 	// Initialize poll descriptor structure.
-	pfd.fd = sockfd;
+	pfd.fd = net->sockfd;
 	pfd.events = POLLIN;
 	pfd.revents = 0;
 
-	if (get_socktype(params, type) == SOCK_STREAM) {
+	if (net->socktype == SOCK_STREAM) {
 		uint16_t msg_len;
 		uint32_t total = 0;
 
 		// Wait for data.
-		if (poll(&pfd, 1, 1000 * params->wait) != 1) {
-			WARN("can't wait for TCP message length\n");
+		if (poll(&pfd, 1, 1000 * net->wait) != 1) {
+			WARN("can't wait for leading TCP bytes from %s#%i\n",
+			     net->addr, net->port);
 			return KNOT_ERROR;
 		}
 
 		// Receive TCP message header.
-		if (recv(sockfd, &msg_len, sizeof(msg_len), 0) !=
+		if (recv(net->sockfd, &msg_len, sizeof(msg_len), 0) !=
 		    sizeof(msg_len)) {
-			WARN("can't receive TCP message length\n");
+			WARN("can't receive leading TCP bytes from %s#%i\n",
+			     net->addr, net->port);
 			return KNOT_ERROR;
 		}
 
@@ -226,16 +312,18 @@ int receive_msg(const params_t *params,
 
 		// Receive whole answer message by parts.
 		while (total < msg_len) {
-			if (poll(&pfd, 1, 1000 * params->wait) != 1) {
-				WARN("can't wait for TCP answer\n");
+			if (poll(&pfd, 1, 1000 * net->wait) != 1) {
+				WARN("can't wait for TCP answer from %s#%i\n",
+				     net->addr, net->port);
 				return KNOT_ERROR;
 			}
 
 			// Receive piece of message.
-			ret = recv(sockfd, buf + total, msg_len - total, 0);
+			ret = recv(net->sockfd, buf + total, msg_len - total, 0);
 
 			if (ret <= 0) {
-				WARN("can't receive TCP answer\n");
+				WARN("can't receive TCP answer from %s#%i\n",
+				     net->addr, net->port);
 				return KNOT_ERROR;
 			}
 
@@ -245,16 +333,18 @@ int receive_msg(const params_t *params,
 		return total;
 	} else {
 		// Wait for datagram data.
-		if (poll(&pfd, 1, 1000 * params->wait) != 1) {
-			WARN("can't wait for UDP answer\n");
+		if (poll(&pfd, 1, 1000 * net->wait) != 1) {
+			WARN("can't wait for UDP answer from %s#%i\n",
+			     net->addr, net->port);
 			return KNOT_ERROR;
 		}
 
 		// Receive whole UDP datagram.
-		ret = recv(sockfd, buf, buf_len, 0);
+		ret = recv(net->sockfd, buf, buf_len, 0);
 
 		if (ret <= 0) {
-			WARN("can't receive UDP answer\n");
+			WARN("can't receive UDP answer from %s#%i\n",
+			     net->addr, net->port);
 			return KNOT_ERROR;
 		}
 
@@ -263,3 +353,14 @@ int receive_msg(const params_t *params,
 
 	return KNOT_EOK;
 }
+
+void net_close(net_t *net)
+{
+	if (net == NULL) {
+		return;
+	}
+
+	close(net->sockfd);
+	net_clean(net);
+}
+
diff --git a/src/utils/common/netio.h b/src/utils/common/netio.h
index c70459a7946171af82a0f96d6cf733a704e4f4c1..83f2ace1fe55dc3f67a756339a1ee755925699ff 100644
--- a/src/utils/common/netio.h
+++ b/src/utils/common/netio.h
@@ -42,16 +42,34 @@ typedef struct {
 	char	*service;
 } server_t;
 
+typedef struct {
+	int	sockfd;
+	int	socktype;
+	char	*proto;
+	char	*addr;
+	int	port;
+	int	wait;
+} net_t;
+
 server_t* server_create(const char *name, const char *service);
 
 void server_free(server_t *server);
 
-int get_socktype(const params_t *params, const uint16_t type);
+int get_iptype(const ip_t ip);
+
+int get_socktype(const protocol_t proto, const uint16_t type);
+
+int net_connect(const server_t *local,
+                const server_t *remote,
+                const int      iptype,
+                const int      socktype,
+                const int      wait,
+                net_t          *net);
+
+int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len);
 
-int send_msg(const params_t *params, const uint16_t type,
-             const server_t *server, const uint8_t *buf, size_t buf_len);
+int net_receive(const net_t *net, uint8_t *buf, const size_t buf_len);
 
-int receive_msg(const params_t *params, const uint16_t type,
-                int sockfd, uint8_t *buf, size_t buf_len);
+void net_close(net_t *net);
 
 #endif // _UTILS__NETIO_H_
diff --git a/src/utils/common/params.c b/src/utils/common/params.c
index 62a2cb65456ad46bf3f6dc16b086aceb6a5d4779..90ae1b515e45ca41d752a86f6f736bc2b502e865 100644
--- a/src/utils/common/params.c
+++ b/src/utils/common/params.c
@@ -23,7 +23,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>			// free
-#include <netinet/in.h>                 // in_addr
+#include <netinet/in.h>			// in_addr
 #include <arpa/inet.h>			// inet_pton
 #include <sys/socket.h>			// AF_INET (BSD)
 
@@ -38,6 +38,19 @@
 #define IPV4_REVERSE_DOMAIN	"in-addr.arpa."
 #define IPV6_REVERSE_DOMAIN	"ip6.arpa."
 
+const style_t DEFAULT_STYLE = {
+	.format = FORMAT_VERBOSE,
+	.show_header = true,
+	.show_footer = true,
+	.show_query = false,
+	.show_question = true,
+	.show_answer = true,
+	.show_authority = true,
+	.show_additional = true,
+	.show_class = true,
+	.show_ttl = true,
+};
+
 static knot_dname_t* create_fqdn_from_str(const char *str, size_t len)
 {
 	knot_dname_t *d = NULL;
@@ -53,14 +66,14 @@ static knot_dname_t* create_fqdn_from_str(const char *str, size_t len)
 
 /* Table of known keys in private-key-format */
 static const char *pkey_tbl[] = {
-        "\x09" "Activate:",
-        "\x0a" "Algorithm:",
-        "\x05" "Bits:",
-        "\x08" "Created:",
-        "\x04" "Key:",
-        "\x13" "Private-key-format:",
-        "\x08" "Publish:",
-        NULL
+	"\x09" "Activate:",
+	"\x0a" "Algorithm:",
+	"\x05" "Bits:",
+	"\x08" "Created:",
+	"\x04" "Key:",
+	"\x13" "Private-key-format:",
+	"\x08" "Publish:",
+	NULL
 };
 
 enum {
@@ -183,60 +196,6 @@ char* get_fqd_name(const char *name)
 	return fqd_name;
 }
 
-void params_flag_ipv4(params_t *params)
-{
-	if (params == NULL) {
-		return;
-	}
-
-	params->ip = IP_4;
-}
-
-void params_flag_ipv6(params_t *params)
-{
-	if (params == NULL) {
-		return;
-	}
-
-	params->ip = IP_6;
-}
-
-void params_flag_servfail(params_t *params)
-{
-	if (params == NULL) {
-		return;
-	}
-
-	params->servfail_stop = true;
-}
-
-void params_flag_nowait(params_t *params)
-{
-	if (params == NULL) {
-		return;
-	}
-
-	params->wait = -1;
-}
-
-void params_flag_tcp(params_t *params)
-{
-	if (params == NULL) {
-		return;
-	}
-
-	params->protocol = PROTO_TCP;
-}
-
-void params_flag_verbose(params_t *params)
-{
-	if (params == NULL) {
-		return;
-	}
-
-	params->format = FORMAT_VERBOSE;
-}
-
 int params_parse_port(const char *value, char **port)
 {
 	char *new_port = strdup(value);
@@ -253,18 +212,20 @@ int params_parse_port(const char *value, char **port)
 	return KNOT_EOK;
 }
 
-int params_parse_class(const char *value, int32_t *rclass)
+int params_parse_class(const char *value, uint16_t *rclass)
 {
 	if (value == NULL || rclass == NULL) {
 		return KNOT_EINVAL;
 	}
 
-	*rclass = knot_rrclass_from_string(value);
-
-	return KNOT_EOK;
+	if (knot_rrclass_from_string(value, rclass) == 0) {
+		return KNOT_EOK;
+	} else {
+		return KNOT_EINVAL;
+	}
 }
 
-int params_parse_type(const char *value, int32_t *rtype, uint32_t *xfr_serial)
+int params_parse_type(const char *value, uint16_t *rtype, uint32_t *xfr_serial)
 {
 	if (value == NULL || rtype == NULL || xfr_serial == NULL) {
 		return KNOT_EINVAL;
@@ -274,7 +235,9 @@ int params_parse_type(const char *value, int32_t *rtype, uint32_t *xfr_serial)
 
 	// There is no additional parameter.
 	if (param_pos == strlen(value)) {
-		*rtype = knot_rrtype_from_string(value);
+		if (knot_rrtype_from_string(value, rtype) != 0) {
+			return KNOT_EINVAL;
+		}
 
 		// IXFR requires serial parameter.
 		if (*rtype == KNOT_RRTYPE_IXFR) {
@@ -284,7 +247,10 @@ int params_parse_type(const char *value, int32_t *rtype, uint32_t *xfr_serial)
 	} else {
 		char *type_char = strndup(value, param_pos);
 
-		*rtype = knot_rrtype_from_string(type_char);
+		if (knot_rrtype_from_string(type_char, rtype) != 0) {
+			free(type_char);
+			return KNOT_EINVAL;
+		}
 
 		free(type_char);
 
@@ -427,6 +393,12 @@ int params_parse_tsig(const char *value, knot_key_t *key)
 		k = h;
 	}
 
+	if (!s) {
+		ERR("invalid key option format, use [hmac:]keyname:secret\n");
+		free(h);
+		return KNOT_EINVAL;
+	}
+
 	/* Parse key name. */
 	key->name = create_fqdn_from_str(k, strlen(k));
 	key->secret = strdup(s);
diff --git a/src/utils/common/params.h b/src/utils/common/params.h
index d5e73bd18a528c4343f7c170af7042081311c3fd..c32225145978a1728898bb6320d543ca1ef0b8bd 100644
--- a/src/utils/common/params.h
+++ b/src/utils/common/params.h
@@ -27,41 +27,35 @@
 #ifndef _UTILS__PARAMS_H_
 #define _UTILS__PARAMS_H_
 
-#include <stdbool.h>			// bool
 #include <stdint.h>			// uint16_t
 
-#include "common/lists.h"		// node
+#include "common/lists.h"		// list
 #include "libknot/tsig.h"		// knot_key_t
 
 #define DEFAULT_IPV4_NAME	"127.0.0.1"
 #define DEFAULT_IPV6_NAME	"::1"
 #define DEFAULT_DNS_PORT	"53"
 #define DEFAULT_UDP_SIZE	512
+#define DEFAULT_EDNS_SIZE	4096
 #define MAX_PACKET_SIZE		65535
 
 #define SEP_CHARS		"\n\t "
 
+/*! \brief Variants of IP protocol. */
 typedef enum {
 	IP_ALL,
 	IP_4,
 	IP_6
-} ip_version_t;
+} ip_t;
 
+/*! \brief Variants of transport protocol. */
 typedef enum {
 	PROTO_ALL,
 	PROTO_TCP,
 	PROTO_UDP
 } protocol_t;
 
-typedef enum {
-	/*!< Classic queries in list. */
-	OPERATION_QUERY,
-	/*!< Query for NS and all authoritative SOA records. */
-	OPERATION_LIST_SOA,
-	/*!< Default mode for nsupdate. */
-	OPERATION_UPDATE,
-} operation_t;
-
+/*! \brief Variants of text output format. */
 typedef enum {
 	/*!< Short dig output. */
 	FORMAT_DIG,
@@ -75,63 +69,46 @@ typedef enum {
 	FORMAT_MULTILINE,
 } format_t;
 
-/*! \brief Structure containing parameters. */
+/*! \brief Text output settings. */
 typedef struct {
-	/*!< List of nameservers to query to. */
-	list		servers;
-	/*!< Operation mode. */
-	operation_t	operation;
-	/*!< Version of ip protocol to use. */
-	ip_version_t	ip;
-	/*!< Type (TCP, UDP) protocol to use. */
-	protocol_t	protocol;
-	/*!< Default port/service to connect to. */
-	char		*port;
-	/*!< Default class number (16unsigned + -1 uninitialized). */
-	int32_t		class_num;
-	/*!< Default type number (16unsigned + -1 uninitialized). */
-	int32_t		type_num;
-	/*!< Default TTL. */
-	uint32_t	ttl;
-	/*!< Default SOA serial for XFR. */
-	uint32_t	xfr_serial;
-	/*!< UDP buffer size. */
-	uint32_t	udp_size;
-	/*!< Number of UDP retries. */
-	uint32_t	retries;
-	/*!< Wait for network response in seconds (-1 means forever). */
-	int32_t		wait;
-	/*!< Stop quering if servfail. */
-	bool		servfail_stop;
 	/*!< Output format. */
 	format_t	format;
-	/*!< TSIG key used. */
-	knot_key_t	key;
-	/*!< Implementation specific data. */
-	void		*d;
-} params_t;
 
-char* get_reverse_name(const char *name);
+	/*!< Show header info. */
+	bool		show_header;
+	/*!< Show footer info. */
+	bool		show_footer;
 
-char* get_fqd_name(const char *name);
+	/*!< Show query packet. */
+	bool		show_query;
 
-void params_flag_ipv4(params_t *params);
+	/*!< Show QUERY/ZONE section. */
+	bool		show_question;
+	/*!< Show ANSWER/PREREQ section. */
+	bool		show_answer;
+	/*!< Show UPDATE/AUTHORITY section. */
+	bool		show_authority;
+	/*!< Show ADDITIONAL section. */
+	bool		show_additional;
 
-void params_flag_ipv6(params_t *params);
+	/*!< Show class. */
+	bool		show_class;
+	/*!< Show ttl. */
+	bool		show_ttl;
+} style_t;
 
-void params_flag_servfail(params_t *params);
+/*! \brief Default style settings. */
+extern const style_t DEFAULT_STYLE;
 
-void params_flag_nowait(params_t *params);
-
-void params_flag_tcp(params_t *params);
+char* get_reverse_name(const char *name);
 
-void params_flag_verbose(params_t *params);
+char* get_fqd_name(const char *name);
 
 int params_parse_port(const char *value, char **port);
 
-int params_parse_class(const char *value, int32_t *rclass);
+int params_parse_class(const char *value, uint16_t *rclass);
 
-int params_parse_type(const char *value, int32_t *rtype, uint32_t *xfr_serial);
+int params_parse_type(const char *value, uint16_t *rtype, uint32_t *xfr_serial);
 
 int params_parse_server(const char *value, list *servers, const char *def_port);
 
diff --git a/src/utils/dig/dig_exec.c b/src/utils/dig/dig_exec.c
index 6f5651d2ff6ddfdbcb6298bb67afeef9a6b28889..e46bf55cdfa4c2402aefc4250e86844bb27ff20b 100644
--- a/src/utils/dig/dig_exec.c
+++ b/src/utils/dig/dig_exec.c
@@ -26,46 +26,66 @@
 #include "libknot/consts.h"		// KNOT_RCODE_NOERROR
 #include "libknot/util/wire.h"		// knot_wire_set_rd
 #include "libknot/packet/query.h"	// knot_query_init
+#include "libknot/packet/response.h"	// knot_response_add_opt
 #include "utils/common/msg.h"		// WARN
-#include "utils/common/params.h"	// params_t
 #include "utils/common/netio.h"		// get_socktype
 #include "utils/common/exec.h"		// print_packet
 
-static knot_packet_t* create_query_packet(const params_t *params,
-                                          const query_t  *query,
-                                          uint8_t        **data,
-                                          size_t         *data_len)
+static knot_packet_t* create_query_packet(const query_t *query,
+                                          uint8_t       **data,
+                                          size_t        *data_len)
 {
 	knot_question_t q;
-
-	dig_params_t *ext_params = DIG_PARAM(params);
+	knot_packet_t   *packet;
 
 	// Set packet buffer size.
-	int max_size = MAX_PACKET_SIZE;
-	if (get_socktype(params, query->qtype) != SOCK_STREAM) {
-		// For UDP default or specified EDNS size.
-		max_size = params->udp_size;
+	int max_size = query->udp_size;
+
+	if (max_size < 0) {
+		if (get_socktype(query->protocol, query->type_num)
+		    == SOCK_STREAM) {
+			max_size = MAX_PACKET_SIZE;
+		} else if (query->flags.do_flag == true) {
+			max_size = DEFAULT_EDNS_SIZE;
+		} else {
+			max_size = DEFAULT_UDP_SIZE;
+		}
 	}
 
 	// Create packet skeleton.
-	knot_packet_t *packet = create_empty_packet(KNOT_PACKET_PREALLOC_NONE,
-	                                            max_size);
+	packet = create_empty_packet(KNOT_PACKET_PREALLOC_NONE, max_size);
 
 	if (packet == NULL) {
 		return NULL;
 	}
 
-	// Set recursion bit to wireformat.
-	if (ext_params->rd_flag == true) {
+	// Set flags to wireformat.
+	if (query->flags.aa_flag == true) {
+		knot_wire_set_aa(packet->wireformat);
+	}
+	if (query->flags.tc_flag == true) {
+		knot_wire_set_tc(packet->wireformat);
+	}
+	if (query->flags.rd_flag == true) {
 		knot_wire_set_rd(packet->wireformat);
-	} else {
-		knot_wire_flags_clear_rd(packet->wireformat);
+	}
+	if (query->flags.ra_flag == true) {
+		knot_wire_set_ra(packet->wireformat);
+	}
+	if (query->flags.z_flag == true) {
+		knot_wire_set_z(packet->wireformat);
+	}
+	if (query->flags.ad_flag == true) {
+		knot_wire_set_ad(packet->wireformat);
+	}
+	if (query->flags.cd_flag == true) {
+		knot_wire_set_cd(packet->wireformat);
 	}
 
 	// Fill auxiliary question structure.
-	q.qclass = query->qclass;
-	q.qtype = query->qtype;
-	q.qname = knot_dname_new_from_str(query->qname, strlen(query->qname), 0);
+	q.qclass = query->class_num;
+	q.qtype = query->type_num;
+	q.qname = knot_dname_new_from_str(query->owner, strlen(query->owner), 0);
 
 	if (q.qname == NULL) {
 		knot_dname_release(q.qname);
@@ -81,7 +101,7 @@ static knot_packet_t* create_query_packet(const params_t *params,
 	}
 
 	// For IXFR query add authority section.
-	if (query->qtype == KNOT_RRTYPE_IXFR) {
+	if (query->type_num == KNOT_RRTYPE_IXFR) {
 		int ret;
 		size_t pos = 0;
 		// SOA rdata in wireformat.
@@ -110,7 +130,7 @@ static knot_packet_t* create_query_packet(const params_t *params,
 		// Create rrset with SOA record.
 		knot_rrset_t *soa = knot_rrset_new(q.qname,
 		                                   KNOT_RRTYPE_SOA,
-		                                   params->class_num,
+		                                   query->class_num,
 		                                   0);
 		ret = knot_rrset_add_rdata(soa, soa_data);
 
@@ -134,6 +154,34 @@ static knot_packet_t* create_query_packet(const params_t *params,
 		}
 	}
 
+	// Set DO flag to EDNS section.
+	if (query->flags.do_flag == true) {
+		knot_opt_rr_t *opt_rr = knot_edns_new();
+
+		if (opt_rr == NULL) {
+			ERR("can't create EDNS section\n");
+			knot_edns_free(&opt_rr);
+			knot_dname_release(q.qname);
+			knot_packet_free(&packet);
+			return NULL;
+		}
+
+		knot_edns_set_version(opt_rr, 0);
+		knot_edns_set_payload(opt_rr, max_size);
+
+		if (knot_response_add_opt(packet, opt_rr, 0, 0) != KNOT_EOK) {
+			ERR("can't set EDNS section\n");
+			knot_edns_free(&opt_rr);
+			knot_dname_release(q.qname);
+			knot_packet_free(&packet);
+			return NULL;
+		}
+
+		knot_edns_set_do(&packet->opt_rr);
+
+		knot_edns_free(&opt_rr);
+	}
+
 	// Create wire query.
 	if (knot_packet_to_wire(packet, data, data_len) != KNOT_EOK) {
 		ERR("can't create wire query packet\n");
@@ -159,31 +207,34 @@ static bool check_id(const knot_packet_t *query, const knot_packet_t *reply)
 	return true;
 }
 
-static int check_rcode(const params_t *params, const knot_packet_t *reply)
+static int check_rcode(const bool servfail_stop, const knot_packet_t *reply)
 {
 	uint8_t rcode = knot_wire_get_rcode(reply->wireformat);
 
-	if (rcode == KNOT_RCODE_SERVFAIL && params->servfail_stop == true) {
+	if (rcode == KNOT_RCODE_SERVFAIL && servfail_stop == true) {
 		return -1;
 	}
 
 	return rcode;
 }
 
-static bool check_question(const knot_packet_t *query,
+static void check_question(const knot_packet_t *query,
                            const knot_packet_t *reply)
 {
+	if (reply->header.qdcount < 1) {
+		WARN("response doesn't have question section\n");
+		return;
+	}
+
 	int name_diff = knot_dname_compare_cs(reply->question.qname,
 	                                      query->question.qname);
 
 	if (reply->question.qclass != query->question.qclass ||
 	    reply->question.qtype  != query->question.qtype ||
 	    name_diff != 0) {
-		WARN("different question sections\n");
-		return false;
+		WARN("query/response question sections are different\n");
+		return;
 	}
-
-	return true;
 }
 
 static int64_t first_serial_check(const knot_packet_t *reply)
@@ -222,7 +273,7 @@ static bool last_serial_check(const uint32_t serial, const knot_packet_t *reply)
 	}
 }
 
-void process_query(const params_t *params, const query_t *query)
+void process_query(const query_t *query)
 {
 	float		elapsed;
 	bool 		id_ok, stop;
@@ -237,29 +288,40 @@ void process_query(const params_t *params, const query_t *query)
 	size_t		total_len = 0;
 	size_t		msg_count = 0;
 
-	if (params == NULL || query == NULL) {
+	if (query == NULL) {
 		return;
 	}
 
 	// Create query packet.
-	out_packet = create_query_packet(params, query, &out, &out_len);
+	out_packet = create_query_packet(query, &out, &out_len);
 
 	if (out_packet == NULL) {
 		return;
 	}
 
-	WALK_LIST(server, params->servers) {
-		int  sockfd;
-		int  rcode;
+	WALK_LIST(server, query->servers) {
+		net_t net;
+		int   rcode, ret;
 
 		// Start meassuring of query/xfr time.
 		gettimeofday(&t_start, NULL);
 
 		// Send query message.
-		sockfd = send_msg(params, query->qtype, (server_t *)server,
-		                  out, out_len);
+		ret = net_connect(NULL,
+		                  (server_t *)server,
+		                  get_iptype(query->ip),
+		                  get_socktype(query->protocol, query->type_num),
+		                  query->wait,
+		                  &net);
 
-		if (sockfd < 0) {
+		if (ret != KNOT_EOK) {
+			continue;
+		}
+
+		ret = net_send(&net, out, out_len);
+
+		if (ret != KNOT_EOK) {
+			net_close(&net);
 			continue;
 		}
 
@@ -268,8 +330,7 @@ void process_query(const params_t *params, const query_t *query)
 		// Loop over incomming messages, unless reply id is correct.
 		while (id_ok == false) {
 			// Receive reply message.
-			in_len = receive_msg(params, query->qtype, sockfd,
-			                     in, sizeof(in));
+			in_len = net_receive(&net, in, sizeof(in));
 
 			if (in_len <= 0) {
 				stop = true;
@@ -300,32 +361,29 @@ void process_query(const params_t *params, const query_t *query)
 
 		// Timeout/data error -> try next nameserver.
 		if (stop == true) {
-			shutdown(sockfd, SHUT_RDWR);
+			net_close(&net);
 			continue;
 		}
 
 		// Check rcode.
-		rcode = check_rcode(params, in_packet);
+		rcode = check_rcode(query->servfail_stop, in_packet);
 
 		// Servfail + stop if servfail -> stop processing.
 		if (rcode == -1) {
-			shutdown(sockfd, SHUT_RDWR);
+			net_close(&net);
 			break;
 		// Servfail -> try next nameserver.
 		} else if (rcode == KNOT_RCODE_SERVFAIL) {
-			shutdown(sockfd, SHUT_RDWR);
+			net_close(&net);
 			continue;
 		}
 
 		// Check for question sections equality.
-		if (check_question(out_packet, in_packet) == false) {
-			shutdown(sockfd, SHUT_RDWR);
-			continue;
-		}
+		check_question(out_packet, in_packet);
 
 		// Dump one standard reply message and finish.
-		if (query->qtype != KNOT_RRTYPE_AXFR &&
-		    query->qtype != KNOT_RRTYPE_IXFR) {
+		if (query->type_num != KNOT_RRTYPE_AXFR &&
+		    query->type_num != KNOT_RRTYPE_IXFR) {
 			// Stop meassuring of query time.
 			gettimeofday(&t_end, NULL);
 
@@ -338,12 +396,12 @@ void process_query(const params_t *params, const query_t *query)
 			total_len += in_len;
 
 			// Print formated data.
-			print_packet(params->format, in_packet, total_len,
-			             sockfd, elapsed, msg_count);
+			print_packet(&net, &query->style, in_packet, elapsed,
+			             total_len, msg_count);
 
 			knot_packet_free(&in_packet);
 
-			shutdown(sockfd, SHUT_RDWR);
+			net_close(&net);
 
 			// Stop quering nameservers.
 			break;
@@ -354,16 +412,16 @@ void process_query(const params_t *params, const query_t *query)
 		total_len += in_len;
 
 		// Start XFR dump.
-		print_header_xfr(params->format, query->qtype);
+		print_header_xfr(&query->style, query->type_num);
 
-		print_data_xfr(params->format, in_packet);
+		print_data_xfr(&query->style, in_packet);
 
 		// Read first SOA serial.
 		int64_t serial = first_serial_check(in_packet);
 
 		if (serial < 0) {
 			ERR("first answer resource record must be SOA\n");
-			shutdown(sockfd, SHUT_RDWR);
+			net_close(&net);
 			continue;
 		}
 
@@ -374,8 +432,7 @@ void process_query(const params_t *params, const query_t *query)
 			knot_packet_free(&in_packet);
 
 			// Receive reply message.
-			in_len = receive_msg(params, query->qtype, sockfd,
-					     in, sizeof(in));
+			in_len = net_receive(&net, in, sizeof(in));
 
 			if (in_len <= 0) {
 				stop = true;
@@ -404,7 +461,7 @@ void process_query(const params_t *params, const query_t *query)
 			id_ok = check_id(out_packet, in_packet);
 
 			// Check rcode.
-			rcode = check_rcode(params, in_packet);
+			rcode = check_rcode(query->servfail_stop, in_packet);
 
 			if (rcode != KNOT_RCODE_NOERROR) {
 				stop = true;
@@ -413,7 +470,7 @@ void process_query(const params_t *params, const query_t *query)
 			}
 
 			// Dump message data.
-			print_data_xfr(params->format, in_packet);
+			print_data_xfr(&query->style, in_packet);
 
 			// Count non-first XFR message.
 			msg_count++;
@@ -429,13 +486,13 @@ void process_query(const params_t *params, const query_t *query)
 			elapsed = (t_end.tv_sec - t_start.tv_sec) * 1000 +
 			          ((t_end.tv_usec - t_start.tv_usec) / 1000.0);
 
-			print_footer_xfr(params->format, total_len, sockfd,
-			                 elapsed, msg_count);
+			print_footer_xfr(&net, &query->style, elapsed,
+			                 total_len, msg_count);
 
 			knot_packet_free(&in_packet);
 		}
 
-		shutdown(sockfd, SHUT_RDWR);
+		net_close(&net);
 
 		// Stop quering nameservers.
 		break;
@@ -445,29 +502,28 @@ void process_query(const params_t *params, const query_t *query)
 	knot_packet_free(&out_packet);
 }
 
-int dig_exec(const params_t *params)
+int dig_exec(const dig_params_t *params)
 {
-	node *query = NULL;
+	node *n = NULL;
 
 	if (params == NULL) {
 		return KNOT_EINVAL;
 	}
 
-	dig_params_t *ext_params = DIG_PARAM(params);
+	// Loop over query list.
+	WALK_LIST(n, params->queries) {
+		query_t *query = (query_t *)n;
 
-	switch (params->operation) {
-	case OPERATION_QUERY:
-		// Loop over query list.
-		WALK_LIST(query, ext_params->queries) {
-			process_query(params, (query_t *)query);
+		switch (query->operation) {
+		case OPERATION_QUERY:
+			process_query(query);
+			break;
+		case OPERATION_LIST_SOA:
+			break;
+		default:
+			ERR("unsupported operation\n");
+			break;
 		}
-
-		break;
-	case OPERATION_LIST_SOA:
-		break;
-	default:
-		ERR("unsupported operation\n");
-		break;
 	}
 
 	return KNOT_EOK;
diff --git a/src/utils/dig/dig_exec.h b/src/utils/dig/dig_exec.h
index 4406e57d097a20d0539a387f67a5cb559f2a6c26..6ddaeeeac489b34d5b7ec57849d596468e05febf 100644
--- a/src/utils/dig/dig_exec.h
+++ b/src/utils/dig/dig_exec.h
@@ -30,9 +30,9 @@
 #include "utils/common/params.h"	// params_t
 #include "utils/dig/dig_params.h"	// query_t
 
-int dig_exec(const params_t *params);
+int dig_exec(const dig_params_t *params);
 
-void process_query(const params_t *params, const query_t *query);
+void process_query(const query_t *query);
 
 #endif // _DIG__DIG_EXEC_H_
 
diff --git a/src/utils/dig/dig_main.c b/src/utils/dig/dig_main.c
index 3a96ede5688fdde5b0f95d12592b2589f1605650..7cfbcc7112108e5966c8e43a0e5d37cf3e84225d 100644
--- a/src/utils/dig/dig_main.c
+++ b/src/utils/dig/dig_main.c
@@ -17,23 +17,29 @@
 #include <stdlib.h>			// EXIT_FAILURE
 
 #include "common/errcode.h"		// KNOT_EOK
-#include "utils/dig/dig_params.h"	// dig_params_parse
+#include "utils/dig/dig_params.h"	// dig_parse
 #include "utils/dig/dig_exec.h"		// dig_exec
 
 int main(int argc, char *argv[])
 {
 	int ret = EXIT_SUCCESS;
 
-	params_t params;
-	if (dig_params_parse(&params, argc, argv) == KNOT_EOK) {
+	dig_params_t params;
+	switch (dig_parse(&params, argc, argv)) {
+	case KNOT_EOK:
 		if (dig_exec(&params) != KNOT_EOK) {
 			ret = EXIT_FAILURE;
 		}
-	} else {
+		break;
+	case KNOT_ESTOP:
+		ret = EXIT_SUCCESS;
+		break;
+	default:
 		ret = EXIT_FAILURE;
+		break;
 	}
 
-	dig_params_clean(&params);
+	dig_clean(&params);
 	return ret;
 }
 
diff --git a/src/utils/dig/dig_params.c b/src/utils/dig/dig_params.c
index aa08522d7b30d209c67b8f923eb2889a3f1a87c4..d2701bae7f13747fcac30a806746e95bcf43f8f6 100644
--- a/src/utils/dig/dig_params.c
+++ b/src/utils/dig/dig_params.c
@@ -31,394 +31,632 @@
 #define DEFAULT_RETRIES_DIG	3
 #define DEFAULT_TIMEOUT_DIG	5
 
-query_t* query_create(const char    *qname,
-                      const int32_t qtype,
-                      const int32_t qclass)
+const flags_t DEFAULT_FLAGS = {
+	.aa_flag = false,
+	.tc_flag = false,
+	.rd_flag = true,
+	.ra_flag = false,
+	.z_flag  = false,
+	.ad_flag = false,
+	.cd_flag = false,
+	.do_flag = false
+};
+
+query_t* query_create(const char *owner, const query_t *conf)
 {
-	if (qname == NULL) {
-		return NULL;
-	}
-
 	// Create output structure.
 	query_t *query = calloc(1, sizeof(query_t));
 
-	// Check output.
 	if (query == NULL) {
 		return NULL;
 	}
 
-	// Fill output.
-	query->qname = strdup(qname);
-	query->qclass = qclass;
-	query->qtype = qtype;
-	query->xfr_serial = 0;
+	// Set an owner if any.
+	if (owner != NULL) {
+		if ((query->owner = strdup(owner)) == NULL) {
+			query_free(query);
+			return NULL;
+		}
+	}
+
+	// Initialize list of servers.
+	init_list(&query->servers);
+
+	// Initialization with defaults or with reference query.
+	if (conf == NULL) {
+		query->operation = OPERATION_QUERY;
+		query->ip = IP_ALL;
+		query->protocol = PROTO_ALL;
+		query->port = strdup(DEFAULT_DNS_PORT);
+		query->udp_size = -1;
+		query->retries = DEFAULT_RETRIES_DIG;
+		query->wait = DEFAULT_TIMEOUT_DIG;
+		query->servfail_stop = false;
+		query->class_num = -1;
+		query->type_num = -1;
+		query->xfr_serial = 0;
+		query->flags = DEFAULT_FLAGS;
+		query->style = DEFAULT_STYLE;
+		query->style.format = FORMAT_VERBOSE;
+	} else {
+		query->operation = conf->operation;
+		query->ip = conf->ip;
+		query->protocol = conf->protocol;
+		query->port = strdup(conf->port);
+		query->udp_size = conf->udp_size;
+		query->retries = conf->retries;
+		query->wait = conf->wait;
+		query->servfail_stop = conf->servfail_stop;
+		query->class_num = conf->class_num;
+		query->type_num = conf->type_num;
+		query->xfr_serial = conf->xfr_serial;
+		query->flags = conf->flags;
+		query->style = conf->style;
+	}
+
+	// Check port.
+	if (query->port == NULL) {
+		query_free(query);
+		return NULL;
+	}
 
 	return query;
 }
 
 void query_free(query_t *query)
 {
+	node *n = NULL, *nxt = NULL;
+
 	if (query == NULL) {
 		return;
 	}
 
-	free(query->qname);
+	// Clean up servers.
+	WALK_LIST_DELSAFE(n, nxt, query->servers) {
+		server_free((server_t *)n);
+	}
+
+	free(query->owner);
+	free(query->port);
 	free(query);
 }
 
-static void complete_queries(params_t *params)
+int dig_init(dig_params_t *params)
 {
-	node *query = NULL;
+	memset(params, 0, sizeof(*params));
 
-	WALK_LIST(query, DIG_PARAM(params)->queries) {
-		query_t *q = (query_t *)query;
+	// Initialize list of queries.
+	init_list(&params->queries);
 
-		if (q->qclass <= 0) {
-			if (params->class_num >= 0) {
-				q->qclass = params->class_num;
-			} else {
-				q->qclass = KNOT_CLASS_IN;
-			}
-		}
-		if (q->qtype <= 0) {
-			if (params->type_num >= 0) {
-				q->qtype = params->type_num;
-				q->xfr_serial = params->xfr_serial;
-			} else {
-				q->qtype = KNOT_RRTYPE_A;
-			}
-		}
+	// Create config query.
+	if ((params->config = query_create(NULL, NULL)) == NULL) {
+		return KNOT_ENOMEM;
 	}
+
+	return KNOT_EOK;
 }
 
-static int dig_params_init(params_t *params)
+void dig_clean(dig_params_t *params)
 {
+	node *n = NULL, *nxt = NULL;
+
+	if (params == NULL) {
+		return;
+	}
+
+	// Clean up queries.
+	WALK_LIST_DELSAFE(n, nxt, params->queries) {
+		query_free((query_t *)n);
+	}
+
+	// Clean up config.
+	query_free((query_t *)params->config);
+
+	// Clean up the structure.
 	memset(params, 0, sizeof(*params));
+}
 
-	// Create dig specific data structure.
-	params->d = calloc(1, sizeof(dig_params_t));
-	if (!params->d) {
+static int parse_name(const char *value, list *queries, const query_t *conf)
+{
+	query_t *query = NULL;
+
+	// If name is not FQDN, append trailing dot.
+	char *fqd_name = get_fqd_name(value);
+
+	// Create new query.
+	query = query_create(fqd_name, conf);
+
+	free(fqd_name);
+
+	if (query == NULL) {
 		return KNOT_ENOMEM;
 	}
-	dig_params_t *ext_params = DIG_PARAM(params);
-
-	// Initialize blank server list.
-	init_list(&params->servers);
-
-	// Default values.
-	params->operation = OPERATION_QUERY;
-	params->ip = IP_ALL;
-	params->protocol = PROTO_ALL;
-	params->port = strdup(DEFAULT_DNS_PORT);
-	params->udp_size = DEFAULT_UDP_SIZE;
-	params->class_num = -1;
-	params->type_num = -1;
-	params->xfr_serial = 0;
-	params->retries = DEFAULT_RETRIES_DIG;
-	params->wait = DEFAULT_TIMEOUT_DIG;
-	params->servfail_stop = false;
-	params->format = FORMAT_VERBOSE;
-
-	// Initialize list of queries.
-	init_list(&ext_params->queries);
 
-	// Extended params.
-	ext_params->rd_flag = true;
+	// Add new query to the queries.
+	add_tail(queries, (node *)query);
 
 	return KNOT_EOK;
 }
 
-void dig_params_clean(params_t *params)
+static int parse_reverse(const char *value, list *queries, const query_t *conf)
 {
-	node *n = NULL, *nxt = NULL;
+	query_t *query = NULL;
 
-	if (params == NULL) {
-		return;
+	// Create reverse name.
+	char *reverse = get_reverse_name(value);
+
+	if (reverse == NULL) {
+		return KNOT_EINVAL;
 	}
 
-	dig_params_t *ext_params = DIG_PARAM(params);
+	// Create reverse query for given address.
+	query = query_create(reverse, conf);
 
-	// Clean up server list.
-	WALK_LIST_DELSAFE(n, nxt, params->servers) {
-		server_free((server_t *)n);
-	}
+	free(reverse);
 
-	// Clean up query list.
-	WALK_LIST_DELSAFE(n, nxt, ext_params->queries) {
-		query_free((query_t *)n);
+	if (query == NULL) {
+		return KNOT_ENOMEM;
 	}
 
-	free(params->port);
+	// Set type for reverse query.
+	query->type_num = KNOT_RRTYPE_PTR;
 
-	// Destroy dig specific structure.
-	free(ext_params);
+	// Add new query to the queries.
+	add_tail(queries, (node *)query);
 
-	// Clean up the structure.
-	memset(params, 0, sizeof(*params));
+	return KNOT_EOK;
 }
 
-/*
-static void dig_params_flag_soa(params_t *params)
-{
-	params->type_num = KNOT_RRTYPE_SOA;
-	params->operation = OPERATION_LIST_SOA;
-}
-*/
-/*
-static int dig_params_parse_name(params_t *params, const char *name)
+static void complete_servers(query_t *query, const query_t *conf)
 {
-	char	*reverse = get_reverse_name(name);
-	char	*fqd_name = NULL;
-	query_t	*query;
+	node *n = NULL;
 
-	dig_params_t *ext_params = DIG_PARAM(params);
+	// Use servers from config if any.
+	if (list_size(&conf->servers) > 0) {
+		server_t *server;
 
-	// If name is not FQDN, append trailing dot.
-	fqd_name = get_fqd_name(name);
-
-	// RR type is known.
-	if (params->type_num >= 0) {
-		if (params->type_num == KNOT_RRTYPE_PTR) {
-			// Check for correct address.
-			if (reverse == NULL) {
-				ERR("invalid IPv4 or IPv6 address\n");
-				free(fqd_name);
-				return KNOT_EINVAL;
-			}
+		WALK_LIST(n, conf->servers) {
+			server_t *s = (server_t *)n;
 
-			// Add reverse query for address.
-			query = query_create(reverse, params->type_num);
-			if (query == NULL) {
-				free(reverse);
-				free(fqd_name);
-				return KNOT_ENOMEM;
-			}
-			add_tail(&ext_params->queries, (node *)query);
-		} else {
-			// Add query for name and specified type.
-			query = query_create(fqd_name, params->type_num);
-			if (query == NULL) {
-				free(reverse);
-				free(fqd_name);
-				return KNOT_ENOMEM;
-			}
-			// Set SOA serial for IXFR query.
-			if (params->type_num == KNOT_RRTYPE_IXFR) {
-				query_set_serial(query, params->xfr_serial);
+			server = server_create(s->name, query->port);
+
+			if (server == NULL) {
+				WARN("can't set nameserver %s port %s\n",
+				     s->name, s->service);
+				return;
 			}
-			add_tail(&ext_params->queries, (node *)query);
+			add_tail(&query->servers, (node *)server);
 		}
-	// RR type is unknown, use defaults.
-	} else {
-		if (reverse == NULL) {
-			// Add query for name and type A.
-			query = query_create(fqd_name, KNOT_RRTYPE_A);
-			if (query == NULL) {
-				free(fqd_name);
-				return KNOT_ENOMEM;
-			}
-			add_tail(&ext_params->queries, (node *)query);
+	// Use system specific.
+	} else if (get_nameservers(&query->servers, query->port) <= 0) {
+		WARN("can't read any nameservers\n");
+	}
+}
 
-			// Add query for name and type AAAA.
-			query = query_create(fqd_name, KNOT_RRTYPE_AAAA);
-			if (query == NULL) {
-				free(fqd_name);
-				return KNOT_ENOMEM;
-			}
-			add_tail(&ext_params->queries, (node *)query);
+void complete_queries(list *queries, const query_t *conf)
+{
+	query_t *q = NULL;
+	node    *n = NULL;
+
+	// If there is no query, add default query: NS to ".".
+	if (list_size(queries) == 0) {
+		q = query_create(".", conf);
+		if (q == NULL) {
+			WARN("can't create query . NS IN\n");
+			return;
+		}
+		q->class_num = KNOT_CLASS_IN;
+		q->type_num = KNOT_RRTYPE_NS;
+		add_tail(queries, (node *)q);
+	}
 
-			// Add query for name and type MX.
-			query = query_create(fqd_name, KNOT_RRTYPE_MX);
-			if (query == NULL) {
-				free(fqd_name);
-				return KNOT_ENOMEM;
+	WALK_LIST(n, *queries) {
+		query_t *q = (query_t *)n;
+
+		// Fill class number if missing.
+		if (q->class_num < 0) {
+			if (conf->class_num >= 0) {
+				q->class_num = conf->class_num;
+			} else {
+				q->class_num = KNOT_CLASS_IN;
 			}
-			add_tail(&ext_params->queries, (node *)query);
-		} else {
-			// Add reverse query for address.
-			query = query_create(reverse, KNOT_RRTYPE_PTR);
-			if (query == NULL) {
-				free(reverse);
-				free(fqd_name);
-				return KNOT_ENOMEM;
+		}
+
+		// Fill type number if missing.
+		if (q->type_num < 0) {
+			if (conf->type_num >= 0) {
+				q->type_num = conf->type_num;
+				q->xfr_serial = conf->xfr_serial;
+			} else {
+				q->type_num = KNOT_RRTYPE_A;
 			}
-			add_tail(&ext_params->queries, (node *)query);
+		}
+
+		// Fill nameserver list if empty.
+		if (list_size(&q->servers) == 0) {
+			complete_servers(q, conf);
 		}
 	}
+}
 
-	free(reverse);
-	free(fqd_name);
+static int parse_class(const char *value, query_t *query)
+{
+	uint16_t rclass;
+
+	if (params_parse_class(value, &rclass) != KNOT_EOK) {
+		return KNOT_EINVAL;
+	}
+
+	query->class_num = rclass;
 
 	return KNOT_EOK;
-}*/
+}
 
-static int dig_params_parse_name(const char *value, params_t *params)
+static int parse_type(const char *value, query_t *query)
 {
-	query_t	*query;
-	char    *fqd_name = NULL;
-
-	// If name is not FQDN, append trailing dot.
-	fqd_name = get_fqd_name(value);
+	uint16_t rtype;
+	uint32_t serial;
 
-	// Add name to query list.
-	query = query_create(fqd_name, params->type_num, params->class_num);
-	if (query == NULL) {
-		free(fqd_name);
-		return KNOT_ENOMEM;
+	if (params_parse_type(value, &rtype, &serial) != KNOT_EOK) {
+		return KNOT_EINVAL;
 	}
-	add_tail(&DIG_PARAM(params)->queries, (node *)query);
 
-	free(fqd_name);
+	query->type_num = rtype;
+	query->xfr_serial = serial;
 
 	return KNOT_EOK;
 }
 
-static int dig_params_parse_reverse(const char *value, params_t *params)
+static void dig_help(const bool verbose)
 {
-	char	*reverse = get_reverse_name(value);
-	query_t	*query;
-
-	// Check reverse input.
-	if (reverse == NULL) {
-		ERR("invalid IPv4 or IPv6 address\n");
-		return KNOT_EINVAL;
+	if (verbose == true) {
+		printf("Big help\n");
+	} else {
+		printf("Usage: [-aCdlrsTvw] [-4] [-6] [-c class] [-R retries]\n"
+	       	"       [-t type] [-W time] name [server]\n");
 	}
+}
 
-	// Add reverse query for address.
-	query = query_create(reverse, KNOT_RRTYPE_PTR, params->class_num);
-	if (query == NULL) {
-		free(reverse);
-		return KNOT_ENOMEM;
+static int parse_server(const char *value, dig_params_t *params)
+{
+	query_t *query;
+
+	// Set current query (last or config).
+	if (list_size(&params->queries) > 0) {
+		query = TAIL(params->queries);
+	} else {
+		query = params->config;
 	}
-	add_tail(&DIG_PARAM(params)->queries, (node *)query);
 
-	free(reverse);
+	int ret = params_parse_server(value, &query->servers, query->port);
 
-	return KNOT_EOK;
+	if (ret != KNOT_EOK) {
+		ERR("invalid nameserver: %s\n", value);
+	}
+
+	return ret;
 }
 
-static void set_default_query(list *queries)
+static int parse_opt1(const char *opt, const char *value, dig_params_t *params,
+                      int *index)
 {
-	query_t	*query;
+	const char *val = value;
+	size_t     len = strlen(opt);
+	int        add = 1;
+	query_t    *query;
+
+	// Set current query (last or config).
+	if (list_size(&params->queries) > 0) {
+		query = TAIL(params->queries);
+	} else {
+		query = params->config;
+	}
 
-	// Add default query: NS to ".".
-	query = query_create(".", KNOT_RRTYPE_NS, KNOT_CLASS_IN);
-	if (query == NULL) {
-		return;
+	// If there is no space between option and argument.
+	if (len > 1) {
+		val = opt + 1;
+		add = 0;
 	}
-	add_tail(queries, (node *)query);
+
+	switch (opt[0]) {
+	case '4':
+		if (len > 1) {
+			return KNOT_ENOTSUP;
+		}
+
+		query->ip = IP_4;
+		break;
+	case '6':
+		if (len > 1) {
+			return KNOT_ENOTSUP;
+		}
+
+		query->ip = IP_6;
+		break;
+	case 'c':
+		if (val == NULL) {
+			ERR("missing class\n");
+			return KNOT_EINVAL;
+		}
+
+		if (parse_class(val, query) != KNOT_EOK) {
+			ERR("invalid class: %s\n", val);
+			return KNOT_EINVAL;
+		}
+		*index += add;
+		break;
+	case 'h':
+		if (len > 1) {
+			return KNOT_ENOTSUP;
+		}
+
+		dig_help(true);
+		return KNOT_ESTOP;;
+	case 'p':
+		if (val == NULL) {
+			ERR("missing port\n");
+			return KNOT_EINVAL;
+		}
+
+		if (params_parse_port(val, &query->port)
+		    != KNOT_EOK) {
+			ERR("invalid port: %s\n", val);
+			return KNOT_EINVAL;
+		}
+		*index += add;
+		break;
+	case 'q':
+		if (val == NULL) {
+			ERR("missing name\n");
+			return KNOT_EINVAL;
+		}
+
+		if (parse_name(val, &params->queries, params->config)
+		    != KNOT_EOK) {
+			ERR("invalid name: %s\n", val);
+			return KNOT_EINVAL;
+		}
+		*index += add;
+		break;
+	case 't':
+		if (val == NULL) {
+			ERR("missing type\n");
+			return KNOT_EINVAL;
+		}
+
+		if (parse_type(val, query) != KNOT_EOK) {
+			ERR("invalid type: %s\n", val);
+			return KNOT_EINVAL;
+		}
+		*index += add;
+		break;
+	case 'x':
+		if (val == NULL) {
+			ERR("missing address\n");
+			return KNOT_EINVAL;
+		}
+
+		if (parse_reverse(val, &params->queries, params->config)
+		    != KNOT_EOK) {
+			ERR("invalid IPv4 or IPv6 address: %s\n", val);
+			return KNOT_EINVAL;
+		}
+		*index += add;
+		break;
+	default:
+		ERR("unknown option: -%s\n", opt);
+		return KNOT_ENOTSUP;
+	}
+
+	return KNOT_EOK;
 }
 
-static void dig_params_help(int argc, char *argv[])
+static int parse_opt2(const char *value, dig_params_t *params)
 {
-	printf("Usage: %s [-aCdlrsTvw] [-4] [-6] [-c class] [-R retries]\n"
-	       "       %*c [-t type] [-W time] name [server]\n",
-	       argv[0], (int)strlen(argv[0]), ' ');
+	query_t *query;
+
+	// Set current query (last or config).
+	if (list_size(&params->queries) > 0) {
+		query = TAIL(params->queries);
+	} else {
+		query = params->config;
+	}
+
+	// Check for format option.
+	if (strcmp(value, "multiline") == 0) {
+		query->style.format = FORMAT_MULTILINE;
+	} else if (strcmp(value, "nomultiline") == 0) {
+		query->style.format = FORMAT_VERBOSE;
+	}
+	else if (strcmp(value, "short") == 0) {
+		query->style.format = FORMAT_DIG;
+	} else if (strcmp(value, "noshort") == 0) {
+		query->style.format = FORMAT_VERBOSE;
+	}
+
+	// Check for flag option.
+	else if (strcmp(value, "aaflag") == 0) {
+		query->flags.aa_flag = true;
+	} else if (strcmp(value, "noaaflag") == 0) {
+		query->flags.aa_flag = false;
+	}
+	else if (strcmp(value, "tcflag") == 0) {
+		query->flags.tc_flag = true;
+	} else if (strcmp(value, "notcflag") == 0) {
+		query->flags.tc_flag = false;
+	}
+	else if (strcmp(value, "rdflag") == 0 ||
+	         strcmp(value, "recurse") == 0) {
+		query->flags.rd_flag = true;
+	} else if (strcmp(value, "nordflag") == 0 ||
+	           strcmp(value, "norecurse") == 0) {
+		query->flags.rd_flag = false;
+	}
+	else if (strcmp(value, "raflag") == 0) {
+		query->flags.ra_flag = true;
+	} else if (strcmp(value, "noraflag") == 0) {
+		query->flags.ra_flag = false;
+	}
+	else if (strcmp(value, "zflag") == 0) {
+		query->flags.z_flag = true;
+	} else if (strcmp(value, "nozflag") == 0) {
+		query->flags.z_flag = false;
+	}
+	else if (strcmp(value, "adflag") == 0) {
+		query->flags.ad_flag = true;
+	} else if (strcmp(value, "noadflag") == 0) {
+		query->flags.ad_flag = false;
+	}
+	else if (strcmp(value, "cdflag") == 0) {
+		query->flags.cd_flag = true;
+	} else if (strcmp(value, "nocdflag") == 0) {
+		query->flags.cd_flag = false;
+	}
+	else if (strcmp(value, "dnssec") == 0) {
+		query->flags.do_flag = true;
+	} else if (strcmp(value, "nodnssec") == 0) {
+		query->flags.do_flag = false;
+	}
+
+	// Check for display option.
+	else if (strcmp(value, "all") == 0) {
+		query->style.show_question = true;
+		query->style.show_answer = true;
+		query->style.show_authority = true;
+		query->style.show_additional = true;
+	} else if (strcmp(value, "noall") == 0) {
+		query->style.show_query = false;
+		query->style.show_question = false;
+		query->style.show_answer = false;
+		query->style.show_authority = false;
+		query->style.show_additional = false;
+	}
+	else if (strcmp(value, "qr") == 0) {
+		query->style.show_query = true;
+	} else if (strcmp(value, "noqr") == 0) {
+		query->style.show_query = false;
+	}
+	else if (strcmp(value, "question") == 0) {
+		query->style.show_question = true;
+	} else if (strcmp(value, "noquestion") == 0) {
+		query->style.show_question = false;
+	}
+	else if (strcmp(value, "answer") == 0) {
+		query->style.show_answer = true;
+	} else if (strcmp(value, "noanswer") == 0) {
+		query->style.show_answer = false;
+	}
+	else if (strcmp(value, "authority") == 0) {
+		query->style.show_authority = true;
+	} else if (strcmp(value, "noauthority") == 0) {
+		query->style.show_authority = false;
+	}
+	else if (strcmp(value, "additional") == 0) {
+		query->style.show_additional = true;
+	} else if (strcmp(value, "noadditional") == 0) {
+		query->style.show_additional = false;
+	}
+	else if (strcmp(value, "cl") == 0) {
+		query->style.show_class = true;
+	} else if (strcmp(value, "nocl") == 0) {
+		query->style.show_class = false;
+	}
+	else if (strcmp(value, "ttl") == 0) {
+		query->style.show_ttl = true;
+	} else if (strcmp(value, "nottl") == 0) {
+		query->style.show_ttl = false;
+	}
+
+	// Check for connection option.
+	else if (strcmp(value, "tcp") == 0) {
+		query->protocol = PROTO_TCP;
+	} else if (strcmp(value, "notcp") == 0) {
+		query->protocol = PROTO_UDP;
+	}
+	else if (strcmp(value, "fail") == 0) {
+		query->servfail_stop = true;
+	} else if (strcmp(value, "nofail") == 0) {
+		query->servfail_stop = false;
+	}
+
+	// Unknown option.
+	else {
+		ERR("invalid option: %s\n", value);
+	}
+
+	return KNOT_EOK;
 }
 
-void dig_params_flag_norecurse(params_t *params)
+static int parse_token(const char *value, dig_params_t *params)
 {
-	if (params == NULL) {
-		return;
+	query_t *query;
+
+	// Set current query (last or config).
+	if (list_size(&params->queries) > 0) {
+		query = TAIL(params->queries);
+	} else {
+		query = params->config;
+	}
+
+	// Try to guess the meaning of the token.
+	if (parse_type(value, query) == KNOT_EOK) {
+		return KNOT_EOK;
+	} else if (parse_class(value, query) == KNOT_EOK) {
+		return KNOT_EOK;
+	} else if (parse_name(value, &params->queries, params->config)
+	           == KNOT_EOK) {
+		return KNOT_EOK;
 	}
 
-	DIG_PARAM(params)->rd_flag = false;
+	ERR("invalid parameter: %s\n", value);
+
+	return KNOT_ERROR;
 }
 
-int dig_params_parse(params_t *params, int argc, char *argv[])
+int dig_parse(dig_params_t *params, int argc, char *argv[])
 {
-	int opt = 0;
-
 	if (params == NULL || argv == NULL) {
 		return KNOT_EINVAL;
 	}
 
-	if (dig_params_init(params) != KNOT_EOK) {
+	// Initialize parameters.
+	if (dig_init(params) != KNOT_EOK) {
 		return KNOT_ERROR;
 	}
 
-	dig_params_t *ext_params = DIG_PARAM(params);
+	// Command line parameters processing.
+	for (int i = 1; i < argc; i++) {
+		int ret = KNOT_ERROR;
 
-	// Command line options processing.
-	while ((opt = getopt(argc, argv, "46hc:p:q:t:x:")) != -1) {
-		switch (opt) {
-		case '4':
-			params_flag_ipv4(params);
-			break;
-		case '6':
-			params_flag_ipv6(params);
-			break;
-		case 'c':
-			if (params_parse_class(optarg, &params->class_num)
-			    != KNOT_EOK) {
-				return KNOT_EINVAL;
-			}
-			break;
-		case 'p':
-			if (params_parse_port(optarg, &params->port)
-			    != KNOT_EOK) {
-				return KNOT_EINVAL;
-			}
-			break;
-		case 'q':
-			if (dig_params_parse_name(optarg, params)
-			    != KNOT_EOK) {
-				return KNOT_EINVAL;
-			}
-			break;
-		case 't':
-			if (params_parse_type(optarg, &params->type_num,
-			                      &params->xfr_serial)
-			    != KNOT_EOK) {
-				return KNOT_EINVAL;
-			}
-			break;
-		case 'x':
-			if (dig_params_parse_reverse(optarg, params)
-			    != KNOT_EOK) {
-				return KNOT_EINVAL;
-			}
-			break;
-		case 'h':
-		default: // Fall through.
-			dig_params_help(argc, argv);
-			return KNOT_ENOTSUP;
-		}
-	}
-
-	// Process non-option parameters.
-	for (int i = optind; i < argc; i++) {
+		// Process parameter.
 		switch (argv[i][0]) {
 		case '@':
-			if (params_parse_server(argv[i] + 1, &params->servers,
-			                        params->port)
-			    != KNOT_EOK) {
-				ERR("invalid nameserver\n");
-				return KNOT_EINVAL;
-			}
+			ret = parse_server(argv[i] + 1, params);
+			break;
+		case '-':
+			ret = parse_opt1(argv[i] + 1, argv[i + 1], params, &i);
 			break;
 		case '+':
-
+			ret = parse_opt2(argv[i] + 1, params);
 			break;
 		default:
+			ret = parse_token(argv[i], params);
 			break;
 		}
-	}
 
-	// If server list is empty, try to read defaults.
-	if (list_size(&params->servers) == 0 &&
-	    get_nameservers(&params->servers, params->port) <= 0) {
-		WARN("can't read any default nameservers\n");
-	}
-
-	// If there is no query, add the default one.
-	if (list_size(&ext_params->queries) == 0) {
-	    set_default_query(&ext_params->queries);
+		// Check return.
+		switch (ret) {
+		case KNOT_EOK:
+			break;
+		case KNOT_ENOTSUP:
+			dig_help(false);
+		default: // Fall through.
+			return ret;
+		}
 	}
 
-	// After all parameters processing, complete missing data in queries.
-	complete_queries(params);
+	// Complete missing data in queries based on defaults.
+	complete_queries(&params->queries, params->config);
 
 	return KNOT_EOK;
 }
-
diff --git a/src/utils/dig/dig_params.h b/src/utils/dig/dig_params.h
index 25fe281966b17f703e777c4bde2a50432f619098..bd43a5efb1349d551a9a21b1c584ed25cdaf5cef 100644
--- a/src/utils/dig/dig_params.h
+++ b/src/utils/dig/dig_params.h
@@ -31,42 +31,88 @@
 
 #include "utils/common/params.h"	// params_t
 
-typedef struct {
+/*! \brief Operation mode of dig. */
+typedef enum {
+	/*!< Classic queries in list. */
+	OPERATION_QUERY,
+	/*!< Query for NS and all authoritative SOA records. */
+	OPERATION_LIST_SOA,
+} operation_t;
 
-} options_t;
+/*! \brief DNS header and EDNS flags. */
+typedef struct {
+	/*!< Authoritative answer flag. */
+	bool	aa_flag;
+	/*!< Truncated flag. */
+	bool	tc_flag;
+	/*!< Recursion desired flag. */
+	bool	rd_flag;
+	/*!< Recursion available flag. */
+	bool	ra_flag;
+	/*!< Z flag. */
+	bool	z_flag;
+	/*!< Authenticated data flag. */
+	bool	ad_flag;
+	/*!< Checking disabled flag. */
+	bool	cd_flag;
+	/*!< DNSSEC OK flag. */
+	bool	do_flag;
+} flags_t;
 
-/*! \brief Structure containing basic parameters for DNS query. */
+/*! \brief Basic parameters for DNS query. */
 typedef struct {
 	/*!< List node (for list container). */
 	node		n;
 	/*!< Name to query on. */
-	char		*qname;
+	char		*owner;
+	/*!< List of nameservers to query to. */
+	list		servers;
+	/*!< Operation mode. */
+	operation_t	operation;
+	/*!< Version of ip protocol to use. */
+	ip_t		ip;
+	/*!< Protocol type (TCP, UDP) to use. */
+	protocol_t	protocol;
+	/*!< Port/service to connect to. */
+	char		*port;
+	/*!< UDP buffer size (16unsigned + -1 uninitialized). */
+	int32_t		udp_size;
+	/*!< Number of UDP retries. */
+	uint32_t	retries;
+	/*!< Wait for network response in seconds (-1 means forever). */
+	int32_t		wait;
+	/*!< Stop quering if servfail. */
+	bool		servfail_stop;
 	/*!< Class number (16unsigned + -1 uninitialized). */
-	int32_t		qclass;
+	int32_t		class_num;
 	/*!< Type number (16unsigned + -1 uninitialized). */
-	int32_t		qtype;
+	int32_t		type_num;
 	/*!< SOA serial for XFR. */
 	uint32_t	xfr_serial;
+	/*!< Header flags. */
+	flags_t		flags;
+	/*!< Output settings. */
+	style_t		style;
 } query_t;
 
-/*! \brief dig-specific params data. */
+/*! \brief Settings for dig. */
 typedef struct {
 	/*!< List of DNS queries to process. */
-	list		queries;
-	/*!< Recursion desiredflag. */
-	bool		rd_flag;
+	list	queries;
+	/*!< Default settings for queries. */
+	query_t	*config;
 } dig_params_t;
-#define DIG_PARAM(p) ((dig_params_t*)p->d)
 
-query_t* query_create(const char    *qname,
-                      const int32_t qtype,
-                      const int32_t qclass);
-void query_free(query_t *query);
+/*! \brief Default header flags. */ 
+extern const flags_t DEFAULT_FLAGS;
 
-int dig_params_parse(params_t *params, int argc, char *argv[]);
-void dig_params_clean(params_t *params);
+query_t* query_create(const char *owner, const query_t *config);
+void query_free(query_t *query);
+void complete_queries(list *queries, const query_t *conf);
 
-void dig_params_flag_norecurse(params_t *params);
+int dig_init(dig_params_t *params);
+int dig_parse(dig_params_t *params, int argc, char *argv[]);
+void dig_clean(dig_params_t *params);
 
 #endif // _DIG__DIG_PARAMS_H_
 
diff --git a/src/utils/host/host_main.c b/src/utils/host/host_main.c
index e135177223d71e5676c6be96971d0f0937eb4e2f..5485ef4eafb1d945b0c37d7035ff3b5ae859cf62 100644
--- a/src/utils/host/host_main.c
+++ b/src/utils/host/host_main.c
@@ -17,23 +17,23 @@
 #include <stdlib.h>			// EXIT_FAILURE
 
 #include "common/errcode.h"		// KNOT_EOK
-#include "utils/host/host_params.h"	// params_t
-#include "utils/host/host_exec.h"	// host_exec
+#include "utils/host/host_params.h"	// host_parse
+#include "utils/dig/dig_exec.h"		// dig_exec
 
 int main(int argc, char *argv[])
 {
 	int ret = EXIT_SUCCESS;
 
-	params_t params;
-	if (host_params_parse(&params, argc, argv) == KNOT_EOK) {
-		if (host_exec(&params) != KNOT_EOK) {
+	dig_params_t params;
+	if (host_parse(&params, argc, argv) == KNOT_EOK) {
+		if (dig_exec(&params) != KNOT_EOK) {
 			ret = EXIT_FAILURE;
 		}
 	} else {
 		ret = EXIT_FAILURE;
 	}
 
-	host_params_clean(&params);
+	host_clean(&params);
 	return ret;
 }
 
diff --git a/src/utils/host/host_params.c b/src/utils/host/host_params.c
index 6be881e0f1003ed431f229da6b6cd63c31cd1a4a..1971a459d09e4bb0f78bf1a559eb965ed7f1da21 100644
--- a/src/utils/host/host_params.c
+++ b/src/utils/host/host_params.c
@@ -31,103 +31,41 @@
 #define DEFAULT_RETRIES_HOST	1
 #define DEFAULT_TIMEOUT_HOST	1
 
-static int host_params_init(params_t *params)
+static int host_init(dig_params_t *params)
 {
-	memset(params, 0, sizeof(*params));
+	// Initialize params with dig defaults.
+	int ret = dig_init(params);
 
-	// Create dig specific data structure.
-	params->d = calloc(1, sizeof(dig_params_t));
-	if (!params->d) {
-		return KNOT_ENOMEM;
+	if (ret != KNOT_EOK) {
+		return ret;
 	}
-	dig_params_t *ext_params = DIG_PARAM(params);
 
-	// Initialize blank server list.
-	init_list(&params->servers);
-
-	// Default values.
-	params->operation = OPERATION_QUERY;
-	params->ip = IP_ALL;
-	params->protocol = PROTO_ALL;
-	params->port = strdup(DEFAULT_DNS_PORT);
-	params->udp_size = DEFAULT_UDP_SIZE;
-	params->class_num = KNOT_CLASS_IN;
-	params->type_num = -1;
-	params->xfr_serial = 0;
-	params->retries = DEFAULT_RETRIES_HOST;
-	params->wait = DEFAULT_TIMEOUT_HOST;
-	params->servfail_stop = false;
-	params->format = FORMAT_HOST;
-
-	// Initialize list of queries.
-	init_list(&ext_params->queries);
-
-	// Extended params.
-	ext_params->rd_flag = true;
+	// Set host specific defaults.
+	params->config->retries = DEFAULT_RETRIES_HOST;
+	params->config->wait = DEFAULT_TIMEOUT_HOST;
+	params->config->class_num = KNOT_CLASS_IN;
+	params->config->style.format = FORMAT_HOST;
 
 	return KNOT_EOK;
 }
 
-void host_params_clean(params_t *params)
-{
-	node *n = NULL, *nxt = NULL;
-
-	if (params == NULL) {
-		return;
-	}
-
-	dig_params_t *ext_params = DIG_PARAM(params);
-
-	// Clean up server list.
-	WALK_LIST_DELSAFE(n, nxt, params->servers) {
-		server_free((server_t *)n);
-	}
-
-	// Clean up query list.
-	WALK_LIST_DELSAFE(n, nxt, ext_params->queries) {
-		query_free((query_t *)n);
-	}
-
-	free(params->port);
-
-	// Destroy dig specific structure.
-	free(ext_params);
-
-	// Clean up the structure.
-	memset(params, 0, sizeof(*params));
-}
-
-static void host_params_flag_all(params_t *params)
+void host_clean(dig_params_t *params)
 {
-	params->type_num = KNOT_RRTYPE_ANY;
-	params->format = FORMAT_VERBOSE;
+	dig_clean(params);
 }
 
-static void host_params_flag_soa(params_t *params)
+static int parse_name(const char *value, list *queries, const query_t *conf)
 {
-	params->type_num = KNOT_RRTYPE_SOA;
-	params->operation = OPERATION_LIST_SOA;
-}
-
-static void host_params_flag_axfr(params_t *params)
-{
-	params->type_num = KNOT_RRTYPE_AXFR;
-}
-
-static int host_params_parse_name(const char *name, params_t *params)
-{
-	char	*reverse = get_reverse_name(name);
+	char	*reverse = get_reverse_name(value);
 	char	*fqd_name = NULL;
 	query_t	*query;
 
-	dig_params_t *ext_params = DIG_PARAM(params);
-
 	// If name is not FQDN, append trailing dot.
-	fqd_name = get_fqd_name(name);
+	fqd_name = get_fqd_name(value);
 
 	// RR type is known.
-	if (params->type_num >= 0) {
-		if (params->type_num == KNOT_RRTYPE_PTR) {
+	if (conf->type_num >= 0) {
+		if (conf->type_num == KNOT_RRTYPE_PTR) {
 			// Check for correct address.
 			if (reverse == NULL) {
 				ERR("invalid IPv4 or IPv6 address\n");
@@ -136,68 +74,62 @@ static int host_params_parse_name(const char *name, params_t *params)
 			}
 
 			// Add reverse query for address.
-			query = query_create(reverse, params->type_num,
-			                     params->class_num);
+			query = query_create(reverse, conf);
 			if (query == NULL) {
 				free(reverse);
 				free(fqd_name);
 				return KNOT_ENOMEM;
 			}
-			add_tail(&ext_params->queries, (node *)query);
+			add_tail(queries, (node *)query);
 		} else {
 			// Add query for name and specified type.
-			query = query_create(fqd_name, params->type_num,
-			                     params->class_num);
+			query = query_create(fqd_name, conf);
 			if (query == NULL) {
 				free(reverse);
 				free(fqd_name);
 				return KNOT_ENOMEM;
 			}
-			// Set SOA serial for IXFR query.
-			if (params->type_num == KNOT_RRTYPE_IXFR) {
-				query->xfr_serial = params->xfr_serial;
-			}
-			add_tail(&ext_params->queries, (node *)query);
+			add_tail(queries, (node *)query);
 		}
 	// RR type is unknown, use defaults.
 	} else {
 		if (reverse == NULL) {
 			// Add query for name and type A.
-			query = query_create(fqd_name, KNOT_RRTYPE_A,
-			                     params->class_num);
+			query = query_create(fqd_name, conf);
 			if (query == NULL) {
 				free(fqd_name);
 				return KNOT_ENOMEM;
 			}
-			add_tail(&ext_params->queries, (node *)query);
+			query->type_num = KNOT_RRTYPE_A;
+			add_tail(queries, (node *)query);
 
 			// Add query for name and type AAAA.
-			query = query_create(fqd_name, KNOT_RRTYPE_AAAA,
-			                     params->class_num);
+			query = query_create(fqd_name, conf);
 			if (query == NULL) {
 				free(fqd_name);
 				return KNOT_ENOMEM;
 			}
-			add_tail(&ext_params->queries, (node *)query);
+			query->type_num = KNOT_RRTYPE_AAAA;
+			add_tail(queries, (node *)query);
 
 			// Add query for name and type MX.
-			query = query_create(fqd_name, KNOT_RRTYPE_MX,
-			                     params->class_num);
+			query = query_create(fqd_name, conf);
 			if (query == NULL) {
 				free(fqd_name);
 				return KNOT_ENOMEM;
 			}
-			add_tail(&ext_params->queries, (node *)query);
+			query->type_num = KNOT_RRTYPE_MX;
+			add_tail(queries, (node *)query);
 		} else {
 			// Add reverse query for address.
-			query = query_create(reverse, KNOT_RRTYPE_PTR,
-			                     params->class_num);
+			query = query_create(fqd_name, conf);
 			if (query == NULL) {
 				free(reverse);
 				free(fqd_name);
 				return KNOT_ENOMEM;
 			}
-			add_tail(&ext_params->queries, (node *)query);
+			query->type_num = KNOT_RRTYPE_PTR;
+			add_tail(queries, (node *)query);
 		}
 	}
 
@@ -207,14 +139,14 @@ static int host_params_parse_name(const char *name, params_t *params)
 	return KNOT_EOK;
 }
 
-static void host_params_help(int argc, char *argv[])
+static void host_help(int argc, char *argv[])
 {
 	printf("Usage: %s [-aCdlrsTvw] [-4] [-6] [-c class] [-R retries]\n"
 	       "       %*c [-t type] [-W time] name [server]\n",
 	       argv[0], (int)strlen(argv[0]), ' ');
 }
 
-int host_params_parse(params_t *params, int argc, char *argv[])
+int host_parse(dig_params_t *params, int argc, char *argv[])
 {
 	int opt = 0;
 
@@ -222,71 +154,79 @@ int host_params_parse(params_t *params, int argc, char *argv[])
 		return KNOT_EINVAL;
 	}
 
-	if (host_params_init(params) != KNOT_EOK) {
+	if (host_init(params) != KNOT_EOK) {
 		return KNOT_ERROR;
 	}
 
+	query_t  *conf = params->config;
+	uint16_t rclass, rtype;
+	uint32_t serial;
+
 	// Command line options processing.
 	while ((opt = getopt(argc, argv, "46aCdlrsTvwc:R:t:W:")) != -1) {
 		switch (opt) {
 		case '4':
-			params_flag_ipv4(params);
+			conf->ip = IP_4;
 			break;
 		case '6':
-			params_flag_ipv6(params);
+			conf->ip = IP_6;
 			break;
 		case 'a':
-			host_params_flag_all(params);
+			conf->type_num = KNOT_RRTYPE_ANY;
+			conf->style.format = FORMAT_VERBOSE;
 			break;
 		case 'C':
-			host_params_flag_soa(params);
+			conf->type_num = KNOT_RRTYPE_SOA;
+			conf->operation = OPERATION_LIST_SOA;
 			break;
 		case 'd':
 		case 'v': // Fall through.
-			params_flag_verbose(params);
+			conf->style.format = FORMAT_VERBOSE;
 			break;
 		case 'l':
-			host_params_flag_axfr(params);
+			conf->type_num = KNOT_RRTYPE_AXFR;
 			break;
 		case 'r':
-			dig_params_flag_norecurse(params);
+			conf->flags.rd_flag = false;
 			break;
 		case 's':
-			params_flag_servfail(params);
+			conf->servfail_stop = true;
 			break;
 		case 'T':
-			params_flag_tcp(params);
+			conf->protocol = PROTO_TCP;
 			break;
 		case 'w':
-			params_flag_nowait(params);
+			conf->wait = -1;
 			break;
 		case 'c':
-			if (params_parse_class(optarg, &params->class_num)
+			if (params_parse_class(optarg, &rclass)
 			    != KNOT_EOK) {
 				return KNOT_EINVAL;
 			}
+			conf->class_num = rclass;
 			break;
 		case 'R':
-			if (params_parse_num(optarg, &params->retries)
+			if (params_parse_num(optarg, &conf->retries)
 			    != KNOT_EOK) {
 				return KNOT_EINVAL;
 			}
 			break;
 		case 't':
-			if (params_parse_type(optarg, &params->type_num,
-			                      &params->xfr_serial)
+			if (params_parse_type(optarg, &rtype, &serial)
 			    != KNOT_EOK) {
 				return KNOT_EINVAL;
 			}
+			conf->type_num = rtype;
+			conf->xfr_serial = serial;
 			break;
 		case 'W':
-			if (params_parse_interval(optarg, &params->wait)
+			if (params_parse_interval(optarg, &conf->wait)
 			    != KNOT_EOK) {
 				return KNOT_EINVAL;
 			}
 			break;
 		default:
-			host_params_help(argc, argv);
+			host_help(argc, argv);
 			return KNOT_ENOTSUP;
 		}
 	}
@@ -294,28 +234,25 @@ int host_params_parse(params_t *params, int argc, char *argv[])
 	// Process non-option parameters.
 	switch (argc - optind) {
 	case 2:
-		if (params_parse_server(argv[optind + 1], &params->servers,
-		                        params->port)
+		if (params_parse_server(argv[optind + 1], &conf->servers,
+		                        conf->port)
 		    != KNOT_EOK) {
 			ERR("invalid nameserver\n");
 			return KNOT_EINVAL;
 		}
 	case 1: // Fall through.
-		if (host_params_parse_name(argv[optind], params)
+		if (parse_name(argv[optind], &params->queries, conf)
 		    != KNOT_EOK) {
 			return KNOT_EINVAL;
 		}
 		break;
 	default:
-		host_params_help(argc, argv);
+		host_help(argc, argv);
 		return KNOT_ENOTSUP;
 	}
 
-	// If server list is empty, try to read defaults.
-	if (list_size(&params->servers) == 0 &&
-	    get_nameservers(&params->servers, params->port) <= 0) {
-		WARN("can't read any default nameservers\n");
-	}
+	// Complete missing data in queries based on defaults.
+	complete_queries(&params->queries, params->config);
 
 	return KNOT_EOK;
 }
diff --git a/src/utils/host/host_params.h b/src/utils/host/host_params.h
index 7ab5835772b17eb851095dce4a90bb0eb19da5ca..675ecb17cf3c18c041a2c28439069eb403071145 100644
--- a/src/utils/host/host_params.h
+++ b/src/utils/host/host_params.h
@@ -27,10 +27,10 @@
 #ifndef _HOST__HOST_PARAMS_H_
 #define _HOST__HOST_PARAMS_H_
 
-#include "utils/common/params.h"	// params_t
+#include "utils/dig/dig_params.h"	// dig_params_t
 
-int host_params_parse(params_t *params, int argc, char *argv[]);
-void host_params_clean(params_t *params);
+int host_parse(dig_params_t *params, int argc, char *argv[]);
+void host_clean(dig_params_t *params);
 
 #endif // _HOST__HOST_PARAMS_H_
 
diff --git a/src/utils/nsupdate/nsupdate_exec.c b/src/utils/nsupdate/nsupdate_exec.c
index 4159f6516472a29d96ed48496dfb52ebb23f4c0d..19b6e76ddb94ca29a80b75c75a1e8e88ce89f8d7 100644
--- a/src/utils/nsupdate/nsupdate_exec.c
+++ b/src/utils/nsupdate/nsupdate_exec.c
@@ -39,24 +39,24 @@
 #include "libknot/tsig-op.h"
 
 /* Declarations of cmd parse functions. */
-typedef int (*cmd_handle_f)(const char *lp, params_t *params);
-int cmd_add(const char* lp, params_t *params);
-int cmd_answer(const char* lp, params_t *params);
-int cmd_class(const char* lp, params_t *params);
-int cmd_debug(const char* lp, params_t *params);
-int cmd_del(const char* lp, params_t *params);
-int cmd_gsstsig(const char* lp, params_t *params);
-int cmd_key(const char* lp, params_t *params);
-int cmd_local(const char* lp, params_t *params);
-int cmd_oldgsstsig(const char* lp, params_t *params);
-int cmd_prereq(const char* lp, params_t *params);
-int cmd_realm(const char* lp, params_t *params);
-int cmd_send(const char* lp, params_t *params);
-int cmd_server(const char* lp, params_t *params);
-int cmd_show(const char* lp, params_t *params);
-int cmd_ttl(const char* lp, params_t *params);
-int cmd_update(const char* lp, params_t *params);
-int cmd_zone(const char* lp, params_t *params);
+typedef int (*cmd_handle_f)(const char *lp, nsupdate_params_t *params);
+int cmd_add(const char* lp, nsupdate_params_t *params);
+int cmd_answer(const char* lp, nsupdate_params_t *params);
+int cmd_class(const char* lp, nsupdate_params_t *params);
+int cmd_debug(const char* lp, nsupdate_params_t *params);
+int cmd_del(const char* lp, nsupdate_params_t *params);
+int cmd_gsstsig(const char* lp, nsupdate_params_t *params);
+int cmd_key(const char* lp, nsupdate_params_t *params);
+int cmd_local(const char* lp, nsupdate_params_t *params);
+int cmd_oldgsstsig(const char* lp, nsupdate_params_t *params);
+int cmd_prereq(const char* lp, nsupdate_params_t *params);
+int cmd_realm(const char* lp, nsupdate_params_t *params);
+int cmd_send(const char* lp, nsupdate_params_t *params);
+int cmd_server(const char* lp, nsupdate_params_t *params);
+int cmd_show(const char* lp, nsupdate_params_t *params);
+int cmd_ttl(const char* lp, nsupdate_params_t *params);
+int cmd_update(const char* lp, nsupdate_params_t *params);
+int cmd_zone(const char* lp, nsupdate_params_t *params);
 
 /* Sorted list of commands.
  * This way we could identify command byte-per-byte and
@@ -212,8 +212,9 @@ static int parse_partial_rr(scanner_t *s, const char *lp, unsigned flags) {
 	len = strcspn(lp, SEP_CHARS); /* Try to find class */
 	memset(b1, 0, sizeof(b1));
 	strncpy(b1, lp, len < sizeof(b1) ? len : sizeof(b1));
-	uint16_t v = knot_rrclass_from_string(b1);
-	if (v > 0) {
+
+	uint16_t v;
+	if (knot_rrclass_from_string(b1, &v) == 0) {
 		s->r_class = v;
 		DBG("%s: parsed class=%u\n", __func__, s->r_class);
 		lp = tok_skipspace(lp + len);
@@ -231,8 +232,7 @@ static int parse_partial_rr(scanner_t *s, const char *lp, unsigned flags) {
 	len = strcspn(lp, SEP_CHARS); /* Type */
 	memset(b2, 0, sizeof(b2));
 	strncpy(b2, lp, len < sizeof(b2) ? len : sizeof(b2));
-	v = knot_rrtype_from_string(b2);
-	if (v > 0) {
+	if (knot_rrtype_from_string(b2, &v) == 0) {
 		s->r_type = v;
 		DBG("%s: parsed type=%u '%s'\n", __func__, s->r_type, b2);
 		lp = tok_skipspace(lp + len);
@@ -294,31 +294,29 @@ static server_t *parse_host(const char *lp, const char* default_port)
 	return srv;
 }
 
-static int pkt_append(params_t *p, int sect)
+static int pkt_append(nsupdate_params_t *p, int sect)
 {
 	/* Check packet state first. */
 	int ret = KNOT_EOK;
-	nsupdate_params_t *npar = NSUP_PARAM(p);
-	scanner_t *s = npar->rrp;
-	if (!npar->pkt) {
-		npar->pkt = create_empty_packet(KNOT_PACKET_PREALLOC_RESPONSE,
-		                                MAX_PACKET_SIZE);
+	scanner_t *s = p->rrp;
+	if (!p->pkt) {
+		p->pkt = create_empty_packet(KNOT_PACKET_PREALLOC_RESPONSE,
+		                             MAX_PACKET_SIZE);
 		knot_question_t q;
 		q.qclass = p->class_num;
 		q.qtype = p->type_num;
 		q.qname = knot_dname_new_from_wire(s->zone_origin,
 		                                   s->zone_origin_length,
 		                                   NULL);
-		ret = knot_query_set_question(npar->pkt, &q);
-		knot_dname_release(q.qname); /* Already on wire. */
+		ret = knot_query_set_question(p->pkt, &q);
 		if (ret != KNOT_EOK) {
 			return ret;
 		}
-		knot_query_set_opcode(npar->pkt, KNOT_OPCODE_UPDATE);
+		knot_query_set_opcode(p->pkt, KNOT_OPCODE_UPDATE);
 		
 		/* Reserve space for TSIG. */
 		if (p->key.name) {
-			knot_packet_set_tsig_size(npar->pkt,
+			knot_packet_set_tsig_size(p->pkt,
 			                          tsig_wire_maxsize(&p->key));
 		}
 	}
@@ -370,13 +368,13 @@ static int pkt_append(params_t *p, int sect)
 	switch(sect) {
 	case UP_ADD:
 	case UP_DEL:
-		ret = knot_response_add_rrset_authority(npar->pkt, rr, 0, 0, 0, 0);
+		ret = knot_response_add_rrset_authority(p->pkt, rr, 0, 0, 0, 0);
 		break;
 	case PQ_NXDOMAIN:
 	case PQ_NXRRSET:
 	case PQ_YXDOMAIN:
 	case PQ_YXRRSET:
-		ret = knot_response_add_rrset_answer(npar->pkt, rr, 0, 0, 0, 0);
+		ret = knot_response_add_rrset_answer(p->pkt, rr, 0, 0, 0, 0);
 		break;
 	default:
 		assert(0); /* Should never happen. */
@@ -388,34 +386,51 @@ static int pkt_append(params_t *p, int sect)
 		    __func__, knot_strerror(ret));
 		if (ret == KNOT_ESPACE) {
 			ERR("exceeded UPDATE message maximum size %zu\n",
-			    knot_packet_max_size(npar->pkt));
+			    knot_packet_max_size(p->pkt));
 		}
 	}
 	
 	return ret;
 }
 
-static int pkt_sendrecv(params_t *params, server_t *srv,
+static int pkt_sendrecv(nsupdate_params_t *params,
                         uint8_t *qwire, size_t qlen,
                         uint8_t *rwire, size_t rlen)
 {
-	/*! \todo Bind to local if specified by params. */
-	
-	int sock = send_msg(params, KNOT_RRTYPE_SOA, srv, qwire, qlen);
-	DBG("%s: send_msg = %d\n", __func__, sock);
-	if (sock < 0) return sock;
-	
+	net_t net;
+	int   ret;
+
+	ret = net_connect(params->srcif,
+	                  params->server,
+	                  get_iptype(params->ip),
+	                  get_socktype(params->protocol, KNOT_RRTYPE_SOA),
+	                  params->wait,
+	                  &net);
+	DBG("%s: send_msg = %d\n", __func__, net.sockfd);
+	if (ret != KNOT_EOK) return -1;
+
+	ret = net_send(&net, qwire, qlen);
+	if (ret != KNOT_EOK) {
+		net_close(&net);
+		return -1;
+	}
+
 	/* Wait for reception. */
-	int rb = receive_msg(params, KNOT_RRTYPE_SOA, sock, rwire, rlen);
+	int rb = net_receive(&net, rwire, rlen); 
 	DBG("%s: receive_msg = %d\n", __func__, rb);
-	close(sock);
+	if (rb <= 0) {
+		net_close(&net);
+		return -1;
+	}
 	
+	net_close(&net);
+
 	return rb;
 }
 
 static int nsupdate_process_line(char *lp, int len, void *arg)
 {
-	params_t *params = (params_t *)arg;
+	nsupdate_params_t *params = (nsupdate_params_t *)arg;
 	
 	if (lp[len - 1] == '\n') lp[len - 1] = '\0'; /* Discard nline */
 	int ret = tok_find(lp, cmd_array);
@@ -432,40 +447,38 @@ static int nsupdate_process_line(char *lp, int len, void *arg)
 	return ret;
 }
 
-static int nsupdate_process(params_t *params, FILE *fp)
+static int nsupdate_process(nsupdate_params_t *params, FILE *fp)
 {
 	/* Process lines. */
 	int ret = tok_process_lines(fp, nsupdate_process_line, params);
 	
 	/* Check for longing query. */
-	nsupdate_params_t *npar = NSUP_PARAM(params);
-	if (npar->pkt && ret == KNOT_EOK) {
+	if (params->pkt && ret == KNOT_EOK) {
 		cmd_send("", params);
 	}
-	
+
 	/* Free last answer. */
-	knot_packet_free(&npar->resp);
+	knot_packet_free(&params->resp);
 
 	return ret;
 }
 
-int nsupdate_exec(params_t *params)
+int nsupdate_exec(nsupdate_params_t *params)
 {
 	if (!params) {
 		return KNOT_EINVAL;
 	}
 	
 	int ret = KNOT_EOK;
-	nsupdate_params_t* npar = NSUP_PARAM(params);
 
 	/* If not file specified, use stdin. */
-	if (EMPTY_LIST(npar->qfiles)) {
+	if (EMPTY_LIST(params->qfiles)) {
 		return nsupdate_process(params, stdin);
 	}
 
 	/* Read from each specified file. */
 	strnode_t *n = NULL;
-	WALK_LIST(n, npar->qfiles) {
+	WALK_LIST(n, params->qfiles) {
 		if (strcmp(n->str, "-") == 0) {
 			ret = nsupdate_process(params, stdin);
 			continue;
@@ -483,7 +496,7 @@ int nsupdate_exec(params_t *params)
 	return ret;
 }
 
-int cmd_update(const char* lp, params_t *params)
+int cmd_update(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
@@ -503,11 +516,11 @@ int cmd_update(const char* lp, params_t *params)
 }
 
 
-int cmd_add(const char* lp, params_t *params)
+int cmd_add(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
-	scanner_t *rrp = NSUP_PARAM(params)->rrp;
+	scanner_t *rrp = params->rrp;
 	if (parse_full_rr(rrp, lp) != KNOT_EOK) {
 		return KNOT_EPARSEFAIL;
 	}
@@ -519,11 +532,11 @@ int cmd_add(const char* lp, params_t *params)
 	return pkt_append(params, UP_ADD); /* Append to packet. */
 }
 
-int cmd_del(const char* lp, params_t *params)
+int cmd_del(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
-	scanner_t *rrp = NSUP_PARAM(params)->rrp;
+	scanner_t *rrp = params->rrp;
 	if (parse_partial_rr(rrp, lp, PARSE_NODEFAULT) != KNOT_EOK) {
 		return KNOT_EPARSEFAIL;
 	}
@@ -550,45 +563,46 @@ int cmd_del(const char* lp, params_t *params)
 	return pkt_append(params, UP_DEL); /* Append to packet. */
 }
 
-int cmd_class(const char* lp, params_t *params)
+int cmd_class(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
-	params->class_num = knot_rrclass_from_string(lp);
-	if (params->class_num == 0) {
+	uint16_t cls;
+
+	if (knot_rrclass_from_string(lp, &cls) != 0) {
 		ERR("failed to parse class '%s'\n", lp);
 		return KNOT_EPARSEFAIL;
 	} else {
-		scanner_t *s = NSUP_PARAM(params)->rrp;
+		params->class_num = cls;
+		scanner_t *s = params->rrp;
 		s->default_class = params->class_num;
 	}
 	
 	return KNOT_EOK;
 }
 
-int cmd_ttl(const char* lp, params_t *params)
+int cmd_ttl(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
 	uint32_t ttl = 0;
 	params_parse_num(lp, &ttl);
-	nsupdate_params_set_ttl(params, ttl);
+	nsupdate_set_ttl(params, ttl);
 	
 	return KNOT_EOK;
 }
 
-int cmd_debug(const char* lp, params_t *params)
+int cmd_debug(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
-	params_flag_verbose(params);
 	msg_enable_debug(1);
 	return KNOT_EOK;
 }
 
-int cmd_prereq_domain(const char *lp, params_t *params, unsigned type)
+int cmd_prereq_domain(const char *lp, nsupdate_params_t *params, unsigned type)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
-	scanner_t *s = NSUP_PARAM(params)->rrp;
+	scanner_t *s = params->rrp;
 	int ret = parse_partial_rr(s, lp, PARSE_NODEFAULT|PARSE_NAMEONLY);
 	if (ret != KNOT_EOK) {
 		return ret;
@@ -597,11 +611,11 @@ int cmd_prereq_domain(const char *lp, params_t *params, unsigned type)
 	return ret;
 }
 
-int cmd_prereq_rrset(const char *lp, params_t *params, unsigned type)
+int cmd_prereq_rrset(const char *lp, nsupdate_params_t *params, unsigned type)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
-	scanner_t *rrp = NSUP_PARAM(params)->rrp;
+	scanner_t *rrp = params->rrp;
 	if (parse_partial_rr(rrp, lp, 0) != KNOT_EOK) {
 		return KNOT_EPARSEFAIL;
 	}
@@ -619,7 +633,7 @@ int cmd_prereq_rrset(const char *lp, params_t *params, unsigned type)
 	return KNOT_EOK;
 }
 
-int cmd_prereq(const char* lp, params_t *params)
+int cmd_prereq(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
@@ -646,7 +660,7 @@ int cmd_prereq(const char* lp, params_t *params)
 	
 	/* Append to packet. */
 	if (ret == KNOT_EOK) {
-		scanner_t *s = NSUP_PARAM(params)->rrp;
+		scanner_t *s = params->rrp;
 		s->r_ttl = 0; /* Set TTL = 0 for prereq. */
 		/* YX{RRSET,DOMAIN} - cls ANY */
 		if (bp == PQ_YXRRSET || bp == PQ_YXDOMAIN) {
@@ -661,7 +675,7 @@ int cmd_prereq(const char* lp, params_t *params)
 	return ret;
 }
 
-int cmd_send(const char* lp, params_t *params)
+int cmd_send(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	DBG("sending packet\n");
@@ -670,8 +684,8 @@ int cmd_send(const char* lp, params_t *params)
 	int ret = KNOT_EOK;
 	uint8_t *wire = NULL;
 	size_t len = 0;
-	nsupdate_params_t *npar = NSUP_PARAM(params);
-	if ((ret = knot_packet_to_wire(npar->pkt, &wire, &len))!= KNOT_EOK) {
+
+	if ((ret = knot_packet_to_wire(params->pkt, &wire, &len))!= KNOT_EOK) {
 		ERR("couldn't serialize packet, %s\n", knot_strerror(ret));
 		return ret;
 	}
@@ -679,7 +693,7 @@ int cmd_send(const char* lp, params_t *params)
 	/* Sign if possible. */
 	size_t dlen = 0;
 	uint8_t *digest = NULL;
-	size_t maxlen = knot_packet_max_size(npar->pkt);
+	size_t maxlen = knot_packet_max_size(params->pkt);
 	if (params->key.name) {
 		dlen = tsig_alg_digest_length(params->key.algorithm);
 		digest = malloc(dlen);
@@ -693,8 +707,6 @@ int cmd_send(const char* lp, params_t *params)
 		}
 	}
 	
-	if (EMPTY_LIST(params->servers)) return KNOT_EINVAL;
-	server_t *srv = TAIL(params->servers);
 	
 	/* Send/recv message (N retries). */
 	int retries = params->retries;
@@ -703,28 +715,30 @@ int cmd_send(const char* lp, params_t *params)
 	}
 	int rb = 0;
 	for (; retries > 0; --retries) {
-		memset(npar->rwire, 0, MAX_PACKET_SIZE);
-		rb = pkt_sendrecv(params, srv, wire, len,
-		                  npar->rwire, MAX_PACKET_SIZE);
+		memset(params->rwire, 0, MAX_PACKET_SIZE);
+		rb = pkt_sendrecv(params, wire, len,
+		                  params->rwire, MAX_PACKET_SIZE);
 		if (rb > 0) break;
 	}
 	
 	/* Clear previous response. */
-	if (npar->resp) knot_packet_free(&npar->resp);
+	if (params->resp) knot_packet_free(&params->resp);
 	if (rb <= 0) {
 		free(digest);
 		return KNOT_ECONNREFUSED;
 	}
 	
 	/* Clear sent packet and parse response. */
-	knot_packet_free_rrsets(npar->pkt);
-	knot_packet_free(&npar->pkt);
-	npar->resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
-	if (!npar->resp) {
+	knot_question_t *q = knot_packet_question(params->pkt);
+	knot_dname_release(q->qname);
+	knot_packet_free_rrsets(params->pkt);
+	knot_packet_free(&params->pkt);
+	params->resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+	if (!params->resp) {
 		free(digest);
 		return KNOT_ENOMEM;
 	}
-	ret = knot_packet_parse_from_wire(npar->resp, npar->rwire, rb, 0, 0);
+	ret = knot_packet_parse_from_wire(params->resp, params->rwire, rb, 0, 0);
 	if (ret != KNOT_EOK) {
 		ERR("failed to parse response, %s\n", knot_strerror(ret));
 		free(digest);
@@ -733,11 +747,11 @@ int cmd_send(const char* lp, params_t *params)
 	
 	/* Check TSIG if required. */
 	const char *ep = "; TSIG error with server";
-	const knot_rrset_t *tsig_rr = knot_packet_tsig(npar->resp);
+	const knot_rrset_t *tsig_rr = knot_packet_tsig(params->resp);
 	if (digest && !tsig_rr) {
 		ret = KNOT_ENOTSIG;
 	} else if (digest) {
-		ret = knot_tsig_client_check(tsig_rr, npar->rwire, rb,
+		ret = knot_tsig_client_check(tsig_rr, params->rwire, rb,
 		                             digest, dlen, &params->key,
 		                             0);
 	}
@@ -749,7 +763,7 @@ int cmd_send(const char* lp, params_t *params)
 	
 	/* Check return code. */
 	knot_lookup_table_t *rcode;
-	int rc = knot_packet_rcode(npar->resp);
+	int rc = knot_packet_rcode(params->resp);
 	DBG("%s: received rcode=%d\n", __func__, rc);
 	rcode = knot_lookup_by_id(rcodes, rc);
 	ERR("update failed: %s\n", rcode->name);
@@ -759,7 +773,7 @@ int cmd_send(const char* lp, params_t *params)
 	return KNOT_EOK;
 }
 
-int cmd_zone(const char* lp, params_t *params)
+int cmd_zone(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
@@ -772,12 +786,12 @@ int cmd_zone(const char* lp, params_t *params)
 	
 	/* Extract name. */
 	char *zone = strndup(lp, len);
-	nsupdate_params_set_origin(params, zone);
+	nsupdate_set_origin(params, zone);
 	free(zone);
 	return KNOT_EOK;
 }
 
-int cmd_server(const char* lp, params_t *params)
+int cmd_server(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
@@ -787,48 +801,51 @@ int cmd_server(const char* lp, params_t *params)
 	/* Enqueue. */
 	if (!srv) return KNOT_ENOMEM;
 	
-	add_tail(&params->servers, (node *)srv);
+	server_free(params->server);
+	params->server = srv;
+
 	return KNOT_EOK;
 }
 
-int cmd_local(const char* lp, params_t *params)
+int cmd_local(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	
 	/* Parse host. */
-	nsupdate_params_t *npar = NSUP_PARAM(params);
-	if ((npar->srcif = parse_host(lp, "0")) == NULL) {
-		return KNOT_ENOMEM;
-	}
-	
+	server_t *srv = parse_host(lp, "0");
+
+	/* Enqueue. */
+	if (!srv) return KNOT_ENOMEM;
+
+	server_free(params->srcif);
+	params->srcif = srv;
+
 	return KNOT_EOK;
 }
 
-int cmd_show(const char* lp, params_t *params)
+int cmd_show(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
-	nsupdate_params_t *npar = NSUP_PARAM(params);
 	/* Show current packet. */
-	if (!npar->pkt) return KNOT_EOK;
+	if (!params->pkt) return KNOT_EOK;
 	printf("Outgoing update query:\n");
-	size_t len = knot_packet_size(npar->pkt);
-	print_packet(params->format, npar->pkt, len, 0, 0.0f, 1);
+	size_t len = knot_packet_size(params->pkt);
+	print_packet(NULL, &params->style, params->pkt, 0.0f, len, 1);
 	return KNOT_EOK;
 }
 
-int cmd_answer(const char* lp, params_t *params)
+int cmd_answer(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
-	nsupdate_params_t *npar = NSUP_PARAM(params);
 	/* Show current answer. */
-	if (!npar->resp) return KNOT_EOK;
+	if (!params->resp) return KNOT_EOK;
 	printf("Answer:\n");
-	size_t len = knot_packet_size(npar->resp);
-	print_packet(params->format, npar->resp, len, 0, 0.0f, 1);
+	size_t len = knot_packet_size(params->resp);
+	print_packet(NULL, &params->style, params->resp, 0.0f, len, 1);
 	return KNOT_EOK;
 }
 
-int cmd_key(const char* lp, params_t *params)
+int cmd_key(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	char *kstr = strdup(lp); /* Convert to default format. */
@@ -853,19 +870,19 @@ int cmd_key(const char* lp, params_t *params)
  *   Not implemented.
  */
 
-int cmd_gsstsig(const char* lp, params_t *params)
+int cmd_gsstsig(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	return KNOT_ENOTSUP;
 }
 
-int cmd_oldgsstsig(const char* lp, params_t *params)
+int cmd_oldgsstsig(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	return KNOT_ENOTSUP;
 }
 
-int cmd_realm(const char* lp, params_t *params)
+int cmd_realm(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
 	return KNOT_ENOTSUP;
diff --git a/src/utils/nsupdate/nsupdate_exec.h b/src/utils/nsupdate/nsupdate_exec.h
index aabc0eb210a7a5287d1b47d1b8390e921bc3f2fd..7230a4e7a4946c0c98d0afca9cf34b2a7bf3162c 100644
--- a/src/utils/nsupdate/nsupdate_exec.h
+++ b/src/utils/nsupdate/nsupdate_exec.h
@@ -27,9 +27,9 @@
 #ifndef _NSUPDATE__NSUPDATE_EXEC_H_
 #define _NSUPDATE__NSUPDATE_EXEC_H_
 
-#include "utils/nsupdate/nsupdate_params.h"
+#include "utils/nsupdate/nsupdate_params.h"	// nsupdate_params_t
 
-int nsupdate_exec(params_t *params);
+int nsupdate_exec(nsupdate_params_t *params);
 
 #endif // _NSUPDATE__NSUPDATE_EXEC_H_
 
diff --git a/src/utils/nsupdate/nsupdate_main.c b/src/utils/nsupdate/nsupdate_main.c
index f075273c5a6cacc54dbb4554e312bb7155052524..c96db657038aaa8e673c5e7c919214f42cb46cef 100644
--- a/src/utils/nsupdate/nsupdate_main.c
+++ b/src/utils/nsupdate/nsupdate_main.c
@@ -24,8 +24,8 @@ int main(int argc, char *argv[])
 {
 	int ret = EXIT_SUCCESS;
 	
-	params_t params;
-	if (nsupdate_params_parse(&params, argc, argv) == KNOT_EOK) {
+	nsupdate_params_t params;
+	if (nsupdate_parse(&params, argc, argv) == KNOT_EOK) {
 		if (nsupdate_exec(&params) != KNOT_EOK) {
 			ret = EXIT_FAILURE;
 		}
@@ -33,7 +33,7 @@ int main(int argc, char *argv[])
 		ret = EXIT_FAILURE;
 	}
 
-	nsupdate_params_clean(&params);
+	nsupdate_clean(&params);
 	return ret;
 }
 
diff --git a/src/utils/nsupdate/nsupdate_params.c b/src/utils/nsupdate/nsupdate_params.c
index f0e57686bd9a31ef78ee985e62c6f87b3ce9b3db..ce34a0d824d9d3c2d64c450ec715b9158ee27217 100644
--- a/src/utils/nsupdate/nsupdate_params.c
+++ b/src/utils/nsupdate/nsupdate_params.c
@@ -23,9 +23,9 @@
 #include "utils/nsupdate/nsupdate_params.h"
 #include "utils/common/msg.h"
 #include "utils/common/netio.h"
-#include "libknot/util/descriptor.h"
 #include "common/errcode.h"
 #include "libknot/packet/packet.h"
+#include "libknot/util/descriptor.h"
 
 #define DEFAULT_RETRIES_NSUPDATE	3
 #define DEFAULT_TIMEOUT_NSUPDATE	1
@@ -59,97 +59,77 @@ static int parser_set_default(scanner_t *s, const char *fmt, ...)
 	return KNOT_EOK;
 }
 
-static int nsupdate_params_init(params_t *params)
+static int nsupdate_init(nsupdate_params_t *params)
 {
 	memset(params, 0, sizeof(*params));
 
-	/* Specific data ptr. */
-	params->d = malloc(sizeof(nsupdate_params_t));
-	if (!params->d) return KNOT_ENOMEM;
-	memset(params->d, 0, sizeof(nsupdate_params_t));
-	nsupdate_params_t *npar = NSUP_PARAM(params);
+	/* Initialize list. */
+	init_list(&params->qfiles);
 
-	/* Lists */
-	init_list(&params->servers);
-	init_list(&npar->qfiles);
+	/* Default server. */
+	params->server = server_create(DEFAULT_IPV4_NAME, DEFAULT_DNS_PORT);
+	if (!params->server) return KNOT_ENOMEM;
 
-	/* Default values. */
-	params->class_num = KNOT_CLASS_IN;
-	params->operation = OPERATION_UPDATE;
+	/* Default settings. */
+	params->ip = IP_ALL;
 	params->protocol = PROTO_ALL;
-	params->port = strdup(DEFAULT_DNS_PORT);
-	params->udp_size = DEFAULT_UDP_SIZE;
 	params->retries = DEFAULT_RETRIES_NSUPDATE;
 	params->wait = DEFAULT_TIMEOUT_NSUPDATE;
-	params->format = FORMAT_NSUPDATE;
+	params->class_num = KNOT_CLASS_IN;
 	params->type_num = KNOT_RRTYPE_SOA;
 
-	/* Create default server. */
-	if (params_parse_server(DEFAULT_IPV4_NAME, &params->servers,
-	                        params->port)
-	    != KNOT_EOK) {
-		return KNOT_EINVAL;
-	}
+	/* Default style. */
+	params->style = DEFAULT_STYLE;
+	params->style.format = FORMAT_NSUPDATE;
 
 	/* Initialize RR parser. */
-	npar->rrp = scanner_create(".");
-	if (!npar->rrp) return KNOT_ENOMEM;
-	npar->rrp->process_record = parse_rr;
-	npar->rrp->process_error = parse_err;
-	npar->rrp->default_class = params->class_num;
-	nsupdate_params_set_ttl(params, 0);
-	nsupdate_params_set_origin(params, ".");
+	params->rrp = scanner_create(".");
+	if (!params->rrp) return KNOT_ENOMEM;
+	params->rrp->process_record = parse_rr;
+	params->rrp->process_error = parse_err;
+	params->rrp->default_class = params->class_num;
+	nsupdate_set_ttl(params, 0);
+	nsupdate_set_origin(params, ".");
+
 	return KNOT_EOK;
 }
 
-void nsupdate_params_clean(params_t *params)
+void nsupdate_clean(nsupdate_params_t *params)
 {
+	strnode_t *n = NULL, *nxt = NULL;
+
 	if (params == NULL) {
 		return;
 	}
 
-	/* Free specific structure. */
-	nsupdate_params_t* npar = NSUP_PARAM(params);
-	if (npar) {
-		free(npar->zone);
-		if (npar->rrp) {
-			scanner_free(npar->rrp);
-		}
+	server_free(params->server);
+	server_free(params->srcif);
+	free(params->zone);
+	scanner_free(params->rrp);
+	knot_packet_free(&params->pkt);
+	knot_packet_free(&params->resp);
 
-		/* Free qfiles. */
-		strnode_t *n = NULL, *nxt = NULL;
-		WALK_LIST_DELSAFE(n, nxt, npar->qfiles) {
-			free(n);
-		}
-
-		free(npar);
-		params->d = NULL;
-	}
-
-	/* Free server list. */
-	server_t *n = NULL, *nxt = NULL;
-	WALK_LIST_DELSAFE(n, nxt, params->servers) {
-		server_free(n);
+	/* Free qfiles. */
+	WALK_LIST_DELSAFE(n, nxt, params->qfiles) {
+		free(n);
 	}
 
-	free(params->port);
-
 	/* Free TSIG key. */
 	knot_dname_free(&params->key.name);
 	free(params->key.secret);
 
 	/* Clean up the structure. */
-	memset(params, 0, sizeof(params_t));
+	memset(params, 0, sizeof(*params));
 }
 
-static void nsupdate_params_help(int argc, char *argv[])
+static void nsupdate_help(int argc, char *argv[])
 {
 	printf("Usage: %s [-d] [-v] [-y [hmac:]name:key] [-p port] "
 	       "[-t timeout] [-r retries] [filename]\n",
 	       argv[0]);
 }
 
-int nsupdate_params_parse(params_t *params, int argc, char *argv[])
+int nsupdate_parse(nsupdate_params_t *params, int argc, char *argv[])
 {
 	int opt = 0;
 	int ret = KNOT_EOK;
@@ -158,29 +138,25 @@ int nsupdate_params_parse(params_t *params, int argc, char *argv[])
 		return KNOT_EINVAL;
 	}
 
-	ret = nsupdate_params_init(params);
+	ret = nsupdate_init(params);
 	if (ret != KNOT_EOK) {
 		return ret;
 	}
 
-	/* Fetch default server. */
-	server_t *srv = TAIL(params->servers);
-
 	/* Command line options processing. */
 	while ((opt = getopt(argc, argv, "dDvp:t:r:y:k:")) != -1) {
 		switch (opt) {
 		case 'd':
 		case 'D': /* Extra debugging. */
 			msg_enable_debug(1);
-			params_flag_verbose(params);
 			break;
 		case 'v':
-			params_flag_tcp(params);
+			params->protocol = PROTO_TCP;
 			break;
 		case 'p':
-			free(srv->service);
-			srv->service = strdup(optarg);
-			if (!srv->service) {
+			free(params->server->service);
+			params->server->service = strdup(optarg);
+			if (!params->server->service) {
 				ERR("failed to set default port '%s'\n", optarg);
 				return KNOT_ENOMEM;
 			}
@@ -202,29 +178,27 @@ int nsupdate_params_parse(params_t *params, int argc, char *argv[])
 			if (ret != KNOT_EOK) return ret;
 			break;
 		default:
-			nsupdate_params_help(argc, argv);
+			nsupdate_help(argc, argv);
 			return KNOT_ENOTSUP;
 		}
 	}
 
 	/* Process non-option parameters. */
-	nsupdate_params_t* npar = NSUP_PARAM(params);
 	for (; optind < argc; ++optind) {
 		strnode_t *n = malloc(sizeof(strnode_t));
 		if (!n) { /* Params will be cleaned on exit. */
 			return KNOT_ENOMEM;
 		}
 		n->str = argv[optind];
-		add_tail(&npar->qfiles, &n->n);
+		add_tail(&params->qfiles, &n->n);
 	}
 
 	return ret;
 }
 
-int nsupdate_params_set_ttl(params_t *params, uint32_t ttl)
+int nsupdate_set_ttl(nsupdate_params_t *params, const uint32_t ttl)
 {
-	nsupdate_params_t* npar = NSUP_PARAM(params);
-	int ret = parser_set_default(npar->rrp, "$TTL %u\n", ttl);
+	int ret = parser_set_default(params->rrp, "$TTL %u\n", ttl);
 	if (ret == KNOT_EOK) {
 		params->ttl = ttl;
 	} else {
@@ -233,13 +207,17 @@ int nsupdate_params_set_ttl(params_t *params, uint32_t ttl)
 	return ret;
 }
 
-int nsupdate_params_set_origin(params_t *params, const char *origin)
+int nsupdate_set_origin(nsupdate_params_t *params, const char *origin)
 {
-	nsupdate_params_t* npar = NSUP_PARAM(params);
-	int ret = parser_set_default(npar->rrp, "$ORIGIN %s\n", origin);
+	char *fqdn = get_fqd_name(origin);
+
+	int ret = parser_set_default(params->rrp, "$ORIGIN %s\n", fqdn);
+
+	free(fqdn);
+
 	if (ret == KNOT_EOK) {
-		if (npar->zone) free(npar->zone);
-		npar->zone = strdup(origin);
+		if (params->zone) free(params->zone);
+		params->zone = strdup(origin);
 	} else {
 		ERR("failed to set default origin, %s\n", knot_strerror(ret));
 	}
diff --git a/src/utils/nsupdate/nsupdate_params.h b/src/utils/nsupdate/nsupdate_params.h
index 65661ad6934af208dc61d932797ea2580a99c265..f590f8ca5d9ed8704a5e4b934aa943354ca55f25 100644
--- a/src/utils/nsupdate/nsupdate_params.h
+++ b/src/utils/nsupdate/nsupdate_params.h
@@ -30,10 +30,10 @@
 #include <stdint.h>
 
 #include "common/lists.h"		// list
-#include "utils/common/params.h"	// protocol_t
-#include "zscanner/scanner.h"		// scanner_t
 #include "libknot/packet/packet.h"	// knot_packet_t
-#include "utils/common/netio.h"
+#include "zscanner/scanner.h"		// scanner_t
+#include "utils/common/netio.h"		// server_t
+#include "utils/common/params.h"	// protocol_t
 
 /*! Parser init string. */
 #define PARSER_INIT_STR "$ORIGIN %s\n$TTL %u\n"
@@ -42,6 +42,24 @@
 typedef struct {
 	/*!< List of files with query data. */
 	list		qfiles;
+	/*!< List of nameservers to query to. */
+	server_t	*server;
+	/*!< Local interface (optional). */
+	server_t	*srcif;
+	/*!< Operation mode. */
+	ip_t		ip;
+	/*!< Type (TCP, UDP) protocol to use. */
+	protocol_t	protocol;
+	/*!< Default class number. */
+	uint16_t	class_num;
+	/*!< Default type number. */
+	uint16_t	type_num;
+	/*!< Default TTL. */
+	uint32_t	ttl;
+	/*!< Number of UDP retries. */
+	uint32_t	retries;
+	/*!< Wait for network response in seconds (-1 means forever). */
+	int32_t		wait;
 	/*!< Current zone. */
 	char		*zone;
 	/*!< RR parser. */
@@ -52,15 +70,16 @@ typedef struct {
 	knot_packet_t	*resp;
 	/*!< Buffer for response. */
 	uint8_t		rwire[MAX_PACKET_SIZE];
-	/*!< Local interface (optional). */
-	server_t	*srcif;
+	/*!< TSIG key used. */
+	knot_key_t	key;
+	/*!< Default output settings. */
+	style_t		style;
 } nsupdate_params_t;
-#define NSUP_PARAM(p) ((nsupdate_params_t*)p->d)
 
-int nsupdate_params_parse(params_t *params, int argc, char *argv[]);
-int nsupdate_params_set_ttl(params_t *params, uint32_t ttl);
-int nsupdate_params_set_origin(params_t *params, const char *origin);
-void nsupdate_params_clean(params_t *params);
+int nsupdate_parse(nsupdate_params_t *params, int argc, char *argv[]);
+int nsupdate_set_ttl(nsupdate_params_t *params, const uint32_t ttl);
+int nsupdate_set_origin(nsupdate_params_t *params, const char *origin);
+void nsupdate_clean(nsupdate_params_t *params);
 
 #endif // _NSUPDATE__NSUPDATE_PARAMS_H_
 
diff --git a/src/zscanner/scanner.c b/src/zscanner/scanner.c
index 02256e8979bfd23e1b51e41c3f3ce796478acf1e..5b7659d82611a6c25abc11416f6cd294f6e93b38 100644
--- a/src/zscanner/scanner.c
+++ b/src/zscanner/scanner.c
@@ -4367,8 +4367,10 @@ scanner_t* scanner_create(const char *file_name)
 
 void scanner_free(scanner_t *s)
 {
-	free(s->file_name);
-	free(s);
+	if (s != NULL) {
+		free(s->file_name);
+		free(s);
+	}
 }
 
 int scanner_process(const char *start,
@@ -4407,7 +4409,7 @@ int scanner_process(const char *start,
 
 	// Writing scanner body (in C).
 	
-#line 4411 "zscanner/scanner.c"
+#line 4413 "zscanner/scanner.c"
 	{
 	int _klen;
 	unsigned int _trans;
@@ -6583,7 +6585,7 @@ _match:
 		s->process_record(s);
 	}
 	break;
-#line 6587 "zscanner/scanner.c"
+#line 6589 "zscanner/scanner.c"
 		}
 	}
 
@@ -6809,7 +6811,7 @@ _again:
 		p--; {cs = 230; goto _again;}
 	}
 	break;
-#line 6813 "zscanner/scanner.c"
+#line 6815 "zscanner/scanner.c"
 		}
 	}
 	}
@@ -6817,7 +6819,7 @@ _again:
 	_out: {}
 	}
 
-#line 140 "zscanner/scanner.rl"
+#line 142 "zscanner/scanner.rl"
 
 	// Check if scanner state machine is in uncovered state.
 	if (cs == zone_scanner_error) {
diff --git a/src/zscanner/scanner.rl b/src/zscanner/scanner.rl
index 4208133e82d11cdbc23870c2aabed389815e0242..24c4a2aa4864327f7f1bc6244c13aa39ca12a72c 100644
--- a/src/zscanner/scanner.rl
+++ b/src/zscanner/scanner.rl
@@ -97,8 +97,10 @@ scanner_t* scanner_create(const char *file_name)
 
 void scanner_free(scanner_t *s)
 {
-	free(s->file_name);
-	free(s);
+	if (s != NULL) {
+		free(s->file_name);
+		free(s);
+	}
 }
 
 int scanner_process(const char *start,