\fBzone\-thaw\fP [\fIzone\fP\&...]
Trigger dismissal of zone freeze. (#)
.TP
+\fBzone\-xfr\-freeze\fP [\fIzone\fP\&...]
+Temporarily disable outgoing AXFR/IXFR for the zone(s). (#)
+.TP
+\fBzone\-xfr\-thaw\fP [\fIzone\fP\&...]
+Dismiss outgoing XFR freeze. (#)
+.TP
\fBzone\-read\fP \fIzone\fP [\fIowner\fP [\fItype\fP]]
Get zone data that are currently being presented.
.TP
**zone-thaw** [*zone*...]
Trigger dismissal of zone freeze. (#)
+**zone-xfr-freeze** [*zone*...]
+ Temporarily disable outgoing AXFR/IXFR for the zone(s). (#)
+
+**zone-xfr-thaw** [*zone*...]
+ Dismiss outgoing XFR freeze. (#)
+
**zone-read** *zone* [*owner* [*type*]]
Get zone data that are currently being presented.
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2022 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
return schedule_trigger(zone, args, ZONE_EVENT_UTHAW, false);
}
+static int zone_xfr_freeze(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ zone_set_flag(zone, ZONE_XFR_FROZEN);
+
+ log_zone_info(zone->name, "outgoing XFR frozen");
+
+ return KNOT_EOK;
+}
+
+static int zone_xfr_thaw(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ zone_unset_flag(zone, ZONE_XFR_FROZEN);
+
+ log_zone_info(zone->name, "outgoing XFR unfrozen");
+
+ return KNOT_EOK;
+}
+
static int zone_txn_begin(zone_t *zone, _unused_ ctl_args_t *args)
{
if (zone->control_update != NULL) {
return zones_apply(args, zone_freeze);
case CTL_ZONE_THAW:
return zones_apply(args, zone_thaw);
+ case CTL_ZONE_XFR_FREEZE:
+ return zones_apply(args, zone_xfr_freeze);
+ case CTL_ZONE_XFR_THAW:
+ return zones_apply(args, zone_xfr_thaw);
case CTL_ZONE_READ:
return zones_apply(args, zone_read);
case CTL_ZONE_BEGIN:
[CTL_ZONE_KSK_SBM] = { "zone-ksk-submitted", ctl_zone },
[CTL_ZONE_FREEZE] = { "zone-freeze", ctl_zone },
[CTL_ZONE_THAW] = { "zone-thaw", ctl_zone },
+ [CTL_ZONE_XFR_FREEZE] = { "zone-xfr-freeze", ctl_zone },
+ [CTL_ZONE_XFR_THAW] = { "zone-xfr-thaw", ctl_zone },
[CTL_ZONE_READ] = { "zone-read", ctl_zone },
[CTL_ZONE_BEGIN] = { "zone-begin", ctl_zone },
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2022 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
CTL_ZONE_KSK_SBM,
CTL_ZONE_FREEZE,
CTL_ZONE_THAW,
+ CTL_ZONE_XFR_FREEZE,
+ CTL_ZONE_XFR_THAW,
CTL_ZONE_READ,
CTL_ZONE_BEGIN,
false, fmt)
static void init_qdata_from_request(knotd_qdata_t *qdata,
- const zone_t *zone,
+ zone_t *zone,
knot_request_t *req,
knotd_qdata_params_t *params,
knotd_qdata_extra_t *extra)
}
}
-static void send_update_response(conf_t *conf, const zone_t *zone, knot_request_t *req)
+static void send_update_response(conf_t *conf, zone_t *zone, knot_request_t *req)
{
if (req->resp) {
if (!zone_is_slave(conf, zone)) {
free(req);
}
-static void send_update_responses(conf_t *conf, const zone_t *zone, list_t *updates)
+static void send_update_responses(conf_t *conf, zone_t *zone, list_t *updates)
{
ptrnode_t *node, *nxt;
WALK_LIST_DELSAFE(node, nxt, *updates) {
}
}
+ if (zone_get_flag(qdata->extra->zone, ZONE_XFR_FROZEN, false)) {
+ qdata->rcode = KNOT_RCODE_REFUSED;
+ qdata->rcode_ede = KNOT_EDNS_EDE_NOT_READY;
+ return KNOT_EAGAIN;
+ }
+
/* Create transfer processing context. */
knot_mm_t *mm = qdata->mm;
struct axfr_proc *axfr = mm_alloc(mm, sizeof(struct axfr_proc));
case KNOT_EMALF: /* Malformed query. */
AXFROUT_LOG(LOG_DEBUG, qdata, "malformed query");
return KNOT_STATE_FAIL;
+ case KNOT_EAGAIN: /* Outgoing AXFR temporarily disabled. */
+ AXFROUT_LOG(LOG_INFO, qdata, "outgoing AXFR frozen");
+ return KNOT_STATE_FAIL;
default:
AXFROUT_LOG(LOG_ERR, qdata, "failed to start (%s)",
knot_strerror(ret));
}
}
+ if (zone_get_flag(qdata->extra->zone, ZONE_XFR_FROZEN, false)) {
+ qdata->rcode = KNOT_RCODE_REFUSED;
+ qdata->rcode_ede = KNOT_EDNS_EDE_NOT_READY;
+ return KNOT_EAGAIN;
+ }
+
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);
case KNOT_EMALF: /* Malformed query. */
IXFROUT_LOG(LOG_DEBUG, qdata, "malformed query");
return KNOT_STATE_FAIL;
+ case KNOT_EAGAIN: /* Outgoing IXFR temporarily disabled. */
+ IXFROUT_LOG(LOG_INFO, qdata, "outgoing IXFR frozen");
+ return KNOT_STATE_FAIL;
default: /* Server errors. */
IXFROUT_LOG(LOG_ERR, qdata, "failed to start (%s)",
knot_strerror(ret));
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2022 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
}
/*! \brief Find zone for given question. */
-static const zone_t *answer_zone_find(const knot_pkt_t *query, knot_zonedb_t *zonedb)
+static zone_t *answer_zone_find(const knot_pkt_t *query, knot_zonedb_t *zonedb)
{
uint16_t qtype = knot_pkt_qtype(query);
uint16_t qclass = knot_pkt_qclass(query);
const knot_dname_t *qname = knot_pkt_qname(query);
- const zone_t *zone = NULL;
+ zone_t *zone = NULL;
// search for zone only for IN and ANY classes
if (qclass != KNOT_CLASS_IN && qclass != KNOT_CLASS_ANY) {
-/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2022 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
/*! \brief Query processing intermediate data. */
typedef struct knotd_qdata_extra {
- const zone_t *zone; /*!< Zone from which is answered. */
+ zone_t *zone; /*!< Zone from which is answered. */
const zone_contents_t *contents; /*!< Zone contents from which is answered. */
list_t wildcards; /*!< Visited wildcards. */
list_t rrsigs; /*!< Section RRSIGs. */
pthread_mutex_unlock(&zone->preferred_lock);
}
-void zone_set_flag(zone_t *zone, zone_flag_t flag)
+static void set_flag(zone_t *zone, zone_flag_t flag, bool remove)
{
if (zone == NULL) {
return;
}
pthread_mutex_lock(&zone->preferred_lock); // this mutex seems OK to be reused for this
- zone->flags |= flag;
+ zone->flags = remove ? (zone->flags & ~flag) : (zone->flags | flag);
pthread_mutex_unlock(&zone->preferred_lock);
if (flag & ZONE_IS_CATALOG) {
}
}
+void zone_set_flag(zone_t *zone, zone_flag_t flag)
+{
+ return set_flag(zone, flag, false);
+}
+
+void zone_unset_flag(zone_t *zone, zone_flag_t flag)
+{
+ return set_flag(zone, flag, true);
+}
+
zone_flag_t zone_get_flag(zone_t *zone, zone_flag_t flag, bool clear)
{
if (zone == NULL) {
ZONE_FORCE_ZSK_ROLL = 1 << 4, /*!< Force ZSK rollover. */
ZONE_IS_CATALOG = 1 << 5, /*!< This is a catalog. */
ZONE_IS_CAT_MEMBER = 1 << 6, /*!< This zone exists according to a catalog. */
+ ZONE_XFR_FROZEN = 1 << 7, /*!< Outgoing AXFR/IXFR temporarily disabled. */
} zone_flag_t;
/*!
/*! \brief Sets a zone flag. */
void zone_set_flag(zone_t *zone, zone_flag_t flag);
+/*! \brief Unsets a zone flag. */
+void zone_unset_flag(zone_t *zone, zone_flag_t flag);
+
/*! \brief Returns if a flag is set (and optionally clears it). */
zone_flag_t zone_get_flag(zone_t *zone, zone_flag_t flag, bool clear);
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2022 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 CMD_ZONE_KSK_SBM "zone-ksk-submitted"
#define CMD_ZONE_FREEZE "zone-freeze"
#define CMD_ZONE_THAW "zone-thaw"
+#define CMD_ZONE_XFR_FREEZE "zone-xfr-freeze"
+#define CMD_ZONE_XFR_THAW "zone-xfr-thaw"
#define CMD_ZONE_READ "zone-read"
#define CMD_ZONE_BEGIN "zone-begin"
case CTL_ZONE_KSK_SBM:
case CTL_ZONE_FREEZE:
case CTL_ZONE_THAW:
+ case CTL_ZONE_XFR_FREEZE:
+ case CTL_ZONE_XFR_THAW:
case CTL_ZONE_BEGIN:
case CTL_ZONE_COMMIT:
case CTL_ZONE_ABORT:
{ CMD_ZONE_KSK_SBM, cmd_zone_ctl, CTL_ZONE_KSK_SBM, CMD_FREQ_ZONE | CMD_FOPT_ZONE },
{ CMD_ZONE_FREEZE, cmd_zone_ctl, CTL_ZONE_FREEZE, CMD_FOPT_ZONE },
{ CMD_ZONE_THAW, cmd_zone_ctl, CTL_ZONE_THAW, CMD_FOPT_ZONE },
+ { CMD_ZONE_XFR_FREEZE, cmd_zone_ctl, CTL_ZONE_XFR_FREEZE, CMD_FOPT_ZONE },
+ { CMD_ZONE_XFR_THAW, cmd_zone_ctl, CTL_ZONE_XFR_THAW, CMD_FOPT_ZONE },
{ CMD_ZONE_READ, cmd_zone_node_ctl, CTL_ZONE_READ, CMD_FREQ_ZONE },
{ CMD_ZONE_BEGIN, cmd_zone_ctl, CTL_ZONE_BEGIN, CMD_FREQ_ZONE | CMD_FOPT_ZONE },
{ CMD_ZONE_KSK_SBM, " <zone>...", "When KSK submission, confirm parent's DS presence. (#)" },
{ CMD_ZONE_FREEZE, "[<zone>...]", "Temporarily postpone automatic zone-changing events. (#)" },
{ CMD_ZONE_THAW, "[<zone>...]", "Dismiss zone freeze. (#)" },
+ { CMD_ZONE_XFR_FREEZE, "[<zone>...]", "Temporarily disable outgoing AXFR/IXFR. (#)" },
+ { CMD_ZONE_XFR_THAW, "[<zone>...]", "Dismiss outgoing XFR freeze. (#)" },
{ "", "", "" },
{ CMD_ZONE_READ, "<zone> [<owner> [<type>]]", "Get zone data that are currently being presented." },
{ CMD_ZONE_BEGIN, "<zone>...", "Begin a zone transaction." },
--- /dev/null
+#!/usr/bin/env python3
+
+'''Test of IXFR freeze.'''
+
+from dnstest.test import Test
+
+t = Test()
+
+master = t.server("knot")
+slave = t.server("knot")
+zone = t.zone_rnd(1, records=50)
+
+t.link(zone, master, slave)
+
+t.start()
+
+serial_init = master.zone_wait(zone)
+slave.zone_wait(zone)
+
+master.ctl("zone-xfr-freeze", wait=True)
+master.random_ddns(zone, allow_empty=False)
+t.sleep(4)
+
+resp = master.dig(zone[0].name, "AXFR", tries=1)
+resp.check_xfr(rcode="REFUSED")
+
+resp = slave.dig(zone[0].name, "SOA")
+serial = resp.soa_serial()
+if serial != serial_init:
+ set_err("SOA serial mismatch")
+ detail_log("SOA serial mismatch %d != %d" % (serial, serial_init))
+
+master.ctl("zone-xfr-thaw", wait=True)
+master.ctl("zone-notify")
+slave.zone_wait(zone, serial_init)
+t.xfr_diff(master, slave, zone)
+
+t.end()
changes = 0
with open(self.path, 'r') as file:
for fline in file:
- line = fline.split(None, 3)
+ line = fline.split(None, 4)
if len(line) < 3:
continue
if line[0][0] not in [";", "@"]:
dname = line[0]
+ ttl = 0
if line[1].isnumeric():
ttl = line[1]
- rtype = line[2]
- rdata = ' '.join(line[3:])
- else:
- ttl = 0
- rtype = line[1]
- rdata = ' '.join(line[2:])
+ del line[1]
+ if line[1] == "IN":
+ del line[1]
+ rtype = line[1]
+ rdata = ' '.join(line[2:])
if rtype not in ["SOA", "RRSIG", "DNSKEY", "DS", "CDS", "CDNSKEY", "NSEC", "NSEC3", "NSEC3PARAM"]:
try:
if random.randint(1, 20) in [4, 5]: