summaryrefslogtreecommitdiff
path: root/src/libknot
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot')
-rw-r--r--src/libknot/consts.h3
-rw-r--r--src/libknot/dname.c41
-rw-r--r--src/libknot/dname.h17
-rw-r--r--src/libknot/hash/cuckoo-hash-table.c9
-rw-r--r--src/libknot/nameserver/name-server.c395
-rw-r--r--src/libknot/nameserver/name-server.h32
-rw-r--r--src/libknot/packet/packet.c41
-rw-r--r--src/libknot/packet/packet.h4
-rw-r--r--src/libknot/packet/query.h2
-rw-r--r--src/libknot/packet/response.c37
-rw-r--r--src/libknot/packet/response.h3
-rw-r--r--src/libknot/rdata.c56
-rw-r--r--src/libknot/rdata.h5
-rw-r--r--src/libknot/rrset.c17
-rw-r--r--src/libknot/rrset.h6
-rw-r--r--src/libknot/tsig-op.c27
-rw-r--r--src/libknot/updates/changesets.c356
-rw-r--r--src/libknot/updates/changesets.h59
-rw-r--r--src/libknot/updates/ddns.c1916
-rw-r--r--src/libknot/updates/ddns.h23
-rw-r--r--src/libknot/updates/xfr-in.c961
-rw-r--r--src/libknot/updates/xfr-in.h20
-rw-r--r--src/libknot/util/debug.h10
-rw-r--r--src/libknot/util/descriptor.c25
-rw-r--r--src/libknot/util/descriptor.h37
-rw-r--r--src/libknot/zone/zone-contents.c169
-rw-r--r--src/libknot/zone/zone-diff.c124
27 files changed, 3577 insertions, 818 deletions
diff --git a/src/libknot/consts.h b/src/libknot/consts.h
index 4249763..3431bc3 100644
--- a/src/libknot/consts.h
+++ b/src/libknot/consts.h
@@ -57,7 +57,8 @@ typedef enum knot_packet_type {
KNOT_RESPONSE_NORMAL, /*!< Normal response. */
KNOT_RESPONSE_AXFR, /*!< AXFR transfer response. */
KNOT_RESPONSE_IXFR, /*!< IXFR transfer response. */
- KNOT_RESPONSE_NOTIFY /*!< NOTIFY response. */
+ KNOT_RESPONSE_NOTIFY, /*!< NOTIFY response. */
+ KNOT_RESPONSE_UPDATE /*!< Dynamic update response. */
} knot_packet_type_t;
/*
diff --git a/src/libknot/dname.c b/src/libknot/dname.c
index eed2fd6..5c7e961 100644
--- a/src/libknot/dname.c
+++ b/src/libknot/dname.c
@@ -178,8 +178,13 @@ static int knot_dname_str_to_wire(const char *name, uint size,
while (ch - (const uint8_t *)name < size) {
assert(w - wire - 1 == ch - (const uint8_t *)name);
-
+
if (*ch == '.') {
+ /* Zero-length label inside a dname - invalid. */
+ if (label_length == 0) {
+ free(wire);
+ return -1;
+ }
dbg_dname_detail("Position %zd (%p): "
"label length: %u\n",
label_start - wire,
@@ -262,6 +267,11 @@ static int knot_dname_find_labels(knot_dname_t *dname, int alloc)
short label_count = 0;
while (pos - name < size && *pos != '\0' && label_count < KNOT_MAX_DNAME_LABELS ) {
+ if (*pos > 63) { /* Check label lengths. */
+ dbg_dname("Wrong wire format of domain name!\n");
+ dbg_dname("Label %d exceeds 63 bytes.\n", label_count);
+ return -1;
+ }
labels[label_count++] = pos - name;
pos += *pos + 1;
}
@@ -471,8 +481,9 @@ knot_dname_t *knot_dname_new_from_wire(const uint8_t *name, uint size,
/*----------------------------------------------------------------------------*/
knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
- size_t *pos, size_t size,
- knot_node_t *node)
+ size_t *pos, size_t size,
+ knot_node_t *node,
+ knot_dname_t *dname)
{
uint8_t name[KNOT_MAX_DNAME_LENGTH];
uint8_t labels[KNOT_MAX_DNAME_LABELS];
@@ -539,34 +550,30 @@ knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
*pos += 1;
}
- knot_dname_t *dname = knot_dname_new();
+ /* Allocate if NULL. */
+ if (dname == NULL) {
+ dname = knot_dname_new();
+ if (dname) {
+ dname->name = (uint8_t *)malloc((i + 1) * sizeof(uint8_t));
+ dname->labels = (uint8_t *)malloc((l + 1) * sizeof(uint8_t));
+ }
+ }
if (dname == NULL) {
ERR_ALLOC_FAILED;
return NULL;
}
- dname->name = (uint8_t *)malloc((i + 1) * sizeof(uint8_t));
- if (dname->name == NULL) {
+ if (dname->name == NULL || dname->labels == NULL) {
ERR_ALLOC_FAILED;
knot_dname_free(&dname);
return NULL;
}
memcpy(dname->name, name, i + 1);
- dname->size = i + 1;
-
- /*! \todo Why l + 1 ?? */
- dname->labels = (uint8_t *)malloc((l + 1) * sizeof(uint8_t));
- if (dname->labels == NULL) {
- ERR_ALLOC_FAILED;
- knot_dname_free(&dname);
- return NULL;
- }
memcpy(dname->labels, labels, l + 1);
-
+ dname->size = i + 1;
dname->label_count = l;
-
dname->node = node;
return dname;
diff --git a/src/libknot/dname.h b/src/libknot/dname.h
index 347e699..6f649ce 100644
--- a/src/libknot/dname.h
+++ b/src/libknot/dname.h
@@ -124,9 +124,22 @@ knot_dname_t *knot_dname_new_from_wire(const uint8_t *name,
unsigned int size,
struct knot_node *node);
+/*!
+ * \brief Parse dname from wire.
+ *
+ * \param wire Message in wire format.
+ * \param pos Position of the domain name on wire.
+ * \param size Domain name length.
+ * \param node Zone node the domain name belongs to. Set to NULL if not
+ * applicable.
+ * \param dname Destination dname (will allocate new when NULL).
+ *
+ * \return parsed domain name or NULL.
+ */
knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
- size_t *pos, size_t size,
- struct knot_node *node);
+ size_t *pos, size_t size,
+ struct knot_node *node,
+ knot_dname_t *dname);
/*!
* \brief Initializes domain name by the name given in wire format.
diff --git a/src/libknot/hash/cuckoo-hash-table.c b/src/libknot/hash/cuckoo-hash-table.c
index 7358e14..7d454a9 100644
--- a/src/libknot/hash/cuckoo-hash-table.c
+++ b/src/libknot/hash/cuckoo-hash-table.c
@@ -1434,7 +1434,7 @@ int ck_deep_copy(ck_hash_table_t *from, ck_hash_table_t **to)
return -2;
}
- dbg_ck_detail("Copying stash item: %p with item %p, ", si,
+ dbg_ck_detail("Copying stash item: %p with item %p.\n", si,
si->item);
if (si->item == NULL) {
@@ -1473,9 +1473,10 @@ dbg_ck_exec_detail(
(si_new) ? si_new->item : NULL);
assert(si_new != NULL);
- assert(si_new->item != NULL);
- dbg_ck_detail("key: %.*s\n", (int)si_new->item->key_length,
- si_new->item->key);
+ if (si_new->item != NULL) {
+ dbg_ck_detail("key: %.*s\n", (int)si_new->item->key_length,
+ si_new->item->key);
+ }
);
}
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
index 0a7a298..8238d7e 100644
--- a/src/libknot/nameserver/name-server.c
+++ b/src/libknot/nameserver/name-server.c
@@ -2950,7 +2950,7 @@ static int ns_ixfr(knot_ns_xfr_t *xfr)
/*----------------------------------------------------------------------------*/
static int knot_ns_prepare_response(knot_packet_t *query, knot_packet_t **resp,
- size_t max_size)
+ size_t max_size, int copy_question)
{
assert(max_size >= 500);
@@ -2969,7 +2969,7 @@ static int knot_ns_prepare_response(knot_packet_t *query, knot_packet_t **resp,
return ret;
}
- ret = knot_response_init_from_query(*resp, query);
+ ret = knot_response_init_from_query(*resp, query, copy_question);
if (ret != KNOT_EOK) {
dbg_ns("Failed to init response structure.\n");
@@ -3207,7 +3207,7 @@ int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
if(knot_packet_is_query(packet)) {
*type = KNOT_QUERY_UPDATE;
} else {
- return KNOT_RCODE_FORMERR;
+ *type = KNOT_RESPONSE_UPDATE;
}
break;
default:
@@ -3437,7 +3437,7 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
resp_max_size = MAX_UDP_PAYLOAD;
}
- ret = knot_ns_prepare_response(query, resp, resp_max_size);
+ ret = knot_ns_prepare_response(query, resp, resp_max_size, 1);
if (ret != KNOT_EOK) {
return KNOT_ERROR;
}
@@ -3487,6 +3487,143 @@ dbg_ns_exec_verb(
/*----------------------------------------------------------------------------*/
+int knot_ns_prep_update_response(knot_nameserver_t *nameserver,
+ knot_packet_t *query, knot_packet_t **resp,
+ knot_zone_t **zone, size_t max_size)
+{
+ dbg_ns_verb("knot_ns_prep_update_response()\n");
+
+ if (nameserver == NULL || query == NULL || resp == NULL
+ || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // first, parse the rest of the packet
+ assert(knot_packet_is_query(query));
+ dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n",
+ knot_packet_parsed(query), knot_packet_size(query));
+ int ret;
+
+ ret = knot_packet_parse_rest(query);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to parse rest of the query: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /*
+ * Semantic checks
+ *
+ * Check the QDCOUNT and in case of anything but 1 send back
+ * FORMERR
+ */
+ if (knot_packet_qdcount(query) != 1) {
+ dbg_ns("QDCOUNT != 1. Reply FORMERR.\n");
+ return KNOT_EMALF;
+ }
+
+ /*
+ * Check what is in the Additional section. Only OPT and TSIG are
+ * allowed. TSIG must be the last record if present.
+ */
+ /*! \todo Put to separate function - used in prep_normal_response(). */
+ if (knot_packet_arcount(query) > 0) {
+ int ok = 0;
+ const knot_rrset_t *add1 =
+ knot_packet_additional_rrset(query, 0);
+ if (knot_packet_additional_rrset_count(query) == 1
+ && (knot_rrset_type(add1) == KNOT_RRTYPE_OPT
+ || knot_rrset_type(add1) == KNOT_RRTYPE_TSIG)) {
+ ok = 1;
+ } else if (knot_packet_additional_rrset_count(query) == 2) {
+ const knot_rrset_t *add2 =
+ knot_packet_additional_rrset(query, 1);
+ if (knot_rrset_type(add1) == KNOT_RRTYPE_OPT
+ && knot_rrset_type(add2) == KNOT_RRTYPE_TSIG) {
+ ok = 1;
+ }
+ }
+
+ if (!ok) {
+ dbg_ns("Additional section malformed. Reply FORMERR\n");
+ return KNOT_EMALF;
+ }
+ }
+
+ size_t resp_max_size = 0;
+
+ knot_packet_dump(query);
+
+ /*! \todo Put to separate function - used in prep_normal_response(). */
+ if (max_size > 0) {
+ // if TCP is used, buffer size is the only constraint
+ assert(max_size > 0);
+ resp_max_size = max_size;
+ } else if (knot_query_edns_supported(query)) {
+ assert(max_size == 0);
+ if (knot_edns_get_payload(&query->opt_rr) <
+ knot_edns_get_payload(nameserver->opt_rr)) {
+ resp_max_size = knot_edns_get_payload(&query->opt_rr);
+ } else {
+ resp_max_size = knot_edns_get_payload(
+ nameserver->opt_rr);
+ }
+ }
+
+ if (resp_max_size < MAX_UDP_PAYLOAD) {
+ resp_max_size = MAX_UDP_PAYLOAD;
+ }
+
+ ret = knot_ns_prepare_response(query, resp, resp_max_size, 0);
+ if (ret != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+ dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n",
+ query->parsed, query->size);
+ dbg_ns_detail("Opt RR: version: %d, payload: %d\n",
+ query->opt_rr.version, query->opt_rr.payload);
+
+ // get the answer for the query
+ knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
+
+ dbg_ns_detail("EDNS supported in query: %d\n",
+ knot_query_edns_supported(query));
+
+ // set the OPT RR to the response
+ if (knot_query_edns_supported(query)) {
+ ret = knot_response_add_opt(*resp, nameserver->opt_rr, 1,
+ knot_query_nsid_requested(query));
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to set OPT RR to the response"
+ ": %s\n", knot_strerror(ret));
+ } else {
+ // copy the DO bit from the query
+ if (knot_query_dnssec_requested(query)) {
+ knot_edns_set_do(&(*resp)->opt_rr);
+ }
+ }
+ }
+
+ dbg_ns_verb("Response max size: %zu\n", (*resp)->max_size);
+
+ const knot_dname_t *qname = knot_packet_qname(knot_packet_query(*resp));
+ assert(qname != NULL);
+
+// uint16_t qtype = knot_packet_qtype(*resp);
+dbg_ns_exec_verb(
+ char *name_str = knot_dname_to_str(qname);
+ dbg_ns_verb("Trying to find zone %s\n", name_str);
+ free(name_str);
+);
+ // find zone
+ *zone = knot_zonedb_find_zone(zonedb, qname);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
int knot_ns_answer_normal(knot_nameserver_t *nameserver,
const knot_zone_t *zone, knot_packet_t *resp,
uint8_t *response_wire, size_t *rsize, int check_any)
@@ -3619,7 +3756,7 @@ dbg_ns_exec_verb(
response->wireformat = xfr->wire;
response->max_size = xfr->wire_size;
- ret = knot_response_init_from_query(response, xfr->query);
+ ret = knot_response_init_from_query(response, xfr->query, 1);
if (ret != KNOT_EOK) {
dbg_ns("Failed to init response structure.\n");
@@ -3796,8 +3933,9 @@ int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
if (ret < 0 && ret != KNOT_ECONN) {
dbg_ns("AXFR failed, sending SERVFAIL.\n");
// now only one type of error (SERVFAIL), later maybe more
- /*! \todo xfr->wire is not NULL, will fail on assert! */
- knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
+ knot_ns_error_response_from_query(nameserver, xfr->query,
+ KNOT_RCODE_SERVFAIL,
+ xfr->wire, &xfr->wire_size);
ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
xfr->wire_size);
} else if (ret > 0) {
@@ -4076,143 +4214,140 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
}
/*----------------------------------------------------------------------------*/
-
-int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
- uint8_t *response_wire, size_t *rsize,
- knot_zone_t **zone, knot_changeset_t **changeset)
+/*
+ * The given query is already fully parsed. But the parameter contains an
+ * already prepared response structure.
+ *
+ * This function should process the contents, prepare prerequisities, prepare
+ * changeset and return to the caller.
+ */
+int knot_ns_process_update(const knot_packet_t *query,
+ const knot_zone_contents_t *zone,
+ knot_changeset_t *changeset, knot_rcode_t *rcode)
{
- // 1) Parse the rest of the packet
assert(knot_packet_is_query(query));
- knot_packet_t *response;
- assert(*rsize >= MAX_UDP_PAYLOAD);
- int ret = knot_ns_prepare_response(query, &response, MAX_UDP_PAYLOAD);
- if (ret != KNOT_EOK) {
- knot_ns_error_response_from_query(nameserver, query,
- KNOT_RCODE_SERVFAIL,
- response_wire, rsize);
- return KNOT_EOK;
- }
-
- assert(response != NULL);
+ dbg_ns("Processing Dynamic Update.\n");
- dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n",
- query->parsed, query->size);
-
- if (knot_packet_parsed(query) < knot_packet_size(query)) {
- ret = knot_packet_parse_rest(query);
- if (ret != KNOT_EOK) {
- dbg_ns("Failed to parse rest of the query: "
- "%s.\n", knot_strerror(ret));
- knot_ns_error_response_full(nameserver, response,
- (ret == KNOT_EMALF)
- ? KNOT_RCODE_FORMERR
- : KNOT_RCODE_SERVFAIL,
- response_wire, rsize);
- knot_packet_free(&response);
- return KNOT_EOK;
- }
+ *rcode = KNOT_RCODE_NOERROR;
+
+ // 1) Check zone
+ // Already done
+// dbg_ns_verb("Checking zone for DDNS.\n");
+// int ret = knot_ddns_check_zone(zone, query, rcode);
+// if (ret != KNOT_EOK) {
+// dbg_ns("Failed to check zone for update: "
+// "%s.\n", knot_strerror(ret));
+// return ret;
+// }
+
+ // 2) Convert prerequisities
+ // Already done
+// dbg_ns_verb("Processing prerequisities.\n");
+// knot_ddns_prereq_t *prereqs = NULL;
+// int ret = knot_ddns_process_prereqs(query, &prereqs, rcode);
+// if (ret != KNOT_EOK) {
+// dbg_ns("Failed to check zone for update: "
+// "%s.\n", knot_strerror(ret));
+// return ret;
+// }
+
+// assert(prereqs != NULL);
+
+ // 3) Check prerequisities
+ /*! \todo Somehow ensure the zone will not be changed until the update
+ * is finished.
+ */
+ // Already done
+// dbg_ns_verb("Checking prerequisities.\n");
+// ret = knot_ddns_check_prereqs(zone, &prereqs, rcode);
+// if (ret != KNOT_EOK) {
+// knot_ddns_prereqs_free(&prereqs);
+// dbg_ns("Failed to check zone for update: "
+// "%s.\n", knot_strerror(ret));
+// return ret;
+// }
+
+ // 4) Convert update to changeset
+ dbg_ns_verb("Converting UPDATE packet to changeset.\n");
+ int ret = knot_ddns_process_update(zone, query, changeset, rcode);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to check zone for update: "
+ "%s.\n", knot_strerror(ret));
+ return ret;
}
- dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n",
- knot_packet_parsed(query), knot_packet_size(query));
+ assert(changeset != NULL);
- /*! \todo API for EDNS values. */
- dbg_ns_verb("Opt RR: version: %d, payload: %d\n",
- query->opt_rr.version, query->opt_rr.payload);
-
- // 2) Find zone for the query
- // we do not check if there is only one entry in the Question section
- // because the packet structure does not allow it
- if (knot_packet_qtype(query) != KNOT_RRTYPE_SOA) {
- dbg_ns("Question is not of type SOA.\n");
- knot_ns_error_response_full(nameserver, response,
- KNOT_RCODE_FORMERR,
- response_wire, rsize);
- knot_packet_free(&response);
- return KNOT_EOK;
- }
+ // Done in zones.c
+// knot_ddns_prereqs_free(&prereqs);
+ return ret;
+}
- *zone = knot_zonedb_find_zone(nameserver->zone_db,
- knot_packet_qname(query));
- if (*zone == NULL) {
- dbg_ns("Zone not found for the update.\n");
- knot_ns_error_response_full(nameserver, response,
- KNOT_RCODE_NOTAUTH,
- response_wire, rsize);
- knot_packet_free(&response);
- return KNOT_EOK;
+/*----------------------------------------------------------------------------*/
+/*
+ * This function should:
+ * 1) Create zone shallow copy and the changes structure.
+ * 2) Call knot_ddns_process_update2().
+ * - If something went bad, call xfrin_rollback_update() and return an error.
+ * - If everything went OK, continue.
+ * 3) Finalize the updated zone.
+ *
+ * NOTE: Mostly copied from xfrin_apply_changesets(). Should be refactored in
+ * order to get rid of duplicate code.
+ */
+int knot_ns_process_update2(const knot_packet_t *query,
+ knot_zone_contents_t *old_contents,
+ knot_zone_contents_t **new_contents,
+ knot_changesets_t *chgs, knot_rcode_t *rcode)
+{
+ /*! \todo Implement. */
+ if (query == NULL || old_contents == NULL || chgs == NULL ||
+ chgs->sets == NULL || new_contents == NULL || rcode == NULL) {
+ return KNOT_EINVAL;
}
- uint8_t rcode = 0;
- // 3) Check zone
- ret = knot_ddns_check_zone(*zone, query, &rcode);
- if (ret == KNOT_EBADZONE) {
- // zone is slave, forward the request
- /*! \todo Implement forwarding. */
- return KNOT_EBADZONE;
- } else if (ret != KNOT_EOK) {
- dbg_ns("Failed to check zone for update: "
- "%s.\n", knot_strerror(ret));
- knot_ns_error_response_full(nameserver, response, rcode,
- response_wire, rsize);
- knot_packet_free(&response);
- return KNOT_EOK;
- }
+ dbg_ns("Applying UPDATE to zone...\n");
- // 4) Convert prerequisities
- knot_ddns_prereq_t *prereqs = NULL;
- ret = knot_ddns_process_prereqs(query, &prereqs, &rcode);
+ /* 1) Create zone shallow copy. */
+ dbg_ns_verb("Creating shallow copy of the zone...\n");
+ knot_zone_contents_t *contents_copy = NULL;
+ knot_changes_t *changes = NULL;
+ int ret = xfrin_prepare_zone_copy(old_contents, &contents_copy,
+ &changes);
if (ret != KNOT_EOK) {
- dbg_ns("Failed to check zone for update: "
- "%s.\n", knot_strerror(ret));
- knot_ns_error_response_full(nameserver, response, rcode,
- response_wire, rsize);
- knot_packet_free(&response);
- return KNOT_EOK;
+ dbg_ns("Failed to prepare zone copy: %s\n",
+ knot_strerror(ret));
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return ret;
}
-
- assert(prereqs != NULL);
-
- // 5) Check prerequisities
- /*! \todo Somehow ensure the zone will not be changed until the update
- * is finished.
- */
- ret = knot_ddns_check_prereqs(knot_zone_contents(*zone), &prereqs,
- &rcode);
+
+ /* 2) Apply the UPDATE and create changesets. */
+ dbg_ns_verb("Applying the UPDATE and creating changeset...\n");
+ ret = knot_ddns_process_update2(contents_copy, query, &chgs->sets[0],
+ changes, rcode);
if (ret != KNOT_EOK) {
- dbg_ns("Failed to check zone for update: "
- "%s.\n", knot_strerror(ret));
- knot_ns_error_response_full(nameserver, response, rcode,
- response_wire, rsize);
- knot_ddns_prereqs_free(&prereqs);
- knot_packet_free(&response);
- return KNOT_EOK;
+ dbg_ns("Failed to apply UPDATE to the zone copy or no update"
+ " made: %s\n", (ret < 0) ? knot_strerror(ret)
+ : "No change made.");
+ xfrin_rollback_update(old_contents, &contents_copy, &changes);
+ return ret;
}
-
- // 6) Convert update to changeset
- ret = knot_ddns_process_update(query, changeset, &rcode);
+
+ dbg_ns_verb("Finalizing updated zone...\n");
+ ret = xfrin_finalize_updated_zone(contents_copy, changes, old_contents);
if (ret != KNOT_EOK) {
- dbg_ns("Failed to check zone for update: "
- "%s.\n", knot_strerror(ret));
- knot_ns_error_response_full(nameserver, response, rcode,
- response_wire, rsize);
- knot_ddns_prereqs_free(&prereqs);
- knot_packet_free(&response);
- return KNOT_EOK;
+ dbg_ns("Failed to finalize updated zone: %s\n",
+ knot_strerror(ret));
+ xfrin_rollback_update(old_contents, &contents_copy, &changes);
+ *rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL;
+ return ret;
}
- assert(changeset != NULL);
-
- // 7) Create response
- dbg_ns("Update converted successfuly.\n");
-
- /*! \todo No response yet. Distinguish somehow in the caller.
- * Maybe only this case will be EOK, other cases some error.
- */
-
- knot_ddns_prereqs_free(&prereqs);
- knot_packet_free(&response);
+ chgs->changes = changes;
+ *new_contents = contents_copy;
+
return KNOT_EOK;
}
@@ -4221,7 +4356,10 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
int knot_ns_create_forward_query(const knot_packet_t *query,
uint8_t *query_wire, size_t *size)
{
- // just copy the wireformat of the query and set a new random ID to it
+ /* Forward UPDATE query:
+ * assign a new packet id
+ */
+ int ret = KNOT_EOK;
if (knot_packet_size(query) > *size) {
return KNOT_ESPACE;
}
@@ -4229,10 +4367,9 @@ int knot_ns_create_forward_query(const knot_packet_t *query,
memcpy(query_wire, knot_packet_wireformat(query),
knot_packet_size(query));
*size = knot_packet_size(query);
-
knot_wire_set_id(query_wire, knot_random_id());
- return KNOT_EOK;
+ return ret;
}
/*----------------------------------------------------------------------------*/
diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h
index 3fe1210..6bb7a86 100644
--- a/src/libknot/nameserver/name-server.h
+++ b/src/libknot/nameserver/name-server.h
@@ -121,6 +121,9 @@ typedef struct knot_ns_xfr {
uint8_t *digest; /*!< Buffer for counting digest. */
size_t digest_size; /*!< Size of the digest. */
size_t digest_max_size; /*!< Size of the buffer. */
+
+ /*! \note [DDNS] Update forwarding fields. */
+ int fwd_src_fd; /*!< Query originator fd. */
uint16_t tsig_rcode;
uint64_t tsig_prev_time_signed;
@@ -158,12 +161,14 @@ typedef enum knot_ns_transport {
*/
typedef enum knot_ns_xfr_type_t {
/* DNS events. */
- XFR_TYPE_AIN = 1 << 0, /*!< AXFR-IN request (start transfer). */
- XFR_TYPE_AOUT= 1 << 1, /*!< AXFR-OUT request (incoming transfer). */
- XFR_TYPE_IIN = 1 << 2, /*!< IXFR-IN request (start transfer). */
- XFR_TYPE_IOUT = 1 << 3, /*!< IXFR-OUT request (incoming transfer). */
- XFR_TYPE_SOA = 1 << 4, /*!< Pending SOA request. */
- XFR_TYPE_NOTIFY = 1 << 5 /*!< Pending NOTIFY query. */
+ XFR_TYPE_AIN = 1 << 0, /*!< AXFR-IN request (start transfer). */
+ XFR_TYPE_AOUT= 1 << 1, /*!< AXFR-OUT request (incoming transfer). */
+ XFR_TYPE_IIN = 1 << 2, /*!< IXFR-IN request (start transfer). */
+ XFR_TYPE_IOUT = 1 << 3, /*!< IXFR-OUT request (incoming transfer). */
+ XFR_TYPE_SOA = 1 << 4, /*!< Pending SOA request. */
+ XFR_TYPE_NOTIFY = 1 << 5, /*!< Pending NOTIFY query. */
+ XFR_TYPE_UPDATE = 1 << 6, /*!< UPDATE request (incoming UPDATE). */
+ XFR_TYPE_FORWARD = 1 << 7 /*!< UPDATE forward request. */
} knot_ns_xfr_type_t;
/*----------------------------------------------------------------------------*/
@@ -227,6 +232,10 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
knot_packet_t *query, knot_packet_t **resp,
const knot_zone_t **zone, size_t max_size);
+int knot_ns_prep_update_response(knot_nameserver_t *nameserver,
+ knot_packet_t *query, knot_packet_t **resp,
+ knot_zone_t **zone, size_t max_size);
+
/*!
* \brief Creates a response for the given normal query using the data of the
* nameserver.
@@ -339,9 +348,14 @@ int knot_ns_switch_zone(knot_nameserver_t *nameserver,
int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
knot_ns_xfr_t *xfr);
-int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
- uint8_t *response_wire, size_t *rsize,
- knot_zone_t **zone, knot_changeset_t **changeset);
+int knot_ns_process_update(const knot_packet_t *query,
+ const knot_zone_contents_t *zone,
+ knot_changeset_t *changeset, knot_rcode_t *rcode);
+
+int knot_ns_process_update2(const knot_packet_t *query,
+ knot_zone_contents_t *old_contents,
+ knot_zone_contents_t **new_contents,
+ knot_changesets_t *chgs, knot_rcode_t *rcode);
int knot_ns_create_forward_query(const knot_packet_t *query,
uint8_t *query_wire, size_t *size);
diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c
index 6a047fb..9b7e7c7 100644
--- a/src/libknot/packet/packet.c
+++ b/src/libknot/packet/packet.c
@@ -291,22 +291,25 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
dbg_packet_verb("Parsing dname starting on position %zu and "
"%zu bytes long.\n", *pos, i - *pos + 1);
dbg_packet_verb("Alloc: %d\n", alloc);
+ size_t bp = *pos;
if (alloc) {
- question->qname = knot_dname_new_from_wire(
- wire + *pos, i - *pos + 1, NULL);
+ question->qname = knot_dname_parse_from_wire(wire, pos,
+ i + 1,
+ NULL, NULL);
if (question->qname == NULL) {
return KNOT_ENOMEM;
}
} else {
- int res = knot_dname_from_wire(wire + *pos, i - *pos + 1,
- NULL, question->qname);
- if (res != KNOT_EOK) {
- assert(res != KNOT_EINVAL);
- return res;
+ void *parsed = knot_dname_parse_from_wire(wire, pos,
+ i + 1,
+ NULL, question->qname);
+ if (!parsed) {
+ return KNOT_EMALF;
}
}
-
- *pos = i + 1;
+ if (*pos != i + 1) {
+ dbg_packet("Parsed dname expected len=%zu, parsed=%zu.\n", i+1-bp, *pos-bp);
+ }
question->qtype = knot_wire_read_u16(wire + i + 1);
question->qclass = knot_wire_read_u16(wire + i + 3);
*pos += 4;
@@ -386,7 +389,7 @@ static knot_rrset_t *knot_packet_parse_rr(const uint8_t *wire, size_t *pos,
*pos, size);
knot_dname_t *owner = knot_dname_parse_from_wire(wire, pos, size,
- NULL);
+ NULL, NULL);
dbg_packet_detail("Created owner: %p, actual position: %zu\n", owner,
*pos);
if (owner == NULL) {
@@ -512,7 +515,7 @@ dbg_packet_exec_detail(
free(name);
);
- if (knot_rrset_compare((*rrsets)[i], rrset,
+ if (knot_rrset_match((*rrsets)[i], rrset,
KNOT_RRSET_COMPARE_HEADER)) {
/*! \todo Test this!!! */
// no duplicate checking here, the packet should
@@ -1257,7 +1260,7 @@ const knot_rrset_t *knot_packet_answer_rrset(
/*----------------------------------------------------------------------------*/
const knot_rrset_t *knot_packet_authority_rrset(
- knot_packet_t *packet, short pos)
+ const knot_packet_t *packet, short pos)
{
if (packet == NULL || pos > packet->ns_rrsets) {
return NULL;
@@ -1269,7 +1272,7 @@ const knot_rrset_t *knot_packet_authority_rrset(
/*----------------------------------------------------------------------------*/
const knot_rrset_t *knot_packet_additional_rrset(
- knot_packet_t *packet, short pos)
+ const knot_packet_t *packet, short pos)
{
if (packet == NULL || pos > packet->ar_rrsets) {
return NULL;
@@ -1289,19 +1292,19 @@ int knot_packet_contains(const knot_packet_t *packet,
}
for (int i = 0; i < packet->an_rrsets; ++i) {
- if (knot_rrset_compare(packet->answer[i], rrset, cmp)) {
+ if (knot_rrset_match(packet->answer[i], rrset, cmp)) {
return 1;
}
}
for (int i = 0; i < packet->ns_rrsets; ++i) {
- if (knot_rrset_compare(packet->authority[i], rrset, cmp)) {
+ if (knot_rrset_match(packet->authority[i], rrset, cmp)) {
return 1;
}
}
for (int i = 0; i < packet->ar_rrsets; ++i) {
- if (knot_rrset_compare(packet->additional[i], rrset, cmp)) {
+ if (knot_rrset_match(packet->additional[i], rrset, cmp)) {
return 1;
}
}
@@ -1390,7 +1393,7 @@ void knot_packet_header_to_wire(const knot_header_t *header,
int knot_packet_question_to_wire(knot_packet_t *packet)
{
- if (packet == NULL) {
+ if (packet == NULL || packet->question.qname == NULL) {
return KNOT_EINVAL;
}
@@ -1551,6 +1554,10 @@ void knot_packet_dump(const knot_packet_t *packet)
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(" RCODE: %u\n", knot_wire_flags_get_rcode(
+ packet->header.flags2));
+ dbg_packet(" OPCODE: %u\n", knot_wire_flags_get_opcode(
+ packet->header.flags1));
dbg_packet(" QDCOUNT: %u\n", packet->header.qdcount);
dbg_packet(" ANCOUNT: %u\n", packet->header.ancount);
dbg_packet(" NSCOUNT: %u\n", packet->header.nscount);
diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h
index 522ae8e..5a95bae 100644
--- a/src/libknot/packet/packet.h
+++ b/src/libknot/packet/packet.h
@@ -451,7 +451,7 @@ const knot_rrset_t *knot_packet_answer_rrset(
* or NULL if there is no such RRSet.
*/
const knot_rrset_t *knot_packet_authority_rrset(
- knot_packet_t *packet, short pos);
+ const knot_packet_t *packet, short pos);
/*!
* \brief Returns the requested Additional RRset.
@@ -465,7 +465,7 @@ const knot_rrset_t *knot_packet_authority_rrset(
* or NULL if there is no such RRSet.
*/
const knot_rrset_t *knot_packet_additional_rrset(
- knot_packet_t *packet, short pos);
+ const knot_packet_t *packet, short pos);
/*!
* \brief Checks if the packet already contains the given RRSet.
diff --git a/src/libknot/packet/query.h b/src/libknot/packet/query.h
index cda72b9..3ca4fd3 100644
--- a/src/libknot/packet/query.h
+++ b/src/libknot/packet/query.h
@@ -81,6 +81,8 @@ int knot_query_set_opcode(knot_packet_t *query, uint8_t opcode);
int knot_query_add_rrset_authority(knot_packet_t *query,
const knot_rrset_t *rrset);
+int knot_query_rr_to_wire(const knot_rrset_t *rrset, const knot_rdata_t *rdata,
+ uint8_t **wire, uint8_t *endp);
#endif /* _KNOT_QUERY_H_ */
diff --git a/src/libknot/packet/response.c b/src/libknot/packet/response.c
index c6a1a09..476c6b3 100644
--- a/src/libknot/packet/response.c
+++ b/src/libknot/packet/response.c
@@ -903,7 +903,8 @@ int knot_response_init(knot_packet_t *response)
/*----------------------------------------------------------------------------*/
int knot_response_init_from_query(knot_packet_t *response,
- knot_packet_t *query)
+ knot_packet_t *query,
+ int copy_question)
{
if (response == NULL || query == NULL) {
@@ -913,21 +914,29 @@ int knot_response_init_from_query(knot_packet_t *response,
// copy the header from the query
memcpy(&response->header, &query->header, sizeof(knot_header_t));
- // copy the Question section (but do not copy the QNAME)
- memcpy(&response->question, &query->question,
- sizeof(knot_question_t));
-
int err = 0;
- // put the qname into the compression table
- // TODO: get rid of the numeric constants
- if ((err = knot_response_store_dname_pos(&response->compression,
- response->question.qname, 0, 12, 12, 0)) != KNOT_EOK) {
- return err;
- }
+ /*! \todo Constant. */
+ size_t to_copy = 12;
+
+ if (copy_question) {
+ // copy the Question section (but do not copy the QNAME)
+ memcpy(&response->question, &query->question,
+ sizeof(knot_question_t));
+
+ // put the qname into the compression table
+ // TODO: get rid of the numeric constants
+ if ((err = knot_response_store_dname_pos(&response->compression,
+ response->question.qname, 0, 12, 12, 0))
+ != KNOT_EOK) {
+ return err;
+ }
- // copy the wireformat of Header and Question from the query
- // TODO: get rid of the numeric constants
- size_t to_copy = 12 + 4 + knot_dname_size(response->question.qname);
+ /*! \todo Constant. */
+ to_copy += 4 + knot_dname_size(response->question.qname);
+ } else {
+ response->header.qdcount = 0;
+ knot_wire_set_qdcount(response->wireformat, 0);
+ }
assert(response->max_size >= to_copy);
memcpy(response->wireformat, query->wireformat, to_copy);
diff --git a/src/libknot/packet/response.h b/src/libknot/packet/response.h
index beb1a59..277b1aa 100644
--- a/src/libknot/packet/response.h
+++ b/src/libknot/packet/response.h
@@ -62,7 +62,8 @@ int knot_response_init(knot_packet_t *response);
* \retval KNOT_EOK
*/
int knot_response_init_from_query(knot_packet_t *response,
- knot_packet_t *query);
+ knot_packet_t *query,
+ int copy_question);
/*!
* \brief Clears the response structure for reuse.
diff --git a/src/libknot/rdata.c b/src/libknot/rdata.c
index 9bcdbe5..352bb6c 100644
--- a/src/libknot/rdata.c
+++ b/src/libknot/rdata.c
@@ -242,7 +242,7 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
case KNOT_RDATA_WF_LITERAL_DNAME:
pos2 = *pos;
dname = knot_dname_parse_from_wire(
- wire, &pos2, total_size, NULL);
+ wire, &pos2, total_size, NULL, NULL);
if (dname == NULL) {
free(items);
return KNOT_ERROR;
@@ -308,7 +308,7 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
case 3:
pos2 = *pos;
dname = knot_dname_parse_from_wire(
- wire, &pos2, total_size, NULL);
+ wire, &pos2, total_size, NULL, NULL);
if (dname == NULL) {
knot_rdata_free_items(items, i,
desc->type, 1);
@@ -698,6 +698,18 @@ int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata)
}
/*---------------------------------------------------------------------------*/
+void knot_rdata_soa_serial_set(knot_rdata_t *rdata, uint32_t serial)
+{
+ if (!rdata || rdata->count < 3) {
+ return;
+ }
+
+ // the number is in network byte order, transform it
+ knot_wire_write_u32((uint8_t *)(rdata->items[2].raw_data + 1),
+ serial);
+}
+
+/*---------------------------------------------------------------------------*/
uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata)
{
@@ -816,3 +828,43 @@ const uint8_t *knot_rdata_nsec3_salt(const knot_rdata_t *rdata)
return ((uint8_t *)(rdata->items[3].raw_data + 1)) + 1;
}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_rdata_ds_digest_type(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 3) {
+ return 0;
+ }
+
+ return *((uint8_t *)(rdata->items[2].raw_data + 1));
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_rdata_ds_digest_len(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 4) {
+ return 0;
+ }
+
+ return *(rdata->items[3].raw_data);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_ds_check(const knot_rdata_t *rdata)
+{
+ // Check if the legth of the digest corresponds to the proper size of
+ // the digest according to the given algorithm
+ uint16_t len = knot_rdata_ds_digest_len(rdata);
+ uint8_t type = knot_rdata_ds_digest_type(rdata);
+
+ if (type == 0 || len == 0) {
+ return KNOT_EINVAL;
+ } else if (len != knot_ds_digest_length(type)) {
+ return KNOT_EDSDIGESTLEN;
+ } else {
+ return KNOT_EOK;
+ }
+}
diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h
index 57517bd..eb7bd55 100644
--- a/src/libknot/rdata.h
+++ b/src/libknot/rdata.h
@@ -329,6 +329,7 @@ const knot_dname_t *knot_rdata_get_name(const knot_rdata_t *rdata,
uint16_t type);
int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata);
+void knot_rdata_soa_serial_set(knot_rdata_t *rdata, uint32_t serial);
uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata);
uint32_t knot_rdata_soa_retry(const knot_rdata_t *rdata);
@@ -342,6 +343,10 @@ uint16_t knot_rdata_nsec3_iterations(const knot_rdata_t *rdata);
uint8_t knot_rdata_nsec3_salt_length(const knot_rdata_t *rdata);
const uint8_t *knot_rdata_nsec3_salt(const knot_rdata_t *rdata);
+uint8_t knot_rdata_ds_digest_type(const knot_rdata_t *rdata);
+uint16_t knot_rdata_ds_digest_len(const knot_rdata_t *rdata);
+int knot_rdata_ds_check(const knot_rdata_t *rdata);
+
#endif /* _KNOT_RDATA_H */
/*! @} */
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
index d8b10ce..84d5075 100644
--- a/src/libknot/rrset.c
+++ b/src/libknot/rrset.c
@@ -215,7 +215,7 @@ int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
if (dupl == KNOT_RRSET_DUPL_MERGE) {
rc = knot_rrset_merge_no_dupl((void **)&rrset->rrsigs,
(void **)&rrsigs);
- if (rc != KNOT_EOK) {
+ if (rc < 0) {
return rc;
} else {
return 1;
@@ -280,6 +280,15 @@ void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl)
/*----------------------------------------------------------------------------*/
+void knot_rrset_set_class(knot_rrset_t *rrset, uint16_t rclass)
+{
+ if (rrset) {
+ rrset->rclass = rclass;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
uint16_t knot_rrset_type(const knot_rrset_t *rrset)
{
return rrset->type;
@@ -604,7 +613,7 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
/*----------------------------------------------------------------------------*/
-int knot_rrset_compare(const knot_rrset_t *r1,
+int knot_rrset_match(const knot_rrset_t *r1,
const knot_rrset_t *r2,
knot_rrset_compare_type_t cmp)
{
@@ -838,6 +847,7 @@ dbg_rrset_exec_detail(
}
knot_rdata_t *walk2 = rrset2->rdata;
+ int deleted = 0;
// no RDATA in RRSet 1
if (rrset1->rdata == NULL && rrset2->rdata != NULL) {
@@ -934,6 +944,7 @@ dbg_rrset_exec_detail(
knot_rdata_deep_free(&tmp, rrset1->type, 1);
assert(tmp == NULL);
/* Maybe caller should be warned about this. */
+ ++deleted;
}
}
@@ -954,5 +965,5 @@ dbg_rrset_exec_detail(
*/
rrset2->rdata = NULL;
- return KNOT_EOK;
+ return deleted;
}
diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h
index b5b62db..077b067 100644
--- a/src/libknot/rrset.h
+++ b/src/libknot/rrset.h
@@ -163,6 +163,8 @@ void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner);
void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl);
+void knot_rrset_set_class(knot_rrset_t *rrset, uint16_t rclass);
+
/*!
* \brief Returns the TYPE of the RRSet.
*
@@ -259,7 +261,7 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
* \retval <> 0 If RRSets are equal.
* \retval 0 if RRSets are not equal.
*/
-int knot_rrset_compare(const knot_rrset_t *r1,
+int knot_rrset_match(const knot_rrset_t *r1,
const knot_rrset_t *r2,
knot_rrset_compare_type_t cmp);
@@ -344,6 +346,8 @@ int knot_rrset_merge(void **r1, void **r2);
* \retval KNOT_EOK
* \retval KNOT_EINVAL if the RRSets could not be merged, because their
* Owner, Type, Class or TTL does not match.
+ * \retval >0 if some RDATA have been removed because they were duplicate. The
+ * return value indicates number of such RDATA.
*/
int knot_rrset_merge_no_dupl(void **r1, void **r2);
diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c
index cb280ab..55316a9 100644
--- a/src/libknot/tsig-op.c
+++ b/src/libknot/tsig-op.c
@@ -108,20 +108,16 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len,
char decoded_key[B64BUFSIZE];
memset(decoded_key, 0, sizeof(decoded_key));
- size_t decoded_key_size = B64BUFSIZE;
- int ret = base64_decode(key->secret, strlen(key->secret),
- decoded_key,
- &decoded_key_size);
- if (ret != 1) {
- dbg_tsig("TSIG: New decode function failed! (%d)\n", ret);
- return KNOT_ERROR;
- }
-
- if (decoded_key_size < 0) {
+ int32_t ret = base64_decode((uint8_t *)key->secret, strlen(key->secret),
+ (uint8_t *)decoded_key, B64BUFSIZE);
+
+ if (ret < 0) {
dbg_tsig("TSIG: Could not decode Base64\n");
return KNOT_ERROR;
}
+ size_t decoded_key_size = ret;
+
dbg_tsig_detail("TSIG: decoded key size: %d\n", decoded_key_size);
dbg_tsig_detail("TSIG: decoded key:\n");
dbg_tsig_hex_detail(decoded_key, decoded_key_size);
@@ -221,14 +217,14 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire,
/* Copy class. */
knot_wire_write_u16(wire + offset, knot_rrset_class(tsig_rr));
- dbg_tsig_verb("TSIG: write variables: written CLASS: %u - ",
+ dbg_tsig_verb("TSIG: write variables: written CLASS: %u - \n",
knot_rrset_class(tsig_rr));
dbg_tsig_hex_detail(wire + offset, sizeof(uint16_t));
offset += sizeof(uint16_t);
/* Copy TTL - always 0. */
knot_wire_write_u32(wire + offset, knot_rrset_ttl(tsig_rr));
- dbg_tsig_verb("TSIG: write variables: written TTL: %u - ",
+ dbg_tsig_verb("TSIG: write variables: written TTL: %u - \n",
knot_rrset_ttl(tsig_rr));
dbg_tsig_hex_detail(wire + offset, sizeof(uint32_t));
offset += sizeof(uint32_t);
@@ -816,6 +812,9 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
memset(wire_to_sign, 0, sizeof(uint8_t) * size);
memcpy(wire_to_sign, wire, size);
+
+ /* Restore message id. */
+ knot_wire_set_id(wire_to_sign, tsig_rdata_orig_id(tsig_rr));
/* Decrease arcount. */
knot_wire_set_arcount(wire_to_sign,
@@ -864,10 +863,10 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
return KNOT_TSIG_EBADSIG;
}
- dbg_tsig_verb("TSIG: calc digest : ");
+ dbg_tsig_verb("TSIG: calc digest :\n");
dbg_tsig_hex_verb(digest_tmp, digest_tmp_len);
- dbg_tsig_verb("TSIG: given digest: ");
+ dbg_tsig_verb("TSIG: given digest:\n");
dbg_tsig_hex_verb(tsig_mac, mac_length);
if (strncasecmp((char *)(tsig_mac), (char *)digest_tmp,
diff --git a/src/libknot/updates/changesets.c b/src/libknot/updates/changesets.c
index ab83b07..5405726 100644
--- a/src/libknot/updates/changesets.c
+++ b/src/libknot/updates/changesets.c
@@ -66,7 +66,7 @@ static int knot_changeset_check_count(knot_rrset_t ***rrsets, size_t count,
static int knot_changeset_rrsets_match(const knot_rrset_t *rrset1,
const knot_rrset_t *rrset2)
{
- return knot_rrset_compare(rrset1, rrset2, KNOT_RRSET_COMPARE_HEADER)
+ return knot_rrset_match(rrset1, rrset2, KNOT_RRSET_COMPARE_HEADER)
&& (knot_rrset_type(rrset1) != KNOT_RRTYPE_RRSIG
|| knot_rdata_rrsig_type_covered(
knot_rrset_rdata(rrset1))
@@ -76,7 +76,8 @@ static int knot_changeset_rrsets_match(const knot_rrset_t *rrset1,
/*----------------------------------------------------------------------------*/
-int knot_changeset_allocate(knot_changesets_t **changesets)
+int knot_changeset_allocate(knot_changesets_t **changesets,
+ uint32_t flags)
{
// create new changesets
*changesets = (knot_changesets_t *)(malloc(sizeof(knot_changesets_t)));
@@ -85,8 +86,15 @@ int knot_changeset_allocate(knot_changesets_t **changesets)
}
memset(*changesets, 0, sizeof(knot_changesets_t));
+ (*changesets)->flags = flags;
- return knot_changesets_check_size(*changesets);
+ if (knot_changesets_check_size(*changesets) != KNOT_EOK) {
+ free(*changesets);
+ *changesets = NULL;
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
@@ -138,19 +146,19 @@ int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count,
int knot_changeset_add_new_rr(knot_changeset_t *changeset,
knot_rrset_t *rrset,
- xfrin_changeset_part_t part)
+ knot_changeset_part_t part)
{
knot_rrset_t ***rrsets = NULL;
size_t *count = NULL;
size_t *allocated = NULL;
switch (part) {
- case XFRIN_CHANGESET_ADD:
+ case KNOT_CHANGESET_ADD:
rrsets = &changeset->add;
count = &changeset->add_count;
allocated = &changeset->add_allocated;
break;
- case XFRIN_CHANGESET_REMOVE:
+ case KNOT_CHANGESET_REMOVE:
rrsets = &changeset->remove;
count = &changeset->remove_count;
allocated = &changeset->remove_allocated;
@@ -173,6 +181,29 @@ int knot_changeset_add_new_rr(knot_changeset_t *changeset,
/*----------------------------------------------------------------------------*/
+knot_rrset_t *knot_changeset_remove_rr(knot_rrset_t **rrsets, size_t *count,
+ int pos)
+{
+ if (pos >= *count || *count == 0) {
+ return NULL;
+ }
+
+ knot_rrset_t *removed = rrsets[pos];
+
+ // shift all RRSets from pos+1 one cell to the left
+ for (int i = pos; i < *count - 1; ++i) {
+ rrsets[i] = rrsets[i + 1];
+ }
+
+ // just to be sure, set the last previously occupied position to NULL
+ rrsets[*count - 1] = NULL;
+ *count -= 1;
+
+ return removed;
+}
+
+/*----------------------------------------------------------------------------*/
+
void knot_changeset_store_soa(knot_rrset_t **chg_soa,
uint32_t *chg_serial, knot_rrset_t *soa)
{
@@ -183,14 +214,14 @@ void knot_changeset_store_soa(knot_rrset_t **chg_soa,
/*----------------------------------------------------------------------------*/
int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
- xfrin_changeset_part_t part)
+ knot_changeset_part_t part)
{
switch (part) {
- case XFRIN_CHANGESET_ADD:
+ case KNOT_CHANGESET_ADD:
knot_changeset_store_soa(&changeset->soa_to,
&changeset->serial_to, soa);
break;
- case XFRIN_CHANGESET_REMOVE:
+ case KNOT_CHANGESET_REMOVE:
knot_changeset_store_soa(&changeset->soa_from,
&changeset->serial_from, soa);
break;
@@ -212,7 +243,8 @@ int knot_changesets_check_size(knot_changesets_t *changesets)
}
/* How many steps is needed to content count? */
- size_t extra = (changesets->count - changesets->allocated) % KNOT_CHANGESET_STEP;
+ size_t extra = (changesets->count - changesets->allocated)
+ % KNOT_CHANGESET_STEP;
extra = (extra + 1) * KNOT_CHANGESET_STEP;
/* Allocate new memory block. */
@@ -223,10 +255,15 @@ int knot_changesets_check_size(knot_changesets_t *changesets)
return KNOT_ENOMEM;
}
- /* Clear old memory block and copy old data. */
+ /* Clear new memory block and copy old data. */
memset(sets, 0, new_count * item_len);
memcpy(sets, changesets->sets, changesets->allocated * item_len);
+ /* Set type to all newly allocated changesets. */
+ for (int i = changesets->allocated; i < new_count; ++i) {
+ sets[i].flags = changesets->flags;
+ }
+
/* Replace old changesets. */
free(changesets->sets);
changesets->sets = sets;
@@ -237,6 +274,32 @@ int knot_changesets_check_size(knot_changesets_t *changesets)
/*----------------------------------------------------------------------------*/
+void knot_changeset_set_flags(knot_changeset_t *changeset,
+ uint32_t flags)
+{
+ changeset->flags = flags;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint32_t knot_changeset_flags(knot_changeset_t *changeset)
+{
+ return changeset->flags;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_is_empty(const knot_changeset_t *changeset)
+{
+ if (changeset == NULL) {
+ return 0;
+ }
+
+ return (changeset->add_count == 0 && changeset->remove_count == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
void knot_free_changeset(knot_changeset_t **changeset)
{
assert((*changeset)->add_allocated >= (*changeset)->add_count);
@@ -287,3 +350,274 @@ void knot_free_changesets(knot_changesets_t **changesets)
free(*changesets);
*changesets = NULL;
}
+
+/*----------------------------------------------------------------------------*/
+/* knot_changes_t manipulation */
+/*----------------------------------------------------------------------------*/
+
+int knot_changes_rrsets_reserve(knot_rrset_t ***rrsets,
+ int *count, int *allocated, int to_add)
+{
+ if (rrsets == NULL || count == NULL || allocated == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (*count + to_add <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ int new_count = (*allocated == 0) ? 2 : *allocated * 2;
+ while (new_count < *count + to_add) {
+ new_count *= 2;
+ }
+
+ /* Allocate new memory block. */
+ knot_rrset_t **rrsets_new = malloc(new_count * sizeof(knot_rrset_t *));
+ if (rrsets_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Initialize new memory and copy old data. */
+ memset(rrsets_new, 0, new_count * sizeof(knot_rrset_t *));
+ memcpy(rrsets_new, *rrsets, (*allocated) * sizeof(knot_rrset_t *));
+
+ /* Free old nodes and switch pointers. */
+ free(*rrsets);
+ *rrsets = rrsets_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changes_nodes_reserve(knot_node_t ***nodes,
+ int *count, int *allocated)
+{
+ if (nodes == NULL || count == NULL || allocated == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (*count + 2 <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ int new_count = (*allocated == 0) ? 2 : *allocated * 2;
+
+ /* Allocate new memory block. */
+ const size_t node_len = sizeof(knot_node_t *);
+ knot_node_t **nodes_new = malloc(new_count * node_len);
+ if (nodes_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Clear memory block and copy old data. */
+ memset(nodes_new, 0, new_count * node_len);
+ memcpy(nodes_new, *nodes, (*allocated) * node_len);
+
+ /* Free old nodes and switch pointers. */
+ free(*nodes);
+ *nodes = nodes_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changes_rdata_reserve(knot_rdata_t ***rdatas, uint16_t **types,
+ int count, int *allocated, int to_add)
+{
+ if (rdatas == NULL || types == NULL || allocated == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (count + to_add <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ int new_count = (*allocated == 0) ? 2 : *allocated * 2;
+ while (new_count < count + to_add) {
+ new_count *= 2;
+ }
+
+ /* Allocate new memory block. */
+ knot_rdata_t **rdatas_new = malloc(new_count * sizeof(knot_rdata_t *));
+ if (rdatas_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ uint16_t *types_new = malloc(new_count * sizeof(uint));
+ if (types_new == NULL) {
+ free(rdatas_new);
+ return KNOT_ENOMEM;
+ }
+
+ /* Initialize new memory and copy old data. */
+ memset(rdatas_new, 0, new_count * sizeof(knot_rdata_t *));
+ memcpy(rdatas_new, *rdatas, (*allocated) * sizeof(knot_rdata_t *));
+
+ memset(types_new, 0, new_count * sizeof(uint));
+ memcpy(types_new, *types, (*allocated) * sizeof(uint));
+
+ /* Free old rdatas and switch pointers. */
+ free(*rdatas);
+ free(*types);
+ *rdatas = rdatas_new;
+ *types = types_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_changes_add_rdata(knot_rdata_t **rdatas, uint16_t *types,
+ int *count, knot_rdata_t *rdata, uint16_t type)
+{
+ if (rdatas == NULL || types == NULL || count == NULL || rdata == NULL) {
+ return;
+ }
+
+ // Add all RDATAs from the chain!!
+
+ knot_rdata_t *r = rdata;
+ do {
+// dbg_xfrin_detail("Adding RDATA to RDATA list: %p\n", r);
+ rdatas[*count] = r;
+ types[*count] = type;
+ ++*count;
+
+ r = r->next;
+ } while (r != NULL && r != rdata);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changes_add_old_rrsets(knot_rrset_t **rrsets, int count,
+ knot_changes_t *changes, int add_rdata)
+{
+ if (rrsets == NULL || changes == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (count == 0) {
+ return KNOT_EOK;
+ }
+
+ /* Reserve twice the space, to have enough space for RRSIGs if
+ * there are some.
+ */
+ int ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 2 * count);
+ if (ret != KNOT_EOK) {
+// dbg_xfrin("Failed to reserve changes rrsets.\n");
+ return ret;
+ }
+
+ /* Mark RRsets and RDATA for removal. */
+ for (unsigned i = 0; i < count; ++i) {
+ if (rrsets[i] == NULL) {
+ continue;
+ }
+
+ knot_rrset_t *rrsigs = knot_rrset_get_rrsigs(rrsets[i]);
+
+ if (add_rdata) {
+
+ /* RDATA count in the RRSet. */
+ int rdata_count = knot_rrset_rdata_rr_count(rrsets[i]);
+
+ if (rrsigs != NULL) {
+ /* Increment the RDATA count by the count of
+ * RRSIGs. */
+ rdata_count += knot_rrset_rdata_rr_count(
+ rrsigs);
+ }
+
+ /* Remove old RDATA. */
+
+ ret = knot_changes_rdata_reserve(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated,
+ rdata_count);
+ if (ret != KNOT_EOK) {
+// dbg_xfrin("Failed to reserve changes rdata.\n");
+ return ret;
+ }
+
+ knot_changes_add_rdata(changes->old_rdata,
+ changes->old_rdata_types,
+ &changes->old_rdata_count,
+ knot_rrset_get_rdata(rrsets[i]),
+ knot_rrset_type(rrsets[i]));
+
+ knot_changes_add_rdata(changes->old_rdata,
+ changes->old_rdata_types,
+ &changes->old_rdata_count,
+ knot_rrset_get_rdata(rrsigs),
+ KNOT_RRTYPE_RRSIG);
+ }
+
+ /* Disconnect RRsigs from rrset. */
+ knot_rrset_set_rrsigs(rrsets[i], NULL);
+ changes->old_rrsets[changes->old_rrsets_count++] = rrsets[i];
+ changes->old_rrsets[changes->old_rrsets_count++] = rrsigs;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changes_add_new_rrsets(knot_rrset_t **rrsets, int count,
+ knot_changes_t *changes, int add_rdata)
+{
+ if (rrsets == NULL || changes == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (count == 0) {
+ return KNOT_EOK;
+ }
+
+ int ret = knot_changes_rrsets_reserve(&changes->new_rrsets,
+ &changes->new_rrsets_count,
+ &changes->new_rrsets_allocated,
+ count);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Mark RRsets and RDATA for removal. */
+ for (unsigned i = 0; i < count; ++i) {
+ if (rrsets[i] == NULL) {
+ continue;
+ }
+
+ if (add_rdata) {
+ int rdata_count = knot_rrset_rdata_rr_count(rrsets[i]);
+ ret = knot_changes_rdata_reserve(&changes->new_rdata,
+ &changes->new_rdata_types,
+ changes->new_rdata_count,
+ &changes->new_rdata_allocated,
+ rdata_count);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_changes_add_rdata(changes->new_rdata,
+ changes->new_rdata_types,
+ &changes->new_rdata_count,
+ knot_rrset_get_rdata(rrsets[i]),
+ knot_rrset_type(rrsets[i]));
+ }
+
+ changes->new_rrsets[changes->new_rrsets_count++] = rrsets[i];
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h
index 642b155..4585abc 100644
--- a/src/libknot/updates/changesets.h
+++ b/src/libknot/updates/changesets.h
@@ -30,6 +30,14 @@
#include "rrset.h"
#include "zone/node.h"
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Changeset flags, stored as first 4 bytes in serialized changeset. */
+typedef enum {
+ KNOT_CHANGESET_TYPE_IXFR = 1 << 0,
+ KNOT_CHANGESET_TYPE_DDNS = 1 << 1
+} knot_changeset_flag_t;
+
/*! \todo Changeset must be serializable/deserializable, so
* all data and pointers have to be changeset-exclusive,
* or more advanced structure serialization scheme has to be
@@ -53,6 +61,8 @@ typedef struct {
size_t allocated;
uint32_t serial_from;
uint32_t serial_to;
+
+ uint32_t flags; /*!< DDNS / IXFR */
} knot_changeset_t;
/*----------------------------------------------------------------------------*/
@@ -69,7 +79,7 @@ typedef struct {
* Deleted after successful update.
*/
knot_rdata_t **old_rdata;
- unsigned *old_rdata_types;
+ uint16_t *old_rdata_types;
int old_rdata_count;
int old_rdata_allocated;
@@ -86,7 +96,7 @@ typedef struct {
* Deleted after failed update.
*/
knot_rdata_t **new_rdata;
- unsigned *new_rdata_types;
+ uint16_t *new_rdata_types;
int new_rdata_count;
int new_rdata_allocated;
@@ -109,19 +119,21 @@ typedef struct {
size_t count;
size_t allocated;
knot_rrset_t *first_soa;
+ uint32_t flags;
knot_changes_t *changes;
} knot_changesets_t;
/*----------------------------------------------------------------------------*/
typedef enum {
- XFRIN_CHANGESET_ADD,
- XFRIN_CHANGESET_REMOVE
-} xfrin_changeset_part_t;
+ KNOT_CHANGESET_ADD,
+ KNOT_CHANGESET_REMOVE
+} knot_changeset_part_t;
/*----------------------------------------------------------------------------*/
-int knot_changeset_allocate(knot_changesets_t **changesets);
+int knot_changeset_allocate(knot_changesets_t **changesets,
+ uint32_t flags);
int knot_changeset_add_rrset(knot_rrset_t ***rrsets,
size_t *count, size_t *allocated,
@@ -132,20 +144,51 @@ int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count,
int knot_changeset_add_new_rr(knot_changeset_t *changeset,
knot_rrset_t *rrset,
- xfrin_changeset_part_t part);
+ knot_changeset_part_t part);
+
+knot_rrset_t *knot_changeset_remove_rr(knot_rrset_t **rrsets, size_t *count,
+ int pos);
void knot_changeset_store_soa(knot_rrset_t **chg_soa,
uint32_t *chg_serial, knot_rrset_t *soa);
int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
- xfrin_changeset_part_t part);
+ knot_changeset_part_t part);
int knot_changesets_check_size(knot_changesets_t *changesets);
+void knot_changeset_set_flags(knot_changeset_t *changeset,
+ uint32_t flags);
+
+uint32_t knot_changeset_flags(knot_changeset_t *changeset);
+
+int knot_changeset_is_empty(const knot_changeset_t *changeset);
+
void knot_free_changeset(knot_changeset_t **changeset);
void knot_free_changesets(knot_changesets_t **changesets);
+int knot_changes_rrsets_reserve(knot_rrset_t ***rrsets,
+ int *count, int *allocated, int to_add);
+
+int knot_changes_nodes_reserve(knot_node_t ***nodes,
+ int *count, int *allocated);
+
+int knot_changes_rdata_reserve(knot_rdata_t ***rdatas, uint16_t **types,
+ int count, int *allocated, int to_add);
+
+void knot_changes_add_rdata(knot_rdata_t **rdatas, uint16_t *types,
+ int *count, knot_rdata_t *rdata, uint16_t type);
+
+/*!
+ * \note Also processes RRSIGs. May be switched by a parameter later, if needed.
+ */
+int knot_changes_add_old_rrsets(knot_rrset_t **rrsets, int count,
+ knot_changes_t *changes, int add_rdata);
+
+int knot_changes_add_new_rrsets(knot_rrset_t **rrsets, int count,
+ knot_changes_t *changes, int add_rdata);
+
#endif /* _KNOT_CHANGESETS_H_ */
/*! @} */
diff --git a/src/libknot/updates/ddns.c b/src/libknot/updates/ddns.c
index 72a1be9..90a578f 100644
--- a/src/libknot/updates/ddns.c
+++ b/src/libknot/updates/ddns.c
@@ -21,26 +21,24 @@
#include "util/debug.h"
#include "packet/packet.h"
#include "consts.h"
+#include "common/mempattern.h"
+#include "nameserver/name-server.h" // ns_serial_compare() - TODO: extract
+#include "updates/xfr-in.h"
/*----------------------------------------------------------------------------*/
// Copied from XFR - maybe extract somewhere else
static int knot_ddns_prereq_check_rrsets(knot_rrset_t ***rrsets,
size_t *count, size_t *allocated)
{
- int new_count = 0;
- if (*count == *allocated) {
- new_count = *allocated * 2;
- }
-
- knot_rrset_t **rrsets_new =
- (knot_rrset_t **)calloc(new_count, sizeof(knot_rrset_t *));
- if (rrsets_new == NULL) {
+ /* This is really confusing, it's ptr -> array of "knot_rrset_t*" */
+ char *arr = (char*)*rrsets;
+ int ret = 0;
+ ret = mreserve(&arr, sizeof(knot_rrset_t*), *count + 1, 0, allocated);
+ if (ret < 0) {
return KNOT_ENOMEM;
}
-
- memcpy(rrsets_new, *rrsets, (*count) * sizeof(knot_rrset_t *));
- *rrsets = rrsets_new;
- *allocated = new_count;
+
+ *rrsets = (knot_rrset_t**)arr;
return KNOT_EOK;
}
@@ -50,20 +48,15 @@ static int knot_ddns_prereq_check_rrsets(knot_rrset_t ***rrsets,
static int knot_ddns_prereq_check_dnames(knot_dname_t ***dnames,
size_t *count, size_t *allocated)
{
- int new_count = 0;
- if (*count == *allocated) {
- new_count = *allocated * 2;
- }
-
- knot_dname_t **dnames_new =
- (knot_dname_t **)calloc(new_count, sizeof(knot_dname_t *));
- if (dnames_new == NULL) {
+ /* This is really confusing, it's ptr -> array of "knot_dname_t*" */
+ char *arr = (char*)*dnames;
+ int ret = 0;
+ ret = mreserve(&arr, sizeof(knot_dname_t*), *count + 1, 0, allocated);
+ if (ret < 0) {
return KNOT_ENOMEM;
}
-
- memcpy(dnames_new, *dnames, (*count) * sizeof(knot_dname_t *));
- *dnames = dnames_new;
- *allocated = new_count;
+
+ *dnames = (knot_dname_t**)arr;
return KNOT_EOK;
}
@@ -77,8 +70,8 @@ static int knot_ddns_add_prereq_rrset(const knot_rrset_t *rrset,
// check if such RRSet is not already there and merge if needed
int ret;
for (int i = 0; i < *count; ++i) {
- if (knot_rrset_compare(rrset, (*rrsets)[i],
- KNOT_RRSET_COMPARE_HEADER) == 0) {
+ if (knot_rrset_match(rrset, (*rrsets)[i],
+ KNOT_RRSET_COMPARE_HEADER) == 1) {
ret = knot_rrset_merge((void **)&((*rrsets)[i]),
(void **)&rrset);
if (ret != KNOT_EOK) {
@@ -188,39 +181,119 @@ static int knot_ddns_add_prereq(knot_ddns_prereq_t *prereqs,
/*----------------------------------------------------------------------------*/
+static int knot_ddns_check_remove_rr(knot_changeset_t *changeset,
+ const knot_rrset_t *rr)
+{
+ dbg_ddns_verb("Removing possible redundant RRs from changeset.\n");
+ for (int i = 0; i < changeset->add_count; ++i) {
+ // Removing RR(s) from this owner
+ if (knot_dname_compare(knot_rrset_owner(rr),
+ knot_rrset_owner(changeset->add[i])) == 0) {
+ // Removing one or all RRSets
+ if (knot_rrset_class(rr) == KNOT_CLASS_ANY) {
+ dbg_ddns_detail("Removing one or all "
+ "RRSets\n");
+ if (knot_rrset_type(rr)
+ == knot_rrset_type(changeset->add[i])
+ || knot_rrset_type(rr) == KNOT_RRTYPE_ANY) {
+ knot_rrset_t *remove =
+ knot_changeset_remove_rr(
+ changeset->add,
+ &changeset->add_count, i);
+ dbg_ddns_detail("Removed RRSet from "
+ "chgset:\n");
+ knot_rrset_dump(remove, 0);
+ knot_rrset_deep_free(&remove, 1, 1, 1);
+ }
+ } else if (knot_rrset_type(rr)
+ == knot_rrset_type(changeset->add[i])){
+ /* All other classes are checked in
+ * knot_ddns_check_update().
+ */
+ assert(knot_rrset_class(rr) == KNOT_CLASS_NONE);
+
+ // Removing specific RR from a RRSet
+ knot_rdata_t *rdata = knot_rrset_remove_rdata(
+ changeset->add[i],
+ knot_rrset_rdata(rr));
+
+ dbg_ddns_detail("Removed RR from chgset: \n");
+ knot_rdata_dump(rdata, knot_rrset_type(rr), 0);
+
+ knot_rdata_deep_free(&rdata,
+ knot_rrset_type(changeset->add[i]), 1);
+ // if the RRSet is empty, remove from changeset
+ if (knot_rrset_rdata_rr_count(changeset->add[i])
+ == 0) {
+ knot_rrset_t *remove =
+ knot_changeset_remove_rr(
+ changeset->add,
+ &changeset->add_count, i);
+ dbg_ddns_detail("RRSet empty, removing."
+ "\n");
+ knot_rrset_deep_free(&remove, 1, 1, 1);
+ }
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
static int knot_ddns_add_update(knot_changeset_t *changeset,
- const knot_rrset_t *rrset, uint16_t qclass)
+ const knot_rrset_t *rrset, uint16_t qclass,
+ knot_rrset_t **rrset_copy)
{
assert(changeset != NULL);
assert(rrset != NULL);
+ assert(rrset_copy != NULL);
int ret;
// create a copy of the RRSet
- /*! \todo If the packet was not parsed all at once, we could save this
+ /*! \todo ref #937 If the packet was not parsed all at once, we could save this
* copy.
*/
- knot_rrset_t *rrset_copy;
- ret = knot_rrset_deep_copy(rrset, &rrset_copy, 0);
+ *rrset_copy = NULL;
+ ret = knot_rrset_deep_copy(rrset, rrset_copy, 0);
if (ret != KNOT_EOK) {
return ret;
}
- /*! \todo What about the SOAs? */
-
if (knot_rrset_class(rrset) == qclass) {
// this RRSet should be added to the zone
+ dbg_ddns_detail(" * adding RR %p\n", *rrset_copy);
ret = knot_changeset_add_rr(&changeset->add,
&changeset->add_count,
&changeset->add_allocated,
- rrset_copy);
+ *rrset_copy);
} else {
// this RRSet marks removal of something from zone
- // what should be removed is distinguished when applying
+
+ /* To imitate in-order processing of UPDATE RRs, we must check
+ * If this REMOVE RR does not affect any of the previous
+ * ADD RRs in this update. If yes, they must be removed from
+ * the changeset.
+ *
+ * See https://git.nic.cz/redmine/issues/937#note-14 and below.
+ */
+
+ // TODO: finish, disabled for now
+
+ dbg_ddns_detail(" * removing RR %p\n", *rrset_copy);
+
+ ret = knot_ddns_check_remove_rr(changeset, *rrset_copy);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(rrset_copy, 1, 1, 1);
+ return ret;
+ }
+
ret = knot_changeset_add_rr(&changeset->remove,
&changeset->remove_count,
&changeset->remove_allocated,
- rrset_copy);
+ *rrset_copy);
}
return ret;
@@ -229,7 +302,7 @@ static int knot_ddns_add_update(knot_changeset_t *changeset,
/*----------------------------------------------------------------------------*/
static int knot_ddns_check_exist(const knot_zone_contents_t *zone,
- const knot_rrset_t *rrset, uint8_t *rcode)
+ const knot_rrset_t *rrset, knot_rcode_t *rcode)
{
assert(zone != NULL);
assert(rrset != NULL);
@@ -261,7 +334,8 @@ static int knot_ddns_check_exist(const knot_zone_contents_t *zone,
/*----------------------------------------------------------------------------*/
static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone,
- const knot_rrset_t *rrset, uint8_t *rcode)
+ const knot_rrset_t *rrset,
+ knot_rcode_t *rcode)
{
assert(zone != NULL);
assert(rrset != NULL);
@@ -305,7 +379,8 @@ static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone,
/*----------------------------------------------------------------------------*/
static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone,
- const knot_rrset_t *rrset, uint8_t *rcode)
+ const knot_rrset_t *rrset,
+ knot_rcode_t *rcode)
{
assert(zone != NULL);
assert(rrset != NULL);
@@ -322,23 +397,15 @@ static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone,
}
const knot_node_t *node;
- const knot_rrset_t *found;
node = knot_zone_contents_find_node(zone, knot_rrset_owner(rrset));
if (node == NULL) {
return KNOT_EOK;
- } else if ((found = knot_node_rrset(node, knot_rrset_type(rrset)))
- == NULL) {
+ } else if (knot_node_rrset(node, knot_rrset_type(rrset)) == NULL) {
return KNOT_EOK;
- } else {
- // do not have to compare the header, it is already done
- assert(knot_rrset_type(found) == knot_rrset_type(rrset));
- assert(knot_dname_compare(knot_rrset_owner(found),
- knot_rrset_owner(rrset)) == 0);
- if (knot_rrset_compare_rdata(found, rrset) <= 0) {
- return KNOT_EOK;
- }
}
+
+ /* RDATA is always empty for simple RRset checks. */
*rcode = KNOT_RCODE_YXRRSET;
return KNOT_EPREREQ;
@@ -347,7 +414,8 @@ static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone,
/*----------------------------------------------------------------------------*/
static int knot_ddns_check_in_use(const knot_zone_contents_t *zone,
- const knot_dname_t *dname, uint8_t *rcode)
+ const knot_dname_t *dname,
+ knot_rcode_t *rcode)
{
assert(zone != NULL);
assert(dname != NULL);
@@ -376,7 +444,8 @@ static int knot_ddns_check_in_use(const knot_zone_contents_t *zone,
/*----------------------------------------------------------------------------*/
static int knot_ddns_check_not_in_use(const knot_zone_contents_t *zone,
- const knot_dname_t *dname, uint8_t *rcode)
+ const knot_dname_t *dname,
+ knot_rcode_t *rcode)
{
assert(zone != NULL);
assert(dname != NULL);
@@ -405,10 +474,13 @@ static int knot_ddns_check_not_in_use(const knot_zone_contents_t *zone,
/* API functions */
/*----------------------------------------------------------------------------*/
-int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query,
- uint8_t *rcode)
+int knot_ddns_check_zone(const knot_zone_contents_t *zone,
+ const knot_packet_t *query, knot_rcode_t *rcode)
{
if (zone == NULL || query == NULL || rcode == NULL) {
+ if (rcode != NULL) {
+ *rcode = KNOT_RCODE_SERVFAIL;
+ }
return KNOT_EINVAL;
}
@@ -417,18 +489,8 @@ int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query,
return KNOT_EMALF;
}
- if(!knot_zone_contents(zone)) {
- *rcode = KNOT_RCODE_REFUSED;
- return KNOT_ENOZONE;
- }
-
- // 1) check if the zone is master or slave
- if (!knot_zone_is_master(zone)) {
- return KNOT_EBADZONE;
- }
-
- // 2) check zone CLASS
- if (knot_zone_contents_class(knot_zone_contents(zone)) !=
+ // check zone CLASS
+ if (knot_zone_contents_class(zone) !=
knot_packet_qclass(query)) {
*rcode = KNOT_RCODE_NOTAUTH;
return KNOT_ENOZONE;
@@ -439,8 +501,8 @@ int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query,
/*----------------------------------------------------------------------------*/
-int knot_ddns_process_prereqs(knot_packet_t *query,
- knot_ddns_prereq_t **prereqs, uint8_t *rcode)
+int knot_ddns_process_prereqs(const knot_packet_t *query,
+ knot_ddns_prereq_t **prereqs, knot_rcode_t *rcode)
{
/*! \todo Consider not parsing the whole packet at once, but
* parsing one RR at a time - could save some memory and time.
@@ -449,6 +511,8 @@ int knot_ddns_process_prereqs(knot_packet_t *query,
if (query == NULL || prereqs == NULL || rcode == NULL) {
return KNOT_EINVAL;
}
+
+ dbg_ddns("Processing prerequisities.\n");
// allocate space for the prerequisities
*prereqs = (knot_ddns_prereq_t *)calloc(1, sizeof(knot_ddns_prereq_t));
@@ -478,9 +542,11 @@ int knot_ddns_process_prereqs(knot_packet_t *query,
/*----------------------------------------------------------------------------*/
int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
- knot_ddns_prereq_t **prereqs, uint8_t *rcode)
+ knot_ddns_prereq_t **prereqs, knot_rcode_t *rcode)
{
int i, ret;
+
+ dbg_ddns("Checking 'exist' prerequisities.\n");
for (i = 0; i < (*prereqs)->exist_count; ++i) {
ret = knot_ddns_check_exist(zone, (*prereqs)->exist[i], rcode);
@@ -489,6 +555,7 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
}
}
+ dbg_ddns("Checking 'exist full' prerequisities.\n");
for (i = 0; i < (*prereqs)->exist_full_count; ++i) {
ret = knot_ddns_check_exist_full(zone,
(*prereqs)->exist_full[i],
@@ -498,6 +565,7 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
}
}
+ dbg_ddns("Checking 'not exist' prerequisities.\n");
for (i = 0; i < (*prereqs)->not_exist_count; ++i) {
ret = knot_ddns_check_not_exist(zone, (*prereqs)->not_exist[i],
rcode);
@@ -506,6 +574,7 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
}
}
+ dbg_ddns("Checking 'in use' prerequisities.\n");
for (i = 0; i < (*prereqs)->in_use_count; ++i) {
ret = knot_ddns_check_in_use(zone, (*prereqs)->in_use[i],
rcode);
@@ -514,6 +583,7 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
}
}
+ dbg_ddns("Checking 'not in use' prerequisities.\n");
for (i = 0; i < (*prereqs)->not_in_use_count; ++i) {
ret = knot_ddns_check_not_in_use(zone,
(*prereqs)->not_in_use[i],
@@ -529,10 +599,15 @@ int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
/*----------------------------------------------------------------------------*/
static int knot_ddns_check_update(const knot_rrset_t *rrset,
- const knot_packet_t *query, uint8_t *rcode)
+ const knot_packet_t *query,
+ knot_rcode_t *rcode)
{
- if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
- knot_packet_qname(query))) {
+ /* Accept both subdomain and dname match. */
+ dbg_ddns("Checking UPDATE packet.\n");
+ const knot_dname_t *owner = knot_rrset_owner(rrset);
+ const knot_dname_t *qname = knot_packet_qname(query);
+ int is_sub = knot_dname_is_subdomain(owner, qname);
+ if (!is_sub && knot_dname_compare(owner, qname) != 0) {
*rcode = KNOT_RCODE_NOTZONE;
return KNOT_EBADZONE;
}
@@ -565,8 +640,9 @@ static int knot_ddns_check_update(const knot_rrset_t *rrset,
/*----------------------------------------------------------------------------*/
-int knot_ddns_process_update(knot_packet_t *query,
- knot_changeset_t **changeset, uint8_t *rcode)
+int knot_ddns_process_update(const knot_zone_contents_t *zone,
+ const knot_packet_t *query,
+ knot_changeset_t *changeset, knot_rcode_t *rcode)
{
// just put all RRSets from query's Authority section
// it will be distinguished when applying to the zone
@@ -575,63 +651,1739 @@ int knot_ddns_process_update(knot_packet_t *query,
return KNOT_EINVAL;
}
- *changeset = (knot_changeset_t *)calloc(1, sizeof(knot_changeset_t));
- CHECK_ALLOC_LOG(*changeset, KNOT_ENOMEM);
+ int ret = KNOT_EOK;
+
+ /* Copy base SOA query. */
+ const knot_rrset_t *soa = knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_SOA);
+ knot_rrset_t *soa_begin = NULL;
+ knot_rrset_t *soa_end = NULL;
+ ret = knot_rrset_deep_copy(soa, &soa_begin, 0);
+ if (ret == KNOT_EOK) {
+ knot_changeset_store_soa(&changeset->soa_from,
+ &changeset->serial_from, soa_begin);
+ } else {
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return ret;
+ }
- int ret;
+ /* Current SERIAL */
+ int64_t sn = knot_rdata_soa_serial(knot_rrset_rdata(soa_begin));
+ int64_t sn_new;
+ /* Incremented SERIAL
+ * We must set it now to be able to compare SERIAL from SOAs in the
+ * UPDATE to it. Although we do not have the new SOA yet.
+ */
+ if (sn > -1) {
+ sn_new = (uint32_t)sn + 1;
+ } else {
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return ret;
+ }
+
+ const knot_rrset_t *rrset = NULL;
+ knot_rrset_t *rrset_copy = NULL;
+ dbg_ddns("Processing UPDATE section.\n");
for (int i = 0; i < knot_packet_authority_rrset_count(query); ++i) {
- const knot_rrset_t *rrset =
- knot_packet_authority_rrset(query, i);
+ rrset = knot_packet_authority_rrset(query, i);
ret = knot_ddns_check_update(rrset, query, rcode);
if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to check update RRSet:%s\n",
+ knot_strerror(ret));
return ret;
}
- ret = knot_ddns_add_update(*changeset, rrset,
- knot_packet_qclass(query));
+ ret = knot_ddns_add_update(changeset, rrset,
+ knot_packet_qclass(query),
+ &rrset_copy);
if (ret != KNOT_EOK) {
dbg_ddns("Failed to add update RRSet:%s\n",
knot_strerror(ret));
*rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
: KNOT_RCODE_SERVFAIL;
- knot_free_changeset(changeset);
return ret;
}
+
+ /* Check if the added record is SOA. If yes, check the SERIAL.
+ * If this record should cause the SOA to be replaced in the
+ * zone, use it as the ending SOA.
+ *
+ * Also handle cases where there are multiple SOAs to be added
+ * in the same UPDATE. The one with the largest SERIAL should
+ * be used.
+ *
+ * TODO: If there are more SOAs in the UPDATE one after another,
+ * the ddns_add_update() function will merge them into a
+ * RRSet. This should be handled somehow.
+ */
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA
+ && ns_serial_compare(knot_rdata_soa_serial(
+ knot_rrset_rdata(rrset)),
+ sn_new) > 0) {
+ sn_new = knot_rdata_soa_serial(knot_rrset_rdata(rrset));
+ soa_end = (knot_rrset_t *)rrset_copy;
+ }
}
- return KNOT_EOK;
+ /* Ending SOA */
+ if (soa_end == NULL) {
+ /* If not set */
+ assert(sn_new == (uint32_t)sn + 1);
+ ret = knot_rrset_deep_copy(soa, &soa_end, 1);
+ knot_rdata_t *rd = knot_rrset_get_rdata(soa_end);
+ knot_rdata_soa_serial_set(rd, sn_new);
+ }
+
+ knot_changeset_store_soa(&changeset->soa_to,
+ &changeset->serial_to,
+ soa_end);
+
+ return ret;
}
/*----------------------------------------------------------------------------*/
void knot_ddns_prereqs_free(knot_ddns_prereq_t **prereq)
{
+ dbg_ddns("Freeing prerequisities.\n");
+
int i;
for (i = 0; i < (*prereq)->exist_count; ++i) {
knot_rrset_deep_free(&(*prereq)->exist[i], 1, 1, 1);
}
+ free((*prereq)->exist);
for (i = 0; i < (*prereq)->exist_full_count; ++i) {
knot_rrset_deep_free(&(*prereq)->exist_full[i], 1, 1, 1);
}
+ free((*prereq)->exist_full);
for (i = 0; i < (*prereq)->not_exist_count; ++i) {
knot_rrset_deep_free(&(*prereq)->not_exist[i], 1, 1, 1);
}
+ free((*prereq)->not_exist);
for (i = 0; i < (*prereq)->in_use_count; ++i) {
knot_dname_free(&(*prereq)->in_use[i]);
}
+ free((*prereq)->in_use);
for (i = 0; i < (*prereq)->not_in_use_count; ++i) {
knot_dname_free(&(*prereq)->not_in_use[i]);
}
+ free((*prereq)->not_in_use);
free(*prereq);
*prereq = NULL;
}
+
+/*----------------------------------------------------------------------------*/
+/* New DDNS processing - */
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_remove_rr2(knot_changeset_t *changeset,
+ const knot_dname_t *owner,
+ uint16_t type, const knot_rdata_t *rdata,
+ knot_rrset_t ***removed,
+ size_t *removed_count)
+{
+ assert(changeset != NULL);
+ assert(removed != NULL);
+ assert(removed_count != NULL);
+
+ *removed_count = 0;
+ *removed = (knot_rrset_t **)malloc(changeset->add_count
+ * sizeof(knot_rrset_t *));
+ if (*removed == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_t *remove = NULL;
+
+ /*
+ * We assume that each RR in the ADD section of the changeset is in its
+ * own RRSet. It should be as this is how they are stored there by the
+ * ddns_process_add() function.
+ */
+
+ dbg_ddns_verb("Removing possible redundant RRs from changeset.\n");
+ for (int i = 0; i < changeset->add_count; ++i) {
+ // Removing RR(s) from this owner
+ if (knot_dname_compare(knot_rrset_owner(changeset->add[i]),
+ owner) == 0) {
+ // Removing one or all RRSets
+ if (rdata == NULL
+ && (type == knot_rrset_type(changeset->add[i])
+ || type == KNOT_RRTYPE_ANY)) {
+ dbg_ddns_detail("Removing one or all RRSets\n");
+ remove = knot_changeset_remove_rr(
+ changeset->add,
+ &changeset->add_count, i);
+ } else if (type == knot_rrset_type(changeset->add[i])) {
+ // Removing specific RR
+ assert(rdata != NULL);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+
+ // We must check if the RDATA match
+ if (knot_rdata_compare(rdata,
+ knot_rrset_rdata(changeset->add[i]),
+ desc->wireformat)) {
+ remove = knot_changeset_remove_rr(
+ changeset->add,
+ &changeset->add_count, i);
+ }
+ }
+
+ dbg_ddns_detail("Removed RRSet from chgset:\n");
+ knot_rrset_dump(remove, 0);
+ (*removed)[(*removed_count)++] = remove;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_ddns_check_add_rr(knot_changeset_t *changeset,
+ const knot_rrset_t *rr,
+ knot_rrset_t **removed)
+{
+ assert(changeset != NULL);
+ assert(rr != NULL);
+ assert(removed != NULL);
+
+ *removed = NULL;
+
+ dbg_ddns_verb("Removing possible redundant RRs from changeset.\n");
+ for (int i = 0; i < changeset->remove_count; ++i) {
+ /* Just check exact match, the changeset contains only
+ * whole RRs that have been removed.
+ */
+ if (knot_rrset_match(rr, changeset->remove[i],
+ KNOT_RRSET_COMPARE_WHOLE) == 1) {
+ *removed = knot_changeset_remove_rr(
+ changeset->remove,
+ &changeset->remove_count, i);
+ dbg_ddns_detail("Removed RRSet from chgset:\n");
+ knot_rrset_dump(*removed, 0);
+ break;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_rr_is_nsec3(const knot_rrset_t *rr)
+{
+ assert(rr != NULL);
+
+ if ((knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3)
+ || (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG
+ && knot_rrset_rdata(rr)
+ && knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr))
+ == KNOT_RRTYPE_NSEC3))
+ {
+ dbg_ddns_detail("This is NSEC3-related RRSet.\n");
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*! \note Copied from xfrin_add_new_node(). */
+static knot_node_t *knot_ddns_add_new_node(knot_zone_contents_t *zone,
+ knot_dname_t *owner, int is_nsec3)
+{
+ assert(zone != NULL);
+ assert(owner != NULL);
+
+ knot_node_t *node = knot_node_new(owner, NULL, 0);
+ if (node == NULL) {
+ dbg_xfrin("Failed to create a new node.\n");
+ return NULL;
+ }
+
+ int ret = 0;
+
+ // insert the node into zone structures and create parents if
+ // necessary
+ if (is_nsec3) {
+ ret = knot_zone_contents_add_nsec3_node(zone, node, 1, 0, 1);
+ } else {
+ ret = knot_zone_contents_add_node(zone, node, 1, 0, 1);
+ }
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new node to zone contents.\n");
+ knot_node_free(&node);
+ return NULL;
+ }
+
+ /*!
+ * \note It is not needed to set the previous node, we will do this
+ * in adjusting after the transfer.
+ */
+ assert(zone->zone != NULL);
+ //knot_node_set_zone(node, zone->zone);
+ assert(node->zone == zone->zone);
+
+ return node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_node_t *knot_ddns_get_node(knot_zone_contents_t *zone,
+ const knot_rrset_t *rr)
+{
+ assert(zone != NULL);
+ assert(rr != NULL);
+
+ knot_node_t *node = NULL;
+ knot_dname_t *owner = knot_rrset_get_owner(rr);
+
+ dbg_ddns_detail("Searching for node...\n");
+ if (knot_ddns_rr_is_nsec3(rr)) {
+ node = knot_zone_contents_get_nsec3_node(zone, owner);
+ } else {
+ node = knot_zone_contents_get_node(zone, owner);
+ }
+
+ return node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_process_add_cname(knot_node_t *node,
+ const knot_rrset_t *rr,
+ knot_changeset_t *changeset,
+ knot_changes_t *changes)
+{
+ assert(node != NULL);
+ assert(rr != NULL);
+ assert(changeset != NULL);
+ assert(changes != NULL);
+
+ dbg_ddns_detail("Adding CNAME RR.\n");
+
+ int ret = 0;
+
+ /* Get the current CNAME RR from the node. */
+ knot_rrset_t *removed = knot_node_get_rrset(node, KNOT_RRTYPE_CNAME);
+
+ if (removed != NULL) {
+ /* If they are identical, ignore. */
+ if (knot_rrset_match(removed, rr, KNOT_RRSET_COMPARE_WHOLE)
+ == 1) {
+ dbg_ddns_verb("CNAME identical to one in the node.\n");
+ return 1;
+ }
+
+ /*! \note
+ * Together with the removed CNAME we remove also its RRSIGs as
+ * they would not be valid for the new CNAME anyway.
+ *
+ * \todo Document!!
+ */
+
+ /* b) Store it to 'changes', together with its RRSIGs. */
+ ret = knot_changes_add_old_rrsets(&removed, 1, changes, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add removed RRSet to "
+ "'changes': %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* c) And remove it from the node. */
+ (void)knot_node_remove_rrset(node, KNOT_RRTYPE_CNAME);
+
+ /* d) Check if this CNAME was not previously added by
+ * the UPDATE. If yes, remove it from the ADD
+ * section and do not add it to the REMOVE section.
+ */
+ knot_rrset_t **from_chgset = NULL;
+ size_t from_chgset_count = 0;
+ ret = knot_ddns_check_remove_rr2(
+ changeset, knot_rrset_owner(removed),
+ KNOT_RRTYPE_CNAME, knot_rrset_rdata(removed),
+ &from_chgset, &from_chgset_count);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to remove possible redundant "
+ "RRs from ADD section: %s.\n",
+ knot_strerror(ret));
+ free(from_chgset);
+ return ret;
+ }
+
+ assert(from_chgset_count <= 1);
+
+ if (from_chgset_count == 1) {
+ /* Just delete the RRSet. */
+ knot_rrset_deep_free(&(from_chgset[0]), 1, 1, 1);
+ /* Okay, &(from_chgset[0]) is basically equal to just
+ * from_chgset, but it's more clear this way that we are
+ * deleting the first RRSet in the array ;-)
+ */
+ } else {
+ /* Otherwise copy the removed CNAME and add it
+ * to the REMOVE section.
+ */
+ knot_rrset_t *removed_copy;
+ ret = knot_rrset_deep_copy(removed,
+ &removed_copy, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy removed RRSet:"
+ " %s\n", knot_strerror(ret));
+ free(from_chgset);
+ return ret;
+ }
+
+ ret = knot_changeset_add_rrset(
+ &changeset->remove,
+ &changeset->remove_count,
+ &changeset->remove_allocated,
+ removed_copy);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&removed_copy,
+ 1, 1, 1);
+ dbg_ddns("Failed to add removed CNAME "
+ "to changeset: %s\n",
+ knot_strerror(ret));
+ free(from_chgset);
+ return ret;
+ }
+ }
+ free(from_chgset);
+ } else if (knot_node_rrset_count(node) != 0) {
+ /* 2) Other occupied node => ignore. */
+ return 1;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_process_add_soa(knot_node_t *node,
+ const knot_rrset_t *rr,
+ knot_changes_t *changes)
+{
+ assert(node != NULL);
+ assert(rr != NULL);
+ assert(changes != NULL);
+
+ dbg_ddns_detail("Adding SOA RR.\n");
+
+ int ret = 0;
+
+ /*
+ * Just remove the SOA from the node, together with its RRSIGs.
+ * Adding the RR is done in the caller function. Note that only SOA
+ * with larger SERIAL than the current one will get to these functions,
+ * so we don't have to check the SERIALS again. But an assert won't
+ * hurt.
+ */
+
+ /* Get the current SOA RR from the node. */
+ knot_rrset_t *removed = knot_node_get_rrset(node, KNOT_RRTYPE_SOA);
+
+ if (removed != NULL) {
+ dbg_ddns_detail("Found SOA in the node.\n");
+ /* If they are identical, ignore. */
+ if (knot_rrset_match(removed, rr, KNOT_RRSET_COMPARE_WHOLE)
+ == 1) {
+ dbg_ddns_detail("Old and new SOA identical.\n");
+ return 1;
+ }
+
+ /* Check that the serial is indeed larger than the current one*/
+ assert(ns_serial_compare(knot_rdata_soa_serial(
+ knot_rrset_rdata(removed)),
+ knot_rdata_soa_serial(
+ knot_rrset_rdata(rr))) < 0);
+
+ /* 1) Store it to 'changes', together with its RRSIGs. */
+ ret = knot_changes_add_old_rrsets(
+ &removed, 1, changes, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add removed RRSet to "
+ "'changes': %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* 2) And remove it from the node. */
+ (void)knot_node_remove_rrset(node, KNOT_RRTYPE_SOA);
+
+ /* No changeset processing needed in this case. */
+ } else {
+ dbg_ddns_detail("No SOA in node, ignoring.\n");
+ /* If there is no SOA in the node, ignore. */
+ return 1;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_rr_new_normal(knot_node_t *node, knot_rrset_t *rr_copy,
+ knot_changes_t *changes)
+{
+ assert(node != NULL);
+ assert(rr_copy != NULL);
+ assert(changes != NULL);
+
+ dbg_ddns_verb("Adding normal RR.\n");
+
+ /* Add the RRSet to 'changes'. */
+ int ret = knot_changes_add_new_rrsets(&rr_copy, 1, changes, 1);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&rr_copy, 1, 1, 1);
+ dbg_ddns("Failed to store copy of the added RR: "
+ "%s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Add the RRSet to the node. */
+ ret = knot_node_add_rrset(node, rr_copy, 0);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add RR to node: %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_rr_new_rrsig(knot_node_t *node, knot_rrset_t *rr_copy,
+ knot_changes_t *changes,
+ uint16_t type_covered)
+{
+ assert(node != NULL);
+ assert(rr_copy != NULL);
+ assert(changes != NULL);
+
+ dbg_ddns_verb("Adding RRSIG RR.\n");
+
+ /* Create RRSet to be covered by the RRSIG. */
+ knot_rrset_t *covered_rrset = knot_rrset_new(
+ knot_rrset_get_owner(rr_copy), type_covered,
+ knot_rrset_class(rr_copy),
+ knot_rrset_ttl(rr_copy));
+ if (covered_rrset == NULL) {
+ dbg_ddns("Failed to create RRSet to be covered"
+ " by the UPDATE RRSIG RR.\n");
+ knot_rrset_deep_free(&rr_copy, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+
+ /* Add the RRSet to the node. */
+ int ret = knot_node_add_rrset(node, covered_rrset, 0);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add the RRSet to be covered to the node: %s"
+ ".\n", knot_strerror(ret));
+ knot_rrset_deep_free(&rr_copy, 1, 1, 1);
+ knot_rrset_deep_free(&covered_rrset, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+
+ /* Add the RRSet to 'changes'. */
+ ret = knot_changes_add_new_rrsets(&covered_rrset, 1, changes, 0);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new RRSet (covered) to list: %s.\n",
+ knot_strerror(ret));
+ knot_rrset_deep_free(&rr_copy, 1, 1, 1);
+ knot_rrset_deep_free(&covered_rrset, 1, 1, 1);
+ return ret;
+ }
+
+ /* Add the RRSIG RRSet to 'changes'. */
+ ret = knot_changes_add_new_rrsets(&rr_copy, 1, changes, 1);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&rr_copy, 1, 1, 1);
+ dbg_ddns("Failed to store copy of the added RRSIG: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Add the RRSIG RRSet to the covered RRSet. */
+ ret = knot_rrset_add_rrsigs(covered_rrset, rr_copy,
+ KNOT_RRSET_DUPL_SKIP);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add RRSIG RR to the covered RRSet.\n");
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_rr_merge_normal(knot_rrset_t *node_rrset_copy,
+ knot_rrset_t **rr_copy)
+{
+ assert(node_rrset_copy != NULL);
+ assert(rr_copy != NULL);
+ assert(*rr_copy != NULL);
+
+ dbg_ddns_verb("Merging normal RR to existing RRSet.\n");
+
+ /* In case the RRSet is empty (and only remained there because
+ * of the RRSIGs) it may happen that the TTL may be different
+ * than that of he new RRs. Update the TTL according to the
+ * first RR.
+ */
+ if (knot_rrset_rdata(node_rrset_copy) == NULL
+ && knot_rrset_ttl(node_rrset_copy)
+ != knot_rrset_ttl(*rr_copy)) {
+ knot_rrset_set_ttl(node_rrset_copy,
+ knot_rrset_ttl(*rr_copy));
+ }
+
+ int rdata_in_copy = knot_rrset_rdata_rr_count(*rr_copy);
+ int ret = knot_rrset_merge_no_dupl((void **)&node_rrset_copy,
+ (void **)rr_copy);
+ dbg_ddns_detail("Merge returned: %d\n", ret);
+
+ if (ret < 0) {
+ dbg_ddns("Failed to merge UPDATE RR to node RRSet: %s."
+ "\n", knot_strerror(ret));
+ return ret;
+ }
+
+ knot_rrset_free(rr_copy);
+
+ if (rdata_in_copy == ret) {
+ /* All RDATA have been removed, because they were duplicates
+ * or there were none (0). In general this means, that no
+ * change was made.
+ */
+ return 1;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_rr_merge_rrsig(knot_rrset_t *node_rrset_copy,
+ knot_rrset_t **rr_copy,
+ knot_changes_t *changes)
+{
+ assert(node_rrset_copy != NULL);
+ assert(rr_copy != NULL);
+ assert(*rr_copy != NULL);
+ assert(changes != NULL);
+
+ dbg_ddns_verb("Adding RRSIG RR to existing RRSet.\n");
+
+ knot_rrset_t *rrsigs_old = knot_rrset_get_rrsigs(node_rrset_copy);
+ int ret = 0;
+
+ if (rrsigs_old != NULL) {
+ /* If there is an RRSIG RRSet already, copy it too. */
+ knot_rrset_t *rrsigs_copy = NULL;
+ ret = xfrin_copy_old_rrset(rrsigs_old, &rrsigs_copy,
+ changes, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy RRSIG RRSet: "
+ "%s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Replace the RRSIGs by the copy. */
+ ret = knot_rrset_set_rrsigs(node_rrset_copy, rrsigs_copy);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to replace RRSIGs in "
+ "the RRSet: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Merge the UPDATE RR to the copied RRSIG
+ * RRSet.
+ */
+ dbg_ddns_detail("Merging RRSIG to the one in the RRSet.\n");
+
+ int rdata_in_copy = knot_rrset_rdata_rr_count(*rr_copy);
+ ret = knot_rrset_merge_no_dupl(
+ (void **)&rrsigs_copy, (void **)rr_copy);
+ if (ret < 0) {
+ dbg_xfrin("Failed to merge UPDATE RRSIG to copy: %s.\n",
+ knot_strerror(ret));
+ return KNOT_ERROR;
+ }
+
+ knot_rrset_free(rr_copy);
+
+ if (rdata_in_copy == ret) {
+ /* All RDATA have been removed, because they were
+ * duplicates or there were none (0). In general this
+ * means, that no change was made.
+ */
+ return 1;
+ }
+ } else {
+ /* If there is no RRSIG RRSet yet, just add the
+ * UPDATE RR to the copied covered RRSet.
+ */
+ /* Add the RRSet to 'changes'. */
+ ret = knot_changes_add_new_rrsets(rr_copy, 1, changes, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to store copy of the added RR: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Add the RRSet to the covered RRSet. */
+ ret = knot_rrset_add_rrsigs(node_rrset_copy, *rr_copy,
+ KNOT_RRSET_DUPL_SKIP);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add RRSIG RR to the"
+ " covered RRSet.\n");
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \todo We should check, how it's possible that IXFR is not leaking due to the
+ * same issue with merge. Or maybe it is, we should try it!!
+ */
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_rr(knot_node_t *node, const knot_rrset_t *rr,
+ knot_changes_t *changes, knot_rrset_t **rr_copy)
+{
+ assert(node != NULL);
+ assert(rr != NULL);
+ assert(changes != NULL);
+ assert(rr_copy != NULL);
+
+ /* Copy the RRSet from the packet. */
+ //knot_rrset_t *rr_copy;
+ int ret = knot_rrset_deep_copy(rr, rr_copy, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy RR: %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ uint16_t type = knot_rrset_type(rr);
+ uint16_t type_covered = (type == KNOT_RRTYPE_RRSIG)
+ ? knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr))
+ : type;
+
+ /* If the RR belongs to a RRSet already present in the node, we must
+ * take this RRSet from the node, copy it, and merge this RR into it.
+ *
+ * This code is more or less copied from xfr-in.c.
+ */
+ knot_rrset_t *node_rrset_copy = NULL;
+ ret = xfrin_copy_rrset(node, type_covered, &node_rrset_copy, changes,
+ 0);
+
+ if (node_rrset_copy == NULL) {
+ /* No such RRSet in the node. Add the whole UPDATE RRSet. */
+ dbg_ddns_detail("Adding whole UPDATE RR to the zone.\n");
+ if (type_covered != type) {
+ /* Adding RRSIG. */
+ ret = knot_ddns_add_rr_new_rrsig(node, *rr_copy,
+ changes, type_covered);
+ } else {
+ ret = knot_ddns_add_rr_new_normal(node, *rr_copy,
+ changes);
+ }
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add new RR to node.\n");
+ return ret;
+ }
+ } else {
+ /* We have copied the RRSet from the node. */
+dbg_ddns_exec_detail(
+ dbg_ddns_detail("Merging RR to an existing RRSet.\n");
+ knot_rrset_dump(node_rrset_copy, 1);
+ dbg_ddns_detail("New RR:\n");
+ knot_rrset_dump(*rr_copy, 0);
+);
+
+ if (type_covered != type) {
+ /* Adding RRSIG. */
+ ret = knot_ddns_add_rr_merge_rrsig(node_rrset_copy,
+ rr_copy, changes);
+ } else {
+ ret = knot_ddns_add_rr_merge_normal(node_rrset_copy,
+ rr_copy);
+ }
+
+dbg_ddns_exec_detail(
+ dbg_ddns_detail("After merge:\n");
+ knot_rrset_dump(node_rrset_copy, 1);
+);
+
+ if (ret < KNOT_EOK) {
+ dbg_ddns("Failed to merge UPDATE RR to node RRSet.\n");
+ knot_rrset_deep_free(rr_copy, 1, 1, 1);
+ knot_rrset_deep_free(&node_rrset_copy, 1, 1, 1);
+ return ret;
+ }
+
+ // save the new RRSet together with the new RDATA to 'changes'
+ // do not overwrite 'ret', it have to be returned
+ int r = knot_changes_add_new_rrsets(&node_rrset_copy, 1,
+ changes, 1);
+ if (r != KNOT_EOK) {
+ dbg_ddns("Failed to store RRSet copy to 'changes'\n");
+ knot_rrset_deep_free(&node_rrset_copy, 1, 1, 1);
+ return r;
+ }
+ }
+
+ assert(ret >= 0);
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_final_soa_to_chgset(const knot_rrset_t *soa,
+ knot_changeset_t *changeset)
+{
+ assert(soa != NULL);
+ assert(changeset != NULL);
+
+ knot_rrset_t *soa_copy = NULL;
+ int ret = knot_rrset_deep_copy(soa, &soa_copy, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy SOA RR to the changeset: "
+ "%s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ knot_changeset_store_soa(&changeset->soa_to,
+ &changeset->serial_to,
+ soa_copy);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_rr_to_chgset(const knot_rrset_t *rr,
+ knot_changeset_t *changeset)
+{
+ assert(rr != NULL);
+ assert(changeset != NULL);
+
+ int ret = 0;
+ knot_rrset_t *chgset_rr = NULL;
+ knot_ddns_check_add_rr(changeset, rr, &chgset_rr);
+ if (chgset_rr == NULL) {
+ ret = knot_rrset_deep_copy(rr, &chgset_rr, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy RR to the changeset: "
+ "%s\n", knot_strerror(ret));
+ return ret;
+ }
+ /* No such RR in the changeset, add it. */
+ ret = knot_changeset_add_rrset(&changeset->add,
+ &changeset->add_count,
+ &changeset->add_allocated,
+ chgset_rr);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&chgset_rr, 1, 1, 1);
+ dbg_ddns("Failed to add RR to changeset: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+ } else {
+ knot_rrset_deep_free(&chgset_rr, 1, 1, 1);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_process_add(const knot_rrset_t *rr,
+ knot_node_t *node,
+ knot_zone_contents_t *zone,
+ knot_changeset_t *changeset,
+ knot_changes_t *changes,
+ knot_rrset_t **rr_copy)
+{
+ assert(rr != NULL);
+ assert(zone != NULL);
+ assert(changeset != NULL);
+ assert(changes != NULL);
+ assert(rr_copy != NULL);
+
+ dbg_ddns_verb("Adding RR.\n");
+
+ if (node == NULL) {
+ // create new node, connect it properly to the
+ // zone nodes
+ dbg_ddns_detail("Node not found. Creating new.\n");
+ node = knot_ddns_add_new_node(zone, knot_rrset_get_owner(rr),
+ knot_ddns_rr_is_nsec3(rr));
+ if (node == NULL) {
+ dbg_xfrin("Failed to create new node in zone.\n");
+ return KNOT_ERROR;
+ }
+ }
+
+ uint16_t type = knot_rrset_type(rr);
+ *rr_copy = NULL;
+ int ret = 0;
+
+ /*
+ * First, rule out special cases: CNAME, SOA and adding to CNAME node.
+ */
+ if (type == KNOT_RRTYPE_CNAME) {
+ /* 1) CNAME */
+ ret = knot_ddns_process_add_cname(node, rr, changeset, changes);
+ } else if (type == KNOT_RRTYPE_SOA) {
+ /* 2) SOA */
+ ret = knot_ddns_process_add_soa(node, rr, changes);
+ } else if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) {
+ /*
+ * Adding RR to CNAME node. Ignore the UPDATE RR.
+ *
+ * TODO: This may or may not be according to the RFC, it's quite
+ * unclear (see 3.4.2.2)
+ */
+ return KNOT_EOK;
+ }
+
+ if (ret == 1) {
+ dbg_ddns_detail("Ignoring the added RR.\n");
+ // Ignore
+ return KNOT_EOK;
+ } else if (ret != KNOT_EOK) {
+ dbg_ddns_detail("Adding RR failed.\n");
+ return ret;
+ }
+
+ /*
+ * In all other cases, the RR should just be added to the node.
+ */
+
+ /* Add the RRSet to the node (RRSIGs handled in the function). */
+ dbg_ddns_detail("Adding RR to the node.\n");
+ ret = knot_ddns_add_rr(node, rr, changes, rr_copy);
+ if (ret < 0) {
+ dbg_ddns("Failed to add RR to the node.\n");
+ return ret;
+ }
+
+ /*
+ * If adding SOA, it should not be stored in the changeset.
+ * (This is done in the calling function, and the SOA is stored in the
+ * soa_final field.)
+ */
+ if (type == KNOT_RRTYPE_SOA) {
+ return KNOT_EOK;
+ }
+
+ /* Add the RR to ADD section of the changeset. */
+ /* If the RR was previously removed, do not add it to the
+ * changeset, and remove the entry from the REMOVE section.
+ *
+ * If there was no change (i.e. all RDATA were duplicates), do not add
+ * the RR to the changeset.
+ */
+ if (ret == KNOT_EOK) {
+ dbg_ddns_detail("Adding RR to the changeset.\n");
+ ret = knot_ddns_add_rr_to_chgset(rr, changeset);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add the UPDATE RR to the changeset."
+ "\n");
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \todo Geez, this is soooooo long even I don't exactly know what it does...
+ * Refactor!
+ */
+static int knot_ddns_process_rem_rr(const knot_rrset_t *rr,
+ knot_node_t *node,
+ knot_zone_contents_t *zone,
+ knot_changeset_t *changeset,
+ knot_changes_t *changes, uint16_t qclass)
+{
+ assert(rr != NULL);
+ assert(node != NULL);
+ assert(zone != NULL);
+ assert(changeset != NULL);
+ assert(changes != NULL);
+
+ uint16_t type = knot_rrset_type(rr);
+ dbg_ddns_verb("Removing one RR.\n");
+
+ /*
+ * When doing changes to RRSets, we must:
+ * 1) Copy the RRSet (same as in IXFR changeset applying, maybe the
+ * function xfrin_copy_rrset() may be used for this).
+ * 2) Remove the RDATA (in this case only one). Check if it is not the
+ * last NS RR in the zone.
+ * 3) Store the removed RDATA in 'changes'.
+ * 4) If the RRSet is empty, remove it and store in 'changes'.
+ * 5) Check redundant RRs in changeset.
+ * 6) Store the RRSet containing the one RDATA in the changeset. We may
+ * use the RRSet from the packet for this - copy it, set CLASS
+ * and TTL.
+ *
+ * Special handling of RRSIGs is required in that the RRSet containing
+ * them must be copied as well. However, copying of RRSet copies also
+ * the RRSIGs, so copying the base RRSet is enough for both cases!
+ */
+
+ assert(type != KNOT_RRTYPE_SOA);
+ int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL;
+
+ /* If removing NS from an apex and there is only one NS left, ignore
+ * this removal right away. We do not have to check if the RRs match:
+ * - if they don't match, the removal will be ignored
+ * - if they match, the last NS cannot be removed anyway.
+ */
+ if (is_apex && type == KNOT_RRTYPE_NS
+ && knot_rrset_rdata_rr_count(knot_node_rrset(node, type)) == 1) {
+ return KNOT_EOK;
+ }
+
+ /*
+ * 1) Copy the RRSet.
+ */
+ uint16_t type_to_copy = (type != KNOT_RRTYPE_RRSIG) ? type
+ : knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr));
+ knot_rrset_t *rrset_copy = NULL;
+ int ret = xfrin_copy_rrset(node, type_to_copy, &rrset_copy, changes, 1);
+ if (ret < 0) {
+ dbg_ddns("Failed to copy RRSet for removal: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ if (rrset_copy == NULL) {
+ dbg_ddns_verb("RRSet not found.\n");
+ return KNOT_EOK;
+ }
+
+ /*
+ * Set some variables needed, according to the modified RR type.
+ */
+
+ int rdata_count;
+ knot_rrset_t *to_modify;
+ if (type == KNOT_RRTYPE_RRSIG) {
+ rdata_count = knot_rrset_rdata_rr_count(
+ knot_rrset_rrsigs(rrset_copy));
+ to_modify = knot_rrset_get_rrsigs(rrset_copy);
+ } else {
+ rdata_count = knot_rrset_rdata_rr_count(rrset_copy);
+ to_modify = rrset_copy;
+ }
+
+ /*
+ * 1.5) Prepare place for the removed RDATA.
+ * We don't know if there are some, but if this fails, at least we
+ * haven't removed them yet.
+ */
+ ret = knot_changes_rdata_reserve(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated,
+ rdata_count);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to reserve place for RDATA.\n");
+ return ret;
+ }
+
+ /*
+ * 2) Remove the proper RDATA from the RRSet copy, or its RRSIGs.
+ */
+ knot_rdata_t *removed = knot_rrset_remove_rdata(to_modify,
+ knot_rrset_rdata(rr));
+
+ /* No such RR in the RRSet. */
+ if (removed == NULL) {
+ dbg_ddns_detail("No such RR found to be removed.\n");
+ return KNOT_EOK;
+ }
+
+ /* If we removed NS from apex, there should be at least one more. */
+ assert(!is_apex || type != KNOT_RRTYPE_NS
+ || knot_rrset_rdata(rrset_copy) != NULL);
+
+ /*
+ * 3) Store the removed RDATA in 'changes'.
+ */
+ knot_changes_add_rdata(changes->old_rdata, changes->old_rdata_types,
+ &changes->old_rdata_count, removed, type);
+
+ /*
+ * 4) If the RRSet is empty, remove it and store in 'changes'.
+ * Do this also if the RRSIGs are empty.
+ * And if both are empty, remove both.
+ */
+ if (type == KNOT_RRTYPE_RRSIG
+ && knot_rrset_rdata(to_modify) == NULL) {
+ /* Empty RRSIGs, remove the RRSIG RRSet */
+ ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 1);
+ if (ret == KNOT_EOK) {
+ knot_rrset_t *rrsig = knot_rrset_get_rrsigs(rrset_copy);
+ dbg_xfrin_detail("Removed RRSIG RRSet (%p).\n", rrsig);
+
+ assert(rrsig == to_modify);
+
+ // add the removed RRSet to list of old RRSets
+ changes->old_rrsets[changes->old_rrsets_count++]
+ = rrsig;
+
+ // remove it from the RRSet
+ knot_rrset_set_rrsigs(rrset_copy, NULL);
+ } else {
+ dbg_ddns("Failed to reserve space for empty RRSet.\n");
+ }
+ }
+
+ /*! \note Copied from xfr-in.c - maybe extract to some function. */
+ /*! \note This is not needed as rrset is already on the old_rrsets */
+// if (knot_rrset_rdata(rrset_copy) == NULL
+// && knot_rrset_rrsigs(rrset_copy) == NULL) {
+// // The RRSet should not be empty if we were removing NSs from
+// // apex in case of DDNS
+// assert(!is_apex);
+
+// ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
+// &changes->old_rrsets_count,
+// &changes->old_rrsets_allocated,
+// 1);
+// if (ret == KNOT_EOK) {
+// knot_rrset_t *tmp = knot_node_remove_rrset(node, type);
+// dbg_xfrin_detail("Removed whole RRSet (%p).\n", tmp);
+
+// assert(tmp == rrset_copy);
+
+// // add the removed RRSet to list of old RRSets
+// changes->old_rrsets[changes->old_rrsets_count++]
+// = rrset_copy;
+// } else {
+// dbg_ddns("Failed to reserve space for empty RRSet.\n");
+// }
+// }
+
+ /*
+ * 5) Check if the RR is not in the ADD section. If yes, remove it
+ * from there and do not add it to the REMOVE section.
+ */
+ knot_rrset_t **from_chgset = NULL;
+ size_t from_chgset_count = 0;
+ ret = knot_ddns_check_remove_rr2(changeset, knot_node_owner(node),
+ type, knot_rrset_rdata(rr),
+ &from_chgset, &from_chgset_count);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to remove possible redundant RRs from ADD "
+ "section: %s.\n", knot_strerror(ret));
+ free(from_chgset);
+ return ret;
+ }
+
+ assert(from_chgset_count <= 1);
+
+ if (from_chgset_count == 1) {
+ /* Just delete the RRSet. */
+ knot_rrset_deep_free(&(from_chgset[0]), 1, 1, 1);
+
+ /* Finish processing, no adding to changeset. */
+ free(from_chgset);
+ return KNOT_EOK;
+ }
+
+ free(from_chgset);
+
+ /*
+ * 6) Store the RRSet containing the one RDATA in the changeset. We may
+ * use the RRSet from the packet for this - copy it, set CLASS
+ * and TTL.
+ */
+ knot_rrset_t *to_chgset = NULL;
+ ret = knot_rrset_deep_copy(rr, &to_chgset, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy RRSet from packet to changeset.\n");
+ return ret;
+ }
+ knot_rrset_set_class(to_chgset, qclass);
+ knot_rrset_set_ttl(to_chgset, knot_rrset_ttl(to_modify));
+
+ ret = knot_changeset_add_rrset(&changeset->remove,
+ &changeset->remove_count,
+ &changeset->remove_allocated,
+ to_chgset);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to store the RRSet copy to changeset: %s.\n",
+ knot_strerror(ret));
+ knot_rrset_deep_free(&to_chgset, 1, 1, 1);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_process_rem_rrsig(knot_node_t *node,
+ knot_rrset_t *rrset,
+ knot_changes_t *changes,
+ knot_rrset_t **rrsig)
+{
+ assert(node != NULL);
+ assert(rrset != NULL);
+ assert(changes != NULL);
+
+ knot_rrset_t *rrset_copy = NULL;
+
+ /* Copy RRSet. */
+ int ret = xfrin_copy_old_rrset(rrset, &rrset_copy, changes, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy RRSet from node: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Remove RRSIGs from the copy. */
+ *rrsig = knot_rrset_get_rrsigs(rrset_copy);
+ if (*rrsig != NULL) {
+ knot_rrset_set_rrsigs(rrset_copy, NULL);
+ }
+
+ /* Put the copy to the node. */
+ ret = knot_node_add_rrset(node, rrset_copy, 0);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add RRSet copy to the node: %s\n",
+ knot_strerror(ret));
+ knot_rrset_deep_free(&rrset_copy, 1, 1, 1);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_process_rem_rrsigs(knot_node_t *node,
+ knot_changes_t *changes,
+ knot_rrset_t ***removed,
+ size_t *removed_count)
+{
+ assert(node != NULL);
+ assert(removed != NULL);
+ assert(removed_count != NULL);
+ assert(changes != NULL);
+
+ /* If removing RRSIGs, we must remove them from all RRSets in
+ * the node. This means to copy all RRSets and then remove the
+ * RRSIGs from them.
+ */
+ dbg_ddns_verb("Removing all RRSIGs from node.\n");
+
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ if (rrsets == NULL) {
+ // No RRSets in the node, nothing to remove
+ return KNOT_EOK;
+ }
+
+ /* Allocate space for the removed RRSIGs. There may be as many as there
+ * are RRSets.
+ */
+ short rrset_count = knot_node_rrset_count(node);
+
+ *removed = malloc(rrset_count * sizeof(knot_rrset_t *));
+ CHECK_ALLOC_LOG(*removed, KNOT_ENOMEM);
+ *removed_count = 0;
+
+ /* Remove all the RRSets from the node, so that we may insert the copies
+ * right away.
+ */
+ knot_node_remove_all_rrsets(node);
+
+ knot_rrset_t *rrsig = NULL;
+ int ret = 0;
+ for (int i = 0; i < rrset_count; ++i) {
+ ret = knot_ddns_process_rem_rrsig(node, rrsets[i], changes,
+ &rrsig);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to remove RRSIG.\n");
+ return ret;
+ }
+ /* Store the RRSIGs to the array of removed RRSets. */
+ (*removed)[(*removed_count)++] = rrsig;
+ }
+
+ free(rrsets);
+
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_process_rem_rrset(uint16_t type,
+ knot_node_t *node,
+ knot_changeset_t *changeset,
+ knot_changes_t *changes)
+{
+ assert(node != NULL);
+ assert(changeset != NULL);
+ assert(changes != NULL);
+
+ /*! \note
+ * We decided to automatically remove RRSIGs together with the removed
+ * RRSet as they are no longer valid or required anyway.
+ *
+ * Also refer to RFC3007, section 4.3:
+ * 'When the contents of an RRset are updated, the server MAY delete
+ * all associated SIG records, since they will no longer be valid.'
+ *
+ * (Although we are compliant with this RFC only selectively. The next
+ * section says: 'If any changes are made, the server MUST, if
+ * necessary, generate a new SOA record and new NXT records, and sign
+ * these with the appropriate zone keys.' and we are definitely not
+ * doing this...
+ *
+ * \todo Document!!
+ */
+
+ // this should be ruled out before
+ assert(type != KNOT_RRTYPE_SOA);
+
+ if (knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL
+ && type == KNOT_RRTYPE_NS) {
+ // if removing NS from apex, ignore
+ return KNOT_EOK;
+ }
+
+ knot_rrset_t **removed = NULL;
+ size_t removed_count = 0;
+ int ret = 0;
+
+ if (type == KNOT_RRTYPE_RRSIG) {
+ /* Remove all RRSIGs from the node. */
+ ret = knot_ddns_process_rem_rrsigs(node, changes, &removed,
+ &removed_count);
+ } else {
+ /* Remove the RRSet from the node. */
+ removed = malloc(sizeof(knot_rrset_t *));
+ if (!removed) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ dbg_ddns_detail("Removing RRSet of type: %d\n", type);
+
+ *removed = knot_node_remove_rrset(node, type);
+ removed_count = 1;
+ }
+
+ dbg_ddns_detail("Removed: %p (first item: %p), removed count: %d\n",
+ removed, (removed == NULL) ? "none" : *removed,
+ removed_count);
+
+ // no such RR
+ if (removed_count == 0 || removed == NULL) {
+ // ignore
+ return KNOT_EOK;
+ }
+
+ /* 2) Store them to 'changes' for later deallocation, together with
+ * their RRSIGs.
+ */
+ ret = knot_changes_add_old_rrsets(removed, removed_count, changes, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add removed RRSet to 'changes': %s.\n",
+ knot_strerror(ret));
+ free(removed);
+ return ret;
+ }
+
+ /* 3) Copy the RRSets, so that they can be stored to the changeset. */
+ knot_rrset_t **to_chgset = malloc(removed_count
+ * sizeof(knot_rrset_t *));
+ if (to_chgset == NULL) {
+ dbg_ddns("Failed to allocate space for RRSets going to "
+ "changeset.\n");
+ free(removed);
+ return KNOT_ENOMEM;
+ }
+
+ for (int i = 0; i < removed_count; ++i) {
+ ret = knot_rrset_deep_copy(removed[i], &to_chgset[i], 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy the removed RRSet: %s.\n",
+ knot_strerror(ret));
+ for (int j = 0; j < i; ++j) {
+ knot_rrset_deep_free(&to_chgset[j], 1, 1, 1);
+ }
+ free(to_chgset);
+ free(removed);
+ return ret;
+ }
+ }
+
+ free(removed);
+
+ /* 4) But we must check if some of the RRs were not previously added
+ * by the same UPDATE. If yes, these must be removed from the ADD
+ * section of the changeset and also from this RRSet copy (so they
+ * are neither stored in the REMOVE section of the changeset).
+ */
+ knot_rrset_t **from_chgset = NULL;
+ size_t from_chgset_count = 0;
+
+ /* 4 a) Remove redundant RRs from the ADD section of the changeset. */
+ ret = knot_ddns_check_remove_rr2(changeset, knot_node_owner(node), type,
+ NULL, &from_chgset,
+ &from_chgset_count);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to remove possible redundant RRs from ADD "
+ "section: %s.\n", knot_strerror(ret));
+ for (int i = 0; i < removed_count; ++i) {
+ knot_rrset_deep_free(&to_chgset[i], 1, 1, 1);
+ }
+ free(from_chgset);
+ free(to_chgset);
+ return ret;
+ }
+
+ /* 4 b) Remove these RRs from the copy of the RRSets removed from zone*/
+ knot_rdata_t *rem = NULL;
+ for (int j = 0; j < removed_count; ++j) {
+ /* In each RRSet removed from the node (each can have more
+ * RDATAs) ...
+ */
+ for (int i = 0; i < from_chgset_count; ++i) {
+ /* ...try to remove redundant RDATA. Each RRSet in
+ * 'from_chgset' contains only one RDATA.
+ */
+ rem = knot_rrset_remove_rdata(to_chgset[j],
+ knot_rrset_rdata(
+ from_chgset[i]));
+ /* And delete it right away, no use for that. */
+ knot_rdata_deep_free(&rem, knot_rrset_type(
+ from_chgset[i]), 1);
+ }
+ }
+
+ /* The array is cleared, we may delete the redundant RRs. */
+ for (int i = 0; i < from_chgset_count; ++i) {
+ knot_rrset_deep_free(&from_chgset[i], 1, 1, 1);
+ }
+ free(from_chgset);
+
+ /* 5) Store the remaining RRSet to the changeset. Do not try to merge
+ * to some previous RRSet, there should be none.
+ */
+ for (int i = 0; i < removed_count; ++i) {
+ ret = knot_changeset_add_rrset(&changeset->remove,
+ &changeset->remove_count,
+ &changeset->remove_allocated,
+ to_chgset[i]);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to store the RRSet copy to changeset: "
+ "%s.\n", knot_strerror(ret));
+ for (int j = i; j < removed_count; ++j) {
+ knot_rrset_deep_free(&to_chgset[j], 1, 1, 1);
+ }
+ free(to_chgset);
+ return ret;
+ }
+ }
+
+ free(to_chgset);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_process_rem_all(knot_node_t *node,
+ knot_changeset_t *changeset,
+ knot_changes_t *changes)
+{
+ assert(node != NULL);
+ assert(changeset != NULL);
+ assert(changes != NULL);
+
+ /*! \note
+ * This basically means to call knot_ddns_process_rem_rrset() for every
+ * type present in the node.
+ *
+ * In case of SOA and NS in apex, the RRSets should not be removed, but
+ * what about their RRSIGs??
+ *
+ * If the zone has to remain properly signed, the UPDATE will have to
+ * contain at least new SOA and RRSIGs for it (as the auto-incremented
+ * SOA would not be signed). So it should not matter if we leave the
+ * RRSIGs there or not. But in case of the NSs it's not that clear.
+ *
+ * For now, we will leave the RRSIGs there. It's easier to implement.
+ *
+ * \todo Should document this!!
+ */
+ int ret = 0;
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ int count = knot_node_rrset_count(node);
+
+ if (rrsets == NULL && count != 0) {
+ dbg_ddns("Failed to fetch RRSets from node.\n");
+ return KNOT_ENOMEM;
+ }
+
+ int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL;
+
+ dbg_ddns_verb("Removing all RRSets (count: %d).\n", count);
+ for (int i = 0; i < count; ++i) {
+ // If the node is apex, skip NS and SOA
+ if (is_apex &&
+ (knot_rrset_type(rrsets[i]) == KNOT_RRTYPE_SOA
+ || knot_rrset_type(rrsets[i]) == KNOT_RRTYPE_NS)) {
+ /* Do not remove these RRSets, nor their RRSIGs. */
+ continue;
+ }
+
+ ret = knot_ddns_process_rem_rrset(knot_rrset_type(rrsets[i]),
+ node, changeset, changes);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to remove RRSet: %s\n",
+ knot_strerror(ret));
+ free(rrsets);
+ return ret;
+ }
+ }
+
+ free(rrsets);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_process_rr(const knot_rrset_t *rr,
+ knot_zone_contents_t *zone,
+ knot_changeset_t *changeset,
+ knot_changes_t *changes, uint16_t qclass,
+ knot_rrset_t **rr_copy)
+{
+ assert(rr != NULL);
+ assert(zone != NULL);
+ assert(changeset != NULL);
+ assert(changes != NULL);
+ assert(rr_copy != NULL);
+
+ /* 1) Find node that will be affected. */
+ knot_node_t *node = knot_ddns_get_node(zone, rr);
+
+ /* 2) Decide what to do. */
+
+ if (knot_rrset_class(rr) == knot_zone_contents_class(zone)) {
+ return knot_ddns_process_add(rr, node, zone, changeset,
+ changes, rr_copy);
+ } else if (knot_rrset_class(rr) == KNOT_CLASS_NONE) {
+ return knot_ddns_process_rem_rr(rr, node, zone, changeset,
+ changes, qclass);
+ } else if (knot_rrset_class(rr) == KNOT_CLASS_ANY) {
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_ANY) {
+ return knot_ddns_process_rem_all(node, changeset,
+ changes);
+ } else {
+ return knot_ddns_process_rem_rrset(knot_rrset_type(rr),
+ node, changeset,
+ changes);
+ }
+ } else {
+ assert(0);
+ return KNOT_ERROR;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*
+ * NOTES:
+ * - 'zone' must be a copy of the current zone.
+ * - changeset must be allocated
+ * - changes must be allocated
+ *
+ * All this is done in the first parts of xfrin_apply_changesets() - extract
+ * to separate function, if possible.
+ *
+ * If anything fails, rollback must be done. The xfrin_rollback_update() may
+ * be good for this.
+ */
+int knot_ddns_process_update2(knot_zone_contents_t *zone,
+ const knot_packet_t *query,
+ knot_changeset_t *changeset,
+ knot_changes_t *changes,
+ knot_rcode_t *rcode)
+{
+ if (zone == NULL || query == NULL || changeset == NULL || rcode == NULL
+ || changes == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+
+ /* Copy base SOA RR. */
+ const knot_rrset_t *soa = knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_SOA);
+ knot_rrset_t *soa_begin = NULL;
+ knot_rrset_t *soa_end = NULL;
+ ret = knot_rrset_deep_copy(soa, &soa_begin, 0);
+ if (ret == KNOT_EOK) {
+ knot_changeset_store_soa(&changeset->soa_from,
+ &changeset->serial_from, soa_begin);
+ } else {
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return ret;
+ }
+
+ /* Current SERIAL */
+ int64_t sn = knot_rdata_soa_serial(knot_rrset_rdata(soa_begin));
+ int64_t sn_new;
+
+ /* Incremented SERIAL
+ * We must set it now to be able to compare SERIAL from SOAs in the
+ * UPDATE to it. Although we do not have the new SOA yet.
+ */
+ if (sn > -1) {
+ sn_new = (uint32_t)sn + 1;
+ } else {
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return ret;
+ }
+
+ /* Process all RRs the Authority (Update) section. */
+
+ const knot_rrset_t *rr = NULL;
+ knot_rrset_t *rr_copy = NULL;
+
+ dbg_ddns("Processing UPDATE section.\n");
+ for (int i = 0; i < knot_packet_authority_rrset_count(query); ++i) {
+
+ rr = knot_packet_authority_rrset(query, i);
+
+ /* Check if the entry is correct. */
+ ret = knot_ddns_check_update(rr, query, rcode);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to check update RRSet:%s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Check if the record is SOA. If yes, check the SERIAL.
+ * If this record should cause the SOA to be replaced in the
+ * zone, use it as the ending SOA.
+ *
+ * Also handle cases where there are multiple SOAs to be added
+ * in the same UPDATE. The one with the largest SERIAL should
+ * be used.
+ *
+ * TODO: If there are more SOAs in the UPDATE one after another,
+ * the ddns_add_update() function will merge them into a
+ * RRSet. This should be handled somehow.
+ *
+ * If the serial is not larger than the current zone serial,
+ * ignore the record and continue. This will ensure that the
+ * RR processing function receives only SOA RRs that should be
+ * added to the zone (replacing the old one).
+ */
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA
+ && (knot_rrset_class(rr) == KNOT_CLASS_NONE
+ || knot_rrset_class(rr) == KNOT_CLASS_ANY
+ || ns_serial_compare(knot_rdata_soa_serial(
+ knot_rrset_rdata(rr)), sn_new) < 0)) {
+ // This ignores also SOA removals
+ dbg_ddns_verb("Ignoring SOA...\n");
+ continue;
+ }
+
+ ret = knot_ddns_process_rr(rr, zone, changeset, changes,
+ knot_packet_qclass(query),
+ &rr_copy);
+
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to process update RR:%s\n",
+ knot_strerror(ret));
+ *rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL;
+ return ret;
+ }
+
+ // we need the RR copy, that's why this code is here
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
+ int64_t sn_rr = knot_rdata_soa_serial(
+ knot_rrset_rdata(rr));
+ dbg_ddns_verb("Replacing SOA. Old serial: %d, new "
+ "serial: %d\n", sn_new, sn_rr);
+ assert(ns_serial_compare(sn_rr, sn_new) >= 0);
+ assert(rr_copy != NULL);
+ sn_new = sn_rr;
+ soa_end = (knot_rrset_t *)rr_copy;
+ }
+ }
+
+ /* Ending SOA (not in the UPDATE) */
+ if (soa_end == NULL) {
+ /* If the changeset is empty, do not process anything further
+ * and indicate this to the caller, so that the changeset is not
+ * saved and zone is not switched.
+ */
+ if (knot_changeset_is_empty(changeset)) {
+ return 1;
+ }
+
+ /* If not set, create new SOA. */
+ assert(sn_new == (uint32_t)sn + 1);
+ ret = knot_rrset_deep_copy(soa, &soa_end, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy ending SOA: %s\n",
+ knot_strerror(ret));
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return ret;
+ }
+ knot_rdata_t *rd = knot_rrset_get_rdata(soa_end);
+ knot_rdata_soa_serial_set(rd, sn_new);
+
+ /* And replace it in the zone. */
+ ret = xfrin_replace_rrset_in_node(
+ knot_zone_contents_get_apex(zone),
+ soa_end, changes, zone);
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to copy replace SOA in zone: %s\n",
+ knot_strerror(ret));
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return ret;
+ }
+ }
+
+ ret = knot_ddns_final_soa_to_chgset(soa_end, changeset);
+
+ return ret;
+}
diff --git a/src/libknot/updates/ddns.h b/src/libknot/updates/ddns.h
index 35dfcb7..d3cb359 100644
--- a/src/libknot/updates/ddns.h
+++ b/src/libknot/updates/ddns.h
@@ -32,6 +32,8 @@
#include "packet/packet.h"
#include "rrset.h"
#include "dname.h"
+#include "consts.h"
+#include "common/lists.h"
typedef struct knot_ddns_prereq_t {
knot_rrset_t **exist;
@@ -55,17 +57,24 @@ typedef struct knot_ddns_prereq_t {
size_t not_in_use_allocd;
} knot_ddns_prereq_t;
-int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query,
- uint8_t *rcode);
+int knot_ddns_check_zone(const knot_zone_contents_t *zone,
+ const knot_packet_t *query, knot_rcode_t *rcode);
-int knot_ddns_process_prereqs(knot_packet_t *query,
- knot_ddns_prereq_t **prereqs, uint8_t *rcode);
+int knot_ddns_process_prereqs(const knot_packet_t *query,
+ knot_ddns_prereq_t **prereqs, knot_rcode_t *rcode);
int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
- knot_ddns_prereq_t **prereqs, uint8_t *rcode);
+ knot_ddns_prereq_t **prereqs, knot_rcode_t *rcode);
-int knot_ddns_process_update(knot_packet_t *query,
- knot_changeset_t **changeset, uint8_t *rcode);
+int knot_ddns_process_update(const knot_zone_contents_t *zone,
+ const knot_packet_t *query,
+ knot_changeset_t *changeset, knot_rcode_t *rcode);
+
+int knot_ddns_process_update2(knot_zone_contents_t *zone,
+ const knot_packet_t *query,
+ knot_changeset_t *changeset,
+ knot_changes_t *changes,
+ knot_rcode_t *rcode);
void knot_ddns_prereqs_free(knot_ddns_prereq_t **prereq);
diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c
index 7d3ffdf..98d4df6 100644
--- a/src/libknot/updates/xfr-in.c
+++ b/src/libknot/updates/xfr-in.c
@@ -267,14 +267,14 @@ static int xfrin_add_orphan_rrsig(xfrin_orphan_rrsig_t **rrsigs,
while (last != NULL) {
// check if the RRSIG is not similar to the one we want to add
assert(last->rrsig != NULL);
- if (knot_rrset_compare(last->rrsig, rr,
+ if (knot_rrset_match(last->rrsig, rr,
KNOT_RRSET_COMPARE_HEADER) == 1
&& knot_rdata_rrsig_type_covered(knot_rrset_rdata(
last->rrsig))
== knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr))) {
ret = knot_rrset_merge_no_dupl((void **)&last->rrsig,
(void **)&rr);
- if (ret != KNOT_EOK) {
+ if (ret < 0) {
return ret;
} else {
return 1;
@@ -955,13 +955,13 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
if (*chs == NULL) {
dbg_xfrin_verb("Changesets empty, creating new.\n");
- ret = knot_changeset_allocate(chs);
+ ret = knot_changeset_allocate(chs, KNOT_CHANGESET_TYPE_IXFR);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(&rr, 1, 1, 1);
knot_packet_free(&packet);
return ret;
}
-
+
// the first RR must be a SOA
if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
dbg_xfrin("First RR is not a SOA RR!\n");
@@ -1069,9 +1069,9 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
assert((*chs)->sets[(*chs)->count - 1].soa_from != NULL);
if ((*chs)->sets[(*chs)->count - 1].soa_to == NULL) {
- state = XFRIN_CHANGESET_REMOVE;
+ state = KNOT_CHANGESET_REMOVE;
} else {
- state = XFRIN_CHANGESET_ADD;
+ state = KNOT_CHANGESET_ADD;
}
}
@@ -1134,17 +1134,17 @@ dbg_xfrin_exec_verb(
ret = knot_changeset_add_soa(
&(*chs)->sets[(*chs)->count - 1], rr,
- XFRIN_CHANGESET_REMOVE);
+ KNOT_CHANGESET_REMOVE);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(&rr, 1, 1, 1);
goto cleanup;
}
// change state to REMOVE
- state = XFRIN_CHANGESET_REMOVE;
+ state = KNOT_CHANGESET_REMOVE;
}
break;
- case XFRIN_CHANGESET_REMOVE:
+ case KNOT_CHANGESET_REMOVE:
// if the next RR is SOA, store it and change state to
// ADD
if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
@@ -1154,25 +1154,25 @@ dbg_xfrin_exec_verb(
ret = knot_changeset_add_soa(
&(*chs)->sets[(*chs)->count - 1], rr,
- XFRIN_CHANGESET_ADD);
+ KNOT_CHANGESET_ADD);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(&rr, 1, 1, 1);
goto cleanup;
}
- state = XFRIN_CHANGESET_ADD;
+ state = KNOT_CHANGESET_ADD;
} else {
// just add the RR to the REMOVE part and
// continue
if ((ret = knot_changeset_add_new_rr(
&(*chs)->sets[(*chs)->count - 1], rr,
- XFRIN_CHANGESET_REMOVE)) != KNOT_EOK) {
+ KNOT_CHANGESET_REMOVE)) != KNOT_EOK) {
knot_rrset_deep_free(&rr, 1, 1, 1);
goto cleanup;
}
}
break;
- case XFRIN_CHANGESET_ADD:
+ case KNOT_CHANGESET_ADD:
// if the next RR is SOA change to state -1 and do not
// parse next RR
if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
@@ -1183,7 +1183,7 @@ dbg_xfrin_exec_verb(
// just add the RR to the ADD part and continue
if ((ret = knot_changeset_add_new_rr(
&(*chs)->sets[(*chs)->count - 1], rr,
- XFRIN_CHANGESET_ADD)) != KNOT_EOK) {
+ KNOT_CHANGESET_ADD)) != KNOT_EOK) {
knot_rrset_deep_free(&rr, 1, 1, 1);
goto cleanup;
}
@@ -1230,137 +1230,6 @@ cleanup:
/* Applying changesets to zone */
/*----------------------------------------------------------------------------*/
-static int xfrin_changes_check_rrsets(knot_rrset_t ***rrsets,
- int *count, int *allocated, int to_add)
-{
- if (*count + to_add <= *allocated) {
- return KNOT_EOK;
- }
-
- int new_count = (*allocated == 0) ? 2 : *allocated * 2;
- while (new_count < *count + to_add) {
- new_count *= 2;
- }
-
- /* Allocate new memory block. */
- knot_rrset_t **rrsets_new = malloc(new_count * sizeof(knot_rrset_t *));
- if (rrsets_new == NULL) {
- return KNOT_ENOMEM;
- }
-
- /* Initialize new memory and copy old data. */
- memset(rrsets_new, 0, new_count * sizeof(knot_rrset_t *));
- memcpy(rrsets_new, *rrsets, (*allocated) * sizeof(knot_rrset_t *));
-
- /* Free old nodes and switch pointers. */
- free(*rrsets);
- *rrsets = rrsets_new;
- *allocated = new_count;
-
- return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int xfrin_changes_check_nodes(knot_node_t ***nodes,
- int *count, int *allocated)
-{
- assert(nodes != NULL);
- assert(count != NULL);
- assert(allocated != 0);
-
- if (*count + 2 <= *allocated) {
- return KNOT_EOK;
- }
-
- int new_count = (*allocated == 0) ? 2 : *allocated * 2;
-
- /* Allocate new memory block. */
- const size_t node_len = sizeof(knot_node_t *);
- knot_node_t **nodes_new = malloc(new_count * node_len);
- if (nodes_new == NULL) {
- return KNOT_ENOMEM;
- }
-
- /* Clear memory block and copy old data. */
- memset(nodes_new, 0, new_count * node_len);
- memcpy(nodes_new, *nodes, (*allocated) * node_len);
-
- /* Free old nodes and switch pointers. */
- free(*nodes);
- *nodes = nodes_new;
- *allocated = new_count;
-
- return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int xfrin_changes_check_rdata(knot_rdata_t ***rdatas, uint **types,
- int count, int *allocated, int to_add)
-{
- if (count + to_add <= *allocated) {
- return KNOT_EOK;
- }
-
- int new_count = (*allocated == 0) ? 2 : *allocated * 2;
- while (new_count < count + to_add) {
- new_count *= 2;
- }
-
- /* Allocate new memory block. */
- knot_rdata_t **rdatas_new = malloc(new_count * sizeof(knot_rdata_t *));
- if (rdatas_new == NULL) {
- return KNOT_ENOMEM;
- }
-
- uint *types_new = malloc(new_count * sizeof(uint));
- if (types_new == NULL) {
- free(rdatas_new);
- return KNOT_ENOMEM;
- }
-
- /* Initialize new memory and copy old data. */
- memset(rdatas_new, 0, new_count * sizeof(knot_rdata_t *));
- memcpy(rdatas_new, *rdatas, (*allocated) * sizeof(knot_rdata_t *));
-
- memset(types_new, 0, new_count * sizeof(uint));
- memcpy(types_new, *types, (*allocated) * sizeof(uint));
-
- /* Free old rdatas and switch pointers. */
- free(*rdatas);
- free(*types);
- *rdatas = rdatas_new;
- *types = types_new;
- *allocated = new_count;
-
- return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static void xfrin_changes_add_rdata(knot_rdata_t **rdatas, uint *types,
- int *count, knot_rdata_t *rdata, uint type)
-{
- if (rdata == NULL) {
- return;
- }
-
- // Add all RDATAs from the chain!!
-
- knot_rdata_t *r = rdata;
- do {
- dbg_xfrin_detail("Adding RDATA to RDATA list: %p\n", r);
- rdatas[*count] = r;
- types[*count] = type;
- ++*count;
-
- r = r->next;
- } while (r != NULL && r != rdata);
-}
-
-/*----------------------------------------------------------------------------*/
-
static void xfrin_zone_contents_free(knot_zone_contents_t **contents)
{
/*! \todo This should be all in some API!! */
@@ -1386,7 +1255,8 @@ static void xfrin_zone_contents_free(knot_zone_contents_t **contents)
/*----------------------------------------------------------------------------*/
static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from,
- const knot_rrset_t *what)
+ const knot_rrset_t *what,
+ int ddns_check)
{
knot_rdata_t *old = NULL;
knot_rdata_t *old_actual = NULL;
@@ -1396,6 +1266,20 @@ static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from,
while (rdata != NULL) {
// rdata - the RDATA to be removed
// old_actual - removed RDATA
+
+ /*
+ * DDNS special handling - last apex NS should remain in the
+ * zone.
+ *
+ * TODO: this is not correct, the last NS from the 'what' RRSet
+ * may not even be in the zone.
+ */
+ if (ddns_check
+ && knot_rrset_rdata_next(what, rdata) == NULL) {
+ assert(knot_rrset_type(from) == KNOT_RRTYPE_NS);
+ return old;
+ }
+
old_actual = knot_rrset_remove_rdata(from, rdata);
if (old_actual != NULL) {
old_actual->next = old;
@@ -1410,8 +1294,8 @@ static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from,
/*----------------------------------------------------------------------------*/
-static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
- knot_changes_t *changes)
+int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
+ knot_changes_t *changes, int save_new)
{
dbg_xfrin_detail("Copying old RRSet: %p\n", old);
// create new RRSet by copying the old one
@@ -1420,57 +1304,65 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
dbg_xfrin("Failed to create RRSet copy.\n");
return KNOT_ENOMEM;
}
+
+ int count = 0;
// add the RRSet to the list of new RRSets
// create place also for RRSIGs
- ret = xfrin_changes_check_rrsets(&changes->new_rrsets,
- &changes->new_rrsets_count,
- &changes->new_rrsets_allocated, 2);
- if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to add new RRSet to list.\n");
- knot_rrset_deep_free(copy, 1, 1, 1);
- return ret;
- }
-
- int count = knot_rrset_rdata_rr_count(*copy);
- count += knot_rrset_rdata_rr_count((*copy)->rrsigs);
-
- // add the copied RDATA to the list of new RDATA
- ret = xfrin_changes_check_rdata(&changes->new_rdata,
- &changes->new_rdata_types,
- changes->new_rdata_count,
- &changes->new_rdata_allocated, count);
- if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to add new RRSet to list.\n");
- knot_rrset_deep_free(copy, 1, 1, 1);
- return ret;
- }
-
- changes->new_rrsets[changes->new_rrsets_count++] = *copy;
+ if (save_new) {
+ ret = knot_changes_rrsets_reserve(&changes->new_rrsets,
+ &changes->new_rrsets_count,
+ &changes->new_rrsets_allocated,
+ 2);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new RRSet to list.\n");
+ knot_rrset_deep_free(copy, 1, 1, 1);
+ return ret;
+ }
- dbg_xfrin_detail("Adding RDATA from the RRSet copy to new RDATA list."
- "\n");
- xfrin_changes_add_rdata(changes->new_rdata, changes->new_rdata_types,
- &changes->new_rdata_count,
- knot_rrset_get_rdata(*copy),
- knot_rrset_type(*copy));
+ count = knot_rrset_rdata_rr_count(*copy);
+ count += knot_rrset_rdata_rr_count((*copy)->rrsigs);
+
+ // add the copied RDATA to the list of new RDATA
+ ret = knot_changes_rdata_reserve(&changes->new_rdata,
+ &changes->new_rdata_types,
+ changes->new_rdata_count,
+ &changes->new_rdata_allocated,
+ count);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new RRSet to list.\n");
+ knot_rrset_deep_free(copy, 1, 1, 1);
+ return ret;
+ }
+
+ changes->new_rrsets[changes->new_rrsets_count++] = *copy;
+
+ dbg_xfrin_detail("Adding RDATA from the RRSet copy to new RDATA list."
+ "\n");
+ knot_changes_add_rdata(changes->new_rdata,
+ changes->new_rdata_types,
+ &changes->new_rdata_count,
+ knot_rrset_get_rdata(*copy),
+ knot_rrset_type(*copy));
- if ((*copy)->rrsigs != NULL) {
- assert(old->rrsigs != NULL);
- changes->new_rrsets[changes->new_rrsets_count++] =
- (*copy)->rrsigs;
-
- dbg_xfrin_detail("Adding RDATA from RRSIG of the RRSet copy to "
- "new RDATA list.\n");
- xfrin_changes_add_rdata(changes->new_rdata,
- changes->new_rdata_types,
- &changes->new_rdata_count,
- knot_rrset_get_rdata((*copy)->rrsigs),
- KNOT_RRTYPE_RRSIG);
+ if ((*copy)->rrsigs != NULL) {
+ assert(old->rrsigs != NULL);
+ changes->new_rrsets[changes->new_rrsets_count++] =
+ (*copy)->rrsigs;
+
+ dbg_xfrin_detail("Adding RDATA from RRSIG of the RRSet copy to "
+ "new RDATA list.\n");
+ knot_changes_add_rdata(changes->new_rdata,
+ changes->new_rdata_types,
+ &changes->new_rdata_count,
+ knot_rrset_get_rdata(
+ (*copy)->rrsigs),
+ KNOT_RRTYPE_RRSIG);
+ }
}
// add the old RRSet to the list of old RRSets
- ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
&changes->old_rrsets_count,
&changes->old_rrsets_allocated, 2);
if (ret != KNOT_EOK) {
@@ -1482,7 +1374,7 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
count += knot_rrset_rdata_rr_count(old->rrsigs);
// and old RDATA to the list of old RDATA
- ret = xfrin_changes_check_rdata(&changes->old_rdata,
+ ret = knot_changes_rdata_reserve(&changes->old_rdata,
&changes->old_rdata_types,
changes->old_rdata_count,
&changes->old_rdata_allocated, count);
@@ -1494,7 +1386,7 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
changes->old_rrsets[changes->old_rrsets_count++] = old;
dbg_xfrin_detail("Adding RDATA from old RRSet to old RDATA list.\n");
- xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types,
+ knot_changes_add_rdata(changes->old_rdata, changes->old_rdata_types,
&changes->old_rdata_count, old->rdata,
knot_rrset_type(old));
@@ -1504,7 +1396,7 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
dbg_xfrin_detail("Adding RDATA from RRSIG of the old RRSet to "
"old RDATA list.\n");
- xfrin_changes_add_rdata(changes->old_rdata,
+ knot_changes_add_rdata(changes->old_rdata,
changes->old_rdata_types,
&changes->old_rdata_count,
old->rrsigs->rdata,
@@ -1516,8 +1408,9 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
/*----------------------------------------------------------------------------*/
-static int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type,
- knot_rrset_t **rrset, knot_changes_t *changes)
+int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type,
+ knot_rrset_t **rrset, knot_changes_t *changes,
+ int save_new)
{
dbg_xfrin_exec_detail(
char *name = knot_dname_to_str(knot_node_owner(node));
@@ -1536,7 +1429,7 @@ dbg_xfrin_exec_detail(
return 1;
}
- int ret = xfrin_copy_old_rrset(old, rrset, changes);
+ int ret = xfrin_copy_old_rrset(old, rrset, changes, save_new);
if (ret != KNOT_EOK) {
return ret;
}
@@ -1591,7 +1484,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
// copy the rrset
dbg_xfrin_detail("Copying RRSet that carries the RRSIGs.\n");
- ret = xfrin_copy_rrset(node, type, rrset, changes);
+ ret = xfrin_copy_rrset(node, type, rrset, changes, 1);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to copy rrset from changeset.\n");
return ret;
@@ -1616,7 +1509,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
dbg_xfrin_verb("Using RRSIG from previous iteration\n");
rrsigs = *rrsigs_old;
} else {
- ret = xfrin_copy_old_rrset(old, &rrsigs, changes);
+ ret = xfrin_copy_old_rrset(old, &rrsigs, changes, 1);
if (ret != KNOT_EOK) {
return ret;
}
@@ -1640,7 +1533,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
// now in '*rrset' we have a copy of the RRSet which holds the RRSIGs
// and in 'rrsigs' we have the copy of the RRSIGs
- knot_rdata_t *rdata = xfrin_remove_rdata(rrsigs, remove);
+ knot_rdata_t *rdata = xfrin_remove_rdata(rrsigs, remove, 0);
if (rdata == NULL) {
dbg_xfrin("Failed to remove RDATA from RRSet: %s.\n",
knot_strerror(ret));
@@ -1650,7 +1543,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
int count = knot_rdata_count(rdata);
// connect the RDATA to the list of old RDATA
- ret = xfrin_changes_check_rdata(&changes->old_rdata,
+ ret = knot_changes_rdata_reserve(&changes->old_rdata,
&changes->old_rdata_types,
changes->old_rdata_count,
&changes->old_rdata_allocated, count);
@@ -1658,7 +1551,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
return ret;
}
- xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types,
+ knot_changes_add_rdata(changes->old_rdata, changes->old_rdata_types,
&changes->old_rdata_count, rdata,
knot_rrset_type(remove));
@@ -1670,7 +1563,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
knot_rrset_set_rrsigs(*rrset, NULL);
// add RRSet to the list of old RRSets
- ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
&changes->old_rrsets_count,
&changes->old_rrsets_allocated,
1);
@@ -1695,7 +1588,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
knot_rrset_type(*rrset));
assert(tmp == *rrset);
- ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
&changes->old_rrsets_count,
&changes->old_rrsets_allocated,
1);
@@ -1720,7 +1613,8 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
static int xfrin_apply_remove_normal(knot_changes_t *changes,
const knot_rrset_t *remove,
knot_node_t *node,
- knot_rrset_t **rrset)
+ knot_rrset_t **rrset,
+ uint32_t chflags)
{
assert(changes != NULL);
assert(remove != NULL);
@@ -1731,7 +1625,18 @@ static int xfrin_apply_remove_normal(knot_changes_t *changes,
dbg_xfrin_detail("Removing RRSet: \n");
knot_rrset_dump(remove, 0);
-
+
+ int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL;
+
+ /*
+ * First handle the special case of DDNS - do not remove SOA from apex.
+ */
+ if ((chflags & KNOT_CHANGESET_TYPE_DDNS) && is_apex
+ && knot_rrset_type(remove) == KNOT_RRTYPE_SOA) {
+ dbg_xfrin_verb("Ignoring SOA removal in UPDATE.\n");
+ return KNOT_EOK;
+ }
+
// now we have the copy of the node, so lets get the right RRSet
// check if we do not already have it
if (*rrset
@@ -1748,7 +1653,7 @@ static int xfrin_apply_remove_normal(knot_changes_t *changes,
* probably not cause problems. TEST!!
*/
ret = xfrin_copy_rrset(node,
- knot_rrset_type(remove), rrset, changes);
+ knot_rrset_type(remove), rrset, changes, 1);
if (ret != KNOT_EOK) {
return ret;
}
@@ -1769,8 +1674,12 @@ dbg_xfrin_exec_detail(
);
// remove the specified RRs from the RRSet (de facto difference of sets)
- knot_rdata_t *rdata = xfrin_remove_rdata(*rrset, remove);
- if (rdata == NULL) {
+ int ddns_remove_ns_from_apex =
+ ((chflags & KNOT_CHANGESET_TYPE_DDNS) && is_apex
+ && knot_rrset_type(*rrset) == KNOT_RRTYPE_NS);
+ knot_rdata_t *rdata = xfrin_remove_rdata(*rrset, remove,
+ ddns_remove_ns_from_apex);
+ if (rdata == NULL && !ddns_remove_ns_from_apex) {
dbg_xfrin_verb("Failed to remove RDATA from RRSet\n");
// In this case, the RDATA was not found in the RRSet
return 1;
@@ -1788,25 +1697,32 @@ dbg_xfrin_exec_detail(
}
);
- int count = knot_rdata_count(rdata);
- // connect the RDATA to the list of old RDATA
- ret = xfrin_changes_check_rdata(&changes->old_rdata,
- &changes->old_rdata_types,
- changes->old_rdata_count,
- &changes->old_rdata_allocated, count);
- if (ret != KNOT_EOK) {
- return ret;
- }
+ if (rdata != NULL) {
+ int count = knot_rdata_count(rdata);
+ // connect the RDATA to the list of old RDATA
+ ret = knot_changes_rdata_reserve(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated,
+ count);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
- xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types,
- &changes->old_rdata_count, rdata,
- knot_rrset_type(remove));
+ knot_changes_add_rdata(changes->old_rdata,
+ changes->old_rdata_types,
+ &changes->old_rdata_count, rdata,
+ knot_rrset_type(remove));
+ }
// if the RRSet is empty, remove from node and add to old RRSets
// check if there is no RRSIGs; if there are, leave the RRSet
// there; it may be eventually removed when the RRSIGs are removed
if (knot_rrset_rdata(*rrset) == NULL
&& knot_rrset_rrsigs(*rrset) == NULL) {
+ // The RRSet should not be empty if we were removing NSs from
+ // apex in case of DDNS
+ assert(!ddns_remove_ns_from_apex);
knot_rrset_t *tmp = knot_node_remove_rrset(node,
knot_rrset_type(*rrset));
@@ -1815,7 +1731,7 @@ dbg_xfrin_exec_detail(
// add the removed RRSet to list of old RRSets
assert(tmp == *rrset);
- ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
&changes->old_rrsets_count,
&changes->old_rrsets_allocated,
1);
@@ -1836,43 +1752,124 @@ dbg_xfrin_exec_detail(
/*----------------------------------------------------------------------------*/
/*! \todo Needs review - RRs may not be merged into RRSets. */
static int xfrin_apply_remove_all_rrsets(knot_changes_t *changes,
- knot_node_t *node, uint16_t type)
+ knot_node_t *node, uint16_t type,
+ uint32_t chflags)
{
- int ret;
+ int ret = KNOT_EOK;
+ knot_rrset_t **rrsets = NULL;
+ unsigned rrsets_count = 0;
+ int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL;
- if (type == KNOT_RRTYPE_ANY) {
- // put all the RRSets to the changes structure
- ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
- &changes->old_rrsets_count,
- &changes->old_rrsets_allocated,
- knot_node_rrset_count(node));
- if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to check changeset rrsets.\n");
- return ret;
- }
+dbg_xfrin_exec_verb(
+ char *name = knot_dname_to_str(knot_node_owner(node));
+ dbg_xfrin_verb("Removing all RRSets from node %s of type %s. "
+ "Is apex: %d, changeset flags: %u\n",
+ name, knot_rrtype_to_string(type), is_apex, chflags);
+ free(name);
+);
- knot_rrset_t **rrsets = knot_node_get_rrsets(node);
- knot_rrset_t **place = changes->old_rrsets
- + changes->old_rrsets_count;
- /*! \todo Test this!!! */
- memcpy(place, rrsets, knot_node_rrset_count(node)
- * sizeof(knot_rrset_t *));
+ /*! \todo ref #937 is it OK to modify nodes at this point?
+ * shouldn't it be after the zones are switched? */
+
+ /* Assemble RRSets to remove. */
+ if (type == KNOT_RRTYPE_ANY) {
+ /* Remove all RRSets from the node. */
+ /* If removing from zone apex in an UPDATE, NS and SOA records
+ * should be left unchanged.
+ * We might either remove all RRSets and then return SOA and
+ * NS RRSets to the node. Or find all existing types in the node
+ * and remove all except NS and SOA. The first approach is
+ * IMHO faster.
+ */
- // remove all RRSets from the node
+ rrsets = knot_node_get_rrsets(node);
+ short rr_count = knot_node_rrset_count(node);
+ if (rr_count > 0) {
+ rrsets_count = (unsigned)rr_count;
+ }
knot_node_remove_all_rrsets(node);
+
+ /*
+ * If apex, return SOA and NS RRSets to the node and remove
+ * them from the list (so they are not deleted later).
+ *
+ * This function is called only when processing DDNS, but one
+ * never knows, so we'll rather check it
+ */
+ if (is_apex && (chflags & KNOT_CHANGESET_TYPE_DDNS)) {
+ dbg_xfrin_detail("DDNS: returning SOA and NS to the "
+ "node.\n");
+ for (unsigned i = 0; i < rrsets_count; ++i) {
+ if (knot_rrset_type(rrsets[i])
+ == KNOT_RRTYPE_SOA
+ || knot_rrset_type(rrsets[i])
+ == KNOT_RRTYPE_NS) {
+ dbg_xfrin_detail("Returning...\n");
+ knot_node_add_rrset(node, rrsets[i], 0);
+ rrsets[i] = NULL;
+ }
+ }
+ }
} else {
- ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
- &changes->old_rrsets_count,
- &changes->old_rrsets_allocated,
- 1);
+ /* Remove only the RRSet with given type. */
+ /* First we must check if we're not removing NS or SOA from
+ * apex. This change should be ignored.
+ *
+ * This function is called only when processing DDNS, but one
+ * never knows, so we'll rather check it
+ */
+ if (is_apex && (chflags & KNOT_CHANGESET_TYPE_DDNS)
+ && (type == KNOT_RRTYPE_SOA || type == KNOT_RRTYPE_NS)) {
+ dbg_xfrin_detail("DDNS: ignoring SOA or NS removal.\n");
+ return KNOT_EOK;
+ }
+
+ rrsets = malloc(sizeof(knot_rrset_t*));
+ if (rrsets) {
+ *rrsets = knot_node_remove_rrset(node, type);
+ rrsets_count = 1;
+ }
+ }
+
+ ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ rrsets_count);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to reserve changes rrsets.\n");
+ free(rrsets);
+ return ret;
+ }
+
+ /* Mark RRsets and RDATA for removal. */
+ for (unsigned i = 0; i < rrsets_count; ++i) {
+ if (rrsets[i] == NULL) {
+ continue;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] = rrsets[i];
+
+ /* Remove old RDATA. */
+ int rdata_count = knot_rrset_rdata_rr_count(rrsets[i]);
+ ret = knot_changes_rdata_reserve(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated,
+ rdata_count);
if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to check changeset rrsets.\n");
+ dbg_xfrin("Failed to reserve changes rdata.\n");
+ free(rrsets);
return ret;
}
- // remove only RRSet with the given type
- knot_rrset_t *rrset = knot_node_remove_rrset(node, type);
- changes->old_rrsets[changes->old_rrsets_count++] = rrset;
+
+ knot_changes_add_rdata(changes->old_rdata,
+ changes->old_rdata_types,
+ &changes->old_rdata_count,
+ knot_rrset_get_rdata(rrsets[i]),
+ knot_rrset_type(rrsets[i]));
}
+
+ free(rrsets);
return KNOT_EOK;
}
@@ -1917,11 +1914,179 @@ static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents,
/*----------------------------------------------------------------------------*/
+int xfrin_replace_rrset_in_node(knot_node_t *node,
+ knot_rrset_t *rrset_new,
+ knot_changes_t *changes,
+ knot_zone_contents_t *contents)
+{
+ knot_rr_type_t type = knot_rrset_type(rrset_new);
+ // remove RRSet of the proper type from the node
+ dbg_xfrin_verb("Removing RRSet of type: %s.\n",
+ knot_rrtype_to_string(type));
+ knot_rrset_t *rrset_old = knot_node_remove_rrset(node, type);
+ assert(rrset_old != NULL);
+
+ // add the old RRSet to the list of old RRSets
+ int ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old RRSet to list.\n");
+ return ret;
+ }
+
+ // save also the RDATA, because RDATA are not deleted with the RRSet
+ // The count should be 1, but just to be sure....
+ int count = knot_rrset_rdata_rr_count(rrset_old);
+ ret = knot_changes_rdata_reserve(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated, count);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old RDATA to list.\n");
+ return ret;
+ }
+
+ // save the new RRSet to the new RRSet, so that it is deleted if the
+ // apply fails
+ ret = knot_changes_rrsets_reserve(&changes->new_rrsets,
+ &changes->new_rrsets_count,
+ &changes->new_rrsets_allocated, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new RRSet to list.\n");
+ return ret;
+ }
+
+ // The count should be 1, but just to be sure....
+ count = knot_rrset_rdata_rr_count(rrset_new);
+ // save the new RDATA
+ ret = knot_changes_rdata_reserve(&changes->new_rdata,
+ &changes->new_rdata_types,
+ changes->new_rdata_count,
+ &changes->new_rdata_allocated, count);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new RDATA to list.\n");
+ return ret;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] = rrset_old;
+
+ dbg_xfrin_verb("Adding RDATA from old RRSet to the list of old RDATA."
+ "\n");
+ knot_changes_add_rdata(changes->old_rdata, changes->old_rdata_types,
+ &changes->old_rdata_count,
+ knot_rrset_get_rdata(rrset_old), type);
+
+ // store RRSIGs from the old RRSet to the new
+ knot_rrset_set_rrsigs(rrset_new, knot_rrset_get_rrsigs(rrset_old));
+
+ // insert the new RRSet to the node
+ dbg_xfrin_verb("Adding new RRSet.\n");
+ ret = knot_zone_contents_add_rrset(contents, rrset_new, &node,
+ KNOT_RRSET_DUPL_SKIP, 1);
+
+ if (ret < 0) {
+ dbg_xfrin("Failed to add RRSet to node.\n");
+ return KNOT_ERROR;
+ }
+ assert(ret == 0);
+
+ changes->new_rrsets[changes->new_rrsets_count++] = rrset_new;
+
+ dbg_xfrin_verb("Adding RDATA from new RRSet to the list of new RDATA."
+ "\n");
+ knot_changes_add_rdata(changes->new_rdata, changes->new_rdata_types,
+ &changes->new_rdata_count,
+ knot_rrset_get_rdata(rrset_new), type);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_add_normal_ddns(knot_changes_t *changes,
+ knot_rrset_t *add, knot_node_t *node,
+ knot_zone_contents_t *contents)
+{
+ int ret;
+
+ /* 1) Adding SOA. */
+ if (knot_rrset_type(add) == KNOT_RRTYPE_SOA) {
+ /* a) If trying to add SOA to non-apex node, or the
+ * serial is less than the current serial, ignore.
+ */
+ if (knot_node_rrset(node, KNOT_RRTYPE_SOA) == NULL
+ || ns_serial_compare(knot_rdata_soa_serial(
+ knot_rrset_rdata(
+ knot_node_rrset(node, KNOT_RRTYPE_SOA))),
+ knot_rdata_soa_serial(knot_rrset_rdata(add)
+ )) > 0
+ ) {
+ dbg_ddns_verb("DDNS: Ignoring SOA.\n");
+ return KNOT_EOK;
+ } else {
+ dbg_ddns_verb("DDNS: replacing SOA (old serial: %u,"
+ " new serial: %u.\n",
+ knot_rdata_soa_serial(knot_rrset_rdata(
+ knot_node_rrset(node,
+ KNOT_RRTYPE_SOA))),
+ knot_rdata_soa_serial(knot_rrset_rdata(
+ add)));
+ /* b) Otherwise, replace the current SOA. */
+ ret = xfrin_replace_rrset_in_node(node, add,
+ changes,
+ contents);
+ /* In this case we must however remove the ADD RRSet
+ * from the changeset, so that it is not deleted
+ * afterwards.
+ */
+ if (ret == KNOT_EOK) {
+ return 3;
+ } else {
+ return ret;
+ }
+ }
+ } else if (knot_rrset_type(add) == KNOT_RRTYPE_CNAME) {
+ /* 2) Adding CNAME... */
+ if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) {
+ dbg_ddns_verb("DDNS: replacing CNAME.\n");
+ /* a) ... to a CNAME node => replace. */
+ ret = xfrin_replace_rrset_in_node(node, add, changes,
+ contents);
+ /* In this case we must however remove the ADD RRSet
+ * from the changeset, so that it is not deleted
+ * afterwards.
+ */
+ if (ret == KNOT_EOK) {
+ return 3;
+ } else {
+ return ret;
+ }
+ } else if (knot_node_rrset_count(node) > 0) {
+ dbg_ddns_verb("DDNS: ignoring CNAME (non-empty node)\n");
+ /* b) ... to a non-empty node => ignore. */
+ return KNOT_EOK;
+ }
+ /* c) ... to an empty node => process normally. */
+ } else if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) {
+ /* 3) Adding other RRSets to CNAME node => ignore. */
+ dbg_ddns_verb("DDNS: ignoring RRSet (CNAME node)\n");
+ // handled in previous case
+ assert(knot_rrset_type(add) != KNOT_RRTYPE_CNAME);
+ return KNOT_EOK;
+ }
+
+ return 1; // Continue normal processing
+}
+
+/*----------------------------------------------------------------------------*/
+
static int xfrin_apply_add_normal(knot_changes_t *changes,
knot_rrset_t *add,
knot_node_t *node,
knot_rrset_t **rrset,
- knot_zone_contents_t *contents)
+ knot_zone_contents_t *contents,
+ uint32_t chflags)
{
assert(changes != NULL);
assert(add != NULL);
@@ -1935,6 +2100,15 @@ dbg_xfrin_exec_detail(
dbg_xfrin_detail("applying rrset:\n");
knot_rrset_dump(add, 0);
);
+
+ /* DDNS special cases. */
+ if (chflags & KNOT_CHANGESET_TYPE_DDNS) {
+ ret = xfrin_apply_add_normal_ddns(changes, add, node, contents);
+ /* Continue only if return value is 1. */
+ if (ret != 1) {
+ return ret;
+ }
+ }
int copied = 0;
/*! \note Reusing RRSet from previous function caused it not to be
@@ -1955,7 +2129,7 @@ dbg_xfrin_exec_detail(
knot_rrset_t *old = *rrset;
if (*rrset != NULL) {
- ret = xfrin_copy_old_rrset(old, rrset, changes);
+ ret = xfrin_copy_old_rrset(old, rrset, changes, 1);
if (ret != KNOT_EOK) {
return ret;
}
@@ -1995,7 +2169,7 @@ dbg_xfrin_exec_detail(
if (ret < 0) {
dbg_xfrin("Failed to add RRSet to node.\n");
- return KNOT_ERROR;
+ return ret;
}
assert(ret == 0);
@@ -2021,6 +2195,7 @@ dbg_xfrin_exec_detail(
*
* TODO: add the 'add' rrset to list of old RRSets?
*/
+
dbg_xfrin_detail("Merging RRSets with owners: %s, %s types: %s, %s\n",
(*rrset)->owner->name, add->owner->name,
knot_rrtype_to_string((*rrset)->type),
@@ -2039,7 +2214,7 @@ dbg_xfrin_exec_detail(
}
ret = knot_rrset_merge_no_dupl((void **)rrset, (void **)&add);
- if (ret != KNOT_EOK) {
+ if (ret < 0) {
dbg_xfrin("Failed to merge changeset RRSet.\n");
return ret;
}
@@ -2103,7 +2278,7 @@ dbg_xfrin_exec_verb(
dbg_xfrin_verb("Using RRSet from previous iteration.\n");
} else {
// copy the rrset
- ret = xfrin_copy_rrset(node, type, rrset, changes);
+ ret = xfrin_copy_rrset(node, type, rrset, changes, 1);
if (ret < 0) {
return ret;
} else if (ret != KNOT_EOK) {
@@ -2128,7 +2303,7 @@ dbg_xfrin_exec_verb(
dbg_xfrin_detail("Created new RRSet for RRSIG: %p.\n", *rrset);
// add the RRset to the list of new RRsets
- ret = xfrin_changes_check_rrsets(
+ ret = knot_changes_rrsets_reserve(
&changes->new_rrsets,
&changes->new_rrsets_count,
&changes->new_rrsets_allocated, 1);
@@ -2183,7 +2358,8 @@ dbg_xfrin_exec_detail(
dbg_xfrin_verb("Using RRSIG from previous iteration\n");
rrsig = *rrsigs_old;
} else {
- ret = xfrin_copy_old_rrset(old, &rrsig, changes);
+ ret = xfrin_copy_old_rrset(old, &rrsig, changes,
+ 1);
if (ret != KNOT_EOK) {
return ret;
}
@@ -2202,7 +2378,7 @@ dbg_xfrin_exec_detail(
// merge the changeset RRSet to the copy
dbg_xfrin_detail("Merging RRSIG to the one in the RRSet.\n");
ret = knot_rrset_merge_no_dupl((void **)&rrsig, (void **)&add);
- if (ret != KNOT_EOK) {
+ if (ret < 0) {
dbg_xfrin("Failed to merge changeset RRSIG to copy: %s"
".\n", knot_strerror(ret));
return KNOT_ERROR;
@@ -2535,10 +2711,14 @@ dbg_xfrin_exec_detail(
knot_rrset_rdata(chset->remove[i]))
== KNOT_RRTYPE_NSEC3))
{
+ dbg_xfrin_verb("Removed RRSet belongs to NSEC3 tree.\n");
is_nsec3 = 1;
}
// check if the old node is not the one we should use
+ dbg_xfrin_verb("Node:%p Owner: %p Node owner: %p\n",
+ node, knot_rrset_owner(chset->remove[i]),
+ knot_node_owner(node));
if (!node || knot_rrset_owner(chset->remove[i])
!= knot_node_owner(node)) {
if (is_nsec3) {
@@ -2559,10 +2739,12 @@ dbg_xfrin_exec_detail(
assert(node != NULL);
// first check if all RRSets should be removed
+ dbg_xfrin_verb("RRSet class to be removed=%u\n",
+ knot_rrset_class(chset->remove[i]));
if (knot_rrset_class(chset->remove[i]) == KNOT_CLASS_ANY) {
ret = xfrin_apply_remove_all_rrsets(
changes, node,
- knot_rrset_type(chset->remove[i]));
+ knot_rrset_type(chset->remove[i]), chset->flags);
} else if (knot_rrset_type(chset->remove[i])
== KNOT_RRTYPE_RRSIG) {
// this should work also for UPDATE
@@ -2573,7 +2755,8 @@ dbg_xfrin_exec_detail(
// this should work also for UPDATE
ret = xfrin_apply_remove_normal(changes,
chset->remove[i],
- node, &rrset);
+ node, &rrset,
+ chset->flags);
}
dbg_xfrin_detail("xfrin_apply_remove() ret = %d\n", ret);
@@ -2594,7 +2777,7 @@ static int xfrin_apply_add(knot_zone_contents_t *contents,
knot_changeset_t *chset,
knot_changes_t *changes)
{
- int ret = 0;
+ int ret = KNOT_EOK;
knot_node_t *node = NULL;
knot_rrset_t *rrset = NULL;
knot_rrset_t *rrsigs = NULL;
@@ -2661,22 +2844,27 @@ dbg_xfrin_exec_detail(
ret = xfrin_apply_add_rrsig(changes, chset->add[i],
node, &rrset, &rrsigs,
contents);
+ assert(ret != KNOT_EOK);
} else {
ret = xfrin_apply_add_normal(changes, chset->add[i],
- node, &rrset, contents);
+ node, &rrset, contents,
+ chset->flags);
+ assert(ret <= 3);
}
- assert(ret != KNOT_EOK);
+ // Not correct anymore, add_normal() returns KNOT_EOK if the
+ // changeset RR should be removed
+ //assert(ret != KNOT_EOK);
- dbg_xfrin_detail("xfrin_apply_..() returned %s, rrset: %p\n",
- knot_strerror(ret), rrset);
+ dbg_xfrin_detail("xfrin_apply_..() returned %d, rrset: %p\n",
+ ret, rrset);
if (ret > 0) {
if (ret == 1) {
// the ADD RRSet was used, i.e. it should be
// removed from the changeset and saved in the
// list of new RRSets
- ret = xfrin_changes_check_rrsets(
+ ret = knot_changes_rrsets_reserve(
&changes->new_rrsets,
&changes->new_rrsets_count,
&changes->new_rrsets_allocated, 1);
@@ -2694,7 +2882,7 @@ dbg_xfrin_exec_detail(
chset->add[i]);
// connect the RDATA to the list of new RDATA
- int res = xfrin_changes_check_rdata(
+ int res = knot_changes_rdata_reserve(
&changes->new_rdata,
&changes->new_rdata_types,
changes->new_rdata_count,
@@ -2703,7 +2891,7 @@ dbg_xfrin_exec_detail(
return res;
}
- xfrin_changes_add_rdata(changes->new_rdata,
+ knot_changes_add_rdata(changes->new_rdata,
changes->new_rdata_types,
&changes->new_rdata_count,
knot_rrset_get_rdata(chset->add[i]),
@@ -2721,6 +2909,11 @@ dbg_xfrin_exec_detail(
// stored in the list of new RDATA, because
// it is joined to the copy of RDATA, that is
// already stored there
+ } else if (ret == 3) {
+ // the RRSet was used and both RRSet and RDATA
+ // were properly stored. Just clear the place
+ // in the changeset
+ chset->add[i] = NULL;
} else {
assert(0);
}
@@ -2743,97 +2936,18 @@ static int xfrin_apply_replace_soa(knot_zone_contents_t *contents,
knot_node_t *node = knot_zone_contents_get_apex(contents);
assert(node != NULL);
- int ret = 0;
-
assert(node != NULL);
- // set the node copy as the apex of the contents
- contents->apex = node;
-
- // remove the SOA RRSet from the apex
- dbg_xfrin_verb("Removing SOA.\n");
- knot_rrset_t *rrset = knot_node_remove_rrset(node, KNOT_RRTYPE_SOA);
- assert(rrset != NULL);
-
- // add the old RRSet to the list of old RRSets
- ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
- &changes->old_rrsets_count,
- &changes->old_rrsets_allocated, 1);
- if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to add old RRSet to list.\n");
- return ret;
- }
-
- // save also the SOA RDATA, because RDATA are not deleted with the
- // RRSet
- ret = xfrin_changes_check_rdata(&changes->old_rdata,
- &changes->old_rdata_types,
- changes->old_rdata_count,
- &changes->old_rdata_allocated, 1);
- if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to add old RDATA to list.\n");
- return ret;
- }
- // save the SOA to the new RRSet, so that it is deleted if the
- // apply fails
- ret = xfrin_changes_check_rrsets(&changes->new_rrsets,
- &changes->new_rrsets_count,
- &changes->new_rrsets_allocated, 1);
- if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to add new RRSet to list.\n");
- return ret;
+ int ret = xfrin_replace_rrset_in_node(node, chset->soa_to, changes,
+ contents);
+ if (ret == KNOT_EOK) {
+ // remove the SOA from the changeset, so it will not be deleted
+ // after successful apply
+ chset->soa_to = NULL;
}
- int count = knot_rrset_rdata_rr_count(rrset);
- count += knot_rrset_rdata_rr_count(chset->soa_to);
- // save the new SOA RDATA
- ret = xfrin_changes_check_rdata(&changes->new_rdata,
- &changes->new_rdata_types,
- changes->new_rdata_count,
- &changes->new_rdata_allocated, count);
- if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to add new RDATA to list.\n");
- return ret;
- }
-
- changes->old_rrsets[changes->old_rrsets_count++] = rrset;
-
- /*! \todo Maybe check if the SOA does not have more RDATA? */
- dbg_xfrin_verb("Adding RDATA from old SOA to the list of old RDATA.\n");
- xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types,
- &changes->old_rdata_count, rrset->rdata,
- KNOT_RRTYPE_SOA);
-
- // store RRSIGs from the old SOA to the new SOA
- knot_rrset_set_rrsigs(chset->soa_to, knot_rrset_get_rrsigs(rrset));
-
- // insert the new SOA RRSet to the node
- dbg_xfrin_verb("Adding SOA.\n");
- //ret = knot_node_add_rrset(node, chset->soa_to, 0);
- ret = knot_zone_contents_add_rrset(contents, chset->soa_to, &node,
- KNOT_RRSET_DUPL_SKIP, 1);
-
- if (ret < 0) {
- dbg_xfrin("Failed to add RRSet to node.\n");
- return KNOT_ERROR;
- }
- assert(ret == 0);
-
- changes->new_rrsets[changes->new_rrsets_count++] = chset->soa_to;
-
- dbg_xfrin_verb("Adding RDATA from new SOA to the list of new RDATA.\n");
- xfrin_changes_add_rdata(changes->new_rdata,
- changes->new_rdata_types,
- &changes->new_rdata_count,
- knot_rrset_get_rdata(chset->soa_to),
- knot_rrset_type(chset->soa_to));
-
- // remove the SOA from the changeset, so it will not be deleted after
- // successful apply
- chset->soa_to = NULL;
-
- return KNOT_EOK;
+ return ret;
}
/*----------------------------------------------------------------------------*/
@@ -2885,7 +2999,7 @@ static void xfrin_mark_empty(knot_node_t *node, void *data)
if (knot_node_rrset_count(node) == 0
&& knot_node_children(node) == 0) {
- int ret = xfrin_changes_check_nodes(&changes->old_nodes,
+ int ret = knot_changes_nodes_reserve(&changes->old_nodes,
&changes->old_nodes_count,
&changes->old_nodes_allocated);
if (ret != KNOT_EOK) {
@@ -2919,7 +3033,7 @@ static void xfrin_mark_empty_nsec3(knot_node_t *node, void *data)
if (knot_node_rrset_count(node) == 0
&& knot_node_children(node) == 0) {
- int ret = xfrin_changes_check_nodes(&changes->old_nsec3,
+ int ret = knot_changes_nodes_reserve(&changes->old_nsec3,
&changes->old_nsec3_count,
&changes->old_nsec3_allocated);
if (ret != KNOT_EOK) {
@@ -3069,22 +3183,15 @@ static int xfrin_check_contents_copy(knot_zone_contents_t *old_contents)
/*----------------------------------------------------------------------------*/
-int xfrin_apply_changesets(knot_zone_t *zone,
- knot_changesets_t *chsets,
- knot_zone_contents_t **new_contents)
+int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
+ knot_zone_contents_t **new_contents,
+ knot_changes_t **changes)
{
- if (zone == NULL || chsets == NULL || chsets->count == 0
- || new_contents == NULL) {
+ if (old_contents == NULL || new_contents == NULL || changes == NULL) {
return KNOT_EINVAL;
}
- knot_zone_contents_t *old_contents = knot_zone_get_contents(zone);
- if (!old_contents) {
- dbg_xfrin("Cannot apply changesets to empty zone.\n");
- return KNOT_EINVAL;
- }
-
- dbg_xfrin("Applying changesets to zone...\n");
+ dbg_xfrin("Preparing zone copy...\n");
/*
* Ensure that the zone generation is set to 0.
@@ -3116,15 +3223,15 @@ int xfrin_apply_changesets(knot_zone_t *zone,
return ret;
}
- knot_changes_t *changes = (knot_changes_t *)malloc(
+ knot_changes_t *chgs = (knot_changes_t *)malloc(
sizeof(knot_changes_t));
- if (changes == NULL) {
+ if (chgs == NULL) {
dbg_xfrin("Failed to allocate structure for changes!\n");
- xfrin_rollback_update(old_contents, &contents_copy, &changes);
+ xfrin_rollback_update(old_contents, &contents_copy, &chgs);
return KNOT_ENOMEM;
}
- memset(changes, 0, sizeof(knot_changes_t));
+ memset(chgs, 0, sizeof(knot_changes_t));
/*!
* \todo Check if all nodes have their copy.
@@ -3132,7 +3239,7 @@ int xfrin_apply_changesets(knot_zone_t *zone,
ret = xfrin_check_contents_copy(old_contents);
if (ret != KNOT_EOK) {
dbg_xfrin("Contents copy check failed!\n");
- xfrin_rollback_update(old_contents, &contents_copy, &changes);
+ xfrin_rollback_update(old_contents, &contents_copy, &chgs);
return ret;
}
@@ -3145,30 +3252,23 @@ int xfrin_apply_changesets(knot_zone_t *zone,
dbg_xfrin("Switching ptrs pointing to old nodes to the new nodes.\n");
ret = xfrin_switch_nodes(contents_copy);
assert(knot_zone_contents_apex(contents_copy) != NULL);
+
+ *new_contents = contents_copy;
+ *changes = chgs;
+
+ return KNOT_EOK;
+}
- /*
- * Apply the changesets.
- */
- dbg_xfrin("Applying changesets.\n");
- dbg_xfrin_verb("Old contents apex: %p, new apex: %p\n",
- old_contents->apex, contents_copy->apex);
- for (int i = 0; i < chsets->count; ++i) {
- if ((ret = xfrin_apply_changeset(contents_copy, changes,
- &chsets->sets[i]))
- != KNOT_EOK) {
- xfrin_rollback_update(old_contents,
- &contents_copy, &changes);
- dbg_xfrin("Failed to apply changesets to zone: "
- "%s\n", knot_strerror(ret));
- return ret;
- }
- }
- assert(knot_zone_contents_apex(contents_copy) != NULL);
-
- /*!
- * \todo Test failure of IXFR.
- */
+/*----------------------------------------------------------------------------*/
+int xfrin_finalize_updated_zone(knot_zone_contents_t *contents_copy,
+ knot_changes_t *changes,
+ knot_zone_contents_t *old_contents)
+{
+ if (contents_copy == NULL || changes == NULL || old_contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
/*
* Finalize the new zone contents:
* - delete empty nodes
@@ -3181,11 +3281,11 @@ int xfrin_apply_changesets(knot_zone_t *zone,
* Select and remove empty nodes from zone trees. Do not free them right
* away as they may be referenced by some domain names.
*/
- ret = xfrin_remove_empty_nodes(contents_copy, changes);
+ int ret = xfrin_remove_empty_nodes(contents_copy, changes);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to remove empty nodes: %s\n",
knot_strerror(ret));
- xfrin_rollback_update(old_contents, &contents_copy, &changes);
+// xfrin_rollback_update(old_contents, &contents_copy, &changes);
return ret;
}
@@ -3196,7 +3296,7 @@ int xfrin_apply_changesets(knot_zone_t *zone,
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to finalize zone contents: %s\n",
knot_strerror(ret));
- xfrin_rollback_update(old_contents, &contents_copy, &changes);
+// xfrin_rollback_update(old_contents, &contents_copy, &changes);
return ret;
}
assert(knot_zone_contents_apex(contents_copy) != NULL);
@@ -3205,6 +3305,71 @@ int xfrin_apply_changesets(knot_zone_t *zone,
ret = knot_zone_contents_check_loops(contents_copy);
if (ret != KNOT_EOK) {
dbg_xfrin("CNAME loop check failed: %s\n", knot_strerror(ret));
+// xfrin_rollback_update(old_contents, &contents_copy, &changes);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_apply_changesets(knot_zone_t *zone,
+ knot_changesets_t *chsets,
+ knot_zone_contents_t **new_contents)
+{
+ if (zone == NULL || chsets == NULL || chsets->count == 0
+ || new_contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_zone_contents_t *old_contents = knot_zone_get_contents(zone);
+ if (!old_contents) {
+ dbg_xfrin("Cannot apply changesets to empty zone.\n");
+ return KNOT_EINVAL;
+ }
+
+ dbg_xfrin("Applying changesets to zone...\n");
+
+ dbg_xfrin_verb("Creating shallow copy of the zone...\n");
+ knot_zone_contents_t *contents_copy = NULL;
+ knot_changes_t *changes = NULL;
+ int ret = xfrin_prepare_zone_copy(old_contents, &contents_copy,
+ &changes);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to prepare zone copy: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /*
+ * Apply the changesets.
+ */
+ dbg_xfrin("Applying changesets.\n");
+ dbg_xfrin_verb("Old contents apex: %p, new apex: %p\n",
+ old_contents->apex, contents_copy->apex);
+ for (int i = 0; i < chsets->count; ++i) {
+ if ((ret = xfrin_apply_changeset(contents_copy, changes,
+ &chsets->sets[i]))
+ != KNOT_EOK) {
+ xfrin_rollback_update(old_contents,
+ &contents_copy, &changes);
+ dbg_xfrin("Failed to apply changesets to zone: "
+ "%s\n", knot_strerror(ret));
+ return ret;
+ }
+ }
+ assert(knot_zone_contents_apex(contents_copy) != NULL);
+
+ /*!
+ * \todo Test failure of IXFR.
+ */
+
+ dbg_xfrin_verb("Finalizing updated zone...\n");
+ ret = xfrin_finalize_updated_zone(contents_copy, changes, old_contents);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to finalize updated zone: %s\n",
+ knot_strerror(ret));
xfrin_rollback_update(old_contents, &contents_copy, &changes);
return ret;
}
@@ -3237,7 +3402,7 @@ int xfrin_switch_zone(knot_zone_t *zone,
old, (old) ? old->apex : NULL, new_contents->apex);
// switch pointers in domain names, now only the new zone is used
- if (transfer_type == XFR_TYPE_IIN) {
+ if (transfer_type == XFR_TYPE_IIN || transfer_type == XFR_TYPE_UPDATE) {
// Traverse also the dname table and change the node pointers
// in dnames
int ret = knot_zone_contents_dname_table_apply(
diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h
index a762b81..c65e708 100644
--- a/src/libknot/updates/xfr-in.h
+++ b/src/libknot/updates/xfr-in.h
@@ -185,6 +185,14 @@ int xfrin_apply_changesets(knot_zone_t *zone,
knot_changesets_t *chsets,
knot_zone_contents_t **new_contents);
+int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
+ knot_zone_contents_t **new_contents,
+ knot_changes_t **changes);
+
+int xfrin_finalize_updated_zone(knot_zone_contents_t *contents_copy,
+ knot_changes_t *changes,
+ knot_zone_contents_t *old_contents);
+
int xfrin_switch_zone(knot_zone_t *zone,
knot_zone_contents_t *new_contents,
int deep_free);
@@ -195,6 +203,18 @@ void xfrin_rollback_update(knot_zone_contents_t *old_contents,
knot_zone_contents_t **new_contents,
knot_changes_t **changes);
+int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type,
+ knot_rrset_t **rrset, knot_changes_t *changes,
+ int save_new);
+
+int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
+ knot_changes_t *changes, int save_new);
+
+int xfrin_replace_rrset_in_node(knot_node_t *node,
+ knot_rrset_t *rrset_new,
+ knot_changes_t *changes,
+ knot_zone_contents_t *contents);
+
#endif /* _KNOTXFR_IN_H_ */
/*! @} */
diff --git a/src/libknot/util/debug.h b/src/libknot/util/debug.h
index 731fed8..abe335b 100644
--- a/src/libknot/util/debug.h
+++ b/src/libknot/util/debug.h
@@ -68,6 +68,7 @@
#ifdef KNOT_XFR_DEBUG
#define KNOT_XFRIN_DEBUG
#define KNOT_TSIG_DEBUG
+ #define KNOT_DDNS_DEBUG
#endif
/* KNOT_DNAME_DEBUG -- in configure.ac */
@@ -758,27 +759,33 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
#ifdef DEBUG_ENABLE_BRIEF
#define dbg_ddns(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg)
#define dbg_ddns_hex(data, len) hex_log(LOG_ZONE, (data), (len))
+#define dbg_ddns_exec(cmds) do { cmds } while (0)
#else
#define dbg_ddns(msg...)
#define dbg_ddns_hex(data, len)
+#define dbg_ddns_exec(cmds)
#endif
/* Verbose messages. */
#ifdef DEBUG_ENABLE_VERBOSE
#define dbg_ddns_verb(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg)
#define dbg_ddns_hex_verb(data, len) hex_log(LOG_ZONE, (data), (len))
+#define dbg_ddns_exec_verb(cmds) do { cmds } while (0)
#else
#define dbg_ddns_verb(msg...)
#define dbg_ddns_hex_verb(data, len)
+#define dbg_ddns_exec_verb(cmds)
#endif
/* Detail messages. */
#ifdef DEBUG_ENABLE_DETAILS
#define dbg_ddns_detail(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg)
#define dbg_ddns_hex_detail(data, len) hex_log(LOG_ZONE, (data), (len))
+#define dbg_ddns_exec_detail(cmds) do { cmds } while (0)
#else
#define dbg_ddns_detail(msg...)
#define dbg_ddns_hex_detail(data, len)
+#define dbg_ddns_exec_detail(cmds)
#endif
/* No messages. */
@@ -786,9 +793,12 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
#define dbg_ddns(msg...)
#define dbg_ddns_hex(data, len)
#define dbg_ddns_verb(msg...)
+#define dbg_ddns_exec(cmds)
#define dbg_ddns_hex_verb(data, len)
+#define dbg_ddns_exec_verb(cmds)
#define dbg_ddns_detail(msg...)
#define dbg_ddns_hex_detail(data, len)
+#define dbg_ddns_exec_detail(cmds)
#endif
#ifdef KNOT_TSIG_DEBUG
diff --git a/src/libknot/util/descriptor.c b/src/libknot/util/descriptor.c
index 1588a2e..8cab6b4 100644
--- a/src/libknot/util/descriptor.c
+++ b/src/libknot/util/descriptor.c
@@ -68,6 +68,15 @@ static knot_lookup_table_t dns_rrclasses[] = {
{ 0, NULL }
};
+/*! \brief DS digest lengths. */
+enum knot_ds_algorithm_len
+{
+ KNOT_DS_DIGEST_LEN_SHA1 = 20, /* 20B - RFC 3658 */
+ KNOT_DS_DIGEST_LEN_SHA256 = 32, /* 32B - RFC 4509 */
+ KNOT_DS_DIGEST_LEN_GOST = 32, /* 32B - RFC 5933 */
+ KNOT_DS_DIGEST_LEN_SHA384 = 48 /* 48B - RFC 6605 */
+};
+
/*! \brief RR type descriptors. */
static knot_rrtype_descriptor_t
knot_rrtype_descriptors[KNOT_RRTYPE_DESCRIPTORS_LENGTH] = {
@@ -572,5 +581,21 @@ int knot_rrtype_is_metatype(uint16_t type)
|| type == KNOT_RRTYPE_OPT);
}
+size_t knot_ds_digest_length(uint8_t algorithm)
+{
+ switch (algorithm) {
+ case KNOT_DS_ALG_SHA1:
+ return KNOT_DS_DIGEST_LEN_SHA1;
+ case KNOT_DS_ALG_SHA256:
+ return KNOT_DS_DIGEST_LEN_SHA256;
+ case KNOT_DS_ALG_GOST:
+ return KNOT_DS_DIGEST_LEN_GOST;
+ case KNOT_DS_ALG_SHA384:
+ return KNOT_DS_DIGEST_LEN_SHA384;
+ default:
+ return 0;
+ }
+}
+
/*! @} */
diff --git a/src/libknot/util/descriptor.h b/src/libknot/util/descriptor.h
index 6364e5b..23b5d13 100644
--- a/src/libknot/util/descriptor.h
+++ b/src/libknot/util/descriptor.h
@@ -237,6 +237,41 @@ typedef enum knot_rdata_zoneformat knot_rdata_zoneformat_t;
/*! \brief Enum containing wireformat codes. */
typedef enum knot_rdatawireformat knot_rdata_wireformat_t;
+/*! \brief Constants for DNSSEC algorithm types.
+ *
+ * Source: http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml
+ */
+enum knot_dnssec_algorithm
+{
+ KNOT_DNSSEC_ALG_RSAMD5 = 1,
+ KNOT_DNSSEC_ALG_DH = 2,
+ KNOT_DNSSEC_ALG_DSA = 3,
+ KNOT_DNSSEC_ALG_RSASHA1 = 5,
+ KNOT_DNSSEC_ALG_DSA_NSEC3_SHA1 = 6,
+ KNOT_DNSSEC_ALG_RSASHA1_NSEC3_SHA1 = 7,
+ KNOT_DNSSEC_ALG_RSASHA256 = 8,
+ KNOT_DNSSEC_ALG_RSASHA512 = 10,
+ KNOT_DNSSEC_ALG_ECC_GOST = 12,
+ KNOT_DNSSEC_ALG_ECDSAP256SHA256 = 13,
+ KNOT_DNSSEC_ALG_ECDSAP384SHA384 = 14
+};
+
+typedef enum knot_dnssec_algorithm knot_dnssec_algorithm_t;
+
+/*! \brief Constants for DNSSEC algorithm types.
+ *
+ * Source: http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xml
+ */
+enum knot_ds_algorithm
+{
+ KNOT_DS_ALG_SHA1 = 1, /* 20B - RFC 3658 */
+ KNOT_DS_ALG_SHA256 = 2, /* 32B - RFC 4509 */
+ KNOT_DS_ALG_GOST = 3, /* 32B - RFC 5933 */
+ KNOT_DS_ALG_SHA384 = 4 /* 48B - RFC 6605 */
+};
+
+typedef enum knot_ds_algorithm knot_ds_algorithm_t;
+
/*! \brief Structure holding RR descriptor. */
struct knot_rrtype_descriptor {
uint16_t type; /*!< RR type */
@@ -328,6 +363,8 @@ size_t knot_wireformat_size(unsigned int wire_type);
int knot_rrtype_is_metatype(uint16_t type);
+size_t knot_ds_digest_length(uint8_t algorithm);
+
#endif /* _KNOT_DESCRIPTOR_H_ */
/*! @} */
diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c
index 61e9e51..bdc268b 100644
--- a/src/libknot/zone/zone-contents.c
+++ b/src/libknot/zone/zone-contents.c
@@ -408,6 +408,40 @@ static void knot_zone_contents_adjust_rdata_item(knot_rdata_t *rdata,
}
/*----------------------------------------------------------------------------*/
+
+static int knot_zone_contents_adjust_rdata(knot_rdata_t *rdata,
+ knot_rrtype_descriptor_t *desc,
+ knot_zone_contents_t *zone,
+ knot_node_t *node)
+{
+ for (int i = 0; i < rdata->count; ++i) {
+ if (desc->wireformat[i]
+ == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_LITERAL_DNAME) {
+ dbg_zone_detail("Adjusting domain name at position %d"
+ " of the RDATA\n", i);
+ knot_zone_contents_adjust_rdata_item(rdata, zone, node,
+ i);
+ }
+ }
+
+ /*
+ * DS digest length check (#2050).
+ */
+ int ret;
+ if (desc->type == KNOT_RRTYPE_DS
+ && (ret = knot_rdata_ds_check(rdata)) != KNOT_EOK) {
+ dbg_zone("DS RDATA check failed: %s\n", knot_strerror(ret));
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
/*!
* \brief Adjusts all RDATA in the given RRSet by replacing domain names by ones
* present in the zone.
@@ -419,9 +453,9 @@ static void knot_zone_contents_adjust_rdata_item(knot_rdata_t *rdata,
* \param rrset RRSet to adjust RDATA in.
* \param zone Zone to which the RRSet belongs.
*/
-static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset,
- knot_zone_contents_t *zone,
- knot_node_t *node)
+static int knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset,
+ knot_zone_contents_t *zone,
+ knot_node_t *node)
{
uint16_t type = knot_rrset_type(rrset);
@@ -433,49 +467,37 @@ static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset,
knot_rdata_t *rdata = rdata_first;
if (rdata == NULL) {
- return;
+ return KNOT_EOK;
}
+ int i = 1;
+ int ret;
while (rdata->next != rdata_first) {
- for (int i = 0; i < rdata->count; ++i) {
- if (desc->wireformat[i]
- == KNOT_RDATA_WF_COMPRESSED_DNAME
- || desc->wireformat[i]
- == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
- || desc->wireformat[i]
- == KNOT_RDATA_WF_LITERAL_DNAME) {
- dbg_zone("Adjusting domain name at "
- "position %d of RDATA of record with owner "
- "%s and type %s.\n",
- i, rrset->owner->name,
- knot_rrtype_to_string(type));
-
- knot_zone_contents_adjust_rdata_item(rdata,
- zone, node,
- i);
- }
+dbg_zone_exec_detail(
+ char *name = knot_dname_to_str(knot_rrset_owner(rrset));
+ dbg_zone_detail("Adjusting domain name at %d. RDATA of RRSet "
+ "with owner %s and type %s.\n", i, name,
+ knot_rrtype_to_string(type));
+ free(name);
+);
+ ret = knot_zone_contents_adjust_rdata(rdata, desc,
+ zone, node);
+ if (ret != KNOT_EOK) {
+ return ret;
}
- rdata = rdata->next;
- }
- for (int i = 0; i < rdata->count; ++i) {
- if (desc->wireformat[i]
- == KNOT_RDATA_WF_COMPRESSED_DNAME
- || desc->wireformat[i]
- == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
- || desc->wireformat[i]
- == KNOT_RDATA_WF_LITERAL_DNAME) {
- dbg_zone("Adjusting domain name at "
- "position %d of RDATA of record with owner "
- "%s and type %s.\n",
- i, rrset->owner->name,
- knot_rrtype_to_string(type));
-
- knot_zone_contents_adjust_rdata_item(rdata, zone, node,
- i);
- }
+ rdata = rdata->next;
+ ++i;
}
+dbg_zone_exec_detail(
+ char *name = knot_dname_to_str(knot_rrset_owner(rrset));
+ dbg_zone_detail("Adjusting domain name at %d. RDATA of RRSet "
+ "with owner %s and type %s.\n", i, name,
+ knot_rrtype_to_string(type));
+ free(name);
+);
+ return knot_zone_contents_adjust_rdata(rdata, desc, zone, node);
}
/*----------------------------------------------------------------------------*/
@@ -489,28 +511,37 @@ static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset,
* \param node Zone node to adjust the RRSets in.
* \param zone Zone to which the node belongs.
*/
-static void knot_zone_contents_adjust_rrsets(knot_node_t *node,
- knot_zone_contents_t *zone)
+static int knot_zone_contents_adjust_rrsets(knot_node_t *node,
+ knot_zone_contents_t *zone)
{
knot_rrset_t **rrsets = knot_node_get_rrsets(node);
short count = knot_node_rrset_count(node);
assert(count == 0 || rrsets != NULL);
+ int ret = KNOT_EOK;
for (int r = 0; r < count; ++r) {
assert(rrsets[r] != NULL);
dbg_zone("Adjusting next RRSet.\n");
- knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone,
- node);
+ ret = knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone,
+ node);
+
knot_rrset_t *rrsigs = rrsets[r]->rrsigs;
- if (rrsigs != NULL) {
+ if (ret == KNOT_EOK && rrsigs != NULL) {
dbg_zone("Adjusting next RRSIGs.\n");
- knot_zone_contents_adjust_rdata_in_rrset(rrsigs, zone,
- node);
+ ret = knot_zone_contents_adjust_rdata_in_rrset(rrsigs,
+ zone,
+ node);
+ }
+
+ if (ret != KNOT_EOK) {
+ break;
}
}
free(rrsets);
+
+ return ret;
}
/*----------------------------------------------------------------------------*/
/*!
@@ -529,12 +560,15 @@ static void knot_zone_contents_adjust_rrsets(knot_node_t *node,
* in the inserting function, though that may not be always used (e.g.
* old changeset processing).
*/
-static void knot_zone_contents_adjust_node(knot_node_t *node,
- knot_zone_contents_t *zone)
+static int knot_zone_contents_adjust_node(knot_node_t *node,
+ knot_zone_contents_t *zone)
{
// adjust domain names in RDATA
/*! \note Enabled again after a LONG time. Should test thoroughly. */
- knot_zone_contents_adjust_rrsets(node, zone);
+ int ret = knot_zone_contents_adjust_rrsets(node, zone);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
// assure that owner has proper node
if (knot_dname_node(knot_node_owner(node)) == NULL) {
@@ -566,6 +600,8 @@ static void knot_zone_contents_adjust_node(knot_node_t *node,
knot_node_is_deleg_point(node) ? "yes" : "no");
dbg_zone_detail("Non-authoritative: %s\n",
knot_node_is_non_auth(node) ? "yes" : "no");
+
+ return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
@@ -621,7 +657,12 @@ dbg_zone_exec_verb(
* 2) Do other adjusting (flags, closest enclosers, wildcard children,
* etc.).
*/
- knot_zone_contents_adjust_node(node, zone);
+ ret = knot_zone_contents_adjust_node(node, zone);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to adjust node: %s\n", knot_strerror(ret));
+ args->err = ret;
+ return;
+ }
}
/*----------------------------------------------------------------------------*/
@@ -816,10 +857,10 @@ dbg_zone_exec_verb(
dbg_zone("\n");
char *name_b32 = NULL;
- size_t size = base32hex_encode_alloc((char *)hashed_name, hash_size,
- &name_b32);
+ int32_t b32_ret = base32hex_encode_alloc(hashed_name, hash_size,
+ (uint8_t **)(&name_b32));
- if (size == 0) {
+ if (b32_ret <= 0) {
char *n = knot_dname_to_str(name);
dbg_zone("Error while encoding hashed name %s to base32.\n", n);
free(n);
@@ -829,6 +870,8 @@ dbg_zone_exec_verb(
return KNOT_ECRYPTO;
}
+ size_t size = b32_ret;
+
assert(name_b32 != NULL);
free(hashed_name);
@@ -1316,22 +1359,21 @@ dbg_zone_exec_detail(
return ret;
}
- ret = knot_zone_tree_insert(zone->nodes, node);
- if (ret != KNOT_EOK) {
- dbg_zone("Failed to insert node into zone tree.\n");
- return ret;
- }
-
if (use_domain_table) {
ret = knot_zone_contents_dnames_from_node_to_table(
zone->dname_table, node);
if (ret != KNOT_EOK) {
- /*! \todo Remove node from the tree and hash table.*/
dbg_zone("Failed to add dnames into table.\n");
return ret;
}
}
+ ret = knot_zone_tree_insert(zone->nodes, node);
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to insert node into zone tree.\n");
+ return ret;
+ }
+
#ifdef USE_HASH_TABLE
// add the node also to the hash table if authoritative, or deleg. point
if (zone->table != NULL
@@ -1339,7 +1381,10 @@ dbg_zone_exec_detail(
(const char *)node->owner->name,
node->owner->size, (void *)node) != 0) {
dbg_zone("Error inserting node into hash table!\n");
- /*! \todo Remove the node from the tree. */
+ knot_zone_tree_node_t *removed;
+ (void)knot_zone_tree_remove(zone->nodes, knot_node_owner(node),
+ &removed);
+ assert(removed->node == node);
return KNOT_EHASH;
}
#endif
@@ -2119,7 +2164,7 @@ const knot_node_t *knot_zone_contents_find_previous_nsec3(
static void knot_zone_contents_left_chop(char *name, size_t *size)
{
- short label_size = name[0];
+ short label_size = (unsigned char)name[0];
memmove(name, name + label_size + 1, *size -label_size - 1);
*size = *size - label_size - 1;
diff --git a/src/libknot/zone/zone-diff.c b/src/libknot/zone/zone-diff.c
index 734c7c2..a4efff9 100644
--- a/src/libknot/zone/zone-diff.c
+++ b/src/libknot/zone/zone-diff.c
@@ -163,11 +163,11 @@ static int knot_zone_diff_changeset_add_rrset(knot_changeset_t *changeset,
}
if (rrset_copy->rrsigs != NULL) {
knot_rrset_deep_free(&rrset_copy->rrsigs, 1, 1, 1);
- }
+ }
assert(knot_rrset_rrsigs(rrset_copy) == NULL);
ret = knot_changeset_add_new_rr(changeset, rrset_copy,
- XFRIN_CHANGESET_ADD);
+ KNOT_CHANGESET_ADD);
if (ret != KNOT_EOK) {
/* We have to free the copy now! */
knot_rrset_deep_free(&rrset_copy, 1, 1, 1);
@@ -193,6 +193,7 @@ static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset,
}
if (knot_rrset_rdata_rr_count(rrset) == 0) {
+ /* RDATA are the same, however*/
dbg_zonediff_detail("zone_diff: Nothing to remove.\n");
return KNOT_EOK;
}
@@ -209,11 +210,12 @@ static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset,
}
if (rrset_copy->rrsigs != NULL) {
knot_rrset_deep_free(&rrset_copy->rrsigs, 1, 1, 1);
- }
+ }
+
assert(knot_rrset_rrsigs(rrset_copy) == NULL);
ret = knot_changeset_add_new_rr(changeset, rrset_copy,
- XFRIN_CHANGESET_REMOVE);
+ KNOT_CHANGESET_REMOVE);
if (ret != KNOT_EOK) {
/* We have to free the copy now. */
knot_rrset_deep_free(&rrset_copy, 1, 1, 1);
@@ -251,6 +253,19 @@ static int knot_zone_diff_add_node(const knot_node_t *node,
free(rrsets);
return ret;
}
+
+ if (knot_rrset_rrsigs(rrsets[i])) {
+ /* Add RRSIGs of the new node. */
+ ret = knot_zone_diff_changeset_add_rrset(changeset,
+ knot_rrset_rrsigs(rrsets[i]));
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: add_node: Cannot "
+ "add RRSIG (%s).\n",
+ knot_strerror(ret));
+ free(rrsets);
+ return ret;
+ }
+ }
}
free(rrsets);
@@ -292,6 +307,18 @@ dbg_zonediff_exec_detail(
free(rrsets);
return ret;
}
+ if (knot_rrset_rrsigs(rrsets[i])) {
+ /* Remove RRSIGs of the old node. */
+ ret = knot_zone_diff_changeset_remove_rrset(changeset,
+ knot_rrset_rrsigs(rrsets[i]));
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: remove_node: Cannot "
+ "remove RRSIG (%s).\n",
+ knot_strerror(ret));
+ free(rrsets);
+ return ret;
+ }
+ }
}
free(rrsets);
@@ -356,8 +383,8 @@ static int knot_zone_diff_rdata_return_changes(const knot_rrset_t *rrset1,
* because the list was traversed - there's no match.
*/
dbg_zonediff("zone_diff: diff_rdata: "
- "No match for RR (type=%s owner=%s).\n",
- knot_rrtype_to_string(knot_rrset_type(rrset1)),
+ "No match for RR (type=%u owner=%s).\n",
+ knot_rrset_type(rrset1),
knot_dname_to_str(rrset1->owner));
/* Make a copy of tmp_rdata. */
knot_rdata_t *tmp_rdata_copy =
@@ -457,7 +484,7 @@ static int knot_zone_diff_rdata(const knot_rrset_t *rrset1,
}
int ret = knot_zone_diff_changeset_remove_rrset(changeset,
- to_remove);
+ to_remove);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(&to_remove, 1, 1, 1);
dbg_zonediff("zone_diff: diff_rdata: Could not remove RRs. "
@@ -587,12 +614,13 @@ static int knot_zone_diff_rrsets(const knot_rrset_t *rrset1,
int ret = knot_zone_diff_rdata(knot_rrset_rrsigs(rrset1),
knot_rrset_rrsigs(rrset2), changeset);
if (ret != KNOT_EOK) {
- dbg_zonediff("zone_diff: diff_rrsets (%s:%s): Failed to diff RRSIGs. "
- "They were: %p %p. (%s).\n",
- knot_dname_to_str(rrset1->owner),
- knot_rrtype_to_string(rrset1->type),
- rrset1->rrsigs,
- rrset2->rrsigs, knot_strerror(ret));
+ dbg_zonediff("zone_diff: diff_rrsets (%s:%s): "
+ "Failed to diff RRSIGs. "
+ "They were: %p %p. (%s).\n",
+ knot_dname_to_str(rrset1->owner),
+ knot_rrtype_to_string(rrset1->type),
+ rrset1->rrsigs,
+ rrset2->rrsigs, knot_strerror(ret));
}
/* RRs (=rdata) have to be cross-compared, unfortunalely. */
@@ -706,6 +734,20 @@ static void knot_zone_diff_node(knot_node_t *node, void *data)
free(rrsets);
return;
}
+
+ /* Remove RRSet's RRSIGs as well. */
+ if (knot_rrset_rrsigs(rrset)) {
+ ret = knot_zone_diff_changeset_remove_rrset(
+ param->changeset,
+ knot_rrset_rrsigs(rrset));
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_node+: "
+ "Failed to remove RRSIGs.\n");
+ param->ret = ret;
+ free(rrsets);
+ return;
+ }
+ }
} else {
dbg_zonediff("zone_diff: diff_node: There is a counterpart "
"for RRSet of type %s in second tree.\n",
@@ -790,6 +832,18 @@ static void knot_zone_diff_node(knot_node_t *node, void *data)
free(rrsets);
return;
}
+ if (knot_rrset_rrsigs(rrset)) {
+ int ret = knot_zone_diff_changeset_add_rrset(
+ param->changeset,
+ knot_rrset_rrsigs(rrset));
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_node: "
+ "Failed to add RRSIGs.\n");
+ param->ret = ret;
+ free(rrsets);
+ return;
+ }
+ }
} else {
/* Already handled. */
;
@@ -989,7 +1043,10 @@ int knot_zone_diff_create_changesets(const knot_zone_contents_t *z1,
return KNOT_EINVAL;
}
/* Create changesets. */
- int ret = knot_changeset_allocate(changesets);
+ /* Setting type to IXFR - that's the default, DDNS triggers special
+ * processing when applied. See #2110 and #2111.
+ */
+ int ret = knot_changeset_allocate(changesets, KNOT_CHANGESET_TYPE_IXFR);
if (ret != KNOT_EOK) {
dbg_zonediff("zone_diff: create_changesets: "
"Could not allocate changesets."
@@ -1014,14 +1071,13 @@ int knot_zone_diff_create_changesets(const knot_zone_contents_t *z1,
dbg_zonediff_exec_detail(
knot_zone_diff_dump_changeset((*changesets)->sets);
);
-
return KNOT_EOK;
}
-/* Mostly just for testing. We only shall diff zones in memory later. */
+///* Mostly just for testing. We only shall diff zones in memory later. */
//int knot_zone_diff_zones(const char *zonefile1, const char *zonefile2)
//{
- /* Compile test zones. */
+// /* Compile test zones. */
// int ret = zone_read("example.com.", "/home/jan/test/testzone1", "tmpzone1.db", 0);
// assert(ret == KNOT_EOK);
// ret = zone_read("example.com.", "/home/jan/test/testzone2", "tmpzone2.db", 0);
@@ -1040,29 +1096,29 @@ dbg_zonediff_exec_detail(
// assert(knot_zone_contents_diff(z1->contents, z2->contents,
// changeset) == KNOT_EOK);
// dbg_zonediff("Changeset created: From=%d to=%d.\n", changeset.serial_from,
-// changeset.serial_to);
-//// knot_changesets_t chngsets;
-//// chngsets->sets = malloc(sizeof(knot_changeset_t));
-//// chngsets->sets[0] = changeset;
-//// chngsets->count = 1;
-//// chngsets->allocated = 1;
-//// knot_zone_contents_t *new_zone = NULL;
-//// ret = xfrin_apply_changesets(z1, chngsets, &new_zone);
-//// if (ret != KNOT_EOK) {
-//// dbg_zonediff("Application of changesets failed. (%s)\n",
-//// knot_strerror(ret));
-//// }
+// changeset.serial_to);
+// // knot_changesets_t chngsets;
+// // chngsets->sets = malloc(sizeof(knot_changeset_t));
+// // chngsets->sets[0] = changeset;
+// // chngsets->count = 1;
+// // chngsets->allocated = 1;
+// // knot_zone_contents_t *new_zone = NULL;
+// // ret = xfrin_apply_changesets(z1, chngsets, &new_zone);
+// // if (ret != KNOT_EOK) {
+// // dbg_zonediff("Application of changesets failed. (%s)\n",
+// // knot_strerror(ret));
+// // }
-//// assert(new_zone);
+// // assert(new_zone);
// /* Dump creted zone. */
-//// FILE *f = fopen("testovani", "w");
-//// zone_dump_text(new_zone, f);
+// // FILE *f = fopen("testovani", "w");
+// // zone_dump_text(new_zone, f);
// knot_zone_deep_free(&z2, 0);
// knot_zone_deep_free(&z1, 0);
-//// knot_zone_contents_deep_free(&new_zone, 1);
-//// knot_zone_free(&z1);
+// // knot_zone_contents_deep_free(&new_zone, 1);
+// // knot_zone_free(&z1);
// knot_free_changeset(&changeset);
// exit(0);