Skip to content
Snippets Groups Projects
Commit d951b29b authored by Jan Kadlec's avatar Jan Kadlec
Browse files

new_node: DDNS doxygen, cleanup, functions reorganized.

parent 172a6419
No related branches found
No related tags found
No related merge requests found
......@@ -31,99 +31,9 @@
#include "common/descriptor.h"
#include "common/lists.h"
static bool list_contains_rr(const list_t *l, const knot_rrset_t *rr)
{
knot_rr_ln_t *n;
WALK_LIST(n, *l) {
const knot_rrset_t *list_rr = n->rr;
if (knot_rrset_equal(rr, list_rr, KNOT_RRSET_COMPARE_WHOLE)) {
return true;
}
};
return false;
}
static bool removed_rr(const knot_changeset_t *changeset, const knot_rrset_t *rr)
{
return list_contains_rr(&changeset->remove, rr);
}
static void remove_rr_from_list(list_t *l, const knot_rrset_t *rr)
{
knot_rr_ln_t *rr_node = NULL;
node_t *nxt = NULL;
WALK_LIST_DELSAFE(rr_node, nxt, *l) {
knot_rrset_t *rrset = rr_node->rr;
if (knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_WHOLE)) {
knot_rrset_free(&rrset, NULL);
rem_node((node_t *)rr_node);
return;
}
}
}
static void remove_header_from_list(list_t *l, const knot_rrset_t *rr)
{
knot_rr_ln_t *rr_node = NULL;
node_t *nxt = NULL;
WALK_LIST_DELSAFE(rr_node, nxt, *l) {
knot_rrset_t *rrset = rr_node->rr;
if (knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_HEADER)) {
knot_rrset_free(&rrset, NULL);
rem_node((node_t *)rr_node);
}
}
}
static void remove_owner_from_list(list_t *l, const knot_dname_t *owner)
{
knot_rr_ln_t *rr_node = NULL;
node_t *nxt = NULL;
WALK_LIST_DELSAFE(rr_node, nxt, *l) {
knot_rrset_t *rrset = rr_node->rr;
if (knot_dname_is_equal(rrset->owner, owner)) {
knot_rrset_free(&rrset, NULL);
rem_node((node_t *)rr_node);
}
}
}
static bool node_empty(const knot_node_t *node, const knot_changeset_t *changeset)
{
if (node == NULL) {
return true;
}
for (uint16_t i = 0; i < node->rrset_count; ++i) {
knot_rrset_t node_rrset = NODE_RR_INIT_N(node, i);
knot_rrset_t node_rr;
knot_rrset_init(&node_rr, node->owner, node_rrset.type, KNOT_CLASS_IN);
for (uint16_t j = 0; j < node_rrset.rrs.rr_count; ++j) {
knot_rrset_add_rr_from_rrset(&node_rr, &node_rrset, j, NULL);
if (!removed_rr(changeset, &node_rr)) {
knot_rrs_clear(&node_rr.rrs, NULL);
return false;
}
knot_rrs_clear(&node_rr.rrs, NULL);
}
}
return true;
}
static bool rrset_empty(const knot_rrset_t *rrset)
{
uint16_t rr_count = knot_rrset_rr_count(rrset);
if (rr_count == 0) {
return true;
}
if (rr_count == 1) {
return knot_rrset_rr_size(rrset, 0) == 0;
}
return false;
}
/* ----------------------------- prereq check ------------------------------- */
/*!< \brief Clears prereq RRSet list. */
static void rrset_list_clear(list_t *l)
{
node_t *n, *nxt;
......@@ -135,6 +45,7 @@ static void rrset_list_clear(list_t *l)
};
}
/*!< \brief Adds RR to prereq RRSet list, merges RRs into RRSets. */
static int add_rr_to_list(list_t *l, const knot_rrset_t *rr)
{
node_t *n;
......@@ -157,6 +68,7 @@ static int add_rr_to_list(list_t *l, const knot_rrset_t *rr)
return ptrlist_add(l, rr_copy, NULL) != NULL ? KNOT_EOK : KNOT_ENOMEM;
}
/*!< \brief Checks whether RR type exists in the zone. */
static int knot_ddns_check_exist(const knot_zone_contents_t *zone,
const knot_rrset_t *rrset, uint16_t *rcode)
{
......@@ -185,6 +97,7 @@ static int knot_ddns_check_exist(const knot_zone_contents_t *zone,
return KNOT_EOK;
}
/*!< \brief Checks whether RRSet exists in the zone. */
static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone,
const knot_rrset_t *rrset,
uint16_t *rcode)
......@@ -210,10 +123,7 @@ static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone,
return KNOT_EPREREQ;
} else {
knot_rrset_t found = NODE_RR_INIT(node, rrset->type);
// do not have to compare the header, it is already done
assert((&found)->type == rrset->type);
assert(knot_dname_cmp((&found)->owner,
rrset->owner) == 0);
assert(!knot_rrset_empty(&found));
if (!knot_rrset_equal(&found, rrset, KNOT_RRSET_COMPARE_WHOLE)) {
*rcode = KNOT_RCODE_NXRRSET;
return KNOT_EPREREQ;
......@@ -223,6 +133,7 @@ static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone,
return KNOT_EOK;
}
/*!< \brief Checks whether RRSets in the list exist in the zone. */
static int check_exists_in_list(list_t *l, const knot_zone_contents_t *zone,
uint16_t *rcode)
{
......@@ -239,6 +150,7 @@ static int check_exists_in_list(list_t *l, const knot_zone_contents_t *zone,
return KNOT_EOK;
}
/*!< \brief Checks whether RR type is not in the zone. */
static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone,
const knot_rrset_t *rrset,
uint16_t *rcode)
......@@ -264,12 +176,11 @@ static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone,
return KNOT_EOK;
}
/* RDATA is always empty for simple RRset checks. */
*rcode = KNOT_RCODE_YXRRSET;
return KNOT_EPREREQ;
}
/*!< \brief Checks whether DNAME is in the zone. */
static int knot_ddns_check_in_use(const knot_zone_contents_t *zone,
const knot_dname_t *dname,
uint16_t *rcode)
......@@ -298,6 +209,7 @@ static int knot_ddns_check_in_use(const knot_zone_contents_t *zone,
return KNOT_EOK;
}
/*!< \brief Checks whether DNAME is not in the zone. */
static int knot_ddns_check_not_in_use(const knot_zone_contents_t *zone,
const knot_dname_t *dname,
uint16_t *rcode)
......@@ -325,6 +237,20 @@ static int knot_ddns_check_not_in_use(const knot_zone_contents_t *zone,
return KNOT_EPREREQ;
}
/*!< \brief Returns true if rrset has 0 data or RDATA of size 0 (we need TTL). */
static bool rrset_empty(const knot_rrset_t *rrset)
{
uint16_t rr_count = knot_rrset_rr_count(rrset);
if (rr_count == 0) {
return true;
}
if (rr_count == 1) {
return knot_rrset_rr_size(rrset, 0) == 0;
}
return false;
}
/*!< \brief Checks prereq for given packet RR. */
static int knot_ddns_check_prereq(const knot_rrset_t *rrset,
uint16_t qclass,
const knot_zone_contents_t *zone,
......@@ -357,6 +283,7 @@ static int knot_ddns_check_prereq(const knot_rrset_t *rrset,
return knot_ddns_check_not_exist(zone, rrset, rcode);
}
} else if (rrset->rclass == qclass) {
// Store RRs for full check into list
return add_rr_to_list(rrset_list, rrset);
} else {
dbg_ddns("ddns: add_prereq: Bad class.\n");
......@@ -364,111 +291,68 @@ static int knot_ddns_check_prereq(const knot_rrset_t *rrset,
}
}
/* API functions */
/* --------------------------- DDNS processing ------------------------------ */
/* ----------------------- changeset lists helpers -------------------------- */
int knot_ddns_check_zone(const knot_zone_contents_t *zone,
const knot_pkt_t *query, uint16_t *rcode)
/*!< \brief Checks whether RR was already removed. */
static bool removed_rr(const knot_changeset_t *changeset, const knot_rrset_t *rr)
{
if (zone == NULL || query == NULL || rcode == NULL) {
if (rcode != NULL) {
*rcode = KNOT_RCODE_SERVFAIL;
knot_rr_ln_t *n;
WALK_LIST(n, changeset->remove) {
const knot_rrset_t *list_rr = n->rr;
if (knot_rrset_equal(rr, list_rr, KNOT_RRSET_COMPARE_WHOLE)) {
return true;
}
return KNOT_EINVAL;
}
if (knot_pkt_qtype(query) != KNOT_RRTYPE_SOA) {
*rcode = KNOT_RCODE_FORMERR;
return KNOT_EMALF;
}
// check zone CLASS
if (knot_pkt_qclass(query) != KNOT_CLASS_IN) {
*rcode = KNOT_RCODE_NOTAUTH;
return KNOT_ENOZONE;
}
};
return KNOT_EOK;
return false;
}
int knot_ddns_process_prereqs(const knot_pkt_t *query, const knot_zone_contents_t *zone,
uint16_t *rcode)
/*!< \brief Removes RR from list, full equality check. */
static void remove_rr_from_list(list_t *l, const knot_rrset_t *rr)
{
if (query == NULL || rcode == NULL || zone == NULL) {
return KNOT_EINVAL;
}
dbg_ddns("Processing prerequisities.\n");
int ret = KNOT_EOK;
list_t rrset_list; // List used to store merged RRSets
init_list(&rrset_list);
const knot_pktsection_t *answer = knot_pkt_section(query, KNOT_ANSWER);
for (int i = 0; i < answer->count; ++i) {
// Check what can be checked, store full RRs into list
ret = knot_ddns_check_prereq(&answer->rr[i],
knot_pkt_qclass(query),
zone, rcode, &rrset_list);
if (ret != KNOT_EOK) {
rrset_list_clear(&rrset_list);
return ret;
knot_rr_ln_t *rr_node = NULL;
node_t *nxt = NULL;
WALK_LIST_DELSAFE(rr_node, nxt, *l) {
knot_rrset_t *rrset = rr_node->rr;
if (knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_WHOLE)) {
knot_rrset_free(&rrset, NULL);
rem_node((node_t *)rr_node);
return;
}
}
// Check stored RRSets
ret = check_exists_in_list(&rrset_list, zone, rcode);
rrset_list_clear(&rrset_list);
return ret;
}
static int knot_ddns_check_update(const knot_rrset_t *rrset,
const knot_pkt_t *query,
uint16_t *rcode)
/*!< \brief Removes RR from list, owner and type check. */
static void remove_header_from_list(list_t *l, const knot_rrset_t *rr)
{
/* Accept both subdomain and dname match. */
dbg_ddns("Checking UPDATE packet.\n");
const knot_dname_t *owner = rrset->owner;
const knot_dname_t *qname = knot_pkt_qname(query);
int is_sub = knot_dname_is_sub(owner, qname);
if (!is_sub && knot_dname_cmp(owner, qname) != 0) {
*rcode = KNOT_RCODE_NOTZONE;
return KNOT_EOUTOFZONE;
}
if (knot_rrtype_is_ddns_forbidden(rrset->type)) {
*rcode = KNOT_RCODE_REFUSED;
log_zone_error("Refusing to update DNSSEC-related record!\n");
return KNOT_EDENIED;
knot_rr_ln_t *rr_node = NULL;
node_t *nxt = NULL;
WALK_LIST_DELSAFE(rr_node, nxt, *l) {
knot_rrset_t *rrset = rr_node->rr;
if (knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_HEADER)) {
knot_rrset_free(&rrset, NULL);
rem_node((node_t *)rr_node);
}
}
}
if (rrset->rclass == knot_pkt_qclass(query)) {
if (knot_rrtype_is_metatype(rrset->type)) {
*rcode = KNOT_RCODE_FORMERR;
return KNOT_EMALF;
}
} else if (rrset->rclass == KNOT_CLASS_ANY) {
if (!rrset_empty(rrset)
|| (knot_rrtype_is_metatype(rrset->type)
&& rrset->type != KNOT_RRTYPE_ANY)) {
*rcode = KNOT_RCODE_FORMERR;
return KNOT_EMALF;
}
} else if (rrset->rclass == KNOT_CLASS_NONE) {
if (knot_rrset_rr_ttl(rrset, 0) != 0
|| knot_rrtype_is_metatype(rrset->type)) {
*rcode = KNOT_RCODE_FORMERR;
return KNOT_EMALF;
/*!< \brief Removes RR from list, owner check. */
static void remove_owner_from_list(list_t *l, const knot_dname_t *owner)
{
knot_rr_ln_t *rr_node = NULL;
node_t *nxt = NULL;
WALK_LIST_DELSAFE(rr_node, nxt, *l) {
knot_rrset_t *rrset = rr_node->rr;
if (knot_dname_is_equal(rrset->owner, owner)) {
knot_rrset_free(&rrset, NULL);
rem_node((node_t *)rr_node);
}
} else {
*rcode = KNOT_RCODE_FORMERR;
return KNOT_EMALF;
}
return KNOT_EOK;
}
/* DDNS processing */
/* --------------------- true/false helper functions ------------------------ */
static inline bool is_addition(const knot_rrset_t *rr)
{
......@@ -495,6 +379,7 @@ static inline bool is_node_removal(const knot_rrset_t *rr)
return rr->rclass == KNOT_CLASS_ANY && rr->type == KNOT_RRTYPE_ANY;
}
/*!< \brief Returns true if last addition of certain types is to be replaced. */
static bool should_replace(const knot_rrset_t *chg_rrset,
const knot_rrset_t *rrset)
{
......@@ -507,6 +392,86 @@ static bool should_replace(const knot_rrset_t *chg_rrset,
}
}
/*!< \brief Returns true if node will be empty after update application. */
static bool node_empty(const knot_node_t *node, const knot_changeset_t *changeset)
{
if (node == NULL) {
return true;
}
for (uint16_t i = 0; i < node->rrset_count; ++i) {
knot_rrset_t node_rrset = NODE_RR_INIT_N(node, i);
knot_rrset_t node_rr;
knot_rrset_init(&node_rr, node->owner, node_rrset.type, KNOT_CLASS_IN);
for (uint16_t j = 0; j < node_rrset.rrs.rr_count; ++j) {
knot_rrset_add_rr_from_rrset(&node_rr, &node_rrset, j, NULL);
if (!removed_rr(changeset, &node_rr)) {
knot_rrs_clear(&node_rr.rrs, NULL);
return false;
}
knot_rrs_clear(&node_rr.rrs, NULL);
}
}
return true;
}
/*!< \brief Returns true if node contains given RR in its RRSets. */
static bool node_contains_rr(const knot_node_t *node,
const knot_rrset_t *rr)
{
knot_rrset_t zone_rrset = NODE_RR_INIT(node, rr->type);
if (!knot_rrset_empty(&zone_rrset)) {
knot_rrset_t intersection;
int ret = knot_rrset_intersection(&zone_rrset, rr,
&intersection, NULL);
if (ret != KNOT_EOK) {
return false;
}
const bool contains = !knot_rrset_empty(&intersection);
knot_rrs_clear(&intersection.rrs, NULL);
return contains;
} else {
return false;
}
}
/*!< \brief Returns true if CNAME is in this node. */
static bool adding_to_cname(const knot_node_t *node,
knot_changeset_t *changeset)
{
if (node == NULL) {
// Node did not exist before update.
return false;
}
knot_rrset_t cname = NODE_RR_INIT(node, KNOT_RRTYPE_CNAME);
if (knot_rrset_empty(&cname)) {
// Node did not contain CNAME before update.
return false;
}
// Return true if we have not removed CNAME in this update.
return !removed_rr(changeset, &cname);
}
/*!< \brief Used to ignore SOA deletions and SOAs with lower serial than zone. */
static bool skip_soa(const knot_rrset_t *rr, int64_t sn)
{
if (rr->type == KNOT_RRTYPE_SOA
&& (rr->rclass == KNOT_CLASS_NONE
|| rr->rclass == KNOT_CLASS_ANY
|| knot_serial_compare(knot_rrs_soa_serial(&rr->rrs),
sn) <= 0)) {
return true;
}
return false;
}
/* ---------------------- changeset manipulation ---------------------------- */
/*!< \brief Checks whether record should be added or replaced. */
static bool skip_record_addition(knot_changeset_t *changeset,
knot_rrset_t *rr)
{
......@@ -514,10 +479,12 @@ static bool skip_record_addition(knot_changeset_t *changeset,
WALK_LIST(rr_node, changeset->add) {
knot_rrset_t *rrset = rr_node->rr;
if (should_replace(rr, rrset)) {
// Replacing singleton RR.
knot_rrset_free(&rrset, NULL);
rrset = rr;
return true;
} else if (knot_rrset_equal(rr, rrset, KNOT_RRSET_COMPARE_WHOLE)) {
// Freeing duplication.
knot_rrset_free(&rr, NULL);
return true;
}
......@@ -526,6 +493,7 @@ static bool skip_record_addition(knot_changeset_t *changeset,
return false;
}
/*!< \brief Adds RR into add section of changeset if it is deemed worthy. */
static int add_rr_to_chgset(const knot_rrset_t *rr, knot_changeset_t *changeset,
int *apex_ns_rem)
{
......@@ -541,12 +509,14 @@ static int add_rr_to_chgset(const knot_rrset_t *rr, knot_changeset_t *changeset,
}
if (apex_ns_rem) {
// Increase post update apex NS count.
(*apex_ns_rem)--;
}
return knot_changeset_add_rrset(changeset, rr_copy, KNOT_CHANGESET_ADD);
}
/*!< \brief Checks whether record should be removed (duplicate check). */
static bool skip_record_removal(knot_changeset_t *changeset, knot_rrset_t *rr)
{
knot_rr_ln_t *rr_node = NULL;
......@@ -559,21 +529,10 @@ static bool skip_record_removal(knot_changeset_t *changeset, knot_rrset_t *rr)
}
}
node_t *nxt = NULL;
WALK_LIST_DELSAFE(rr_node, nxt, changeset->add) {
knot_rrset_t *rrset = rr_node->rr;
if (knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_WHOLE)) {
// Adding and removing identical RRs, drop both.
knot_rrset_free(&rrset, NULL);
knot_rrset_free(&rr, NULL);
rem_node((node_t *)rr_node);
return true;
}
}
return false;
}
/*!< \brief Adds RR into remove section of changeset if it is deemed worthy. */
static int rem_rr_to_chgset(const knot_rrset_t *rr, knot_changeset_t *changeset,
int *apex_ns_rem)
{
......@@ -589,12 +548,14 @@ static int rem_rr_to_chgset(const knot_rrset_t *rr, knot_changeset_t *changeset,
}
if (apex_ns_rem) {
// Decrease post update apex NS count.
(*apex_ns_rem)++;
}
return knot_changeset_add_rrset(changeset, rr_copy, KNOT_CHANGESET_REMOVE);
}
/*!< \brief Adds all RRs from RRSet into remove section of changeset. */
static int rem_rrset_to_chgset(const knot_rrset_t *rrset,
knot_changeset_t *changeset,
int *apex_ns_rem)
......@@ -616,6 +577,11 @@ static int rem_rrset_to_chgset(const knot_rrset_t *rrset,
return KNOT_EOK;
}
/* ------------------------ RR processing logic ----------------------------- */
/* --------------------------- RR additions --------------------------------- */
/*!< \brief Processes CNAME addition (replace or ignore) */
static int process_add_cname(const knot_node_t *node,
const knot_rrset_t *rr,
knot_changeset_t *changeset)
......@@ -637,12 +603,14 @@ static int process_add_cname(const knot_node_t *node,
// Other occupied node => ignore.
return KNOT_EOK;
} else {
// Can add.
return add_rr_to_chgset(rr, changeset, NULL);
}
return KNOT_EOK;
}
/*!< \brief Processes CNAME addition (ignore when not removed, or non-apex) */
static int process_add_nsec3param(const knot_node_t *node,
const knot_rrset_t *rr,
knot_changeset_t *changeset)
......@@ -669,14 +637,81 @@ static int process_add_nsec3param(const knot_node_t *node,
return KNOT_EOK;
}
/*!
* \brief Processes SOA addition (ignore when non-apex), lower serials
* dropped before.
*/
static int process_add_soa(const knot_node_t *node,
const knot_rrset_t *rr,
knot_changeset_t *changeset)
{
if (node == NULL || !knot_node_rrtype_exists(node, KNOT_RRTYPE_SOA)) {
// Adding SOA to non-apex node, ignore
return KNOT_EOK;
}
/* Get the current SOA RR from the node. */
knot_rrset_t removed = NODE_RR_INIT(node, KNOT_RRTYPE_SOA);
/* If they are identical, ignore. */
if (knot_rrset_equal(&removed, rr, KNOT_RRSET_COMPARE_WHOLE)) {
return KNOT_EOK;
}
return add_rr_to_chgset(rr, changeset, NULL);
}
/*!< \brief Adds normal RR, ignores when CNAME exists in node. */
static int process_add_normal(const knot_node_t *node,
const knot_rrset_t *rr,
knot_changeset_t *changeset,
int *apex_ns_rem)
{
if (adding_to_cname(node, changeset)) {
// Adding RR to CNAME node, ignore.
return KNOT_EOK;
}
if (node && node_contains_rr(node, rr)) {
// Adding existing RR, remove removal from changeset if it's there.
remove_rr_from_list(&changeset->remove, rr);
// And ignore.
return KNOT_EOK;
}
const bool apex_ns = knot_node_rrtype_exists(node, KNOT_RRTYPE_SOA) &&
rr->type == KNOT_RRTYPE_NS;
return add_rr_to_chgset(rr, changeset, apex_ns ? apex_ns_rem : NULL);
}
/*!< \brief Decides what to do with RR addition. */
static int process_add(const knot_rrset_t *rr,
const knot_node_t *node,
knot_changeset_t *changeset,
int *apex_ns_rem)
{
switch(rr->type) {
case KNOT_RRTYPE_CNAME:
return process_add_cname(node, rr, changeset);
case KNOT_RRTYPE_SOA:
return process_add_soa(node, rr, changeset);
case KNOT_RRTYPE_NSEC3PARAM:
return process_add_nsec3param(node, rr, changeset);
default:
return process_add_normal(node, rr, changeset, apex_ns_rem);
}
}
/* --------------------------- RR deletions --------------------------------- */
/*!< \brief Removes single RR from zone. */
static int process_rem_rr(const knot_rrset_t *rr,
const knot_node_t *node,
knot_changeset_t *changeset,
int *apex_ns_rem)
{
// Remove possible previously added RR
remove_rr_from_list(&changeset->add, rr);
if (node == NULL) {
// Removing from node that did not exists before update
remove_rr_from_list(&changeset->add, rr);
return KNOT_EOK;
}
......@@ -693,8 +728,7 @@ static int process_rem_rr(const knot_rrset_t *rr,
knot_rrset_t to_modify = NODE_RR_INIT(node, rr->type);
if (knot_rrset_empty(&to_modify)) {
// No such RRSet, but check duplicates
remove_rr_from_list(&changeset->add, rr);
// No such RRSet
return KNOT_EOK;
}
......@@ -705,8 +739,7 @@ static int process_rem_rr(const knot_rrset_t *rr,
}
if (knot_rrset_empty(&intersection)) {
// No such RR, but check duplicates
remove_rr_from_list(&changeset->add, rr);
// No such RR
return KNOT_EOK;
}
assert(intersection.rrs.rr_count == 1);
......@@ -716,47 +749,50 @@ static int process_rem_rr(const knot_rrset_t *rr,
return ret;
}
/*!< \brief Removes RRSet from zone. */
static int process_rem_rrset(const knot_rrset_t *rrset,
const knot_node_t *node,
knot_changeset_t *changeset)
{
// Removing all added RRs with this owner and type
// Removing all previously added RRs with this owner and type from changeset
remove_header_from_list(&changeset->add, rrset);
if (node == NULL) {
return KNOT_EOK;
}
uint16_t type = rrset->type;
if (type == KNOT_RRTYPE_SOA || knot_rrtype_is_ddns_forbidden(type)) {
if (rrset->type == KNOT_RRTYPE_SOA ||
knot_rrtype_is_ddns_forbidden(rrset->type)) {
// Ignore SOA and DNSSEC removals.
return KNOT_EOK;
}
if (knot_node_rrtype_exists(node, KNOT_RRTYPE_SOA) && type == KNOT_RRTYPE_NS) {
// Ignore whole NS apex removals.
if (knot_node_rrtype_exists(node, KNOT_RRTYPE_SOA) &&
rrset->type == KNOT_RRTYPE_NS) {
// Ignore NS apex RRSet removals.
return KNOT_EOK;
}
// no such RR
if (!knot_node_rrtype_exists(node, type)) {
// ignore
if (!knot_node_rrtype_exists(node, rrset->type)) {
// no such RR, ignore
return KNOT_EOK;
}
knot_rrset_t to_remove = NODE_RR_INIT(node, type);
knot_rrset_t to_remove = NODE_RR_INIT(node, rrset->type);
return rem_rrset_to_chgset(&to_remove, changeset, NULL);
}
/*!< \brief Removes node from zone. */
static int process_rem_node(const knot_rrset_t *rr,
const knot_node_t *node, knot_changeset_t *changeset)
{
// Remove all added records
// Remove all previously added records with given owner from changeset
remove_owner_from_list(&changeset->add, rr->owner);
if (node == NULL) {
return KNOT_EOK;
}
// Remove all RRSets from node
for (int i = 0; i < node->rrset_count; ++i) {
knot_rrset_t rrset = NODE_RR_INIT_N(node, i);
int ret = process_rem_rrset(&rrset, node, changeset);
......@@ -768,99 +804,7 @@ static int process_rem_node(const knot_rrset_t *rr,
return KNOT_EOK;
}
static int process_add_soa(const knot_node_t *node,
const knot_rrset_t *rr,
knot_changeset_t *changeset)
{
if (node == NULL || !knot_node_rrtype_exists(node, KNOT_RRTYPE_SOA)) {
// Adding SOA to non-apex node, ignore
return KNOT_EOK;
}
/* Get the current SOA RR from the node. */
knot_rrset_t removed = NODE_RR_INIT(node, KNOT_RRTYPE_SOA);
/* If they are identical, ignore. */
if (knot_rrset_equal(&removed, rr, KNOT_RRSET_COMPARE_WHOLE)) {
return KNOT_EOK;
}
return add_rr_to_chgset(rr, changeset, NULL);
}
static bool node_contains_rr(const knot_node_t *node,
const knot_rrset_t *rr)
{
knot_rrset_t zone_rrset = NODE_RR_INIT(node, rr->type);
if (!knot_rrset_empty(&zone_rrset)) {
knot_rrset_t intersection;
int ret = knot_rrset_intersection(&zone_rrset, rr,
&intersection, NULL);
if (ret != KNOT_EOK) {
return false;
}
const bool contains = !knot_rrset_empty(&intersection);
knot_rrs_clear(&intersection.rrs, NULL);
return contains;
} else {
return false;
}
}
static bool adding_to_cname(const knot_node_t *node,
knot_changeset_t *changeset)
{
if (node == NULL) {
// Node did not exist before update.
return false;
}
knot_rrset_t cname = NODE_RR_INIT(node, KNOT_RRTYPE_CNAME);
if (knot_rrset_empty(&cname)) {
// Node did not contain CNAME before update.
return false;
}
return !removed_rr(changeset, &cname);
}
static int process_add_normal(const knot_node_t *node,
const knot_rrset_t *rr,
knot_changeset_t *changeset,
int *apex_ns_rem)
{
if (adding_to_cname(node, changeset)) {
// Adding RR to CNAME node, ignore.
return KNOT_EOK;
}
if (node && node_contains_rr(node, rr)) {
// Adding existing RR, remove from changeset if it's there.
remove_rr_from_list(&changeset->remove, rr);
// And ignore.
return KNOT_EOK;
}
const bool apex_ns = knot_node_rrtype_exists(node, KNOT_RRTYPE_SOA) &&
rr->type == KNOT_RRTYPE_NS;
return add_rr_to_chgset(rr, changeset, apex_ns ? apex_ns_rem : NULL);
}
static int process_add(const knot_rrset_t *rr,
const knot_node_t *node,
knot_changeset_t *changeset,
int *apex_ns_rem)
{
switch(rr->type) {
case KNOT_RRTYPE_CNAME:
return process_add_cname(node, rr, changeset);
case KNOT_RRTYPE_SOA:
return process_add_soa(node, rr, changeset);
case KNOT_RRTYPE_NSEC3PARAM:
return process_add_nsec3param(node, rr, changeset);
default:
return process_add_normal(node, rr, changeset, apex_ns_rem);
}
}
/*!< \brief Decides what to with removal. */
static int process_remove(const knot_rrset_t *rr,
const knot_node_t *node,
knot_changeset_t *changeset,
......@@ -877,14 +821,9 @@ static int process_remove(const knot_rrset_t *rr,
}
}
static int knot_ddns_final_soa_to_chgset(knot_rrset_t *soa,
knot_changeset_t *changeset)
{
knot_changeset_add_soa(changeset, soa, KNOT_CHANGESET_ADD);
return KNOT_EOK;
}
/* --------------------------- validity checks ------------------------------ */
/*!< \brief Checks whether addition has not violated DNAME rules. */
static bool sem_check(const knot_rrset_t *rr,
const knot_node_t *zone_node,
const knot_zone_contents_t *zone)
......@@ -915,8 +854,56 @@ static bool sem_check(const knot_rrset_t *rr,
return true;
}
/*!< \brief Checks whether we can accept this RR. */
static int knot_ddns_check_update(const knot_rrset_t *rrset,
const knot_pkt_t *query,
uint16_t *rcode)
{
/* Accept both subdomain and dname match. */
dbg_ddns("Checking UPDATE packet.\n");
const knot_dname_t *owner = rrset->owner;
const knot_dname_t *qname = knot_pkt_qname(query);
int is_sub = knot_dname_is_sub(owner, qname);
if (!is_sub && knot_dname_cmp(owner, qname) != 0) {
*rcode = KNOT_RCODE_NOTZONE;
return KNOT_EOUTOFZONE;
}
if (knot_rrtype_is_ddns_forbidden(rrset->type)) {
*rcode = KNOT_RCODE_REFUSED;
log_zone_error("Refusing to update DNSSEC-related record!\n");
return KNOT_EDENIED;
}
if (rrset->rclass == knot_pkt_qclass(query)) {
if (knot_rrtype_is_metatype(rrset->type)) {
*rcode = KNOT_RCODE_FORMERR;
return KNOT_EMALF;
}
} else if (rrset->rclass == KNOT_CLASS_ANY) {
if (!rrset_empty(rrset)
|| (knot_rrtype_is_metatype(rrset->type)
&& rrset->type != KNOT_RRTYPE_ANY)) {
*rcode = KNOT_RCODE_FORMERR;
return KNOT_EMALF;
}
} else if (rrset->rclass == KNOT_CLASS_NONE) {
if (knot_rrset_rr_ttl(rrset, 0) != 0
|| knot_rrtype_is_metatype(rrset->type)) {
*rcode = KNOT_RCODE_FORMERR;
return KNOT_EMALF;
}
} else {
*rcode = KNOT_RCODE_FORMERR;
return KNOT_EMALF;
}
return KNOT_EOK;
}
/*!< \brief Checks RR and decides what to do with it. */
static int knot_ddns_process_rr(const knot_rrset_t *rr,
knot_zone_contents_t *zone,
const knot_zone_contents_t *zone,
knot_changeset_t *changeset,
int *apex_ns_rem)
{
......@@ -936,24 +923,7 @@ static int knot_ddns_process_rr(const knot_rrset_t *rr,
}
}
/*
* 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.
*/
static bool skip_soa(const knot_rrset_t *rr, int64_t sn)
{
if (rr->type == KNOT_RRTYPE_SOA
&& (rr->rclass == KNOT_CLASS_NONE
|| rr->rclass == KNOT_CLASS_ANY
|| knot_serial_compare(knot_rrs_soa_serial(&rr->rrs),
sn) <= 0)) {
return true;
}
return false;
}
/*!< \brief Maps Knot return code to RCODE. */
static uint16_t ret_to_rcode(int ret)
{
if (ret == KNOT_EMALF) {
......@@ -965,7 +935,40 @@ static uint16_t ret_to_rcode(int ret)
}
}
int knot_ddns_process_update(knot_zone_contents_t *zone,
/* ---------------------------------- API ----------------------------------- */
int knot_ddns_process_prereqs(const knot_pkt_t *query, const knot_zone_contents_t *zone,
uint16_t *rcode)
{
if (query == NULL || rcode == NULL || zone == NULL) {
return KNOT_EINVAL;
}
dbg_ddns("Processing prerequisities.\n");
int ret = KNOT_EOK;
list_t rrset_list; // List used to store merged RRSets
init_list(&rrset_list);
const knot_pktsection_t *answer = knot_pkt_section(query, KNOT_ANSWER);
for (int i = 0; i < answer->count; ++i) {
// Check what can be checked, store full RRs into list
ret = knot_ddns_check_prereq(&answer->rr[i],
knot_pkt_qclass(query),
zone, rcode, &rrset_list);
if (ret != KNOT_EOK) {
rrset_list_clear(&rrset_list);
return ret;
}
}
// Check stored RRSets
ret = check_exists_in_list(&rrset_list, zone, rcode);
rrset_list_clear(&rrset_list);
return ret;
}
int knot_ddns_process_update(const knot_zone_contents_t *zone,
const knot_pkt_t *query,
knot_changeset_t *changeset,
uint16_t *rcode, uint32_t new_serial)
......@@ -977,25 +980,14 @@ int knot_ddns_process_update(knot_zone_contents_t *zone,
/* Copy base SOA RR. */
knot_rrset_t *soa_begin = knot_node_create_rrset(zone->apex,
KNOT_RRTYPE_SOA);
knot_rrset_t *soa_end = NULL;
knot_changeset_add_soa(changeset, soa_begin, KNOT_CHANGESET_REMOVE);
/* Current SERIAL */
int64_t sn = knot_rrs_soa_serial(&soa_begin->rrs);
int64_t sn_new;
/* Set the new serial according to policy. */
if (sn > -1) {
sn_new = new_serial;
assert(sn_new != KNOT_EINVAL);
} else {
*rcode = KNOT_RCODE_SERVFAIL;
return KNOT_EINVAL;
}
int64_t sn_old = knot_zone_serial(zone);
/* Process all RRs the Authority (Update) section. */
dbg_ddns("Processing UPDATE section.\n");
knot_rrset_t *soa_end = NULL;
int apex_ns_rem = 0;
const knot_pktsection_t *authority = knot_pkt_section(query, KNOT_AUTHORITY);
for (uint16_t i = 0; i < authority->count; ++i) {
......@@ -1007,7 +999,7 @@ int knot_ddns_process_update(knot_zone_contents_t *zone,
return ret;
}
if (skip_soa(rr, sn)) {
if (skip_soa(rr, sn_old)) {
continue;
}
......@@ -1023,8 +1015,7 @@ int knot_ddns_process_update(knot_zone_contents_t *zone,
knot_rrset_free(&soa_end, NULL);
}
int64_t sn_rr = knot_rrs_soa_serial(&rr->rrs);
assert(knot_serial_compare(sn_rr, sn) > 0);
sn_new = sn_rr;
assert(knot_serial_compare(sn_rr, sn_old) > 0);
soa_end = knot_rrset_cpy(rr, NULL);
if (soa_end == NULL) {
return KNOT_ENOMEM;
......@@ -1044,8 +1035,9 @@ int knot_ddns_process_update(knot_zone_contents_t *zone,
*rcode = KNOT_RCODE_SERVFAIL;
return KNOT_ENOMEM;
}
knot_rrs_soa_serial_set(&soa_end->rrs, sn_new);
knot_rrs_soa_serial_set(&soa_end->rrs, new_serial);
}
return knot_ddns_final_soa_to_chgset(soa_end, changeset);
knot_changeset_add_soa(changeset, soa_end, KNOT_CHANGESET_ADD);
return KNOT_EOK;
}
......@@ -2,6 +2,7 @@
* \file ddns.h
*
* \author Lubos Slovak <lubos.slovak@nic.cz>
* \author Jan Kadlec <jan.kadlec@nic.cz>
*
* \brief Dynamic updates processing.
*
......@@ -30,17 +31,34 @@
#include "knot/updates/changesets.h"
#include "knot/zone/zone.h"
#include "libknot/packet/pkt.h"
#include "libknot/rrset.h"
#include "libknot/dname.h"
#include "libknot/consts.h"
#include "common/lists.h"
int knot_ddns_check_zone(const knot_zone_contents_t *zone,
const knot_pkt_t *query, uint16_t *rcode);
int knot_ddns_process_prereqs(const knot_pkt_t *query, const knot_zone_contents_t *zone, uint16_t *rcode);
/*!
* \brief Checks update prerequisite section.
*
* \param query DNS message containing the update.
* \param zone Zone to be checked.
* \param rcode Returned DNS RCODE.
*
* \return KNOT_E*
*/
int knot_ddns_process_prereqs(const knot_pkt_t *query,
const knot_zone_contents_t *zone,
uint16_t *rcode);
int knot_ddns_process_update(knot_zone_contents_t *zone,
/*!
* \brief Processes DNS update and creates a changeset out of it. Zone is left
* intact.
*
* \param zone Zone to be updated.
* \param query DNS message containing the update.
* \param changeset Output changeset.
* \param rcode Output DNS RCODE.
* \param new_serial New serial to use for updated zone.
*
* \return KNOT_E*
*/
int knot_ddns_process_update(const knot_zone_contents_t *zone,
const knot_pkt_t *query,
knot_changeset_t *changeset,
uint16_t *rcode, uint32_t new_serial);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment