]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
ixfr: add 'zone.provide-ixfr' option for enforcing IXFR fallback
authorDaniel Salzman <daniel.salzman@nic.cz>
Thu, 19 Jan 2023 12:51:09 +0000 (13:51 +0100)
committerDaniel Salzman <daniel.salzman@nic.cz>
Wed, 25 Jan 2023 18:01:09 +0000 (19:01 +0100)
doc/man/knot.conf.5in
doc/reference.rst
src/knot/conf/schema.c
src/knot/conf/schema.h
src/knot/nameserver/ixfr.c
tests-extra/tests/ixfr/provide_ixfr/test.py [new file with mode: 0644]
tests-extra/tools/dnstest/server.py

index 1dae26cc27125a035abb6ca6ecbce25fea67465c..abe815c82eda2ea14e01f1b295e5e2a9e4d34da1 100644 (file)
@@ -1996,6 +1996,7 @@ zone:
     ddns\-master: remote_id
     notify: remote_id | remotes_id ...
     acl: acl_id ...
+    provide\-ixfr: BOOL
     semantic\-checks: BOOL | soft
     zonefile\-sync: TIME
     zonefile\-load: none | difference | difference\-no\-serial | whole
@@ -2099,6 +2100,12 @@ An ordered list of \fI\%references\fP to ACL rules which can allow
 or disallow zone transfers, updates or incoming notifies.
 .sp
 \fIDefault:\fP not set
+.SS provide\-ixfr
+.sp
+If disabled, the server is forced to respond with AXFR to IXFR queries.
+If enabled, IXFR requests are responded normally.
+.sp
+\fIDefault:\fP \fBon\fP
 .SS semantic\-checks
 .sp
 Selects if extra zone semantic checks are used or impacts of the mandatory checks.
index 696903ce332c4f0f4df69639a3d5118dd9b2ead6..5376d6c17fff57fdbba7d790e72d23657231c068 100644 (file)
@@ -2190,6 +2190,7 @@ Definition of zones served by the server.
      ddns-master: remote_id
      notify: remote_id | remotes_id ...
      acl: acl_id ...
+     provide-ixfr: BOOL
      semantic-checks: BOOL | soft
      zonefile-sync: TIME
      zonefile-load: none | difference | difference-no-serial | whole
@@ -2313,6 +2314,16 @@ or disallow zone transfers, updates or incoming notifies.
 
 *Default:* not set
 
+.. _zone_provide-ixfr:
+
+provide-ixfr
+------------
+
+If disabled, the server is forced to respond with AXFR to IXFR queries.
+If enabled, IXFR requests are responded normally.
+
+*Default:* ``on``
+
 .. _zone_semantic-checks:
 
 semantic-checks
index 4523e346da0750f1c4cbf3613c3321794e0db27c..9ca3d09d0878e06002cba7cbfd70650b30419466 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2023 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
@@ -446,6 +446,7 @@ static const yp_item_t desc_policy[] = {
        { C_DDNS_MASTER,         YP_TREF,  YP_VREF = { C_RMT }, YP_FNONE, { check_ref } }, \
        { C_NOTIFY,              YP_TREF,  YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI, { check_ref } }, \
        { C_ACL,                 YP_TREF,  YP_VREF = { C_ACL }, YP_FMULTI, { check_ref } }, \
+       { C_PROVIDE_IXFR,        YP_TBOOL, YP_VBOOL = { true } }, \
        { C_SEM_CHECKS,          YP_TOPT,  YP_VOPT = { semantic_checks, SEMCHECKS_OFF }, FLAGS }, \
        { C_ZONEFILE_SYNC,       YP_TINT,  YP_VINT = { -1, INT32_MAX, 0, YP_STIME } }, \
        { C_ZONEFILE_LOAD,       YP_TOPT,  YP_VOPT = { zonefile_load, ZONEFILE_LOAD_WHOLE } }, \
index b78c17d01526d2e4c0f46ed322b7109ace2821ad..153b3cbd7887e07de123f6d0405cdebbf48e84d8 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2023 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
 #define C_PIDFILE              "\x07""pidfile"
 #define C_POLICY               "\x06""policy"
 #define C_PROPAG_DELAY         "\x11""propagation-delay"
+#define C_PROVIDE_IXFR         "\x0C""provide-ixfr"
 #define C_PROXY_ALLOWLIST      "\x0F""proxy-allowlist"
 #define C_QUIC                 "\x04""quic"
 #define C_QUIC_IDLE_CLOSE      "\x17""quic-idle-close-timeout"
index c6d28c39235926a3979f3118cf1f20c25750558c..03a9fdf130964949c194290bb10667c34dc8f1ae 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2023 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
@@ -179,6 +179,12 @@ static int ixfr_answer_init(knotd_qdata_t *qdata, uint32_t *serial_from)
                return KNOT_EAGAIN;
        }
 
