diff --git a/Knot.files b/Knot.files
index e15a4336951a4364e20ee231d06d569efabff18b..c6af0132104826a1ab8b16a5b0df7f1eb4898034 100644
--- a/Knot.files
+++ b/Knot.files
@@ -49,6 +49,12 @@ src/dnslib/descriptor.h
 src/dnslib/descriptor.c
 src/dnslib/zone.h
 src/dnslib/zone.c
+src/dnslib/packet.h
+src/dnslib/packet.c
+src/dnslib/query.h
+src/dnslib/query.c
+src/dnslib/response2.h
+src/dnslib/response2.c
 src/dnslib/response.h
 src/dnslib/wire.h
 src/dnslib/response.c
diff --git a/src/dnslib/debug.h b/src/dnslib/debug.h
index 5bbf95930bac4b6ca365c26da5c628d5d039e58d..8ddcc7c313bc9880933ee1b1630f80dd06304313 100644
--- a/src/dnslib/debug.h
+++ b/src/dnslib/debug.h
@@ -78,6 +78,7 @@ void dnslib_zone_dump(dnslib_zone_t *zone, char loaded_zone);
 //#define DNSLIB_ZONEDB_DEBUG
 //#define DNSLIB_DNAME_DEBUG
 //#define DNSLIB_RESPONSE_DEBUG
+//#define DNSLIB_PACKET_DEBUG
 //#define DNSLIB_EDNS_DEBUG
 //#define DNSLIB_RRSET_DEBUG
 //#define DNSLIB_NSEC3_DEBUG
@@ -124,6 +125,16 @@ void dnslib_zone_dump(dnslib_zone_t *zone, char loaded_zone);
 #define DEBUG_DNSLIB_RESPONSE(cmds)
 #endif
 
+#ifdef DNSLIB_PACKET_DEBUG
+#define debug_dnslib_packet(msg...) fprintf(stderr, msg)
+#define debug_dnslib_packet_hex(data, len) hex_print((data), (len))
+#define DEBUG_DNSLIB_PACKET(cmds) do { cmds } while (0)
+#else
+#define debug_dnslib_packet(msg...)
+#define debug_dnslib_packet_hex(data, len)
+#define DEBUG_DNSLIB_PACKET(cmds)
+#endif
+
 #ifdef DNSLIB_EDNS_DEBUG
 #define debug_dnslib_edns(msg...) fprintf(stderr, msg)
 #else
diff --git a/src/dnslib/packet.c b/src/dnslib/packet.c
new file mode 100644
index 0000000000000000000000000000000000000000..2628b40bf3539bedf4e579c90e730cb41c26d751
--- /dev/null
+++ b/src/dnslib/packet.c
@@ -0,0 +1,330 @@
+#include "dnslib/packet.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Default sizes for response structure parts and steps for increasing
+ *        them.
+ */
+enum {
+	DEFAULT_ANCOUNT = 6,         /*!< Default count of Answer RRSets. */
+	DEFAULT_NSCOUNT = 8,         /*!< Default count of Authority RRSets. */
+	DEFAULT_ARCOUNT = 28,        /*!< Default count of Additional RRSets. */
+
+	DEFAULT_ANCOUNT_QUERY = 1,   /*!< Default count of Answer RRSets. */
+	DEFAULT_NSCOUNT_QUERY = 0,   /*!< Default count of Authority RRSets. */
+	DEFAULT_ARCOUNT_QUERY = 1,  /*!< Default count of Additional RRSets. */
+	/*!
+	 * \brief Default count of all domain names in response.
+	 *
+	 * Used for compression table.
+	 */
+	DEFAULT_DOMAINS_IN_RESPONSE = 22,
+
+	/*! \brief Default count of temporary RRSets stored in response. */
+	DEFAULT_TMP_RRSETS = 5,
+
+	/*! \brief Default count of temporary RRSets stored in query. */
+	DEFAULT_TMP_RRSETS_QUERY = 2,
+
+	STEP_ANCOUNT = 6, /*!< Step for increasing space for Answer RRSets. */
+	STEP_NSCOUNT = 8, /*!< Step for increasing space for Authority RRSets.*/
+	STEP_ARCOUNT = 8,/*!< Step for increasing space for Additional RRSets.*/
+	STEP_DOMAINS = 10,   /*!< Step for resizing compression table. */
+	STEP_TMP_RRSETS = 5  /*!< Step for increasing temorary RRSets count. */
+};
+
+/*----------------------------------------------------------------------------*/
+#define PREALLOC_RRSETS(count) (count * sizeof(dnslib_rrset_t *))
+
+/*! \brief Sizes for preallocated space in the response structure. */
+enum {
+	/*! \brief Size of the response structure itself. */
+	PREALLOC_PACKET = sizeof(dnslib_packet_t),
+	/*! \brief Space for QNAME dname structure. */
+	PREALLOC_QNAME_DNAME = sizeof(dnslib_dname_t),
+	/*! \brief Space for QNAME name (maximum domain name size). */
+	PREALLOC_QNAME_NAME = 256,
+	/*! \brief Space for QNAME labels (maximum label count). */
+	PREALLOC_QNAME_LABELS = 127,
+	/*! \brief Total space for QNAME. */
+	PREALLOC_QNAME = PREALLOC_QNAME_DNAME
+	                 + PREALLOC_QNAME_NAME
+	                 + PREALLOC_QNAME_LABELS,
+	/*!
+	 * \brief Space for RR owner wire format.
+	 *
+	 * Temporary buffer, used when putting RRSets to the response.
+	 */
+	PREALLOC_RR_OWNER = 256,
+
+//	/*! \brief Space for Answer RRSets. */
+//	PREALLOC_ANSWER = DEFAULT_ANCOUNT * sizeof(dnslib_dname_t *),
+//	/*! \brief Space for Authority RRSets. */
+//	PREALLOC_AUTHORITY = DEFAULT_NSCOUNT * sizeof(dnslib_dname_t *),
+//	/*! \brief Space for Additional RRSets. */
+//	PREALLOC_ADDITIONAL = DEFAULT_ARCOUNT * sizeof(dnslib_dname_t *),
+//	/*! \brief Total size for Answer, Authority and Additional RRSets. */
+//	PREALLOC_RRSETS = PREALLOC_ANSWER
+//	                  + PREALLOC_AUTHORITY
+//	                  + PREALLOC_ADDITIONAL,
+	/*! \brief Space for one part of the compression table (domain names).*/
+	PREALLOC_DOMAINS =
+		DEFAULT_DOMAINS_IN_RESPONSE * sizeof(dnslib_dname_t *),
+	/*! \brief Space for other part of the compression table (offsets). */
+	PREALLOC_OFFSETS =
+		DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t),
+	PREALLOC_COMPRESSION = PREALLOC_DOMAINS + PREALLOC_OFFSETS,
+
+//	/*! \brief Space for temporary RRSets. */
+//	PREALLOC_TMP_RRSETS =
+//		DEFAULT_TMP_RRSETS * sizeof(dnslib_rrset_t *),
+
+	PREALLOC_QUERY = PREALLOC_PACKET
+	                 + PREALLOC_QNAME
+	                 + PREALLOC_RRSETS(DEFAULT_ANCOUNT_QUERY)
+	                 + PREALLOC_RRSETS(DEFAULT_NSCOUNT_QUERY)
+	                 + PREALLOC_RRSETS(DEFAULT_ARCOUNT_QUERY)
+	                 + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS_QUERY),
+
+	/*! \brief Total preallocated size for the response. */
+	PREALLOC_TOTAL = PREALLOC_PACKET
+	                 + PREALLOC_QNAME
+	                 + PREALLOC_RR_OWNER
+	                 + PREALLOC_RRSETS
+	                 + PREALLOC_COMPRESSION
+	                 + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS),
+};
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions                                                          */
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* API functions                                                              */
+/*----------------------------------------------------------------------------*/
+
+dnslib_packet_t *dnslib_packet_new(dnslib_packet_prealloc_type_t prealloc)
+{
+	/*! \todo Implement! */
+	return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_packet_parse_from_wire(dnslib_packet_t *packet, uint8_t *wireformat,
+                                  size_t size, int question_only)
+{
+	/*! \todo Implement! */
+	return DNSLIB_ERROR;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t dnslib_packet_opcode(const dnslib_packet_t *packet)
+{
+	return dnslib_wire_flags_get_opcode(packet->header.flags1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const dnslib_dname_t *dnslib_packet_qname(const dnslib_packet_t *packet)
+{
+	return packet->question.qname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t dnslib_packet_qtype(const dnslib_packet_t *packet)
+{
+	return packet->question.qtype;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t dnslib_packet_qclass(const dnslib_packet_t *packet)
+{
+	return packet->question.qclass;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short dnslib_packet_answer_rrset_count(const dnslib_packet_t *packet)
+{
+	return packet->an_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short dnslib_packet_authority_rrset_count(const dnslib_packet_t *packet)
+{
+	return packet->ns_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short dnslib_packet_additional_rrset_count(const dnslib_packet_t *packet)
+{
+	return packet->ar_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const dnslib_rrset_t *dnslib_packet_answer_rrset(
+	const dnslib_packet_t *packet, short pos)
+{
+	if (pos > packet->an_rrsets) {
+		return NULL;
+	}
+
+	return packet->answer[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+const dnslib_rrset_t *dnslib_packet_authority_rrset(
+	dnslib_packet_t *packet, short pos)
+{
+	if (pos > packet->ns_rrsets) {
+		return NULL;
+	}
+
+	return packet->authority[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+const dnslib_rrset_t *dnslib_packet_additional_rrset(
+	dnslib_packet_t *packet, short pos)
+{
+	if (pos > packet->ar_rrsets) {
+		return NULL;
+	}
+
+	return packet->additional[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+void dnslib_packet_free(dnslib_packet_t **packet)
+{
+	if (packet == NULL || *packet == NULL) {
+		return;
+	}
+
+	// free temporary domain names
+	debug_dnslib_packet("Freeing tmp domains...\n");
+	dnslib_packet_free_tmp_rrsets(*packet);
+
+	// check if some additional space was allocated for the packet
+	debug_dnslib_packet("Freeing additional allocated space...\n");
+	dnslib_packet_free_allocated_space(*packet);
+
+	// free the space for wireformat
+	assert((*packet)->wireformat != NULL);
+	free((*packet)->wireformat);
+
+	debug_dnslib_packet("Freeing packet structure\n");
+	free(*packet);
+	*packet = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+#ifdef DNSLIB_PACKET_DEBUG
+static void dnslib_packet_dump_rrsets(const dnslib_rrset_t **rrsets,
+                                      int count)
+{
+	for (int i = 0; i < count; ++i) {
+		debug_dnslib_packet("  RRSet %d:\n", i + 1);
+		char *name = dnslib_dname_to_str(rrsets[i]->owner);
+		debug_dnslib_packet("    Owner: %s\n", name);
+		free(name);
+		debug_dnslib_packet("    Type: %s\n",
+		                      dnslib_rrtype_to_string(rrsets[i]->type));
+		debug_dnslib_packet("    Class: %s\n",
+		                   dnslib_rrclass_to_string(rrsets[i]->rclass));
+		debug_dnslib_packet("    TTL: %d\n", rrsets[i]->ttl);
+		debug_dnslib_packet("    RDATA: ");
+
+		dnslib_rrtype_descriptor_t *desc =
+			dnslib_rrtype_descriptor_by_type(rrsets[i]->type);
+
+		const dnslib_rdata_t *rdata = dnslib_rrset_rdata(rrsets[i]);
+		while (rdata != NULL) {
+			for (int j = 0; j < rdata->count; ++j) {
+				switch (desc->wireformat[j]) {
+				case DNSLIB_RDATA_WF_COMPRESSED_DNAME:
+				case DNSLIB_RDATA_WF_LITERAL_DNAME:
+				case DNSLIB_RDATA_WF_UNCOMPRESSED_DNAME:
+					name = dnslib_dname_to_str(
+						rdata->items[j].dname);
+					debug_dnslib_packet("%s \n",name);
+					free(name);
+					break;
+				case DNSLIB_RDATA_WF_BINARYWITHLENGTH:
+					debug_dnslib_packet_hex(
+					    (char *)rdata->items[j].raw_data,
+					    rdata->items[j].raw_data[0]);
+					break;
+				default:
+					debug_dnslib_packet_hex(
+					   (char *)&rdata->items[j].raw_data[1],
+					   rdata->items[j].raw_data[0]);
+					break;
+				}
+			}
+			rdata = dnslib_rrset_rdata_next(rrsets[i], rdata);
+		}
+	}
+}
+#endif
+/*----------------------------------------------------------------------------*/
+
+void dnslib_packet_dump(const dnslib_packet_t *packet)
+{
+#ifdef DNSLIB_PACKET_DEBUG
+	debug_dnslib_packet("DNS packet:\n-----------------------------\n");
+
+	debug_dnslib_packet("\nHeader:\n");
+	debug_dnslib_packet("  ID: %u", packet->header.id);
+	debug_dnslib_packet("  FLAGS: %s %s %s %s %s %s %s\n",
+	       dnslib_wire_flags_get_qr(packet->header.flags1) ? "qr" : "",
+	       dnslib_wire_flags_get_aa(packet->header.flags1) ? "aa" : "",
+	       dnslib_wire_flags_get_tc(packet->header.flags1) ? "tc" : "",
+	       dnslib_wire_flags_get_rd(packet->header.flags1) ? "rd" : "",
+	       dnslib_wire_flags_get_ra(packet->header.flags2) ? "ra" : "",
+	       dnslib_wire_flags_get_ad(packet->header.flags2) ? "ad" : "",
+	       dnslib_wire_flags_get_cd(packet->header.flags2) ? "cd" : "");
+	debug_dnslib_packet("  QDCOUNT: %u\n", packet->header.qdcount);
+	debug_dnslib_packet("  ANCOUNT: %u\n", packet->header.ancount);
+	debug_dnslib_packet("  NSCOUNT: %u\n", packet->header.nscount);
+	debug_dnslib_packet("  ARCOUNT: %u\n", packet->header.arcount);
+
+	debug_dnslib_packet("\nQuestion:\n");
+	char *qname = dnslib_dname_to_str(packet->question.qname);
+	debug_dnslib_packet("  QNAME: %s\n", qname);
+	free(qname);
+	debug_dnslib_packet("  QTYPE: %u (%s)\n", packet->question.qtype,
+	       dnslib_rrtype_to_string(packet->question.qtype));
+	debug_dnslib_packet("  QCLASS: %u (%s)\n", packet->question.qclass,
+	       dnslib_rrclass_to_string(packet->question.qclass));
+
+	debug_dnslib_packet("\nAnswer RRSets:\n");
+	dnslib_packet_dump_rrsets(packet->answer, packet->an_rrsets);
+	debug_dnslib_packet("\nAuthority RRSets:\n");
+	dnslib_packet_dump_rrsets(packet->authority, packet->ns_rrsets);
+	debug_dnslib_packet("\nAdditional RRSets:\n");
+	dnslib_packet_dump_rrsets(packet->additional, packet->ar_rrsets);
+
+	/*! \todo Dumping of Answer, Authority and Additional sections. */
+
+	debug_dnslib_packet("\nEDNS:\n");
+	debug_dnslib_packet("  Version: %u\n", packet->opt_rr.version);
+	debug_dnslib_packet("  Payload: %u\n", packet->opt_rr.payload);
+	debug_dnslib_packet("  Extended RCODE: %u\n",
+	                      packet->opt_rr.ext_rcode);
+
+	debug_dnslib_packet("\nPacket size: %d\n", packet->size);
+	debug_dnslib_packet("\n-----------------------------\n");
+#endif
+}
+
diff --git a/src/dnslib/packet.h b/src/dnslib/packet.h
new file mode 100644
index 0000000000000000000000000000000000000000..a927fc67a370cb214a18fff9c63bb1ecbdf38470
--- /dev/null
+++ b/src/dnslib/packet.h
@@ -0,0 +1,269 @@
+/*!
+ * \file packet.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Structure for holding DNS packet data and metadata.
+ *
+ * \addtogroup dnslib
+ * @{
+ */
+
+#ifndef _KNOT_DNSLIB_PACKET_H_
+#define _KNOT_DNSLIB_PACKET_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "dnslib/dname.h"
+#include "dnslib/rrset.h"
+#include "dnslib/edns.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure for holding information needed for compressing domain names.
+ *
+ * It's a simple table of domain names and their offsets in wire format of the
+ * packet.
+ *
+ * \todo Consider using some better lookup structure, such as skip-list.
+ */
+struct dnslib_compressed_dnames {
+	const dnslib_dname_t **dnames;  /*!< Domain names present in packet. */
+	size_t *offsets;          /*!< Offsets of domain names in the packet. */
+	short count;              /*!< Count of items in the previous arrays. */
+	short max;                /*!< Capacity of the structure (allocated). */
+};
+
+typedef struct dnslib_compressed_dnames dnslib_compressed_dnames_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure representing the DNS packet header.
+ */
+struct dnslib_header {
+	uint16_t id;      /*!< ID stored in host byte order. */
+	uint8_t flags1;   /*!< First octet of header flags. */
+	uint8_t flags2;   /*!< Second octet of header flags. */
+	uint16_t qdcount; /*!< Number of Question RRs, in host byte order. */
+	uint16_t ancount; /*!< Number of Answer RRs, in host byte order. */
+	uint16_t nscount; /*!< Number of Authority RRs, in host byte order. */
+	uint16_t arcount; /*!< Number of Additional RRs, in host byte order. */
+};
+
+typedef struct dnslib_header dnslib_header_t;
+
+/*!
+ * \brief Structure representing one Question entry in the DNS packet.
+ */
+struct dnslib_question {
+	dnslib_dname_t *qname;  /*!< Question domain name. */
+	uint16_t qtype;         /*!< Question TYPE. */
+	uint16_t qclass;        /*!< Question CLASS. */
+};
+
+typedef struct dnslib_question dnslib_question_t;
+
+enum dnslib_packet_prealloc_type {
+	DNSLIB_PACKET_PREALLOC_NONE,
+	DNSLIB_PACKET_PREALLOC_QUERY,
+	DNSLIB_PACKET_PREALLOC_RESPONSE
+};
+
+typedef enum dnslib_packet_prealloc_type dnslib_packet_prealloc_type_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure representing a DNS packet.
+ *
+ * \note QNAME, Answer, Authority and Additonal sections are by default put to
+ *       preallocated space after the structure with default sizes. If the
+ *       space is not enough, more space is allocated dynamically.
+ */
+struct dnslib_packet {
+	/*! \brief DNS header. */
+	dnslib_header_t header;
+
+	/*!
+	 * \brief Question section.
+	 *
+	 * \note Only one Question is supported!
+	 */
+	dnslib_question_t question;
+
+	uint8_t *owner_tmp;  /*!< Allocated space for RRSet owner wire format.*/
+
+	const dnslib_rrset_t **answer;      /*!< Answer RRSets. */
+	const dnslib_rrset_t **authority;   /*!< Authority RRSets. */
+	const dnslib_rrset_t **additional;  /*!< Additional RRSets. */
+
+	short an_rrsets;     /*!< Count of Answer RRSets in the response. */
+	short ns_rrsets;     /*!< Count of Authority RRSets in the response. */
+	short ar_rrsets;     /*!< Count of Additional RRSets in the response. */
+
+	short max_an_rrsets; /*!< Allocated space for Answer RRsets. */
+	short max_ns_rrsets; /*!< Allocated space for Authority RRsets. */
+	short max_ar_rrsets; /*!< Allocated space for Additional RRsets. */
+
+	dnslib_opt_rr_t opt_rr;     /*!< OPT RR included in the packet. */
+
+	uint8_t *wireformat;  /*!< Wire format of the packet. */
+	size_t size;      /*!< Current wire size of the packet. */
+	size_t max_size;  /*!< Maximum allowed size of the packet. */
+
+	/*! \brief Information needed for compressing domain names in packet. */
+	dnslib_compressed_dnames_t compression;
+
+	/*! \brief RRSets to be destroyed with the packet structure. */
+	const dnslib_rrset_t **tmp_rrsets;
+	short tmp_rrsets_count;  /*!< Count of temporary RRSets. */
+	short tmp_rrsets_max;    /*!< Allocated space for temporary RRSets. */
+
+	struct dnslib_packet *query; /*!< Associated query. */
+};
+
+typedef struct dnslib_packet dnslib_packet_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates new empty packet structure.
+ *
+ * \param prealloc What space should be preallocated in the structure.
+ *
+ * \return New packet structure or NULL if an error occured.
+ */
+dnslib_packet_t *dnslib_packet_new(dnslib_packet_prealloc_type_t prealloc);
+
+/*!
+ * \brief Parses the DNS packet from wire format.
+ *
+ * \param packet Packet structure to parse into.
+ * \param wireformat Wire format of the DNS packet.
+ * \param size Size of the wire format in bytes.
+ * \param question_only Set to <> 0 if you do not want to parse the whole
+ *                      packet. In such case the parsing will end after the
+ *                      Question section. Set to 0 to parse the whole packet.
+ *
+ * \retval DNSLIB_EOK
+ */
+int dnslib_packet_parse_from_wire(dnslib_packet_t *packet, uint8_t *wireformat,
+                                  size_t size, int question_only);
+
+/*!
+ * \brief Returns the OPCODE of the packet.
+ *
+ * \param packet Packet (with parsed query) to get the OPCODE from.
+ *
+ * \return OPCODE stored in the packet.
+ */
+uint8_t dnslib_packet_opcode(const dnslib_packet_t *packet);
+
+/*!
+ * \brief Returns the QNAME from the packet.
+ *
+ * \param packet Packet (with parsed query) to get the QNAME from.
+ *
+ * \return QNAME stored in the packet.
+ */
+const dnslib_dname_t *dnslib_packet_qname(const dnslib_packet_t *packet);
+
+/*!
+ * \brief Returns the QTYPE from the packet.
+ *
+ * \param packet Packet (with parsed query) to get the QTYPE from.
+ *
+ * \return QTYPE stored in the packet.
+ */
+uint16_t dnslib_packet_qtype(const dnslib_packet_t *packet);
+
+/*!
+ * \brief Returns the QCLASS from the packet.
+ *
+ * \param response Packet (with parsed query) to get the QCLASS from.
+ *
+ * \return QCLASS stored in the packet.
+ */
+uint16_t dnslib_packet_qclass(const dnslib_packet_t *packet);
+
+/*!
+ * \brief Returns number of RRSets in Answer section of the packet.
+ *
+ * \param response Packet to get the Answer RRSet count from.
+ */
+short dnslib_packet_answer_rrset_count(const dnslib_packet_t *packet);
+
+/*!
+ * \brief Returns number of RRSets in Authority section of the packet.
+ *
+ * \param response Packet to get the Authority RRSet count from.
+ */
+short dnslib_packet_authority_rrset_count(const dnslib_packet_t *packet);
+
+/*!
+ * \brief Returns number of RRSets in Additional section of the packet.
+ *
+ * \param response Packet to get the Additional RRSet count from.
+ */
+short dnslib_packet_additional_rrset_count(const dnslib_packet_t *packet);
+
+/*!
+ * \brief Returns the requested Answer RRset.
+ *
+ * \param packet Packet to get the RRSet from.
+ * \param pos Position of the RRSet in the Answer section (RRSets are stored
+ *            in the order they were added to the response or parsed from the
+ *            query).
+ *
+ * \return The RRSet on position \a pos in the Answer section of \a packet
+ *         or NULL if there is no such RRSet.
+ */
+const dnslib_rrset_t *dnslib_packet_answer_rrset(
+	const dnslib_packet_t *packet, short pos);
+
+/*!
+ * \brief Returns the requested Authority RRset.
+ *
+ * \param packet Packet to get the RRSet from.
+ * \param pos Position of the RRSet in the Authority section (RRSets are stored
+ *            in the order they were added to the response or parsed from the
+ *            query).
+ *
+ * \return The RRSet on position \a pos in the Authority section of \a packet
+ *         or NULL if there is no such RRSet.
+ */
+const dnslib_rrset_t *dnslib_packet_authority_rrset(
+	dnslib_packet_t *packet, short pos);
+
+/*!
+ * \brief Returns the requested Additional RRset.
+ *
+ * \param packet Packet to get the RRSet from.
+ * \param pos Position of the RRSet in the Additional section (RRSets are stored
+ *            in the order they were added to the response or parsed from the
+ *            query).
+ *
+ * \return The RRSet on position \a pos in the Additional section of \a packet
+ *         or NULL if there is no such RRSet.
+ */
+const dnslib_rrset_t *dnslib_packet_additional_rrset(
+	dnslib_packet_t *packet, short pos);
+
+/*!
+ * \brief Properly destroys the packet structure.
+ *
+ * \param response Packet to be destroyed.
+ */
+void dnslib_packet_free(dnslib_packet_t **packet);
+
+/*!
+ * \brief Dumps the whole packet in human-readable form.
+ *
+ * \note This function is empty unless DNSLIB_PACKET_DEBUG is defined.
+ *
+ * \param resp Packet to dump.
+ */
+void dnslib_packet_dump(const dnslib_packet_t *packet);
+
+#endif /* _KNOT_DNSLIB_PACKET_H_ */
+
+/*! @} */
diff --git a/src/dnslib/query.c b/src/dnslib/query.c
new file mode 100644
index 0000000000000000000000000000000000000000..7824f79651fa5cfcef396e9e7e3291c6695be09b
--- /dev/null
+++ b/src/dnslib/query.c
@@ -0,0 +1,15 @@
+#include "dnslib/query.h"
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_dnssec_requested(const dnslib_packet_t *query)
+{
+	return dnslib_edns_do(&query->opt_rr);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_nsid_requested(const dnslib_packet_t *query)
+{
+	return dnslib_edns_has_option(&query->opt_rr, EDNS_OPTION_NSID);
+}
diff --git a/src/dnslib/query.h b/src/dnslib/query.h
new file mode 100644
index 0000000000000000000000000000000000000000..f78ed8887ad09fba6d71544fd55768c8445207a9
--- /dev/null
+++ b/src/dnslib/query.h
@@ -0,0 +1,49 @@
+/*!
+ * \file query.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief API for manipulating queries.
+ *
+ * \addtogroup dnslib
+ * @{
+ */
+
+#ifndef _KNOT_DNSLIB_QUERY_H_
+#define _KNOT_DNSLIB_QUERY_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "dnslib/packet.h"
+#include "dnslib/dname.h"
+#include "dnslib/rrset.h"
+#include "dnslib/edns.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if DNSSEC was requested in the query (i.e. the DO bit was set).
+ *
+ * \param query Packet where the parsed query is stored.
+ *
+ * \retval 0 if the DO bit was not set in the query, or the query is not yet
+ *         parsed.
+ * \retval > 0 if DO bit was set in the query.
+ */
+int dnslib_query_dnssec_requested(const dnslib_packet_t *query);
+
+/*!
+ * \brief Checks if NSID was requested in the query (i.e. the NSID option was
+ *        present in the query OPT RR).
+ *
+ * \param query Packet where the parsed query is stored.
+ *
+ * \retval 0 if the NSID option was not present in the query, or the query is
+ *         not yet parsed.
+ * \retval > 0 if the NSID option was present in the query.
+ */
+int dnslib_query_nsid_requested(const dnslib_packet_t *query);
+
+#endif /* _KNOT_DNSLIB_QUERY_H_ */
+
+/*! @} */
diff --git a/src/dnslib/response2.c b/src/dnslib/response2.c
new file mode 100644
index 0000000000000000000000000000000000000000..a873c08a0dc6362fdec2deb93d4a63cad61415ad
--- /dev/null
+++ b/src/dnslib/response2.c
@@ -0,0 +1,277 @@
+#include "dnslib/response2.h"
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_packet_response_from_query(dnslib_packet_t *response,
+                                      dnslib_packet_t *query)
+{
+	/*! \todo Implement! */
+	return DNSLIB_ERROR;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_add_opt(dnslib_packet_t *resp,
+                            const dnslib_opt_rr_t *opt_rr,
+                            int override_max_size)
+{
+	if (resp == NULL || opt_rr == NULL) {
+		return DNSLIB_EBADARG;
+	}
+
+	// copy the OPT RR
+	resp->edns_response.version = opt_rr->version;
+	resp->edns_response.ext_rcode = opt_rr->ext_rcode;
+	resp->edns_response.payload = opt_rr->payload;
+	resp->edns_response.size = opt_rr->size;
+
+	// if max size is set, it means there is some reason to be that way,
+	// so we can't just set it to higher value
+
+	if (override_max_size && resp->max_size > 0
+	    && resp->max_size < opt_rr->payload) {
+		return DNSLIB_EPAYLOAD;
+	}
+
+	// set max size (less is OK)
+	if (override_max_size) {
+		resp->max_size = resp->edns_response.payload;
+	}
+
+	return DNSLIB_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_set_max_size(dnslib_packet_t *resp, int max_size)
+{
+	if (resp == NULL || max_size <= 0) {
+		return DNSLIB_EBADARG;
+	}
+
+	if (resp->max_size < max_size) {
+		// reallocate space for the wire format (and copy anything
+		// that might have been there before
+		uint8_t *wire_new = (uint8_t *)malloc(max_size);
+		if (wire_new == NULL) {
+			return DNSLIB_ENOMEM;
+		}
+
+		memcpy(wire_new, resp->wireformat, resp->max_size);
+		resp->wireformat = wire_new;
+	}
+
+	// set max size
+	resp->max_size = max_size;
+
+	return DNSLIB_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_add_rrset_answer(dnslib_packet_t *response,
+                                     const dnslib_rrset_t *rrset, int tc,
+                                     int check_duplicates, int compr_cs)
+{
+	if (response == NULL || rrset == NULL) {
+		return DNSLIB_EBADARG;
+	}
+
+	debug_dnslib_response("add_rrset_answer()\n");
+	assert(response->header.arcount == 0);
+	assert(response->header.nscount == 0);
+
+	if (response->an_rrsets == response->max_an_rrsets
+	    && dnslib_response_realloc_rrsets(&response->answer,
+	          &response->max_an_rrsets, DEFAULT_ANCOUNT, STEP_ANCOUNT)
+	       != DNSLIB_EOK) {
+		return DNSLIB_ENOMEM;
+	}
+
+	if (check_duplicates && dnslib_response_contains(response, rrset)) {
+		return DNSLIB_EOK;
+	}
+
+	debug_dnslib_response("Trying to add RRSet to Answer section.\n");
+	debug_dnslib_response("RRset: %p\n", rrset);
+	debug_dnslib_response("Owner: %p\n", rrset->owner);
+
+	int rrs = dnslib_response_try_add_rrset(response->answer,
+	                                        &response->an_rrsets, response,
+	                                        response->max_size
+	                                        - response->size
+	                                        - response->edns_response.size,
+	                                        rrset, tc, compr_cs);
+
+	if (rrs >= 0) {
+		response->header.ancount += rrs;
+		return DNSLIB_EOK;
+	}
+
+	return DNSLIB_ESPACE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_add_rrset_authority(dnslib_packet_t *response,
+                                        const dnslib_rrset_t *rrset, int tc,
+                                        int check_duplicates, int compr_cs)
+{
+	if (response == NULL || rrset == NULL) {
+		return DNSLIB_EBADARG;
+	}
+
+	assert(response->header.arcount == 0);
+
+	if (response->ns_rrsets == response->max_ns_rrsets
+	    && dnslib_response_realloc_rrsets(&response->authority,
+			&response->max_ns_rrsets, DEFAULT_NSCOUNT, STEP_NSCOUNT)
+		!= 0) {
+		return DNSLIB_ENOMEM;
+	}
+
+	if (check_duplicates && dnslib_response_contains(response, rrset)) {
+		return DNSLIB_EOK;
+	}
+
+	debug_dnslib_response("Trying to add RRSet to Authority section.\n");
+
+	int rrs = dnslib_response_try_add_rrset(response->authority,
+	                                        &response->ns_rrsets, response,
+	                                        response->max_size
+	                                        - response->size
+	                                        - response->edns_response.size,
+	                                        rrset, tc, compr_cs);
+
+	if (rrs >= 0) {
+		response->header.nscount += rrs;
+		return DNSLIB_EOK;
+	}
+
+	return DNSLIB_ESPACE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_add_rrset_additional(dnslib_packet_t *response,
+                                         const dnslib_rrset_t *rrset, int tc,
+                                         int check_duplicates, int compr_cs)
+{
+	if (response == NULL || rrset == NULL) {
+		return DNSLIB_EBADARG;
+	}
+
+	// if this is the first additional RRSet, add EDNS OPT RR first
+	if (response->header.arcount == 0
+	    && response->edns_response.version != EDNS_NOT_SUPPORTED) {
+		dnslib_response_edns_to_wire(response);
+	}
+
+	if (response->ar_rrsets == response->max_ar_rrsets
+	    && dnslib_response_realloc_rrsets(&response->additional,
+			&response->max_ar_rrsets, DEFAULT_ARCOUNT, STEP_ARCOUNT)
+		!= 0) {
+		return DNSLIB_ENOMEM;
+	}
+
+	if (check_duplicates && dnslib_response_contains(response, rrset)) {
+		return DNSLIB_EOK;
+	}
+
+	debug_dnslib_response("Trying to add RRSet to Additional section.\n");
+
+	int rrs = dnslib_response_try_add_rrset(response->additional,
+	                                        &response->ar_rrsets, response,
+	                                        response->max_size
+	                                        - response->size, rrset, tc,
+	                                        compr_cs);
+
+	if (rrs >= 0) {
+		response->header.arcount += rrs;
+		return DNSLIB_EOK;
+	}
+
+	return DNSLIB_ESPACE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void dnslib_response_set_rcode(dnslib_packet_t *response, short rcode)
+{
+	dnslib_wire_flags_set_rcode(&response->header.flags2, rcode);
+	dnslib_wire_set_rcode(response->wireformat, rcode);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void dnslib_response_set_aa(dnslib_packet_t *response)
+{
+	dnslib_wire_flags_set_aa(&response->header.flags1);
+	dnslib_wire_set_aa(response->wireformat);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void dnslib_response_set_tc(dnslib_packet_t *response)
+{
+	dnslib_wire_flags_set_tc(&response->header.flags1);
+	dnslib_wire_set_tc(response->wireformat);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_add_tmp_rrset(dnslib_packet_t *response,
+                                  dnslib_rrset_t *tmp_rrset)
+{
+	if (response->tmp_rrsets_count == response->tmp_rrsets_max
+	    && dnslib_response_realloc_rrsets(&response->tmp_rrsets,
+			&response->tmp_rrsets_max, DEFAULT_TMP_RRSETS,
+			STEP_TMP_RRSETS) != DNSLIB_EOK) {
+		return DNSLIB_ENOMEM;
+	}
+
+	response->tmp_rrsets[response->tmp_rrsets_count++] = tmp_rrset;
+
+	return DNSLIB_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_add_nsid(dnslib_packet_t *response, const uint8_t *data,
+                             uint16_t length)
+{
+	return dnslib_edns_add_option(&response->edns_response,
+	                              EDNS_OPTION_NSID, length, data);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int dnslib_response_to_wire(dnslib_packet_t *resp,
+                            uint8_t **resp_wire, size_t *resp_size)
+{
+	if (resp == NULL || resp_wire == NULL || resp_size == NULL
+	    || *resp_wire != NULL) {
+		return DNSLIB_EBADARG;
+	}
+
+	assert(resp->size <= resp->max_size);
+
+	// if there are no additional RRSets, add EDNS OPT RR
+	if (resp->header.arcount == 0
+	    && resp->edns_response.version != EDNS_NOT_SUPPORTED) {
+	    dnslib_response_edns_to_wire(resp);
+	}
+
+	// set ANCOUNT to the packet
+	dnslib_wire_set_ancount(resp->wireformat, resp->header.ancount);
+	// set NSCOUNT to the packet
+	dnslib_wire_set_nscount(resp->wireformat, resp->header.nscount);
+	// set ARCOUNT to the packet
+	dnslib_wire_set_arcount(resp->wireformat, resp->header.arcount);
+
+	//assert(response->size == size);
+	*resp_wire = resp->wireformat;
+	*resp_size = resp->size;
+
+	return DNSLIB_EOK;
+}
diff --git a/src/dnslib/response2.h b/src/dnslib/response2.h
new file mode 100644
index 0000000000000000000000000000000000000000..ff5686fabd07c874f4d77240ec37eb8a9c75bd83
--- /dev/null
+++ b/src/dnslib/response2.h
@@ -0,0 +1,219 @@
+/*!
+ * \file response2.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief API for response manipulation.
+ *
+ * \addtogroup dnslib
+ * @{
+ */
+
+#ifndef _KNOT_DNSLIB_RESPONSE2_H_
+#define _KNOT_DNSLIB_RESPONSE2_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "dnslib/packet.h"
+
+#include "dnslib/dname.h"
+#include "dnslib/rrset.h"
+#include "dnslib/edns.h"
+
+/*!
+ * \brief Default maximum DNS response size
+ *
+ * This size must be supported by all servers and clients.
+ */
+static const short DNSLIB_MAX_RESPONSE_SIZE = 512;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Initializes response from the given query.
+ *
+ * Copies the header, Changes QR bit to 1, copies the Question section and
+ * stores pointer to the query packet structure in the response packet
+ * structure.
+ *
+ * \param response Packet structure representing the response.
+ * \param query Packet structure representing the query.
+ *
+ * \retval DNSLIB_EOK
+ */
+int dnslib_packet_response_from_query(dnslib_packet_t *response,
+                                      dnslib_packet_t *query);
+
+/*!
+ * \brief Sets the OPT RR of the response.
+ *
+ * This function also allocates space for the wireformat of the response, if
+ * the payload in the OPT RR is larger than the current maximum size of the
+ * response and copies the current wireformat over to the new space.
+ *
+ * \note The contents of the OPT RR are copied.
+ *
+ * \param resp Response to set the OPT RR to.
+ * \param opt_rr OPT RR to set.
+ *
+ * \retval DNSLIB_EOK
+ * \retval DNSLIB_EBADARG
+ * \retval DNSLIB_ENOMEM
+ *
+ * \todo Needs test.
+ */
+int dnslib_response_add_opt(dnslib_packet_t *resp,
+                            const dnslib_opt_rr_t *opt_rr,
+                            int override_max_size);
+
+/*!
+ * \brief Sets the maximum size of the response and allocates space for wire
+ *        format (if needed).
+ *
+ * This function also allocates space for the wireformat of the response, if
+ * the given max size is larger than the current maximum size of the response
+ * and copies the current wireformat over to the new space.
+ *
+ * \warning Do not call this function if you are not completely sure that the
+ *          current wire format of the response fits into the new space.
+ *          It does not update the current size of the wire format, so the
+ *          produced response may be larger than the given max size.
+ *
+ * \param resp Response to set the maximum size of.
+ * \param max_size Maximum size of the response.
+ *
+ * \retval DNSLIB_EOK
+ * \retval DNSLIB_EBADARG
+ * \retval DNSLIB_ENOMEM
+ *
+ * \todo Needs test.
+ */
+int dnslib_response_set_max_size(dnslib_packet_t *resp, int max_size);
+
+/*!
+ * \brief Adds a RRSet to the Answer section of the response.
+ *
+ * \param response Response to add the RRSet into.
+ * \param rrset RRSet to be added.
+ * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set.
+ *           Otherwise set to 0.
+ * \param check_duplicates Set to <> 0 if the RRSet should not be added to the
+ *                         response in case it is already there.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ *                 comparation. Set to 0 otherwise.
+ *
+ * \retval DNSLIB_EOK if successful, or the RRSet was already in the answer.
+ * \retval DNSLIB_ENOMEM
+ * \retval DNSLIB_ESPACE
+ */
+int dnslib_response_add_rrset_answer(dnslib_packet_t *response,
+                                     const dnslib_rrset_t *rrset, int tc,
+                                     int check_duplicates, int compr_cs);
+
+/*!
+ * \brief Adds a RRSet to the Authority section of the response.
+ *
+ * \param response Response to add the RRSet into.
+ * \param rrset RRSet to be added.
+ * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set.
+ *           Otherwise set to 0.
+ * \param check_duplicates Set to <> 0 if the RRSet should not be added to the
+ *                         response in case it is already there.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ *                 comparation. Set to 0 otherwise.
+ *
+ * \retval DNSLIB_EOK if successful, or the RRSet was already in the answer.
+ * \retval DNSLIB_ENOMEM
+ * \retval DNSLIB_ESPACE
+ */
+int dnslib_response_add_rrset_authority(dnslib_packet_t *response,
+                                        const dnslib_rrset_t *rrset, int tc,
+                                        int check_duplicates, int compr_cs);
+
+/*!
+ * \brief Adds a RRSet to the Additional section of the response.
+ *
+ * \param response Response to add the RRSet into.
+ * \param rrset RRSet to be added.
+ * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set.
+ *           Otherwise set to 0.
+ * \param check_duplicates Set to <> 0 if the RRSet should not be added to the
+ *                         response in case it is already there.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ *                 comparation. Set to 0 otherwise.
+ *
+ * \retval DNSLIB_EOK if successful, or the RRSet was already in the answer.
+ * \retval DNSLIB_ENOMEM
+ * \retval DNSLIB_ESPACE
+ */
+int dnslib_response_add_rrset_additional(dnslib_packet_t *response,
+                                         const dnslib_rrset_t *rrset, int tc,
+                                         int check_duplicates, int compr_cs);
+
+/*!
+ * \brief Sets the RCODE of the response.
+ *
+ * \param response Response to set the RCODE in.
+ * \param rcode RCODE to set.
+ */
+void dnslib_response_set_rcode(dnslib_packet_t *response, short rcode);
+
+/*!
+ * \brief Sets the AA bit of the response to 1.
+ *
+ * \param response Response in which the AA bit should be set.
+ */
+void dnslib_response_set_aa(dnslib_packet_t *response);
+
+/*!
+ * \brief Sets the TC bit of the response to 1.
+ *
+ * \param response Response in which the TC bit should be set.
+ */
+void dnslib_response_set_tc(dnslib_packet_t *response);
+
+/*!
+ * \brief Adds RRSet to the list of temporary RRSets.
+ *
+ * Temporary RRSets are fully freed when the response structure is destroyed.
+ *
+ * \param response Response to which the temporary RRSet should be added.
+ * \param tmp_rrset Temporary RRSet to be stored in the response.
+ *
+ * \retval DNSLIB_EOK
+ * \retval DNSLIB_ENOMEM
+ */
+int dnslib_response_add_tmp_rrset(dnslib_packet_t *response,
+                                  dnslib_rrset_t *tmp_rrset);
+
+/*!
+ * \brief Adds NSID option to the response.
+ *
+ * \param response Response to add the NSID option into.
+ * \param data NSID data.
+ * \param length Size of NSID data in bytes.
+ *
+ * \retval DNSLIB_EOK
+ * \retval DNSLIB_ENOMEM
+ */
+int dnslib_response_add_nsid(dnslib_packet_t *response, const uint8_t *data,
+                             uint16_t length);
+
+/*!
+ * \brief Converts the response to wire format.
+ *
+ * \param response Response to be converted to wire format.
+ * \param resp_wire Here the wire format of the response will be stored.
+ *                  Space for the response will be allocated. *resp_wire must
+ *                  be set to NULL (to avoid leaks).
+ * \param resp_size The size of the response in wire format will be stored here.
+ *
+ * \retval DNSLIB_EOK
+ * \retval DNSLIB_EBADARG
+ */
+int dnslib_response_to_wire(dnslib_packet_t *response,
+                            uint8_t **resp_wire, size_t *resp_size);
+
+#endif /* _KNOT_DNSLIB_RESPONSE2_H_ */
+
+/*! @} */