diff --git a/.gitignore b/.gitignore index 0ff94a4eed18d00308f16413d4c621c830259006..75fb4a5164c45d3cd814da69c3eb0ebf3a27efd3 100644 --- a/.gitignore +++ b/.gitignore @@ -58,9 +58,9 @@ /src/knot/conf/libknotd_la-cf-lex.c /src/knot/conf/libknotd_la-cf-parse.c /src/knot/conf/libknotd_la-cf-parse.h -/src/zscanner/scanner.c # zscanner +/src/zscanner/scanner.c /src/zscanner/tests/tmp/ /src/zscanner/tests/unittests /src/zscanner/tests/zscanner-tool @@ -74,7 +74,7 @@ /src/knsec3hash *.gcda *.gcno -/*-coverage.info +/*.info /*coverage/ # alternative allocators diff --git a/Knot.files b/Knot.files index 66d882fb000232b9068d4f214098be48a010093e..497a6727ef64d86486cff6d2262c150ac97e36d8 100644 --- a/Knot.files +++ b/Knot.files @@ -157,8 +157,8 @@ src/knot/updates/changesets.c src/knot/updates/changesets.h src/knot/updates/ddns.c src/knot/updates/ddns.h -src/knot/updates/xfr-in.c -src/knot/updates/xfr-in.h +src/knot/updates/apply.c +src/knot/updates/apply.h src/knot/worker/pool.c src/knot/worker/pool.h src/knot/worker/queue.c diff --git a/Makefile.am b/Makefile.am index 4885799b9aee1c38a7963c5fd68e5ab5bbcd4d0a..33f9e2beeaff20a1e4be2198b6408afd2fade645 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,16 +1,50 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = libtap src tests samples doc man patches -AM_DISTCHECK_CONFIGURE_FLAGS = \ +AM_DISTCHECK_CONFIGURE_FLAGS = \ --disable-code-coverage code_coverage_quiet = --quiet check-code-coverage: if CODE_COVERAGE_ENABLED + $(MAKE) $(AM_MAKEFLAGS) code-coverage-initial -$(MAKE) $(AM_MAKEFLAGS) -k check $(MAKE) $(AM_MAKEFLAGS) code-coverage-capture $(MAKE) $(AM_MAKEFLAGS) code-coverage-html + $(MAKE) $(AM_MAKEFLAGS) code-coverage-summary +else + @echo "You need to run configure with --enable-code-coverage to enable code coverage" +endif + +code-coverage-initial: +if CODE_COVERAGE_ENABLED + $(LCOV) $(code_coverage_quiet) \ + --directory $(top_builddir)/src/common \ + --directory $(top_builddir)/src/knot \ + --directory $(top_builddir)/src/libknot \ + --capture --initial \ + --ignore-errors source \ + --base-directory $(top_builddir)/src \ + --output-file "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-src_base.info" \ + --test-name "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ + --no-checksum --compat-libtool + $(LCOV) $(code_coverage_quiet) \ + --directory $(top_builddir)/src/knot/conf \ + --capture --initial \ + --ignore-errors source \ + --base-directory $(top_builddir)/src/knot/conf \ + --output-file "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-conf_base.info" \ + --test-name "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ + --no-checksum --compat-libtool + $(LCOV) $(code_coverage_quiet) \ + --directory $(top_builddir)/src/zscanner \ + --capture --initial \ + --ignore-errors source \ + --base-directory $(top_builddir)/src/zscanner \ + --output-file "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-zscanner_base.info" \ + --test-name "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ + --no-checksum --compat-libtool else @echo "You need to run configure with --enable-code-coverage to enable code coverage" endif @@ -44,11 +78,13 @@ if CODE_COVERAGE_ENABLED --test-name "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ --no-checksum --compat-libtool $(LCOV) $(code_coverage_quiet) \ + --add-tracefile "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-src_base.info" \ + --add-tracefile "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-conf_base.info" \ + --add-tracefile "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-zscanner_base.info" \ --add-tracefile "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-src_capture.info" \ --add-tracefile "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-conf_capture.info" \ --add-tracefile "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-zscanner_capture.info" \ --output-file "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info" - -@rm -f $(PACKAGE_NAME)-$(PACKAGE_VERSION)-*_base.info $(PACKAGE_NAME)-$(PACKAGE_VERSION)-*_capture.info else @echo "You need to run configure with --enable-code-coverage to enable code coverage" endif @@ -63,14 +99,24 @@ else @echo "You need to run configure with --enable-code-coverage to enable code coverage" endif +code-coverage-summary: +if CODE_COVERAGE_ENABLED + $(LCOV) \ + --summary "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info" +else + @echo "You need to run configure with --enable-code-coverage to enable code coverage" +endif + if CODE_COVERAGE_ENABLED clean-local: code-coverage-clean + -find . -name "*.gcno" -delete code-coverage-clean: -$(LCOV) --directory $(top_builddir) -z -rm -rf $(PACKAGE_NAME)-$(PACKAGE_VERSION)-*_base.info \ - $(PACKAGE_NAME)-$(PACKAGE_VERSION)-*_coverage.info \ + $(PACKAGE_NAME)-$(PACKAGE_VERSION)-*_capture.info \ + $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info \ $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage/ -find . -name "*.gcda" -o -name "*.gcov" -delete endif -.PHONY: check-code-coverage code-coverage-initial code-coverage-capture code-coverage-html code-coverage-clean +.PHONY: check-code-coverage code-coverage-initial code-coverage-capture code-coverage-html code-coverage-summary code-coverage-clean diff --git a/src/Makefile.am b/src/Makefile.am index e61a2e18048f9fb18bd873a57d4288d04afec34a..f0974df31227b2352fce172d2c0c38dad339c0bd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -273,8 +273,8 @@ libknotd_la_SOURCES = \ knot/updates/changesets.h \ knot/updates/ddns.c \ knot/updates/ddns.h \ - knot/updates/xfr-in.c \ - knot/updates/xfr-in.h \ + knot/updates/apply.c \ + knot/updates/apply.h \ knot/worker/pool.c \ knot/worker/pool.h \ knot/worker/queue.c \ diff --git a/src/knot/dnssec/nsec-chain.c b/src/knot/dnssec/nsec-chain.c index 07acd248b889aff9c014d61ab95d76ca930fa8ca..1ca9d58ff923092c78b678ef797e1782ad1ff8d7 100644 --- a/src/knot/dnssec/nsec-chain.c +++ b/src/knot/dnssec/nsec-chain.c @@ -142,8 +142,8 @@ static int connect_nsec_nodes(zone_node_t *a, zone_node_t *b, dbg_dnssec_detail("Adding new NSEC to changeset.\n"); // Add new NSEC to the changeset (no matter if old was removed) - return knot_changeset_add_rrset(data->changeset, new_nsec, - KNOT_CHANGESET_ADD); + return changeset_add_rrset(data->changeset, new_nsec, + CHANGESET_ADD); } /* - API - iterations ------------------------------------------------------- */ @@ -205,7 +205,7 @@ int knot_nsec_chain_iterate_create(knot_zone_tree_t *nodes, * \brief Add entry for removed NSEC to the changeset. */ int knot_nsec_changeset_remove(const zone_node_t *n, - knot_changeset_t *changeset) + changeset_t *changeset) { if (changeset == NULL) { return KNOT_EINVAL; @@ -219,8 +219,8 @@ int knot_nsec_changeset_remove(const zone_node_t *n, } if (nsec) { // update changeset - result = knot_changeset_add_rrset(changeset, nsec, - KNOT_CHANGESET_REMOVE); + result = changeset_add_rrset(changeset, nsec, + CHANGESET_REMOVE); if (result != KNOT_EOK) { knot_rrset_free(&nsec, NULL); return result; @@ -253,8 +253,8 @@ int knot_nsec_changeset_remove(const zone_node_t *n, } // store RRSIG - result = knot_changeset_add_rrset(changeset, synth_rrsigs, - KNOT_CHANGESET_REMOVE); + result = changeset_add_rrset(changeset, synth_rrsigs, + CHANGESET_REMOVE); if (result != KNOT_EOK) { knot_rrset_free(&synth_rrsigs, NULL); return result; @@ -288,7 +288,7 @@ bool knot_nsec_empty_nsec_and_rrsigs_in_node(const zone_node_t *n) * \brief Create new NSEC chain, add differences from current into a changeset. */ int knot_nsec_create_chain(const zone_contents_t *zone, uint32_t ttl, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(zone); assert(zone->nodes); diff --git a/src/knot/dnssec/nsec-chain.h b/src/knot/dnssec/nsec-chain.h index 0a9c17804d5bf66c2846e3d870df3072dd91f2ac..db0000220b7a674ac3a45981eafeda6b903b2703 100644 --- a/src/knot/dnssec/nsec-chain.h +++ b/src/knot/dnssec/nsec-chain.h @@ -39,7 +39,7 @@ */ typedef struct { uint32_t ttl; // TTL for NSEC(3) records - knot_changeset_t *changeset; // Changeset for NSEC(3) changes + changeset_t *changeset; // Changeset for NSEC(3) changes const zone_contents_t *zone; // Updated zone } nsec_chain_iterate_data_t; @@ -96,7 +96,7 @@ int knot_nsec_chain_iterate_create(knot_zone_tree_t *nodes, * \return Error code, KNOT_EOK if successful. */ int knot_nsec_changeset_remove(const zone_node_t *n, - knot_changeset_t *changeset); + changeset_t *changeset); /*! * \brief Checks whether the node is empty or eventually contains only NSEC and @@ -119,6 +119,6 @@ bool knot_nsec_empty_nsec_and_rrsigs_in_node(const zone_node_t *n); * \return Error code, KNOT_EOK if successful. */ int knot_nsec_create_chain(const zone_contents_t *zone, uint32_t ttl, - knot_changeset_t *changeset); + changeset_t *changeset); #endif // _KNOT_DNSSEC_NSEC_CHAIN_FIX_H_ diff --git a/src/knot/dnssec/nsec3-chain.c b/src/knot/dnssec/nsec3-chain.c index 1495e48cf80f4006a1bca2c5e4379f4f9444488c..a64726385efb3837572abe5b7863ef7015214c0e 100644 --- a/src/knot/dnssec/nsec3-chain.c +++ b/src/knot/dnssec/nsec3-chain.c @@ -415,7 +415,7 @@ static int connect_nsec3_nodes(zone_node_t *a, zone_node_t *b, */ static int create_nsec3_nodes(const zone_contents_t *zone, uint32_t ttl, knot_zone_tree_t *nsec3_nodes, - knot_changeset_t *chgset) + changeset_t *chgset) { assert(zone); assert(nsec3_nodes); @@ -583,7 +583,7 @@ static void reset_nodes(const zone_contents_t *zone) * \brief Create new NSEC3 chain, add differences from current into a changeset. */ int knot_nsec3_create_chain(const zone_contents_t *zone, uint32_t ttl, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(zone); assert(changeset); diff --git a/src/knot/dnssec/nsec3-chain.h b/src/knot/dnssec/nsec3-chain.h index 2238a09cf15e6aa829c26d46a3e88144e932461a..defeaf60eac980c84dd98819daf4ac99a8177244 100644 --- a/src/knot/dnssec/nsec3-chain.h +++ b/src/knot/dnssec/nsec3-chain.h @@ -40,6 +40,6 @@ * \return KNOT_E* */ int knot_nsec3_create_chain(const zone_contents_t *zone, uint32_t ttl, - knot_changeset_t *changeset); + changeset_t *changeset); #endif // _KNOT_DNSSEC_NSEC3_CHAIN_FIX_H_ diff --git a/src/knot/dnssec/zone-events.c b/src/knot/dnssec/zone-events.c index c3f02f3832f9ac8115c6c304bb9b2107507a5d55..e4713a96efdd28f10f84448648c2ed58bb466cdb 100644 --- a/src/knot/dnssec/zone-events.c +++ b/src/knot/dnssec/zone-events.c @@ -64,7 +64,7 @@ static int init_dnssec_structs(const zone_contents_t *zone, } static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, - knot_changeset_t *out_ch, bool force, + changeset_t *out_ch, bool force, knot_update_serial_t soa_up, uint32_t *refresh_at) { assert(zone); @@ -79,7 +79,7 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, uint32_t new_serial = zone_contents_next_serial(zone, zone_config->serial_policy); dbg_dnssec_verb("Changeset empty before generating NSEC chain: %d\n", - knot_changeset_is_empty(out_ch)); + changeset_is_empty(out_ch)); // Init needed structs knot_zone_keys_t zone_keys = { '\0' }; @@ -102,7 +102,7 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, return result; } dbg_dnssec_verb("Changeset empty after generating NSEC chain: %d\n", - knot_changeset_is_empty(out_ch)); + changeset_is_empty(out_ch)); // add missing signatures result = knot_zone_sign(zone, &zone_keys, &policy, out_ch, @@ -115,16 +115,16 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, return result; } dbg_dnssec_verb("Changeset emtpy after signing: %d\n", - knot_changeset_is_empty(out_ch)); + changeset_is_empty(out_ch)); // Check if only SOA changed - if (knot_changeset_is_empty(out_ch) && + if (changeset_is_empty(out_ch) && !knot_zone_sign_soa_expired(zone, &zone_keys, &policy)) { log_zone_info("%s No signing performed, zone is valid.\n", msgpref); free(msgpref); knot_free_zone_keys(&zone_keys); - assert(knot_changeset_is_empty(out_ch)); + assert(changeset_is_empty(out_ch)); return KNOT_EOK; } @@ -144,7 +144,7 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, knot_free_zone_keys(&zone_keys); dbg_dnssec_detail("Zone signed: changes=%zu\n", - knot_changeset_size(out_ch)); + changeset_size(out_ch)); log_zone_info("%s Successfully signed.\n", msgpref); free(msgpref); @@ -153,7 +153,7 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, } int knot_dnssec_zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, - knot_changeset_t *out_ch, + changeset_t *out_ch, knot_update_serial_t soa_up, uint32_t *refresh_at) { if (zone == NULL || zone_config == NULL || out_ch == NULL) { @@ -164,7 +164,7 @@ int knot_dnssec_zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, } int knot_dnssec_zone_sign_force(zone_contents_t *zone, const conf_zone_t *zone_config, - knot_changeset_t *out_ch, uint32_t *refresh_at) + changeset_t *out_ch, uint32_t *refresh_at) { if (zone == NULL || zone_config == NULL || out_ch == NULL) { return KNOT_EINVAL; @@ -175,8 +175,8 @@ int knot_dnssec_zone_sign_force(zone_contents_t *zone, const conf_zone_t *zone_c int knot_dnssec_sign_changeset(const zone_contents_t *zone, conf_zone_t *zone_config, - const knot_changeset_t *in_ch, - knot_changeset_t *out_ch, + const changeset_t *in_ch, + changeset_t *out_ch, uint32_t *refresh_at) { if (!refresh_at || zone == NULL || in_ch == NULL || out_ch == NULL) { diff --git a/src/knot/dnssec/zone-events.h b/src/knot/dnssec/zone-events.h index c4ddd2dc0a951c162f0d4d7251cdd3665f3bbc03..e0f7667e6035c78d94d2d67d987363c2ec2c36c4 100644 --- a/src/knot/dnssec/zone-events.h +++ b/src/knot/dnssec/zone-events.h @@ -44,7 +44,7 @@ * \return Error code, KNOT_EOK if successful. */ int knot_dnssec_zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, - knot_changeset_t *out_ch, + changeset_t *out_ch, knot_update_serial_t soa_up, uint32_t *refresh_at); /*! @@ -59,7 +59,7 @@ int knot_dnssec_zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, * \return Error code, KNOT_EOK if successful. */ int knot_dnssec_zone_sign_force(zone_contents_t *zone, const conf_zone_t *zone_config, - knot_changeset_t *out_ch, + changeset_t *out_ch, uint32_t *refresh_at); /*! @@ -76,8 +76,8 @@ int knot_dnssec_zone_sign_force(zone_contents_t *zone, const conf_zone_t *zone_c */ int knot_dnssec_sign_changeset(const zone_contents_t *zone, conf_zone_t *zone_config, - const knot_changeset_t *in_ch, - knot_changeset_t *out_ch, + const changeset_t *in_ch, + changeset_t *out_ch, uint32_t *refresh_at); #endif // _KNOT_DNSSEC_ZONE_EVENTS_H_ diff --git a/src/knot/dnssec/zone-nsec.c b/src/knot/dnssec/zone-nsec.c index aacd7226cd32a63366b031ef573371fbdb5e075d..6f9fa2a9d3b23d4b6374140b96ce69a74280405d 100644 --- a/src/knot/dnssec/zone-nsec.c +++ b/src/knot/dnssec/zone-nsec.c @@ -44,7 +44,7 @@ * \return KNOT_E* */ static int delete_nsec3_chain(const zone_contents_t *zone, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(zone); assert(changeset); @@ -149,14 +149,14 @@ static int mark_nsec3(knot_rrset_t *rrset, void *data) * For each NSEC3 RRSet in the changeset finds its node and marks it with the * 'removed' flag. */ -static int mark_removed_nsec3(knot_changeset_t *out_ch, +static int mark_removed_nsec3(changeset_t *out_ch, const zone_contents_t *zone) { if (knot_zone_tree_is_empty(zone->nsec3_nodes)) { return KNOT_EOK; } - int ret = knot_changeset_apply(out_ch, KNOT_CHANGESET_REMOVE, + int ret = changeset_apply(out_ch, CHANGESET_REMOVE, mark_nsec3, (void *)zone->nsec3_nodes); return ret; } @@ -246,7 +246,7 @@ knot_dname_t *knot_nsec3_hash_to_dname(const uint8_t *hash, size_t hash_size, * \brief Create NSEC or NSEC3 chain in the zone. */ int knot_zone_create_nsec_chain(const zone_contents_t *zone, - knot_changeset_t *changeset, + changeset_t *changeset, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy) { diff --git a/src/knot/dnssec/zone-nsec.h b/src/knot/dnssec/zone-nsec.h index 65bb0546540f394041de775187fbd8d1ddd0364e..8fc569da575b42a85a3386cfc1b1cedc01aef4cf 100644 --- a/src/knot/dnssec/zone-nsec.h +++ b/src/knot/dnssec/zone-nsec.h @@ -81,7 +81,7 @@ knot_dname_t *knot_create_nsec3_owner(const knot_dname_t *owner, * \return Error code, KNOT_EOK if successful. */ int knot_zone_create_nsec_chain(const zone_contents_t *zone, - knot_changeset_t *changeset, + changeset_t *changeset, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy); diff --git a/src/knot/dnssec/zone-sign.c b/src/knot/dnssec/zone-sign.c index d92bd3035d561db5e43e3fca0a7e5f7fc1ead2a4..72963ccc30ce0fceb9e0f642838c09b756be9145 100644 --- a/src/knot/dnssec/zone-sign.c +++ b/src/knot/dnssec/zone-sign.c @@ -212,7 +212,7 @@ static int remove_expired_rrsigs(const knot_rrset_t *covered, const knot_rrset_t *rrsigs, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset, + changeset_t *changeset, uint32_t *expires_at) { assert(changeset); @@ -274,8 +274,8 @@ static int remove_expired_rrsigs(const knot_rrset_t *covered, } if (to_remove != NULL && result == KNOT_EOK) { - result = knot_changeset_add_rrset(changeset, to_remove, - KNOT_CHANGESET_REMOVE); + result = changeset_add_rrset(changeset, to_remove, + CHANGESET_REMOVE); } if (to_remove != NULL && result != KNOT_EOK) { @@ -302,7 +302,7 @@ static int add_missing_rrsigs(const knot_rrset_t *covered, const knot_rrset_t *rrsigs, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(!knot_rrset_empty(covered)); assert(zone_keys); @@ -337,8 +337,8 @@ static int add_missing_rrsigs(const knot_rrset_t *covered, } if (to_add != NULL && result == KNOT_EOK) { - result = knot_changeset_add_rrset(changeset, to_add, - KNOT_CHANGESET_ADD); + result = changeset_add_rrset(changeset, to_add, + CHANGESET_ADD); } if (to_add != NULL && result != KNOT_EOK) { @@ -358,7 +358,7 @@ static int add_missing_rrsigs(const knot_rrset_t *covered, */ static int remove_rrset_rrsigs(const knot_dname_t *owner, uint16_t type, const knot_rrset_t *rrsigs, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(owner); assert(changeset); @@ -376,8 +376,8 @@ static int remove_rrset_rrsigs(const knot_dname_t *owner, uint16_t type, return KNOT_EOK; } - ret = knot_changeset_add_rrset(changeset, synth_rrsig, - KNOT_CHANGESET_REMOVE); + ret = changeset_add_rrset(changeset, synth_rrsig, + CHANGESET_REMOVE); if (ret != KNOT_EOK) { knot_rrset_free(&synth_rrsig, NULL); } @@ -399,7 +399,7 @@ static int force_resign_rrset(const knot_rrset_t *covered, const knot_rrset_t *rrsigs, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(!knot_rrset_empty(covered)); @@ -429,7 +429,7 @@ static int resign_rrset(const knot_rrset_t *covered, const knot_rrset_t *rrsigs, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset, + changeset_t *changeset, uint32_t *expires_at) { assert(!knot_rrset_empty(covered)); @@ -453,7 +453,7 @@ static int resign_rrset(const knot_rrset_t *covered, static int remove_standalone_rrsigs(const zone_node_t *node, const knot_rrset_t *rrsigs, - knot_changeset_t *changeset) + changeset_t *changeset) { if (rrsigs == NULL) { return KNOT_EOK; @@ -476,8 +476,8 @@ static int remove_standalone_rrsigs(const zone_node_t *node, knot_rrset_free(&to_remove, NULL); return ret; } - ret = knot_changeset_add_rrset(changeset, - to_remove, KNOT_CHANGESET_REMOVE); + ret = changeset_add_rrset(changeset, + to_remove, CHANGESET_REMOVE); if (ret != KNOT_EOK) { knot_rrset_free(&to_remove, NULL); return ret; @@ -502,7 +502,7 @@ static int remove_standalone_rrsigs(const zone_node_t *node, static int sign_node_rrsets(const zone_node_t *node, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset, + changeset_t *changeset, uint32_t *expires_at) { assert(node); @@ -548,7 +548,7 @@ static int sign_node_rrsets(const zone_node_t *node, typedef struct node_sign_args { const knot_zone_keys_t *zone_keys; const knot_dnssec_policy_t *policy; - knot_changeset_t *changeset; + changeset_t *changeset; uint32_t expires_at; } node_sign_args_t; @@ -594,7 +594,7 @@ static int sign_node(zone_node_t **node, void *data) static int zone_tree_sign(knot_zone_tree_t *tree, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset, + changeset_t *changeset, uint32_t *expires_at) { assert(zone_keys); @@ -623,7 +623,7 @@ typedef struct { const zone_contents_t *zone; const knot_zone_keys_t *zone_keys; const knot_dnssec_policy_t *policy; - knot_changeset_t *changeset; + changeset_t *changeset; hattrie_t *signed_tree; } changeset_signing_data_t; @@ -738,7 +738,7 @@ static int rrset_add_zone_key(knot_rrset_t *rrset, static int remove_invalid_dnskeys(const knot_rrset_t *soa, const knot_rrset_t *dnskeys, const knot_zone_keys_t *zone_keys, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(soa->type == KNOT_RRTYPE_SOA); assert(changeset); @@ -802,8 +802,8 @@ static int remove_invalid_dnskeys(const knot_rrset_t *soa, done: if (to_remove != NULL && result == KNOT_EOK) { - result = knot_changeset_add_rrset(changeset, to_remove, - KNOT_CHANGESET_REMOVE); + result = changeset_add_rrset(changeset, to_remove, + CHANGESET_REMOVE); } if (to_remove != NULL && result != KNOT_EOK) { @@ -840,7 +840,7 @@ static knot_rrset_t *create_dnskey_rrset_from_soa(const knot_rrset_t *soa) static int add_missing_dnskeys(const knot_rrset_t *soa, const knot_rrset_t *dnskeys, const knot_zone_keys_t *zone_keys, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(soa); assert(soa->type == KNOT_RRTYPE_SOA); @@ -883,8 +883,8 @@ static int add_missing_dnskeys(const knot_rrset_t *soa, } if (to_add != NULL && result == KNOT_EOK) { - result = knot_changeset_add_rrset(changeset, to_add, - KNOT_CHANGESET_ADD); + result = changeset_add_rrset(changeset, to_add, + CHANGESET_ADD); } if (to_add != NULL && result != KNOT_EOK) { @@ -910,7 +910,7 @@ static int update_dnskeys_rrsigs(const knot_rrset_t *dnskeys, const knot_rrset_t *soa, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(zone_keys); assert(changeset); @@ -988,7 +988,7 @@ fail: static int update_dnskeys(const zone_contents_t *zone, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(zone); assert(zone->apex); @@ -1003,7 +1003,7 @@ static int update_dnskeys(const zone_contents_t *zone, } int result; - size_t changes_before = knot_changeset_size(changeset); + size_t changes_before = changeset_size(changeset); result = remove_invalid_dnskeys(&soa, &dnskeys, zone_keys, changeset); if (result != KNOT_EOK) { @@ -1025,7 +1025,7 @@ static int update_dnskeys(const zone_contents_t *zone, } } - bool modified = (knot_changeset_size(changeset) != changes_before); + bool modified = (changeset_size(changeset) != changes_before); bool signatures_exist = (!knot_rrset_empty(&dnskeys) && all_signatures_exist(&dnskeys, &dnskey_rrsig, zone_keys, policy)); @@ -1259,7 +1259,7 @@ static void knot_zone_clear_sorted_changes(hattrie_t *t) int knot_zone_sign(const zone_contents_t *zone, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset, + changeset_t *changeset, uint32_t *refresh_at) { if (!zone || !zone_keys || !policy || !changeset || !refresh_at) { @@ -1327,7 +1327,7 @@ int knot_zone_sign_update_soa(const knot_rrset_t *soa, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, uint32_t new_serial, - knot_changeset_t *changeset) + changeset_t *changeset) { if (knot_rrset_empty(soa) || !zone_keys || !policy || !changeset) { return KNOT_EINVAL; @@ -1404,8 +1404,8 @@ int knot_zone_sign_update_soa(const knot_rrset_t *soa, * \brief Sign changeset created by DDNS or zone-diff. */ int knot_zone_sign_changeset(const zone_contents_t *zone, - const knot_changeset_t *in_ch, - knot_changeset_t *out_ch, + const changeset_t *in_ch, + changeset_t *out_ch, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy) { @@ -1424,14 +1424,14 @@ int knot_zone_sign_changeset(const zone_contents_t *zone, } // Sign all RRs that are new in changeset - int ret = knot_changeset_apply((knot_changeset_t *)in_ch, - KNOT_CHANGESET_ADD, + int ret = changeset_apply((changeset_t *)in_ch, + CHANGESET_ADD, sign_changeset_wrap, &args); // Sign all RRs that are removed in changeset if (ret == KNOT_EOK) { - ret = knot_changeset_apply((knot_changeset_t *)in_ch, - KNOT_CHANGESET_REMOVE, + ret = changeset_apply((changeset_t *)in_ch, + CHANGESET_REMOVE, sign_changeset_wrap, &args); } @@ -1446,7 +1446,7 @@ int knot_zone_sign_changeset(const zone_contents_t *zone, */ int knot_zone_sign_nsecs_in_changeset(const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(zone_keys); assert(policy); @@ -1457,7 +1457,7 @@ int knot_zone_sign_nsecs_in_changeset(const knot_zone_keys_t *zone_keys, .policy = policy, .changeset = changeset }; - return knot_changeset_apply(changeset, KNOT_CHANGESET_ADD, + return changeset_apply(changeset, CHANGESET_ADD, add_rrsigs_for_nsec, &data); } diff --git a/src/knot/dnssec/zone-sign.h b/src/knot/dnssec/zone-sign.h index a986e425e52e44f77b38df338f6ba6c0026b30a9..dd6831749bfa85f4ad7a7630125416f80b0c5175 100644 --- a/src/knot/dnssec/zone-sign.h +++ b/src/knot/dnssec/zone-sign.h @@ -62,7 +62,7 @@ typedef struct signed_info { int knot_zone_sign(const zone_contents_t *zone, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *out_ch, uint32_t *refresh_at); + changeset_t *out_ch, uint32_t *refresh_at); /*! * \brief Update and sign SOA and store performed changes in changeset. @@ -77,7 +77,7 @@ int knot_zone_sign(const zone_contents_t *zone, int knot_zone_sign_update_soa(const knot_rrset_t *soa, const knot_rrset_t *rrsigs, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - uint32_t new_serial, knot_changeset_t *changeset); + uint32_t new_serial, changeset_t *changeset); /*! * \brief Check if zone SOA signatures are expired. @@ -105,8 +105,8 @@ bool knot_zone_sign_soa_expired(const zone_contents_t *zone, * \return Error code, KNOT_EOK if successful. */ int knot_zone_sign_changeset(const zone_contents_t *zone, - const knot_changeset_t *in_ch, - knot_changeset_t *out_ch, + const changeset_t *in_ch, + changeset_t *out_ch, const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy); @@ -121,7 +121,7 @@ int knot_zone_sign_changeset(const zone_contents_t *zone, */ int knot_zone_sign_nsecs_in_changeset(const knot_zone_keys_t *zone_keys, const knot_dnssec_policy_t *policy, - knot_changeset_t *changeset); + changeset_t *changeset); /*! * \brief Checks whether RRSet in a node has to be signed. Will not return diff --git a/src/knot/nameserver/axfr.c b/src/knot/nameserver/axfr.c index 4d4464ac781d20bdf41493ee45776979b73d24ec..a4656ce4fd8a2194d47c7c1006010ec1cc39c3b6 100644 --- a/src/knot/nameserver/axfr.c +++ b/src/knot/nameserver/axfr.c @@ -18,7 +18,7 @@ #include "knot/nameserver/internet.h" #include "knot/nameserver/process_query.h" #include "knot/nameserver/process_answer.h" -#include "knot/updates/xfr-in.h" +#include "knot/updates/apply.h" #include "knot/zone/zonefile.h" #include "common/debug.h" #include "common/descriptor.h" @@ -225,7 +225,7 @@ int axfr_query(knot_pkt_t *pkt, struct query_data *qdata) return NS_PROC_DONE; break; default: /* Generic error. */ - AXFROUT_LOG(LOG_ERR, "%s", knot_strerror(ret)); + AXFROUT_LOG(LOG_ERR, "Failed: %s", knot_strerror(ret)); return NS_PROC_FAIL; } } @@ -235,7 +235,7 @@ static void axfr_answer_cleanup(struct answer_data *data) { struct xfr_proc *proc = data->ext; if (proc) { - zone_contents_deep_free(&proc->zone); + zone_contents_deep_free(&proc->contents); mm_free(data->mm, proc); data->ext = NULL; } @@ -260,7 +260,7 @@ static int axfr_answer_init(struct answer_data *data) } memset(proc, 0, sizeof(struct xfr_proc)); - proc->zone = new_contents; + proc->contents = new_contents; gettimeofday(&proc->tstamp, NULL); /* Set up cleanup callback. */ @@ -284,23 +284,24 @@ static int axfr_answer_finalize(struct answer_data *data) * marked authoritative / delegation point. */ struct xfr_proc *proc = data->ext; - int rc = zone_contents_adjust_full(proc->zone, NULL, NULL); + int rc = zone_contents_adjust_full(proc->contents, NULL, NULL); if (rc != KNOT_EOK) { return rc; } /* Write zone file. */ zone_t *zone = data->param->zone; - rc = zonefile_write(zone->conf->file, proc->zone, data->param->remote); + rc = zonefile_write(zone->conf->file, proc->contents, data->param->remote); if (rc != KNOT_EOK) { return rc; } /* Switch contents. */ - zone_contents_t *old_contents = xfrin_switch_zone(zone, proc->zone); + zone_contents_t *old_contents = zone_switch_contents(zone, proc->contents); + synchronize_rcu(); AXFRIN_LOG(LOG_INFO, "Serial %u -> %u", zone_contents_serial(old_contents), - zone_contents_serial(proc->zone)); + zone_contents_serial(proc->contents)); AXFRIN_LOG(LOG_INFO, "Finished in %.02fs (%u messages, ~%.01fkB).", time_diff(&proc->tstamp, &now) / 1000.0, @@ -308,17 +309,49 @@ static int axfr_answer_finalize(struct answer_data *data) /* Do not free new contents with cleanup. */ zone_contents_deep_free(&old_contents); - proc->zone = NULL; + proc->contents = NULL; return KNOT_EOK; } +static int process_axfr_packet(knot_pkt_t *pkt, struct xfr_proc *proc) +{ + if (pkt == NULL) { + return KNOT_EINVAL; + } + + ++proc->npkts; + + // Init zone creator + zcreator_t zc = {.z = proc->contents, .master = false, .ret = KNOT_EOK }; + + const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); + for (uint16_t i = 0; i < answer->count; ++i) { + const knot_rrset_t *rr = &answer->rr[i]; + if (rr->type == KNOT_RRTYPE_SOA && + node_rrtype_exists(zc.z->apex, KNOT_RRTYPE_SOA)) { + return NS_PROC_DONE; + } else { + int ret = zcreator_step(&zc, rr); + if (ret != KNOT_EOK) { + return NS_PROC_FAIL; + } + } + } + + return NS_PROC_MORE; +} + int axfr_process_answer(knot_pkt_t *pkt, struct answer_data *data) { /* Initialize processing with first packet. */ int ret = KNOT_EOK; if (data->ext == NULL) { NS_NEED_TSIG_SIGNED(&data->param->tsig_ctx, 0); + if (!zone_transfer_needed(data->param->zone, pkt)) { + AXFRIN_LOG(LOG_INFO, "Zone is up-to-date."); + return NS_PROC_DONE; + } ret = axfr_answer_init(data); if (ret != KNOT_EOK) { @@ -329,26 +362,19 @@ int axfr_process_answer(knot_pkt_t *pkt, struct answer_data *data) } /* Process answer packet. */ - ret = xfrin_process_axfr_packet(pkt, (struct xfr_proc *)data->ext); -#warning TODO: this function wraps the old processing interface, hence the retval conversion below - if (ret > 0) { + ret = process_axfr_packet(pkt, (struct xfr_proc *)data->ext); + if (ret == NS_PROC_DONE) { NS_NEED_TSIG_SIGNED(&data->param->tsig_ctx, 0); - /* This was the last packet, finalize zone and publish it. */ ret = axfr_answer_finalize(data); if (ret != KNOT_EOK) { return NS_PROC_FAIL; } - return NS_PROC_DONE; - } - - if (ret != KNOT_EOK) { - fprintf(stderr, "%s:%d\n", __FILE__, __LINE__); - return NS_PROC_FAIL; + return ret; } - return NS_PROC_MORE; + return ret; } #undef AXFR_QRLOG diff --git a/src/knot/nameserver/axfr.h b/src/knot/nameserver/axfr.h index ff4aba5b6ceae4431c7003a6d7791d607b0cef03..1be08c583b89eda64caf8bb6955eb870b3c63b9d 100644 --- a/src/knot/nameserver/axfr.h +++ b/src/knot/nameserver/axfr.h @@ -39,7 +39,7 @@ struct xfr_proc { unsigned npkts; /* Packets processed. */ unsigned nbytes; /* Bytes processed. */ struct timeval tstamp; /* Start time. */ - zone_contents_t *zone; /* Processed zone. */ + zone_contents_t *contents; /* Processed zone. */ }; /*! \brief Generic transfer processing (reused for IXFR). diff --git a/src/knot/nameserver/ixfr.c b/src/knot/nameserver/ixfr.c index c01f603b2d772abc950f3f09e70b3e14cceb42ec..75bdaab7524f317a51e952e291b04aa8d32c3a1a 100644 --- a/src/knot/nameserver/ixfr.c +++ b/src/knot/nameserver/ixfr.c @@ -3,32 +3,39 @@ #include "knot/nameserver/internet.h" #include "knot/nameserver/process_query.h" #include "knot/nameserver/process_answer.h" -#include "knot/updates/xfr-in.h" +#include "knot/updates/apply.h" #include "common/debug.h" #include "common/descriptor.h" #include "libknot/util/utils.h" #include "libknot/rdata/soa.h" -/*! \brief Current IXFR answer sections. */ -enum { - SOA_REMOVE = 0, - REMOVE, - SOA_ADD, - ADD +/* ------------------------ IXFR-out processing ----------------------------- */ + +/*! \brief IXFR-in processing states. */ +enum ixfr_states { + IXFR_START = 0, /* IXFR-in starting, expecting final SOA. */ + IXFR_SOA_DEL, /* Expecting starting SOA. */ + IXFR_SOA_ADD, /* Expecting ending SOA. */ + IXFR_DEL, /* Expecting RR to delete. */ + IXFR_ADD, /* Expecting RR to add. */ + IXFR_DONE /* Processing done, IXFR-in complete. */ }; -/*! \brief Extended structure for IXFR processing. */ +/*! \brief Extended structure for IXFR-in/IXFR-out processing. */ struct ixfr_proc { - struct xfr_proc proc; + struct xfr_proc proc; /* Generic transfer processing context. */ node_t *cur; - unsigned state; - knot_changesets_t *changesets; + int state; /* IXFR-in state. */ + changesets_t *changesets; /* Processed changesets. */ + zone_t *zone; /* Modified zone - for journal access. */ + mm_ctx_t *mm; /* Memory context for RR allocations. */ struct query_data *qdata; - const knot_rrset_t *soa_from, *soa_to; + const knot_rrset_t *soa_from; + const knot_rrset_t *soa_to; }; -/* IXFR-specific logging (internal, expects 'qdata' variable set). */ -#define IXFR_LOG(severity, msg...) \ +/* IXFR-out-specific logging (internal, expects 'qdata' variable set). */ +#define IXFROUT_LOG(severity, msg...) \ QUERY_LOG(severity, qdata, "Outgoing IXFR", msg) /*! \brief Helper macro for putting RRs into packet. */ @@ -74,56 +81,57 @@ static int ixfr_put_rrlist(knot_pkt_t *pkt, struct ixfr_proc *ixfr, list_t *list * with next empty answer and it must resume the processing exactly where * it's left off. */ -static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item, struct xfr_proc *xfer) +static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item, + struct xfr_proc *xfer) { int ret = KNOT_EOK; struct ixfr_proc *ixfr = (struct ixfr_proc *)xfer; - knot_changeset_t *chgset = (knot_changeset_t *)item; + changeset_t *chgset = (changeset_t *)item; /* Put former SOA. */ - if (ixfr->state == SOA_REMOVE) { + if (ixfr->state == IXFR_SOA_DEL) { IXFR_SAFE_PUT(pkt, chgset->soa_from); dbg_ns("%s: put 'REMOVE' SOA\n", __func__); - ixfr->state = REMOVE; + ixfr->state = IXFR_DEL; } /* Put REMOVE RRSets. */ - if (ixfr->state == REMOVE) { + if (ixfr->state == IXFR_DEL) { ret = ixfr_put_rrlist(pkt, ixfr, &chgset->remove); if (ret != KNOT_EOK) { return ret; } dbg_ns("%s: put 'REMOVE' RRs\n", __func__); - ixfr->state = SOA_ADD; + ixfr->state = IXFR_SOA_ADD; } /* Put next SOA. */ - if (ixfr->state == SOA_ADD) { + if (ixfr->state == IXFR_SOA_ADD) { IXFR_SAFE_PUT(pkt, chgset->soa_to); - dbg_ns("%s: put 'ADD' SOA\n", __func__); - ixfr->state = ADD; + dbg_ns("%s: put 'IXFR_ADD' SOA\n", __func__); + ixfr->state = IXFR_ADD; } /* Put REMOVE RRSets. */ - if (ixfr->state == ADD) { + if (ixfr->state == IXFR_ADD) { ret = ixfr_put_rrlist(pkt, ixfr, &chgset->add); if (ret != KNOT_EOK) { return ret; } - dbg_ns("%s: put 'ADD' RRs\n", __func__); - ixfr->state = SOA_REMOVE; + dbg_ns("%s: put 'IXFR_ADD' RRs\n", __func__); + ixfr->state = IXFR_SOA_DEL; } /* Finished change set. */ - struct query_data *qdata = ixfr->qdata; /*< Required for IXFR_LOG() */ - IXFR_LOG(LOG_INFO, "Serial %u -> %u.", chgset->serial_from, chgset->serial_to); + struct query_data *qdata = ixfr->qdata; /*< Required for IXFROUT_LOG() */ + IXFROUT_LOG(LOG_INFO, "Serial %u -> %u.", chgset->serial_from, chgset->serial_to); return ret; } #undef IXFR_SAFE_PUT -static int ixfr_load_chsets(knot_changesets_t **chgsets, const zone_t *zone, +static int ixfr_load_chsets(changesets_t **chgsets, const zone_t *zone, const knot_rrset_t *their_soa) { assert(chgsets); @@ -137,7 +145,7 @@ static int ixfr_load_chsets(knot_changesets_t **chgsets, const zone_t *zone, return KNOT_EUPTODATE; } - *chgsets = knot_changesets_create(0); + *chgsets = changesets_create(0); if (*chgsets == NULL) { return KNOT_ENOMEM; } @@ -145,7 +153,7 @@ static int ixfr_load_chsets(knot_changesets_t **chgsets, const zone_t *zone, ret = journal_load_changesets(zone->conf->ixfr_db, *chgsets, serial_from, serial_to); if (ret != KNOT_EOK) { - knot_changesets_free(chgsets); + changesets_free(chgsets, NULL); } return ret; @@ -181,7 +189,7 @@ static void ixfr_answer_cleanup(struct query_data *qdata) mm_ctx_t *mm = qdata->mm; ptrlist_free(&ixfr->proc.nodes, mm); - knot_changesets_free(&ixfr->changesets); + changesets_free(&ixfr->changesets, NULL); mm->free(qdata->ext); /* Allow zone changes (finished). */ @@ -202,7 +210,7 @@ static int ixfr_answer_init(struct query_data *qdata) /* Compare serials. */ const knot_rrset_t *their_soa = &knot_pkt_section(qdata->query, KNOT_AUTHORITY)->rr[0]; - knot_changesets_t *chgsets = NULL; + changesets_t *chgsets = NULL; int ret = ixfr_load_chsets(&chgsets, qdata->zone, their_soa); if (ret != KNOT_EOK) { dbg_ns("%s: failed to load changesets => %d\n", __func__, ret); @@ -213,17 +221,18 @@ static int ixfr_answer_init(struct query_data *qdata) mm_ctx_t *mm = qdata->mm; struct ixfr_proc *xfer = mm->alloc(mm->ctx, sizeof(struct ixfr_proc)); if (xfer == NULL) { - knot_changesets_free(&chgsets); + changesets_free(&chgsets, NULL); return KNOT_ENOMEM; } memset(xfer, 0, sizeof(struct ixfr_proc)); gettimeofday(&xfer->proc.tstamp, NULL); + xfer->state = IXFR_SOA_DEL; init_list(&xfer->proc.nodes); xfer->qdata = qdata; /* Put all changesets to processing queue. */ xfer->changesets = chgsets; - knot_changeset_t *chs = NULL; + changeset_t *chs = NULL; WALK_LIST(chs, chgsets->sets) { ptrlist_add(&xfer->proc.nodes, chs, mm); dbg_ns("%s: preparing %u -> %u\n", __func__, chs->serial_from, chs->serial_to); @@ -276,7 +285,283 @@ static int ixfr_answer_soa(knot_pkt_t *pkt, struct query_data *qdata) return NS_PROC_DONE; } -int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata) +/* ------------------------- IXFR-in processing ----------------------------- */ + +/* IXFR-in-specific logging (internal, expects 'adata' variable set). */ +#define IXFRIN_LOG(severity, msg...) \ + ANSWER_LOG(severity, adata, "Incoming IXFR", msg) + +/*! \brief Cleans up data allocated by IXFR-in processing. */ +static void ixfrin_cleanup(struct answer_data *data) +{ + struct ixfr_proc *proc = data->ext; + if (proc) { + changesets_free(&proc->changesets, data->mm); + mm_free(data->mm, proc); + data->ext = NULL; + } +} + +/*! \brief Initializes IXFR-in processing context. */ +static int ixfrin_answer_init(struct answer_data *data) +{ + struct ixfr_proc *proc = mm_alloc(data->mm, sizeof(struct ixfr_proc)); + if (proc == NULL) { + return KNOT_ENOMEM; + } + memset(proc, 0, sizeof(struct ixfr_proc)); + gettimeofday(&proc->proc.tstamp, NULL); + + proc->changesets = changesets_create(0); + if (proc->changesets == NULL) { + mm_free(data->mm, proc); + return KNOT_ENOMEM; + } + proc->state = IXFR_START; + proc->zone = data->param->zone; + proc->mm = data->mm; + + data->ext = proc; + data->ext_cleanup = &ixfrin_cleanup; + + return KNOT_EOK; +} + +/*! \brief Finalizes IXFR-in processing. */ +static int ixfrin_finalize(struct answer_data *adata) +{ + struct ixfr_proc *ixfr = adata->ext; + + assert(ixfr->state == IXFR_DONE); + int ret = zone_change_apply_and_store(&ixfr->changesets, + ixfr->zone, "IXFR", adata->mm); + if (ret != KNOT_EOK) { + IXFRIN_LOG(LOG_ERR, "Failed to apply changes to zone - %s", + knot_strerror(ret)); + ixfrin_cleanup(adata); + return ret; + } + + struct timeval now = {0}; + gettimeofday(&now, NULL); + IXFRIN_LOG(LOG_INFO, "Finished in %.02fs (%u messages, ~%.01fkB).", + time_diff(&ixfr->proc.tstamp, &now) / 1000.0, + ixfr->proc.npkts, ixfr->proc.nbytes / 1024.0); + ixfrin_cleanup(adata); + + return KNOT_EOK; +} + +/*! \brief Stores starting SOA into changesets structure. */ +static int solve_start(const knot_rrset_t *rr, changesets_t *changesets, mm_ctx_t *mm) +{ + assert(changesets->first_soa == NULL); + if (rr->type != KNOT_RRTYPE_SOA) { + return KNOT_EMALF; + } + + // Store the first SOA for later use. + changesets->first_soa = knot_rrset_copy(rr, mm); + if (changesets->first_soa == NULL) { + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +/*! \brief Decides what to do with a starting SOA (deletions). */ +static int solve_soa_del(const knot_rrset_t *rr, changesets_t *changesets, + mm_ctx_t *mm) +{ + if (rr->type != KNOT_RRTYPE_SOA) { + return KNOT_EMALF; + } + + // Create new changeset. + changeset_t *change = changesets_create_changeset(changesets); + if (change == NULL) { + return KNOT_ENOMEM; + } + + // Store SOA into changeset. + change->soa_from = knot_rrset_copy(rr, mm); + if (change->soa_from == NULL) { + return KNOT_ENOMEM; + } + change->serial_from = knot_soa_serial(&rr->rrs); + + return KNOT_EOK; +} + +/*! \brief Stores ending SOA into changeset. */ +static int solve_soa_add(const knot_rrset_t *rr, changeset_t *change, mm_ctx_t *mm) +{ + assert(rr->type == KNOT_RRTYPE_SOA); + change->soa_to= knot_rrset_copy(rr, mm); + if (change->soa_to == NULL) { + return KNOT_ENOMEM; + } + change->serial_to = knot_soa_serial(&rr->rrs); + + return KNOT_EOK; +} + +/*! \brief Adds single RR into given section of changeset. */ +static int add_part(const knot_rrset_t *rr, changeset_t *change, int part, mm_ctx_t *mm) +{ + assert(rr->type != KNOT_RRTYPE_SOA); + knot_rrset_t *copy = knot_rrset_copy(rr, mm); + if (copy) { + int ret = changeset_add_rrset(change, copy, part); + if (ret != KNOT_EOK) { + return ret; + } + return KNOT_EOK; + } else { + return KNOT_ENOMEM; + } +} + +/*! \brief Adds single RR into remove section of changeset. */ +static int solve_del(const knot_rrset_t *rr, changeset_t *change, mm_ctx_t *mm) +{ + return add_part(rr, change, CHANGESET_REMOVE, mm); +} + +/*! \brief Adds single RR into add section of changeset. */ +static int solve_add(const knot_rrset_t *rr, changeset_t *change, mm_ctx_t *mm) +{ + return add_part(rr, change, CHANGESET_ADD, mm); +} + +/*! \brief Decides what the next IXFR-in state should be. */ +static int ixfrin_next_state(struct ixfr_proc *proc, const knot_rrset_t *rr) +{ + const bool soa = (rr->type == KNOT_RRTYPE_SOA); + if (soa && + (proc->state == IXFR_SOA_ADD || proc->state == IXFR_ADD)) { + // Check end of transfer. + if (knot_rrset_equal(rr, proc->changesets->first_soa, + KNOT_RRSET_COMPARE_WHOLE)) { + // Final SOA encountered, transfer done. + return IXFR_DONE; + } + } + + switch (proc->state) { + case IXFR_START: + // Final SOA already stored or transfer start. + return proc->changesets->first_soa ? IXFR_SOA_DEL : IXFR_START; + case IXFR_SOA_DEL: + // Empty delete section or start of delete section. + return soa ? IXFR_SOA_ADD : IXFR_DEL; + case IXFR_SOA_ADD: + // Empty add section or start of add section. + return soa ? IXFR_SOA_DEL : IXFR_ADD; + case IXFR_DEL: + // End of delete section or continue. + return soa ? IXFR_SOA_ADD : IXFR_DEL; + case IXFR_ADD: + // End of add section or continue. + return soa ? IXFR_SOA_DEL : IXFR_ADD; + default: + assert(0); + return 0; + } +} + +/*! + * \brief Processes single RR according to current IXFR-in state. The states + * correspond with IXFR-in message structure, in the order they are + * mentioned in the code. + * + * \param rr RR to process. + * \param proc Processing context. + * + * \return KNOT_E* + */ +static int ixfrin_step(const knot_rrset_t *rr, struct ixfr_proc *proc) +{ + proc->state = ixfrin_next_state(proc, rr); + changeset_t *change = changesets_get_last(proc->changesets); + + switch (proc->state) { + case IXFR_START: + return solve_start(rr, proc->changesets, proc->mm); + case IXFR_SOA_DEL: + return solve_soa_del(rr, proc->changesets, proc->mm); + case IXFR_DEL: + return solve_del(rr, change, proc->mm); + case IXFR_SOA_ADD: + return solve_soa_add(rr, change, proc->mm); + case IXFR_ADD: + return solve_add(rr, change, proc->mm); + case IXFR_DONE: + return KNOT_EOK; + default: + return KNOT_ERROR; + } +} + +/*! \brief Checks whether journal node limit has not been exceeded. */ +static bool journal_limit_exceeded(struct ixfr_proc *proc) +{ + return proc->changesets->count > JOURNAL_NCOUNT; +} + +/*! \brief Checks whether RR belongs into zone. */ +static bool out_of_zone(const knot_rrset_t *rr, struct ixfr_proc *proc) +{ + return !knot_dname_is_sub(rr->owner, proc->zone->name) && + !knot_dname_is_equal(rr->owner, proc->zone->name); +} + +/*! + * \brief Processes IXFR reply packet and fills in the changesets structure. + * + * \param pkt Packet containing the IXFR reply in wire format. + * \param adata Answer data, including processing context. + * + * \return NS_PROC_MORE, NS_PROC_DONE, NS_PROC_FAIL + */ +static int process_ixfrin_packet(knot_pkt_t *pkt, struct answer_data *adata) +{ + struct ixfr_proc *ixfr = (struct ixfr_proc *)adata->ext; + // Update counters. + ixfr->proc.npkts += 1; + ixfr->proc.nbytes += pkt->size; + + // Process RRs in the message. + const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); + for (uint16_t i = 0; i < answer->count; ++i) { + if (journal_limit_exceeded(ixfr)) { + IXFRIN_LOG(LOG_WARNING, "Journal is full."); + return NS_PROC_FAIL; + } + + const knot_rrset_t *rr = &answer->rr[i]; + if (out_of_zone(rr, ixfr)) { + continue; + } + + int ret = ixfrin_step(rr, ixfr); + if (ret != KNOT_EOK) { + IXFRIN_LOG(LOG_ERR, "Failed - %s", knot_strerror(ret)); + return NS_PROC_FAIL; + } + + if (ixfr->state == IXFR_DONE) { + // Transfer done, do not consume more RRs. + return NS_PROC_DONE; + } + } + + return NS_PROC_MORE; +} + +/* --------------------------------- API ------------------------------------ */ + +int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata) { if (pkt == NULL || qdata == NULL) { return NS_PROC_FAIL; @@ -297,20 +582,20 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata) switch(ret) { case KNOT_EOK: /* OK */ ixfr = (struct ixfr_proc*)qdata->ext; - IXFR_LOG(LOG_INFO, "Started (serial %u -> %u).", - knot_soa_serial(&ixfr->soa_from->rrs), - knot_soa_serial(&ixfr->soa_to->rrs)); + IXFROUT_LOG(LOG_INFO, "Started (serial %u -> %u).", + knot_soa_serial(&ixfr->soa_from->rrs), + knot_soa_serial(&ixfr->soa_to->rrs)); break; case KNOT_EUPTODATE: /* Our zone is same age/older, send SOA. */ - IXFR_LOG(LOG_INFO, "Zone is up-to-date."); + IXFROUT_LOG(LOG_INFO, "Zone is up-to-date."); return ixfr_answer_soa(pkt, qdata); case KNOT_ERANGE: /* No history -> AXFR. */ case KNOT_ENOENT: - IXFR_LOG(LOG_INFO, "Incomplete history, fallback to AXFR."); + IXFROUT_LOG(LOG_INFO, "Incomplete history, fallback to AXFR."); qdata->packet_type = KNOT_QUERY_AXFR; /* Solve as AXFR. */ return axfr_query(pkt, qdata); default: /* Server errors. */ - IXFR_LOG(LOG_ERR, "Failed to start (%s).", knot_strerror(ret)); + IXFROUT_LOG(LOG_ERR, "Failed to start (%s).", knot_strerror(ret)); return NS_PROC_FAIL; } } @@ -325,13 +610,13 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata) return NS_PROC_FULL; /* Check for more. */ case KNOT_EOK: /* Last response. */ gettimeofday(&now, NULL); - IXFR_LOG(LOG_INFO, "Finished in %.02fs (%u messages, ~%.01fkB).", - time_diff(&ixfr->proc.tstamp, &now) / 1000.0, - ixfr->proc.npkts, ixfr->proc.nbytes / 1024.0); + IXFROUT_LOG(LOG_INFO, "Finished in %.02fs (%u messages, ~%.01fkB).", + time_diff(&ixfr->proc.tstamp, &now) / 1000.0, + ixfr->proc.npkts, ixfr->proc.nbytes / 1024.0); ret = NS_PROC_DONE; break; default: /* Generic error. */ - IXFR_LOG(LOG_ERR, "%s", knot_strerror(ret)); + IXFROUT_LOG(LOG_ERR, "%s", knot_strerror(ret)); ret = NS_PROC_FAIL; break; } @@ -339,90 +624,42 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata) return ret; } -int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *data) +int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata) { -#warning TODO: reimplement ixfr_process_answer -#if 0 - dbg_ns("ns_process_ixfrin: incoming packet\n"); - - /* - * [TSIG] Here we assume that 'xfr' contains TSIG information - * and the digest of the query sent to the master or the previous - * digest. - */ - int ret = xfrin_process_ixfr_packet(pkt, xfr); - - if (ret == XFRIN_RES_FALLBACK) { - dbg_ns("ns_process_ixfrin: Fallback to AXFR.\n"); - ret = KNOT_ENOIXFR; - } - - if (ret < 0) { - knot_pkt_free(&xfr->query); - return ret; - } else if (ret > 0) { - dbg_ns("ns_process_ixfrin: IXFR finished\n"); - gettimeofday(&xfr->t_end, NULL); - - knot_changesets_t *chgsets = (knot_changesets_t *)xfr->data; - if (chgsets == NULL || chgsets->first_soa == NULL) { - // nothing to be done?? - dbg_ns("No changesets created for incoming IXFR!\n"); - return ret; + if (adata->ext == NULL) { + NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0); + if (!zone_transfer_needed(adata->param->zone, pkt)) { + IXFRIN_LOG(LOG_INFO, "Server has newer zone."); + return NS_PROC_DONE; } - // find zone associated with the changesets - /* Must not search for the zone in zonedb as it may fetch a - * different zone than the one the transfer started on. */ - zone_t *zone = xfr->zone; - if (zone == NULL) { - dbg_ns("No zone found for incoming IXFR!\n"); - knot_changesets_free( - (knot_changesets_t **)(&xfr->data)); - return KNOT_ENOZONE; + IXFRIN_LOG(LOG_INFO, "Starting."); + // First packet with IXFR, init context + int ret = ixfrin_answer_init(adata); + if (ret != KNOT_EOK) { + IXFRIN_LOG(LOG_ERR, "Failed - %s", knot_strerror(ret)); + return NS_PROC_FAIL; } + } else { + NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 100); + } - switch (ret) { - case XFRIN_RES_COMPLETE: - break; - case XFRIN_RES_SOA_ONLY: { - // compare the SERIAL from the changeset with the zone's - // serial - uint32_t zone_serial = zone_contents_serial(zone->contents); - if (knot_serial_compare( - knot_soa_serial(&chgsets->first_soa->rrs), - zone_serial) - > 0) { - if ((xfr->flags & XFR_FLAG_UDP) != 0) { - // IXFR over UDP - dbg_ns("Update did not fit.\n"); - return KNOT_EIXFRSPACE; - } else { - // fallback to AXFR - dbg_ns("ns_process_ixfrin: " - "Fallback to AXFR.\n"); - knot_changesets_free( - (knot_changesets_t **)&xfr->data); - knot_pkt_free(&xfr->query); - return KNOT_ENOIXFR; - } - - } else { - // free changesets - dbg_ns("No update needed.\n"); - knot_changesets_free( - (knot_changesets_t **)(&xfr->data)); - return KNOT_ENOXFR; - } - } break; + int ret = process_ixfrin_packet(pkt, adata); + if (ret == NS_PROC_DONE) { + NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0); + int fret = ixfrin_finalize(adata); + if (fret != KNOT_EOK) { + IXFRIN_LOG(LOG_ERR, "Failed - %s", knot_strerror(ret)); + ret = NS_PROC_FAIL; } } - /*! \todo In case of error, shouldn't the zone be destroyed here? */ + if (ret == NS_PROC_FAIL) { + ixfrin_cleanup(adata); + IXFRIN_LOG(LOG_ERR, "Failed."); + } return ret; -#endif - return NS_PROC_FAIL; } -#undef IXFR_LOG +#undef IXFROUT_LOG diff --git a/src/knot/nameserver/ixfr.h b/src/knot/nameserver/ixfr.h index 1ee9130ac5f3a80eb6d4cfbe68abcdcc98a277a2..fcb0a9cb93db6dda7e81ece2986d95fe2945b468 100644 --- a/src/knot/nameserver/ixfr.h +++ b/src/knot/nameserver/ixfr.h @@ -39,32 +39,16 @@ struct answer_data; * \retval FAIL if it encountered an error. * \retval DONE if finished. */ -int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata); +int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata); /*! - * \brief Process an IXFR query response. - * - * \param pkt Processed packet. - * \param xfr Persistent transfer-specific data. - * - * \retval KNOT_EOK If this packet was processed successfuly and another packet - * is expected. (RFC1995bis, case c) - * \retval KNOT_ENOXFR If the transfer is not taking place because server's - * SERIAL is the same as this client's SERIAL. The client - * should close the connection and do no further processing. - * (RFC1995bis case a). - * \retval KNOT_EAGAIN If the server could not fit the transfer into the packet. - * This should happen only if UDP was used. In this case - * the client should retry the request via TCP. If UDP was - * not used, it should be considered that the transfer was - * malformed and the connection should be closed. - * (RFC1995bis case b). - * \retval >0 Transfer successully finished. Changesets are created and furter - * processing is needed. - * \retval Other If any other error occured. The connection should be closed. + * \brief IXFR query response processing module. * + * \retval MORE if more data are required. + * \retval FAIL if it encountered an error, retry over AXFR will be done. + * \retval DONE if finished. */ -int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *data); +int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata); #endif /* _KNOT_IXFR_H_ */ diff --git a/src/knot/nameserver/process_query.c b/src/knot/nameserver/process_query.c index 7c28186c6d196fc2334636607432d980d6ff7db1..885e0b4c5dfdb163501f0d4f93c4f20b63294ad4 100644 --- a/src/knot/nameserver/process_query.c +++ b/src/knot/nameserver/process_query.c @@ -372,7 +372,7 @@ static int query_internet(knot_pkt_t *pkt, knot_process_t *ctx) next_state = axfr_query(pkt, data); break; case KNOT_QUERY_IXFR: - next_state = ixfr_answer(pkt, data); + next_state = ixfr_query(pkt, data); break; case KNOT_QUERY_UPDATE: next_state = update_answer(pkt, data); diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c index 99f1d71721f92b0c919031d12f493eeba127d53b..a32005c3977b4bf0bd9a09ab57790373b20faf82 100644 --- a/src/knot/nameserver/update.c +++ b/src/knot/nameserver/update.c @@ -1,7 +1,7 @@ #include "knot/nameserver/update.h" #include "knot/nameserver/internet.h" #include "knot/nameserver/process_query.h" -#include "knot/updates/xfr-in.h" +#include "knot/updates/apply.h" #include "knot/dnssec/zone-sign.h" #include "common/debug.h" #include "knot/dnssec/zone-events.h" @@ -135,13 +135,13 @@ static bool zones_nsec3param_changed(const zone_contents_t *old_contents, } static int sign_update(zone_t *zone, const zone_contents_t *old_contents, - zone_contents_t *new_contents, knot_changeset_t *ddns_ch) + zone_contents_t *new_contents, changeset_t *ddns_ch) { - knot_changesets_t *sec_chs = knot_changesets_create(1); + changesets_t *sec_chs = changesets_create(1); if (sec_chs == NULL) { return KNOT_ENOMEM; } - knot_changeset_t *sec_ch = knot_changesets_get_last(sec_chs); + changeset_t *sec_ch = changesets_get_last(sec_chs); /* * Check if the UPDATE changed DNSKEYs or NSEC3PARAM. @@ -162,22 +162,21 @@ static int sign_update(zone_t *zone, const zone_contents_t *old_contents, &refresh_at); } if (ret != KNOT_EOK) { - knot_changesets_free(&sec_chs); + changesets_free(&sec_chs, NULL); return ret; } // Apply DNSSEC changeset - zone_contents_set_gen_old(new_contents); - ret = xfrin_apply_changesets_directly(new_contents, sec_chs); + ret = apply_changesets_directly(new_contents, sec_chs); if (ret != KNOT_EOK) { - knot_changesets_free(&sec_chs); + changesets_free(&sec_chs, NULL); return ret; } // Merge changesets - ret = knot_changeset_merge(ddns_ch, sec_ch); + ret = changeset_merge(ddns_ch, sec_ch); if (ret != KNOT_EOK) { - knot_changesets_free(&sec_chs); + changesets_free(&sec_chs, NULL); return ret; } @@ -203,33 +202,33 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata) } // Create DDNS changesets - knot_changesets_t *ddns_chs = knot_changesets_create(1); + changesets_t *ddns_chs = changesets_create(1); if (ddns_chs == NULL) { *rcode = KNOT_RCODE_SERVFAIL; return KNOT_ENOMEM; } - knot_changeset_t *ddns_ch = knot_changesets_get_last(ddns_chs); + changeset_t *ddns_ch = changesets_get_last(ddns_chs); ret = ddns_process_update(zone, query, ddns_ch, rcode); if (ret != KNOT_EOK) { - knot_changesets_free(&ddns_chs); + changesets_free(&ddns_chs, NULL); return ret; } zone_contents_t *new_contents = NULL; - const bool change_made = !knot_changeset_is_empty(ddns_ch); + const bool change_made = !changeset_is_empty(ddns_ch); if (change_made) { - ret = xfrin_apply_changesets(zone, ddns_chs, &new_contents); + ret = apply_changesets(zone, ddns_chs, &new_contents); if (ret != KNOT_EOK) { if (ret == KNOT_ETTL) { *rcode = KNOT_RCODE_REFUSED; } else { *rcode = KNOT_RCODE_SERVFAIL; } - knot_changesets_free(&ddns_chs); + changesets_free(&ddns_chs, NULL); return ret; } } else { - knot_changesets_free(&ddns_chs); + changesets_free(&ddns_chs, NULL); *rcode = KNOT_RCODE_NOERROR; return KNOT_EOK; } @@ -238,8 +237,8 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata) if (zone->conf->dnssec_enable) { ret = sign_update(zone, zone->contents, new_contents, ddns_ch); if (ret != KNOT_EOK) { - xfrin_rollback_update(ddns_chs, &new_contents); - knot_changesets_free(&ddns_chs); + update_rollback(ddns_chs, &new_contents); + changesets_free(&ddns_chs, NULL); *rcode = KNOT_RCODE_SERVFAIL; return ret; } @@ -248,20 +247,19 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata) // Write changes to journal if all went well. (DNSSEC merged) ret = zone_change_store(zone, ddns_chs); if (ret != KNOT_EOK) { - xfrin_rollback_update(ddns_chs, &new_contents); - knot_changesets_free(&ddns_chs); + update_rollback(ddns_chs, &new_contents); + changesets_free(&ddns_chs, NULL); *rcode = KNOT_RCODE_SERVFAIL; return ret; } // Switch zone contents. - zone_contents_t *old_contents = xfrin_switch_zone(zone, new_contents); - // Wait for readers. + zone_contents_t *old_contents = zone_switch_contents(zone, new_contents); synchronize_rcu(); - xfrin_zone_contents_free(&old_contents); + update_free_old_zone(&old_contents); - xfrin_cleanup_successful_update(ddns_chs); - knot_changesets_free(&ddns_chs); + update_cleanup(ddns_chs); + changesets_free(&ddns_chs, NULL); /* Trim extra heap. */ mem_trim(); diff --git a/src/knot/server/journal.c b/src/knot/server/journal.c index 3e5f72c2d9131b9f294b023e6e122e529cb0d800..eb629f6bfccec9cfd5dcf39ea35aa521bbb18a8b 100644 --- a/src/knot/server/journal.c +++ b/src/knot/server/journal.c @@ -897,7 +897,15 @@ int journal_unmap(journal_t *journal, uint64_t id, void *ptr, int finalize) } /* Mapped node is on tail. */ + /* @todo: This is hack to allow read-only correct unmap. */ + int ret = KNOT_EOK; journal_node_t *n = journal->nodes + journal->qtail; + if (!finalize) { + ret = journal_fetch(journal, id, journal_cmp_eq, &n); + if (ret != KNOT_EOK) { + return KNOT_ENOENT; + } + } if(n->id != id) { dbg_journal("journal: failed to find mmap node with id=%llu\n", (unsigned long long)id); @@ -917,7 +925,6 @@ int journal_unmap(journal_t *journal, uint64_t id, void *ptr, int finalize) } /* Finalize. */ - int ret = KNOT_EOK; if (finalize) { ret = journal_write_out(journal, n); } @@ -953,7 +960,7 @@ bool journal_exists(const char *path) } /*! \brief No doc here. Moved from zones.h (@mvavrusa) */ -static int changesets_unpack(knot_changeset_t *chs) +static int changesets_unpack(changeset_t *chs) { knot_rrset_t *rrset = NULL; @@ -963,8 +970,6 @@ static int changesets_unpack(knot_changeset_t *chs) return KNOT_EMALF; } size_t remaining = chs->size; - memcpy(&chs->flags, chs->data, sizeof(uint32_t)); - remaining -= sizeof(uint32_t); /* Read initial changeset RRSet - SOA. */ uint8_t *stream = chs->data + (chs->size - remaining); @@ -979,7 +984,7 @@ static int changesets_unpack(knot_changeset_t *chs) */ assert(rrset->type == KNOT_RRTYPE_SOA); assert(chs->serial_from == knot_soa_serial(&rrset->rrs)); - knot_changeset_add_soa(chs, rrset, KNOT_CHANGESET_REMOVE); + changeset_add_soa(chs, rrset, CHANGESET_REMOVE); /* Read remaining RRSets */ int in_remove_section = 1; @@ -998,8 +1003,8 @@ static int changesets_unpack(knot_changeset_t *chs) /* Move to ADD section if in REMOVE. */ if (in_remove_section) { - knot_changeset_add_soa(chs, rrset, - KNOT_CHANGESET_ADD); + changeset_add_soa(chs, rrset, + CHANGESET_ADD); in_remove_section = 0; } else { /* Final SOA. */ @@ -1009,14 +1014,14 @@ static int changesets_unpack(knot_changeset_t *chs) } else { /* Remove RRSets. */ if (in_remove_section) { - ret = knot_changeset_add_rrset( + ret = changeset_add_rrset( chs, rrset, - KNOT_CHANGESET_REMOVE); + CHANGESET_REMOVE); } else { /* Add RRSets. */ - ret = knot_changeset_add_rrset( + ret = changeset_add_rrset( chs, rrset, - KNOT_CHANGESET_ADD); + CHANGESET_ADD); } } } @@ -1038,14 +1043,9 @@ static int zones_rrset_write_to_mem(const knot_rrset_t *rr, char **entry, return ret; } -static int zones_serialize_and_store_chgset(const knot_changeset_t *chs, +static int zones_serialize_and_store_chgset(const changeset_t *chs, char *entry, size_t max_size) { - /* Write changeset flags. */ - memcpy(entry, (char*)&chs->flags, sizeof(uint32_t)); - entry += sizeof(uint32_t); - max_size -= sizeof(uint32_t); - /* Serialize SOA 'from'. */ int ret = zones_rrset_write_to_mem(chs->soa_from, &entry, &max_size); if (ret != KNOT_EOK) { @@ -1085,7 +1085,7 @@ static int zones_serialize_and_store_chgset(const knot_changeset_t *chs, return KNOT_EOK; } -static int changeset_pack(const knot_changeset_t *chs, journal_t *j) +static int changeset_pack(const changeset_t *chs, journal_t *j) { assert(chs != NULL); assert(j != NULL); @@ -1174,8 +1174,8 @@ finish: static int load_changeset(journal_t *journal, journal_node_t *n, void *data) { - knot_changesets_t *chsets = (knot_changesets_t *)data; - knot_changeset_t *chs = knot_changesets_create_changeset(chsets); + changesets_t *chsets = (changesets_t *)data; + changeset_t *chs = changesets_create_changeset(chsets); if (chs == NULL) { return KNOT_ERROR; } @@ -1199,7 +1199,7 @@ static int load_changeset(journal_t *journal, journal_node_t *n, void *data) return KNOT_EOK; } -int journal_load_changesets(const char *path, knot_changesets_t *dst, +int journal_load_changesets(const char *path, changesets_t *dst, uint32_t from, uint32_t to) { int ret = journal_walk(path, from, to, &load_changeset, dst); @@ -1213,7 +1213,7 @@ int journal_load_changesets(const char *path, knot_changesets_t *dst, * Parses changesets from the binary format stored in chgsets->data * into the changeset_t structures. */ - knot_changeset_t* chs = NULL; + changeset_t* chs = NULL; WALK_LIST(chs, dst->sets) { ret = changesets_unpack(chs); if (ret != KNOT_EOK) { @@ -1222,7 +1222,7 @@ int journal_load_changesets(const char *path, knot_changesets_t *dst, } /* Check for complete history. */ - knot_changeset_t *last = knot_changesets_get_last(dst); + changeset_t *last = changesets_get_last(dst); if (to != last->serial_to) { return KNOT_ERANGE; } @@ -1230,7 +1230,7 @@ int journal_load_changesets(const char *path, knot_changesets_t *dst, return KNOT_EOK; } -int journal_store_changesets(knot_changesets_t *src, const char *path, size_t size_limit) +int journal_store_changesets(changesets_t *src, const char *path, size_t size_limit) { if (src == NULL || path == NULL) { return KNOT_EINVAL; @@ -1244,7 +1244,7 @@ int journal_store_changesets(knot_changesets_t *src, const char *path, size_t si } /* Begin writing to journal. */ - knot_changeset_t *chs = NULL; + changeset_t *chs = NULL; WALK_LIST(chs, src->sets) { /* Make key from serials. */ ret = changeset_pack(chs, journal); diff --git a/src/knot/server/journal.h b/src/knot/server/journal.h index f0c43be0daf3d59c50baa936690979c5eedf1909..21da6a50776a478687b07da0797b360ff5c147e6 100644 --- a/src/knot/server/journal.h +++ b/src/knot/server/journal.h @@ -102,7 +102,7 @@ typedef struct journal_t * Journal defaults and constants. */ #define JOURNAL_NCOUNT 1024 /*!< Default node count. */ -#define JOURNAL_MAGIC {'k', 'n', 'o', 't', '1', '5', '1'} +#define JOURNAL_MAGIC {'k', 'n', 'o', 't', '1', '5', '2'} #define MAGIC_LENGTH 7 /* HEADER = magic, crc, max_entries, qhead, qtail */ #define JOURNAL_HSIZE (MAGIC_LENGTH + sizeof(crc_t) + sizeof(uint16_t) * 3) @@ -173,7 +173,7 @@ bool journal_exists(const char *path); * * \param changesets Double pointer to changesets structure to be freed. */ -int journal_load_changesets(const char *path, knot_changesets_t *dst, +int journal_load_changesets(const char *path, changesets_t *dst, uint32_t from, uint32_t to); /*! @@ -192,7 +192,7 @@ int journal_load_changesets(const char *path, knot_changesets_t *dst, * \todo Expects the xfr structure to be initialized in some way. * \todo Update documentation!!! */ -int journal_store_changesets(knot_changesets_t *src, const char *path, size_t size_limit); +int journal_store_changesets(changesets_t *src, const char *path, size_t size_limit); /*! \brief Function for unmarking dirty nodes. */ int journal_mark_synced(const char *path); diff --git a/src/knot/server/net.c b/src/knot/server/net.c index 38e1e6f0b5bd05a41511cae1118752bfa73c6097..e0a010dea4f2ded2c839d02eff90dc5163addbb8 100644 --- a/src/knot/server/net.c +++ b/src/knot/server/net.c @@ -114,6 +114,11 @@ int net_connected_socket(int type, const struct sockaddr_storage *dst_addr, int socket = -1; + /* Check port. */ + if (sockaddr_port(dst_addr) == 0) { + return KNOT_ECONN; + } + /* Bind to specific source address - if set. */ if (src_addr != NULL && src_addr->ss_family != AF_UNSPEC) { socket = net_bound_socket(type, src_addr); diff --git a/src/knot/server/serialization.c b/src/knot/server/serialization.c index f79ec458bddd409ab50ba04c3da6603c37b2c1e0..21c0d9d68c9fb7b951704abdaf4c02af6d4cde4e 100644 --- a/src/knot/server/serialization.c +++ b/src/knot/server/serialization.c @@ -69,7 +69,7 @@ static int deserialize_rr(knot_rrset_t *rrset, const uint8_t *stream, uint32_t r rdata_size - sizeof(uint32_t), ttl, NULL); } -int changeset_binary_size(const knot_changeset_t *chgset, size_t *size) +int changeset_binary_size(const changeset_t *chgset, size_t *size) { if (chgset == NULL || size == NULL) { return KNOT_EINVAL; diff --git a/src/knot/server/serialization.h b/src/knot/server/serialization.h index cd0f9aa139f7b22bacddc08a90679c625a7f1e3c..1fa37ddddb5182e3b55d0960d6362f2fa12d45d2 100644 --- a/src/knot/server/serialization.h +++ b/src/knot/server/serialization.h @@ -38,7 +38,7 @@ * * \return KNOT_E* */ -int changeset_binary_size(const knot_changeset_t *chgset, size_t *size); +int changeset_binary_size(const changeset_t *chgset, size_t *size); /*! * \brief Serializes one RRSet into given stream. diff --git a/src/knot/server/server.c b/src/knot/server/server.c index 8a8e72f2c057b673bef98f60368b63c60873b319..3781abc35864cd3fafed55b023529e6c4099be8d 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -278,6 +278,7 @@ int server_init(server_t *server) } /* Create zone events threads. */ +#warning TODO: config option server->workers = worker_pool_create(4); //! \todo config option if (server->workers == NULL) { dt_delete(&server->iosched); diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c index 1ae51dba73eb3df163ef8a4099a6e3d5c6980cb3..0d5fa1eee2e783ed64823de479d8342458226bf5 100644 --- a/src/knot/server/xfr-handler.c +++ b/src/knot/server/xfr-handler.c @@ -32,7 +32,7 @@ #include "knot/server/net.h" #include "knot/server/udp-handler.h" #include "knot/server/tcp-handler.h" -#include "knot/updates/xfr-in.h" +#include "knot/updates/apply.h" #include "knot/nameserver/axfr.h" #include "knot/nameserver/ixfr.h" #include "knot/nameserver/internet.h" @@ -255,8 +255,8 @@ static void xfr_task_cleanup(knot_ns_xfr_t *rq) rq->data = NULL; } } else if (rq->type == XFR_TYPE_IIN) { - knot_changesets_t *chs = (knot_changesets_t *)rq->data; - knot_changesets_free(&chs); + changesets_t *chs = (changesets_t *)rq->data; + changesets_free(&chs); rq->data = NULL; assert(rq->new_contents == NULL); } else if (rq->type == XFR_TYPE_FORWARD) { @@ -590,7 +590,7 @@ static int xfr_task_finalize(knot_ns_xfr_t *rq) ret = zones_save_zone(rq); if (ret == KNOT_EOK) { zone_contents_t *old_contents = NULL; - old_contents = xfrin_switch_zone(rq->zone, rq->new_contents); + old_contents = update_switch_contents(rq->zone, rq->new_contents); zone_contents_deep_free(&old_contents); } else { log_zone_error("%s %s\n", @@ -599,8 +599,8 @@ static int xfr_task_finalize(knot_ns_xfr_t *rq) rq->msg); } } else if (rq->type == XFR_TYPE_IIN) { - knot_changesets_t *chs = (knot_changesets_t *)rq->data; - ret = zone_change_apply_and_store(chs, rq->zone, + changesets_t *chs = (changesets_t *)rq->data; + ret = zone_change_apply_and_store(&chs, rq->zone, &rq->new_contents, rq->msg); rq->data = NULL; /* Freed or applied in prev function. */ @@ -862,7 +862,7 @@ static int xfr_task_xfer(xfrhandler_t *xfr, knot_ns_xfr_t *rq, knot_pkt_t *pkt) ret = axfr_process_answer(pkt, rq); break; case XFR_TYPE_IIN: - ret = ixfr_process_answer(pkt, rq); + ret = ixfrin_process_answer(pkt, rq); break; default: ret = KNOT_EINVAL; diff --git a/src/knot/updates/apply.c b/src/knot/updates/apply.c new file mode 100644 index 0000000000000000000000000000000000000000..77d3d1c138cbcd508d136f3c9898360c0609b1ea --- /dev/null +++ b/src/knot/updates/apply.c @@ -0,0 +1,723 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/updates/apply.h" + +#include "common/debug.h" +#include "libknot/packet/pkt.h" +#include "libknot/processing/process.h" +#include "libknot/dname.h" +#include "knot/zone/zone.h" +#include "libknot/common.h" +#include "knot/updates/changesets.h" +#include "knot/zone/zonefile.h" +#include "common/lists.h" +#include "common/descriptor.h" +#include "libknot/util/utils.h" +#include "libknot/rdata/soa.h" + +/* ------------------------ legacy, to be removed --------------------------- */ + +#define KNOT_NS_TSIG_FREQ 100 + +static int knot_ns_tsig_required(int packet_nr) +{ + /*! \bug This can overflow to negative numbers. Proper solution is to + * count exactly at one place for each incoming/outgoing packet + * with packet_nr = (packet_nr + 1) % FREQ and require TSIG on 0. + */ + dbg_ns_verb("ns_tsig_required(%d): %d\n", packet_nr, + (packet_nr % KNOT_NS_TSIG_FREQ == 0)); + return (packet_nr % KNOT_NS_TSIG_FREQ == 0); +} + +int xfrin_transfer_needed(const zone_contents_t *zone, + knot_pkt_t *soa_response) +{ +#warning Reimplement or remove + /* + * Retrieve the local Serial + */ + const knot_rdataset_t *soa_rrs = + node_rdataset(zone->apex, KNOT_RRTYPE_SOA); + if (soa_rrs == NULL) { + char *name = knot_dname_to_str(zone->apex->owner); + dbg_xfrin("SOA RRSet missing in the zone %s!\n", name); + free(name); + return KNOT_ERROR; + } + + uint32_t local_serial = knot_soa_serial(soa_rrs); + /* + * Retrieve the remote Serial + */ + // the SOA should be the first (and only) RRSet in the response + const knot_pktsection_t *answer = knot_pkt_section(soa_response, KNOT_ANSWER); + if (answer->count < 1) { + return KNOT_EMALF; + } + knot_rrset_t soa_rr = answer->rr[0]; + if (soa_rr.type != KNOT_RRTYPE_SOA) { + return KNOT_EMALF; + } + + uint32_t remote_serial = knot_soa_serial(&soa_rr.rrs); + return (knot_serial_compare(local_serial, remote_serial) < 0); +} + +static int xfrin_check_tsig(knot_pkt_t *packet, knot_ns_xfr_t *xfr, + int tsig_req) +{ +#warning reimplement, but not inside this file + assert(packet != NULL); + assert(xfr != NULL); + + dbg_xfrin_verb("xfrin_check_tsig(): packet nr: %d, required: %d\n", + xfr->packet_nr, tsig_req); + + /* + * If we are expecting it (i.e. xfr->prev_digest_size > 0) + * a) it should be there (first, last or each 100th packet) and it + * is not + * Then we should discard the changes and close the connection. + * b) it should be there and it is or it may not be there (other + * packets) and it is + * We validate the TSIG and reset packet number counting and + * data aggregation. + * + * If we are not expecting it (i.e. xfr->prev_digest_size <= 0) and + * it is there => it should probably be considered an error + */ + + int ret = KNOT_EOK; + if (xfr->tsig_key) { + // just append the wireformat to the TSIG data + uint8_t *wire_buf = xfr->tsig_data + xfr->tsig_data_size; + memcpy(wire_buf, packet->wire, packet->size); + xfr->tsig_data_size += packet->size; + } + + if (xfr->tsig_key) { + if (tsig_req && packet->tsig_rr == NULL) { + // TSIG missing!! + return KNOT_ENOTSIG; + } else if (packet->tsig_rr != NULL) { + // TSIG there, either required or not, process + if (xfr->packet_nr == 0) { + ret = knot_tsig_client_check(packet->tsig_rr, + xfr->tsig_data, xfr->tsig_data_size, + xfr->digest, xfr->digest_size, + xfr->tsig_key, + xfr->tsig_prev_time_signed); + } else { + ret = knot_tsig_client_check_next(packet->tsig_rr, + xfr->tsig_data, xfr->tsig_data_size, + xfr->digest, xfr->digest_size, + xfr->tsig_key, + xfr->tsig_prev_time_signed); + } + + if (ret != KNOT_EOK) { + /* No need to check TSIG error + * here, propagate and check elsewhere.*/ + return ret; + } + + // and reset the data storage + //xfr->packet_nr = 1; + xfr->tsig_data_size = 0; + + // Extract the digest from the TSIG RDATA and store it. + if (xfr->digest_max_size < tsig_rdata_mac_length(packet->tsig_rr)) { + return KNOT_ESPACE; + } + memcpy(xfr->digest, tsig_rdata_mac(packet->tsig_rr), + tsig_rdata_mac_length(packet->tsig_rr)); + xfr->digest_size = tsig_rdata_mac_length(packet->tsig_rr); + + // Extract the time signed from the TSIG and store it + // We may rewrite the tsig_req_time_signed field + xfr->tsig_prev_time_signed = + tsig_rdata_time_signed(packet->tsig_rr); + + } + } else if (packet->tsig_rr != NULL) { + // TSIG where it should not be + return KNOT_EMALF; + } + + return KNOT_EOK; +} + +/* --------------------------- Update cleanup ------------------------------- */ + +/*! + * \brief Post update cleanup: frees data that are in the tree that will not + * be used (old tree if success, new tree if failure). + * Freed data: + * - actual data inside knot_rrs_t. (the rest is part of the node) + */ +static void rrs_list_clear(list_t *l, mm_ctx_t *mm) +{ + ptrnode_t *n; + node_t *nxt; + WALK_LIST_DELSAFE(n, nxt, *l) { + mm_free(mm, (void *)n->d); + mm_free(mm, n); + }; +} + +/*! \brief Frees additional data from single node */ +static int free_additional(zone_node_t **node, void *data) +{ +#warning - TODO: still needed? + UNUSED(data); + if ((*node)->flags & NODE_FLAGS_NONAUTH) { + // non-auth nodes have no additionals. + return KNOT_EOK; + } + + for (uint16_t i = 0; i < (*node)->rrset_count; ++i) { + struct rr_data *data = &(*node)->rrs[i]; + if (data->additional) { + free(data->additional); + data->additional = NULL; + } + } + + return KNOT_EOK; +} + +/* -------------------- Changeset application helpers ----------------------- */ + +/*! \brief Replaces rdataset of given type with a copy. */ +static int replace_rdataset_with_copy(zone_node_t *node, uint16_t type) +{ + // Find data to copy. + struct rr_data *data = NULL; + for (uint16_t i = 0; i < node->rrset_count; ++i) { + if (node->rrs[i].type == type) { + data = &node->rrs[i]; + break; + } + } + assert(data); + + // Create new data. + knot_rdataset_t *rrs = &data->rrs; + void *copy = malloc(knot_rdataset_size(rrs)); + if (copy == NULL) { + return KNOT_ENOMEM; + } + + memcpy(copy, rrs->data, knot_rdataset_size(rrs)); + + // Store new data into node RRS. + rrs->data = copy; + + return KNOT_EOK; +} + +/*! \brief Stores RR data for update cleanup. */ +static void clear_new_rrs(zone_node_t *node, uint16_t type) +{ + knot_rdataset_t *new_rrs = node_rdataset(node, type); + if (new_rrs) { + knot_rdataset_clear(new_rrs, NULL); + } +} + +/*! \brief Stores RR data for update cleanup. */ +static int add_old_data(changeset_t *chset, knot_rdata_t *old_data) +{ + if (ptrlist_add(&chset->old_data, old_data, NULL) == NULL) { + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +/*! \brief Stores RR data for update rollback. */ +static int add_new_data(changeset_t *chset, knot_rdata_t *new_data) +{ + if (ptrlist_add(&chset->new_data, new_data, NULL) == NULL) { + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +/*! \brief Returns true if given RR is present in node and can be removed. */ +static bool can_remove(const zone_node_t *node, const knot_rrset_t *rr) +{ + if (node == NULL) { + // Node does not exist, cannot remove anything. + return false; + } + const knot_rdataset_t *node_rrs = node_rdataset(node, rr->type); + if (node_rrs == NULL) { + // Node does not have this type at all. + return false; + } + + const bool compare_ttls = false; + for (uint16_t i = 0; i < rr->rrs.rr_count; ++i) { + knot_rdata_t *rr_cmp = knot_rdataset_at(&rr->rrs, i); + if (knot_rdataset_member(node_rrs, rr_cmp, compare_ttls)) { + // At least one RR matches. + return true; + } + } + + // Node does have the type, but no RRs match. + return false; +} + +/*! \brief Removes single RR from zone contents. */ +static int remove_rr(zone_node_t *node, const knot_rrset_t *rr, + changeset_t *chset) +{ + knot_rrset_t removed_rrset = node_rrset(node, rr->type); + knot_rdata_t *old_data = removed_rrset.rrs.data; + int ret = replace_rdataset_with_copy(node, rr->type); + if (ret != KNOT_EOK) { + return ret; + } + + // Store old data for cleanup. + ret = add_old_data(chset, old_data); + if (ret != KNOT_EOK) { + clear_new_rrs(node, rr->type); + return ret; + } + + knot_rdataset_t *changed_rrs = node_rdataset(node, rr->type); + // Subtract changeset RRS from node RRS. + ret = knot_rdataset_subtract(changed_rrs, &rr->rrs, NULL); + if (ret != KNOT_EOK) { + clear_new_rrs(node, rr->type); + return ret; + } + + if (changed_rrs->rr_count > 0) { + // Subtraction left some data in RRSet, store it for rollback. + ret = add_new_data(chset, changed_rrs->data); + if (ret != KNOT_EOK) { + knot_rdataset_clear(changed_rrs, NULL); + return ret; + } + } else { + // RRSet is empty now, remove it from node, all data freed. + node_remove_rdataset(node, rr->type); + } + + return KNOT_EOK; +} + +/*! \brief Removes all RRs from changeset from zone contents. */ +static int apply_remove(zone_contents_t *contents, changeset_t *chset) +{ + knot_rr_ln_t *rr_node = NULL; + WALK_LIST(rr_node, chset->remove) { + const knot_rrset_t *rr = rr_node->rr; + + // Find node for this owner + zone_node_t *node = zone_contents_find_node_for_rr(contents, + rr); + if (!can_remove(node, rr)) { + // Nothing to remove from, skip. + continue; + } + + int ret = remove_rr(node, rr, chset); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +/*! \brief Adds a single RR into zone contents. */ +static int add_rr(zone_node_t *node, const knot_rrset_t *rr, + changeset_t *chset, bool master) +{ + knot_rrset_t changed_rrset = node_rrset(node, rr->type); + if (!knot_rrset_empty(&changed_rrset)) { + // Modifying existing RRSet. + knot_rdata_t *old_data = changed_rrset.rrs.data; + int ret = replace_rdataset_with_copy(node, rr->type); + if (ret != KNOT_EOK) { + return ret; + } + + // Store old RRS for cleanup. + ret = add_old_data(chset, old_data); + if (ret != KNOT_EOK) { + clear_new_rrs(node, rr->type); + return ret; + } + } + + // Insert new RR to RRSet, data will be copied. + int ret = node_add_rrset(node, rr); + if (ret == KNOT_EOK || ret == KNOT_ETTL) { + // RR added, store for possible rollback. + knot_rdataset_t *rrs = node_rdataset(node, rr->type); + int data_ret = add_new_data(chset, rrs->data); + if (data_ret != KNOT_EOK) { + knot_rdataset_clear(rrs, NULL); + return data_ret; + } + + if (ret == KNOT_ETTL) { + // Handle possible TTL errors. + log_ttl_error(node, rr); + if (!master) { + // TTL errors fatal only for master. + return KNOT_EOK; + } + } + } + + return ret; +} + +/*! \brief Adds all RRs from changeset into zone contents. */ +static int apply_add(zone_contents_t *contents, changeset_t *chset, + bool master) +{ + knot_rr_ln_t *rr_node = NULL; + WALK_LIST(rr_node, chset->add) { + knot_rrset_t *rr = rr_node->rr; + + // Get or create node with this owner + zone_node_t *node = zone_contents_get_node_for_rr(contents, rr); + if (node == NULL) { + return KNOT_ENOMEM; + } + + int ret = add_rr(node, rr, chset, master); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +/*! \brief Replace old SOA with a new one. */ +static int apply_replace_soa(zone_contents_t *contents, changeset_t *chset) +{ + assert(chset->soa_from); + int ret = remove_rr(contents->apex, chset->soa_from, chset); + if (ret != KNOT_EOK) { + return ret; + } + + assert(!node_rrtype_exists(contents->apex, KNOT_RRTYPE_SOA)); + + return add_rr(contents->apex, chset->soa_to, chset, false); +} + +/*! \brief Apply single change to zone contents structure. */ +static int apply_changeset(zone_contents_t *contents, changeset_t *chset, + bool master) +{ + /* + * Applies one changeset to the zone. Checks if the changeset may be + * applied (i.e. the origin SOA (soa_from) has the same serial as + * SOA in the zone apex. + */ + + dbg_xfrin("APPLYING CHANGESET: from serial %u to serial %u\n", + chset->serial_from, chset->serial_to); + + // check if serial matches + const knot_rdataset_t *soa = node_rdataset(contents->apex, KNOT_RRTYPE_SOA); + if (soa == NULL || knot_soa_serial(soa) != chset->serial_from) { + dbg_xfrin("SOA serials do not match!!\n"); + return KNOT_EINVAL; + } + + int ret = apply_remove(contents, chset); + if (ret != KNOT_EOK) { + return ret; + } + + ret = apply_add(contents, chset, master); + if (ret != KNOT_EOK) { + return ret; + } + + return apply_replace_soa(contents, chset); +} + +/* ------------------------- Empty node cleanup ----------------------------- */ + +/*! \brief Mark empty nodes in updated tree. */ +static int mark_empty(zone_node_t **node_p, void *data) +{ + assert(node_p && *node_p); + zone_node_t *node = *node_p; + list_t *l = (list_t *)data; + assert(data); + if (node->rrset_count == 0 && node->children == 0 && + !(node->flags & NODE_FLAGS_EMPTY)) { + /*! + * Mark this node and all parent nodes that have 0 RRSets and + * no children for removal. + */ + if (ptrlist_add(l, node, NULL) == NULL) { + return KNOT_ENOMEM; + } + node->flags |= NODE_FLAGS_EMPTY; + if (node->parent) { + if ((node->parent->flags & NODE_FLAGS_WILDCARD_CHILD) + && knot_dname_is_wildcard(node->owner)) { + node->parent->flags &= ~NODE_FLAGS_WILDCARD_CHILD; + } + node->parent->children--; + // Recurse using the parent node + return mark_empty(&node->parent, data); + } + } + + return KNOT_EOK; +} + +/*! \brief Removes node that were previously marked as empty. */ +static int remove_empty_nodes(zone_contents_t *z) +{ + list_t l; + init_list(&l); + // walk through the zone and select nodes to be removed + int ret = knot_zone_tree_apply(z->nodes, mark_empty, &l); + if (ret != KNOT_EOK) { + return ret; + } + + ptrnode_t *n = NULL; + node_t *nxt = NULL; + WALK_LIST_DELSAFE(n, nxt, l) { + zone_node_t *node = (zone_node_t *)n->d; + ret = zone_contents_remove_node(z, node->owner); + if (ret != KNOT_EOK) { + return ret; + } + node_free(&node); + free(n); + } + + init_list(&l); + // Do the same with NSEC3 nodes. + ret = knot_zone_tree_apply(z->nsec3_nodes, mark_empty, &l); + if (ret != KNOT_EOK) { + return ret; + } + + WALK_LIST_DELSAFE(n, nxt, l) { + zone_node_t *node = (zone_node_t *)n->d; + ret = zone_contents_remove_nsec3_node(z, node->owner); + if (ret != KNOT_EOK) { + return ret; + } + node_free(&node); + free(n); + } + + return KNOT_EOK; +} + +/* --------------------- Zone copy and finalization ------------------------- */ + +/*! \brief Creates a shallow zone contents copy. */ +static int prepare_zone_copy(zone_contents_t *old_contents, + zone_contents_t **new_contents) +{ + if (old_contents == NULL || new_contents == NULL) { + return KNOT_EINVAL; + } + + /* + * Create a shallow copy of the zone, so that the structures may be + * updated. + * + * This will create new zone contents structures (normal nodes' tree, + * NSEC3 tree), and copy all nodes. + * The data in the nodes (RRSets) remain the same though. + */ + zone_contents_t *contents_copy = NULL; + int ret = zone_contents_shallow_copy(old_contents, &contents_copy); + if (ret != KNOT_EOK) { + return ret; + } + + *new_contents = contents_copy; + + return KNOT_EOK; +} + +/*! \brief Removes empty nodes from updated zone a does zone adjusting. */ +static int finalize_updated_zone(zone_contents_t *contents_copy, + bool set_nsec3_names) +{ + if (contents_copy == NULL) { + return KNOT_EINVAL; + } + + int ret = remove_empty_nodes(contents_copy); + if (ret != KNOT_EOK) { + return ret; + } + + if (set_nsec3_names) { + ret = zone_contents_adjust_full(contents_copy, NULL, NULL); + } else { + ret = zone_contents_adjust_pointers(contents_copy); + } + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +/* ------------------------------- API -------------------------------------- */ + +int apply_changesets(zone_t *zone, changesets_t *chsets, + zone_contents_t **new_contents) +{ + if (zone == NULL || changesets_empty(chsets) || new_contents == NULL) { + return KNOT_EINVAL; + } + + zone_contents_t *old_contents = zone->contents; + if (!old_contents) { + return KNOT_EINVAL; + } + + zone_contents_t *contents_copy = NULL; + int ret = prepare_zone_copy(old_contents, &contents_copy); + if (ret != KNOT_EOK) { + return ret; + } + + /* + * Apply the changesets. + */ + changeset_t *set = NULL; + const bool master = (zone_master(zone) == NULL); + WALK_LIST(set, chsets->sets) { + ret = apply_changeset(contents_copy, set, master); + if (ret != KNOT_EOK) { + update_rollback(chsets, &contents_copy); + return ret; + } + } + + assert(contents_copy->apex != NULL); + + ret = finalize_updated_zone(contents_copy, true); + if (ret != KNOT_EOK) { + update_rollback(chsets, &contents_copy); + return ret; + } + + *new_contents = contents_copy; + + return KNOT_EOK; +} + +int apply_changesets_directly(zone_contents_t *contents, changesets_t *chsets) +{ + if (contents == NULL || chsets == NULL) { + return KNOT_EINVAL; + } + + changeset_t *set = NULL; + WALK_LIST(set, chsets->sets) { + const bool master = true; // Only DNSSEC changesets are applied directly. + int ret = apply_changeset(contents, set, master); + if (ret != KNOT_EOK) { + update_cleanup(chsets); + return ret; + } + } + + int ret = finalize_updated_zone(contents, true); + + /* + * HACK: Cleanup for successful update is used for both success and fail + * when modifying the zone directly, will fix in new zone API. + */ + update_cleanup(chsets); + return ret; +} + +void update_cleanup(changesets_t *chgs) +{ + if (chgs == NULL) { + return; + } + + changeset_t *change = NULL; + WALK_LIST(change, chgs->sets) { + // Delete old RR data + rrs_list_clear(&change->old_data, NULL); + init_list(&change->old_data); + // Keep new RR data + ptrlist_free(&change->new_data, NULL); + init_list(&change->new_data); + }; +} + +void update_rollback(changesets_t *chgs, zone_contents_t **new_contents) +{ + if (chgs != NULL) { + changeset_t *change = NULL; + WALK_LIST(change, chgs->sets) { + // Delete new RR data + rrs_list_clear(&change->new_data, NULL); + init_list(&change->new_data); + // Keep old RR data + ptrlist_free(&change->old_data, NULL); + init_list(&change->old_data); + }; + } + if (new_contents) { + update_free_old_zone(new_contents); + } +} + +void update_free_old_zone(zone_contents_t **contents) +{ + /* + * Free the zone tree, but only the structure + * (nodes are already destroyed) and free additional arrays. + */ + knot_zone_tree_apply((*contents)->nodes, free_additional, NULL); + knot_zone_tree_deep_free(&(*contents)->nodes); + knot_zone_tree_deep_free(&(*contents)->nsec3_nodes); + + knot_nsec3param_free(&(*contents)->nsec3_params); + + free(*contents); + *contents = NULL; +} + diff --git a/src/knot/updates/apply.h b/src/knot/updates/apply.h new file mode 100644 index 0000000000000000000000000000000000000000..3e9ec88f8ef88604b342c89604236ed25a5f45da --- /dev/null +++ b/src/knot/updates/apply.h @@ -0,0 +1,98 @@ +/*! + * \file apply.h + * + * \author Lubos Slovak <lubos.slovak@nic.cz> + * \author Jan Kadlec <jan.kadlec@nic.cz> + * + * \brief Changesets application and update helpers. + * + * \addtogroup xfr + * @{ + */ +/* 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <string.h> + +#include "knot/zone/zone.h" +#include "knot/server/xfr-handler.h" +#include "knot/updates/changesets.h" + +/*! + * \brief Checks if a zone transfer is required by comparing the zone's SOA with + * the one received from master server. + * + * \param zone Zone to check. + * \param soa_response Response to SOA query received from master server. + * + * \retval < 0 if an error occured. + * \retval 1 if the transfer is needed. + * \retval 0 if the transfer is not needed. + */ +int xfrin_transfer_needed(const zone_contents_t *zone, + knot_pkt_t *soa_response); + +/*! + * \brief Applies changesets *with* zone shallow copy. + * + * \param zone Zone to be updated. + * \param chsets Changes to be made. + * \param new_contents New zone will be returned using this arg. + * + * \return KNOT_E* + */ +int apply_changesets(zone_t *zone, changesets_t *chsets, + zone_contents_t **new_contents); + +/*! + * \brief Applies changesets directly to the zone, without copying it. + * + * \param contents Zone contents to apply the changesets to. Will be modified. + * \param chsets Changesets to be applied to the zone. + * + * \retval KNOT_EOK if successful. + * \retval KNOT_EINVAL if given one of the arguments is NULL. + * \return Other error code if the application went wrong. + */ +int apply_changesets_directly(zone_contents_t *contents, + changesets_t *chsets); + +/*! + * \brief Cleanups successful update. (IXFR, DNSSEC, DDNS). + * \param chgs Changesets used to create the update. + */ +void update_cleanup(changesets_t *chgs); + +/*! + * \brief Rollbacks failed update (IXFR, DNSSEC, DDNS). + * + * \param chgs Changesets used to create the update. + * \param new_contents Created zone contents. + */ +void update_rollback(changesets_t *chgs, zone_contents_t **new_contents); + +/*! + * \brief Frees old zone contents - i.e. contents that were used to create the + * shallow copy, but are now obsolete. + * \note Exported because of update.c, zone.c. + * \param contents Contents to free. + */ +void update_free_old_zone(zone_contents_t **contents); + +/*! @} */ diff --git a/src/knot/updates/changesets.c b/src/knot/updates/changesets.c index b4b6ac01438571e34291036f76805d5a5ddce849..bd6669398a3f437f2d24acb205b813fc4b71532e 100644 --- a/src/knot/updates/changesets.c +++ b/src/knot/updates/changesets.c @@ -27,17 +27,17 @@ #include "libknot/rdata/soa.h" #include "common/debug.h" -static int knot_changesets_init(knot_changesets_t *changesets) +static int knot_changesets_init(changesets_t *changesets) { if (changesets == NULL) { return KNOT_EINVAL; } // Create new changesets structure - memset(changesets, 0, sizeof(knot_changesets_t)); + memset(changesets, 0, sizeof(changesets_t)); // Initialize memory context for changesets (xmalloc'd) - struct mempool *chs_pool = mp_new(sizeof(knot_changeset_t)); + struct mempool *chs_pool = mp_new(sizeof(changeset_t)); changesets->mmc_chs.ctx = chs_pool; changesets->mmc_chs.alloc = (mm_alloc_t)mp_alloc; changesets->mmc_chs.free = NULL; @@ -54,18 +54,18 @@ static int knot_changesets_init(knot_changesets_t *changesets) return KNOT_EOK; } -knot_changesets_t *knot_changesets_create(unsigned count) +changesets_t *changesets_create(unsigned count) { - knot_changesets_t *ch = malloc(sizeof(knot_changesets_t)); + changesets_t *ch = malloc(sizeof(changesets_t)); int ret = knot_changesets_init(ch); if (ret != KNOT_EOK) { return NULL; } for (unsigned i = 0; i < count; ++i) { - knot_changeset_t *change = knot_changesets_create_changeset(ch); + changeset_t *change = changesets_create_changeset(ch); if (change == NULL) { - knot_changesets_free(&ch); + changesets_free(&ch, NULL); return NULL; } } @@ -73,19 +73,19 @@ knot_changesets_t *knot_changesets_create(unsigned count) return ch; } -knot_changeset_t *knot_changesets_create_changeset(knot_changesets_t *ch) +changeset_t *changesets_create_changeset(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)); + changeset_t *set = ch->mmc_chs.alloc(ch->mmc_chs.ctx, + sizeof(changeset_t)); if (set == NULL) { return NULL; } - memset(set, 0, sizeof(knot_changeset_t)); + memset(set, 0, sizeof(changeset_t)); // Init set's memory context (Allocator from changests structure is used) set->mem_ctx = ch->mmc_rr; @@ -106,27 +106,27 @@ knot_changeset_t *knot_changesets_create_changeset(knot_changesets_t *ch) return set; } -knot_changeset_t *knot_changesets_get_last(const knot_changesets_t *chs) +changeset_t *changesets_get_last(const changesets_t *chs) { if (chs == NULL || EMPTY_LIST(chs->sets)) { return NULL; } - return (knot_changeset_t *)(TAIL(chs->sets)); + return (changeset_t *)(TAIL(chs->sets)); } -bool knot_changesets_empty(const knot_changesets_t *chs) +bool changesets_empty(const changesets_t *chs) { - knot_changeset_t *last = knot_changesets_get_last(chs); + changeset_t *last = changesets_get_last(chs); if (last == NULL) { return true; } - return knot_changeset_is_empty(last); + return changeset_is_empty(last); } -int knot_changeset_add_rrset(knot_changeset_t *chgs, knot_rrset_t *rrset, - knot_changeset_part_t part) +int changeset_add_rrset(changeset_t *chgs, knot_rrset_t *rrset, + changeset_part_t part) { // Create wrapper node for list knot_rr_ln_t *rr_node = @@ -138,7 +138,7 @@ int knot_changeset_add_rrset(knot_changeset_t *chgs, knot_rrset_t *rrset, } rr_node->rr = rrset; - if (part == KNOT_CHANGESET_ADD) { + if (part == CHANGESET_ADD) { add_tail(&chgs->add, (node_t *)rr_node); } else { add_tail(&chgs->remove, (node_t *)rr_node); @@ -154,15 +154,15 @@ static void knot_changeset_store_soa(knot_rrset_t **chg_soa, *chg_serial = knot_soa_serial(&soa->rrs); } -void knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa, - knot_changeset_part_t part) +void changeset_add_soa(changeset_t *changeset, knot_rrset_t *soa, + changeset_part_t part) { switch (part) { - case KNOT_CHANGESET_ADD: + case CHANGESET_ADD: knot_changeset_store_soa(&changeset->soa_to, &changeset->serial_to, soa); break; - case KNOT_CHANGESET_REMOVE: + case CHANGESET_REMOVE: knot_changeset_store_soa(&changeset->soa_from, &changeset->serial_from, soa); break; @@ -171,7 +171,7 @@ void knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa, } } -bool knot_changeset_is_empty(const knot_changeset_t *changeset) +bool changeset_is_empty(const changeset_t *changeset) { if (changeset == NULL) { return true; @@ -181,17 +181,17 @@ bool knot_changeset_is_empty(const knot_changeset_t *changeset) EMPTY_LIST(changeset->add) && EMPTY_LIST(changeset->remove)); } -size_t knot_changeset_size(const knot_changeset_t *changeset) +size_t changeset_size(const changeset_t *changeset) { - if (!changeset || knot_changeset_is_empty(changeset)) { + if (!changeset || changeset_is_empty(changeset)) { return 0; } return list_size(&changeset->add) + list_size(&changeset->remove); } -int knot_changeset_apply(knot_changeset_t *changeset, - knot_changeset_part_t part, +int changeset_apply(changeset_t *changeset, + changeset_part_t part, int (*func)(knot_rrset_t *, void *), void *data) { if (changeset == NULL || func == NULL) { @@ -199,14 +199,14 @@ int knot_changeset_apply(knot_changeset_t *changeset, } knot_rr_ln_t *rr_node = NULL; - if (part == KNOT_CHANGESET_ADD) { + if (part == 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) { + } else if (part == CHANGESET_REMOVE) { WALK_LIST(rr_node, changeset->remove) { int res = func(rr_node->rr, data); if (res != KNOT_EOK) { @@ -218,7 +218,7 @@ int knot_changeset_apply(knot_changeset_t *changeset, return KNOT_EOK; } -int knot_changeset_merge(knot_changeset_t *ch1, knot_changeset_t *ch2) +int changeset_merge(changeset_t *ch1, changeset_t *ch2) { if (ch1 == NULL || ch2 == NULL || ch1->data != NULL || ch2->data != NULL) { return KNOT_EINVAL; @@ -238,7 +238,7 @@ int knot_changeset_merge(knot_changeset_t *ch1, knot_changeset_t *ch2) return KNOT_EOK; } -static void knot_free_changeset(knot_changeset_t *changeset) +static void knot_free_changeset(changeset_t *changeset) { if (changeset == NULL) { return; @@ -260,10 +260,10 @@ static void knot_free_changeset(knot_changeset_t *changeset) free(changeset->data); } -static void knot_changesets_deinit(knot_changesets_t *changesets) +static void knot_changesets_deinit(changesets_t *changesets, mm_ctx_t *rr_mm) { if (!EMPTY_LIST(changesets->sets)) { - knot_changeset_t *chg = NULL; + changeset_t *chg = NULL; WALK_LIST(chg, changesets->sets) { knot_free_changeset(chg); } @@ -274,28 +274,28 @@ static void knot_changesets_deinit(knot_changesets_t *changesets) // Free pool with RRs in sets / changes mp_delete(changesets->mmc_rr.ctx); - knot_rrset_free(&changesets->first_soa, NULL); + knot_rrset_free(&changesets->first_soa, rr_mm); } -void knot_changesets_free(knot_changesets_t **changesets) +void changesets_free(changesets_t **changesets, mm_ctx_t *rr_mm) { if (changesets == NULL || *changesets == NULL) { return; } - knot_changesets_deinit(*changesets); + knot_changesets_deinit(*changesets, rr_mm); free(*changesets); *changesets = NULL; } -int knot_changesets_clear(knot_changesets_t *changesets) +int changesets_clear(changesets_t *changesets, mm_ctx_t *rr_mm) { if (changesets == NULL) { return KNOT_EINVAL; } - knot_changesets_deinit(changesets); + knot_changesets_deinit(changesets, rr_mm); return knot_changesets_init(changesets); } diff --git a/src/knot/updates/changesets.h b/src/knot/updates/changesets.h index 5d3f9bab4f8d4000e23d5e9a5c001e266124181b..0cc872db0f410beeb103a2bffd6766902154dbab 100644 --- a/src/knot/updates/changesets.h +++ b/src/knot/updates/changesets.h @@ -34,15 +34,8 @@ /*----------------------------------------------------------------------------*/ -/*! \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_TYPE_DNSSEC = 1 << 2 -} knot_changeset_flag_t; - /*! \brief One changeset received from wire, with parsed RRs. */ -typedef struct knot_changeset { +typedef struct changeset { node_t n; /*!< List node. */ mm_ctx_t mem_ctx; /*!< Memory context */ knot_rrset_t *soa_from; /*!< Start SOA. */ @@ -53,10 +46,9 @@ typedef struct knot_changeset { size_t size; /*!< Size of serialized changeset. */ uint32_t serial_from; /*!< SOA start serial. */ uint32_t serial_to; /*!< SOA destination serial. */ - uint32_t flags; /*!< DDNS / IXFR flags. */ list_t old_data; /*!< Old data, to be freed after succesfull update. */ list_t new_data; /*!< New data, to be freed after failed update. */ -} knot_changeset_t; +} changeset_t; /*----------------------------------------------------------------------------*/ @@ -79,14 +71,14 @@ typedef struct { size_t count; /*!< Changeset count. */ knot_rrset_t *first_soa; /*!< First received SOA. */ uint32_t flags; /*!< DDNS / IXFR flags. */ -} knot_changesets_t; +} changesets_t; /*----------------------------------------------------------------------------*/ typedef enum { - KNOT_CHANGESET_ADD, - KNOT_CHANGESET_REMOVE -} knot_changeset_part_t; + CHANGESET_ADD, + CHANGESET_REMOVE +} changeset_part_t; /*----------------------------------------------------------------------------*/ @@ -99,17 +91,10 @@ typedef enum { * \retval Created structure on success. * \retval NULL on failure. */ -knot_changesets_t *knot_changesets_create(unsigned count); - -/*! - * \brief Frees the 'changesets' structure, including all its internal data. - * - * \param changesets Double pointer to changesets structure to be freed. - */ -void knot_changesets_free(knot_changesets_t **changesets); +changesets_t *changesets_create(unsigned count); /*! \brief Reinitialize changesets structure. */ -int knot_changesets_clear(knot_changesets_t *changesets); +int changesets_clear(changesets_t *changesets, mm_ctx_t *rr_mm); /*! * \brief Creates new changeset structure and returns it to caller. @@ -120,7 +105,7 @@ int knot_changesets_clear(knot_changesets_t *changesets); * \retval Created structure on success. * \retval NULL on failure. */ -knot_changeset_t *knot_changesets_create_changeset(knot_changesets_t *ch); +changeset_t *changesets_create_changeset(changesets_t *ch); /*! * \brief Gets last changesets from from structure's list. @@ -130,10 +115,10 @@ knot_changeset_t *knot_changesets_create_changeset(knot_changesets_t *ch); * \retval Last changeset on success. * \retval NULL on failure. */ -knot_changeset_t *knot_changesets_get_last(const knot_changesets_t *ch); +changeset_t *changesets_get_last(const changesets_t *ch); /*! \brief Return true if changesets are empty. */ -bool knot_changesets_empty(const knot_changesets_t *chs); +bool changesets_empty(const changesets_t *chs); /*! * \brief Add RRSet to changeset. RRSet is either inserted to 'add' or to @@ -146,8 +131,8 @@ bool knot_changesets_empty(const knot_changesets_t *chs); * \retval KNOT_EOK on success. * \retval Error code on failure. */ -int knot_changeset_add_rrset(knot_changeset_t *chgs, - knot_rrset_t *rrset, knot_changeset_part_t part); +int changeset_add_rrset(changeset_t *chgs, + knot_rrset_t *rrset, changeset_part_t part); /*! * \brief Adds a source/destination SOA RRSet to changeset. @@ -156,8 +141,8 @@ int knot_changeset_add_rrset(knot_changeset_t *chgs, * \param soa SOA RRSet to be stored to changeset. * \param part To which part we store SOA (from = REMOVE, add = TO) */ -void knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa, - knot_changeset_part_t part); +void changeset_add_soa(changeset_t *changeset, knot_rrset_t *soa, + changeset_part_t part); /*! * \brief Checks whether changeset is empty. @@ -170,7 +155,7 @@ void knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa, * \retval true if changeset is empty. * \retval false if changeset is not empty. */ -bool knot_changeset_is_empty(const knot_changeset_t *changeset); +bool changeset_is_empty(const changeset_t *changeset); /*! * \brief Get number of changes (additions and removals) in the changeset. @@ -179,7 +164,7 @@ bool knot_changeset_is_empty(const knot_changeset_t *changeset); * * \return Number of changes in the changeset. */ -size_t knot_changeset_size(const knot_changeset_t *changeset); +size_t changeset_size(const changeset_t *changeset); /*! * \brief Apply given function to all RRSets in one part of the changeset. @@ -197,16 +182,17 @@ size_t knot_changeset_size(const knot_changeset_t *changeset); * \retval KNOT_EINVAL if \a changeset or \a func is NULL. * \retval Other error code if the applied function failed. */ -int knot_changeset_apply(knot_changeset_t *changeset, - knot_changeset_part_t part, - int (*func)(knot_rrset_t *, void *), void *data); +int changeset_apply(changeset_t *changeset, + changeset_part_t part, + int (*func)(knot_rrset_t *, void *), void *data); /*! * \brief Frees the 'changesets' structure, including all its internal data. * - * \param changesets Double pointer to changesets structure to be freed. + * \param changesets Double pointer to changesets structure to be freed. + * \param mm Memory context used to allocate RRSets. */ -void knot_changesets_free(knot_changesets_t **changesets); +void changesets_free(changesets_t **changesets, mm_ctx_t *rr_mm); /*! * \brief Merges two changesets together, second changeset's lists are kept. @@ -221,7 +207,7 @@ void knot_changesets_free(knot_changesets_t **changesets); * \retval KNOT_EOK on success. * \retval Error code on failure. */ -int knot_changeset_merge(knot_changeset_t *ch1, knot_changeset_t *ch2); +int changeset_merge(changeset_t *ch1, changeset_t *ch2); #endif /* _KNOT_CHANGESETS_H_ */ diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c index 066a6d70e8ea534cfd6e78c9f12f8794db1fba47..c2a8843732895ad453b0bbb8431c4e7cc56f8d2e 100644 --- a/src/knot/updates/ddns.c +++ b/src/knot/updates/ddns.c @@ -20,7 +20,7 @@ #include "knot/updates/ddns.h" #include "knot/updates/changesets.h" -#include "knot/updates/xfr-in.h" +#include "knot/updates/apply.h" #include "knot/zone/semantic-check.h" #include "common/debug.h" #include "libknot/packet/pkt.h" @@ -31,6 +31,8 @@ #include "common/descriptor.h" #include "common/lists.h" +#warning merge with update.c + /* ----------------------------- prereq check ------------------------------- */ /*!< \brief Clears prereq RRSet list. */ @@ -279,14 +281,14 @@ static int process_prereq(const knot_rrset_t *rrset, uint16_t qclass, }; /*!< \brief Checks whether RR was already removed. */ -static bool removed_rr(const knot_changeset_t *changeset, const knot_rrset_t *rr) +static bool removed_rr(const changeset_t *changeset, const knot_rrset_t *rr) { LIST_MATCH(changeset->remove, knot_rrset_equal(rr, list_rr, KNOT_RRSET_COMPARE_WHOLE)); } /*!< \brief Checks whether any CNAME RR under dname was added. */ -static bool cname_added(const knot_changeset_t *changeset, const knot_dname_t *d) +static bool cname_added(const changeset_t *changeset, const knot_dname_t *d) { knot_rrset_t mock_cname; knot_rrset_init(&mock_cname, (knot_dname_t *)d, @@ -296,7 +298,7 @@ static bool cname_added(const knot_changeset_t *changeset, const knot_dname_t *d } /*!< \brief Checks whether any RR under given name was added. */ -static bool name_added(const knot_changeset_t *changeset, const knot_dname_t *d) +static bool name_added(const changeset_t *changeset, const knot_dname_t *d) { LIST_MATCH(changeset->add, knot_dname_is_equal(d, list_rr->owner)); } @@ -360,7 +362,7 @@ 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 zone_node_t *node, knot_dname_t *owner, - const knot_changeset_t *changeset) + const changeset_t *changeset) { if (node == NULL && name_added(changeset, owner)) { // Node created in update. @@ -413,7 +415,7 @@ static bool node_contains_rr(const zone_node_t *node, /*!< \brief Returns true if CNAME is in this node. */ static bool adding_to_cname(const knot_dname_t *owner, const zone_node_t *node, - knot_changeset_t *changeset) + changeset_t *changeset) { if (cname_added(changeset, owner)) { // Added a CNAME in this update. @@ -455,7 +457,7 @@ static bool skip_soa(const knot_rrset_t *rr, int64_t sn) /* ---------------------- changeset manipulation ---------------------------- */ /*!< \brief Checks whether record should be added or replaced. */ -static bool skip_record_addition(knot_changeset_t *changeset, +static bool skip_record_addition(changeset_t *changeset, knot_rrset_t *rr) { knot_rr_ln_t *rr_node = NULL; @@ -477,7 +479,7 @@ static bool skip_record_addition(knot_changeset_t *changeset, } /*!< \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, +static int add_rr_to_chgset(const knot_rrset_t *rr, changeset_t *changeset, int *apex_ns_rem) { knot_rrset_t *rr_copy = knot_rrset_copy(rr, NULL); @@ -496,11 +498,11 @@ static int add_rr_to_chgset(const knot_rrset_t *rr, knot_changeset_t *changeset, (*apex_ns_rem)--; } - return knot_changeset_add_rrset(changeset, rr_copy, KNOT_CHANGESET_ADD); + return changeset_add_rrset(changeset, rr_copy, CHANGESET_ADD); } /*!< \brief Checks whether record should be removed (duplicate check). */ -static bool skip_record_removal(knot_changeset_t *changeset, knot_rrset_t *rr) +static bool skip_record_removal(changeset_t *changeset, knot_rrset_t *rr) { knot_rr_ln_t *rr_node = NULL; WALK_LIST(rr_node, changeset->remove) { @@ -516,7 +518,7 @@ static bool skip_record_removal(knot_changeset_t *changeset, knot_rrset_t *rr) } /*!< \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, +static int rem_rr_to_chgset(const knot_rrset_t *rr, changeset_t *changeset, int *apex_ns_rem) { knot_rrset_t *rr_copy = knot_rrset_copy(rr, NULL); @@ -535,12 +537,12 @@ static int rem_rr_to_chgset(const knot_rrset_t *rr, knot_changeset_t *changeset, (*apex_ns_rem)++; } - return knot_changeset_add_rrset(changeset, rr_copy, KNOT_CHANGESET_REMOVE); + return changeset_add_rrset(changeset, rr_copy, 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, + changeset_t *changeset, int *apex_ns_rem) { knot_rrset_t rr; @@ -568,7 +570,7 @@ static int rem_rrset_to_chgset(const knot_rrset_t *rrset, /*!< \brief Processes CNAME addition (replace or ignore) */ static int process_add_cname(const zone_node_t *node, const knot_rrset_t *rr, - knot_changeset_t *changeset) + changeset_t *changeset) { knot_rrset_t cname = node_rrset(node, KNOT_RRTYPE_CNAME); if (!knot_rrset_empty(&cname)) { @@ -595,7 +597,7 @@ static int process_add_cname(const zone_node_t *node, /*!< \brief Processes CNAME addition (ignore when not removed, or non-apex) */ static int process_add_nsec3param(const zone_node_t *node, const knot_rrset_t *rr, - knot_changeset_t *changeset) + changeset_t *changeset) { if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) { // Ignore non-apex additions @@ -625,7 +627,7 @@ static int process_add_nsec3param(const zone_node_t *node, */ static int process_add_soa(const zone_node_t *node, const knot_rrset_t *rr, - knot_changeset_t *changeset) + changeset_t *changeset) { if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) { // Adding SOA to non-apex node, ignore @@ -649,14 +651,14 @@ static int process_add_soa(const zone_node_t *node, return KNOT_ENOMEM; } - knot_changeset_add_soa(changeset, soa_cpy, KNOT_CHANGESET_ADD); + changeset_add_soa(changeset, soa_cpy, CHANGESET_ADD); return KNOT_EOK; } /*!< \brief Adds normal RR, ignores when CNAME exists in node. */ static int process_add_normal(const zone_node_t *node, const knot_rrset_t *rr, - knot_changeset_t *changeset, + changeset_t *changeset, int *apex_ns_rem) { if (adding_to_cname(rr->owner, node, changeset)) { @@ -679,7 +681,7 @@ static int process_add_normal(const zone_node_t *node, /*!< \brief Decides what to do with RR addition. */ static int process_add(const knot_rrset_t *rr, const zone_node_t *node, - knot_changeset_t *changeset, + changeset_t *changeset, int *apex_ns_rem) { switch(rr->type) { @@ -699,7 +701,7 @@ static int process_add(const knot_rrset_t *rr, /*!< \brief Removes single RR from zone. */ static int process_rem_rr(const knot_rrset_t *rr, const zone_node_t *node, - knot_changeset_t *changeset, + changeset_t *changeset, int *apex_ns_rem) { const bool apex_ns = node_rrtype_exists(node, KNOT_RRTYPE_SOA) && @@ -751,7 +753,7 @@ static int process_rem_rr(const knot_rrset_t *rr, /*!< \brief Removes RRSet from zone. */ static int process_rem_rrset(const knot_rrset_t *rrset, const zone_node_t *node, - knot_changeset_t *changeset) + changeset_t *changeset) { if (rrset->type == KNOT_RRTYPE_SOA || knot_rrtype_is_ddns_forbidden(rrset->type)) { @@ -782,7 +784,7 @@ static int process_rem_rrset(const knot_rrset_t *rrset, /*!< \brief Removes node from zone. */ static int process_rem_node(const knot_rrset_t *rr, - const zone_node_t *node, knot_changeset_t *changeset) + const zone_node_t *node, changeset_t *changeset) { // Remove all previously added records with given owner from changeset remove_owner_from_list(&changeset->add, rr->owner); @@ -806,7 +808,7 @@ static int process_rem_node(const knot_rrset_t *rr, /*!< \brief Decides what to with removal. */ static int process_remove(const knot_rrset_t *rr, const zone_node_t *node, - knot_changeset_t *changeset, + changeset_t *changeset, int *apex_ns_rem) { if (is_rr_removal(rr)) { @@ -901,7 +903,7 @@ static int check_update(const knot_rrset_t *rrset, const knot_pkt_t *query, /*!< \brief Checks RR and decides what to do with it. */ static int process_rr(const knot_rrset_t *rr, const zone_contents_t *zone, - knot_changeset_t *changeset, int *apex_ns_rem) + changeset_t *changeset, int *apex_ns_rem) { const zone_node_t *node = zone_contents_find_node(zone, rr->owner); @@ -963,7 +965,7 @@ int ddns_process_prereqs(const knot_pkt_t *query, const zone_contents_t *zone, } int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, - knot_changeset_t *changeset, uint16_t *rcode) + changeset_t *changeset, uint16_t *rcode) { if (zone == NULL || query == NULL || changeset == NULL || rcode == NULL) { return KNOT_EINVAL; @@ -975,7 +977,7 @@ int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, return KNOT_ENOMEM; } - knot_changeset_add_soa(changeset, soa_begin, KNOT_CHANGESET_REMOVE); + changeset_add_soa(changeset, soa_begin, CHANGESET_REMOVE); int64_t sn_old = zone_contents_serial(zone->contents); @@ -1005,7 +1007,7 @@ int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, if (changeset->soa_to == NULL) { // No SOA in the update, create according to current policy - if (knot_changeset_is_empty(changeset)) { + if (changeset_is_empty(changeset)) { // No change, no new SOA return KNOT_EOK; } @@ -1019,7 +1021,7 @@ int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, int new_serial = zone_contents_next_serial(zone->contents, zone->conf->serial_policy); knot_soa_serial_set(&soa_cpy->rrs, new_serial); - knot_changeset_add_soa(changeset, soa_cpy, KNOT_CHANGESET_ADD); + changeset_add_soa(changeset, soa_cpy, CHANGESET_ADD); } return KNOT_EOK; diff --git a/src/knot/updates/ddns.h b/src/knot/updates/ddns.h index 9b89de05b2141ce5773949f5fe94b5c6cb358688..f7b1a3c13b702cdec824f0ff290bdf85edbb2732 100644 --- a/src/knot/updates/ddns.h +++ b/src/knot/updates/ddns.h @@ -57,7 +57,7 @@ int ddns_process_prereqs(const knot_pkt_t *query, const zone_contents_t *zone, * \return KNOT_E* */ int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, - knot_changeset_t *changeset, uint16_t *rcode); + changeset_t *changeset, uint16_t *rcode); #endif /* _KNOT_DDNS_H_ */ diff --git a/src/knot/updates/xfr-in.c b/src/knot/updates/xfr-in.c deleted file mode 100644 index 511509c1ccb9392a9d39e38148866db00c46054f..0000000000000000000000000000000000000000 --- a/src/knot/updates/xfr-in.c +++ /dev/null @@ -1,1211 +0,0 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <assert.h> -#include <urcu.h> - -#include "knot/server/journal.h" - -#include "knot/updates/xfr-in.h" - -#include "libknot/packet/wire.h" -#include "common/debug.h" -#include "libknot/packet/pkt.h" -#include "libknot/dname.h" -#include "knot/zone/zone.h" -#include "knot/zone/zonefile.h" -#include "knot/dnssec/zone-nsec.h" -#include "knot/dnssec/zone-sign.h" -#include "libknot/dnssec/random.h" -#include "libknot/common.h" -#include "knot/updates/changesets.h" -#include "libknot/rdata/tsig.h" -#include "libknot/tsig-op.h" -#include "knot/zone/semantic-check.h" -#include "common/lists.h" -#include "common/descriptor.h" -#include "libknot/util/utils.h" -#include "libknot/rdata/soa.h" -#include "knot/nameserver/axfr.h" - -#define KNOT_NS_TSIG_FREQ 100 - -/*! - * \brief Post update cleanup: frees data that are in the tree that will not - * be used (old tree if success, new tree if failure). - * Freed data: - * - actual data inside knot_rrs_t. (the rest is part of the node) - */ -static void rrs_list_clear(list_t *l, mm_ctx_t *mm) -{ - ptrnode_t *n; - node_t *nxt; - WALK_LIST_DELSAFE(n, nxt, *l) { - mm_free(mm, (void *)n->d); - mm_free(mm, n); - }; -} - -static int knot_ns_tsig_required(int packet_nr) -{ - /*! \bug This can overflow to negative numbers. Proper solution is to - * count exactly at one place for each incoming/outgoing packet - * with packet_nr = (packet_nr + 1) % FREQ and require TSIG on 0. - */ - dbg_ns_verb("ns_tsig_required(%d): %d\n", packet_nr, - (packet_nr % KNOT_NS_TSIG_FREQ == 0)); - return (packet_nr % KNOT_NS_TSIG_FREQ == 0); -} - -/*----------------------------------------------------------------------------*/ -/* API functions */ -/*----------------------------------------------------------------------------*/ - -int xfrin_transfer_needed(const zone_contents_t *zone, - knot_pkt_t *soa_response) -{ - /* - * Retrieve the local Serial - */ - const knot_rdataset_t *soa_rrs = - node_rdataset(zone->apex, KNOT_RRTYPE_SOA); - if (soa_rrs == NULL) { - char *name = knot_dname_to_str(zone->apex->owner); - dbg_xfrin("SOA RRSet missing in the zone %s!\n", name); - free(name); - return KNOT_ERROR; - } - - uint32_t local_serial = knot_soa_serial(soa_rrs); - /* - * Retrieve the remote Serial - */ - // the SOA should be the first (and only) RRSet in the response - const knot_pktsection_t *answer = knot_pkt_section(soa_response, KNOT_ANSWER); - if (answer->count < 1) { - return KNOT_EMALF; - } - knot_rrset_t soa_rr = answer->rr[0]; - if (soa_rr.type != KNOT_RRTYPE_SOA) { - return KNOT_EMALF; - } - - uint32_t remote_serial = knot_soa_serial(&soa_rr.rrs); - return (knot_serial_compare(local_serial, remote_serial) < 0); -} - -/*----------------------------------------------------------------------------*/ - -static int xfrin_check_tsig(knot_pkt_t *packet, knot_ns_xfr_t *xfr, - int tsig_req) -{ - assert(packet != NULL); - assert(xfr != NULL); - - dbg_xfrin_verb("xfrin_check_tsig(): packet nr: %d, required: %d\n", - xfr->packet_nr, tsig_req); - - /* - * If we are expecting it (i.e. xfr->prev_digest_size > 0) - * a) it should be there (first, last or each 100th packet) and it - * is not - * Then we should discard the changes and close the connection. - * b) it should be there and it is or it may not be there (other - * packets) and it is - * We validate the TSIG and reset packet number counting and - * data aggregation. - * - * If we are not expecting it (i.e. xfr->prev_digest_size <= 0) and - * it is there => it should probably be considered an error - */ - - int ret = KNOT_EOK; - if (xfr->tsig_key) { - // just append the wireformat to the TSIG data - uint8_t *wire_buf = xfr->tsig_data + xfr->tsig_data_size; - memcpy(wire_buf, packet->wire, packet->size); - xfr->tsig_data_size += packet->size; - } - - if (xfr->tsig_key) { - if (tsig_req && packet->tsig_rr == NULL) { - // TSIG missing!! - return KNOT_ENOTSIG; - } else if (packet->tsig_rr != NULL) { - // TSIG there, either required or not, process - if (xfr->packet_nr == 0) { - ret = knot_tsig_client_check(packet->tsig_rr, - xfr->tsig_data, xfr->tsig_data_size, - xfr->digest, xfr->digest_size, - xfr->tsig_key, - xfr->tsig_prev_time_signed); - } else { - ret = knot_tsig_client_check_next(packet->tsig_rr, - xfr->tsig_data, xfr->tsig_data_size, - xfr->digest, xfr->digest_size, - xfr->tsig_key, - xfr->tsig_prev_time_signed); - } - - if (ret != KNOT_EOK) { - /* No need to check TSIG error - * here, propagate and check elsewhere.*/ - return ret; - } - - // and reset the data storage - //xfr->packet_nr = 1; - xfr->tsig_data_size = 0; - - // Extract the digest from the TSIG RDATA and store it. - if (xfr->digest_max_size < tsig_rdata_mac_length(packet->tsig_rr)) { - return KNOT_ESPACE; - } - memcpy(xfr->digest, tsig_rdata_mac(packet->tsig_rr), - tsig_rdata_mac_length(packet->tsig_rr)); - xfr->digest_size = tsig_rdata_mac_length(packet->tsig_rr); - - // Extract the time signed from the TSIG and store it - // We may rewrite the tsig_req_time_signed field - xfr->tsig_prev_time_signed = - tsig_rdata_time_signed(packet->tsig_rr); - - } - } else if (packet->tsig_rr != NULL) { - // TSIG where it should not be - return KNOT_EMALF; - } - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -static void xfrin_take_rr(const knot_pktsection_t *answer, const knot_rrset_t **rr, uint16_t *cur) -{ - if (*cur < answer->count) { - *rr = &answer->rr[*cur]; - *cur += 1; - } else { - *rr = NULL; - } -} - -/*----------------------------------------------------------------------------*/ - -int xfrin_process_axfr_packet(knot_pkt_t *pkt, struct xfr_proc *proc) -{ - if (pkt == NULL) { - return KNOT_EINVAL; - } - - int ret = KNOT_EOK; - uint16_t rr_id = 0; - const knot_rrset_t *rr = NULL; - const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); - - xfrin_take_rr(answer, &rr, &rr_id); - ++proc->npkts; - - // Init zone creator - zcreator_t zc = {.z = proc->zone, - .master = false, .ret = KNOT_EOK }; - - while (rr) { - if (rr->type == KNOT_RRTYPE_SOA && - node_rrtype_exists(zc.z->apex, KNOT_RRTYPE_SOA)) { - // Last SOA, last message, check TSIG. -// int ret = xfrin_check_tsig(pkt, xfr, 1); -#warning TODO: TSIG API - if (ret != KNOT_EOK) { - return ret; - } - return 1; // Signal that transfer finished. - } else { - int ret = zcreator_step(&zc, rr); - if (ret != KNOT_EOK) { - // 'rr' is either inserted, or free'd - return ret; - } - xfrin_take_rr(answer, &rr, &rr_id); - } - } - - // Check possible TSIG at the end of DNS message. -// return xfrin_check_tsig(pkt, xfr, knot_ns_tsig_required(xfr->packet_nr)); -#warning TODO: TSIG API - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -int xfrin_process_ixfr_packet(knot_pkt_t *pkt, knot_ns_xfr_t *xfr) -{ - knot_changesets_t **chs = (knot_changesets_t **)(&xfr->data); - if (pkt == NULL || chs == NULL) { - dbg_xfrin("Wrong parameters supported.\n"); - return KNOT_EINVAL; - } - - uint16_t rr_id = 0; - const knot_rrset_t *rr = NULL; - const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); - xfrin_take_rr(answer, &rr, &rr_id); - if (rr == NULL) { - return KNOT_EXFRREFUSED; /* Empty, try again with AXFR */ - } - - // state of the transfer - // -1 .. a SOA is expected to create a new changeset - int state = 0; - int ret = KNOT_EOK; - if (*chs == NULL) { - dbg_xfrin_verb("Changesets empty, creating new.\n"); - *chs = knot_changesets_create(0); - if (*chs == NULL) { - return KNOT_ENOMEM; - } - - // the first RR must be a SOA - if (rr->type != KNOT_RRTYPE_SOA) { - dbg_xfrin("First RR is not a SOA RR!\n"); - ret = KNOT_EMALF; - goto cleanup; - } - - // just store the first SOA for later use - (*chs)->first_soa = knot_rrset_copy(rr, NULL); - if ((*chs)->first_soa == NULL) { - ret = KNOT_ENOMEM; - goto cleanup; - } - state = -1; - - dbg_xfrin_verb("First SOA of IXFR saved, state set to -1.\n"); - - // take next RR - xfrin_take_rr(answer, &rr, &rr_id); - - /* - * If there is no other records in the response than the SOA, it - * means one of these three cases: - * - * 1) The server does not have newer zone than ours. - * This is indicated by serial equal to the one of our zone. - * 2) The server wants to send the transfer but is unable to fit - * it in the packet. This is indicated by serial different - * (newer) from the one of our zone, but applies only for - * IXFR/UDP. - * 3) The master is weird and sends only SOA in the first packet - * of a fallback to AXFR answer (PowerDNS does this). - * - * The serials must be compared in other parts of the server, so - * just indicate that the answer contains only one SOA. - */ - if (rr == NULL) { - dbg_xfrin("Response containing only SOA,\n"); - return XFRIN_RES_SOA_ONLY; - } else if (rr->type != KNOT_RRTYPE_SOA) { - dbg_xfrin("Fallback to AXFR.\n"); - return XFRIN_RES_FALLBACK; - } - } else { - if ((*chs)->first_soa == NULL) { - dbg_xfrin("Changesets don't contain SOA first!\n"); - ret = KNOT_EINVAL; - goto cleanup; - } - dbg_xfrin_detail("Changesets present.\n"); - } - - /* - * Process the next RR. Different requirements are in place in - * different cases: - * - * 1) Last changeset has both soa_from and soa_to. - * a) The next RR is a SOA. - * i) The next RR is equal to the first_soa saved in changesets. - * This denotes the end of the transfer. It may be dropped and - * the end should be signalised by returning positive value. - * - * ii) The next RR is some other SOA. - * This means a start of new changeset - create it and add it - * to the list. - * - * b) The next RR is not a SOA. - * Put the RR into the ADD part of the last changeset as this is - * not finished yet. Continue while SOA is not encountered. Then - * jump to 1-a. - * - * 2) Last changeset has only the soa_from and does not have soa_to. - * a) The next RR is a SOA. - * This means start of the ADD section. Put the SOA to the - * changeset. Continue adding RRs to the ADD section while SOA - * is not encountered. This is identical to 1-b. - * - * b) The next RR is not a SOA. - * This means the REMOVE part is not finished yet. Add the RR to - * the REMOVE part. Continue adding next RRs until a SOA is - * encountered. Then jump to 2-a. - */ - - // first, find out in what state we are - /*! \todo It would be more elegant to store the state in the - * changesets structure, or in some place persistent between - * calls to this function. - */ - knot_changeset_t *chset = knot_changesets_get_last(*chs); - if (state != -1) { - dbg_xfrin_detail("State is not -1, deciding...\n"); - // there should be at least one started changeset right now - if (EMPTY_LIST((*chs)->sets)) { - ret = KNOT_EMALF; - goto cleanup; - } - - // a changeset should be created only when there is a SOA - assert(chset->soa_from != NULL); - - if (chset->soa_to == NULL) { - state = KNOT_CHANGESET_REMOVE; - } else { - state = KNOT_CHANGESET_ADD; - } - } - - dbg_xfrin_detail("State before the loop: %d\n", state); - - /*! \todo This may be implemented with much less IFs! */ - - while (rr != NULL) { - if (!knot_dname_is_sub(rr->owner, xfr->zone->name) && - !knot_dname_is_equal(rr->owner, xfr->zone->name)) { - // out-of-zone domain - // take next RR - xfrin_take_rr(answer, &rr, &rr_id); - continue; - } - - switch (state) { - case -1: - // a SOA is expected - // this may be either a start of a changeset or the - // last SOA (in case the transfer was empty, but that - // is quite weird in fact - if (rr->type != KNOT_RRTYPE_SOA) { - dbg_xfrin("First RR is not a SOA RR!\n"); - dbg_xfrin_verb("RR type: %u\n", - rr->type); - ret = KNOT_EMALF; - goto cleanup; - } - - if (knot_soa_serial(&rr->rrs) - == knot_soa_serial(&(*chs)->first_soa->rrs)) { - - /*! \note [TSIG] Check TSIG, we're at the end of - * transfer. - */ - ret = xfrin_check_tsig(pkt, xfr, 1); - - // last SOA, discard and end - /*! \note [TSIG] If TSIG validates, consider - * transfer complete. */ - if (ret == KNOT_EOK) { - ret = XFRIN_RES_COMPLETE; - } - - return ret; - } else { - // normal SOA, start new changeset - /* Check changesets for maximum count (so they fit into journal). */ - if ((*chs)->count + 1 > JOURNAL_NCOUNT) - ret = KNOT_ESPACE; - - if (ret != KNOT_EOK) { - goto cleanup; - } - - chset = knot_changesets_create_changeset(*chs); - if (chset == NULL) { - goto cleanup; - } - knot_rrset_t *soa = knot_rrset_copy(rr, NULL); - if (soa == NULL) { - ret = KNOT_ENOMEM; - goto cleanup; - } - - knot_changeset_add_soa(chset, soa, KNOT_CHANGESET_REMOVE); - - // change state to REMOVE - state = KNOT_CHANGESET_REMOVE; - } - break; - case KNOT_CHANGESET_REMOVE: - // if the next RR is SOA, store it and change state to - // ADD - if (rr->type == KNOT_RRTYPE_SOA) { - // we should not be here if soa_from is not set - assert(chset->soa_from != NULL); - knot_rrset_t *soa = knot_rrset_copy(rr, NULL); - if (soa == NULL) { - ret = KNOT_ENOMEM; - goto cleanup; - } - knot_changeset_add_soa(chset, soa, KNOT_CHANGESET_ADD); - - state = KNOT_CHANGESET_ADD; - } else { - // just add the RR to the REMOVE part and - // continue - knot_rrset_t *cpy = knot_rrset_copy(rr, NULL); - if (cpy == NULL) { - ret = KNOT_ENOMEM; - goto cleanup; - } - ret = knot_changeset_add_rrset(chset, cpy, - KNOT_CHANGESET_REMOVE); - if (ret != KNOT_EOK) { - goto cleanup; - } - } - break; - case KNOT_CHANGESET_ADD: - // if the next RR is SOA change to state -1 and do not - // parse next RR - if (rr->type == KNOT_RRTYPE_SOA) { - log_zone_info("%s Serial %u -> %u.\n", - xfr->msg, - knot_soa_serial(&chset->soa_from->rrs), - knot_soa_serial(&chset->soa_to->rrs)); - state = -1; - continue; - } else { - // just add the RR to the ADD part and continue - knot_rrset_t *cpy = knot_rrset_copy(rr, NULL); - if (cpy == NULL) { - ret = KNOT_ENOMEM; - goto cleanup; - } - ret = knot_changeset_add_rrset(chset, cpy, - KNOT_CHANGESET_ADD); - if (ret != KNOT_EOK) { - goto cleanup; - } - } - break; - } - - // take next RR - xfrin_take_rr(answer, &rr, &rr_id); - } - - /*! \note Check TSIG, we're at the end of packet. It may not be - * required. - */ - ret = xfrin_check_tsig(pkt, xfr, - knot_ns_tsig_required(xfr->packet_nr)); - dbg_xfrin_verb("xfrin_check_tsig() returned %d\n", ret); - ++xfr->packet_nr; - - /*! \note [TSIG] Cleanup and propagate error if TSIG validation fails.*/ - if (ret != KNOT_EOK) { - goto cleanup; - } - - // here no RRs remain in the packet but the transfer is not finished - // yet, return EOK - return KNOT_EOK; - -cleanup: - /* We should go here only if some error occured. */ - assert(ret < 0); - - dbg_xfrin_detail("Cleanup after processing IXFR/IN packet.\n"); - knot_changesets_free(chs); - xfr->data = 0; - return ret; -} - -/*----------------------------------------------------------------------------*/ -/* Applying changesets to zone */ -/*----------------------------------------------------------------------------*/ - -void xfrin_cleanup_successful_update(knot_changesets_t *chgs) -{ - if (chgs == NULL) { - return; - } - - knot_changeset_t *change = NULL; - WALK_LIST(change, chgs->sets) { - // Delete old RR data - rrs_list_clear(&change->old_data, NULL); - init_list(&change->old_data); - // Keep new RR data - ptrlist_free(&change->new_data, NULL); - init_list(&change->new_data); - }; -} - -/*----------------------------------------------------------------------------*/ - -static int free_additional(zone_node_t **node, void *data) -{ - UNUSED(data); - if ((*node)->flags & NODE_FLAGS_NONAUTH) { - // non-auth nodes have no additionals. - return KNOT_EOK; - } - - for (uint16_t i = 0; i < (*node)->rrset_count; ++i) { - struct rr_data *data = &(*node)->rrs[i]; - if (data->additional) { - free(data->additional); - data->additional = NULL; - } - } - - return KNOT_EOK; -} - -void xfrin_zone_contents_free(zone_contents_t **contents) -{ - // free the zone tree, but only the structure - // (nodes are already destroyed) - dbg_zone("Destroying zone tree.\n"); - // free additional arrays - knot_zone_tree_apply((*contents)->nodes, free_additional, NULL); - knot_zone_tree_deep_free(&(*contents)->nodes); - dbg_zone("Destroying NSEC3 zone tree.\n"); - knot_zone_tree_deep_free(&(*contents)->nsec3_nodes); - - knot_nsec3param_free(&(*contents)->nsec3_params); - - free(*contents); - *contents = NULL; -} - -/*----------------------------------------------------------------------------*/ - -static void xfrin_cleanup_failed_update(zone_contents_t **new_contents) -{ - if (new_contents == NULL) { - return; - } - - if (*new_contents != NULL) { - // destroy the shallow copy of zone - xfrin_zone_contents_free(new_contents); - } - -} - -/*----------------------------------------------------------------------------*/ - -void xfrin_rollback_update(knot_changesets_t *chgs, - zone_contents_t **new_contents) -{ - if (chgs != NULL) { - knot_changeset_t *change = NULL; - WALK_LIST(change, chgs->sets) { - // Delete new RR data - rrs_list_clear(&change->new_data, NULL); - init_list(&change->new_data); - // Keep old RR data - ptrlist_free(&change->old_data, NULL); - init_list(&change->old_data); - }; - } - xfrin_cleanup_failed_update(new_contents); -} - -/*----------------------------------------------------------------------------*/ - -static int xfrin_replace_rrs_with_copy(zone_node_t *node, - uint16_t type) -{ - // Find data to copy. - struct rr_data *data = NULL; - for (uint16_t i = 0; i < node->rrset_count; ++i) { - if (node->rrs[i].type == type) { - data = &node->rrs[i]; - break; - } - } - assert(data); - - // Create new data. - knot_rdataset_t *rrs = &data->rrs; - void *copy = malloc(knot_rdataset_size(rrs)); - if (copy == NULL) { - return KNOT_ENOMEM; - } - - memcpy(copy, rrs->data, knot_rdataset_size(rrs)); - - // Store new data into node RRS. - rrs->data = copy; - - return KNOT_EOK; -} - -static void clear_new_rrs(zone_node_t *node, uint16_t type) -{ - knot_rdataset_t *new_rrs = node_rdataset(node, type); - if (new_rrs) { - knot_rdataset_clear(new_rrs, NULL); - } -} - -static bool can_remove(const zone_node_t *node, const knot_rrset_t *rr) -{ - if (node == NULL) { - // Node does not exist, cannot remove anything. - return false; - } - const knot_rdataset_t *node_rrs = node_rdataset(node, rr->type); - if (node_rrs == NULL) { - // Node does not have this type at all. - return false; - } - - const bool compare_ttls = false; - for (uint16_t i = 0; i < rr->rrs.rr_count; ++i) { - knot_rdata_t *rr_cmp = knot_rdataset_at(&rr->rrs, i); - if (knot_rdataset_member(node_rrs, rr_cmp, compare_ttls)) { - // At least one RR matches. - return true; - } - } - - // Node does have the type, but no RRs match. - return false; -} - -static int add_old_data(knot_changeset_t *chset, knot_rdata_t *old_data) -{ - if (ptrlist_add(&chset->old_data, old_data, NULL) == NULL) { - return KNOT_ENOMEM; - } - - return KNOT_EOK; -} - -static int add_new_data(knot_changeset_t *chset, knot_rdata_t *new_data) -{ - if (ptrlist_add(&chset->new_data, new_data, NULL) == NULL) { - return KNOT_ENOMEM; - } - - return KNOT_EOK; -} - -static int remove_rr(zone_node_t *node, const knot_rrset_t *rr, - knot_changeset_t *chset) -{ - knot_rrset_t removed_rrset = node_rrset(node, rr->type); - knot_rdata_t *old_data = removed_rrset.rrs.data; - int ret = xfrin_replace_rrs_with_copy(node, rr->type); - if (ret != KNOT_EOK) { - return ret; - } - - // Store old data for cleanup. - ret = add_old_data(chset, old_data); - if (ret != KNOT_EOK) { - clear_new_rrs(node, rr->type); - return ret; - } - - knot_rdataset_t *changed_rrs = node_rdataset(node, rr->type); - // Subtract changeset RRS from node RRS. - ret = knot_rdataset_subtract(changed_rrs, &rr->rrs, NULL); - if (ret != KNOT_EOK) { - clear_new_rrs(node, rr->type); - return ret; - } - - if (changed_rrs->rr_count > 0) { - // Subtraction left some data in RRSet, store it for rollback. - ret = add_new_data(chset, changed_rrs->data); - if (ret != KNOT_EOK) { - knot_rdataset_clear(changed_rrs, NULL); - return ret; - } - } else { - // RRSet is empty now, remove it from node, all data freed. - node_remove_rdataset(node, rr->type); - } - - return KNOT_EOK; -} - -static int xfrin_apply_remove(zone_contents_t *contents, knot_changeset_t *chset) -{ - knot_rr_ln_t *rr_node = NULL; - WALK_LIST(rr_node, chset->remove) { - const knot_rrset_t *rr = rr_node->rr; - - // Find node for this owner - zone_node_t *node = zone_contents_find_node_for_rr(contents, - rr); - if (!can_remove(node, rr)) { - // Nothing to remove from, skip. - continue; - } - - int ret = remove_rr(node, rr, chset); - if (ret != KNOT_EOK) { - return ret; - } - } - - return KNOT_EOK; -} - -static int add_rr(zone_node_t *node, const knot_rrset_t *rr, - knot_changeset_t *chset, bool master) -{ - knot_rrset_t changed_rrset = node_rrset(node, rr->type); - if (!knot_rrset_empty(&changed_rrset)) { - // Modifying existing RRSet. - knot_rdata_t *old_data = changed_rrset.rrs.data; - int ret = xfrin_replace_rrs_with_copy(node, rr->type); - if (ret != KNOT_EOK) { - return ret; - } - - // Store old RRS for cleanup. - ret = add_old_data(chset, old_data); - if (ret != KNOT_EOK) { - clear_new_rrs(node, rr->type); - return ret; - } - } - - // Insert new RR to RRSet, data will be copied. - int ret = node_add_rrset(node, rr); - if (ret == KNOT_EOK || ret == KNOT_ETTL) { - // RR added, store for possible rollback. - knot_rdataset_t *rrs = node_rdataset(node, rr->type); - int data_ret = add_new_data(chset, rrs->data); - if (data_ret != KNOT_EOK) { - knot_rdataset_clear(rrs, NULL); - return data_ret; - } - - if (ret == KNOT_ETTL) { - // Handle possible TTL errors. - log_ttl_error(node, rr); - if (!master) { - // TTL errors fatal only for master. - return KNOT_EOK; - } - } - } - - return ret; -} - -static int xfrin_apply_add(zone_contents_t *contents, - knot_changeset_t *chset, bool master) -{ - knot_rr_ln_t *rr_node = NULL; - WALK_LIST(rr_node, chset->add) { - knot_rrset_t *rr = rr_node->rr; - - // Get or create node with this owner - zone_node_t *node = zone_contents_get_node_for_rr(contents, rr); - if (node == NULL) { - return KNOT_ENOMEM; - } - - int ret = add_rr(node, rr, chset, master); - if (ret != KNOT_EOK) { - return ret; - } - } - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -static int xfrin_apply_replace_soa(zone_contents_t *contents, - knot_changeset_t *chset) -{ - assert(chset->soa_from); - int ret = remove_rr(contents->apex, chset->soa_from, chset); - if (ret != KNOT_EOK) { - return ret; - } - - assert(!node_rrtype_exists(contents->apex, KNOT_RRTYPE_SOA)); - - return add_rr(contents->apex, chset->soa_to, chset, false); -} - -/*----------------------------------------------------------------------------*/ - -static int xfrin_apply_changeset(zone_contents_t *contents, - knot_changeset_t *chset, bool master) -{ - /* - * Applies one changeset to the zone. Checks if the changeset may be - * applied (i.e. the origin SOA (soa_from) has the same serial as - * SOA in the zone apex. - */ - - dbg_xfrin("APPLYING CHANGESET: from serial %u to serial %u\n", - chset->serial_from, chset->serial_to); - - // check if serial matches - const knot_rdataset_t *soa = node_rdataset(contents->apex, KNOT_RRTYPE_SOA); - if (soa == NULL || knot_soa_serial(soa) != chset->serial_from) { - dbg_xfrin("SOA serials do not match!!\n"); - return KNOT_EINVAL; - } - - int ret = xfrin_apply_remove(contents, chset); - if (ret != KNOT_EOK) { - return ret; - } - - ret = xfrin_apply_add(contents, chset, master); - if (ret != KNOT_EOK) { - return ret; - } - - return xfrin_apply_replace_soa(contents, chset); -} - -/*----------------------------------------------------------------------------*/ - -/*! \brief Wrapper for BIRD lists. Storing: Node. */ -typedef struct knot_node_ln { - node_t n; /*!< List node. */ - zone_node_t *node; /*!< Actual usable data. */ -} knot_node_ln_t; - -static int add_node_to_list(zone_node_t *node, list_t *l) -{ - assert(node && l); - knot_node_ln_t *data = malloc(sizeof(knot_node_ln_t)); - if (data == NULL) { - return KNOT_ENOMEM; - } - data->node = node; - add_head(l, (node_t *)data); - return KNOT_EOK; -} - -static int xfrin_mark_empty(zone_node_t **node_p, void *data) -{ - assert(node_p && *node_p); - zone_node_t *node = *node_p; - list_t *l = (list_t *)data; - assert(data); - if (node->rrset_count == 0 && node->children == 0 && - !(node->flags & NODE_FLAGS_EMPTY)) { - /*! - * Mark this node and all parent nodes that have 0 RRSets and - * no children for removal. - */ - int ret = add_node_to_list(node, l); - if (ret != KNOT_EOK) { - return ret; - } - node->flags |= NODE_FLAGS_EMPTY; - if (node->parent) { - if ((node->parent->flags & NODE_FLAGS_WILDCARD_CHILD) - && knot_dname_is_wildcard(node->owner)) { - node->parent->flags &= ~NODE_FLAGS_WILDCARD_CHILD; - } - node->parent->children--; - // Recurse using the parent node - return xfrin_mark_empty(&node->parent, data); - } - } - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -static int xfrin_remove_empty_nodes(zone_contents_t *z) -{ - dbg_xfrin("Removing empty nodes from zone.\n"); - - list_t l; - init_list(&l); - // walk through the zone and select nodes to be removed - int ret = knot_zone_tree_apply(z->nodes, - xfrin_mark_empty, &l); - if (ret != KNOT_EOK) { - return ret; - } - - node_t *n = NULL; - node_t *nxt = NULL; - WALK_LIST_DELSAFE(n, nxt, l) { - knot_node_ln_t *list_node = (knot_node_ln_t *)n; - ret = zone_contents_remove_node(z, list_node->node->owner); - if (ret != KNOT_EOK) { - return ret; - } - node_free(&list_node->node); - free(n); - } - - init_list(&l); - // Do the same with NSEC3 nodes. - ret = knot_zone_tree_apply(z->nsec3_nodes, - xfrin_mark_empty, &l); - if (ret != KNOT_EOK) { - return ret; - } - - WALK_LIST_DELSAFE(n, nxt, l) { - knot_node_ln_t *list_node = (knot_node_ln_t *)n; - ret = zone_contents_remove_nsec3_node(z, list_node->node->owner); - if (ret != KNOT_EOK) { - return ret; - } - node_free(&list_node->node); - free(n); - } - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -int xfrin_prepare_zone_copy(zone_contents_t *old_contents, zone_contents_t **new_contents) -{ - if (old_contents == NULL || new_contents == NULL) { - return KNOT_EINVAL; - } - - dbg_xfrin("Preparing zone copy...\n"); - - /* - * Ensure that the zone generation is set to 0. - */ - if (!zone_contents_gen_is_old(old_contents)) { - // this would mean that a previous update was not completed - // abort - dbg_zone("Trying to apply changesets to zone that is " - "being updated. Aborting.\n"); - return KNOT_EAGAIN; - } - - /* - * Create a shallow copy of the zone, so that the structures may be - * updated. - * - * This will create new zone contents structures (normal nodes' tree, - * NSEC3 tree), and copy all nodes. - * The data in the nodes (RRSets) remain the same though. - */ - zone_contents_t *contents_copy = NULL; - - dbg_xfrin("Copying zone contents.\n"); - int ret = zone_contents_shallow_copy(old_contents, &contents_copy); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to create shallow copy of zone: %s\n", - knot_strerror(ret)); - return ret; - } - - assert(contents_copy->apex != NULL); - - *new_contents = contents_copy; - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -int xfrin_finalize_updated_zone(zone_contents_t *contents_copy, - bool set_nsec3_names) -{ - if (contents_copy == NULL) { - return KNOT_EINVAL; - } - - /* - * Finalize the new zone contents: - * - delete empty nodes - * - parse NSEC3PARAM - * - do adjusting of nodes and RDATA - * - ??? - * - PROFIT - */ - - /* - * Select and remove empty nodes from zone trees. Do not free them right - * away as they may be referenced by some domain names. - */ - int ret = xfrin_remove_empty_nodes(contents_copy); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to remove empty nodes: %s\n", - knot_strerror(ret)); - return ret; - } - - dbg_xfrin("Adjusting zone contents.\n"); - if (set_nsec3_names) { - ret = zone_contents_adjust_full(contents_copy, NULL, NULL); - } else { - ret = zone_contents_adjust_pointers(contents_copy); - } - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to finalize zone contents: %s\n", - knot_strerror(ret)); - return ret; - } - - assert(contents_copy->apex != NULL); - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -int xfrin_apply_changesets_directly(zone_contents_t *contents, - knot_changesets_t *chsets) -{ - if (contents == NULL || chsets == NULL) { - return KNOT_EINVAL; - } - - knot_changeset_t *set = NULL; - WALK_LIST(set, chsets->sets) { - const bool master = true; // Only DNSSEC changesets are applied directly. - int ret = xfrin_apply_changeset(contents, set, master); - if (ret != KNOT_EOK) { - xfrin_cleanup_successful_update(chsets); - return ret; - } - } - - int ret = xfrin_finalize_updated_zone(contents, true); - - /* - * HACK: Cleanup for successful update is used for both success and fail - * when modifying the zone directly, will fix in new zone API. - */ - xfrin_cleanup_successful_update(chsets); - return ret; -} - -/*----------------------------------------------------------------------------*/ - -int xfrin_apply_changesets(zone_t *zone, - knot_changesets_t *chsets, - zone_contents_t **new_contents) -{ - if (zone == NULL || chsets == NULL || EMPTY_LIST(chsets->sets) - || new_contents == NULL) { - return KNOT_EINVAL; - } - - zone_contents_t *old_contents = zone->contents; - 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"); - zone_contents_t *contents_copy = NULL; - int ret = xfrin_prepare_zone_copy(old_contents, &contents_copy); - 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); - knot_changeset_t *set = NULL; - const bool master = (zone_master(zone) == NULL); - WALK_LIST(set, chsets->sets) { - ret = xfrin_apply_changeset(contents_copy, set, master); - if (ret != KNOT_EOK) { - xfrin_rollback_update(chsets, &contents_copy); - dbg_xfrin("Failed to apply changesets to zone: " - "%s\n", knot_strerror(ret)); - return ret; - } - } - - assert(contents_copy->apex != NULL); - - /*! - * \todo Test failure of IXFR. - */ - - dbg_xfrin_verb("Finalizing updated zone...\n"); - ret = xfrin_finalize_updated_zone(contents_copy, true); - if (ret != KNOT_EOK) { - dbg_xfrin("Failed to finalize updated zone: %s\n", - knot_strerror(ret)); - xfrin_rollback_update(chsets, &contents_copy); - return ret; - } - - *new_contents = contents_copy; - - return KNOT_EOK; -} - -/*----------------------------------------------------------------------------*/ - -zone_contents_t *xfrin_switch_zone(zone_t *zone, zone_contents_t *new_contents) -{ - if (zone == NULL || new_contents == NULL) { - return NULL; - } - - dbg_xfrin("Switching zone contents.\n"); - dbg_xfrin_verb("Old contents: %p, apex: %p, new apex: %p\n", - zone->contents, (zone->contents) - ? zone->contents->apex : NULL, new_contents->apex); - - zone_contents_t *old = - zone_switch_contents(zone, new_contents); - - dbg_xfrin_verb("Old contents: %p, apex: %p, new apex: %p\n", - old, (old) ? old->apex : NULL, new_contents->apex); - - // set generation to old, so that the flags may be used in next transfer - // and we do not search for new nodes anymore - zone_contents_set_gen_old(new_contents); - - // wait for readers to finish - dbg_xfrin_verb("Waiting for readers to finish...\n"); - synchronize_rcu(); - - return old; -} diff --git a/src/knot/updates/xfr-in.h b/src/knot/updates/xfr-in.h deleted file mode 100644 index 8ccd9471f045316cace5a14aeea79e93aac9dc6f..0000000000000000000000000000000000000000 --- a/src/knot/updates/xfr-in.h +++ /dev/null @@ -1,163 +0,0 @@ -/*! - * \file xfr-in.h - * - * \author Lubos Slovak <lubos.slovak@nic.cz> - * - * \brief XFR client API. - * - * \addtogroup xfr - * @{ - */ -/* 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/>. - */ - -#ifndef _KNOT_XFR_IN_H_ -#define _KNOT_XFR_IN_H_ - -#include <stdint.h> -#include <string.h> - -#include "libknot/dname.h" -#include "knot/zone/zone.h" -#include "libknot/packet/pkt.h" -#include "knot/server/xfr-handler.h" -#include "knot/updates/changesets.h" - -struct xfr_proc; - -/*----------------------------------------------------------------------------*/ - -typedef enum xfrin_transfer_result { - XFRIN_RES_COMPLETE = 1, - XFRIN_RES_SOA_ONLY = 2, - XFRIN_RES_FALLBACK = 3 -} xfrin_transfer_result_t; - -/*----------------------------------------------------------------------------*/ - -/*! - * \brief Checks if a zone transfer is required by comparing the zone's SOA with - * the one received from master server. - * - * \param zone Zone to check. - * \param soa_response Response to SOA query received from master server. - * - * \retval < 0 if an error occured. - * \retval 1 if the transfer is needed. - * \retval 0 if the transfer is not needed. - */ -int xfrin_transfer_needed(const zone_contents_t *zone, - knot_pkt_t *soa_response); - -/*! - * \brief Processes one incoming packet of AXFR transfer by updating the given - * zone. - * - * \param pkt Incoming packet in wire format. - * \param size Size of the packet in bytes. - * \param zone Zone being built. If there is no such zone (i.e. this is the - * first packet, \a *zone may be set to NULL, in which case a new - * zone structure is created). - * - * \retval KNOT_EOK - * - * \todo Refactor!!! - */ -int xfrin_process_axfr_packet(knot_pkt_t *pkt, struct xfr_proc *proc); - -/*! - * \brief Destroys the whole changesets structure. - * - * Frees all RRSets present in the changesets and all their data. Also frees - * the changesets structure and sets the parameter to NULL. - * - * \param changesets Changesets to destroy. - */ -void xfrin_free_changesets(knot_changesets_t **changesets); - -/*! - * \brief Parses IXFR reply packet and fills in the changesets structure. - * - * \param pkt Packet containing the IXFR reply in wire format. - * \param size Size of the packet in bytes. - * \param changesets Changesets to be filled in. - * - * \retval KNOT_EOK - * \retval KNOT_EINVAL - * \retval KNOT_EMALF - * \retval KNOT_ENOMEM - */ -int xfrin_process_ixfr_packet(knot_pkt_t *pkt, knot_ns_xfr_t *xfr); - -/*! - * \brief Applies changesets *with* zone shallow copy. - * - * \param zone Zone to be updated. - * \param chsets Changes to be made. - * \param new_contents New zone will be returned using this arg. - * \return KNOT_E* - */ -int xfrin_apply_changesets(zone_t *zone, - knot_changesets_t *chsets, - zone_contents_t **new_contents); - -/*! - * \brief Applies changesets directly to the zone, without copying it. - * - * \param contents Zone contents to apply the changesets to. Will be modified. - * \param chsets Changesets to be applied to the zone. - * - * \retval KNOT_EOK if successful. - * \retval KNOT_EINVAL if given one of the arguments is NULL. - * \return Other error code if the application went wrong. - */ -int xfrin_apply_changesets_directly(zone_contents_t *contents, - knot_changesets_t *chsets); - -int xfrin_prepare_zone_copy(zone_contents_t *old_contents, - zone_contents_t **new_contents); - -/*! - * \brief Sets pointers and NSEC3 nodes after signing/DDNS. - * \param contents_copy Contents to be updated. - * \param set_nsec3_names Set to true if NSEC3 hashes should be set. - * \return KNOT_E* - */ -int xfrin_finalize_updated_zone(zone_contents_t *contents_copy, - bool set_nsec3_names); - -zone_contents_t *xfrin_switch_zone(zone_t *zone, zone_contents_t *new_contents); - -void xfrin_rollback_update(knot_changesets_t *chgs, - zone_contents_t **new_contents); - -int xfrin_copy_rrset(zone_node_t *node, uint16_t type, - knot_rrset_t **rrset); - -int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy); - -int xfrin_replace_rrset_in_node(zone_node_t *node, - knot_rrset_t *rrset_new, - zone_contents_t *contents); - -void xfrin_cleanup_successful_update(knot_changesets_t *chgs); - -/* @note Exported because of update.c */ -void xfrin_zone_contents_free(zone_contents_t **contents); - -#endif /* _KNOTXFR_IN_H_ */ - -/*! @} */ diff --git a/src/knot/zone/contents.c b/src/knot/zone/contents.c index e897d6374cab1acbcaf2f079be67bd697e05ec28..7399e8e6914b2bebc05c74830e80531243bbbfca 100644 --- a/src/knot/zone/contents.c +++ b/src/knot/zone/contents.c @@ -49,13 +49,6 @@ typedef struct { /*----------------------------------------------------------------------------*/ -const uint8_t KNOT_ZONE_FLAGS_GEN_OLD = 0; /* xxxxxx00 */ -const uint8_t KNOT_ZONE_FLAGS_GEN_NEW = 1 << 0; /* xxxxxx01 */ -const uint8_t KNOT_ZONE_FLAGS_GEN_FIN = 1 << 1; /* xxxxxx10 */ -const uint8_t KNOT_ZONE_FLAGS_GEN_MASK = 3; /* 00000011 */ - -/*----------------------------------------------------------------------------*/ - static int tree_apply_cb(zone_node_t **node, void *data) { if (node == NULL || data == NULL) { @@ -418,7 +411,6 @@ zone_contents_t *zone_contents_new(const knot_dname_t *apex_name) } memset(contents, 0, sizeof(zone_contents_t)); - contents->node_count = 1; contents->apex = node_new(apex_name); if (contents->apex == NULL) { goto cleanup; @@ -445,36 +437,6 @@ cleanup: /*----------------------------------------------------------------------------*/ -int zone_contents_gen_is_old(const zone_contents_t *contents) -{ - return ((contents->flags & KNOT_ZONE_FLAGS_GEN_MASK) - == KNOT_ZONE_FLAGS_GEN_OLD); -} - -/*----------------------------------------------------------------------------*/ - -int zone_contents_gen_is_new(const zone_contents_t *contents) -{ - return ((contents->flags & KNOT_ZONE_FLAGS_GEN_MASK) - == KNOT_ZONE_FLAGS_GEN_NEW); -} - -/*----------------------------------------------------------------------------*/ - -void zone_contents_set_gen_old(zone_contents_t *contents) -{ - contents->flags &= ~KNOT_ZONE_FLAGS_GEN_MASK; - contents->flags |= KNOT_ZONE_FLAGS_GEN_OLD; -} - -/*----------------------------------------------------------------------------*/ - -void zone_contents_set_gen_new(zone_contents_t *contents) -{ - contents->flags &= ~KNOT_ZONE_FLAGS_GEN_MASK; - contents->flags |= KNOT_ZONE_FLAGS_GEN_NEW; -} - static zone_node_t *zone_contents_get_node(const zone_contents_t *zone, const knot_dname_t *name) { @@ -513,8 +475,6 @@ static int zone_contents_add_node(zone_contents_t *zone, zone_node_t *node, return ret; } - ++zone->node_count; - if (!create_parents) { return KNOT_EOK; } @@ -561,8 +521,6 @@ static int zone_contents_add_node(zone_contents_t *zone, zone_node_t *node, next_node->flags |= NODE_FLAGS_WILDCARD_CHILD; } - ++zone->node_count; - dbg_zone_detail("Next parent.\n"); node = next_node; parent = knot_wire_next_label(parent, NULL); @@ -1311,9 +1269,6 @@ int zone_contents_shallow_copy(const zone_contents_t *from, zone_contents_t **to return KNOT_ENOMEM; } - contents->flags = from->flags; - zone_contents_set_gen_new(contents); - int ret = recreate_normal_tree(from, contents); if (ret != KNOT_EOK) { knot_zone_tree_free(&contents->nodes); diff --git a/src/knot/zone/contents.h b/src/knot/zone/contents.h index 235e54c56299827418650a8a1a2c1527440b58fe..a521c9f65c5c7d3eea99d968c76f565586117319 100644 --- a/src/knot/zone/contents.h +++ b/src/knot/zone/contents.h @@ -47,35 +47,8 @@ typedef struct zone_contents_t { knot_zone_tree_t *nsec3_nodes; knot_nsec3_params_t nsec3_params; - - /*! - * \todo Unify the use of this field - authoritative nodes vs. all. - */ - size_t node_count; - - /*! \brief Various flags - * - * Two rightmost bits denote zone contents generation. - * - * Possible values: - * - 00 - Original version of the zone. Old nodes should be used. - * - 01 - New (updated) zone. New nodes should be used. - * - 10 - New (updated) zone, but exactly the stored nodes should be - * used, no matter their generation. - * - * The third bit denotes whether ANY queries are enabled or disabled: - * - 1xx - ANY queries disabled - * - 0xx - ANY queries enabled - */ - uint8_t flags; } zone_contents_t; -/*!< \brief Helper linked list list for CNAME loop checking */ -typedef struct cname_chain { - const zone_node_t *node; - struct cname_chain *next; -} cname_chain_t; - /*! * \brief Signature of callback for zone contents apply functions. */ @@ -85,12 +58,6 @@ typedef int (*zone_contents_apply_cb_t)(zone_node_t *node, void *data); zone_contents_t *zone_contents_new(const knot_dname_t *apex_name); -int zone_contents_gen_is_old(const zone_contents_t *contents); -int zone_contents_gen_is_new(const zone_contents_t *contents); - -void zone_contents_set_gen_old(zone_contents_t *contents); -void zone_contents_set_gen_new(zone_contents_t *contents); - int zone_contents_add_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n); int zone_contents_remove_node(zone_contents_t *contents, const knot_dname_t *owner); diff --git a/src/knot/zone/events.c b/src/knot/zone/events.c index 2e0e35d38e772f9a82ed383c4c9c1c9bf80ec7d0..97afbae4e4770d063eff4369001d9a593f8dba32 100644 --- a/src/knot/zone/events.c +++ b/src/knot/zone/events.c @@ -297,9 +297,12 @@ static int event_xfer(zone_t *zone) int ret = zone_query_execute(zone, pkt_type, master); /* IXFR failed, revert to AXFR. */ - if (pkt_type == KNOT_RRTYPE_IXFR && ret != KNOT_EOK) { + if (pkt_type == KNOT_QUERY_IXFR && ret != KNOT_EOK) { + ZONE_QUERY_LOG(LOG_WARNING, zone, master, "IXFR", "Fallback to AXFR"); zone->flags |= ZONE_FORCE_AXFR; - return event_xfer(zone); + ret = event_xfer(zone); + zone->flags &= ~ZONE_FORCE_AXFR; + return ret; } if (zone_contents_is_empty(zone->contents)) { @@ -419,12 +422,12 @@ static int event_dnssec(zone_t *zone) assert(zone); fprintf(stderr, "DNSSEC of '%s'\n", zone->conf->name); - knot_changesets_t *chs = knot_changesets_create(1); + changesets_t *chs = changesets_create(1); if (chs == NULL) { return KNOT_ENOMEM; } - knot_changeset_t *ch = knot_changesets_get_last(chs); + changeset_t *ch = changesets_get_last(chs); assert(ch); int ret = KNOT_ERROR; @@ -454,10 +457,8 @@ static int event_dnssec(zone_t *zone) goto done; } - if (!knot_changesets_empty(chs)) { - zone_contents_t *new_c = NULL; - ret = zone_change_apply_and_store(chs, zone, &new_c, "DNSSEC"); - chs = NULL; // freed by zone_change_apply_and_store() + if (!changesets_empty(chs)) { + ret = zone_change_apply_and_store(&chs, zone, "DNSSEC", NULL); if (ret != KNOT_EOK) { log_zone_error("%s Could not sign zone (%s).\n", msgpref, knot_strerror(ret)); @@ -475,7 +476,7 @@ static int event_dnssec(zone_t *zone) } done: - knot_changesets_free(&chs); + changesets_free(&chs, NULL); free(msgpref); return ret; } diff --git a/src/knot/zone/zone-diff.c b/src/knot/zone/zone-diff.c index c21c8d97a8d1188450da93c3e9a64ba3c98428ba..266c559b1d30881f74ec8562d1168fa85814f95a 100644 --- a/src/knot/zone/zone-diff.c +++ b/src/knot/zone/zone-diff.c @@ -27,17 +27,17 @@ struct zone_diff_param { knot_zone_tree_t *nodes; - knot_changeset_t *changeset; + changeset_t *changeset; }; // forward declaration static int knot_zone_diff_rdata(const knot_rrset_t *rrset1, const knot_rrset_t *rrset2, - knot_changeset_t *changeset); + changeset_t *changeset); static int knot_zone_diff_load_soas(const zone_contents_t *zone1, const zone_contents_t *zone2, - knot_changeset_t *changeset) + changeset_t *changeset) { if (zone1 == NULL || zone2 == NULL || changeset == NULL) { return KNOT_EINVAL; @@ -94,7 +94,7 @@ static int knot_zone_diff_load_soas(const zone_contents_t *zone1, /*!< \todo Only use add or remove function, not both as they are the same. */ /*!< \todo Also, this might be all handled by function in changesets.h!!! */ -static int knot_zone_diff_changeset_add_rrset(knot_changeset_t *changeset, +static int knot_zone_diff_changeset_add_rrset(changeset_t *changeset, const knot_rrset_t *rrset) { /* Remove all RRs of the RRSet. */ @@ -114,8 +114,8 @@ static int knot_zone_diff_changeset_add_rrset(knot_changeset_t *changeset, return KNOT_ENOMEM; } - int ret = knot_changeset_add_rrset(changeset, rrset_copy, - KNOT_CHANGESET_ADD); + int ret = changeset_add_rrset(changeset, rrset_copy, + CHANGESET_ADD); if (ret != KNOT_EOK) { /* We have to free the copy now! */ knot_rrset_free(&rrset_copy, NULL); @@ -127,7 +127,7 @@ static int knot_zone_diff_changeset_add_rrset(knot_changeset_t *changeset, return KNOT_EOK; } -static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset, +static int knot_zone_diff_changeset_remove_rrset(changeset_t *changeset, const knot_rrset_t *rrset) { /* Remove all RRs of the RRSet. */ @@ -152,8 +152,8 @@ static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset, return KNOT_ENOMEM; } - int ret = knot_changeset_add_rrset(changeset, rrset_copy, - KNOT_CHANGESET_REMOVE); + int ret = changeset_add_rrset(changeset, rrset_copy, + CHANGESET_REMOVE); if (ret != KNOT_EOK) { /* We have to free the copy now. */ knot_rrset_free(&rrset_copy, NULL); @@ -166,7 +166,7 @@ static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset, } static int knot_zone_diff_add_node(const zone_node_t *node, - knot_changeset_t *changeset) + changeset_t *changeset) { /* Add all rrsets from node. */ for (uint i = 0; i < node->rrset_count; i++) { @@ -183,7 +183,7 @@ static int knot_zone_diff_add_node(const zone_node_t *node, return KNOT_EOK; } -static int knot_zone_diff_remove_node(knot_changeset_t *changeset, +static int knot_zone_diff_remove_node(changeset_t *changeset, const zone_node_t *node) { /* Remove all the RRSets of the node. */ @@ -262,7 +262,7 @@ static int knot_zone_diff_rdata_return_changes(const knot_rrset_t *rrset1, static int knot_zone_diff_rdata(const knot_rrset_t *rrset1, const knot_rrset_t *rrset2, - knot_changeset_t *changeset) + changeset_t *changeset) { if ((changeset == NULL) || (rrset1 == NULL && rrset2 == NULL)) { dbg_zonediff("zone_diff: diff_rdata: NULL arguments.\n"); @@ -325,7 +325,7 @@ static int knot_zone_diff_rdata(const knot_rrset_t *rrset1, static int knot_zone_diff_rrsets(const knot_rrset_t *rrset1, const knot_rrset_t *rrset2, - knot_changeset_t *changeset) + changeset_t *changeset) { /* RRs (=rdata) have to be cross-compared, unfortunalely. */ return knot_zone_diff_rdata(rrset1, rrset2, changeset); @@ -502,7 +502,7 @@ static int knot_zone_diff_add_new_nodes(zone_node_t **node_ptr, void *data) static int knot_zone_diff_load_trees(knot_zone_tree_t *nodes1, knot_zone_tree_t *nodes2, - knot_changeset_t *changeset) + changeset_t *changeset) { assert(changeset); @@ -526,7 +526,7 @@ static int knot_zone_diff_load_trees(knot_zone_tree_t *nodes1, static int knot_zone_diff_load_content(const zone_contents_t *zone1, const zone_contents_t *zone2, - knot_changeset_t *changeset) + changeset_t *changeset) { int result; @@ -542,7 +542,7 @@ static int knot_zone_diff_load_content(const zone_contents_t *zone1, static int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone2, - knot_changeset_t *changeset) + changeset_t *changeset) { if (zone1 == NULL || zone2 == NULL) { return KNOT_EINVAL; @@ -558,7 +558,7 @@ static int zone_contents_diff(const zone_contents_t *zone1, int zone_contents_create_diff(const zone_contents_t *z1, const zone_contents_t *z2, - knot_changeset_t *changeset) + changeset_t *changeset) { int ret = zone_contents_diff(z1, z2, changeset); if (ret != KNOT_EOK) { @@ -573,7 +573,7 @@ int zone_contents_create_diff(const zone_contents_t *z1, } int knot_zone_tree_add_diff(knot_zone_tree_t *t1, knot_zone_tree_t *t2, - knot_changeset_t *changeset) + changeset_t *changeset) { if (!changeset) { return KNOT_EINVAL; diff --git a/src/knot/zone/zone-diff.h b/src/knot/zone/zone-diff.h index 051efccf1f13672fa8fe1ad5b52f6ae9f68357cb..07d1589b18f2ad09eb5b6128f73590139abc117a 100644 --- a/src/knot/zone/zone-diff.h +++ b/src/knot/zone/zone-diff.h @@ -27,12 +27,12 @@ * */ int zone_contents_create_diff(const zone_contents_t *z1, const zone_contents_t *z2, - knot_changeset_t *changeset); + changeset_t *changeset); /*! * \brief Add diff between two zone trees into the changeset. */ int knot_zone_tree_add_diff(knot_zone_tree_t *t1, knot_zone_tree_t *t2, - knot_changeset_t *changeset); + changeset_t *changeset); #endif // _KNOT_ZONE_DIFF_H_ diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c index 4e787cd21f4aae076dbaf239ac4da9dda98b0cd5..9969da8968e526f227bd48e2a2e917c211f94894 100644 --- a/src/knot/zone/zone-load.c +++ b/src/knot/zone/zone-load.c @@ -21,7 +21,7 @@ #include "knot/zone/contents.h" #include "knot/zone/zonefile.h" #include "knot/dnssec/zone-events.h" -#include "knot/updates/xfr-in.h" +#include "knot/updates/apply.h" #include "libknot/rdata.h" zone_contents_t *zone_load_contents(conf_zone_t *conf) @@ -91,7 +91,7 @@ int zone_load_journal(zone_contents_t *contents, conf_zone_t *conf) uint32_t serial = zone_contents_serial(contents); /* Load all pending changesets. */ - knot_changesets_t* chsets = knot_changesets_create(0); + changesets_t* chsets = changesets_create(0); if (chsets == NULL) { return KNOT_ERROR; } @@ -99,7 +99,7 @@ int zone_load_journal(zone_contents_t *contents, conf_zone_t *conf) /*! \todo Check what should be the upper bound. */ int ret = journal_load_changesets(conf->ixfr_db, chsets, serial, serial - 1); if ((ret != KNOT_EOK && ret != KNOT_ERANGE) || EMPTY_LIST(chsets->sets)) { - knot_changesets_free(&chsets); + changesets_free(&chsets, NULL); /* Absence of records is not an error. */ if (ret == KNOT_ENOENT) { return KNOT_EOK; @@ -109,13 +109,13 @@ int zone_load_journal(zone_contents_t *contents, conf_zone_t *conf) } /* Apply changesets. */ - ret = xfrin_apply_changesets_directly(contents, chsets); + ret = apply_changesets_directly(contents, chsets); log_zone_info("Zone '%s' serial %u -> %u: %s\n", conf->name, serial, zone_contents_serial(contents), knot_strerror(ret)); - knot_changesets_free(&chsets); + changesets_free(&chsets, NULL); return ret; } @@ -127,8 +127,8 @@ int zone_load_post(zone_contents_t *new_contents, zone_t *zone) int ret = KNOT_EOK; const conf_zone_t *conf = zone->conf; - knot_changeset_t *change = NULL; - knot_changesets_t *chset = knot_changesets_create(0); + changeset_t *change = NULL; + changesets_t *chset = changesets_create(0); if (chset == NULL) { return KNOT_ENOMEM; } @@ -137,7 +137,7 @@ int zone_load_post(zone_contents_t *new_contents, zone_t *zone) if (conf->dnssec_enable) { change = zone_change_prepare(chset); if (change == NULL) { - knot_changesets_free(&chset); + changesets_free(&chset, NULL); return KNOT_ENOMEM; } @@ -153,13 +153,13 @@ int zone_load_post(zone_contents_t *new_contents, zone_t *zone) /* Commit existing zone change and prepare new. */ int ret = zone_change_commit(new_contents, chset); if (ret != KNOT_EOK) { - knot_changesets_free(&chset); + changesets_free(&chset, NULL); return ret; } else { - knot_changesets_clear(chset); + changesets_clear(chset, NULL); change = zone_change_prepare(chset); if (change == NULL) { - knot_changesets_free(&chset); + changesets_free(&chset, NULL); return KNOT_ENOMEM; } } @@ -182,7 +182,7 @@ int zone_load_post(zone_contents_t *new_contents, zone_t *zone) if (ret == KNOT_EOK) { ret = zone_change_commit(new_contents, chset); if (ret != KNOT_EOK) { - knot_changesets_free(&chset); + changesets_free(&chset, NULL); return ret; } @@ -191,7 +191,7 @@ int zone_load_post(zone_contents_t *new_contents, zone_t *zone) } /* Free changesets and return. */ - knot_changesets_free(&chset); + changesets_free(&chset, NULL); return ret; } diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c index fee200dac7dfb544848ee4a73914174b4008c2f1..c7f202c3f101cd0f14f1f5e845c03b566f9741fb 100644 --- a/src/knot/zone/zone.c +++ b/src/knot/zone/zone.c @@ -26,12 +26,13 @@ #include "knot/zone/zone.h" #include "knot/zone/zonefile.h" #include "knot/zone/contents.h" -#include "knot/updates/xfr-in.h" +#include "knot/updates/apply.h" #include "knot/nameserver/requestor.h" #include "libknot/common.h" #include "libknot/dname.h" #include "libknot/dnssec/random.h" #include "libknot/util/utils.h" +#include "libknot/rdata/soa.h" /*! * \brief Set ACL list from configuration. @@ -145,24 +146,24 @@ void zone_free(zone_t **zone_ptr) *zone_ptr = NULL; } -knot_changeset_t *zone_change_prepare(knot_changesets_t *chset) +changeset_t *zone_change_prepare(changesets_t *chset) { - return knot_changesets_create_changeset(chset); + return changesets_create_changeset(chset); } -int zone_change_commit(zone_contents_t *contents, knot_changesets_t *chset) +int zone_change_commit(zone_contents_t *contents, changesets_t *chset) { assert(contents); - if (knot_changesets_empty(chset)) { + if (changesets_empty(chset)) { return KNOT_EOK; } /* Apply DNSSEC changeset to the new zone. */ - return xfrin_apply_changesets_directly(contents, chset); + return apply_changesets_directly(contents, chset); } -int zone_change_store(zone_t *zone, knot_changesets_t *chset) +int zone_change_store(zone_t *zone, changesets_t *chset) { assert(zone); assert(chset); @@ -186,39 +187,41 @@ int zone_change_store(zone_t *zone, knot_changesets_t *chset) } /*! \note @mvavrusa Moved from zones.c, this needs a common API. */ -int zone_change_apply_and_store(knot_changesets_t *chs, +int zone_change_apply_and_store(changesets_t **chs, zone_t *zone, - zone_contents_t **new_contents, - const char *msgpref) + const char *msgpref, + mm_ctx_t *rr_mm) { int ret = KNOT_EOK; /* Now, try to apply the changesets to the zone. */ - ret = xfrin_apply_changesets(zone, chs, new_contents); + zone_contents_t *new_contents; + ret = apply_changesets(zone, *chs, &new_contents); if (ret != KNOT_EOK) { log_zone_error("%s Failed to apply changesets.\n", msgpref); /* Free changesets, but not the data. */ - knot_changesets_free(&chs); + changesets_free(chs, rr_mm); return ret; // propagate the error above } /* Write changes to journal if all went well. */ - ret = zone_change_store(zone, chs); + ret = zone_change_store(zone, *chs); if (ret != KNOT_EOK) { log_zone_error("%s Failed to store changesets.\n", msgpref); - xfrin_rollback_update(chs, new_contents); + update_rollback(*chs, &new_contents); /* Free changesets, but not the data. */ - knot_changesets_free(&chs); + changesets_free(chs, rr_mm); return ret; // propagate the error above } /* Switch zone contents. */ - zone_contents_t *old_contents = xfrin_switch_zone(zone, *new_contents); - xfrin_zone_contents_free(&old_contents); + zone_contents_t *old_contents = zone_switch_contents(zone, new_contents); + synchronize_rcu(); + update_free_old_zone(&old_contents); /* Free changesets, but not the data. */ - xfrin_cleanup_successful_update(chs); - knot_changesets_free(&chs); + update_cleanup(*chs); + changesets_free(chs, rr_mm); assert(ret == KNOT_EOK); return KNOT_EOK; } @@ -353,3 +356,16 @@ struct request_data *zone_update_dequeue(zone_t *zone) return ret; } + +bool zone_transfer_needed(const zone_t *zone, const knot_pkt_t *pkt) +{ + const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); + const knot_rrset_t soa = answer->rr[0]; + if (soa.type != KNOT_RRTYPE_SOA) { + return false; + } + + return knot_serial_compare(zone_contents_serial(zone->contents), + knot_soa_serial(&soa.rrs)) < 0; +} + diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index bba1eb220ee5717e7682973937ef5691f0070cca..0724fa52bb15c8caaad5dfc380a49c37acc303f8 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -112,14 +112,14 @@ zone_t *zone_new(conf_zone_t *conf); void zone_free(zone_t **zone_ptr); /*! \note Zone change API, subject to change. */ -knot_changeset_t *zone_change_prepare(knot_changesets_t *chset); -int zone_change_commit(zone_contents_t *contents, knot_changesets_t *chset); -int zone_change_store(zone_t *zone, knot_changesets_t *chset); +changeset_t *zone_change_prepare(changesets_t *chset); +int zone_change_commit(zone_contents_t *contents, changesets_t *chset); +int zone_change_store(zone_t *zone, changesets_t *chset); /*! \note @mvavrusa Moved from zones.c, this needs a common API. */ -int zone_change_apply_and_store(knot_changesets_t *chs, +int zone_change_apply_and_store(changesets_t **chs, zone_t *zone, - zone_contents_t **new_contents, - const char *msgpref); + const char *msgpref, + mm_ctx_t *rr_mm); /*! * \brief Atomically switch the content of the zone. */ @@ -141,4 +141,8 @@ int zone_update_enqueue(zone_t *zone, knot_pkt_t *pkt, struct process_query_para /*! \brief Dequeue UPDATE request. */ struct request_data *zone_update_dequeue(zone_t *zone); +/*! \brief Returns true if final SOA in transfer has newer serial than zone */ +bool zone_transfer_needed(const zone_t *zone, const knot_pkt_t *pkt); + + /*! @} */ diff --git a/tests-extra/runtests.py b/tests-extra/runtests.py index 31449bbb41210c0731d0e1e3be84f05f4d1fb352..b25c0c6bf0c437475d8d0d2985ceda102ab7fae4 100755 --- a/tests-extra/runtests.py +++ b/tests-extra/runtests.py @@ -69,6 +69,16 @@ def parse_args(cmd_args): return included, excluded +def log_failed(log_dir, msg, indent=True): + fname = log_dir + "/failed.log" + first = False if os.path.isfile(fname) else True + + file = open(fname, mode="a") + if first: + print("Failed tests:", file=file) + print("%s%s" % (" * " if indent else "", msg), file=file) + file.close() + def main(args): included, excluded = parse_args(args) @@ -128,6 +138,7 @@ def main(args): test_file = case_dir + "/test.py" if not os.path.isfile(test_file): log.error(" * case \'%s\':\tMISSING" % case) + log_failed(outs_dir, "%s/%s\tMISSING" % (test, case)) fail_cnt += 1 continue @@ -144,6 +155,8 @@ def main(args): except OsError: log.error(" * case \'%s\':\tEXCEPTION (no dir \'%s\')" % (case, out_dir)) + log_failed(outs_dir, "%s/%s\tEXCEPTION (no dir \'%s\')" % + (test, case, out_dir)) fail_cnt += 1 continue @@ -159,6 +172,8 @@ def main(args): else: log.error(" * case \'%s\':\tEXCEPTION (%s)" % (case, format(exc))) + log_failed(outs_dir, "%s/%s\tEXCEPTION (%s)" % + (test, case, format(exc))) fail_cnt += 1 except BaseException as exc: save_traceback(params.out_dir) @@ -174,6 +189,7 @@ def main(args): if params.err: msg = " (%s)" % params.err_msg if params.err_msg else "" log.info(" * case \'%s\':\tFAILED%s" % (case, msg)) + log_failed(outs_dir, "%s/%s\tFAILED%s" % (test, case, msg)) fail_cnt += 1 else: log.info(" * case \'%s\':\tOK" % case) @@ -190,6 +206,7 @@ def main(args): log.info(msg_cases + msg_skips + msg_res) if fail_cnt: + log_failed(outs_dir, "Total %i/%i" % (fail_cnt, case_cnt), indent=False) sys.exit(1) else: sys.exit(0) diff --git a/tests/journal.c b/tests/journal.c index 6a11d3f3e925d61c8cc22ce728ab7c5a2e5ad69b..43611e9f10a3352b3c38b2964068e6c129eeee84 100644 --- a/tests/journal.c +++ b/tests/journal.c @@ -38,7 +38,7 @@ static int randstr(char* dst, size_t len) int main(int argc, char *argv[]) { - plan(7); + plan(10); /* Create tmpdir */ int fsize = 10 * 1024 * 1024; @@ -68,24 +68,33 @@ int main(int argc, char *argv[]) char chk_buf[64] = {'\0'}; randstr(chk_buf, sizeof(chk_buf)); int ret = journal_map(journal, chk_key, &mptr, sizeof(chk_buf), false); - memcpy(mptr, chk_buf, sizeof(chk_buf)); - journal_unmap(journal, chk_key, mptr, 1); - is_int(0, ret, "journal: write data"); + is_int(KNOT_EOK, ret, "journal: write data (map)"); + if (ret == KNOT_EOK) { + memcpy(mptr, chk_buf, sizeof(chk_buf)); + ret = journal_unmap(journal, chk_key, mptr, 1); + is_int(KNOT_EOK, ret, "journal: write data (unmap)"); + } - journal_map(journal, chk_key, &mptr, sizeof(chk_buf), true); - ret = memcmp(chk_buf, mptr, sizeof(chk_buf)); - journal_unmap(journal, chk_key, mptr, 1); - is_int(0, ret, "journal: data integrity check"); + ret = journal_map(journal, chk_key, &mptr, sizeof(chk_buf), true); + is_int(KNOT_EOK, ret, "journal: data integrity check (map)"); + if (ret == KNOT_EOK) { + ret = memcmp(chk_buf, mptr, sizeof(chk_buf)); + is_int(0, ret, "journal: data integrity check (cmp)"); + ret = journal_unmap(journal, chk_key, mptr, 0); + is_int(KNOT_EOK, ret, "journal: data integrity check (unmap)"); + } /* Reopen log and re-read value. */ journal_close(journal); journal = journal_open(jfilename, fsize); ok(journal != NULL, "journal: open journal '%s'", jfilename); - journal_map(journal, chk_key, &mptr, sizeof(chk_buf), true); - ret = memcmp(chk_buf, mptr, sizeof(chk_buf)); - journal_unmap(journal, chk_key, mptr, 1); - is_int(0, ret, "journal: data integrity check after close/open"); + ret = journal_map(journal, chk_key, &mptr, sizeof(chk_buf), true); + if (ret == KNOT_EOK) { + ret = memcmp(chk_buf, mptr, sizeof(chk_buf)); + journal_unmap(journal, chk_key, mptr, 0); + } + is_int(KNOT_EOK, ret, "journal: data integrity check after close/open"); /* Write random data. */ ret = 0; @@ -103,7 +112,7 @@ int main(int argc, char *argv[]) break; } } - is_int(0, ret, "journal: sustained mmap r/w"); + is_int(KNOT_EOK, ret, "journal: sustained mmap r/w"); /* Close journal. */ journal_close(journal); diff --git a/tests/pkt.c b/tests/pkt.c index d0d5d58f3ba4d20504f89ee9743ebd4aef70abc3..1d3efca88003cfee583533fc17a07805a3bd96ca 100644 --- a/tests/pkt.c +++ b/tests/pkt.c @@ -66,7 +66,7 @@ static void packet_match(knot_pkt_t *in, knot_pkt_t *out) int main(int argc, char *argv[]) { - plan(30); + plan(29); /* Create memory pool context. */ int ret = 0; diff --git a/tests/requestor.c b/tests/requestor.c index a8b32236f47f41db66f2163694bc2a0ad7bcabbe..cbe2eee9266c9baa298ba4b6f78f8a3697feaf83 100644 --- a/tests/requestor.c +++ b/tests/requestor.c @@ -64,28 +64,28 @@ static void* responder_thread(void *arg) #define CONNECTED_TESTS 4 #define TESTS_COUNT DISCONNECTED_TESTS + CONNECTED_TESTS -static struct request *make_query(struct requestor *requestor, struct sockaddr_storage *remote) +static struct request *make_query(struct requestor *requestor, conf_iface_t *remote) { knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, requestor->mm); assert(pkt); knot_pkt_put_question(pkt, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA); - return requestor_make(requestor, NULL, remote, pkt); + return requestor_make(requestor, remote, pkt); } -static void test_disconnected(struct requestor *requestor, struct sockaddr_storage *remote) +static void test_disconnected(struct requestor *requestor, conf_iface_t *remote) { /* Enqueue packet. */ int ret = requestor_enqueue(requestor, make_query(requestor, remote), NULL); - is_int(KNOT_EOK, ret, "requestor: disconnected/enqueue"); + is_int(KNOT_ECONN, ret, "requestor: disconnected/enqueue"); /* Wait for completion. */ struct timeval tv = { 5, 0 }; ret = requestor_exec(requestor, &tv); - is_int(KNOT_ECONNREFUSED, ret, "requestor: disconnected/wait"); + is_int(KNOT_ENOENT, ret, "requestor: disconnected/wait"); } -static void test_connected(struct requestor *requestor, struct sockaddr_storage *remote) +static void test_connected(struct requestor *requestor, conf_iface_t *remote) { /* Enqueue packet. */ int ret = requestor_enqueue(requestor, make_query(requestor, remote), NULL);; @@ -118,9 +118,10 @@ int main(int argc, char *argv[]) mm_ctx_t mm; mm_ctx_mempool(&mm, 4096); - struct sockaddr_storage remote, origin; - sockaddr_set(&remote, AF_INET, "127.0.0.1", 0); - sockaddr_set(&origin, AF_INET, "127.0.0.1", 0); + conf_iface_t remote; + memset(&remote, 0, sizeof(conf_iface_t)); + sockaddr_set(&remote.addr, AF_INET, "127.0.0.1", 0); + sockaddr_set(&remote.via, AF_INET, "127.0.0.1", 0); /* Create fake server environment. */ server_t server; @@ -134,10 +135,10 @@ int main(int argc, char *argv[]) test_disconnected(&requestor, &remote); /* Bind to random port. */ - int origin_fd = net_bound_socket(SOCK_STREAM, &remote); + int origin_fd = net_bound_socket(SOCK_STREAM, &remote.addr); assert(origin_fd > 0); - socklen_t addr_len = sockaddr_len(&remote); - getsockname(origin_fd, (struct sockaddr *)&remote, &addr_len); + socklen_t addr_len = sockaddr_len(&remote.addr); + getsockname(origin_fd, (struct sockaddr *)&remote.addr, &addr_len); int ret = listen(origin_fd, 10); assert(ret == 0); @@ -152,7 +153,7 @@ int main(int argc, char *argv[]) #warning TODO: when ported sign_packet/verify_packet /* Terminate responder. */ - int responder = net_connected_socket(SOCK_STREAM, &remote, NULL, 0); + int responder = net_connected_socket(SOCK_STREAM, &remote.addr, NULL, 0); assert(responder > 0); tcp_send(responder, (const uint8_t *)"", 1); (void) pthread_join(thread, 0);