summaryrefslogtreecommitdiff
path: root/src/libknot/packet/packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot/packet/packet.c')
-rw-r--r--src/libknot/packet/packet.c1532
1 files changed, 1532 insertions, 0 deletions
diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c
new file mode 100644
index 0000000..82e818f
--- /dev/null
+++ b/src/libknot/packet/packet.c
@@ -0,0 +1,1532 @@
+/* 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 <assert.h>
+
+#include "packet/packet.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "common.h"
+#include "util/descriptor.h"
+#include "util/wire.h"
+
+/*----------------------------------------------------------------------------*/
+
+#define DEFAULT_RRCOUNT_QUERY(type) DEFAULT_##type##COUNT_QUERY
+#define DEFAULT_RRCOUNT(type) DEFAULT_##type##COUNT
+
+#define DEFAULT_RRSET_COUNT(type, packet) \
+ ((packet->prealloc_type == KNOT_PACKET_PREALLOC_NONE) \
+ ? 0 \
+ : (packet->prealloc_type == KNOT_PACKET_PREALLOC_QUERY) \
+ ? DEFAULT_##type##_QUERY \
+ : DEFAULT_##type)
+
+
+
+typedef enum {
+ KNOT_PACKET_DUPL_IGNORE,
+ KNOT_PACKET_DUPL_SKIP,
+ KNOT_PACKET_DUPL_MERGE
+} knot_packet_duplicate_handling_t;
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets all the pointers in the packet structure to the respective
+ * parts of the pre-allocated space.
+ */
+static void knot_packet_init_pointers_response(knot_packet_t *pkt)
+{
+ dbg_packet("Packet pointer: %p\n", pkt);
+
+ char *pos = (char *)pkt + PREALLOC_PACKET;
+
+ // put QNAME directly after the structure
+ pkt->question.qname = (knot_dname_t *)pos;
+ pos += PREALLOC_QNAME_DNAME;
+
+ dbg_packet("QNAME: %p\n", pkt->question.qname);
+
+ pkt->question.qname->name = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_NAME;
+ pkt->question.qname->labels = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_LABELS;
+
+ pkt->owner_tmp = (uint8_t *)pos;
+ dbg_packet("Tmp owner: %p\n", pkt->owner_tmp);
+ pos += PREALLOC_RR_OWNER;
+
+ // then answer, authority and additional sections
+ if (DEFAULT_ANCOUNT == 0) {
+ pkt->answer = NULL;
+ } else {
+ pkt->answer = (const knot_rrset_t **)pos;
+ pos += DEFAULT_ANCOUNT * sizeof(const knot_rrset_t *);
+ }
+
+ if (DEFAULT_NSCOUNT == 0) {
+ pkt->authority = NULL;
+ } else {
+ pkt->authority = (const knot_rrset_t **)pos;
+ pos += DEFAULT_NSCOUNT * sizeof(const knot_rrset_t *);
+ }
+
+ if (DEFAULT_ARCOUNT == 0) {
+ pkt->additional = NULL;
+ } else {
+ pkt->additional = (const knot_rrset_t **)pos;
+ pos += DEFAULT_ARCOUNT * sizeof(const knot_rrset_t *);
+ }
+
+ dbg_packet("Answer section: %p\n", pkt->answer);
+ dbg_packet("Authority section: %p\n", pkt->authority);
+ dbg_packet("Additional section: %p\n", pkt->additional);
+
+ pkt->max_an_rrsets = DEFAULT_ANCOUNT;
+ pkt->max_ns_rrsets = DEFAULT_NSCOUNT;
+ pkt->max_ar_rrsets = DEFAULT_ARCOUNT;
+
+ // then domain names for compression and offsets
+ pkt->compression.dnames = (const knot_dname_t **)pos;
+ pos += DEFAULT_DOMAINS_IN_RESPONSE * sizeof(const knot_dname_t *);
+ pkt->compression.offsets = (size_t *)pos;
+ pos += DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t);
+
+ dbg_packet("Compression dnames: %p\n", pkt->compression.dnames);
+ dbg_packet("Compression offsets: %p\n", pkt->compression.offsets);
+
+ pkt->compression.max = DEFAULT_DOMAINS_IN_RESPONSE;
+
+ pkt->tmp_rrsets = (const knot_rrset_t **)pos;
+ pos += DEFAULT_TMP_RRSETS * sizeof(const knot_rrset_t *);
+
+ dbg_packet("Tmp rrsets: %p\n", pkt->tmp_rrsets);
+
+ pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS;
+
+ assert((char *)pos == (char *)pkt + PREALLOC_RESPONSE);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets all the pointers in the packet structure to the respective
+ * parts of the pre-allocated space.
+ */
+static void knot_packet_init_pointers_query(knot_packet_t *pkt)
+{
+ dbg_packet("Packet pointer: %p\n", pkt);
+
+ char *pos = (char *)pkt + PREALLOC_PACKET;
+
+ // put QNAME directly after the structure
+ pkt->question.qname = (knot_dname_t *)pos;
+ pos += PREALLOC_QNAME_DNAME;
+
+ dbg_packet("QNAME: %p (%zu after start of packet)\n",
+ pkt->question.qname,
+ (void *)pkt->question.qname - (void *)pkt);
+
+ pkt->question.qname->name = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_NAME;
+ pkt->question.qname->labels = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_LABELS;
+
+// pkt->owner_tmp = (uint8_t *)((char *)pkt->question.qname->labels
+// + PREALLOC_QNAME_LABELS);
+
+ // then answer, authority and additional sections
+ if (DEFAULT_ANCOUNT_QUERY == 0) {
+ pkt->answer = NULL;
+ } else {
+ pkt->answer = (const knot_rrset_t **)pos;
+ pos += DEFAULT_ANCOUNT_QUERY * sizeof(const knot_rrset_t *);
+ }
+
+ if (DEFAULT_NSCOUNT_QUERY == 0) {
+ pkt->authority = NULL;
+ } else {
+ pkt->authority = (const knot_rrset_t **)pos;
+ pos += DEFAULT_NSCOUNT_QUERY * sizeof(const knot_rrset_t *);
+ }
+
+ if (DEFAULT_ARCOUNT_QUERY == 0) {
+ pkt->additional = NULL;
+ } else {
+ pkt->additional = (const knot_rrset_t **)pos;
+ pos += DEFAULT_ARCOUNT_QUERY * sizeof(const knot_rrset_t *);
+ }
+
+ dbg_packet("Answer section: %p\n", pkt->answer);
+ dbg_packet("Authority section: %p\n", pkt->authority);
+ dbg_packet("Additional section: %p\n", pkt->additional);
+
+ pkt->max_an_rrsets = DEFAULT_ANCOUNT_QUERY;
+ pkt->max_ns_rrsets = DEFAULT_NSCOUNT_QUERY;
+ pkt->max_ar_rrsets = DEFAULT_ARCOUNT_QUERY;
+
+ pkt->tmp_rrsets = (const knot_rrset_t **)pos;
+ pos += DEFAULT_TMP_RRSETS_QUERY * sizeof(const knot_rrset_t *);
+
+ dbg_packet("Tmp rrsets: %p\n", pkt->tmp_rrsets);
+
+ pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS_QUERY;
+
+// dbg_packet("End of data: %p (%zu after start of packet)\n",
+// pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY,
+// (void *)(pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY)
+// - (void *)pkt);
+ dbg_packet("Allocated total: %u\n", PREALLOC_QUERY);
+
+ assert(pos == (char *)pkt + PREALLOC_QUERY);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Parses DNS header from the wire format.
+ *
+ * \note This function also adjusts the position (\a pos) and size of remaining
+ * bytes in the wire format (\a remaining) according to what was parsed
+ * (though it actually always parses the 12 bytes of the header).
+ *
+ * \param[in,out] pos Wire format to parse the header from.
+ * \param[in,out] remaining Remaining size of the wire format.
+ * \param[out] header Header structure to fill in.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EFEWDATA
+ */
+static int knot_packet_parse_header(const uint8_t *wire, size_t *pos,
+ size_t size, knot_header_t *header)
+{
+ assert(wire != NULL);
+ assert(pos != NULL);
+ assert(header != NULL);
+
+ if (size - *pos < KNOT_WIRE_HEADER_SIZE) {
+ dbg_response("Not enough data to parse header.\n");
+ return KNOT_EFEWDATA;
+ }
+
+ header->id = knot_wire_get_id(wire);
+ // copy some of the flags: OPCODE and RD
+ // do this by copying flags1 and setting QR to 1, AA to 0 and TC to 0
+ header->flags1 = knot_wire_get_flags1(wire);
+// knot_wire_flags_set_qr(&header->flags1);
+// knot_wire_flags_clear_aa(&header->flags1);
+// knot_wire_flags_clear_tc(&header->flags1);
+ // do not copy flags2 (all set by server)
+ header->qdcount = knot_wire_get_qdcount(wire);
+ header->ancount = knot_wire_get_ancount(wire);
+ header->nscount = knot_wire_get_nscount(wire);
+ header->arcount = knot_wire_get_arcount(wire);
+
+ *pos += KNOT_WIRE_HEADER_SIZE;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Parses DNS Question entry from the wire format.
+ *
+ * \note This function also adjusts the position (\a pos) and size of remaining
+ * bytes in the wire format (\a remaining) according to what was parsed.
+ *
+ * \param[in,out] pos Wire format to parse the Question from.
+ * \param[in,out] remaining Remaining size of the wire format.
+ * \param[out] question DNS Question structure to be filled.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EFEWDATA
+ * \retval KNOT_ENOMEM
+ */
+static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
+ size_t size,
+ knot_question_t *question, int alloc)
+{
+ assert(pos != NULL);
+ assert(wire != NULL);
+ assert(question != NULL);
+
+ if (size - *pos < KNOT_WIRE_QUESTION_MIN_SIZE) {
+ dbg_response("Not enough data to parse question.\n");
+ return KNOT_EFEWDATA; // malformed
+ }
+
+ dbg_response("Parsing Question starting on position %zu.\n",
+ *pos);
+
+ // domain name must end with 0, so just search for 0
+ int i = *pos;
+ while (i < size && wire[i] != 0) {
+ ++i;
+ }
+
+ if (size - i - 1 < 4) {
+ dbg_response("Not enough data to parse question.\n");
+ return KNOT_EFEWDATA; // no 0 found or not enough data left
+ }
+
+ dbg_response("Parsing dname starting on position %zu and "
+ "%zu bytes long.\n", *pos, i - *pos + 1);
+ dbg_response("Alloc: %d\n", alloc);
+ if (alloc) {
+ question->qname = knot_dname_new_from_wire(
+ wire + *pos, i - *pos + 1, 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_EBADARG);
+ return res;
+ }
+ }
+
+ *pos = i + 1;
+ question->qtype = knot_wire_read_u16(wire + i + 1);
+ //*pos += 2;
+ question->qclass = knot_wire_read_u16(wire + i + 3);
+ //*pos += 2;
+
+ *pos += 4;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Reallocate space for RRSets.
+ *
+ * \param rrsets Space for RRSets.
+ * \param max_count Size of the space available for the RRSets.
+ * \param default_max_count Size of the space pre-allocated for the RRSets when
+ * the response structure was initialized.
+ * \param step How much the space should be increased.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+static int knot_packet_realloc_rrsets(const knot_rrset_t ***rrsets,
+ short *max_count,
+ short default_max_count, short step)
+{
+ dbg_packet("Max count: %d, default max count: %d\n",
+ *max_count, default_max_count);
+ int free_old = (*max_count) != default_max_count;
+ const knot_rrset_t **old = *rrsets;
+
+ short new_max_count = *max_count + step;
+ const knot_rrset_t **new_rrsets = (const knot_rrset_t **)malloc(
+ new_max_count * sizeof(knot_rrset_t *));
+ CHECK_ALLOC_LOG(new_rrsets, KNOT_ENOMEM);
+
+ memcpy(new_rrsets, *rrsets, (*max_count) * sizeof(knot_rrset_t *));
+
+ *rrsets = new_rrsets;
+ *max_count = new_max_count;
+
+ if (free_old) {
+ free(old);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_rdata_t *knot_packet_parse_rdata(const uint8_t *wire,
+ size_t *pos, size_t total_size, size_t rdlength,
+ const knot_rrtype_descriptor_t *desc)
+{
+// if (desc->type == 0) {
+// dbg_packet("Unknown RR type.\n");
+// return NULL;
+// }
+
+ knot_rdata_t *rdata = knot_rdata_new();
+ if (rdata == NULL) {
+ return NULL;
+ }
+
+ int rc = knot_rdata_from_wire(rdata, wire, pos, total_size, rdlength,
+ desc);
+
+ if (rc != KNOT_EOK) {
+ dbg_packet("rdata_from_wire() returned: %s\n",
+ knot_strerror(rc));
+ knot_rdata_free(&rdata);
+ return NULL;
+ }
+
+ return rdata;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_rrset_t *knot_packet_parse_rr(const uint8_t *wire, size_t *pos,
+ size_t size)
+{
+// knot_rrset_t *rrset =
+// (knot_rrset_t *)malloc(sizeof(knot_rrset_t));
+// CHECK_ALLOC_LOG(rrset, NULL);
+
+ dbg_packet("Parsing RR from position: %zu, total size: %zu\n",
+ *pos, size);
+
+ knot_dname_t *owner = knot_dname_parse_from_wire(wire, pos, size,
+ NULL);
+ dbg_packet("Created owner: %p, actual position: %zu\n", owner,
+ *pos);
+ if (owner == NULL) {
+ return NULL;
+ }
+
+dbg_packet_exec(
+ char *name = knot_dname_to_str(owner);
+ dbg_packet("Parsed name: %s\n", name);
+ free(name);
+);
+
+ //*remaining -= knot_dname_size(rrset->owner);
+
+ /*! @todo Get rid of the numerical constant. */
+ if (size - *pos < 10) {
+ dbg_packet("Malformed RR: Not enough data to parse RR"
+ " header.\n");
+ knot_dname_release(owner);
+ return NULL;
+ }
+
+ dbg_packet("Reading type from position %zu\n", *pos);
+
+ uint16_t type = knot_wire_read_u16(wire + *pos);
+ uint16_t rclass = knot_wire_read_u16(wire + *pos + 2);
+ uint32_t ttl = knot_wire_read_u32(wire + *pos + 4);
+
+ knot_rrset_t *rrset = knot_rrset_new(owner, type, rclass, ttl);
+
+ /* Owner is either referenced in rrset or rrset creation failed. */
+ knot_dname_release(owner);
+
+ /* Check rrset allocation. */
+ if (rrset == NULL) {
+ return NULL;
+ }
+
+ uint16_t rdlength = knot_wire_read_u16(wire + *pos + 8);
+
+ dbg_packet("Read RR header: type %u, class %u, ttl %u, "
+ "rdlength %u\n", rrset->type, rrset->rclass,
+ rrset->ttl, rdlength);
+
+ *pos += 10;
+
+ if (size - *pos < rdlength) {
+ dbg_packet("Malformed RR: Not enough data to parse RR"
+ " RDATA (size: %zu, position: %zu).\n",
+ size, *pos);
+ knot_rrset_deep_free(&rrset, 1, 1, 0);
+// free(rrset);
+ return NULL;
+ }
+
+ rrset->rrsigs = NULL;
+
+ if (rdlength == 0) {
+ return rrset;
+ }
+
+ // parse RDATA
+ knot_rdata_t *rdata = knot_packet_parse_rdata(wire, pos, size,
+ rdlength,
+ knot_rrtype_descriptor_by_type(rrset->type));
+ if (rdata == NULL) {
+ dbg_packet("Malformed RR: Could not parse RDATA.\n");
+ knot_rrset_deep_free(&rrset, 1, 1, 0);
+// free(rrset);
+ return NULL;
+ }
+
+ if (knot_rrset_add_rdata(rrset, rdata) != KNOT_EOK) {
+ dbg_packet("Malformed RR: Could not add RDATA to RRSet"
+ ".\n");
+ knot_rdata_free(&rdata);
+ knot_rrset_deep_free(&rrset, 1, 1, 0);
+// free(rrset);
+ return NULL;
+ }
+
+ return rrset;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_packet_add_rrset(knot_rrset_t *rrset,
+ const knot_rrset_t ***rrsets,
+ short *rrset_count,
+ short *max_rrsets,
+ short default_rrsets,
+ const knot_packet_t *packet,
+ knot_packet_duplicate_handling_t dupl)
+{
+
+ assert(rrset != NULL);
+ assert(rrsets != NULL);
+ assert(rrset_count != NULL);
+ assert(max_rrsets != NULL);
+
+dbg_packet_exec(
+ char *name = knot_dname_to_str(rrset->owner);
+ dbg_packet("packet_add_rrset(), owner: %s, type: %s\n",
+ name, knot_rrtype_to_string(rrset->type));
+ free(name);
+);
+
+ if (*rrset_count == *max_rrsets
+ && knot_packet_realloc_rrsets(rrsets, max_rrsets, default_rrsets,
+ STEP_ANCOUNT) != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ if (dupl == KNOT_PACKET_DUPL_SKIP &&
+ knot_packet_contains(packet, rrset, KNOT_RRSET_COMPARE_PTR)) {
+ /*! \todo This should also return > 0, as it means that the
+ RRSet was not used actually. */
+ return KNOT_EOK;
+ }
+
+ if (dupl == KNOT_PACKET_DUPL_MERGE) {
+ // try to find the RRSet in this array of RRSets
+ for (int i = 0; i < *rrset_count; ++i) {
+
+dbg_packet_exec(
+ char *name = knot_dname_to_str((*rrsets)[i]->owner);
+ dbg_packet("Comparing to RRSet: owner: %s, "
+ "type: %s\n", name,
+ knot_rrtype_to_string(
+ (*rrsets)[i]->type));
+ free(name);
+);
+
+ if (knot_rrset_compare((*rrsets)[i], rrset,
+ KNOT_RRSET_COMPARE_HEADER)) {
+ //const knot_rrset_t *r = (*rrsets)
+ /*! \todo Test this!!! */
+ int rc = knot_rrset_merge(
+ (void **)((*rrsets) + i), (void **)&rrset);
+ if (rc != KNOT_EOK) {
+ return rc;
+ }
+ return 1;
+ }
+ }
+ }
+
+ (*rrsets)[*rrset_count] = rrset;
+ ++(*rrset_count);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
+ size_t size, uint16_t rr_count,
+ const knot_rrset_t ***rrsets,
+ short *rrset_count, short *max_rrsets,
+ short default_rrsets,
+ knot_packet_t *packet)
+{
+ assert(pos != NULL);
+ assert(wire != NULL);
+ assert(rrsets != NULL);
+ assert(rrset_count != NULL);
+ assert(max_rrsets != NULL);
+ assert(packet != NULL);
+
+ dbg_packet("Parsing RRSets starting on position: %zu\n",
+ *pos);
+
+// if (*rrsets == NULL) {
+// knot_packet_realloc_rrsets(rrsets, max_rrsets, 0, 1);
+// }
+
+ /*
+ * The RRs from one RRSet may be scattered in the current section.
+ * We must parse all RRs separately and try to add them to already
+ * parsed RRSets.
+ */
+ int err = KNOT_EOK;
+ knot_rrset_t *rrset = NULL;
+
+ for (int i = 0; i < rr_count; ++i) {
+ rrset = knot_packet_parse_rr(wire, pos, size);
+ if (rrset == NULL) {
+ dbg_packet("Failed to parse RR!\n");
+ err = KNOT_EMALF;
+ break;
+ }
+
+ err = knot_packet_add_rrset(rrset, rrsets, rrset_count,
+ max_rrsets, default_rrsets, packet,
+ KNOT_PACKET_DUPL_MERGE);
+ if (err < 0) {
+ break;
+ } else if (err > 0) { // merged
+ dbg_packet("RRSet merged, freeing.\n");
+ knot_rrset_deep_free(&rrset, 1, 0, 0); // TODO: ok??
+ continue;
+ }
+
+ err = knot_packet_add_tmp_rrset(packet, rrset);
+ if (err != KNOT_EOK) {
+ // remove the last RRSet from the list of RRSets
+ // - just decrement the count
+ --(*rrset_count);
+ knot_rrset_deep_free(&rrset, 1, 1, 1);
+ break;
+ }
+ }
+
+ return (err < 0) ? err : KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Deallocates all space which was allocated additionally to the
+ * pre-allocated space of the response structure.
+ *
+ * \param resp Response structure that holds pointers to the allocated space.
+ */
+static void knot_packet_free_allocated_space(knot_packet_t *pkt)
+{
+ dbg_packet("Freeing additional space in packet.\n");
+ if (pkt->prealloc_type == KNOT_PACKET_PREALLOC_NONE) {
+ dbg_packet("Freeing QNAME.\n");
+ knot_dname_release(pkt->question.qname);
+ }
+
+ if (pkt->max_an_rrsets > DEFAULT_RRSET_COUNT(ANCOUNT, pkt)) {
+ free(pkt->answer);
+ }
+ if (pkt->max_ns_rrsets > DEFAULT_RRSET_COUNT(NSCOUNT, pkt)) {
+ free(pkt->authority);
+ }
+ if (pkt->max_ar_rrsets > DEFAULT_RRSET_COUNT(ARCOUNT, pkt)) {
+ free(pkt->additional);
+ }
+
+ if (pkt->compression.max > DEFAULT_DOMAINS_IN_RESPONSE) {
+ free(pkt->compression.dnames);
+ free(pkt->compression.offsets);
+ }
+
+ if (pkt->tmp_rrsets_max > DEFAULT_RRSET_COUNT(TMP_RRSETS, pkt)) {
+ free(pkt->tmp_rrsets);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_packet_parse_rr_sections(knot_packet_t *packet,
+ size_t *pos)
+{
+ assert(packet != NULL);
+ assert(packet->wireformat != NULL);
+ assert(packet->size > 0);
+ assert(pos != NULL);
+ assert(*pos > 0);
+
+ int err;
+
+ dbg_packet("Parsing Answer RRs...\n");
+ if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
+ packet->size, packet->header.ancount, &packet->answer,
+ &packet->an_rrsets, &packet->max_an_rrsets,
+ DEFAULT_RRSET_COUNT(ANCOUNT, packet), packet)) != KNOT_EOK) {
+ return err;
+ }
+
+ dbg_packet("Parsing Authority RRs...\n");
+ if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
+ packet->size, packet->header.nscount, &packet->authority,
+ &packet->ns_rrsets, &packet->max_ns_rrsets,
+ DEFAULT_RRSET_COUNT(NSCOUNT, packet), packet)) != KNOT_EOK) {
+ return err;
+ }
+
+ dbg_packet("Parsing Additional RRs...\n");
+ if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
+ packet->size, packet->header.arcount, &packet->additional,
+ &packet->ar_rrsets, &packet->max_ar_rrsets,
+ DEFAULT_RRSET_COUNT(ARCOUNT, packet), packet)) != KNOT_EOK) {
+ return err;
+ }
+
+ dbg_packet("Trying to find OPT RR in the packet.\n");
+
+ for (int i = 0; i < packet->ar_rrsets; ++i) {
+ assert(packet->additional[i] != NULL);
+ if (knot_rrset_type(packet->additional[i])
+ == KNOT_RRTYPE_OPT) {
+ dbg_packet("Found OPT RR, filling.\n");
+ err = knot_edns_new_from_rr(&packet->opt_rr,
+ packet->additional[i]);
+ if (err != KNOT_EOK) {
+ return err;
+ }
+ break;
+ }
+ }
+
+ packet->parsed = *pos;
+
+ if (*pos < packet->size) {
+ // some trailing garbage; ignore, but log
+ dbg_response("Packet: %zu bytes of trailing garbage "
+ "in packet.\n", packet->size - (*pos));
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_packet_t *knot_packet_new(knot_packet_prealloc_type_t prealloc)
+{
+ knot_packet_t *pkt;
+ void (*init_pointers)(knot_packet_t *pkt) = NULL;
+ size_t size = 0;
+
+ switch (prealloc) {
+ case KNOT_PACKET_PREALLOC_NONE:
+ size = sizeof(knot_packet_t);
+ break;
+ case KNOT_PACKET_PREALLOC_QUERY:
+ size = PREALLOC_QUERY;
+ init_pointers = knot_packet_init_pointers_query;
+ break;
+ case KNOT_PACKET_PREALLOC_RESPONSE:
+ size = PREALLOC_RESPONSE;
+ init_pointers = knot_packet_init_pointers_response;
+ break;
+ }
+
+ pkt = (knot_packet_t *)malloc(size);
+ CHECK_ALLOC_LOG(pkt, NULL);
+ memset(pkt, 0, size);
+ if (init_pointers != NULL) {
+ init_pointers(pkt);
+ }
+
+ pkt->prealloc_type = prealloc;
+
+ // set EDNS version to not supported
+ pkt->opt_rr.version = EDNS_NOT_SUPPORTED;
+
+ return pkt;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_from_wire(knot_packet_t *packet,
+ const uint8_t *wireformat, size_t size,
+ int question_only)
+{
+ if (packet == NULL || wireformat == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int err;
+
+ // save the wireformat in the packet
+ // TODO: can we just save the pointer, or we have to copy the data??
+ assert(packet->wireformat == NULL);
+ packet->wireformat = (uint8_t*)wireformat;
+ packet->size = size;
+ packet->free_wireformat = 0;
+
+ //uint8_t *pos = wireformat;
+ size_t pos = 0;
+ //size_t remaining = size;
+
+ dbg_packet("Parsing wire format of packet (size %zu).\nHeader\n",
+ size);
+ if ((err = knot_packet_parse_header(wireformat, &pos, size,
+ &packet->header)) != KNOT_EOK) {
+ return err;
+ }
+
+ packet->parsed = pos;
+
+ dbg_packet("Question (prealloc type: %d)...\n", packet->prealloc_type);
+
+ if (packet->header.qdcount > 1) {
+ dbg_packet("QDCOUNT larger than 1, FORMERR.\n");
+ return KNOT_EMALF;
+ }
+
+ knot_packet_dump(packet);
+
+ if (packet->header.qdcount == 1) {
+ if ((err = knot_packet_parse_question(wireformat, &pos, size,
+ &packet->question, packet->prealloc_type
+ == KNOT_PACKET_PREALLOC_NONE)
+ ) != KNOT_EOK) {
+ return err;
+ }
+ packet->parsed = pos;
+ }
+
+ knot_packet_dump(packet);
+
+ if (question_only) {
+ return KNOT_EOK;
+ }
+
+ /*! \todo Replace by call to parse_rest()? */
+ err = knot_packet_parse_rr_sections(packet, &pos);
+
+#ifdef KNOT_PACKET_DEBUG
+ knot_packet_dump(packet);
+#endif /* KNOT_RESPONSE_DEBUG */
+
+ return err;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_rest(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+// if (packet->parsed >= packet->size) {
+// return KNOT_EOK;
+// }
+
+ if (packet->parsed == packet->size) {
+ return KNOT_EOK;
+ }
+
+ size_t pos = packet->parsed;
+
+ return knot_packet_parse_rr_sections(packet, &pos);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
+ knot_rrset_t **rr)
+{
+ if (packet == NULL || rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *rr = NULL;
+
+ if (packet->parsed >= packet->size) {
+ assert(packet->an_rrsets <= packet->header.ancount);
+ if (packet->an_rrsets != packet->header.ancount) {
+ dbg_packet("Parsed less RRs than expected.\n");
+ return KNOT_EMALF;
+ } else {
+ dbg_packet("Whole packet parsed\n");
+ return KNOT_EOK;
+ }
+ }
+
+ if (packet->an_rrsets == packet->header.ancount) {
+ assert(packet->parsed < packet->size);
+ //dbg_packet("Trailing garbage, ignoring...\n");
+ // there may be other data in the packet
+ // (authority or additional).
+ return KNOT_EOK;
+ }
+
+ size_t pos = packet->parsed;
+
+ dbg_packet("Parsing next Answer RR (pos: %zu)...\n", pos);
+ *rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size);
+ if (*rr == NULL) {
+ dbg_packet("Failed to parse RR!\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_packet("Parsed. Pos: %zu.\n", pos);
+
+ packet->parsed = pos;
+ // increment the number of answer RRSets, though there are no saved
+ // in the packet; it is OK, because packet->answer is NULL
+ ++packet->an_rrsets;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
+ knot_rrset_t **rr)
+{
+ /*! \todo Implement. */
+ if (packet == NULL || rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *rr = NULL;
+
+ if (packet->parsed >= packet->size) {
+ assert(packet->ar_rrsets <= packet->header.arcount);
+ if (packet->ar_rrsets != packet->header.arcount) {
+ dbg_packet("Parsed less RRs than expected.\n");
+ return KNOT_EMALF;
+ } else {
+ dbg_packet("Whole packet parsed\n");
+ return KNOT_EOK;
+ }
+ }
+
+ if (packet->ar_rrsets == packet->header.arcount) {
+ assert(packet->parsed < packet->size);
+ dbg_packet("Trailing garbage, ignoring...\n");
+ /*! \todo Do not ignore. */
+ return KNOT_EOK;
+ }
+
+ size_t pos = packet->parsed;
+
+ dbg_packet("Parsing next Additional RR (pos: %zu)...\n", pos);
+ *rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size);
+ if (*rr == NULL) {
+ dbg_packet("Failed to parse RR!\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_packet("Parsed. Pos: %zu.\n", pos);
+
+ packet->parsed = pos;
+ // increment the number of answer RRSets, though there are no saved
+ // in the packet; it is OK, because packet->answer is NULL
+ ++packet->ar_rrsets;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_packet_size(const knot_packet_t *packet)
+{
+ return packet->size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_packet_question_size(const knot_packet_t *packet)
+{
+ return (KNOT_WIRE_HEADER_SIZE + 4
+ + knot_dname_size(packet->question.qname));
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_packet_parsed(const knot_packet_t *packet)
+{
+ return packet->parsed;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_set_max_size(knot_packet_t *packet, int max_size)
+{
+ if (packet == NULL || max_size <= 0) {
+ return KNOT_EBADARG;
+ }
+
+ if (packet->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 KNOT_ENOMEM;
+ }
+
+ uint8_t *wire_old = packet->wireformat;
+
+ memcpy(wire_new, packet->wireformat, packet->max_size);
+ packet->wireformat = wire_new;
+
+ if (packet->max_size > 0 && packet->free_wireformat) {
+ free(wire_old);
+ }
+
+ packet->free_wireformat = 1;
+ }
+
+ // set max size
+ packet->max_size = max_size;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_packet_id(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return packet->header.id;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_id(knot_packet_t *packet, uint16_t id)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+ packet->header.id = id;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_random_id(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+ packet->header.id = knot_random_id();
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_packet_opcode(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return knot_wire_flags_get_opcode(packet->header.flags1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_packet_qname(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ return packet->question.qname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_packet_qtype(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return packet->question.qtype;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_qtype(knot_packet_t *packet, knot_rr_type_t qtype)
+{
+ assert(packet != NULL);
+ packet->question.qtype = qtype;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_packet_qclass(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return packet->question.qclass;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_is_query(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return (knot_wire_flags_get_qr(packet->header.flags1) == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_packet_t *knot_packet_query(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ return packet->query;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_rcode(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_wire_flags_get_rcode(packet->header.flags2);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_tc(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_wire_flags_get_tc(packet->header.flags1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_qdcount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.qdcount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_ancount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.ancount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_nscount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.nscount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_arcount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.arcount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size)
+{
+ packet->tsig_size = tsig_size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_packet_answer_rrset_count(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->an_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_packet_authority_rrset_count(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->ns_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_packet_additional_rrset_count(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->ar_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_packet_answer_rrset(
+ const knot_packet_t *packet, short pos)
+{
+ if (packet == NULL || pos > packet->an_rrsets) {
+ return NULL;
+ }
+
+ return packet->answer[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_packet_authority_rrset(
+ knot_packet_t *packet, short pos)
+{
+ if (packet == NULL || pos > packet->ns_rrsets) {
+ return NULL;
+ }
+
+ return packet->authority[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_packet_additional_rrset(
+ knot_packet_t *packet, short pos)
+{
+ if (packet == NULL || pos > packet->ar_rrsets) {
+ return NULL;
+ }
+
+ return packet->additional[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_contains(const knot_packet_t *packet,
+ const knot_rrset_t *rrset,
+ knot_rrset_compare_type_t cmp)
+{
+ if (packet == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ for (int i = 0; i < packet->header.ancount; ++i) {
+ if (knot_rrset_compare(packet->answer[i], rrset, cmp)) {
+ return 1;
+ }
+ }
+
+ for (int i = 0; i < packet->header.nscount; ++i) {
+ if (knot_rrset_compare(packet->authority[i], rrset, cmp)) {
+ return 1;
+ }
+ }
+
+ for (int i = 0; i < packet->header.arcount; ++i) {
+ if (knot_rrset_compare(packet->additional[i], rrset, cmp)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_add_tmp_rrset(knot_packet_t *packet,
+ knot_rrset_t *tmp_rrset)
+{
+ if (packet == NULL || tmp_rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (packet->tmp_rrsets_count == packet->tmp_rrsets_max
+ && knot_packet_realloc_rrsets(&packet->tmp_rrsets,
+ &packet->tmp_rrsets_max,
+ DEFAULT_RRSET_COUNT(TMP_RRSETS, packet),
+ STEP_TMP_RRSETS) != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ packet->tmp_rrsets[packet->tmp_rrsets_count++] = tmp_rrset;
+ dbg_packet("Current tmp RRSets count: %d, max count: %d\n",
+ packet->tmp_rrsets_count, packet->tmp_rrsets_max);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Frees all temporary RRSets stored in the response structure.
+ *
+ * \param resp Response structure to free the temporary RRSets from.
+ */
+void knot_packet_free_tmp_rrsets(knot_packet_t *pkt)
+{
+ if (pkt == NULL) {
+ return;
+ }
+
+ for (int i = 0; i < pkt->tmp_rrsets_count; ++i) {
+dbg_packet_exec(
+ char *name = knot_dname_to_str(
+ (((knot_rrset_t **)(pkt->tmp_rrsets))[i])->owner);
+ dbg_packet("Freeing tmp RRSet on ptr: %p (ptr to ptr:"
+ " %p, type: %s, owner: %s)\n",
+ (((knot_rrset_t **)(pkt->tmp_rrsets))[i]),
+ &(((knot_rrset_t **)(pkt->tmp_rrsets))[i]),
+ knot_rrtype_to_string(
+ (((knot_rrset_t **)(pkt->tmp_rrsets))[i])->type),
+ name);
+ free(name);
+);
+ // TODO: this is quite ugly, but better than copying whole
+ // function (for reallocating rrset array)
+ knot_rrset_deep_free(
+ &(((knot_rrset_t **)(pkt->tmp_rrsets))[i]), 1, 1, 1);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_header_to_wire(const knot_header_t *header,
+ uint8_t **pos, size_t *size)
+{
+ if (header == NULL || pos == NULL || *pos == NULL || size == NULL) {
+ return;
+ }
+
+ knot_wire_set_id(*pos, header->id);
+ knot_wire_set_flags1(*pos, header->flags1);
+ knot_wire_set_flags2(*pos, header->flags2);
+ knot_wire_set_qdcount(*pos, header->qdcount);
+ knot_wire_set_ancount(*pos, header->ancount);
+ knot_wire_set_nscount(*pos, header->nscount);
+ knot_wire_set_arcount(*pos, header->arcount);
+
+ *pos += KNOT_WIRE_HEADER_SIZE;
+ *size += KNOT_WIRE_HEADER_SIZE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_question_to_wire(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (packet->size > KNOT_WIRE_HEADER_SIZE) {
+ return KNOT_ERROR;
+ }
+
+ // TODO: get rid of the numeric constants
+ size_t qsize = 4 + knot_dname_size(packet->question.qname);
+ if (qsize > packet->max_size - KNOT_WIRE_HEADER_SIZE) {
+ return KNOT_ESPACE;
+ }
+
+ // create the wireformat of Question
+ uint8_t *pos = packet->wireformat + KNOT_WIRE_HEADER_SIZE;
+ memcpy(pos, knot_dname_name(packet->question.qname),
+ knot_dname_size(packet->question.qname));
+
+ pos += knot_dname_size(packet->question.qname);
+ knot_wire_write_u16(pos, packet->question.qtype);
+ pos += 2;
+ knot_wire_write_u16(pos, packet->question.qclass);
+
+// int err = 0;
+ // TODO: put the qname into the compression table
+// // TODO: get rid of the numeric constants
+// if ((err = knot_response_store_dname_pos(&packet->compression,
+// packet->question.qname,0, 12, 12)) != KNOT_EOK) {
+// return err;
+// }
+
+ packet->size += knot_dname_size(packet->question.qname) + 4;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_edns_to_wire(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ packet->size += knot_edns_to_wire(&packet->opt_rr,
+ packet->wireformat + packet->size,
+ packet->max_size - packet->size);
+
+ packet->header.arcount += 1;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_to_wire(knot_packet_t *packet,
+ uint8_t **wire, size_t *wire_size)
+{
+ if (packet == NULL || wire == NULL || wire_size == NULL
+ || *wire != NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(packet->size <= packet->max_size);
+
+ // if there are no additional RRSets, add EDNS OPT RR
+ if (packet->header.arcount == 0
+ && packet->opt_rr.version != EDNS_NOT_SUPPORTED) {
+ knot_packet_edns_to_wire(packet);
+ }
+
+ // set QDCOUNT (in response it is already set, in query it is needed)
+ knot_wire_set_qdcount(packet->wireformat, packet->header.qdcount);
+ // set ANCOUNT to the packet
+ knot_wire_set_ancount(packet->wireformat, packet->header.ancount);
+ // set NSCOUNT to the packet
+ knot_wire_set_nscount(packet->wireformat, packet->header.nscount);
+ // set ARCOUNT to the packet
+ knot_wire_set_arcount(packet->wireformat, packet->header.arcount);
+
+ //assert(response->size == size);
+ *wire = packet->wireformat;
+ *wire_size = packet->size;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const uint8_t *knot_packet_wireformat(const knot_packet_t *packet)
+{
+ return packet->wireformat;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_free(knot_packet_t **packet)
+{
+ if (packet == NULL || *packet == NULL) {
+ return;
+ }
+
+ // free temporary domain names
+ dbg_packet("Freeing tmp RRSets...\n");
+ knot_packet_free_tmp_rrsets(*packet);
+
+ // check if some additional space was allocated for the packet
+ dbg_packet("Freeing additional allocated space...\n");
+ knot_packet_free_allocated_space(*packet);
+
+ // free the space for wireformat
+// assert((*packet)->wireformat != NULL);
+// free((*packet)->wireformat);
+ if ((*packet)->wireformat != NULL && (*packet)->free_wireformat) {
+ free((*packet)->wireformat);
+ }
+
+ dbg_packet("Freeing packet structure\n");
+ free(*packet);
+ *packet = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+#ifdef KNOT_PACKET_DEBUG
+static void knot_packet_dump_rrsets(const knot_rrset_t **rrsets,
+ int count)
+{
+ assert((rrsets != NULL && *rrsets != NULL) || count < 1);
+
+ for (int i = 0; i < count; ++i) {
+ knot_rrset_dump(rrsets[i], 0);
+ }
+}
+#endif
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_dump(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+#ifdef KNOT_PACKET_DEBUG
+ dbg_packet("DNS packet:\n-----------------------------\n");
+
+ dbg_packet("\nHeader:\n");
+ dbg_packet(" ID: %u", packet->header.id);
+ dbg_packet(" FLAGS: %s %s %s %s %s %s %s\n",
+ knot_wire_flags_get_qr(packet->header.flags1) ? "qr" : "",
+ knot_wire_flags_get_aa(packet->header.flags1) ? "aa" : "",
+ knot_wire_flags_get_tc(packet->header.flags1) ? "tc" : "",
+ knot_wire_flags_get_rd(packet->header.flags1) ? "rd" : "",
+ knot_wire_flags_get_ra(packet->header.flags2) ? "ra" : "",
+ knot_wire_flags_get_ad(packet->header.flags2) ? "ad" : "",
+ knot_wire_flags_get_cd(packet->header.flags2) ? "cd" : "");
+ dbg_packet(" QDCOUNT: %u\n", packet->header.qdcount);
+ dbg_packet(" ANCOUNT: %u\n", packet->header.ancount);
+ dbg_packet(" NSCOUNT: %u\n", packet->header.nscount);
+ dbg_packet(" ARCOUNT: %u\n", packet->header.arcount);
+
+ if (knot_packet_qdcount(packet) > 0) {
+ dbg_packet("\nQuestion:\n");
+ char *qname = knot_dname_to_str(packet->question.qname);
+ dbg_packet(" QNAME: %s\n", qname);
+ free(qname);
+ dbg_packet(" QTYPE: %u (%s)\n", packet->question.qtype,
+ knot_rrtype_to_string(packet->question.qtype));
+ dbg_packet(" QCLASS: %u (%s)\n", packet->question.qclass,
+ knot_rrclass_to_string(packet->question.qclass));
+ }
+
+ dbg_packet("\nAnswer RRSets:\n");
+ knot_packet_dump_rrsets(packet->answer, packet->an_rrsets);
+ dbg_packet("\nAuthority RRSets:\n");
+ knot_packet_dump_rrsets(packet->authority, packet->ns_rrsets);
+ dbg_packet("\nAdditional RRSets:\n");
+ knot_packet_dump_rrsets(packet->additional, packet->ar_rrsets);
+
+ /*! \todo Dumping of Answer, Authority and Additional sections. */
+
+ dbg_packet("\nEDNS:\n");
+ dbg_packet(" Version: %u\n", packet->opt_rr.version);
+ dbg_packet(" Payload: %u\n", packet->opt_rr.payload);
+ dbg_packet(" Extended RCODE: %u\n",
+ packet->opt_rr.ext_rcode);
+
+ dbg_packet("\nPacket size: %zu\n", packet->size);
+ dbg_packet("\n-----------------------------\n");
+#endif
+}
+