/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <config.h> #include <string.h> #include <stdlib.h> #include <assert.h> #include "updates/changesets.h" #include "libknot/common.h" #include "common/descriptor.h" #include "common/mempattern.h" #include "common/mempool.h" #include "rrset.h" #include "util/debug.h" static int knot_changeset_rrsets_match(const knot_rrset_t *rrset1, const knot_rrset_t *rrset2) { return knot_rrset_equal(rrset1, rrset2, KNOT_RRSET_COMPARE_HEADER) && (knot_rrset_type(rrset1) != KNOT_RRTYPE_RRSIG || knot_rrset_rdata_rrsig_type_covered(rrset1) == knot_rrset_rdata_rrsig_type_covered(rrset2)); } int knot_changesets_init(knot_changesets_t **changesets, uint32_t flags) { // Create new changesets structure *changesets = xmalloc(sizeof(knot_changesets_t)); memset(*changesets, 0, sizeof(knot_changesets_t)); (*changesets)->flags = flags; // Initialize memory context for changesets (xmalloc'd) struct mempool *chs_pool = mp_new(sizeof(knot_changeset_t)); (*changesets)->mmc_chs.ctx = chs_pool; (*changesets)->mmc_chs.alloc = (mm_alloc_t)mp_alloc; (*changesets)->mmc_chs.free = NULL; // Initialize memory context for RRs in changesets (xmalloc'd) struct mempool *rr_pool = mp_new(sizeof(knot_rr_ln_t)); (*changesets)->mmc_rr.ctx = rr_pool; (*changesets)->mmc_rr.alloc = (mm_alloc_t)mp_alloc; (*changesets)->mmc_rr.free = NULL; // Init list with changesets init_list(&(*changesets)->sets); // Init changes structure (*changesets)->changes = xmalloc(sizeof(knot_changes_t)); // Init changes' allocator (storing RRs) (*changesets)->changes->mem_ctx = (*changesets)->mmc_rr; // Init changes' lists init_list(&(*changesets)->changes->old_nodes); init_list(&(*changesets)->changes->old_nsec3); init_list(&(*changesets)->changes->new_rrsets); init_list(&(*changesets)->changes->old_rrsets); return KNOT_EOK; } knot_changesets_t *knot_changesets_create(uint32_t flags) { knot_changesets_t *ch = NULL; int ret = knot_changesets_init(&ch, flags); if (ret != KNOT_EOK) { return NULL; } else { return ch; } } knot_changeset_t *knot_changesets_create_changeset(knot_changesets_t *ch) { if (ch == NULL) { return NULL; } // Create set changesets' memory allocator knot_changeset_t *set = ch->mmc_chs.alloc(ch->mmc_chs.ctx, sizeof(knot_changeset_t)); if (set == NULL) { ERR_ALLOC_FAILED; return NULL; } memset(set, 0, sizeof(knot_changeset_t)); set->flags = ch->flags; // Init set's memory context (Allocator from changests structure is used) set->mem_ctx = ch->mmc_rr; // Init local lists init_list(&set->add); init_list(&set->remove); // Insert into list of sets add_tail(&ch->sets, (node *)set); ++ch->count; return set; } knot_changeset_t *knot_changesets_get_last(const knot_changesets_t *chs) { if (chs == NULL || EMPTY_LIST(chs->sets)) { return NULL; } return (knot_changeset_t *)(TAIL(chs->sets)); } int knot_changeset_add_rrset(knot_changeset_t *chgs, knot_rrset_t *rrset, knot_changeset_part_t part) { // Create wrapper node for list knot_rr_ln_t *rr_node = chgs->mem_ctx.alloc(chgs->mem_ctx.ctx, sizeof(knot_rr_ln_t)); if (rr_node == NULL) { // This will not happen with mp_alloc, but allocator can change ERR_ALLOC_FAILED; return KNOT_ENOMEM; } rr_node->rr = rrset; if (part == KNOT_CHANGESET_ADD) { add_tail(&chgs->add, (node *)rr_node); } else { add_tail(&chgs->remove, (node *)rr_node); } return KNOT_EOK; } int knot_changeset_add_rr(knot_changeset_t *chgs, knot_rrset_t *rr, knot_changeset_part_t part) { // Just check the last RRSet. If the RR belongs to it, merge it, // otherwise just add the RR to the end of the list list *l = part == KNOT_CHANGESET_ADD ? &(chgs->add) : &(chgs->remove); knot_rrset_t *tail_rr = EMPTY_LIST(*l) ? NULL : ((knot_rr_ln_t *)(TAIL(*l)))->rr; if (tail_rr && knot_changeset_rrsets_match(tail_rr, rr)) { // Create changesets exactly as they came, with possibly // duplicate records if (knot_rrset_merge(tail_rr, rr) != KNOT_EOK) { return KNOT_ERROR; } knot_rrset_deep_free(&rr, 1, 0); return KNOT_EOK; } else { return knot_changeset_add_rrset(chgs, rr, part); } } int knot_changes_add_rrset(knot_changes_t *ch, knot_rrset_t *rrset, knot_changes_part_t part) { if (ch == NULL || rrset == NULL) { return KNOT_EINVAL; } knot_rr_ln_t *rr_node = ch->mem_ctx.alloc(ch->mem_ctx.ctx, sizeof(knot_rr_ln_t)); if (rr_node == NULL) { // This will not happen with mp_alloc, but allocator can change ERR_ALLOC_FAILED; return KNOT_ENOMEM; } rr_node->rr = rrset; if (part == KNOT_CHANGES_NEW) { add_tail(&ch->new_rrsets, (node *)rr_node); } else { assert(part == KNOT_CHANGES_OLD); add_tail(&ch->old_rrsets, (node *)rr_node); } return KNOT_EOK; } int knot_changes_add_node(knot_changes_t *ch, knot_node_t *kn_node, knot_changes_part_t part) { if (ch == NULL || kn_node == NULL) { return KNOT_EINVAL; } // Using the same allocator for node and rr's, sizes are equal. knot_node_ln_t *list_node = ch->mem_ctx.alloc(ch->mem_ctx.ctx, sizeof(knot_node_ln_t)); if (list_node == NULL) { // This will not happen with mp_alloc, but allocator can change ERR_ALLOC_FAILED; return KNOT_ENOMEM; } list_node->node = kn_node; if (part == KNOT_CHANGES_NORMAL_NODE) { add_tail(&ch->old_nodes, (node *)list_node); } else { assert(part == KNOT_CHANGES_NSEC3_NODE); add_tail(&ch->old_nsec3, (node *)list_node); } return KNOT_EOK; } static void knot_changeset_store_soa(knot_rrset_t **chg_soa, uint32_t *chg_serial, knot_rrset_t *soa) { *chg_soa = soa; *chg_serial = knot_rrset_rdata_soa_serial(soa); } void knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa, knot_changeset_part_t part) { switch (part) { case KNOT_CHANGESET_ADD: knot_changeset_store_soa(&changeset->soa_to, &changeset->serial_to, soa); break; case KNOT_CHANGESET_REMOVE: knot_changeset_store_soa(&changeset->soa_from, &changeset->serial_from, soa); break; default: assert(0); } } bool knot_changeset_is_empty(const knot_changeset_t *changeset) { if (changeset == NULL) { return true; } return EMPTY_LIST(changeset->add) && EMPTY_LIST(changeset->remove); } int knot_changeset_apply(knot_changeset_t *changeset, knot_changeset_part_t part, int (*func)(knot_rrset_t *, void *), void *data) { if (changeset == NULL || func == NULL) { return KNOT_EINVAL; } knot_rr_ln_t *rr_node; if (part == KNOT_CHANGESET_ADD) { WALK_LIST(rr_node, changeset->add) { int res = func(rr_node->rr, data); if (res != KNOT_EOK) { return res; } } } else if (part == KNOT_CHANGESET_REMOVE) { WALK_LIST(rr_node, changeset->remove) { int res = func(rr_node->rr, data); if (res != KNOT_EOK) { return res; } } } return KNOT_EOK; } int knot_changeset_merge(knot_changeset_t *ch1, knot_changeset_t *ch2) { if (ch1 == NULL || ch2 == NULL || ch1->data != NULL || ch2->data != NULL) { return KNOT_EINVAL; } // Connect lists in changesets together add_tail_list(&ch1->add, &ch2->add); add_tail_list(&ch1->remove, &ch2->remove); return KNOT_EOK; } static void knot_free_changeset(knot_changeset_t *changeset) { if (changeset == NULL) { return; } // Delete RRSets in lists, in case there are any left knot_rr_ln_t *rr_node; WALK_LIST(rr_node, changeset->add) { knot_rrset_deep_free(&rr_node->rr, 1, 1); } WALK_LIST(rr_node, changeset->remove) { knot_rrset_deep_free(&rr_node->rr, 1, 1); } knot_rrset_deep_free(&changeset->soa_from, 1, 1); knot_rrset_deep_free(&changeset->soa_to, 1, 1); // Delete binary data free(changeset->data); } void knot_changesets_free(knot_changesets_t **changesets) { if (changesets == NULL || *changesets == NULL) { return; } if (!EMPTY_LIST((*changesets)->sets)) { knot_changeset_t *chg = NULL; WALK_LIST(chg, (*changesets)->sets) { knot_free_changeset(chg); } } // Free pool with sets themselves mp_delete((*changesets)->mmc_chs.ctx); // Free pool with RRs in sets / changes mp_delete((*changesets)->mmc_rr.ctx); knot_rrset_deep_free(&(*changesets)->first_soa, 1, 1); free((*changesets)->changes); free(*changesets); *changesets = NULL; }