+       conf_val_t provide = conf_zone_get(conf(), C_PROVIDE_IXFR,
+                                          qdata->extra->zone->name);
+       if (!conf_bool(&provide)) {
+               return KNOT_ENOTSUP;
+       }
+
        const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
        const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0);
        *serial_from = knot_soa_serial(their_soa->rrs.rdata);
@@ -275,6 +281,10 @@ int ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
                case KNOT_EUPTODATE: /* Our zone is same age/older, send SOA. */
                        IXFROUT_LOG(LOG_INFO, qdata, "zone is up-to-date, serial %u", soa_from);
                        return ixfr_answer_soa(pkt, qdata);
+               case KNOT_ENOTSUP:
+                       IXFROUT_LOG(LOG_INFO, qdata, "cannot provide, fallback to AXFR");
+                       qdata->type = KNOTD_QUERY_TYPE_AXFR; /* Solve as AXFR. */
+                       return axfr_process_query(pkt, qdata);
                case KNOT_ERANGE:    /* No history -> AXFR. */
                case KNOT_ENOENT:
                        IXFROUT_LOG(LOG_INFO, qdata, "incomplete history, serial %u, fallback to AXFR", soa_from);
diff --git a/tests-extra/tests/ixfr/provide_ixfr/test.py b/tests-extra/tests/ixfr/provide_ixfr/test.py
new file mode 100644 (file)
index 0000000..950fe10
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+
+'''Test for AXFR-style IXFR controlled by provide-ixfr configuration'''
+
+from dnstest.test import Test
+
+t = Test()
+
+master = t.server("knot")
+slave = t.server("knot")
+zone = t.zone("example.com")
+
+t.link(zone, master, slave, ixfr=True)
+
+t.start()
+
+serial_init = master.zones_wait(zone)
+
+# Update the zone to create some history.
+master.update_zonefile(zone, random=True)
+master.reload()
+slave.zones_wait(zone, serial_init)
+
+# Disable IXFR and check AXFR-style IXFR.
+master.provide_ixfr = False
+master.gen_confile()
+master.reload()
+master.zones_wait(zone, serial_init)
+
+t.check_axfr_style_ixfr(master, zone[0].name, serial_init[zone[0].name])
+
+# Enable IXFR and compare with slave.
+master.provide_ixfr = True
+master.gen_confile()
+master.reload()
+master.zones_wait(zone, serial_init)
+
+t.xfr_diff(master, slave, zone, serial_init)
+
+t.stop()
index 56d861bd21584195c5818f9fca5b7be930ae89c8..d71bf4b894b6a8961c0942c392a8397d265bbeac 100644 (file)
@@ -192,6 +192,7 @@ class Server(object):
         self.zone_size_limit = None
         self.serial_policy = None
         self.auto_acl = None
+        self.provide_ixfr = None
 
         self.inquirer = None
 
@@ -1583,7 +1584,7 @@ class Knot(Server):
             self._str(s, "serial-policy", self.serial_policy)
 
             s.item_str("journal-content", z.journal_content)
-            
+
             if z.reverse_from:
                 s.item_str("reverse-generate", z.reverse_from.name)
 
@@ -1599,6 +1600,8 @@ class Knot(Server):
             elif z.ixfr:
                 s.item_str("zonefile-load", "difference")
 
+            self._bool(s, "provide-ixfr", self.provide_ixfr)
+
             if z.catalog_role == ZoneCatalogRole.GENERATE:
                 s.item_str("catalog-role", "generate")
             elif z.catalog_role == ZoneCatalogRole.MEMBER: