summaryrefslogtreecommitdiff
path: root/src/libknot/rrset.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot/rrset.c')
-rw-r--r--src/libknot/rrset.c719
1 files changed, 719 insertions, 0 deletions
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
new file mode 100644
index 0000000..6083f77
--- /dev/null
+++ b/src/libknot/rrset.c
@@ -0,0 +1,719 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "rrset.h"
+#include "util/descriptor.h"
+#include "util/error.h"
+#include "util/utils.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+static void knot_rrset_disconnect_rdata(knot_rrset_t *rrset,
+ knot_rdata_t *prev, knot_rdata_t *rdata)
+{
+ if (prev == NULL) {
+ // find the previous RDATA in the series, as its pointer must
+ // be changed
+ prev = rdata->next;
+ while (prev->next != rdata) {
+ prev = prev->next;
+ }
+ }
+
+ assert(prev);
+ assert(prev->next == rdata);
+
+ prev->next = rdata->next;
+
+ if (rrset->rdata == rdata) {
+ if (rdata->next == rdata) {
+ rrset->rdata = NULL;
+ } else {
+ rrset->rdata = rdata->next;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
+ uint16_t rclass, uint32_t ttl)
+{
+ knot_rrset_t *ret = malloc(sizeof(knot_rrset_t));
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ ret->rdata = NULL;
+
+ /* Retain reference to owner. */
+ knot_dname_retain(owner);
+
+ ret->owner = owner;
+ ret->type = type;
+ ret->rclass = rclass;
+ ret->ttl = ttl;
+ ret->rrsigs = NULL;
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_add_rdata(knot_rrset_t *rrset, knot_rdata_t *rdata)
+{
+ if (rrset == NULL || rdata == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (rrset->rdata == NULL) {
+ rrset->rdata = rdata;
+ rrset->rdata->next = rrset->rdata;
+ } else {
+ knot_rdata_t *tmp;
+
+ tmp = rrset->rdata;
+
+ while (tmp->next != rrset->rdata) {
+ tmp = tmp->next;
+ }
+ rdata->next = tmp->next;
+ tmp->next = rdata;
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rrset_remove_rdata(knot_rrset_t *rrset,
+ const knot_rdata_t *rdata)
+{
+ if (rrset == NULL || rdata == NULL) {
+ return NULL;
+ }
+
+ knot_rdata_t *prev = NULL;
+ knot_rdata_t *rr = rrset->rdata;
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ if (desc == NULL) {
+ return NULL;
+ }
+
+ while (rr != NULL) {
+ /*! \todo maybe the dnames should be compared case-insensitive*/
+ if (knot_rdata_compare(rr, rdata, desc->wireformat) == 0) {
+ knot_rrset_disconnect_rdata(rrset, prev, rr);
+ return rr;
+ }
+ prev = rr;
+ rr = knot_rrset_rdata_get_next(rrset, rr);
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_set_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs)
+{
+ if (rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ rrset->rrsigs = rrsigs;
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
+ knot_rrset_dupl_handling_t dupl)
+{
+ if (rrset == NULL || rrsigs == NULL
+ || knot_dname_compare(rrset->owner, rrsigs->owner) != 0) {
+ return KNOT_EBADARG;
+ }
+
+ int rc;
+ if (rrset->rrsigs != NULL) {
+ if (dupl == KNOT_RRSET_DUPL_MERGE) {
+ rc = knot_rrset_merge((void **)&rrset->rrsigs,
+ (void **)&rrsigs);
+ if (rc != KNOT_EOK) {
+ return rc;
+ } else {
+ return 1;
+ }
+ } else if (dupl == KNOT_RRSET_DUPL_SKIP) {
+ return 2;
+ } else if (dupl == KNOT_RRSET_DUPL_REPLACE) {
+ rrset->rrsigs = rrsigs;
+ }
+ } else {
+ rrset->rrsigs = rrsigs;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_rrset_owner(const knot_rrset_t *rrset)
+{
+ return rrset->owner;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset)
+{
+ return rrset->owner;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner)
+{
+ if (rrset) {
+ /* Retain new owner and release old owner. */
+ knot_dname_retain(owner);
+ knot_dname_release(rrset->owner);
+ rrset->owner = owner;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_rrset_type(const knot_rrset_t *rrset)
+{
+ return rrset->type;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_rrset_class(const knot_rrset_t *rrset)
+{
+ return rrset->rclass;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint32_t knot_rrset_ttl(const knot_rrset_t *rrset)
+{
+ return rrset->ttl;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rdata_t *knot_rrset_rdata(const knot_rrset_t *rrset)
+{
+ return rrset->rdata;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rdata_t *knot_rrset_rdata_next(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata)
+{
+ if (rdata == NULL) {
+ return rrset->rdata;
+ }
+ if (rdata->next == rrset->rdata) {
+ return NULL;
+ } else {
+ return rdata->next;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rrset_get_rdata(knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ return NULL;
+ } else {
+ return rrset->rdata;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rrset_rdata_get_next(knot_rrset_t *rrset,
+ knot_rdata_t *rdata)
+{
+ if (rdata->next == rrset->rdata) {
+ return NULL;
+ } else {
+ return rdata->next;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_rdata_rr_count(const knot_rrset_t *rrset)
+{
+ int count = 0;
+ const knot_rdata_t *rdata = rrset->rdata;
+
+ while (rdata != NULL) {
+ ++count;
+ rdata = knot_rrset_rdata_next(rrset, rdata);
+ }
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_rrset_rrsigs(const knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ assert(0);
+ return NULL;
+ } else {
+ return rrset->rrsigs;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_rrset_get_rrsigs(knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ assert(0);
+ return NULL;
+ } else {
+ return rrset->rrsigs;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_compare_rdata(const knot_rrset_t *r1, const knot_rrset_t *r2)
+{
+ if (r1 == NULL || r2 == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(r1->type);
+ if (desc == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // compare RDATA sets (order is not significant)
+ const knot_rdata_t *rdata1= knot_rrset_rdata(r1);
+ const knot_rdata_t *rdata2;
+
+ // find all RDATA from r1 in r2
+ while (rdata1 != NULL) {
+ rdata2 = knot_rrset_rdata(r2);
+ while (rdata2 != NULL && knot_rdata_compare(rdata1, rdata2,
+ desc->wireformat)) {
+ rdata2 = knot_rrset_rdata_next(r2, rdata2);
+ }
+
+ if (rdata2 == NULL) {
+ // RDATA from r1 not found in r2
+ return 0;
+ }
+
+ // otherwise it was found, continue with next r1 RDATA
+ rdata1 = knot_rrset_rdata_next(r1, rdata1);
+ }
+
+ // find all RDATA from r2 in r1 (this can be done in a better way)
+ rdata2 = knot_rrset_rdata(r2);
+ while (rdata2 != NULL) {
+ rdata1 = knot_rrset_rdata(r1);
+ while (rdata2 != NULL && knot_rdata_compare(rdata1, rdata2,
+ desc->wireformat)) {
+ rdata1 = knot_rrset_rdata_next(r1, rdata1);
+ }
+
+ if (rdata1 == NULL) {
+ // RDATA from r1 not found in r2
+ return 0;
+ }
+
+ // otherwise it was found, continue with next r1 RDATA
+ rdata2 = knot_rrset_rdata_next(r2, rdata2);
+ }
+
+ // all RDATA found
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata, uint8_t **pos,
+ size_t max_size)
+{
+ int size = 0;
+
+ assert(rrset != NULL);
+ assert(rrset->owner != NULL);
+ assert(rdata != NULL);
+ assert(pos != NULL);
+ assert(*pos != NULL);
+
+ fprintf(stderr, "Max size: %zu, owner: %p, owner size: %d\n",
+ max_size, rrset->owner, rrset->owner->size);
+
+ // check if owner fits
+ if (size + knot_dname_size(rrset->owner) + 10 > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(*pos, knot_dname_name(rrset->owner),
+ knot_dname_size(rrset->owner));
+ *pos += knot_dname_size(rrset->owner);
+ size += knot_dname_size(rrset->owner);
+
+ fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+
+ fprintf(stderr, "Wire format:\n");
+
+ // put rest of RR 'header'
+ knot_wire_write_u16(*pos, rrset->type);
+ fprintf(stderr, " Type: %u\n", rrset->type);
+ *pos += 2;
+
+ knot_wire_write_u16(*pos, rrset->rclass);
+ fprintf(stderr, " Class: %u\n", rrset->rclass);
+ *pos += 2;
+
+ knot_wire_write_u32(*pos, rrset->ttl);
+ fprintf(stderr, " TTL: %u\n", rrset->ttl);
+ *pos += 4;
+
+ // save space for RDLENGTH
+ uint8_t *rdlength_pos = *pos;
+ *pos += 2;
+
+ size += 10;
+// compr->wire_pos += size;
+
+ fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ uint16_t rdlength = 0;
+
+ for (int i = 0; i < rdata->count; ++i) {
+ if (max_size < size + rdlength) {
+ return KNOT_ESPACE;
+ }
+
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME: {
+ knot_dname_t *dname =
+ knot_rdata_item(rdata, i)->dname;
+ if (size + rdlength + dname->size > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // save whole domain name
+ memcpy(*pos, knot_dname_name(dname),
+ knot_dname_size(dname));
+ fprintf(stderr, "Uncompressed dname size: %d\n",
+ knot_dname_size(dname));
+ *pos += knot_dname_size(dname);
+ rdlength += knot_dname_size(dname);
+// compr->wire_pos += dname->size;
+ break;
+ }
+ default: {
+ uint16_t *raw_data =
+ knot_rdata_item(rdata, i)->raw_data;
+
+ if (size + rdlength + raw_data[0] > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // copy just the rdata item data (without size)
+ memcpy(*pos, raw_data + 1, raw_data[0]);
+ fprintf(stderr, "Raw data size: %d\n", raw_data[0]);
+ *pos += raw_data[0];
+ rdlength += raw_data[0];
+// compr->wire_pos += raw_data[0];
+ break;
+ }
+ }
+ }
+
+ fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+
+ assert(size + rdlength <= max_size);
+ size += rdlength;
+ knot_wire_write_u16(rdlength_pos, rdlength);
+
+ return size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
+ int *rr_count)
+{
+ // if no RDATA in RRSet, return
+ if (rrset->rdata == NULL) {
+ *size = 0;
+ *rr_count = 0;
+ return KNOT_EOK;
+ }
+
+
+ uint8_t *pos = wire;
+ int rrs = 0;
+ short rrset_size = 0;
+
+ const knot_rdata_t *rdata = rrset->rdata;
+ do {
+ int ret = knot_rrset_rr_to_wire(rrset, rdata, &pos,
+ *size - rrset_size);
+
+ assert(ret != 0);
+
+ if (ret < 0) {
+ // some RR didn't fit in, so no RRs should be used
+ // TODO: remove last entries from compression table
+ fprintf(stderr, "Some RR didn't fit in.\n");
+ return KNOT_ESPACE;
+ }
+
+ fprintf(stderr, "RR of size %d added.\n", ret);
+ rrset_size += ret;
+ ++rrs;
+ } while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL);
+
+ // the whole RRSet did fit in
+ assert(rrset_size <= *size);
+ assert(pos - wire == rrset_size);
+ *size = rrset_size;
+
+ fprintf(stderr, " Size after: %zu\n", *size);
+
+ *rr_count = rrs;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_compare(const knot_rrset_t *r1,
+ const knot_rrset_t *r2,
+ knot_rrset_compare_type_t cmp)
+{
+ if (cmp == KNOT_RRSET_COMPARE_PTR) {
+ return (r1 == r2);
+ }
+
+ int res = ((r1->rclass == r2->rclass)
+ && (r1->type == r2->type)
+ && (r1->ttl == r2->ttl)
+ && knot_dname_compare(r1->owner, r2->owner) == 0);
+
+ if (cmp == KNOT_RRSET_COMPARE_WHOLE && res) {
+ res = knot_rrset_compare_rdata(r1, r2);
+ if (res < 0) {
+ return 0;
+ }
+ }
+
+ return res;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to)
+{
+ if (from == NULL || to == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int ret;
+
+ *to = (knot_rrset_t *)calloc(1, sizeof(knot_rrset_t));
+ CHECK_ALLOC_LOG(*to, KNOT_ENOMEM);
+
+ (*to)->owner = knot_dname_deep_copy(from->owner);
+ (*to)->rclass = from->rclass;
+ (*to)->ttl = from->ttl;
+ (*to)->type = from->type;
+ if (from->rrsigs != NULL) {
+ ret = knot_rrset_deep_copy(from->rrsigs, &(*to)->rrsigs);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(to, 1, 0, 0);
+ return ret;
+ }
+ }
+ assert((*to)->rrsigs == NULL || from->rrsigs != NULL);
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(from);
+
+ /*! \note Order of RDATA will be reversed. */
+ while (rdata != NULL) {
+ ret = knot_rrset_add_rdata(*to, knot_rdata_deep_copy(rdata,
+ knot_rrset_type(from)));
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(to, 1, 1, 1);
+ return ret;
+ }
+ rdata = knot_rrset_rdata_next(from, rdata);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to)
+{
+ *to = (knot_rrset_t *)malloc(sizeof(knot_rrset_t));
+ CHECK_ALLOC_LOG(*to, KNOT_ENOMEM);
+
+ memcpy(*to, from, sizeof(knot_rrset_t));
+
+ /* Retain owner. */
+ knot_dname_retain((*to)->owner);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rrset_free(knot_rrset_t **rrset)
+{
+ if (rrset == NULL || *rrset == NULL) {
+ return;
+ }
+
+ /*! \todo Shouldn't we always release owner reference? */
+ knot_dname_release((*rrset)->owner);
+
+ free(*rrset);
+ *rrset = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
+ int free_rdata, int free_rdata_dnames)
+{
+ if (rrset == NULL || *rrset == NULL) {
+ return;
+ }
+
+ if (free_rdata) {
+ knot_rdata_t *tmp_rdata;
+ knot_rdata_t *next_rdata;
+ tmp_rdata = (*rrset)->rdata;
+
+ while ((tmp_rdata != NULL)
+ && (tmp_rdata->next != (*rrset)->rdata)
+ && (tmp_rdata->next != NULL)) {
+ next_rdata = tmp_rdata->next;
+ knot_rdata_deep_free(&tmp_rdata, (*rrset)->type,
+ free_rdata_dnames);
+ tmp_rdata = next_rdata;
+ }
+
+ assert(tmp_rdata == NULL
+ || tmp_rdata->next == (*rrset)->rdata);
+
+ knot_rdata_deep_free(&tmp_rdata, (*rrset)->type,
+ free_rdata_dnames);
+ }
+
+ // RRSIGs should have the same owner as this RRSet, so do not delete it
+ if ((*rrset)->rrsigs != NULL) {
+ knot_rrset_deep_free(&(*rrset)->rrsigs, 0, 1,
+ free_rdata_dnames);
+ }
+
+ /*! \todo Release owner every time? */
+ //if (free_owner) {
+ knot_dname_release((*rrset)->owner);
+ //}
+
+ free(*rrset);
+ *rrset = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_merge(void **r1, void **r2)
+{
+ knot_rrset_t *rrset1 = (knot_rrset_t *)(*r1);
+ knot_rrset_t *rrset2 = (knot_rrset_t *)(*r2);
+
+ if ((knot_dname_compare(rrset1->owner, rrset2->owner) != 0)
+ || rrset1->rclass != rrset2->rclass
+ || rrset1->type != rrset2->type
+ || rrset1->ttl != rrset2->ttl) {
+ return KNOT_EBADARG;
+ }
+
+ // add all RDATAs from rrset2 to rrset1 (i.e. concatenate linked lists)
+
+ // no RDATA in RRSet 1
+ assert(rrset1 && rrset2);
+ if (rrset1->rdata == NULL) {
+ rrset1->rdata = rrset2->rdata;
+ return KNOT_EOK;
+ }
+
+ knot_rdata_t *tmp_rdata = rrset1->rdata;
+
+ if (!tmp_rdata) {
+ return KNOT_EOK;
+ }
+
+ while (tmp_rdata->next != rrset1->rdata) {
+ tmp_rdata = tmp_rdata->next;
+ }
+
+ tmp_rdata->next = rrset2->rdata;
+
+ tmp_rdata = rrset2->rdata; //maybe unnecessary, but is clearer
+
+ while (tmp_rdata->next != rrset2->rdata) {
+ tmp_rdata = tmp_rdata->next;
+ }
+
+ tmp_rdata->next = rrset1->rdata;
+
+ return KNOT_EOK;
+}