]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
zone/events: dont send NOTIFY before server starts answering
authorLibor Peltan <libor.peltan@nic.cz>
Wed, 31 Dec 2025 22:09:02 +0000 (23:09 +0100)
committerDaniel Salzman <daniel.salzman@nic.cz>
Mon, 5 Jan 2026 09:48:35 +0000 (10:48 +0100)
src/knot/events/events.c
src/knot/events/events.h
src/knot/server/server.c
src/knot/zone/zonedb-load.c
tests-extra/tests/notify/too_early/test.py [new file with mode: 0644]
tests-extra/tools/dnstest/test.py

index d93af62ce52d935bbe196012b602b34196d64607..70c0cb08ec533db44a573b25697b8d64066d9911 100644 (file)
@@ -132,7 +132,8 @@ static zone_event_type_t get_next_event(zone_events_t *events)
                time_t current = events->time[i];
 
                if ((next == 0 || current < next) && (current != 0) &&
-                   (events->forced[i] || !events->ufrozen || !ufreeze_applies(i))) {
+                   (events->forced[i] || !events->ufrozen || !ufreeze_applies(i)) &&
+                   (events->answering || !only_started(i))) {
                        next = current;
                        next_type = i;
                }
@@ -489,6 +490,13 @@ void zone_events_freeze_blocking(zone_t *zone)
        pthread_cond_destroy(&cond);
 }
 
+void zone_events_start_answering(zone_t *zone)
+{
+       pthread_mutex_lock(&zone->events.mx);
+       zone->events.answering = true;
+       reschedule(&zone->events, true); // unlocks events->mx
+}
+
 void zone_events_start(zone_t *zone)
 {
        if (!zone) {
@@ -502,7 +510,7 @@ void zone_events_start(zone_t *zone)
        pthread_mutex_lock(&events->mx);
        events->frozen = false;
 
-       reschedule(events, true); //unlocks events->mx
+       reschedule(events, true); // unlocks events->mx
 }
 
 time_t zone_events_get_time(const struct zone *zone, zone_event_type_t type)
index 49bba840f7a5223c4cccfdff4347fc02de261167..7e75d691be9c6fc8d657bff136d681106ae59d8b 100644 (file)
@@ -48,6 +48,7 @@ typedef struct zone_events {
 
        bool frozen;                    //!< Terminated, don't schedule new events.
        bool ufrozen;                   //!< Updates to the zone temporarily frozen by user.
+       bool answering;                 //!< Server is answering.
 
        event_t *event;                 //!< Scheduler event.
        worker_pool_t *pool;            //!< Server worker pool.
@@ -159,6 +160,11 @@ void zone_events_freeze(struct zone *zone);
  */
 void zone_events_freeze_blocking(struct zone *zone);
 
+/*!
+ * \brief Set "answering" bit to true.
+ */
+void zone_events_start_answering(struct zone *zone);
+
 /*!
  * \brief ufreeze_applies
  * \param type Type of event to be checked
@@ -166,6 +172,14 @@ void zone_events_freeze_blocking(struct zone *zone);
  */
 bool ufreeze_applies(zone_event_type_t type);
 
+/*!
+ * \brief Events that require answering server.
+ */
+inline static bool only_started(zone_event_type_t type)
+{
+       return type == ZONE_EVENT_NOTIFY;
+}
+
 /*!
  * \brief Start the events processing.
  *
index 6ff377b20acbcea30c6fe604e26c44242b07c78e..07d3011dcaf5f9cf6a9457919963963898ddce4c 100644 (file)
@@ -1213,6 +1213,8 @@ int server_start_answering(server_t *server)
                }
        }
 
+       knot_zonedb_foreach(server->zone_db, zone_events_start_answering);
+
        return KNOT_EOK;
 }
 
index 46140faba7cd29881d300035e87fd14934ca7b26..b2cf094294f7cf13c69c00cc485e7e3ed8c8e691 100644 (file)
@@ -67,6 +67,7 @@ static zone_t *create_zone_from(const knot_dname_t *name, server_t *server)
                zone_free(&zone);
                return NULL;
        }
+       zone->events.answering = (server->state & ServerAnswering);
 
        return zone;
 }
diff --git a/tests-extra/tests/notify/too_early/test.py b/tests-extra/tests/notify/too_early/test.py
new file mode 100644 (file)
index 0000000..18e4eeb
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+
+'''Test that NOTIFY is not sent before server is answering (on XFR).'''
+
+from dnstest.test import Test
+
+t = Test()
+
+master = t.server("knot")
+slave = t.server("knot")
+
+zones = t.zone_rnd(1, records=1200, names=["zzzzzzzzz."]) + t.zone("example.")
+
+t.link(zones, master, slave)
+
+master.dnssec(zones[0]).enable = True
+slave.conf_zone(zones).retry_min_interval = 300
+
+t.generate_conf()
+slave.start()
+master.start()
+
+serials = master.zones_wait(zones)
+slave.zones_wait(zones)
+
+t.end()
index a0262c890f5270f6426a1bed54c28060f72e9e0a..0facf6063ccd8b4e5533579c60cc10effe60b406 100644 (file)
@@ -384,11 +384,12 @@ class Test(object):
         return [zone]
 
     def zone_rnd(self, number, dnssec=None, nsec3=None, records=None, serial=None,
-                 ttl=None, exists=True):
+                 ttl=None, exists=True, names=None):
         zones = list()
 
         # Generate unique zone names.
-        names = zone_generate.main(["-n", number]).split()
+        if names is None:
+            names = zone_generate.main(["-n", number]).split()
         for name in names:
             zone = dnstest.zonefile.ZoneFile(self.zones_dir)
             zone.set_name(name)