diff --git a/NEWS b/NEWS index 59292e553c701a43a8381905f0c49eb4e689a34b..726c32a0ac5e0a457d92c83057d4d16acb207c89 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,12 @@ +Knot DNS 1.5.2 (2014-09-08) +========================== + +Bugfixes: +--------- + - Some RR parsing corner cases were not handled properly + - AXFR-style IXFR was refused and had to be retransfered + - Hash character (#) was not properly escaped when storing text zone file + Knot DNS 1.5.1 (2014-08-19) =========================== diff --git a/configure.ac b/configure.ac index 1b7afd8114781bd8df9c262984f12ddc58c7039c..8477232706cde804e8722c9e57c471745534e5ad 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # -*- Autoconf -*- AC_PREREQ([2.60]) -AC_INIT([knot], [1.5.1], [knot-dns@labs.nic.cz]) +AC_INIT([knot], [1.5.2], [knot-dns@labs.nic.cz]) AM_INIT_AUTOMAKE([gnits subdir-objects dist-xz -Wall -Werror]) AM_SILENT_RULES([yes]) AC_CONFIG_SRCDIR([src/knot/main.c]) diff --git a/m4/code-coverage.m4 b/m4/code-coverage.m4 index 6df6a00aafd1cbc5c5516c0728b620876e44af10..57a3a6e1509353bc2d96845dc5052b9d8578e9b7 100644 --- a/m4/code-coverage.m4 +++ b/m4/code-coverage.m4 @@ -31,7 +31,7 @@ AC_DEFUN([AX_CODE_COVERAGE], [ AC_CHECK_PROG([LCOV], [lcov], [lcov]) AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) - lcov_version_list="1.6 1.7 1.8 1.9 1.10" + lcov_version_list="1.6 1.7 1.8 1.9 1.10 1.11" AS_IF([test "$LCOV"], [ AC_CACHE_CHECK([for lcov version], ac_cv_lvoc_version, [ diff --git a/src/knot/nameserver/ixfr.c b/src/knot/nameserver/ixfr.c index bcbbd9525cc9e1f43d2676998e1eded676e8382c..f3c7a338e3fab34d51b5ae11000c062bca5084a8 100644 --- a/src/knot/nameserver/ixfr.c +++ b/src/knot/nameserver/ixfr.c @@ -618,7 +618,6 @@ int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata) int ret = KNOT_EOK; struct timeval now = {0}; struct ixfr_proc *ixfr = (struct ixfr_proc*)qdata->ext; - knot_pkt_t *query = qdata->query; /* If IXFR is disabled, respond with SOA. */ if (qdata->param->proc_flags & NS_QUERY_NO_IXFR) { @@ -641,10 +640,6 @@ int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata) case KNOT_ERANGE: /* No history -> AXFR. */ case KNOT_ENOENT: IXFROUT_LOG(LOG_INFO, "incomplete history, fallback to AXFR"); - knot_pkt_clear(pkt); - knot_pkt_put_question(pkt, knot_pkt_qname(query), - knot_pkt_qclass(query), - KNOT_RRTYPE_AXFR); qdata->packet_type = KNOT_QUERY_AXFR; /* Solve as AXFR. */ return axfr_query_process(pkt, qdata); default: /* Server errors. */ @@ -684,14 +679,17 @@ int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata) return NS_PROC_FAIL; } - if (!ixfr_enough_data(pkt)) { - return NS_PROC_FAIL; - } + if (adata->ext == NULL) { + if (!ixfr_enough_data(pkt)) { + return NS_PROC_FAIL; + } - /* Check for AXFR-style IXFR. */ - if (ixfr_is_axfr(pkt)) { - IXFRIN_LOG(LOG_NOTICE, "fallback to AXFR"); - return axfr_answer_process(pkt, adata); + /* Check for AXFR-style IXFR. */ + if (ixfr_is_axfr(pkt)) { + IXFRIN_LOG(LOG_NOTICE, "receiving AXFR-style IXFR"); + adata->response_type = KNOT_RESPONSE_AXFR; + return axfr_answer_process(pkt, adata); + } } /* Check RCODE. */ diff --git a/src/knot/nameserver/process_answer.c b/src/knot/nameserver/process_answer.c index 521d2dcb0f61ff702fc4527145ddb98b460579e0..e18916b1a180ca7a0ada5a88f3bdf663b65e6e77 100644 --- a/src/knot/nameserver/process_answer.c +++ b/src/knot/nameserver/process_answer.c @@ -22,12 +22,14 @@ /*! \brief Accessor to query-specific data. */ #define ANSWER_DATA(ctx) ((struct answer_data *)(ctx)->data) +#define RESPONSE_TYPE_UNSET -1 static void answer_data_init(knot_process_t *ctx, void *module_param) { /* Initialize persistent data. */ struct answer_data *data = ANSWER_DATA(ctx); memset(data, 0, sizeof(struct answer_data)); + data->response_type = RESPONSE_TYPE_UNSET; data->mm = &ctx->mm; data->param = module_param; } @@ -119,9 +121,11 @@ static int process_answer(knot_pkt_t *pkt, knot_process_t *ctx) /* Call appropriate processing handler. */ int next_state = NS_PROC_NOOP; - int response_type = knot_pkt_type(query) | KNOT_RESPONSE; - /* @note We can't derive type from response, as it may not contain QUESTION at all. */ - switch(response_type) { + if (data->response_type == RESPONSE_TYPE_UNSET) { + /* @note We can't derive type from response, as it may not contain QUESTION at all. */ + data->response_type = knot_pkt_type(query) | KNOT_RESPONSE; + } + switch(data->response_type) { case KNOT_RESPONSE_NORMAL: next_state = internet_process_answer(pkt, data); break; diff --git a/src/knot/nameserver/process_answer.h b/src/knot/nameserver/process_answer.h index 571edc82d0602020a86179c7e8104ab076376e8c..e87ea57b5bd52bc8dc54cb417b2b083e4787e424 100644 --- a/src/knot/nameserver/process_answer.h +++ b/src/knot/nameserver/process_answer.h @@ -59,6 +59,7 @@ struct answer_data { knot_sign_context_t sign; /*!< Signing context. */ /* Everything below should be kept on reset. */ + int response_type; /*!< Type of incoming response. */ struct process_answer_param *param; /*!< Module parameters. */ mm_ctx_t *mm; /*!< Memory context. */ }; diff --git a/src/knot/zone/events.c b/src/knot/zone/events.c index 92d1d7a5233b4da92d414eebc7f642e87f1b5818..1235dad54ece6e11f080903923fdad325a3e891b 100644 --- a/src/knot/zone/events.c +++ b/src/knot/zone/events.c @@ -403,11 +403,11 @@ static int event_update(zone_t *zone) mem_trim(); /* Replan event if next update waiting. */ - pthread_spin_lock(&zone->ddns_lock); + pthread_mutex_lock(&zone->ddns_lock); const bool empty = EMPTY_LIST(zone->ddns_queue); - pthread_spin_unlock(&zone->ddns_lock); + pthread_mutex_unlock(&zone->ddns_lock); if (!empty) { zone_events_schedule(zone, ZONE_EVENT_UPDATE, ZONE_EVENT_NOW); @@ -641,14 +641,14 @@ static void duplicate_ddns_q(zone_t *zone, zone_t *old_zone) /*!< Replans DDNS event. */ static void replan_update(zone_t *zone, zone_t *old_zone) { - pthread_spin_lock(&old_zone->ddns_lock); + pthread_mutex_lock(&old_zone->ddns_lock); const bool have_updates = old_zone->ddns_queue_size > 0; if (have_updates) { duplicate_ddns_q(zone, (zone_t *)old_zone); } - pthread_spin_unlock(&old_zone->ddns_lock); + pthread_mutex_unlock(&old_zone->ddns_lock); if (have_updates) { zone_events_schedule(zone, ZONE_EVENT_UPDATE, ZONE_EVENT_NOW); diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c index 68e27c8acc2a23952881a7b4047c3be33e59add5..e3557ebbba4eef651c8da7f2c4544d8c59e05b6e 100644 --- a/src/knot/zone/zone.c +++ b/src/knot/zone/zone.c @@ -72,7 +72,7 @@ zone_t* zone_new(conf_zone_t *conf) zone->conf = conf; // DDNS - pthread_spin_init(&zone->ddns_lock, 0); + pthread_mutex_init(&zone->ddns_lock, NULL); zone->ddns_queue_size = 0; init_list(&zone->ddns_queue); @@ -95,7 +95,7 @@ void zone_free(zone_t **zone_ptr) knot_dname_free(&zone->name, NULL); free_ddns_queue(zone); - pthread_spin_destroy(&zone->ddns_lock); + pthread_mutex_destroy(&zone->ddns_lock); /* Free assigned config. */ conf_free_zone(zone->conf); @@ -266,13 +266,13 @@ int zone_update_enqueue(zone_t *zone, knot_pkt_t *pkt, struct process_query_para return ret; } - pthread_spin_lock(&zone->ddns_lock); + pthread_mutex_lock(&zone->ddns_lock); /* Enqueue created request. */ add_tail(&zone->ddns_queue, (node_t *)req); ++zone->ddns_queue_size; - pthread_spin_unlock(&zone->ddns_lock); + pthread_mutex_unlock(&zone->ddns_lock); /* Schedule UPDATE event. */ zone_events_schedule(zone, ZONE_EVENT_UPDATE, ZONE_EVENT_NOW); @@ -286,10 +286,10 @@ size_t zone_update_dequeue(zone_t *zone, list_t *updates) return 0; } - pthread_spin_lock(&zone->ddns_lock); + pthread_mutex_lock(&zone->ddns_lock); if (EMPTY_LIST(zone->ddns_queue)) { /* Lost race during reload. */ - pthread_spin_unlock(&zone->ddns_lock); + pthread_mutex_unlock(&zone->ddns_lock); return 0; } @@ -298,7 +298,7 @@ size_t zone_update_dequeue(zone_t *zone, list_t *updates) init_list(&zone->ddns_queue); zone->ddns_queue_size = 0; - pthread_spin_unlock(&zone->ddns_lock); + pthread_mutex_unlock(&zone->ddns_lock); return update_count; } diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index 83c94e48ecb0b82384579e4cfa51c7b7209e4e68..50d0a9ccdfbb40d0f049d1a24e0bf235bb815ce3 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -58,7 +58,7 @@ typedef struct zone_t zone_flag_t flags; /*! \brief DDNS queue and lock. */ - pthread_spinlock_t ddns_lock; + pthread_mutex_t ddns_lock; size_t ddns_queue_size; list_t ddns_queue; diff --git a/tests-extra/requirements.txt b/tests-extra/requirements.txt index a42bc44e35e7983531bd460bffa835332b93d5dc..56782b1df8edf425e1da4e4635e2e8f9306c69f5 100644 --- a/tests-extra/requirements.txt +++ b/tests-extra/requirements.txt @@ -1,3 +1,3 @@ -#dnspython3==1.11.1 --e git+https://github.com/rthalley/dnspython.git@python3#egg=dnspython3 +#-e git+https://github.com/rthalley/dnspython.git@python3#egg=dnspython3 +dnspython3==1.12.0 psutil==1.2.1 diff --git a/tests-extra/tests/ixfr/axfr_style/data/xfr.zone b/tests-extra/tests/ixfr/axfr_style/data/xfr.zone new file mode 100644 index 0000000000000000000000000000000000000000..afec07e72f6c30ad377005c1c1b785983ef87d4a --- /dev/null +++ b/tests-extra/tests/ixfr/axfr_style/data/xfr.zone @@ -0,0 +1,3 @@ +xfr. SOA . . 1 100 100 100 1000 +xfr. IN NS a +a IN A 1.2.3.4 diff --git a/tests-extra/tests/ixfr/axfr_style/data/xfr.zone.1 b/tests-extra/tests/ixfr/axfr_style/data/xfr.zone.1 new file mode 100644 index 0000000000000000000000000000000000000000..bebb90afd7472a80550c61a92456a4c1ee417861 --- /dev/null +++ b/tests-extra/tests/ixfr/axfr_style/data/xfr.zone.1 @@ -0,0 +1,4 @@ +xfr. SOA . . 2 100 100 100 100 +xfr. IN NS a +a IN A 1.2.3.4 +test TXT "test" diff --git a/tests-extra/tests/ixfr/axfr_style/test.py b/tests-extra/tests/ixfr/axfr_style/test.py new file mode 100644 index 0000000000000000000000000000000000000000..2c38fec0193a606e57dca84860ddfdb60c102fd0 --- /dev/null +++ b/tests-extra/tests/ixfr/axfr_style/test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +'''Test for AXFR-style IXFR''' + +from dnstest.test import Test +import os + +t = Test(tsig=False) + +master = t.server("bind") +slave = t.server("knot") +zone = t.zone("xfr", storage=".") + +t.link(zone, master, slave) + +t.start() + +serial = master.zone_wait(zone) + +# update zone with small change +master.update_zonefile(zone, version=1) +master.reload() +master.zone_wait(zone, serial) +serial = slave.zone_wait(zone, serial) + +t.xfr_diff(master, slave, zone) + +# update zone with large change (3 messages worth) +filename = master.data_dir + zone[0].name + "zone.2" +f = open(filename, "w") +f.write(zone[0].name + " IN SOA . . 3 100 100 100 100\n") +f.write(zone[0].name + " IN NS smth\n") +f.write("smth IN A 1.2.3.4\n") +for i in range(2000): + f.write("test" + str(i) + " TXT " + "test" * 16 + "\n") +f.close() +master.update_zonefile(zone, version=2) +os.remove(filename) + +master.reload() +master.zone_wait(zone, serial) +slave.zone_wait(zone, serial) +t.xfr_diff(master, slave, zone) + +t.stop() diff --git a/tests-extra/tools/dnstest/server.py b/tests-extra/tools/dnstest/server.py index 61110b14a70fd82018a6c5d630ac881791111c43..1b3e86a018cffb3e4047eb9a70d8df08e364969f 100644 --- a/tests-extra/tools/dnstest/server.py +++ b/tests-extra/tools/dnstest/server.py @@ -660,7 +660,8 @@ class Server(object): if random: self.zones[zone.name].zfile.update_rnd() else: - self.zones[zone.name].zfile.upd_file(version=version) + self.zones[zone.name].zfile.upd_file(storage=self.data_dir, + version=version) def add_query_module(self, zone, module, param): # Convert one item list to single object. diff --git a/tests-extra/tools/dnstest/zonefile.py b/tests-extra/tools/dnstest/zonefile.py index da7c60aed82a8d4bb97701143e463c807866ac11..19c6f45e374ac055bfbc1174ccc25c16687f1bde 100644 --- a/tests-extra/tools/dnstest/zonefile.py +++ b/tests-extra/tools/dnstest/zonefile.py @@ -20,7 +20,6 @@ class ZoneFile(object): # Directory containing source zone file/updates. self.storage = None - self.backup_num = 1 @property @@ -47,12 +46,11 @@ class ZoneFile(object): if not file_name: file_name = self.name + "zone" - - if storage: - self.storage = storage - self.file_name = os.path.basename(file_name) + if not self.storage: + self.storage = storage if storage else os.path.dirname(file_name) + if not exists: return @@ -66,6 +64,19 @@ class ZoneFile(object): src_file += "." + str(version) shutil.copyfile(src_file, self.path) + + # Copy zone keys. + keydir = self.storage + "/keys" + if os.path.isdir(keydir): + prepare_dir(self.key_dir) + + src_files = os.listdir(keydir) + for file_name in src_files: + if (self.name[:-1]).lower() in file_name: + full_file_name = os.path.join(keydir, file_name) + if (os.path.isfile(full_file_name)): + shutil.copy(full_file_name, self.key_dir) + except: raise Exception("Can't use zone file '%s'" % src_file) diff --git a/tests/rrset_wire.c b/tests/rrset_wire.c index 57ae73d0638e16fd5c8cf7161cb78936842960d9..11ce6a9179dac0707abe8a433a152e7e03ec7a61 100644 --- a/tests/rrset_wire.c +++ b/tests/rrset_wire.c @@ -73,7 +73,7 @@ struct wire_data { const char *msg; }; -#define FROM_CASE_COUNT 16 +#define FROM_CASE_COUNT 17 static const struct wire_data FROM_CASES[FROM_CASE_COUNT] = { { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A)}, @@ -155,6 +155,13 @@ static const struct wire_data FROM_CASES[FROM_CASE_COUNT] = { .pos = QUERY_SIZE + QNAME_SIZE, .code = KNOT_EMALF, .msg = "NAPTR bad offset"}, +{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR), + RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x09), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 7, + .pos = QUERY_SIZE + QNAME_SIZE, + .code = KNOT_EMALF, + .msg = "NAPTR no DNAME"}, { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR), RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x0c), 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, QNAME_POINTER},