1999-08-20 05:35:16 +00:00
|
|
|
/*
|
2017-09-08 13:39:09 -07:00
|
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
1999-08-20 05:35:16 +00:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: MPL-2.0
|
2021-06-03 08:37:05 +02:00
|
|
|
*
|
1999-08-20 05:35:16 +00:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
2018-02-23 09:53:12 +01:00
|
|
|
*
|
1999-08-20 05:35:16 +00:00
|
|
|
* See the COPYRIGHT file distributed with this work for additional
|
2009-10-27 05:42:25 +00:00
|
|
|
* information regarding copyright ownership.
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
|
|
|
|
2018-03-28 14:56:40 +02:00
|
|
|
#include <inttypes.h>
|
2018-04-17 08:29:14 -07:00
|
|
|
#include <stdbool.h>
|
2018-03-28 14:56:40 +02:00
|
|
|
|
2022-10-26 23:35:47 -07:00
|
|
|
#include <isc/async.h>
|
2024-08-14 13:25:50 +02:00
|
|
|
#include <isc/log.h>
|
2008-01-02 05:13:42 +00:00
|
|
|
#include <isc/netaddr.h>
|
2008-04-01 01:37:25 +00:00
|
|
|
#include <isc/serial.h>
|
2009-01-27 22:30:00 +00:00
|
|
|
#include <isc/stats.h>
|
2000-05-09 15:02:20 +00:00
|
|
|
#include <isc/string.h>
|
2000-01-27 01:14:13 +00:00
|
|
|
#include <isc/util.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
|
1999-10-14 01:37:00 +00:00
|
|
|
#include <dns/db.h>
|
|
|
|
#include <dns/dbiterator.h>
|
2000-12-09 02:17:12 +00:00
|
|
|
#include <dns/diff.h>
|
1999-10-14 01:37:00 +00:00
|
|
|
#include <dns/dnssec.h>
|
2000-05-30 23:12:47 +00:00
|
|
|
#include <dns/fixedname.h>
|
1999-10-14 01:37:00 +00:00
|
|
|
#include <dns/journal.h>
|
2006-03-06 01:27:52 +00:00
|
|
|
#include <dns/keyvalues.h>
|
1999-10-14 01:37:00 +00:00
|
|
|
#include <dns/message.h>
|
2003-09-30 06:00:40 +00:00
|
|
|
#include <dns/nsec.h>
|
2008-09-24 02:46:23 +00:00
|
|
|
#include <dns/nsec3.h>
|
2009-10-08 23:13:07 +00:00
|
|
|
#include <dns/private.h>
|
2001-01-18 23:31:03 +00:00
|
|
|
#include <dns/rdataclass.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
#include <dns/rdataset.h>
|
|
|
|
#include <dns/rdatasetiter.h>
|
2005-05-19 04:59:05 +00:00
|
|
|
#include <dns/rdatastruct.h>
|
2001-11-20 05:04:41 +00:00
|
|
|
#include <dns/rdatatype.h>
|
2021-10-04 17:14:53 +02:00
|
|
|
#include <dns/result.h>
|
2001-05-31 10:38:01 +00:00
|
|
|
#include <dns/soa.h>
|
2000-03-06 19:08:05 +00:00
|
|
|
#include <dns/ssu.h>
|
2010-12-18 01:56:23 +00:00
|
|
|
#include <dns/tsig.h>
|
2011-07-01 02:25:48 +00:00
|
|
|
#include <dns/update.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
#include <dns/view.h>
|
1999-10-14 01:37:00 +00:00
|
|
|
#include <dns/zone.h>
|
|
|
|
#include <dns/zt.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2017-09-08 13:39:09 -07:00
|
|
|
#include <ns/client.h>
|
2017-10-06 15:13:27 -07:00
|
|
|
#include <ns/interfacemgr.h>
|
2017-09-08 13:39:09 -07:00
|
|
|
#include <ns/server.h>
|
|
|
|
#include <ns/stats.h>
|
|
|
|
#include <ns/update.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*! \file
|
|
|
|
* \brief
|
1999-08-20 05:35:16 +00:00
|
|
|
* This module implements dynamic update as in RFC2136.
|
|
|
|
*/
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/*
|
2006-12-04 01:54:53 +00:00
|
|
|
* XXX TODO:
|
|
|
|
* - document strict minimality
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
|
1999-10-11 20:00:24 +00:00
|
|
|
/**************************************************************************/
|
1999-10-25 20:23:13 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2001-01-18 23:31:03 +00:00
|
|
|
* Log level for tracing dynamic update protocol requests.
|
1999-10-25 20:23:13 +00:00
|
|
|
*/
|
2001-01-18 23:31:03 +00:00
|
|
|
#define LOGLEVEL_PROTOCOL ISC_LOG_INFO
|
1999-10-25 20:23:13 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2001-01-18 23:31:03 +00:00
|
|
|
* Log level for low-level debug tracing.
|
1999-10-25 20:23:13 +00:00
|
|
|
*/
|
2006-12-04 01:54:53 +00:00
|
|
|
#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8)
|
1999-10-25 20:23:13 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-10-11 20:00:24 +00:00
|
|
|
* Check an operation for failure. These macros all assume that
|
|
|
|
* the function using them has a 'result' variable and a 'failure'
|
|
|
|
* label.
|
|
|
|
*/
|
1999-10-25 20:23:13 +00:00
|
|
|
#define CHECK(op) \
|
2006-12-04 01:54:53 +00:00
|
|
|
do { \
|
|
|
|
result = (op); \
|
|
|
|
if (result != ISC_R_SUCCESS) \
|
|
|
|
goto failure; \
|
1999-10-25 20:23:13 +00:00
|
|
|
} while (0)
|
1999-10-11 20:00:24 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-10-11 20:00:24 +00:00
|
|
|
* Fail unconditionally with result 'code', which must not
|
2000-04-06 22:03:35 +00:00
|
|
|
* be ISC_R_SUCCESS. The reason for failure presumably has
|
1999-10-25 20:23:13 +00:00
|
|
|
* been logged already.
|
2000-05-10 03:33:57 +00:00
|
|
|
*
|
|
|
|
* The test against ISC_R_SUCCESS is there to keep the Solaris compiler
|
|
|
|
* from complaining about "end-of-loop code not reached".
|
1999-10-25 20:23:13 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#define FAIL(code) \
|
|
|
|
do { \
|
|
|
|
result = (code); \
|
2000-05-24 21:41:14 +00:00
|
|
|
if (result != ISC_R_SUCCESS) \
|
|
|
|
goto failure; \
|
1999-10-25 20:23:13 +00:00
|
|
|
} while (0)
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-10-25 20:23:13 +00:00
|
|
|
* Fail unconditionally and log as a client error.
|
2000-05-10 03:33:57 +00:00
|
|
|
* The test against ISC_R_SUCCESS is there to keep the Solaris compiler
|
|
|
|
* from complaining about "end-of-loop code not reached".
|
1999-10-11 20:00:24 +00:00
|
|
|
*/
|
1999-10-25 20:23:13 +00:00
|
|
|
#define FAILC(code, msg) \
|
|
|
|
do { \
|
2003-01-21 05:18:11 +00:00
|
|
|
const char *_what = "failed"; \
|
1999-11-02 19:17:39 +00:00
|
|
|
result = (code); \
|
2003-01-21 05:18:11 +00:00
|
|
|
switch (result) { \
|
|
|
|
case DNS_R_NXDOMAIN: \
|
|
|
|
case DNS_R_YXDOMAIN: \
|
|
|
|
case DNS_R_YXRRSET: \
|
|
|
|
case DNS_R_NXRRSET: \
|
|
|
|
_what = "unsuccessful"; \
|
2021-10-04 17:14:53 +02:00
|
|
|
default: \
|
|
|
|
break; \
|
2003-01-21 05:18:11 +00:00
|
|
|
} \
|
2006-12-04 01:54:53 +00:00
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL, \
|
|
|
|
"update %s: %s (%s)", _what, msg, \
|
|
|
|
isc_result_totext(result)); \
|
2000-05-24 18:25:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) \
|
|
|
|
goto failure; \
|
1999-10-25 20:23:13 +00:00
|
|
|
} while (0)
|
2008-04-03 05:55:52 +00:00
|
|
|
#define PREREQFAILC(code, msg) \
|
|
|
|
do { \
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, zone, ns_statscounter_updatebadprereq); \
|
2008-04-03 05:55:52 +00:00
|
|
|
FAILC(code, msg); \
|
|
|
|
} while (0)
|
1999-10-25 20:23:13 +00:00
|
|
|
|
2001-11-20 05:04:41 +00:00
|
|
|
#define FAILN(code, name, msg) \
|
|
|
|
do { \
|
2003-01-21 05:18:11 +00:00
|
|
|
const char *_what = "failed"; \
|
2001-11-20 05:04:41 +00:00
|
|
|
result = (code); \
|
2003-01-21 05:18:11 +00:00
|
|
|
switch (result) { \
|
|
|
|
case DNS_R_NXDOMAIN: \
|
|
|
|
case DNS_R_YXDOMAIN: \
|
|
|
|
case DNS_R_YXRRSET: \
|
|
|
|
case DNS_R_NXRRSET: \
|
|
|
|
_what = "unsuccessful"; \
|
2021-10-04 17:14:53 +02:00
|
|
|
default: \
|
|
|
|
break; \
|
2003-01-21 05:18:11 +00:00
|
|
|
} \
|
2024-08-13 18:20:26 +02:00
|
|
|
if (isc_log_wouldlog(LOGLEVEL_PROTOCOL)) { \
|
2001-11-20 05:04:41 +00:00
|
|
|
char _nbuf[DNS_NAME_FORMATSIZE]; \
|
|
|
|
dns_name_format(name, _nbuf, sizeof(_nbuf)); \
|
2006-12-04 01:54:53 +00:00
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL, \
|
2003-01-21 05:18:11 +00:00
|
|
|
"update %s: %s: %s (%s)", _what, _nbuf, \
|
2001-11-20 05:04:41 +00:00
|
|
|
msg, isc_result_totext(result)); \
|
|
|
|
} \
|
|
|
|
if (result != ISC_R_SUCCESS) \
|
|
|
|
goto failure; \
|
|
|
|
} while (0)
|
2008-04-03 05:55:52 +00:00
|
|
|
#define PREREQFAILN(code, name, msg) \
|
|
|
|
do { \
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, zone, ns_statscounter_updatebadprereq); \
|
2008-04-03 05:55:52 +00:00
|
|
|
FAILN(code, name, msg); \
|
|
|
|
} while (0)
|
2001-11-20 05:04:41 +00:00
|
|
|
|
|
|
|
#define FAILNT(code, name, type, msg) \
|
|
|
|
do { \
|
2003-01-21 05:18:11 +00:00
|
|
|
const char *_what = "failed"; \
|
2001-11-20 05:04:41 +00:00
|
|
|
result = (code); \
|
2003-01-21 05:18:11 +00:00
|
|
|
switch (result) { \
|
|
|
|
case DNS_R_NXDOMAIN: \
|
|
|
|
case DNS_R_YXDOMAIN: \
|
|
|
|
case DNS_R_YXRRSET: \
|
|
|
|
case DNS_R_NXRRSET: \
|
|
|
|
_what = "unsuccessful"; \
|
2021-10-04 17:14:53 +02:00
|
|
|
default: \
|
|
|
|
break; \
|
2003-01-21 05:18:11 +00:00
|
|
|
} \
|
2024-08-13 18:20:26 +02:00
|
|
|
if (isc_log_wouldlog(LOGLEVEL_PROTOCOL)) { \
|
2001-11-20 05:04:41 +00:00
|
|
|
char _nbuf[DNS_NAME_FORMATSIZE]; \
|
|
|
|
char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \
|
|
|
|
dns_name_format(name, _nbuf, sizeof(_nbuf)); \
|
|
|
|
dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \
|
2006-12-04 01:54:53 +00:00
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL, \
|
2003-01-21 05:18:11 +00:00
|
|
|
"update %s: %s/%s: %s (%s)", _what, _nbuf, \
|
2001-11-20 05:04:41 +00:00
|
|
|
_tbuf, msg, isc_result_totext(result)); \
|
|
|
|
} \
|
|
|
|
if (result != ISC_R_SUCCESS) \
|
|
|
|
goto failure; \
|
|
|
|
} while (0)
|
2008-04-03 05:55:52 +00:00
|
|
|
#define PREREQFAILNT(code, name, type, msg) \
|
|
|
|
do { \
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, zone, ns_statscounter_updatebadprereq); \
|
2008-04-03 05:55:52 +00:00
|
|
|
FAILNT(code, name, type, msg); \
|
|
|
|
} while (0)
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-10-25 20:23:13 +00:00
|
|
|
* Fail unconditionally and log as a server error.
|
2000-05-10 03:33:57 +00:00
|
|
|
* The test against ISC_R_SUCCESS is there to keep the Solaris compiler
|
|
|
|
* from complaining about "end-of-loop code not reached".
|
1999-10-25 20:23:13 +00:00
|
|
|
*/
|
|
|
|
#define FAILS(code, msg) \
|
|
|
|
do { \
|
1999-11-02 19:17:39 +00:00
|
|
|
result = (code); \
|
2003-01-21 05:18:11 +00:00
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL, "error: %s: %s", \
|
2006-12-04 01:54:53 +00:00
|
|
|
msg, isc_result_totext(result)); \
|
2000-05-24 18:25:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) \
|
|
|
|
goto failure; \
|
1999-10-25 20:23:13 +00:00
|
|
|
} while (0)
|
1999-10-11 20:00:24 +00:00
|
|
|
|
2008-01-02 05:13:42 +00:00
|
|
|
/*
|
2009-01-17 11:35:11 +00:00
|
|
|
* Return TRUE if NS_CLIENTATTR_TCP is set in the attributes other FALSE.
|
2008-01-02 05:13:42 +00:00
|
|
|
*/
|
2025-05-14 15:32:53 +02:00
|
|
|
#define TCPCLIENT(client) \
|
|
|
|
(((client)->inner.attributes & NS_CLIENTATTR_TCP) != 0)
|
2008-01-02 05:13:42 +00:00
|
|
|
|
1999-10-11 20:00:24 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
typedef struct rr rr_t;
|
|
|
|
|
|
|
|
struct rr {
|
|
|
|
/* dns_name_t name; */
|
2018-03-28 14:19:37 +02:00
|
|
|
uint32_t ttl;
|
2006-12-04 01:54:53 +00:00
|
|
|
dns_rdata_t rdata;
|
1999-10-14 00:03:04 +00:00
|
|
|
};
|
|
|
|
|
2022-10-26 23:35:47 -07:00
|
|
|
typedef struct update update_t;
|
1999-10-14 00:03:04 +00:00
|
|
|
|
2022-10-26 23:35:47 -07:00
|
|
|
struct update {
|
2006-12-04 01:54:53 +00:00
|
|
|
dns_zone_t *zone;
|
2022-10-26 23:35:47 -07:00
|
|
|
ns_client_t *client;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
2000-12-28 01:29:09 +00:00
|
|
|
dns_message_t *answer;
|
2024-11-20 01:20:42 +11:00
|
|
|
unsigned int *maxbytype;
|
|
|
|
size_t maxbytypelen;
|
1999-08-20 05:35:16 +00:00
|
|
|
};
|
|
|
|
|
2015-02-27 15:08:38 +11:00
|
|
|
/*%
|
|
|
|
* Prepare an RR for the addition of the new RR 'ctx->update_rr',
|
|
|
|
* with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting
|
|
|
|
* the RRs if it is replaced by the new RR or has a conflicting TTL.
|
|
|
|
* The necessary changes are appended to ctx->del_diff and ctx->add_diff;
|
|
|
|
* we need to do all deletions before any additions so that we don't run
|
|
|
|
* into transient states with conflicting TTLs.
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
dns_db_t *db;
|
|
|
|
dns_dbversion_t *ver;
|
|
|
|
dns_diff_t *diff;
|
|
|
|
dns_name_t *name;
|
|
|
|
dns_name_t *oldname;
|
|
|
|
dns_rdata_t *update_rr;
|
|
|
|
dns_ttl_t update_rr_ttl;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool ignore_add;
|
2015-02-27 15:08:38 +11:00
|
|
|
dns_diff_t del_diff;
|
|
|
|
dns_diff_t add_diff;
|
|
|
|
} add_rr_prepare_ctx_t;
|
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* Forward declarations.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
2022-10-26 23:35:47 -07:00
|
|
|
update_action(void *arg);
|
1999-10-14 00:03:04 +00:00
|
|
|
static void
|
2022-10-26 23:35:47 -07:00
|
|
|
updatedone_action(void *arg);
|
2000-09-13 01:30:34 +00:00
|
|
|
static isc_result_t
|
2022-10-26 23:35:47 -07:00
|
|
|
send_forward(ns_client_t *client, dns_zone_t *zone);
|
2000-12-28 01:29:09 +00:00
|
|
|
static void
|
2022-10-26 23:35:47 -07:00
|
|
|
forward_done(void *arg);
|
2015-02-27 15:08:38 +11:00
|
|
|
static isc_result_t
|
|
|
|
add_rr_prepare_action(void *data, rr_t *rr);
|
2022-11-08 17:32:41 -08:00
|
|
|
static isc_result_t
|
|
|
|
rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
|
|
|
const dns_rdata_t *rdata, bool *flag);
|
1999-10-14 00:03:04 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/**************************************************************************/
|
2001-01-18 23:31:03 +00:00
|
|
|
|
2002-01-22 21:45:07 +00:00
|
|
|
static void
|
|
|
|
update_log(ns_client_t *client, dns_zone_t *zone, int level, const char *fmt,
|
|
|
|
...) ISC_FORMAT_PRINTF(4, 5);
|
|
|
|
|
2001-01-18 23:31:03 +00:00
|
|
|
static void
|
|
|
|
update_log(ns_client_t *client, dns_zone_t *zone, int level, const char *fmt,
|
|
|
|
...) {
|
|
|
|
va_list ap;
|
|
|
|
char message[4096];
|
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char classbuf[DNS_RDATACLASS_FORMATSIZE];
|
|
|
|
|
2018-10-02 22:33:17 -07:00
|
|
|
if (client == NULL) {
|
2001-01-18 23:31:03 +00:00
|
|
|
return;
|
2018-10-02 22:33:17 -07:00
|
|
|
}
|
2001-01-18 23:31:03 +00:00
|
|
|
|
2024-08-13 18:20:26 +02:00
|
|
|
if (!isc_log_wouldlog(level)) {
|
2001-01-18 23:31:03 +00:00
|
|
|
return;
|
2018-10-02 22:33:17 -07:00
|
|
|
}
|
2001-01-18 23:31:03 +00:00
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2001-09-19 23:08:24 +00:00
|
|
|
vsnprintf(message, sizeof(message), fmt, ap);
|
2001-01-18 23:31:03 +00:00
|
|
|
va_end(ap);
|
|
|
|
|
2018-10-02 22:33:17 -07:00
|
|
|
if (zone != NULL) {
|
|
|
|
dns_name_format(dns_zone_getorigin(zone), namebuf,
|
|
|
|
sizeof(namebuf));
|
|
|
|
dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
|
|
|
|
sizeof(classbuf));
|
|
|
|
|
|
|
|
ns_client_log(client, NS_LOGCATEGORY_UPDATE,
|
|
|
|
NS_LOGMODULE_UPDATE, level,
|
|
|
|
"updating zone '%s/%s': %s", namebuf, classbuf,
|
|
|
|
message);
|
|
|
|
} else {
|
|
|
|
ns_client_log(client, NS_LOGCATEGORY_UPDATE,
|
|
|
|
NS_LOGMODULE_UPDATE, level, "%s", message);
|
|
|
|
}
|
2001-01-18 23:31:03 +00:00
|
|
|
}
|
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
static void
|
|
|
|
update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) {
|
|
|
|
update_log(arg, zone, level, "%s", message);
|
|
|
|
}
|
|
|
|
|
2008-04-03 05:55:52 +00:00
|
|
|
/*%
|
|
|
|
* Increment updated-related statistics counters.
|
|
|
|
*/
|
2021-10-11 13:43:12 +02:00
|
|
|
static void
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(ns_client_t *client, dns_zone_t *zone, isc_statscounter_t counter) {
|
2022-03-23 13:57:15 +01:00
|
|
|
ns_stats_increment(client->manager->sctx->nsstats, counter);
|
2008-04-03 05:55:52 +00:00
|
|
|
|
|
|
|
if (zone != NULL) {
|
2024-12-06 19:46:39 +01:00
|
|
|
isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
|
2008-04-03 05:55:52 +00:00
|
|
|
if (zonestats != NULL) {
|
2009-01-27 22:30:00 +00:00
|
|
|
isc_stats_increment(zonestats, counter);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-04-03 05:55:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-27 05:42:25 +00:00
|
|
|
/*%
|
|
|
|
* Check if we could have queried for the contents of this zone or
|
|
|
|
* if the zone is potentially updateable.
|
|
|
|
* If the zone can potentially be updated and the check failed then
|
|
|
|
* log a error otherwise we log a informational message.
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
checkqueryacl(ns_client_t *client, dns_acl_t *queryacl, dns_name_t *zonename,
|
|
|
|
dns_acl_t *updateacl, dns_ssutable_t *ssutable) {
|
2022-11-08 17:32:41 -08:00
|
|
|
isc_result_t result;
|
2009-10-27 05:42:25 +00:00
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char classbuf[DNS_RDATACLASS_FORMATSIZE];
|
2022-11-08 17:32:41 -08:00
|
|
|
bool update_possible =
|
|
|
|
((updateacl != NULL && !dns_acl_isnone(updateacl)) ||
|
|
|
|
ssutable != NULL);
|
2009-10-27 05:42:25 +00:00
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
result = ns_client_checkaclsilent(client, NULL, queryacl, true);
|
2009-10-27 05:42:25 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2022-11-08 17:32:41 -08:00
|
|
|
int level = update_possible ? ISC_LOG_ERROR : ISC_LOG_INFO;
|
|
|
|
|
2009-10-27 05:42:25 +00:00
|
|
|
dns_name_format(zonename, namebuf, sizeof(namebuf));
|
2025-05-14 15:32:53 +02:00
|
|
|
dns_rdataclass_format(client->inner.view->rdclass, classbuf,
|
2009-10-27 05:42:25 +00:00
|
|
|
sizeof(classbuf));
|
|
|
|
|
|
|
|
ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
|
|
|
|
NS_LOGMODULE_UPDATE, level,
|
|
|
|
"update '%s/%s' denied due to allow-query",
|
|
|
|
namebuf, classbuf);
|
2022-11-08 17:32:41 -08:00
|
|
|
} else if (!update_possible) {
|
2009-11-09 01:28:32 +00:00
|
|
|
dns_name_format(zonename, namebuf, sizeof(namebuf));
|
2025-05-14 15:32:53 +02:00
|
|
|
dns_rdataclass_format(client->inner.view->rdclass, classbuf,
|
2009-11-09 01:28:32 +00:00
|
|
|
sizeof(classbuf));
|
|
|
|
|
2009-10-27 05:42:25 +00:00
|
|
|
result = DNS_R_REFUSED;
|
|
|
|
ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
|
|
|
|
NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
|
|
|
|
"update '%s/%s' denied", namebuf, classbuf);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2007-08-27 04:31:42 +00:00
|
|
|
/*%
|
|
|
|
* Override the default acl logging when checking whether a client
|
|
|
|
* can update the zone or whether we can forward the request to the
|
2021-10-05 11:07:44 +02:00
|
|
|
* primary server based on IP address.
|
2007-08-27 04:31:42 +00:00
|
|
|
*
|
|
|
|
* 'message' contains the type of operation that is being attempted.
|
2021-10-05 11:07:44 +02:00
|
|
|
*
|
|
|
|
* 'secondary' indicates whether this is a secondary zone.
|
|
|
|
*
|
2007-08-27 04:31:42 +00:00
|
|
|
* If the zone has no access controls configured ('acl' == NULL &&
|
2021-10-05 11:07:44 +02:00
|
|
|
* 'has_ssutable == false`), log the attempt at info, otherwise at error.
|
|
|
|
* If 'secondary' is true, log at debug=3.
|
2007-08-27 04:31:42 +00:00
|
|
|
*
|
2021-10-05 11:07:44 +02:00
|
|
|
* If the request was signed, log that we received it.
|
2007-08-27 04:31:42 +00:00
|
|
|
*/
|
2002-06-12 06:29:49 +00:00
|
|
|
static isc_result_t
|
|
|
|
checkupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message,
|
2021-10-05 11:28:24 +02:00
|
|
|
dns_name_t *zonename, bool secondary, bool has_ssutable) {
|
2002-06-12 06:29:49 +00:00
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char classbuf[DNS_RDATACLASS_FORMATSIZE];
|
|
|
|
int level = ISC_LOG_ERROR;
|
|
|
|
const char *msg = "denied";
|
|
|
|
isc_result_t result;
|
|
|
|
|
2021-10-05 11:28:24 +02:00
|
|
|
if (secondary && acl == NULL) {
|
2004-06-20 23:57:13 +00:00
|
|
|
result = DNS_R_NOTIMP;
|
|
|
|
level = ISC_LOG_DEBUG(3);
|
|
|
|
msg = "disabled";
|
2007-08-27 04:31:42 +00:00
|
|
|
} else {
|
2018-04-17 08:29:14 -07:00
|
|
|
result = ns_client_checkaclsilent(client, NULL, acl, false);
|
2007-08-27 04:31:42 +00:00
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
level = ISC_LOG_DEBUG(3);
|
|
|
|
msg = "approved";
|
|
|
|
} else if (acl == NULL && !has_ssutable) {
|
|
|
|
level = ISC_LOG_INFO;
|
|
|
|
}
|
2002-06-12 06:29:49 +00:00
|
|
|
}
|
|
|
|
|
2025-05-14 15:32:53 +02:00
|
|
|
if (client->inner.signer != NULL) {
|
|
|
|
dns_name_format(client->inner.signer, namebuf, sizeof(namebuf));
|
2006-12-04 01:54:53 +00:00
|
|
|
ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
|
|
|
|
NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
|
|
|
|
"signer \"%s\" %s", namebuf, msg);
|
|
|
|
}
|
|
|
|
|
2002-06-12 06:29:49 +00:00
|
|
|
dns_name_format(zonename, namebuf, sizeof(namebuf));
|
2025-05-14 15:32:53 +02:00
|
|
|
dns_rdataclass_format(client->inner.view->rdclass, classbuf,
|
2002-06-12 06:29:49 +00:00
|
|
|
sizeof(classbuf));
|
|
|
|
|
|
|
|
ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
|
2006-12-04 01:54:53 +00:00
|
|
|
NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s", message,
|
|
|
|
namebuf, classbuf, msg);
|
2002-06-12 06:29:49 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Update a single RR in version 'ver' of 'db' and log the
|
|
|
|
* update in 'diff'.
|
1999-09-09 08:58:00 +00:00
|
|
|
*
|
1999-08-20 05:35:16 +00:00
|
|
|
* Ensures:
|
2006-12-04 01:54:53 +00:00
|
|
|
* \li '*tuple' == NULL. Either the tuple is freed, or its
|
|
|
|
* ownership has been transferred to the diff.
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2006-12-04 01:54:53 +00:00
|
|
|
do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_diff_t *diff) {
|
|
|
|
dns_diff_t temp_diff;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2000-05-24 03:26:35 +00:00
|
|
|
/*
|
|
|
|
* Create a singleton diff.
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_diff_init(diff->mctx, &temp_diff);
|
|
|
|
ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
|
|
|
|
|
2000-05-24 03:26:35 +00:00
|
|
|
/*
|
|
|
|
* Apply it to the database.
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
result = dns_diff_apply(&temp_diff, db, ver);
|
2000-10-20 13:29:37 +00:00
|
|
|
ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_difftuple_free(tuple);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2000-05-24 03:26:35 +00:00
|
|
|
/*
|
|
|
|
* Merge it into the current pending journal entry.
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_diff_appendminimal(diff, tuple);
|
|
|
|
|
2000-05-24 03:26:35 +00:00
|
|
|
/*
|
|
|
|
* Do not clear temp_diff.
|
|
|
|
*/
|
2000-04-06 22:03:35 +00:00
|
|
|
return ISC_R_SUCCESS;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-12-16 00:58:03 +00:00
|
|
|
* Perform the updates in 'updates' in version 'ver' of 'db' and log the
|
|
|
|
* update in 'diff'.
|
|
|
|
*
|
|
|
|
* Ensures:
|
2006-12-04 01:54:53 +00:00
|
|
|
* \li 'updates' is empty.
|
2000-12-16 00:58:03 +00:00
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
do_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver,
|
|
|
|
dns_diff_t *diff) {
|
|
|
|
isc_result_t result;
|
2025-05-23 13:02:22 -07:00
|
|
|
ISC_LIST_FOREACH(updates->tuples, t, link) {
|
2000-12-16 00:58:03 +00:00
|
|
|
ISC_LIST_UNLINK(updates->tuples, t, link);
|
|
|
|
CHECK(do_one_tuple(&t, db, ver, diff));
|
|
|
|
}
|
|
|
|
return ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
failure:
|
|
|
|
dns_diff_clear(diff);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
|
2006-12-04 01:54:53 +00:00
|
|
|
dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
|
|
|
|
dns_rdata_t *rdata) {
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_difftuple_t *tuple = NULL;
|
2024-07-11 17:00:38 -07:00
|
|
|
|
|
|
|
dns_difftuple_create(diff->mctx, op, name, ttl, rdata, &tuple);
|
1999-08-20 05:35:16 +00:00
|
|
|
return do_one_tuple(&tuple, db, ver, diff);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* Callback-style iteration over rdatasets and rdatas.
|
|
|
|
*
|
|
|
|
* foreach_rrset() can be used to iterate over the RRsets
|
|
|
|
* of a name and call a callback function with each
|
|
|
|
* one. Similarly, foreach_rr() can be used to iterate
|
|
|
|
* over the individual RRs at name, optionally restricted
|
|
|
|
* to RRs of a given type.
|
|
|
|
*
|
|
|
|
* The callback functions are called "actions" and take
|
|
|
|
* two arguments: a void pointer for passing arbitrary
|
|
|
|
* context information, and a pointer to the current RRset
|
|
|
|
* or RR. By convention, their names end in "_action".
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXXRTH We might want to make this public somewhere in libdns.
|
|
|
|
*/
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Function type for foreach_rrset() iterator actions.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
typedef isc_result_t
|
|
|
|
rrset_func(void *data, dns_rdataset_t *rrset);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Function type for foreach_rr() iterator actions.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
typedef isc_result_t
|
|
|
|
rr_func(void *data, rr_t *rr);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Internal context struct for foreach_node_rr().
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
typedef struct {
|
|
|
|
rr_func *rr_action;
|
|
|
|
void *rr_action_data;
|
|
|
|
} foreach_node_rr_ctx_t;
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Internal helper function for foreach_node_rr().
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2000-05-24 03:26:35 +00:00
|
|
|
foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
foreach_node_rr_ctx_t *ctx = data;
|
2025-03-21 23:32:27 -07:00
|
|
|
DNS_RDATASET_FOREACH(rdataset) {
|
2000-10-25 04:26:57 +00:00
|
|
|
rr_t rr = { 0, DNS_RDATA_INIT };
|
2008-01-18 23:46:58 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_rdataset_current(rdataset, &rr.rdata);
|
|
|
|
rr.ttl = rdataset->ttl;
|
|
|
|
result = (*ctx->rr_action)(ctx->rr_action_data, &rr);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-20 05:35:16 +00:00
|
|
|
return result;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2000-04-06 22:03:35 +00:00
|
|
|
return ISC_R_SUCCESS;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* For each rdataset of 'name' in 'ver' of 'db', call 'action'
|
|
|
|
* with the rdataset and 'action_data' as arguments. If the name
|
|
|
|
* does not exist, do nothing.
|
|
|
|
*
|
|
|
|
* If 'action' returns an error, abort iteration and return the error.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2006-12-04 01:54:53 +00:00
|
|
|
foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
|
|
|
rrset_func *action, void *action_data) {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_dbnode_t *node;
|
|
|
|
dns_rdatasetiter_t *iter;
|
2014-04-25 13:06:30 -07:00
|
|
|
dns_clientinfomethods_t cm;
|
|
|
|
dns_clientinfo_t ci;
|
|
|
|
dns_dbversion_t *oldver = NULL;
|
|
|
|
|
|
|
|
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only set the clientinfo 'versionp' if the new version is
|
|
|
|
* different from the current version
|
|
|
|
*/
|
|
|
|
dns_db_currentversion(db, &oldver);
|
2022-01-31 12:10:29 -08:00
|
|
|
dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
|
2018-04-17 08:29:14 -07:00
|
|
|
dns_db_closeversion(db, &oldver, false);
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
node = NULL;
|
2018-04-17 08:29:14 -07:00
|
|
|
result = dns_db_findnodeext(db, name, false, &cm, &ci, &node);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result == ISC_R_NOTFOUND) {
|
|
|
|
return ISC_R_SUCCESS;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-20 05:35:16 +00:00
|
|
|
return result;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
iter = NULL;
|
2022-11-16 10:47:40 +11:00
|
|
|
result = dns_db_allrdatasets(db, node, ver, 0, (isc_stdtime_t)0, &iter);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-20 05:35:16 +00:00
|
|
|
goto cleanup_node;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2025-03-26 22:49:03 -07:00
|
|
|
DNS_RDATASETITER_FOREACH(iter) {
|
|
|
|
dns_rdataset_t rdataset = DNS_RDATASET_INIT;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_rdatasetiter_current(iter, &rdataset);
|
|
|
|
|
|
|
|
result = (*action)(action_data, &rdataset);
|
|
|
|
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2025-03-26 22:49:03 -07:00
|
|
|
break;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
dns_rdatasetiter_destroy(&iter);
|
|
|
|
|
|
|
|
cleanup_node:
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* For each RR of 'name' in 'ver' of 'db', call 'action'
|
|
|
|
* with the RR and 'action_data' as arguments. If the name
|
|
|
|
* does not exist, do nothing.
|
|
|
|
*
|
|
|
|
* If 'action' returns an error, abort iteration
|
|
|
|
* and return the error.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2006-12-04 01:54:53 +00:00
|
|
|
foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
|
|
|
rr_func *rr_action, void *rr_action_data) {
|
1999-08-20 05:35:16 +00:00
|
|
|
foreach_node_rr_ctx_t ctx;
|
|
|
|
ctx.rr_action = rr_action;
|
|
|
|
ctx.rr_action_data = rr_action_data;
|
|
|
|
return foreach_rrset(db, ver, name, foreach_node_rr_action, &ctx);
|
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-31 22:14:06 +00:00
|
|
|
* For each of the RRs specified by 'db', 'ver', 'name', 'type',
|
|
|
|
* (which can be dns_rdatatype_any to match any type), and 'covers', call
|
|
|
|
* 'action' with the RR and 'action_data' as arguments. If the name
|
1999-08-20 05:35:16 +00:00
|
|
|
* does not exist, or if no RRset of the given type exists at the name,
|
|
|
|
* do nothing.
|
|
|
|
*
|
|
|
|
* If 'action' returns an error, abort iteration and return the error.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2006-12-04 01:54:53 +00:00
|
|
|
foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
|
|
|
dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action,
|
1999-08-20 05:35:16 +00:00
|
|
|
void *rr_action_data) {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_dbnode_t *node;
|
|
|
|
dns_rdataset_t rdataset;
|
2014-04-25 13:06:30 -07:00
|
|
|
dns_clientinfomethods_t cm;
|
|
|
|
dns_clientinfo_t ci;
|
|
|
|
dns_dbversion_t *oldver = NULL;
|
2015-02-27 15:08:38 +11:00
|
|
|
dns_fixedname_t fixed;
|
2014-04-25 13:06:30 -07:00
|
|
|
|
|
|
|
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only set the clientinfo 'versionp' if the new version is
|
|
|
|
* different from the current version
|
|
|
|
*/
|
|
|
|
dns_db_currentversion(db, &oldver);
|
2022-01-31 12:10:29 -08:00
|
|
|
dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
|
2018-04-17 08:29:14 -07:00
|
|
|
dns_db_closeversion(db, &oldver, false);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
if (type == dns_rdatatype_any) {
|
|
|
|
return foreach_node_rr(db, ver, name, rr_action,
|
|
|
|
rr_action_data);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
node = NULL;
|
2008-09-24 02:46:23 +00:00
|
|
|
if (type == dns_rdatatype_nsec3 ||
|
|
|
|
(type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3))
|
|
|
|
{
|
2018-04-17 08:29:14 -07:00
|
|
|
result = dns_db_findnsec3node(db, name, false, &node);
|
2008-09-24 02:46:23 +00:00
|
|
|
} else {
|
2018-04-17 08:29:14 -07:00
|
|
|
result = dns_db_findnodeext(db, name, false, &cm, &ci, &node);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result == ISC_R_NOTFOUND) {
|
|
|
|
return ISC_R_SUCCESS;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-20 05:35:16 +00:00
|
|
|
return result;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
dns_rdataset_init(&rdataset);
|
1999-08-31 22:14:06 +00:00
|
|
|
result = dns_db_findrdataset(db, node, ver, type, covers,
|
1999-09-08 01:10:26 +00:00
|
|
|
(isc_stdtime_t)0, &rdataset, NULL);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result == ISC_R_NOTFOUND) {
|
|
|
|
result = ISC_R_SUCCESS;
|
1999-08-20 05:35:16 +00:00
|
|
|
goto cleanup_node;
|
|
|
|
}
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-20 05:35:16 +00:00
|
|
|
goto cleanup_node;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2015-02-27 15:08:38 +11:00
|
|
|
if (rr_action == add_rr_prepare_action) {
|
|
|
|
add_rr_prepare_ctx_t *ctx = rr_action_data;
|
|
|
|
|
2018-03-28 14:38:09 +02:00
|
|
|
ctx->oldname = dns_fixedname_initname(&fixed);
|
2021-05-21 17:20:44 -07:00
|
|
|
dns_name_copy(name, ctx->oldname);
|
2015-02-27 15:08:38 +11:00
|
|
|
dns_rdataset_getownercase(&rdataset, ctx->oldname);
|
|
|
|
}
|
|
|
|
|
2025-03-21 23:32:27 -07:00
|
|
|
DNS_RDATASET_FOREACH(&rdataset) {
|
2000-10-25 04:26:57 +00:00
|
|
|
rr_t rr = { 0, DNS_RDATA_INIT };
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_rdataset_current(&rdataset, &rr.rdata);
|
|
|
|
rr.ttl = rdataset.ttl;
|
|
|
|
result = (*rr_action)(rr_action_data, &rr);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-20 05:35:16 +00:00
|
|
|
goto cleanup_rdataset;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2000-04-06 22:03:35 +00:00
|
|
|
result = ISC_R_SUCCESS;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
cleanup_rdataset:
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
cleanup_node:
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* Various tests on the database contents (for prerequisites, etc).
|
|
|
|
*/
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Function type for predicate functions that compare a database RR 'db_rr'
|
|
|
|
* against an update RR 'update_rr'.
|
|
|
|
*/
|
2018-04-17 08:29:14 -07:00
|
|
|
typedef bool
|
|
|
|
rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2019-07-04 15:24:20 +10:00
|
|
|
static isc_result_t
|
|
|
|
count_action(void *data, rr_t *rr) {
|
|
|
|
unsigned int *ui = (unsigned int *)data;
|
|
|
|
|
|
|
|
UNUSED(rr);
|
|
|
|
|
|
|
|
(*ui)++;
|
|
|
|
|
|
|
|
return ISC_R_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Helper function for rrset_exists().
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2000-05-24 03:26:35 +00:00
|
|
|
rrset_exists_action(void *data, rr_t *rr) {
|
2000-01-27 01:14:13 +00:00
|
|
|
UNUSED(data);
|
|
|
|
UNUSED(rr);
|
2000-04-06 22:03:35 +00:00
|
|
|
return ISC_R_EXISTS;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-10-11 20:00:24 +00:00
|
|
|
* Utility macro for RR existence checking functions.
|
|
|
|
*
|
2000-04-06 22:03:35 +00:00
|
|
|
* If the variable 'result' has the value ISC_R_EXISTS or
|
2018-04-17 08:29:14 -07:00
|
|
|
* ISC_R_SUCCESS, set *exists to true or false,
|
1999-10-11 20:00:24 +00:00
|
|
|
* respectively, and return success.
|
|
|
|
*
|
|
|
|
* If 'result' has any other value, there was a failure.
|
|
|
|
* Return the failure result code and do not set *exists.
|
|
|
|
*
|
|
|
|
* This would be more readable as "do { if ... } while(0)",
|
|
|
|
* but that form generates tons of warnings on Solaris 2.6.
|
|
|
|
*/
|
2006-12-04 01:54:53 +00:00
|
|
|
#define RETURN_EXISTENCE_FLAG \
|
|
|
|
return ((result == ISC_R_EXISTS) \
|
2018-04-17 08:29:14 -07:00
|
|
|
? (*exists = true, ISC_R_SUCCESS) \
|
2000-04-06 22:03:35 +00:00
|
|
|
: ((result == ISC_R_SUCCESS) \
|
2018-04-17 08:29:14 -07:00
|
|
|
? (*exists = false, ISC_R_SUCCESS) \
|
2000-11-15 19:04:41 +00:00
|
|
|
: result))
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Set '*exists' to true iff an rrset of the given type exists,
|
|
|
|
* to false otherwise.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2006-12-04 01:54:53 +00:00
|
|
|
rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
|
|
|
dns_rdatatype_t type, dns_rdatatype_t covers, bool *exists) {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-31 22:14:06 +00:00
|
|
|
result = foreach_rr(db, ver, name, type, covers, rrset_exists_action,
|
1999-08-20 05:35:16 +00:00
|
|
|
NULL);
|
|
|
|
RETURN_EXISTENCE_FLAG;
|
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Helper function for cname_incompatible_rrset_exists.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2000-05-24 03:26:35 +00:00
|
|
|
cname_compatibility_action(void *data, dns_rdataset_t *rrset) {
|
2000-01-27 01:14:13 +00:00
|
|
|
UNUSED(data);
|
1999-08-20 05:35:16 +00:00
|
|
|
if (rrset->type != dns_rdatatype_cname &&
|
2022-11-02 19:33:14 +01:00
|
|
|
!dns_rdatatype_atcname(rrset->type))
|
|
|
|
{
|
2000-04-06 22:03:35 +00:00
|
|
|
return ISC_R_EXISTS;
|
2018-11-26 10:57:02 +11:00
|
|
|
}
|
2000-04-06 22:03:35 +00:00
|
|
|
return ISC_R_SUCCESS;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Check whether there is an rrset incompatible with adding a CNAME RR,
|
|
|
|
* i.e., anything but another CNAME (which can be replaced) or a
|
|
|
|
* DNSSEC RR (which can coexist).
|
|
|
|
*
|
2018-04-17 08:29:14 -07:00
|
|
|
* If such an incompatible rrset exists, set '*exists' to true.
|
|
|
|
* Otherwise, set it to false.
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
|
2018-04-17 08:29:14 -07:00
|
|
|
dns_name_t *name, bool *exists) {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
result = foreach_rrset(db, ver, name, cname_compatibility_action, NULL);
|
|
|
|
RETURN_EXISTENCE_FLAG;
|
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Helper function for rr_count().
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2000-05-24 03:26:35 +00:00
|
|
|
count_rr_action(void *data, rr_t *rr) {
|
1999-08-20 05:35:16 +00:00
|
|
|
int *countp = data;
|
2000-01-27 01:14:13 +00:00
|
|
|
UNUSED(rr);
|
1999-08-20 05:35:16 +00:00
|
|
|
(*countp)++;
|
2000-04-06 22:03:35 +00:00
|
|
|
return ISC_R_SUCCESS;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
rr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
1999-08-31 22:14:06 +00:00
|
|
|
dns_rdatatype_t type, dns_rdatatype_t covers, int *countp) {
|
1999-08-20 05:35:16 +00:00
|
|
|
*countp = 0;
|
1999-08-31 22:14:06 +00:00
|
|
|
return foreach_rr(db, ver, name, type, covers, count_rr_action, countp);
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Context struct and helper function for name_exists().
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2000-05-24 03:26:35 +00:00
|
|
|
name_exists_action(void *data, dns_rdataset_t *rrset) {
|
2000-01-27 01:14:13 +00:00
|
|
|
UNUSED(data);
|
|
|
|
UNUSED(rrset);
|
2000-04-06 22:03:35 +00:00
|
|
|
return ISC_R_EXISTS;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Set '*exists' to true iff the given name exists, to false otherwise.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
|
|
|
bool *exists) {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
result = foreach_rrset(db, ver, name, name_exists_action, NULL);
|
|
|
|
RETURN_EXISTENCE_FLAG;
|
|
|
|
}
|
|
|
|
|
2008-01-02 05:13:42 +00:00
|
|
|
/*
|
2009-01-17 11:35:11 +00:00
|
|
|
* 'ssu_check_t' is used to pass the arguments to
|
2008-01-02 05:13:42 +00:00
|
|
|
* dns_ssutable_checkrules() to the callback function
|
|
|
|
* ssu_checkrule().
|
|
|
|
*/
|
2000-03-06 19:08:05 +00:00
|
|
|
typedef struct {
|
2008-01-02 05:13:42 +00:00
|
|
|
/* The ownername of the record to be updated. */
|
2006-12-04 01:54:53 +00:00
|
|
|
dns_name_t *name;
|
2008-01-02 05:13:42 +00:00
|
|
|
|
|
|
|
/* The signature's name if the request was signed. */
|
2006-12-04 01:54:53 +00:00
|
|
|
dns_name_t *signer;
|
2008-01-02 05:13:42 +00:00
|
|
|
|
2017-10-06 15:13:27 -07:00
|
|
|
/* The address of the client. */
|
|
|
|
isc_netaddr_t *addr;
|
|
|
|
|
|
|
|
/* The ACL environment */
|
|
|
|
dns_aclenv_t *aclenv;
|
|
|
|
|
|
|
|
/* Whether the request was sent via TCP. */
|
2018-04-17 08:29:14 -07:00
|
|
|
bool tcp;
|
2008-01-02 05:13:42 +00:00
|
|
|
|
|
|
|
/* The ssu table to check against. */
|
2000-03-06 19:08:05 +00:00
|
|
|
dns_ssutable_t *table;
|
2010-12-18 01:56:23 +00:00
|
|
|
|
|
|
|
/* the key used for TKEY requests */
|
|
|
|
dst_key_t *key;
|
2000-03-06 19:08:05 +00:00
|
|
|
} ssu_check_t;
|
|
|
|
|
|
|
|
static isc_result_t
|
2000-05-24 03:26:35 +00:00
|
|
|
ssu_checkrule(void *data, dns_rdataset_t *rrset) {
|
2019-07-03 17:03:13 +10:00
|
|
|
ssu_check_t *ssuinfo = data;
|
2018-08-24 09:19:38 +10:00
|
|
|
bool rule_ok = false;
|
2000-03-06 19:08:05 +00:00
|
|
|
|
|
|
|
/*
|
2003-09-30 06:00:40 +00:00
|
|
|
* If we're deleting all records, it's ok to delete RRSIG and NSEC even
|
2000-03-06 19:08:05 +00:00
|
|
|
* if we're normally not allowed to.
|
|
|
|
*/
|
2003-09-30 06:00:40 +00:00
|
|
|
if (rrset->type == dns_rdatatype_rrsig ||
|
2022-11-02 19:33:14 +01:00
|
|
|
rrset->type == dns_rdatatype_nsec)
|
|
|
|
{
|
2004-10-12 21:57:57 +00:00
|
|
|
return ISC_R_SUCCESS;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2020-03-30 13:47:58 -07:00
|
|
|
|
2018-08-24 09:19:38 +10:00
|
|
|
/*
|
|
|
|
* krb5-subdomain-self-rhs and ms-subdomain-self-rhs need
|
|
|
|
* to check the PTR and SRV target names so extract them
|
|
|
|
* from the resource records.
|
|
|
|
*/
|
|
|
|
if (rrset->rdclass == dns_rdataclass_in &&
|
|
|
|
(rrset->type == dns_rdatatype_srv ||
|
|
|
|
rrset->type == dns_rdatatype_ptr))
|
|
|
|
{
|
|
|
|
dns_name_t *target = NULL;
|
|
|
|
dns_rdata_ptr_t ptr;
|
|
|
|
dns_rdata_in_srv_t srv;
|
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
dns_rdataset_clone(rrset, &rdataset);
|
|
|
|
|
2025-03-21 23:32:27 -07:00
|
|
|
DNS_RDATASET_FOREACH(&rdataset) {
|
2018-08-24 09:19:38 +10:00
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdataset_current(&rdataset, &rdata);
|
|
|
|
if (rrset->type == dns_rdatatype_ptr) {
|
|
|
|
result = dns_rdata_tostruct(&rdata, &ptr, NULL);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
target = &ptr.ptr;
|
|
|
|
}
|
|
|
|
if (rrset->type == dns_rdatatype_srv) {
|
|
|
|
result = dns_rdata_tostruct(&rdata, &srv, NULL);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
target = &srv.target;
|
|
|
|
}
|
|
|
|
rule_ok = dns_ssutable_checkrules(
|
|
|
|
ssuinfo->table, ssuinfo->signer, ssuinfo->name,
|
|
|
|
ssuinfo->addr, ssuinfo->tcp, ssuinfo->aclenv,
|
|
|
|
rrset->type, target, ssuinfo->key, NULL);
|
|
|
|
if (!rule_ok) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
} else {
|
|
|
|
rule_ok = dns_ssutable_checkrules(
|
|
|
|
ssuinfo->table, ssuinfo->signer, ssuinfo->name,
|
|
|
|
ssuinfo->addr, ssuinfo->tcp, ssuinfo->aclenv,
|
|
|
|
rrset->type, NULL, ssuinfo->key, NULL);
|
|
|
|
}
|
2020-03-30 13:47:58 -07:00
|
|
|
return rule_ok ? ISC_R_SUCCESS : ISC_R_FAILURE;
|
2000-03-06 19:08:05 +00:00
|
|
|
}
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
static bool
|
2000-03-06 19:08:05 +00:00
|
|
|
ssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
2008-01-02 05:13:42 +00:00
|
|
|
dns_ssutable_t *ssutable, dns_name_t *signer, isc_netaddr_t *addr,
|
2018-04-17 08:29:14 -07:00
|
|
|
dns_aclenv_t *aclenv, bool tcp, dst_key_t *key) {
|
2000-03-06 19:08:05 +00:00
|
|
|
isc_result_t result;
|
|
|
|
ssu_check_t ssuinfo;
|
|
|
|
|
|
|
|
ssuinfo.name = name;
|
|
|
|
ssuinfo.table = ssutable;
|
|
|
|
ssuinfo.signer = signer;
|
2017-10-06 15:13:27 -07:00
|
|
|
ssuinfo.addr = addr;
|
|
|
|
ssuinfo.aclenv = aclenv;
|
|
|
|
ssuinfo.tcp = tcp;
|
2010-12-18 01:56:23 +00:00
|
|
|
ssuinfo.key = key;
|
2000-03-06 19:08:05 +00:00
|
|
|
result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo);
|
2018-04-17 08:29:14 -07:00
|
|
|
return result == ISC_R_SUCCESS;
|
2000-03-06 19:08:05 +00:00
|
|
|
}
|
|
|
|
|
2018-08-24 09:19:38 +10:00
|
|
|
static isc_result_t
|
|
|
|
ssu_checkrr(void *data, rr_t *rr) {
|
|
|
|
isc_result_t result;
|
|
|
|
ssu_check_t *ssuinfo = data;
|
|
|
|
dns_name_t *target = NULL;
|
|
|
|
dns_rdata_ptr_t ptr;
|
|
|
|
dns_rdata_in_srv_t srv;
|
|
|
|
bool answer;
|
|
|
|
|
|
|
|
if (rr->rdata.type == dns_rdatatype_ptr) {
|
|
|
|
result = dns_rdata_tostruct(&rr->rdata, &ptr, NULL);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
target = &ptr.ptr;
|
|
|
|
}
|
|
|
|
if (rr->rdata.type == dns_rdatatype_srv) {
|
|
|
|
result = dns_rdata_tostruct(&rr->rdata, &srv, NULL);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
target = &srv.target;
|
|
|
|
}
|
|
|
|
|
|
|
|
answer = dns_ssutable_checkrules(
|
|
|
|
ssuinfo->table, ssuinfo->signer, ssuinfo->name, ssuinfo->addr,
|
|
|
|
ssuinfo->tcp, ssuinfo->aclenv, rr->rdata.type, target,
|
|
|
|
ssuinfo->key, NULL);
|
|
|
|
return answer ? ISC_R_SUCCESS : ISC_R_FAILURE;
|
|
|
|
}
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* Checking of "RRset exists (value dependent)" prerequisites.
|
|
|
|
*
|
|
|
|
* In the RFC2136 section 3.2.5, this is the pseudocode involving
|
|
|
|
* a variable called "temp", a mapping of <name, type> tuples to rrsets.
|
|
|
|
*
|
2009-01-17 11:35:11 +00:00
|
|
|
* Here, we represent the "temp" data structure as (non-minimal) "dns_diff_t"
|
|
|
|
* where each tuple has op==DNS_DIFFOP_EXISTS.
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Append a tuple asserting the existence of the RR with
|
|
|
|
* 'name' and 'rdata' to 'diff'.
|
|
|
|
*/
|
2024-07-11 17:00:38 -07:00
|
|
|
static void
|
2000-05-24 03:26:35 +00:00
|
|
|
temp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) {
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_difftuple_t *tuple = NULL;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-09-22 00:31:28 +00:00
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS, name, 0, rdata,
|
|
|
|
&tuple);
|
1999-08-20 05:35:16 +00:00
|
|
|
ISC_LIST_APPEND(diff->tuples, tuple, link);
|
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Compare two rdatasets represented as sorted lists of tuples.
|
|
|
|
* All list elements must have the same owner name and type.
|
2000-04-06 22:03:35 +00:00
|
|
|
* Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset)
|
1999-08-20 05:35:16 +00:00
|
|
|
* if not.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
temp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) {
|
|
|
|
for (;;) {
|
|
|
|
if (a == NULL || b == NULL) {
|
|
|
|
break;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-05-24 03:26:35 +00:00
|
|
|
INSIST(a->op == DNS_DIFFOP_EXISTS &&
|
|
|
|
b->op == DNS_DIFFOP_EXISTS);
|
1999-08-20 05:35:16 +00:00
|
|
|
INSIST(a->rdata.type == b->rdata.type);
|
|
|
|
INSIST(dns_name_equal(&a->name, &b->name));
|
2009-12-04 21:09:34 +00:00
|
|
|
if (dns_rdata_casecompare(&a->rdata, &b->rdata) != 0) {
|
1999-08-20 05:35:16 +00:00
|
|
|
return DNS_R_NXRRSET;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
a = ISC_LIST_NEXT(a, link);
|
|
|
|
b = ISC_LIST_NEXT(b, link);
|
|
|
|
}
|
|
|
|
if (a != NULL || b != NULL) {
|
|
|
|
return DNS_R_NXRRSET;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-04-06 22:03:35 +00:00
|
|
|
return ISC_R_SUCCESS;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* A comparison function defining the sorting order for the entries
|
|
|
|
* in the "temp" data structure. The major sort key is the owner name,
|
|
|
|
* followed by the type and rdata.
|
|
|
|
*/
|
|
|
|
static int
|
2000-05-24 03:26:35 +00:00
|
|
|
temp_order(const void *av, const void *bv) {
|
2001-03-12 22:49:29 +00:00
|
|
|
dns_difftuple_t const *const *ap = av;
|
|
|
|
dns_difftuple_t const *const *bp = bv;
|
|
|
|
dns_difftuple_t const *a = *ap;
|
|
|
|
dns_difftuple_t const *b = *bp;
|
1999-08-20 05:35:16 +00:00
|
|
|
int r;
|
|
|
|
r = dns_name_compare(&a->name, &b->name);
|
|
|
|
if (r != 0) {
|
|
|
|
return r;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
r = (b->rdata.type - a->rdata.type);
|
|
|
|
if (r != 0) {
|
|
|
|
return r;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-12-04 21:09:34 +00:00
|
|
|
r = dns_rdata_casecompare(&a->rdata, &b->rdata);
|
1999-08-20 05:35:16 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Check the "RRset exists (value dependent)" prerequisite information
|
|
|
|
* in 'temp' against the contents of the database 'db'.
|
|
|
|
*
|
2000-04-06 22:03:35 +00:00
|
|
|
* Return ISC_R_SUCCESS if the prerequisites are satisfied,
|
1999-08-20 05:35:16 +00:00
|
|
|
* rcode(dns_rcode_nxrrset) if not.
|
2001-11-20 05:04:41 +00:00
|
|
|
*
|
|
|
|
* 'temp' must be pre-sorted.
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
temp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db,
|
2001-11-20 05:04:41 +00:00
|
|
|
dns_dbversion_t *ver, dns_name_t *tmpname, dns_rdatatype_t *typep) {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_name_t *name;
|
|
|
|
dns_dbnode_t *node;
|
|
|
|
dns_difftuple_t *t;
|
|
|
|
dns_diff_t trash;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_diff_init(mctx, &trash);
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/*
|
|
|
|
* For each name and type in the prerequisites,
|
|
|
|
* construct a sorted rdata list of the corresponding
|
|
|
|
* database contents, and compare the lists.
|
|
|
|
*/
|
|
|
|
t = ISC_LIST_HEAD(temp->tuples);
|
|
|
|
while (t != NULL) {
|
|
|
|
name = &t->name;
|
2021-05-21 17:20:44 -07:00
|
|
|
dns_name_copy(name, tmpname);
|
2001-11-20 05:04:41 +00:00
|
|
|
*typep = t->rdata.type;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/* A new unique name begins here. */
|
|
|
|
node = NULL;
|
2018-04-17 08:29:14 -07:00
|
|
|
result = dns_db_findnode(db, name, false, &node);
|
2008-01-02 04:26:26 +00:00
|
|
|
if (result == ISC_R_NOTFOUND) {
|
|
|
|
dns_diff_clear(&trash);
|
1999-08-20 05:35:16 +00:00
|
|
|
return DNS_R_NXRRSET;
|
2008-01-02 04:26:26 +00:00
|
|
|
}
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
dns_diff_clear(&trash);
|
1999-08-20 05:35:16 +00:00
|
|
|
return result;
|
2008-01-02 04:26:26 +00:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/* A new unique type begins here. */
|
|
|
|
while (t != NULL && dns_name_equal(&t->name, name)) {
|
1999-08-31 22:14:06 +00:00
|
|
|
dns_rdatatype_t type, covers;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
dns_diff_t d_rrs; /* Database RRs with
|
|
|
|
* this name and type */
|
2006-12-04 01:54:53 +00:00
|
|
|
dns_diff_t u_rrs; /* Update RRs with
|
1999-08-20 05:35:16 +00:00
|
|
|
* this name and type */
|
|
|
|
|
2001-11-20 05:04:41 +00:00
|
|
|
*typep = type = t->rdata.type;
|
2025-03-04 15:51:49 -08:00
|
|
|
if (dns_rdatatype_issig(type)) {
|
1999-08-31 22:14:06 +00:00
|
|
|
covers = dns_rdata_covers(&t->rdata);
|
2009-07-28 15:45:43 +00:00
|
|
|
} else if (type == dns_rdatatype_any) {
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
2009-07-28 15:45:43 +00:00
|
|
|
dns_diff_clear(&trash);
|
|
|
|
return DNS_R_NXRRSET;
|
|
|
|
} else {
|
2025-08-11 07:17:43 +02:00
|
|
|
covers = dns_rdatatype_none;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Collect all database RRs for this name and type
|
|
|
|
* onto d_rrs and sort them.
|
|
|
|
*/
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
result = dns_db_findrdataset(db, node, ver, type,
|
1999-08-31 22:14:06 +00:00
|
|
|
covers, (isc_stdtime_t)0,
|
1999-09-08 01:10:26 +00:00
|
|
|
&rdataset, NULL);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
2008-01-02 04:26:26 +00:00
|
|
|
dns_diff_clear(&trash);
|
1999-08-20 05:35:16 +00:00
|
|
|
return DNS_R_NXRRSET;
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_diff_init(mctx, &d_rrs);
|
|
|
|
dns_diff_init(mctx, &u_rrs);
|
|
|
|
|
2025-03-21 23:32:27 -07:00
|
|
|
DNS_RDATASET_FOREACH(&rdataset) {
|
2000-10-25 04:26:57 +00:00
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_rdataset_current(&rdataset, &rdata);
|
2024-07-11 17:00:38 -07:00
|
|
|
temp_append(&d_rrs, name, &rdata);
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2025-03-21 23:32:27 -07:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
result = dns_diff_sort(&d_rrs, temp_order);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-20 05:35:16 +00:00
|
|
|
goto failure;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Collect all update RRs for this name and type
|
|
|
|
* onto u_rrs. No need to sort them here -
|
|
|
|
* they are already sorted.
|
|
|
|
*/
|
|
|
|
while (t != NULL && dns_name_equal(&t->name, name) &&
|
2022-11-02 19:33:14 +01:00
|
|
|
t->rdata.type == type)
|
|
|
|
{
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_difftuple_t *next = ISC_LIST_NEXT(t, link);
|
|
|
|
ISC_LIST_UNLINK(temp->tuples, t, link);
|
|
|
|
ISC_LIST_APPEND(u_rrs.tuples, t, link);
|
|
|
|
t = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare the two sorted lists. */
|
|
|
|
result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples),
|
|
|
|
ISC_LIST_HEAD(d_rrs.tuples));
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-20 05:35:16 +00:00
|
|
|
goto failure;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We are done with the tuples, but we can't free
|
|
|
|
* them yet because "name" still points into one
|
|
|
|
* of them. Move them on a temporary list.
|
|
|
|
*/
|
|
|
|
ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link);
|
|
|
|
ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link);
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
failure:
|
|
|
|
dns_diff_clear(&d_rrs);
|
|
|
|
dns_diff_clear(&u_rrs);
|
|
|
|
dns_diff_clear(&trash);
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
1999-08-20 05:35:16 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_diff_clear(&trash);
|
2000-04-06 22:03:35 +00:00
|
|
|
return ISC_R_SUCCESS;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* Conditional deletion of RRs.
|
|
|
|
*/
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Context structure for delete_if().
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
rr_predicate *predicate;
|
|
|
|
dns_db_t *db;
|
|
|
|
dns_dbversion_t *ver;
|
|
|
|
dns_diff_t *diff;
|
|
|
|
dns_name_t *name;
|
|
|
|
dns_rdata_t *update_rr;
|
|
|
|
} conditional_delete_ctx_t;
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Predicate functions for delete_if().
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2004-10-21 01:29:29 +00:00
|
|
|
* Return true iff 'db_rr' is neither a SOA nor an NS RR nor
|
2008-09-24 02:46:23 +00:00
|
|
|
* an RRSIG nor an NSEC3PARAM nor a NSEC.
|
2000-05-24 03:26:35 +00:00
|
|
|
*/
|
2018-04-17 08:29:14 -07:00
|
|
|
static bool
|
2000-05-24 03:26:35 +00:00
|
|
|
type_not_soa_nor_ns_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
|
2000-01-27 01:14:13 +00:00
|
|
|
UNUSED(update_rr);
|
1999-08-20 05:35:16 +00:00
|
|
|
return (db_rr->type != dns_rdatatype_soa &&
|
2004-10-21 01:29:29 +00:00
|
|
|
db_rr->type != dns_rdatatype_ns &&
|
2008-09-24 02:46:23 +00:00
|
|
|
db_rr->type != dns_rdatatype_nsec3param &&
|
2004-10-21 01:29:29 +00:00
|
|
|
db_rr->type != dns_rdatatype_rrsig &&
|
|
|
|
db_rr->type != dns_rdatatype_nsec)
|
2018-04-17 08:29:14 -07:00
|
|
|
? true
|
|
|
|
: false;
|
2004-10-21 01:29:29 +00:00
|
|
|
}
|
|
|
|
|
2005-07-18 06:03:01 +00:00
|
|
|
/*%
|
2004-10-21 01:29:29 +00:00
|
|
|
* Return true iff 'db_rr' is neither a RRSIG nor a NSEC.
|
|
|
|
*/
|
2018-04-17 08:29:14 -07:00
|
|
|
static bool
|
2004-10-21 01:29:29 +00:00
|
|
|
type_not_dnssec(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
|
|
|
|
UNUSED(update_rr);
|
|
|
|
return (db_rr->type != dns_rdatatype_rrsig &&
|
|
|
|
db_rr->type != dns_rdatatype_nsec)
|
2018-04-17 08:29:14 -07:00
|
|
|
? true
|
|
|
|
: false;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Return true always.
|
|
|
|
*/
|
2018-04-17 08:29:14 -07:00
|
|
|
static bool
|
2000-05-24 03:26:35 +00:00
|
|
|
true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
|
2000-01-27 01:14:13 +00:00
|
|
|
UNUSED(update_rr);
|
|
|
|
UNUSED(db_rr);
|
2018-04-17 08:29:14 -07:00
|
|
|
return true;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Return true iff the two RRs have identical rdata.
|
|
|
|
*/
|
2018-04-17 08:29:14 -07:00
|
|
|
static bool
|
1999-08-20 05:35:16 +00:00
|
|
|
rr_equal_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
|
|
|
|
/*
|
|
|
|
* XXXRTH This is not a problem, but we should consider creating
|
|
|
|
* dns_rdata_equal() (that used dns_name_equal()), since it
|
|
|
|
* would be faster. Not a priority.
|
|
|
|
*/
|
2009-12-04 21:09:34 +00:00
|
|
|
return dns_rdata_casecompare(update_rr, db_rr) == 0 ? true : false;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* Return true iff 'update_rr' should replace 'db_rr' according
|
|
|
|
* to the special RFC2136 rules for CNAME, SOA, and WKS records.
|
2000-10-20 22:34:50 +00:00
|
|
|
*
|
2003-09-30 06:00:40 +00:00
|
|
|
* RFC2136 does not mention NSEC or DNAME, but multiple NSECs or DNAMEs
|
2000-10-20 22:34:50 +00:00
|
|
|
* make little sense, so we replace those, too.
|
2008-04-01 01:37:25 +00:00
|
|
|
*
|
|
|
|
* Additionally replace RRSIG that have been generated by the same key
|
|
|
|
* for the same type. This simplifies refreshing a offline KSK by not
|
2009-01-17 11:35:11 +00:00
|
|
|
* requiring that the old RRSIG be deleted. It also simplifies key
|
2008-04-01 01:37:25 +00:00
|
|
|
* rollover by only requiring that the new RRSIG be added.
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
2018-04-17 08:29:14 -07:00
|
|
|
static bool
|
1999-08-20 05:35:16 +00:00
|
|
|
replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
|
2008-04-01 01:37:25 +00:00
|
|
|
dns_rdata_rrsig_t updatesig, dbsig;
|
|
|
|
isc_result_t result;
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
if (db_rr->type != update_rr->type) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return false;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
if (db_rr->type == dns_rdatatype_cname) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return true;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-10-20 22:34:50 +00:00
|
|
|
if (db_rr->type == dns_rdatatype_dname) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return true;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
if (db_rr->type == dns_rdatatype_soa) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return true;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2003-09-30 06:00:40 +00:00
|
|
|
if (db_rr->type == dns_rdatatype_nsec) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return true;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-04-01 01:37:25 +00:00
|
|
|
if (db_rr->type == dns_rdatatype_rrsig) {
|
|
|
|
/*
|
|
|
|
* Replace existing RRSIG with the same keyid,
|
|
|
|
* covered and algorithm.
|
|
|
|
*/
|
|
|
|
result = dns_rdata_tostruct(db_rr, &dbsig, NULL);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
result = dns_rdata_tostruct(update_rr, &updatesig, NULL);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
if (dbsig.keyid == updatesig.keyid &&
|
|
|
|
dbsig.covered == updatesig.covered &&
|
|
|
|
dbsig.algorithm == updatesig.algorithm)
|
|
|
|
{
|
2018-04-17 08:29:14 -07:00
|
|
|
return true;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-04-01 01:37:25 +00:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
if (db_rr->type == dns_rdatatype_wks) {
|
|
|
|
/*
|
|
|
|
* Compare the address and protocol fields only. These
|
|
|
|
* form the first five bytes of the RR data. Do a
|
|
|
|
* raw binary comparison; unpacking the WKS RRs using
|
2008-09-24 02:46:23 +00:00
|
|
|
* dns_rdata_tostruct() might be cleaner in some ways.
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
|
|
|
INSIST(db_rr->length >= 5 && update_rr->length >= 5);
|
|
|
|
return memcmp(db_rr->data, update_rr->data, 5) == 0 ? true
|
2018-04-17 08:29:14 -07:00
|
|
|
: false;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
|
|
|
if (db_rr->type == dns_rdatatype_nsec3param) {
|
|
|
|
if (db_rr->length != update_rr->length) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return false;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
INSIST(db_rr->length >= 4 && update_rr->length >= 4);
|
|
|
|
/*
|
2010-12-07 02:53:34 +00:00
|
|
|
* Replace NSEC3PARAM records that only differ by the
|
|
|
|
* flags field.
|
2008-09-24 02:46:23 +00:00
|
|
|
*/
|
|
|
|
if (db_rr->data[0] == update_rr->data[0] &&
|
|
|
|
memcmp(db_rr->data + 2, update_rr->data + 2,
|
|
|
|
update_rr->length - 2) == 0)
|
|
|
|
{
|
2018-04-17 08:29:14 -07:00
|
|
|
return true;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
}
|
2018-04-17 08:29:14 -07:00
|
|
|
return false;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-05-24 03:26:35 +00:00
|
|
|
* Internal helper function for delete_if().
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
delete_if_action(void *data, rr_t *rr) {
|
|
|
|
conditional_delete_ctx_t *ctx = data;
|
|
|
|
if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
result = update_one_rr(ctx->db, ctx->ver, ctx->diff,
|
|
|
|
DNS_DIFFOP_DEL, ctx->name, rr->ttl,
|
|
|
|
&rr->rdata);
|
2011-08-30 05:16:15 +00:00
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return ISC_R_SUCCESS;
|
1999-09-09 08:58:00 +00:00
|
|
|
}
|
2011-08-30 05:16:15 +00:00
|
|
|
}
|
1999-09-09 08:58:00 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
/*%
|
|
|
|
* Conditionally delete RRs. Apply 'predicate' to the RRs
|
|
|
|
* specified by 'db', 'ver', 'name', and 'type' (which can
|
|
|
|
* be dns_rdatatype_any to match any type). Delete those
|
|
|
|
* RRs for which the predicate returns true, and log the
|
|
|
|
* deletions in 'diff'.
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver,
|
|
|
|
dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers,
|
|
|
|
dns_rdata_t *update_rr, dns_diff_t *diff) {
|
|
|
|
conditional_delete_ctx_t ctx;
|
|
|
|
ctx.predicate = predicate;
|
|
|
|
ctx.db = db;
|
|
|
|
ctx.ver = ver;
|
|
|
|
ctx.diff = diff;
|
|
|
|
ctx.name = name;
|
|
|
|
ctx.update_rr = update_rr;
|
|
|
|
return foreach_rr(db, ver, name, type, covers, delete_if_action, &ctx);
|
|
|
|
}
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
/**************************************************************************/
|
2008-11-06 02:31:41 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
static isc_result_t
|
|
|
|
add_rr_prepare_action(void *data, rr_t *rr) {
|
|
|
|
add_rr_prepare_ctx_t *ctx = data;
|
|
|
|
dns_difftuple_t *tuple = NULL;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool equal, case_equal, ttl_equal;
|
2015-02-27 15:08:38 +11:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Are the new and old cases equal?
|
|
|
|
*/
|
|
|
|
case_equal = dns_name_caseequal(ctx->name, ctx->oldname);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Are the ttl's equal?
|
|
|
|
*/
|
|
|
|
ttl_equal = rr->ttl == ctx->update_rr_ttl;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-09-09 08:58:00 +00:00
|
|
|
/*
|
2015-02-27 15:08:38 +11:00
|
|
|
* If the update RR is a "duplicate" of a existing RR,
|
2011-08-30 05:16:15 +00:00
|
|
|
* the update should be silently ignored.
|
1999-09-09 08:58:00 +00:00
|
|
|
*/
|
2018-04-17 08:29:14 -07:00
|
|
|
equal = (dns_rdata_casecompare(&rr->rdata, ctx->update_rr) == 0);
|
2015-02-27 15:08:38 +11:00
|
|
|
if (equal && case_equal && ttl_equal) {
|
2018-04-17 08:29:14 -07:00
|
|
|
ctx->ignore_add = true;
|
2011-08-30 05:16:15 +00:00
|
|
|
return ISC_R_SUCCESS;
|
1999-09-09 08:58:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-08-30 05:16:15 +00:00
|
|
|
* If this RR is "equal" to the update RR, it should
|
|
|
|
* be deleted before the update RR is added.
|
1999-09-09 08:58:00 +00:00
|
|
|
*/
|
2011-08-30 05:16:15 +00:00
|
|
|
if (replaces_p(ctx->update_rr, &rr->rdata)) {
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL,
|
|
|
|
ctx->oldname, rr->ttl, &rr->rdata, &tuple);
|
2011-08-30 05:16:15 +00:00
|
|
|
dns_diff_append(&ctx->del_diff, &tuple);
|
|
|
|
return ISC_R_SUCCESS;
|
1999-09-09 08:58:00 +00:00
|
|
|
}
|
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
/*
|
2015-02-27 15:08:38 +11:00
|
|
|
* If this RR differs in TTL or case from the update RR,
|
|
|
|
* its TTL and case must be adjusted.
|
2011-08-30 05:16:15 +00:00
|
|
|
*/
|
2015-02-27 15:08:38 +11:00
|
|
|
if (!ttl_equal || !case_equal) {
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL,
|
|
|
|
ctx->oldname, rr->ttl, &rr->rdata, &tuple);
|
2011-08-30 05:16:15 +00:00
|
|
|
dns_diff_append(&ctx->del_diff, &tuple);
|
|
|
|
if (!equal) {
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(ctx->add_diff.mctx, DNS_DIFFOP_ADD,
|
|
|
|
ctx->name, ctx->update_rr_ttl,
|
|
|
|
&rr->rdata, &tuple);
|
2011-08-30 05:16:15 +00:00
|
|
|
dns_diff_append(&ctx->add_diff, &tuple);
|
1999-09-09 08:58:00 +00:00
|
|
|
}
|
|
|
|
}
|
2024-07-11 17:00:38 -07:00
|
|
|
|
|
|
|
return ISC_R_SUCCESS;
|
2011-08-30 05:16:15 +00:00
|
|
|
}
|
1999-09-09 08:58:00 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* Miscellaneous subroutines.
|
|
|
|
*/
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
/*%
|
|
|
|
* Extract a single update RR from 'section' of dynamic update message
|
|
|
|
* 'msg', with consistency checking.
|
|
|
|
*
|
|
|
|
* Stores the owner name, rdata, and TTL of the update RR at 'name',
|
|
|
|
* 'rdata', and 'ttl', respectively.
|
|
|
|
*/
|
|
|
|
static void
|
2025-03-19 20:29:17 +01:00
|
|
|
get_current_rr(dns_rdataclass_t zoneclass, dns_name_t *name, dns_rdata_t *rdata,
|
|
|
|
dns_rdatatype_t *covers, dns_ttl_t *ttl,
|
2011-08-30 05:16:15 +00:00
|
|
|
dns_rdataclass_t *update_class) {
|
|
|
|
dns_rdataset_t *rdataset;
|
|
|
|
isc_result_t result;
|
2025-03-19 20:29:17 +01:00
|
|
|
rdataset = ISC_LIST_HEAD(name->list);
|
2011-08-30 05:16:15 +00:00
|
|
|
INSIST(rdataset != NULL);
|
|
|
|
INSIST(ISC_LIST_NEXT(rdataset, link) == NULL);
|
|
|
|
*covers = rdataset->covers;
|
|
|
|
*ttl = rdataset->ttl;
|
|
|
|
result = dns_rdataset_first(rdataset);
|
|
|
|
INSIST(result == ISC_R_SUCCESS);
|
|
|
|
dns_rdataset_current(rdataset, rdata);
|
|
|
|
INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
|
|
|
|
*update_class = rdata->rdclass;
|
|
|
|
rdata->rdclass = zoneclass;
|
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
/*%
|
|
|
|
* Increment the SOA serial number of database 'db', version 'ver'.
|
|
|
|
* Replace the SOA record in the database, and log the
|
|
|
|
* change in 'diff'.
|
|
|
|
*/
|
2008-09-24 02:46:23 +00:00
|
|
|
|
|
|
|
/*
|
2011-08-30 05:16:15 +00:00
|
|
|
* XXXRTH Failures in this routine will be worth logging, when
|
|
|
|
* we have a logging system. Failure to find the zonename
|
|
|
|
* or the SOA rdataset warrant at least an UNEXPECTED_ERROR().
|
2008-09-24 02:46:23 +00:00
|
|
|
*/
|
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
static isc_result_t
|
|
|
|
update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
|
|
|
|
isc_mem_t *mctx, dns_updatemethod_t method) {
|
|
|
|
dns_difftuple_t *deltuple = NULL;
|
|
|
|
dns_difftuple_t *addtuple = NULL;
|
2018-03-28 14:19:37 +02:00
|
|
|
uint32_t serial;
|
2011-08-30 05:16:15 +00:00
|
|
|
isc_result_t result;
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_copy(deltuple, &addtuple);
|
2011-08-30 05:16:15 +00:00
|
|
|
addtuple->op = DNS_DIFFOP_ADD;
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
serial = dns_soa_getserial(&addtuple->rdata);
|
2020-12-10 10:31:31 +01:00
|
|
|
serial = dns_update_soaserial(serial, method, NULL);
|
2011-08-30 05:16:15 +00:00
|
|
|
dns_soa_setserial(serial, &addtuple->rdata);
|
|
|
|
CHECK(do_one_tuple(&deltuple, db, ver, diff));
|
|
|
|
CHECK(do_one_tuple(&addtuple, db, ver, diff));
|
|
|
|
result = ISC_R_SUCCESS;
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
failure:
|
|
|
|
if (addtuple != NULL) {
|
|
|
|
dns_difftuple_free(&addtuple);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-30 05:16:15 +00:00
|
|
|
if (deltuple != NULL) {
|
|
|
|
dns_difftuple_free(&deltuple);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-30 05:16:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
/*%
|
|
|
|
* Check that the new SOA record at 'update_rdata' does not
|
|
|
|
* illegally cause the SOA serial number to decrease or stay
|
|
|
|
* unchanged relative to the existing SOA in 'db'.
|
|
|
|
*
|
2018-04-17 08:29:14 -07:00
|
|
|
* Sets '*ok' to true if the update is legal, false if not.
|
2011-08-30 05:16:15 +00:00
|
|
|
*
|
|
|
|
* William King points out that RFC2136 is inconsistent about
|
|
|
|
* the case where the serial number stays unchanged:
|
|
|
|
*
|
|
|
|
* section 3.4.2.2 requires a server to ignore a SOA update request
|
|
|
|
* if the serial number on the update SOA is less_than_or_equal to
|
|
|
|
* the zone SOA serial.
|
|
|
|
*
|
|
|
|
* section 3.6 requires a server to ignore a SOA update request if
|
|
|
|
* the serial is less_than the zone SOA serial.
|
|
|
|
*
|
|
|
|
* Paul says 3.4.2.2 is correct.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
check_soa_increment(dns_db_t *db, dns_dbversion_t *ver,
|
2018-04-17 08:29:14 -07:00
|
|
|
dns_rdata_t *update_rdata, bool *ok) {
|
2018-03-28 14:19:37 +02:00
|
|
|
uint32_t db_serial;
|
|
|
|
uint32_t update_serial;
|
2011-08-30 05:16:15 +00:00
|
|
|
isc_result_t result;
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
update_serial = dns_soa_getserial(update_rdata);
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
result = dns_db_getsoaserial(db, ver, &db_serial);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
return result;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
if (DNS_SERIAL_GE(db_serial, update_serial)) {
|
2018-04-17 08:29:14 -07:00
|
|
|
*ok = false;
|
2011-08-30 05:16:15 +00:00
|
|
|
} else {
|
2018-04-17 08:29:14 -07:00
|
|
|
*ok = true;
|
1999-09-09 08:58:00 +00:00
|
|
|
}
|
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
return ISC_R_SUCCESS;
|
1999-09-09 08:58:00 +00:00
|
|
|
}
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/**************************************************************************/
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-08-20 05:35:16 +00:00
|
|
|
* The actual update code in all its glory. We try to follow
|
|
|
|
* the RFC2136 pseudocode as closely as possible.
|
|
|
|
*/
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
2022-10-26 23:35:47 -07:00
|
|
|
send_update(ns_client_t *client, dns_zone_t *zone) {
|
2000-04-06 22:03:35 +00:00
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
2022-11-08 17:32:41 -08:00
|
|
|
dns_ssutable_t *ssutable = NULL;
|
|
|
|
dns_message_t *request = client->message;
|
|
|
|
isc_mem_t *mctx = client->manager->mctx;
|
|
|
|
dns_aclenv_t *env = client->manager->aclenv;
|
|
|
|
dns_rdataclass_t zoneclass;
|
|
|
|
dns_rdatatype_t covers;
|
|
|
|
dns_name_t *zonename = NULL;
|
2024-11-20 01:20:42 +11:00
|
|
|
unsigned int *maxbytype = NULL;
|
|
|
|
size_t update = 0, maxbytypelen = 0;
|
2022-11-08 17:32:41 -08:00
|
|
|
dns_zoneopt_t options;
|
|
|
|
dns_db_t *db = NULL;
|
|
|
|
dns_dbversion_t *ver = NULL;
|
2022-10-26 23:35:47 -07:00
|
|
|
update_t *uev = NULL;
|
2022-11-08 17:32:41 -08:00
|
|
|
|
|
|
|
CHECK(dns_zone_getdb(zone, &db));
|
|
|
|
zonename = dns_db_origin(db);
|
|
|
|
zoneclass = dns_db_class(db);
|
|
|
|
dns_zone_getssutable(zone, &ssutable);
|
|
|
|
options = dns_zone_getoptions(zone);
|
|
|
|
dns_db_currentversion(db, &ver);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update message processing can leak record existence information
|
|
|
|
* so check that we are allowed to query this zone. Additionally,
|
|
|
|
* if we would refuse all updates for this zone, we bail out here.
|
|
|
|
*/
|
|
|
|
CHECK(checkqueryacl(client, dns_zone_getqueryacl(zone),
|
|
|
|
dns_zone_getorigin(zone),
|
|
|
|
dns_zone_getupdateacl(zone), ssutable));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check requestor's permissions.
|
|
|
|
*/
|
|
|
|
if (ssutable == NULL) {
|
|
|
|
CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone),
|
|
|
|
"update", dns_zone_getorigin(zone), false,
|
|
|
|
false));
|
2025-05-14 15:32:53 +02:00
|
|
|
} else if (client->inner.signer == NULL && !TCPCLIENT(client)) {
|
2022-11-08 17:32:41 -08:00
|
|
|
CHECK(checkupdateacl(client, NULL, "update",
|
|
|
|
dns_zone_getorigin(zone), false, true));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dns_zone_getupdatedisabled(zone)) {
|
2024-11-14 12:34:11 +11:00
|
|
|
FAILC(DNS_R_REFUSED,
|
|
|
|
"dynamic update temporarily disabled because the zone is "
|
|
|
|
"frozen. Use 'rndc thaw' to re-enable updates.");
|
2022-11-08 17:32:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prescan the update section, checking for updates that
|
|
|
|
* are illegal or violate policy.
|
|
|
|
*/
|
|
|
|
if (ssutable != NULL) {
|
2024-11-20 01:20:42 +11:00
|
|
|
maxbytypelen = request->counts[DNS_SECTION_UPDATE];
|
|
|
|
maxbytype = isc_mem_cget(mctx, maxbytypelen,
|
|
|
|
sizeof(*maxbytype));
|
2022-11-08 17:32:41 -08:00
|
|
|
}
|
|
|
|
|
2025-03-19 20:29:17 +01:00
|
|
|
update = 0;
|
|
|
|
MSG_SECTION_FOREACH(request, DNS_SECTION_UPDATE, name) {
|
2022-11-08 17:32:41 -08:00
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_ttl_t ttl;
|
|
|
|
dns_rdataclass_t update_class;
|
|
|
|
|
2024-11-20 01:20:42 +11:00
|
|
|
INSIST(ssutable == NULL || update < maxbytypelen);
|
2025-03-19 20:29:17 +01:00
|
|
|
get_current_rr(zoneclass, name, &rdata, &covers, &ttl,
|
|
|
|
&update_class);
|
2022-11-08 17:32:41 -08:00
|
|
|
|
|
|
|
if (!dns_name_issubdomain(name, zonename)) {
|
|
|
|
FAILC(DNS_R_NOTZONE, "update RR is outside zone");
|
|
|
|
}
|
|
|
|
if (update_class == zoneclass) {
|
|
|
|
/*
|
|
|
|
* Check for meta-RRs. The RFC2136 pseudocode says
|
|
|
|
* check for ANY|AXFR|MAILA|MAILB, but the text adds
|
|
|
|
* "or any other QUERY metatype"
|
|
|
|
*/
|
|
|
|
if (dns_rdatatype_ismeta(rdata.type)) {
|
|
|
|
FAILC(DNS_R_FORMERR, "meta-RR in update");
|
|
|
|
}
|
|
|
|
result = dns_zone_checknames(zone, name, &rdata);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
FAIL(DNS_R_REFUSED);
|
|
|
|
}
|
|
|
|
if ((options & DNS_ZONEOPT_CHECKSVCB) != 0 &&
|
|
|
|
rdata.type == dns_rdatatype_svcb)
|
|
|
|
{
|
|
|
|
result = dns_rdata_checksvcb(name, &rdata);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
const char *reason =
|
|
|
|
isc_result_totext(result);
|
|
|
|
FAILNT(DNS_R_REFUSED, name, rdata.type,
|
|
|
|
reason);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (update_class == dns_rdataclass_any) {
|
|
|
|
if (ttl != 0 || rdata.length != 0 ||
|
|
|
|
(dns_rdatatype_ismeta(rdata.type) &&
|
|
|
|
rdata.type != dns_rdatatype_any))
|
|
|
|
{
|
|
|
|
FAILC(DNS_R_FORMERR, "meta-RR in update");
|
|
|
|
}
|
|
|
|
} else if (update_class == dns_rdataclass_none) {
|
|
|
|
if (ttl != 0 || dns_rdatatype_ismeta(rdata.type)) {
|
|
|
|
FAILC(DNS_R_FORMERR, "meta-RR in update");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
update_log(client, zone, ISC_LOG_WARNING,
|
|
|
|
"update RR has incorrect class %d",
|
|
|
|
update_class);
|
|
|
|
FAIL(DNS_R_FORMERR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* draft-ietf-dnsind-simple-secure-update-01 says
|
|
|
|
* "Unlike traditional dynamic update, the client
|
|
|
|
* is forbidden from updating NSEC records."
|
|
|
|
*/
|
|
|
|
if (rdata.type == dns_rdatatype_nsec3) {
|
|
|
|
FAILC(DNS_R_REFUSED, "explicit NSEC3 updates are not "
|
2024-11-14 12:34:11 +11:00
|
|
|
"allowed in secure zones");
|
2022-11-08 17:32:41 -08:00
|
|
|
} else if (rdata.type == dns_rdatatype_nsec) {
|
|
|
|
FAILC(DNS_R_REFUSED, "explicit NSEC updates are not "
|
2024-11-14 12:34:11 +11:00
|
|
|
"allowed in secure zones");
|
2022-11-08 17:32:41 -08:00
|
|
|
} else if (rdata.type == dns_rdatatype_rrsig &&
|
|
|
|
!dns_name_equal(name, zonename))
|
|
|
|
{
|
2024-11-14 12:34:11 +11:00
|
|
|
FAILC(DNS_R_REFUSED,
|
|
|
|
"explicit RRSIG updates are currently not "
|
|
|
|
"supported in secure zones except at the apex");
|
2022-11-08 17:32:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ssutable != NULL) {
|
|
|
|
isc_netaddr_t netaddr;
|
|
|
|
dns_name_t *target = NULL;
|
|
|
|
dst_key_t *tsigkey = NULL;
|
|
|
|
dns_rdata_ptr_t ptr;
|
|
|
|
dns_rdata_in_srv_t srv;
|
|
|
|
|
2024-11-20 01:20:42 +11:00
|
|
|
maxbytype[update] = 0;
|
|
|
|
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_netaddr_fromsockaddr(&netaddr,
|
|
|
|
&client->inner.peeraddr);
|
2022-11-08 17:32:41 -08:00
|
|
|
|
|
|
|
if (client->message->tsigkey != NULL) {
|
|
|
|
tsigkey = client->message->tsigkey->key;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((update_class == dns_rdataclass_in ||
|
|
|
|
update_class == dns_rdataclass_none) &&
|
|
|
|
rdata.type == dns_rdatatype_ptr)
|
|
|
|
{
|
|
|
|
result = dns_rdata_tostruct(&rdata, &ptr, NULL);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
target = &ptr.ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((update_class == dns_rdataclass_in ||
|
|
|
|
update_class == dns_rdataclass_none) &&
|
|
|
|
rdata.type == dns_rdatatype_srv)
|
|
|
|
{
|
|
|
|
result = dns_rdata_tostruct(&rdata, &srv, NULL);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
target = &srv.target;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (update_class == dns_rdataclass_any &&
|
|
|
|
zoneclass == dns_rdataclass_in &&
|
|
|
|
(rdata.type == dns_rdatatype_ptr ||
|
|
|
|
rdata.type == dns_rdatatype_srv))
|
|
|
|
{
|
|
|
|
ssu_check_t ssuinfo;
|
|
|
|
|
|
|
|
ssuinfo.name = name;
|
|
|
|
ssuinfo.table = ssutable;
|
2025-05-14 15:32:53 +02:00
|
|
|
ssuinfo.signer = client->inner.signer;
|
2022-11-08 17:32:41 -08:00
|
|
|
ssuinfo.addr = &netaddr;
|
|
|
|
ssuinfo.aclenv = env;
|
|
|
|
ssuinfo.tcp = TCPCLIENT(client);
|
|
|
|
ssuinfo.key = tsigkey;
|
|
|
|
|
|
|
|
result = foreach_rr(db, ver, name, rdata.type,
|
|
|
|
dns_rdatatype_none,
|
|
|
|
ssu_checkrr, &ssuinfo);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
FAILC(DNS_R_REFUSED,
|
|
|
|
"rejected by secure update");
|
|
|
|
}
|
|
|
|
} else if (target != NULL &&
|
|
|
|
update_class == dns_rdataclass_none)
|
|
|
|
{
|
|
|
|
bool flag;
|
|
|
|
CHECK(rr_exists(db, ver, name, &rdata, &flag));
|
|
|
|
if (flag &&
|
|
|
|
!dns_ssutable_checkrules(
|
2025-05-14 15:32:53 +02:00
|
|
|
ssutable, client->inner.signer,
|
|
|
|
name, &netaddr, TCPCLIENT(client),
|
|
|
|
env, rdata.type, target, tsigkey,
|
|
|
|
NULL))
|
2022-11-08 17:32:41 -08:00
|
|
|
{
|
|
|
|
FAILC(DNS_R_REFUSED,
|
|
|
|
"rejected by secure update");
|
|
|
|
}
|
|
|
|
} else if (rdata.type != dns_rdatatype_any) {
|
2024-11-20 01:20:42 +11:00
|
|
|
const dns_ssurule_t *ssurule = NULL;
|
2022-11-08 17:32:41 -08:00
|
|
|
if (!dns_ssutable_checkrules(
|
2025-05-14 15:32:53 +02:00
|
|
|
ssutable, client->inner.signer,
|
|
|
|
name, &netaddr, TCPCLIENT(client),
|
|
|
|
env, rdata.type, target, tsigkey,
|
2024-11-20 01:20:42 +11:00
|
|
|
&ssurule))
|
2022-11-08 17:32:41 -08:00
|
|
|
{
|
2024-11-14 12:34:11 +11:00
|
|
|
FAILC(DNS_R_REFUSED,
|
|
|
|
"rejected by secure update");
|
2022-11-08 17:32:41 -08:00
|
|
|
}
|
2024-11-20 01:20:42 +11:00
|
|
|
maxbytype[update] = dns_ssurule_max(ssurule,
|
|
|
|
rdata.type);
|
2022-11-08 17:32:41 -08:00
|
|
|
} else {
|
|
|
|
if (!ssu_checkall(db, ver, name, ssutable,
|
2025-05-14 15:32:53 +02:00
|
|
|
client->inner.signer,
|
|
|
|
&netaddr, env,
|
2022-11-08 17:32:41 -08:00
|
|
|
TCPCLIENT(client), tsigkey))
|
|
|
|
{
|
2024-11-14 12:34:11 +11:00
|
|
|
FAILC(DNS_R_REFUSED,
|
|
|
|
"rejected by secure update");
|
2022-11-08 17:32:41 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-03-19 20:29:17 +01:00
|
|
|
update++;
|
2022-11-08 17:32:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
update_log(client, zone, LOGLEVEL_DEBUG, "update section prescan OK");
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2023-04-11 07:54:58 +02:00
|
|
|
result = isc_quota_acquire(&client->manager->sctx->updquota);
|
2022-09-01 16:05:04 -07:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
|
|
|
"update failed: too many DNS UPDATEs queued (%s)",
|
|
|
|
isc_result_totext(result));
|
|
|
|
ns_stats_increment(client->manager->sctx->nsstats,
|
|
|
|
ns_statscounter_updatequota);
|
2022-11-08 17:32:41 -08:00
|
|
|
CHECK(DNS_R_DROP);
|
2022-09-01 16:05:04 -07:00
|
|
|
}
|
|
|
|
|
2022-10-26 23:35:47 -07:00
|
|
|
uev = isc_mem_get(client->manager->mctx, sizeof(*uev));
|
|
|
|
*uev = (update_t){
|
|
|
|
.zone = zone,
|
|
|
|
.client = client,
|
2024-11-20 01:20:42 +11:00
|
|
|
.maxbytype = maxbytype,
|
|
|
|
.maxbytypelen = maxbytypelen,
|
2022-10-26 23:35:47 -07:00
|
|
|
.result = ISC_R_SUCCESS,
|
|
|
|
};
|
2000-02-11 21:09:32 +00:00
|
|
|
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_nmhandle_attach(client->inner.handle, &client->inner.updatehandle);
|
2022-10-26 23:35:47 -07:00
|
|
|
isc_async_run(dns_zone_getloop(zone), update_action, uev);
|
2024-11-20 01:20:42 +11:00
|
|
|
maxbytype = NULL;
|
1999-10-14 04:20:20 +00:00
|
|
|
|
2022-11-08 17:32:41 -08:00
|
|
|
failure:
|
|
|
|
if (db != NULL) {
|
|
|
|
dns_db_closeversion(db, &ver, false);
|
|
|
|
dns_db_detach(&db);
|
|
|
|
}
|
|
|
|
|
2024-11-20 01:20:42 +11:00
|
|
|
if (maxbytype != NULL) {
|
|
|
|
isc_mem_cput(mctx, maxbytype, maxbytypelen, sizeof(*maxbytype));
|
2022-11-08 17:32:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ssutable != NULL) {
|
|
|
|
dns_ssutable_detach(&ssutable);
|
|
|
|
}
|
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1999-12-23 00:09:04 +00:00
|
|
|
respond(ns_client_t *client, isc_result_t result) {
|
|
|
|
isc_result_t msg_result;
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
msg_result = dns_message_reply(client->message, true);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (msg_result != ISC_R_SUCCESS) {
|
2024-08-13 18:20:26 +02:00
|
|
|
isc_log_write(NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
|
|
|
|
ISC_LOG_ERROR,
|
2022-09-02 14:44:58 -07:00
|
|
|
"could not create update response message: %s",
|
|
|
|
isc_result_totext(msg_result));
|
|
|
|
ns_client_drop(client, msg_result);
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_nmhandle_detach(&client->inner.reqhandle);
|
2022-09-02 14:44:58 -07:00
|
|
|
return;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-03-06 19:08:05 +00:00
|
|
|
|
2022-09-02 14:44:58 -07:00
|
|
|
client->message->rcode = dns_result_torcode(result);
|
1999-10-14 00:03:04 +00:00
|
|
|
ns_client_send(client);
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_nmhandle_detach(&client->inner.reqhandle);
|
1999-10-14 00:03:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-09-03 13:31:27 -07:00
|
|
|
ns_update_start(ns_client_t *client, isc_nmhandle_t *handle,
|
|
|
|
isc_result_t sigresult) {
|
1999-10-14 00:03:04 +00:00
|
|
|
dns_message_t *request = client->message;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_name_t *zonename;
|
|
|
|
dns_rdataset_t *zone_rdataset;
|
2011-08-30 05:16:15 +00:00
|
|
|
dns_zone_t *zone = NULL, *raw = NULL;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2020-09-03 13:31:27 -07:00
|
|
|
/*
|
2022-09-02 14:44:58 -07:00
|
|
|
* Attach to the request handle. This will be held until
|
|
|
|
* we respond, or drop the request.
|
2020-09-03 13:31:27 -07:00
|
|
|
*/
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_nmhandle_attach(handle, &client->inner.reqhandle);
|
2020-09-03 13:31:27 -07:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/*
|
|
|
|
* Interpret the zone section.
|
|
|
|
*/
|
2025-03-19 20:29:17 +01:00
|
|
|
|
|
|
|
if (ISC_LIST_EMPTY(request->sections[DNS_SECTION_ZONE])) {
|
2006-12-04 01:54:53 +00:00
|
|
|
FAILC(DNS_R_FORMERR, "update zone section empty");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The zone section must contain exactly one "question", and
|
|
|
|
* it must be of type SOA.
|
|
|
|
*/
|
2025-03-19 20:29:17 +01:00
|
|
|
zonename = ISC_LIST_HEAD(request->sections[DNS_SECTION_ZONE]);
|
1999-08-20 05:35:16 +00:00
|
|
|
zone_rdataset = ISC_LIST_HEAD(zonename->list);
|
|
|
|
if (zone_rdataset->type != dns_rdatatype_soa) {
|
1999-10-25 20:23:13 +00:00
|
|
|
FAILC(DNS_R_FORMERR, "update zone section contains non-SOA");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) {
|
2024-11-14 12:34:11 +11:00
|
|
|
FAILC(DNS_R_FORMERR,
|
|
|
|
"update zone section contains multiple RRs");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/* The zone section must have exactly one name. */
|
2025-03-19 20:29:17 +01:00
|
|
|
if (ISC_LIST_NEXT(zonename, link) != NULL) {
|
2024-11-14 12:34:11 +11:00
|
|
|
FAILC(DNS_R_FORMERR,
|
|
|
|
"update zone section contains multiple RRs");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2025-05-14 15:32:53 +02:00
|
|
|
result = dns_view_findzone(client->inner.view, zonename,
|
|
|
|
DNS_ZTFIND_EXACT, &zone);
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2022-03-15 17:57:43 +00:00
|
|
|
FAILN(DNS_R_NOTAUTH, zonename,
|
|
|
|
"not authoritative for update zone");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2011-08-30 05:16:15 +00:00
|
|
|
/*
|
|
|
|
* If there is a raw (unsigned) zone associated with this
|
|
|
|
* zone then it processes the UPDATE request.
|
|
|
|
*/
|
|
|
|
dns_zone_getraw(zone, &raw);
|
|
|
|
if (raw != NULL) {
|
|
|
|
dns_zone_detach(&zone);
|
|
|
|
dns_zone_attach(raw, &zone);
|
|
|
|
dns_zone_detach(&raw);
|
|
|
|
}
|
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
switch (dns_zone_gettype(zone)) {
|
2021-08-25 22:29:25 -07:00
|
|
|
case dns_zone_primary:
|
2010-12-18 01:56:23 +00:00
|
|
|
case dns_zone_dlz:
|
2000-09-13 01:30:34 +00:00
|
|
|
/*
|
|
|
|
* We can now fail due to a bad signature as we now know
|
2021-10-05 11:07:44 +02:00
|
|
|
* that we are the primary.
|
2000-09-13 01:30:34 +00:00
|
|
|
*/
|
|
|
|
if (sigresult != ISC_R_SUCCESS) {
|
|
|
|
FAIL(sigresult);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2020-09-18 15:00:35 +10:00
|
|
|
dns_message_clonebuffer(client->message);
|
2022-10-26 23:35:47 -07:00
|
|
|
CHECK(send_update(client, zone));
|
2000-12-01 21:37:17 +00:00
|
|
|
break;
|
2021-08-25 22:29:25 -07:00
|
|
|
case dns_zone_secondary:
|
2018-10-09 10:54:51 +02:00
|
|
|
case dns_zone_mirror:
|
2022-06-03 16:55:56 +10:00
|
|
|
dns_message_clonebuffer(client->message);
|
2022-10-26 23:35:47 -07:00
|
|
|
CHECK(send_forward(client, zone));
|
2000-12-01 21:37:17 +00:00
|
|
|
break;
|
1999-10-14 00:03:04 +00:00
|
|
|
default:
|
2006-12-04 01:54:53 +00:00
|
|
|
FAILC(DNS_R_NOTAUTH, "not authoritative for update zone");
|
1999-10-14 00:03:04 +00:00
|
|
|
}
|
|
|
|
return;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
failure:
|
2008-04-03 05:55:52 +00:00
|
|
|
if (result == DNS_R_REFUSED) {
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, zone, ns_statscounter_updaterej);
|
2008-04-03 05:55:52 +00:00
|
|
|
}
|
2022-09-01 16:05:04 -07:00
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
/*
|
|
|
|
* We failed without having sent an update event to the zone.
|
2022-10-26 23:35:47 -07:00
|
|
|
* We are still in the client context, so we can
|
1999-10-14 00:03:04 +00:00
|
|
|
* simply give an error response without switching tasks.
|
|
|
|
*/
|
2022-09-01 16:05:04 -07:00
|
|
|
if (result == DNS_R_DROP) {
|
|
|
|
ns_client_drop(client, result);
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_nmhandle_detach(&client->inner.reqhandle);
|
2022-09-01 16:05:04 -07:00
|
|
|
} else {
|
|
|
|
respond(client, result);
|
|
|
|
}
|
|
|
|
|
2000-09-13 01:30:34 +00:00
|
|
|
if (zone != NULL) {
|
|
|
|
dns_zone_detach(&zone);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-10-14 00:03:04 +00:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2003-01-10 02:37:44 +00:00
|
|
|
* DS records are not allowed to exist without corresponding NS records,
|
2008-09-24 02:46:23 +00:00
|
|
|
* RFC 3658, 2.2 Protocol Change,
|
2003-01-10 02:37:44 +00:00
|
|
|
* "DS RRsets MUST NOT appear at non-delegation points or at a zone's apex".
|
|
|
|
*/
|
|
|
|
|
2002-11-12 21:52:44 +00:00
|
|
|
static isc_result_t
|
|
|
|
remove_orphaned_ds(dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) {
|
|
|
|
isc_result_t result;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool ns_exists;
|
2009-04-30 06:59:11 +00:00
|
|
|
dns_diff_t temp_diff;
|
2002-11-12 21:52:44 +00:00
|
|
|
|
2009-04-30 06:59:11 +00:00
|
|
|
dns_diff_init(diff->mctx, &temp_diff);
|
|
|
|
|
2025-03-20 22:25:56 -07:00
|
|
|
ISC_LIST_FOREACH(diff->tuples, tuple, link) {
|
2009-04-30 06:59:11 +00:00
|
|
|
if (!((tuple->op == DNS_DIFFOP_DEL &&
|
|
|
|
tuple->rdata.type == dns_rdatatype_ns) ||
|
|
|
|
(tuple->op == DNS_DIFFOP_ADD &&
|
|
|
|
tuple->rdata.type == dns_rdatatype_ds)))
|
|
|
|
{
|
2002-11-12 21:52:44 +00:00
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-04-30 06:59:11 +00:00
|
|
|
CHECK(rrset_exists(db, newver, &tuple->name, dns_rdatatype_ns,
|
2025-08-11 07:17:43 +02:00
|
|
|
dns_rdatatype_none, &ns_exists));
|
2009-04-30 06:59:11 +00:00
|
|
|
if (ns_exists &&
|
2022-11-02 19:33:14 +01:00
|
|
|
!dns_name_equal(&tuple->name, dns_db_origin(db)))
|
|
|
|
{
|
2002-11-12 21:52:44 +00:00
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-04-30 06:59:11 +00:00
|
|
|
CHECK(delete_if(true_p, db, newver, &tuple->name,
|
2025-08-11 07:17:43 +02:00
|
|
|
dns_rdatatype_ds, dns_rdatatype_none, NULL,
|
|
|
|
&temp_diff));
|
2002-11-12 21:52:44 +00:00
|
|
|
}
|
2009-04-30 06:59:11 +00:00
|
|
|
result = ISC_R_SUCCESS;
|
2002-11-12 21:52:44 +00:00
|
|
|
|
|
|
|
failure:
|
2025-05-23 13:02:22 -07:00
|
|
|
ISC_LIST_FOREACH(temp_diff.tuples, tuple, link) {
|
2009-04-30 06:59:11 +00:00
|
|
|
ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
|
|
|
|
dns_diff_appendminimal(diff, &tuple);
|
|
|
|
}
|
2002-11-12 21:52:44 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2005-05-19 04:59:05 +00:00
|
|
|
/*
|
|
|
|
* This implements the post load integrity checks for mx records.
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
check_mx(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
|
|
|
|
dns_dbversion_t *newver, dns_diff_t *diff) {
|
|
|
|
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")];
|
|
|
|
char ownerbuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char altbuf[DNS_NAME_FORMATSIZE];
|
|
|
|
dns_fixedname_t fixed;
|
2025-03-20 22:25:56 -07:00
|
|
|
dns_name_t *foundname = NULL;
|
2005-05-19 04:59:05 +00:00
|
|
|
dns_rdata_mx_t mx;
|
|
|
|
dns_rdata_t rdata;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool ok = true;
|
|
|
|
bool isaddress;
|
2005-05-19 04:59:05 +00:00
|
|
|
isc_result_t result;
|
|
|
|
struct in6_addr addr6;
|
|
|
|
struct in_addr addr;
|
2018-04-03 23:56:04 +02:00
|
|
|
dns_zoneopt_t options;
|
2005-05-19 04:59:05 +00:00
|
|
|
|
2018-03-28 14:38:09 +02:00
|
|
|
foundname = dns_fixedname_initname(&fixed);
|
2005-05-19 04:59:05 +00:00
|
|
|
dns_rdata_init(&rdata);
|
|
|
|
options = dns_zone_getoptions(zone);
|
|
|
|
|
2025-03-20 22:25:56 -07:00
|
|
|
ISC_LIST_FOREACH(diff->tuples, t, link) {
|
2007-08-28 00:05:06 +00:00
|
|
|
if (t->op != DNS_DIFFOP_ADD ||
|
2022-11-02 19:33:14 +01:00
|
|
|
t->rdata.type != dns_rdatatype_mx)
|
|
|
|
{
|
2005-05-19 04:59:05 +00:00
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2005-05-19 04:59:05 +00:00
|
|
|
|
|
|
|
result = dns_rdata_tostruct(&t->rdata, &mx, NULL);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
/*
|
|
|
|
* Check if we will error out if we attempt to reload the
|
|
|
|
* zone.
|
|
|
|
*/
|
|
|
|
dns_name_format(&mx.mx, namebuf, sizeof(namebuf));
|
|
|
|
dns_name_format(&t->name, ownerbuf, sizeof(ownerbuf));
|
2018-04-17 08:29:14 -07:00
|
|
|
isaddress = false;
|
2018-02-26 12:30:48 +01:00
|
|
|
if ((options & DNS_ZONEOPT_CHECKMX) != 0 &&
|
2005-05-19 04:59:05 +00:00
|
|
|
strlcpy(tmp, namebuf, sizeof(tmp)) < sizeof(tmp))
|
|
|
|
{
|
|
|
|
if (tmp[strlen(tmp) - 1] == '.') {
|
|
|
|
tmp[strlen(tmp) - 1] = '\0';
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2017-10-30 15:38:23 +01:00
|
|
|
if (inet_pton(AF_INET, tmp, &addr) == 1 ||
|
2005-05-19 04:59:05 +00:00
|
|
|
inet_pton(AF_INET6, tmp, &addr6) == 1)
|
|
|
|
{
|
2018-04-17 08:29:14 -07:00
|
|
|
isaddress = true;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2005-05-19 04:59:05 +00:00
|
|
|
}
|
|
|
|
|
2018-02-26 12:30:48 +01:00
|
|
|
if (isaddress && (options & DNS_ZONEOPT_CHECKMXFAIL) != 0) {
|
2005-05-19 04:59:05 +00:00
|
|
|
update_log(client, zone, ISC_LOG_ERROR,
|
|
|
|
"%s/MX: '%s': %s", ownerbuf, namebuf,
|
2021-10-04 17:14:53 +02:00
|
|
|
isc_result_totext(DNS_R_MXISADDRESS));
|
2018-04-17 08:29:14 -07:00
|
|
|
ok = false;
|
2005-05-19 04:59:05 +00:00
|
|
|
} else if (isaddress) {
|
|
|
|
update_log(client, zone, ISC_LOG_WARNING,
|
|
|
|
"%s/MX: warning: '%s': %s", ownerbuf,
|
|
|
|
namebuf,
|
2021-10-04 17:14:53 +02:00
|
|
|
isc_result_totext(DNS_R_MXISADDRESS));
|
2005-05-19 04:59:05 +00:00
|
|
|
}
|
2008-01-18 23:46:58 +00:00
|
|
|
|
2005-05-19 04:59:05 +00:00
|
|
|
/*
|
|
|
|
* Check zone integrity checks.
|
|
|
|
*/
|
2005-08-24 23:54:04 +00:00
|
|
|
if ((options & DNS_ZONEOPT_CHECKINTEGRITY) == 0) {
|
2005-05-19 04:59:05 +00:00
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2005-05-19 04:59:05 +00:00
|
|
|
result = dns_db_find(db, &mx.mx, newver, dns_rdatatype_a, 0, 0,
|
|
|
|
NULL, foundname, NULL, NULL);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2005-05-19 04:59:05 +00:00
|
|
|
|
|
|
|
if (result == DNS_R_NXRRSET) {
|
|
|
|
result = dns_db_find(db, &mx.mx, newver,
|
|
|
|
dns_rdatatype_aaaa, 0, 0, NULL,
|
|
|
|
foundname, NULL, NULL);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2005-05-19 04:59:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN) {
|
2024-11-14 12:34:11 +11:00
|
|
|
update_log(
|
|
|
|
client, zone, ISC_LOG_ERROR,
|
|
|
|
"%s/MX '%s' has no address records (A or AAAA)",
|
|
|
|
ownerbuf, namebuf);
|
2018-04-17 08:29:14 -07:00
|
|
|
ok = false;
|
2005-05-19 04:59:05 +00:00
|
|
|
} else if (result == DNS_R_CNAME) {
|
|
|
|
update_log(client, zone, ISC_LOG_ERROR,
|
|
|
|
"%s/MX '%s' is a CNAME (illegal)", ownerbuf,
|
|
|
|
namebuf);
|
2018-04-17 08:29:14 -07:00
|
|
|
ok = false;
|
2005-05-19 04:59:05 +00:00
|
|
|
} else if (result == DNS_R_DNAME) {
|
|
|
|
dns_name_format(foundname, altbuf, sizeof altbuf);
|
|
|
|
update_log(client, zone, ISC_LOG_ERROR,
|
|
|
|
"%s/MX '%s' is below a DNAME '%s' (illegal)",
|
|
|
|
ownerbuf, namebuf, altbuf);
|
2018-04-17 08:29:14 -07:00
|
|
|
ok = false;
|
2005-05-19 04:59:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ok ? ISC_R_SUCCESS : DNS_R_REFUSED;
|
|
|
|
}
|
|
|
|
|
2008-09-24 02:46:23 +00:00
|
|
|
static isc_result_t
|
|
|
|
rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
2018-04-17 08:29:14 -07:00
|
|
|
const dns_rdata_t *rdata, bool *flag) {
|
2008-09-24 02:46:23 +00:00
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
dns_dbnode_t *node = NULL;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
if (rdata->type == dns_rdatatype_nsec3) {
|
2023-08-28 11:53:21 +10:00
|
|
|
result = dns_db_findnsec3node(db, name, false, &node);
|
|
|
|
} else {
|
|
|
|
result = dns_db_findnode(db, name, false, &node);
|
|
|
|
}
|
|
|
|
if (result == ISC_R_NOTFOUND) {
|
|
|
|
*flag = false;
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
goto failure;
|
2008-09-24 02:46:23 +00:00
|
|
|
} else {
|
2023-08-28 11:53:21 +10:00
|
|
|
CHECK(result);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
result = dns_db_findrdataset(db, node, ver, rdata->type, 0,
|
|
|
|
(isc_stdtime_t)0, &rdataset, NULL);
|
|
|
|
if (result == ISC_R_NOTFOUND) {
|
2018-04-17 08:29:14 -07:00
|
|
|
*flag = false;
|
2008-09-24 02:46:23 +00:00
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
|
2025-03-21 23:32:27 -07:00
|
|
|
bool matched = false;
|
|
|
|
DNS_RDATASET_FOREACH(&rdataset) {
|
2008-09-24 02:46:23 +00:00
|
|
|
dns_rdata_t myrdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdataset_current(&rdataset, &myrdata);
|
2025-03-21 23:32:27 -07:00
|
|
|
if (dns_rdata_casecompare(&myrdata, rdata) == 0) {
|
|
|
|
matched = true;
|
2008-09-24 02:46:23 +00:00
|
|
|
break;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
}
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
2025-03-21 23:32:27 -07:00
|
|
|
*flag = matched;
|
2008-09-24 02:46:23 +00:00
|
|
|
|
|
|
|
failure:
|
|
|
|
if (node != NULL) {
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
2009-10-08 23:13:07 +00:00
|
|
|
get_iterations(dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_t privatetype,
|
|
|
|
unsigned int *iterationsp) {
|
2008-09-24 02:46:23 +00:00
|
|
|
dns_dbnode_t *node = NULL;
|
|
|
|
dns_rdata_nsec3param_t nsec3param;
|
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
isc_result_t result;
|
|
|
|
unsigned int iterations = 0;
|
|
|
|
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
|
|
|
|
result = dns_db_getoriginnode(db, &node);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
return result;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0,
|
|
|
|
(isc_stdtime_t)0, &rdataset, NULL);
|
2009-10-08 23:13:07 +00:00
|
|
|
if (result == ISC_R_NOTFOUND) {
|
|
|
|
goto try_private;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
goto failure;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
|
2025-03-21 23:32:27 -07:00
|
|
|
DNS_RDATASET_FOREACH(&rdataset) {
|
2009-10-08 23:13:07 +00:00
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdataset_current(&rdataset, &rdata);
|
|
|
|
CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
|
|
|
|
if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) {
|
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
if (nsec3param.iterations > iterations) {
|
|
|
|
iterations = nsec3param.iterations;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
|
|
|
|
try_private:
|
|
|
|
if (privatetype == 0) {
|
|
|
|
goto success;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
|
|
|
|
result = dns_db_findrdataset(db, node, ver, privatetype, 0,
|
|
|
|
(isc_stdtime_t)0, &rdataset, NULL);
|
2008-09-24 02:46:23 +00:00
|
|
|
if (result == ISC_R_NOTFOUND) {
|
|
|
|
goto success;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
goto failure;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2025-03-21 23:32:27 -07:00
|
|
|
DNS_RDATASET_FOREACH(&rdataset) {
|
2009-10-08 23:13:07 +00:00
|
|
|
unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
|
|
|
|
dns_rdata_t private = DNS_RDATA_INIT;
|
2008-09-24 02:46:23 +00:00
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
2009-10-08 23:13:07 +00:00
|
|
|
|
2008-09-24 02:46:23 +00:00
|
|
|
dns_rdataset_current(&rdataset, &rdata);
|
2009-10-08 23:13:07 +00:00
|
|
|
if (!dns_nsec3param_fromprivate(&private, &rdata, buf,
|
2022-11-02 19:33:14 +01:00
|
|
|
sizeof(buf)))
|
|
|
|
{
|
2009-10-08 23:13:07 +00:00
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
|
|
|
|
if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) {
|
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
if (nsec3param.iterations > iterations) {
|
|
|
|
iterations = nsec3param.iterations;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
success:
|
|
|
|
*iterationsp = iterations;
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
failure:
|
2009-10-08 23:13:07 +00:00
|
|
|
if (node != NULL) {
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
if (dns_rdataset_isassociated(&rdataset)) {
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-01-17 11:35:11 +00:00
|
|
|
* Prevent the zone entering a inconsistent state where
|
2008-09-24 02:46:23 +00:00
|
|
|
* NSEC only DNSKEYs are present with NSEC3 chains.
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
check_dnssec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
|
|
|
|
dns_dbversion_t *ver, dns_diff_t *diff) {
|
|
|
|
isc_result_t result;
|
2021-04-19 16:32:54 +10:00
|
|
|
unsigned int iterations = 0;
|
2009-10-08 23:13:07 +00:00
|
|
|
dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
|
2008-09-24 03:16:58 +00:00
|
|
|
|
2009-12-18 22:16:49 +00:00
|
|
|
/* Refuse to allow NSEC3 with NSEC-only keys */
|
2022-08-10 15:29:59 +02:00
|
|
|
if (!dns_zone_check_dnskey_nsec3(zone, db, ver, diff, NULL, 0)) {
|
2009-12-18 22:16:49 +00:00
|
|
|
update_log(client, zone, ISC_LOG_ERROR,
|
2008-09-24 02:46:23 +00:00
|
|
|
"NSEC only DNSKEYs and NSEC3 chains not allowed");
|
2009-12-18 22:16:49 +00:00
|
|
|
result = DNS_R_REFUSED;
|
|
|
|
goto failure;
|
2008-09-24 02:46:23 +00:00
|
|
|
}
|
|
|
|
|
2009-12-18 22:16:49 +00:00
|
|
|
/* Verify NSEC3 params */
|
|
|
|
CHECK(get_iterations(db, ver, privatetype, &iterations));
|
2021-04-19 16:32:54 +10:00
|
|
|
if (iterations > dns_nsec3_maxiterations()) {
|
2009-12-18 22:16:49 +00:00
|
|
|
update_log(client, zone, ISC_LOG_ERROR,
|
2021-04-19 16:32:54 +10:00
|
|
|
"too many NSEC3 iterations (%u)", iterations);
|
2009-12-18 22:16:49 +00:00
|
|
|
result = DNS_R_REFUSED;
|
|
|
|
goto failure;
|
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
|
|
|
failure:
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delay NSEC3PARAM changes as they need to be applied to the whole zone.
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
add_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
|
2009-10-08 23:13:07 +00:00
|
|
|
dns_dbversion_t *ver, dns_diff_t *diff) {
|
2008-09-24 02:46:23 +00:00
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
2025-03-20 22:25:56 -07:00
|
|
|
dns_difftuple_t *newtuple = NULL;
|
2008-09-24 02:46:23 +00:00
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
2009-10-08 23:13:07 +00:00
|
|
|
unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE + 1];
|
2008-09-24 02:46:23 +00:00
|
|
|
dns_diff_t temp_diff;
|
|
|
|
dns_diffop_t op;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool flag;
|
2009-10-08 23:13:07 +00:00
|
|
|
dns_name_t *name = dns_zone_getorigin(zone);
|
2010-12-09 06:17:33 +00:00
|
|
|
dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
|
2018-03-28 14:19:37 +02:00
|
|
|
uint32_t ttl = 0;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool ttl_good = false;
|
2008-09-24 02:46:23 +00:00
|
|
|
|
|
|
|
update_log(client, zone, ISC_LOG_DEBUG(3),
|
|
|
|
"checking for NSEC3PARAM changes");
|
|
|
|
|
|
|
|
dns_diff_init(diff->mctx, &temp_diff);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract NSEC3PARAM tuples from list.
|
|
|
|
*/
|
2025-05-23 13:02:22 -07:00
|
|
|
ISC_LIST_FOREACH(diff->tuples, tuple, link) {
|
2008-09-24 02:46:23 +00:00
|
|
|
if (tuple->rdata.type != dns_rdatatype_nsec3param ||
|
|
|
|
!dns_name_equal(name, &tuple->name))
|
|
|
|
{
|
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
ISC_LIST_UNLINK(diff->tuples, tuple, link);
|
|
|
|
ISC_LIST_APPEND(temp_diff.tuples, tuple, link);
|
|
|
|
}
|
|
|
|
|
2010-12-07 02:53:34 +00:00
|
|
|
/*
|
|
|
|
* Extract TTL changes pairs, we don't need to convert these to
|
|
|
|
* delayed changes.
|
|
|
|
*/
|
2025-03-20 22:25:56 -07:00
|
|
|
for (dns_difftuple_t *tuple = ISC_LIST_HEAD(temp_diff.tuples),
|
|
|
|
*next = NULL;
|
|
|
|
tuple != NULL; tuple = next)
|
2022-11-02 19:33:14 +01:00
|
|
|
{
|
2008-09-24 02:46:23 +00:00
|
|
|
if (tuple->op == DNS_DIFFOP_ADD) {
|
2010-12-07 02:53:34 +00:00
|
|
|
if (!ttl_good) {
|
|
|
|
/*
|
|
|
|
* Any adds here will contain the final
|
|
|
|
* NSEC3PARAM RRset TTL.
|
|
|
|
*/
|
|
|
|
ttl = tuple->ttl;
|
2018-04-17 08:29:14 -07:00
|
|
|
ttl_good = true;
|
2010-12-07 02:53:34 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Walk the temp_diff list looking for the
|
|
|
|
* corresponding delete.
|
|
|
|
*/
|
|
|
|
next = ISC_LIST_HEAD(temp_diff.tuples);
|
|
|
|
while (next != NULL) {
|
|
|
|
unsigned char *next_data = next->rdata.data;
|
|
|
|
unsigned char *tuple_data = tuple->rdata.data;
|
|
|
|
if (next->op == DNS_DIFFOP_DEL &&
|
|
|
|
next->rdata.length == tuple->rdata.length &&
|
|
|
|
!memcmp(next_data, tuple_data,
|
|
|
|
next->rdata.length))
|
|
|
|
{
|
|
|
|
ISC_LIST_UNLINK(temp_diff.tuples, next,
|
|
|
|
link);
|
|
|
|
ISC_LIST_APPEND(diff->tuples, next,
|
|
|
|
link);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
next = ISC_LIST_NEXT(next, link);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If we have not found a pair move onto the next
|
|
|
|
* tuple.
|
|
|
|
*/
|
|
|
|
if (next == NULL) {
|
|
|
|
next = ISC_LIST_NEXT(tuple, link);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Find the next tuple to be processed before
|
|
|
|
* unlinking then complete moving the pair to 'diff'.
|
|
|
|
*/
|
|
|
|
next = ISC_LIST_NEXT(tuple, link);
|
|
|
|
ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
|
|
|
|
ISC_LIST_APPEND(diff->tuples, tuple, link);
|
|
|
|
} else {
|
2008-09-24 02:46:23 +00:00
|
|
|
next = ISC_LIST_NEXT(tuple, link);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2010-12-07 02:53:34 +00:00
|
|
|
}
|
|
|
|
|
2010-12-07 23:47:02 +00:00
|
|
|
/*
|
|
|
|
* Preserve any ongoing changes from a BIND 9.6.x upgrade.
|
2010-12-07 02:53:34 +00:00
|
|
|
*
|
|
|
|
* Any NSEC3PARAM records with flags other than OPTOUT named
|
|
|
|
* in managing and should not be touched so revert such changes
|
|
|
|
* taking into account any TTL change of the NSEC3PARAM RRset.
|
2010-12-07 23:47:02 +00:00
|
|
|
*/
|
2025-05-23 13:02:22 -07:00
|
|
|
ISC_LIST_FOREACH(temp_diff.tuples, tuple, link) {
|
2010-12-07 23:47:02 +00:00
|
|
|
if ((tuple->rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) != 0) {
|
2010-12-07 02:53:34 +00:00
|
|
|
/*
|
|
|
|
* If we haven't had any adds then the tuple->ttl must
|
|
|
|
* be the original ttl and should be used for any
|
|
|
|
* future changes.
|
|
|
|
*/
|
|
|
|
if (!ttl_good) {
|
|
|
|
ttl = tuple->ttl;
|
2018-04-17 08:29:14 -07:00
|
|
|
ttl_good = true;
|
2010-12-07 02:53:34 +00:00
|
|
|
}
|
2010-12-07 23:47:02 +00:00
|
|
|
op = (tuple->op == DNS_DIFFOP_DEL) ? DNS_DIFFOP_ADD
|
|
|
|
: DNS_DIFFOP_DEL;
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(diff->mctx, op, name, ttl,
|
|
|
|
&tuple->rdata, &newtuple);
|
2010-12-07 23:47:02 +00:00
|
|
|
CHECK(do_one_tuple(&newtuple, db, ver, diff));
|
|
|
|
ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
|
|
|
|
dns_diff_appendminimal(diff, &tuple);
|
|
|
|
}
|
|
|
|
}
|
2010-12-07 02:53:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We now have just the actual changes to the NSEC3PARAM RRset.
|
|
|
|
* Convert the adds to delayed adds and the deletions into delayed
|
|
|
|
* deletions.
|
|
|
|
*/
|
2025-03-20 22:25:56 -07:00
|
|
|
for (dns_difftuple_t *tuple = ISC_LIST_HEAD(temp_diff.tuples),
|
|
|
|
*next = NULL;
|
|
|
|
tuple != NULL; tuple = next)
|
2022-11-02 19:33:14 +01:00
|
|
|
{
|
2010-12-07 02:53:34 +00:00
|
|
|
/*
|
|
|
|
* If we haven't had any adds then the tuple->ttl must be the
|
|
|
|
* original ttl and should be used for any future changes.
|
|
|
|
*/
|
|
|
|
if (!ttl_good) {
|
|
|
|
ttl = tuple->ttl;
|
2018-04-17 08:29:14 -07:00
|
|
|
ttl_good = true;
|
2010-12-07 02:53:34 +00:00
|
|
|
}
|
|
|
|
if (tuple->op == DNS_DIFFOP_ADD) {
|
2018-04-17 08:29:14 -07:00
|
|
|
bool nseconly = false;
|
2011-06-10 01:51:09 +00:00
|
|
|
|
2010-12-07 02:53:34 +00:00
|
|
|
/*
|
|
|
|
* Look for any deletes which match this ADD ignoring
|
2011-06-10 01:51:09 +00:00
|
|
|
* flags. We don't need to explicitly remove them as
|
2010-12-07 02:53:34 +00:00
|
|
|
* they will be removed a side effect of processing
|
|
|
|
* the add.
|
|
|
|
*/
|
|
|
|
next = ISC_LIST_HEAD(temp_diff.tuples);
|
2008-09-24 02:46:23 +00:00
|
|
|
while (next != NULL) {
|
|
|
|
unsigned char *next_data = next->rdata.data;
|
|
|
|
unsigned char *tuple_data = tuple->rdata.data;
|
2010-12-07 02:53:34 +00:00
|
|
|
if (next->op != DNS_DIFFOP_DEL ||
|
|
|
|
next->rdata.length != tuple->rdata.length ||
|
|
|
|
next_data[0] != tuple_data[0] ||
|
2008-09-24 02:46:23 +00:00
|
|
|
next_data[2] != tuple_data[2] ||
|
2008-09-24 03:16:58 +00:00
|
|
|
next_data[3] != tuple_data[3] ||
|
2010-12-07 02:53:34 +00:00
|
|
|
memcmp(next_data + 4, tuple_data + 4,
|
|
|
|
tuple->rdata.length - 4))
|
|
|
|
{
|
2008-09-24 02:46:23 +00:00
|
|
|
next = ISC_LIST_NEXT(next, link);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ISC_LIST_UNLINK(temp_diff.tuples, next, link);
|
2010-12-07 02:53:34 +00:00
|
|
|
ISC_LIST_APPEND(diff->tuples, next, link);
|
|
|
|
next = ISC_LIST_HEAD(temp_diff.tuples);
|
2008-09-24 02:46:23 +00:00
|
|
|
}
|
2011-06-10 01:51:09 +00:00
|
|
|
|
2008-09-24 02:46:23 +00:00
|
|
|
/*
|
2011-06-10 01:51:09 +00:00
|
|
|
* Create a private-type record to signal that
|
|
|
|
* we want a delayed NSEC3 chain add/delete
|
2008-09-24 02:46:23 +00:00
|
|
|
*/
|
2009-10-08 23:13:07 +00:00
|
|
|
dns_nsec3param_toprivate(&tuple->rdata, &rdata,
|
|
|
|
privatetype, buf, sizeof(buf));
|
|
|
|
buf[2] |= DNS_NSEC3FLAG_CREATE;
|
2011-06-10 01:51:09 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the zone is not currently capable of
|
|
|
|
* supporting an NSEC3 chain, then we set the
|
|
|
|
* INITIAL flag to indicate that these parameters
|
|
|
|
* are to be used later.
|
2022-08-10 15:29:59 +02:00
|
|
|
*
|
|
|
|
* Don't provide a 'diff' here because we want to
|
|
|
|
* know the capability of the current database.
|
2011-06-10 01:51:09 +00:00
|
|
|
*/
|
2022-08-10 15:29:59 +02:00
|
|
|
result = dns_nsec_nseconly(db, ver, NULL, &nseconly);
|
2011-06-10 01:51:09 +00:00
|
|
|
if (result == ISC_R_NOTFOUND || nseconly) {
|
|
|
|
buf[2] |= DNS_NSEC3FLAG_INITIAL;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-06-10 01:51:09 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* See if this CREATE request already exists.
|
|
|
|
*/
|
2008-09-24 02:46:23 +00:00
|
|
|
CHECK(rr_exists(db, ver, name, &rdata, &flag));
|
|
|
|
|
|
|
|
if (!flag) {
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
|
|
|
|
name, 0, &rdata,
|
|
|
|
&newtuple);
|
2008-09-24 02:46:23 +00:00
|
|
|
CHECK(do_one_tuple(&newtuple, db, ver, diff));
|
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
|
2009-12-30 02:43:09 +00:00
|
|
|
/*
|
|
|
|
* Remove any existing CREATE request to add an
|
|
|
|
* otherwise identical chain with a reversed
|
|
|
|
* OPTOUT state.
|
|
|
|
*/
|
|
|
|
buf[2] ^= DNS_NSEC3FLAG_OPTOUT;
|
|
|
|
CHECK(rr_exists(db, ver, name, &rdata, &flag));
|
|
|
|
|
|
|
|
if (flag) {
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
|
|
|
|
name, 0, &rdata,
|
|
|
|
&newtuple);
|
2009-12-30 02:43:09 +00:00
|
|
|
CHECK(do_one_tuple(&newtuple, db, ver, diff));
|
|
|
|
}
|
|
|
|
|
2008-09-24 02:46:23 +00:00
|
|
|
/*
|
2010-12-07 02:53:34 +00:00
|
|
|
* Find the next tuple to be processed and remove the
|
|
|
|
* temporary add record.
|
2008-09-24 02:46:23 +00:00
|
|
|
*/
|
2010-12-07 02:53:34 +00:00
|
|
|
next = ISC_LIST_NEXT(tuple, link);
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, name,
|
|
|
|
ttl, &tuple->rdata, &newtuple);
|
2008-09-24 02:46:23 +00:00
|
|
|
CHECK(do_one_tuple(&newtuple, db, ver, diff));
|
|
|
|
ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
|
|
|
|
dns_diff_appendminimal(diff, &tuple);
|
|
|
|
dns_rdata_reset(&rdata);
|
|
|
|
} else {
|
|
|
|
next = ISC_LIST_NEXT(tuple, link);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
}
|
|
|
|
|
2025-03-20 22:25:56 -07:00
|
|
|
for (dns_difftuple_t *tuple = ISC_LIST_HEAD(temp_diff.tuples),
|
|
|
|
*next = NULL;
|
|
|
|
tuple != NULL; tuple = next)
|
2022-11-02 19:33:14 +01:00
|
|
|
{
|
2010-12-07 02:53:34 +00:00
|
|
|
INSIST(ttl_good);
|
|
|
|
|
2008-09-24 02:46:23 +00:00
|
|
|
next = ISC_LIST_NEXT(tuple, link);
|
|
|
|
/*
|
|
|
|
* See if we already have a REMOVE request in progress.
|
|
|
|
*/
|
2010-12-07 02:53:34 +00:00
|
|
|
dns_nsec3param_toprivate(&tuple->rdata, &rdata, privatetype,
|
|
|
|
buf, sizeof(buf));
|
|
|
|
|
|
|
|
buf[2] |= DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC;
|
2008-09-24 02:46:23 +00:00
|
|
|
|
|
|
|
CHECK(rr_exists(db, ver, name, &rdata, &flag));
|
2010-12-07 02:53:34 +00:00
|
|
|
if (!flag) {
|
|
|
|
buf[2] &= ~DNS_NSEC3FLAG_NONSEC;
|
|
|
|
CHECK(rr_exists(db, ver, name, &rdata, &flag));
|
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
|
|
|
if (!flag) {
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
|
|
|
|
0, &rdata, &newtuple);
|
2008-09-24 02:46:23 +00:00
|
|
|
CHECK(do_one_tuple(&newtuple, db, ver, diff));
|
|
|
|
}
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, ttl,
|
|
|
|
&tuple->rdata, &newtuple);
|
2008-09-24 02:46:23 +00:00
|
|
|
CHECK(do_one_tuple(&newtuple, db, ver, diff));
|
|
|
|
ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
|
|
|
|
dns_diff_appendminimal(diff, &tuple);
|
|
|
|
dns_rdata_reset(&rdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
failure:
|
|
|
|
dns_diff_clear(&temp_diff);
|
|
|
|
return result;
|
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
rollback_private(dns_db_t *db, dns_rdatatype_t privatetype,
|
|
|
|
dns_dbversion_t *ver, dns_diff_t *diff) {
|
|
|
|
dns_diff_t temp_diff;
|
|
|
|
dns_diffop_t op;
|
2025-03-20 22:25:56 -07:00
|
|
|
dns_difftuple_t *newtuple = NULL;
|
2009-10-08 23:13:07 +00:00
|
|
|
dns_name_t *name = dns_db_origin(db);
|
|
|
|
isc_mem_t *mctx = diff->mctx;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
if (privatetype == 0) {
|
|
|
|
return ISC_R_SUCCESS;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
|
|
|
|
dns_diff_init(mctx, &temp_diff);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract the changes to be rolled back.
|
|
|
|
*/
|
2025-05-23 13:02:22 -07:00
|
|
|
ISC_LIST_FOREACH(diff->tuples, tuple, link) {
|
2009-10-08 23:13:07 +00:00
|
|
|
if (tuple->rdata.type != privatetype ||
|
2022-11-02 19:33:14 +01:00
|
|
|
!dns_name_equal(name, &tuple->name))
|
|
|
|
{
|
2009-10-08 23:13:07 +00:00
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Allow records which indicate that a zone has been
|
2011-02-15 18:23:34 +00:00
|
|
|
* signed with a DNSKEY to be removed.
|
2009-10-08 23:13:07 +00:00
|
|
|
*/
|
|
|
|
if (tuple->op == DNS_DIFFOP_DEL && tuple->rdata.length == 5 &&
|
|
|
|
tuple->rdata.data[0] != 0 && tuple->rdata.data[4] != 0)
|
|
|
|
{
|
|
|
|
continue;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-10-08 23:13:07 +00:00
|
|
|
|
|
|
|
ISC_LIST_UNLINK(diff->tuples, tuple, link);
|
|
|
|
ISC_LIST_PREPEND(temp_diff.tuples, tuple, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rollback the changes.
|
|
|
|
*/
|
2025-05-23 13:02:22 -07:00
|
|
|
ISC_LIST_FOREACH(temp_diff.tuples, tuple, link) {
|
2009-10-08 23:13:07 +00:00
|
|
|
op = (tuple->op == DNS_DIFFOP_DEL) ? DNS_DIFFOP_ADD
|
|
|
|
: DNS_DIFFOP_DEL;
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(mctx, op, name, tuple->ttl, &tuple->rdata,
|
|
|
|
&newtuple);
|
2009-10-08 23:13:07 +00:00
|
|
|
CHECK(do_one_tuple(&newtuple, db, ver, &temp_diff));
|
|
|
|
}
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
failure:
|
|
|
|
dns_diff_clear(&temp_diff);
|
|
|
|
return result;
|
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
static bool
|
2009-10-08 23:13:07 +00:00
|
|
|
isdnssec(dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_t privatetype) {
|
|
|
|
isc_result_t result;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool build_nsec, build_nsec3;
|
2009-10-08 23:13:07 +00:00
|
|
|
|
|
|
|
if (dns_db_issecure(db)) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return true;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2009-10-08 23:48:10 +00:00
|
|
|
|
2009-10-08 23:13:07 +00:00
|
|
|
result = dns_private_chains(db, ver, privatetype, &build_nsec,
|
|
|
|
&build_nsec3);
|
|
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
return build_nsec || build_nsec3;
|
|
|
|
}
|
2008-04-01 01:37:25 +00:00
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
static void
|
2022-10-26 23:35:47 -07:00
|
|
|
update_action(void *arg) {
|
|
|
|
update_t *uev = (update_t *)arg;
|
1999-10-14 00:03:04 +00:00
|
|
|
dns_zone_t *zone = uev->zone;
|
2022-10-26 23:35:47 -07:00
|
|
|
ns_client_t *client = uev->client;
|
2024-11-20 01:20:42 +11:00
|
|
|
unsigned int *maxbytype = uev->maxbytype;
|
|
|
|
size_t update = 0, maxbytypelen = uev->maxbytypelen;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-10-14 00:03:04 +00:00
|
|
|
dns_db_t *db = NULL;
|
|
|
|
dns_dbversion_t *oldver = NULL;
|
|
|
|
dns_dbversion_t *ver = NULL;
|
2006-12-04 01:54:53 +00:00
|
|
|
dns_diff_t diff; /* Pending updates. */
|
|
|
|
dns_diff_t temp; /* Pending RR existence assertions. */
|
2018-04-17 08:29:14 -07:00
|
|
|
bool soa_serial_changed = false;
|
2022-03-23 13:57:15 +01:00
|
|
|
isc_mem_t *mctx = client->manager->mctx;
|
1999-10-14 00:03:04 +00:00
|
|
|
dns_rdatatype_t covers;
|
|
|
|
dns_message_t *request = client->message;
|
|
|
|
dns_rdataclass_t zoneclass;
|
2022-11-08 17:32:41 -08:00
|
|
|
dns_name_t *zonename = NULL;
|
2000-03-06 19:08:05 +00:00
|
|
|
dns_ssutable_t *ssutable = NULL;
|
2001-11-20 05:04:41 +00:00
|
|
|
dns_fixedname_t tmpnamefixed;
|
|
|
|
dns_name_t *tmpname = NULL;
|
2018-04-03 23:56:04 +02:00
|
|
|
dns_zoneopt_t options;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool had_dnskey;
|
2009-10-08 23:13:07 +00:00
|
|
|
dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
|
2014-02-18 23:26:50 -08:00
|
|
|
dns_ttl_t maxttl = 0;
|
2018-03-28 14:19:37 +02:00
|
|
|
uint32_t maxrecords;
|
|
|
|
uint64_t records;
|
2022-10-12 17:01:57 +11:00
|
|
|
bool is_inline, is_maintain, is_signing;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
dns_diff_init(mctx, &diff);
|
|
|
|
dns_diff_init(mctx, &temp);
|
|
|
|
|
|
|
|
CHECK(dns_zone_getdb(zone, &db));
|
|
|
|
zonename = dns_db_origin(db);
|
|
|
|
zoneclass = dns_db_class(db);
|
2000-03-06 19:08:05 +00:00
|
|
|
dns_zone_getssutable(zone, &ssutable);
|
2022-11-08 17:32:41 -08:00
|
|
|
options = dns_zone_getoptions(zone);
|
2009-10-27 05:42:25 +00:00
|
|
|
|
2022-10-12 17:01:57 +11:00
|
|
|
is_inline = (!dns_zone_israw(zone) && dns_zone_issecure(zone));
|
2025-06-07 14:57:54 -07:00
|
|
|
is_maintain = (dns_zone_getkasp(zone) != NULL) && !dns_zone_israw(zone);
|
|
|
|
is_signing = is_inline || is_maintain;
|
2022-10-12 17:01:57 +11:00
|
|
|
|
2009-10-27 05:42:25 +00:00
|
|
|
/*
|
|
|
|
* Get old and new versions now that queryacl has been checked.
|
|
|
|
*/
|
1999-09-09 08:58:00 +00:00
|
|
|
dns_db_currentversion(db, &oldver);
|
1999-08-20 05:35:16 +00:00
|
|
|
CHECK(dns_db_newversion(db, &ver));
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2000-05-24 03:26:35 +00:00
|
|
|
/*
|
|
|
|
* Check prerequisites.
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2025-03-19 20:29:17 +01:00
|
|
|
MSG_SECTION_FOREACH(request, DNS_SECTION_PREREQUISITE, name) {
|
2000-10-25 04:26:57 +00:00
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_ttl_t ttl;
|
|
|
|
dns_rdataclass_t update_class;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool flag;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2025-03-19 20:29:17 +01:00
|
|
|
get_current_rr(zoneclass, name, &rdata, &covers, &ttl,
|
|
|
|
&update_class);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
if (ttl != 0) {
|
2024-11-14 12:34:11 +11:00
|
|
|
PREREQFAILC(DNS_R_FORMERR,
|
|
|
|
"prerequisite TTL is not zero");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
if (!dns_name_issubdomain(name, zonename)) {
|
2008-04-03 05:55:52 +00:00
|
|
|
PREREQFAILN(DNS_R_NOTZONE, name,
|
|
|
|
"prerequisite name is out of zone");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
if (update_class == dns_rdataclass_any) {
|
|
|
|
if (rdata.length != 0) {
|
2024-11-14 12:34:11 +11:00
|
|
|
PREREQFAILC(DNS_R_FORMERR,
|
|
|
|
"class ANY prerequisite RDATA is "
|
|
|
|
"not empty");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
if (rdata.type == dns_rdatatype_any) {
|
|
|
|
CHECK(name_exists(db, ver, name, &flag));
|
|
|
|
if (!flag) {
|
2024-11-14 12:34:11 +11:00
|
|
|
PREREQFAILN(
|
|
|
|
DNS_R_NXDOMAIN, name,
|
|
|
|
"'name in use' prerequisite "
|
|
|
|
"not satisfied");
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CHECK(rrset_exists(db, ver, name, rdata.type,
|
1999-08-31 22:14:06 +00:00
|
|
|
covers, &flag));
|
1999-08-20 05:35:16 +00:00
|
|
|
if (!flag) {
|
|
|
|
/* RRset does not exist. */
|
2024-11-14 12:34:11 +11:00
|
|
|
PREREQFAILNT(
|
|
|
|
DNS_R_NXRRSET, name, rdata.type,
|
|
|
|
"'rrset exists (value "
|
|
|
|
"independent)' prerequisite "
|
|
|
|
"not satisfied");
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (update_class == dns_rdataclass_none) {
|
|
|
|
if (rdata.length != 0) {
|
2024-11-14 12:34:11 +11:00
|
|
|
PREREQFAILC(DNS_R_FORMERR,
|
|
|
|
"class NONE prerequisite RDATA is "
|
|
|
|
"not empty");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
if (rdata.type == dns_rdatatype_any) {
|
|
|
|
CHECK(name_exists(db, ver, name, &flag));
|
|
|
|
if (flag) {
|
2024-11-14 12:34:11 +11:00
|
|
|
PREREQFAILN(
|
|
|
|
DNS_R_YXDOMAIN, name,
|
|
|
|
"'name not in use' "
|
|
|
|
"prerequisite not satisfied");
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CHECK(rrset_exists(db, ver, name, rdata.type,
|
1999-08-31 22:14:06 +00:00
|
|
|
covers, &flag));
|
1999-08-20 05:35:16 +00:00
|
|
|
if (flag) {
|
|
|
|
/* RRset exists. */
|
2024-11-14 12:34:11 +11:00
|
|
|
PREREQFAILNT(
|
|
|
|
DNS_R_YXRRSET, name, rdata.type,
|
|
|
|
"'rrset does not exist' "
|
|
|
|
"prerequisite not satisfied");
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (update_class == zoneclass) {
|
|
|
|
/* "temp<rr.name, rr.type> += rr;" */
|
2024-07-11 17:00:38 -07:00
|
|
|
temp_append(&temp, name, &rdata);
|
1999-08-20 05:35:16 +00:00
|
|
|
} else {
|
2008-04-03 05:55:52 +00:00
|
|
|
PREREQFAILC(DNS_R_FORMERR, "malformed prerequisite");
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform the final check of the "rrset exists (value dependent)"
|
|
|
|
* prerequisites.
|
|
|
|
*/
|
2001-11-20 05:04:41 +00:00
|
|
|
if (ISC_LIST_HEAD(temp.tuples) != NULL) {
|
|
|
|
dns_rdatatype_t type;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sort the prerequisite records by owner name,
|
|
|
|
* type, and rdata.
|
|
|
|
*/
|
|
|
|
result = dns_diff_sort(&temp, temp_order);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
FAILC(result, "'RRset exists (value dependent)' "
|
|
|
|
"prerequisite not satisfied");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2001-11-20 05:04:41 +00:00
|
|
|
|
2018-03-28 14:38:09 +02:00
|
|
|
tmpname = dns_fixedname_initname(&tmpnamefixed);
|
2001-11-20 05:04:41 +00:00
|
|
|
result = temp_check(mctx, &temp, db, ver, tmpname, &type);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
FAILNT(result, tmpname, type,
|
2024-11-14 12:34:11 +11:00
|
|
|
"'RRset exists (value dependent)' prerequisite "
|
|
|
|
"not satisfied");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2001-11-20 05:04:41 +00:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2001-01-18 23:31:03 +00:00
|
|
|
update_log(client, zone, LOGLEVEL_DEBUG, "prerequisites are OK");
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2000-05-24 03:26:35 +00:00
|
|
|
/*
|
|
|
|
* Process the Update Section.
|
|
|
|
*/
|
2024-11-20 01:20:42 +11:00
|
|
|
INSIST(ssutable == NULL || maxbytype != NULL);
|
2025-03-19 20:29:17 +01:00
|
|
|
|
|
|
|
update = 0;
|
|
|
|
MSG_SECTION_FOREACH(request, DNS_SECTION_UPDATE, name) {
|
2000-10-25 04:26:57 +00:00
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_ttl_t ttl;
|
|
|
|
dns_rdataclass_t update_class;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool flag;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2024-11-20 01:20:42 +11:00
|
|
|
INSIST(ssutable == NULL || update < maxbytypelen);
|
2019-07-03 17:03:13 +10:00
|
|
|
|
2025-03-19 20:29:17 +01:00
|
|
|
get_current_rr(zoneclass, name, &rdata, &covers, &ttl,
|
|
|
|
&update_class);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
if (update_class == zoneclass) {
|
2001-12-11 23:53:13 +00:00
|
|
|
/*
|
2021-10-05 11:07:44 +02:00
|
|
|
* RFC1123 doesn't allow MF and MD in master files.
|
2018-01-22 14:26:04 +05:30
|
|
|
*/
|
2001-12-11 23:53:13 +00:00
|
|
|
if (rdata.type == dns_rdatatype_md ||
|
2022-11-02 19:33:14 +01:00
|
|
|
rdata.type == dns_rdatatype_mf)
|
|
|
|
{
|
2001-12-11 23:53:13 +00:00
|
|
|
char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
|
|
|
|
|
|
|
dns_rdatatype_format(rdata.type, typebuf,
|
|
|
|
sizeof(typebuf));
|
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
|
|
|
"attempt to add %s ignored",
|
|
|
|
typebuf);
|
|
|
|
continue;
|
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
if ((rdata.type == dns_rdatatype_ns ||
|
|
|
|
rdata.type == dns_rdatatype_dname) &&
|
2002-11-12 20:16:30 +00:00
|
|
|
dns_name_iswildcard(name))
|
|
|
|
{
|
2008-09-24 02:46:23 +00:00
|
|
|
char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
|
|
|
|
|
|
|
dns_rdatatype_format(rdata.type, typebuf,
|
|
|
|
sizeof(typebuf));
|
2002-11-12 20:16:30 +00:00
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
2008-09-24 02:46:23 +00:00
|
|
|
"attempt to add wildcard %s record "
|
|
|
|
"ignored",
|
|
|
|
typebuf);
|
2002-11-12 20:16:30 +00:00
|
|
|
continue;
|
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
if (rdata.type == dns_rdatatype_cname) {
|
|
|
|
CHECK(cname_incompatible_rrset_exists(
|
|
|
|
db, ver, name, &flag));
|
|
|
|
if (flag) {
|
2024-11-14 12:34:11 +11:00
|
|
|
update_log(
|
|
|
|
client, zone, LOGLEVEL_PROTOCOL,
|
|
|
|
"attempt to add CNAME "
|
|
|
|
"alongside non-CNAME ignored");
|
1999-08-20 05:35:16 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CHECK(rrset_exists(db, ver, name,
|
2025-08-11 07:17:43 +02:00
|
|
|
dns_rdatatype_cname,
|
|
|
|
dns_rdatatype_none, &flag));
|
2018-11-26 10:57:02 +11:00
|
|
|
if (flag && !dns_rdatatype_atcname(rdata.type))
|
|
|
|
{
|
2001-01-18 23:31:03 +00:00
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
|
|
|
"attempt to add non-CNAME "
|
|
|
|
"alongside CNAME ignored");
|
1999-08-20 05:35:16 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rdata.type == dns_rdatatype_soa) {
|
2018-04-17 08:29:14 -07:00
|
|
|
bool ok;
|
1999-08-20 05:35:16 +00:00
|
|
|
CHECK(rrset_exists(db, ver, name,
|
2025-08-11 07:17:43 +02:00
|
|
|
dns_rdatatype_soa,
|
|
|
|
dns_rdatatype_none, &flag));
|
1999-08-20 05:35:16 +00:00
|
|
|
if (!flag) {
|
2001-01-18 23:31:03 +00:00
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
2024-11-14 12:34:11 +11:00
|
|
|
"attempt to create 2nd SOA "
|
|
|
|
"ignored");
|
1999-08-20 05:35:16 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
CHECK(check_soa_increment(db, ver, &rdata,
|
1999-10-11 22:34:36 +00:00
|
|
|
&ok));
|
1999-08-20 05:35:16 +00:00
|
|
|
if (!ok) {
|
2001-01-18 23:31:03 +00:00
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
|
|
|
"SOA update failed to "
|
2024-11-14 12:34:11 +11:00
|
|
|
"increment serial, ignoring "
|
|
|
|
"it");
|
1999-08-20 05:35:16 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-04-17 08:29:14 -07:00
|
|
|
soa_serial_changed = true;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2020-05-07 09:36:50 +10:00
|
|
|
if (dns_rdatatype_atparent(rdata.type) &&
|
2022-11-02 19:33:14 +01:00
|
|
|
dns_name_equal(name, zonename))
|
|
|
|
{
|
2020-05-07 09:36:50 +10:00
|
|
|
char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
|
|
|
|
|
|
|
dns_rdatatype_format(rdata.type, typebuf,
|
|
|
|
sizeof(typebuf));
|
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
2024-11-14 12:34:11 +11:00
|
|
|
"attempt to add a %s record at zone "
|
|
|
|
"apex ignored",
|
2020-05-07 09:36:50 +10:00
|
|
|
typebuf);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-10-08 23:13:07 +00:00
|
|
|
if (rdata.type == privatetype) {
|
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
2024-11-14 12:34:11 +11:00
|
|
|
"attempt to add a private type (%u) "
|
|
|
|
"record rejected internal use only",
|
2009-10-08 23:13:07 +00:00
|
|
|
privatetype);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-09-24 02:46:23 +00:00
|
|
|
if (rdata.type == dns_rdatatype_nsec3param) {
|
|
|
|
/*
|
|
|
|
* Ignore attempts to add NSEC3PARAM records
|
|
|
|
* with any flags other than OPTOUT.
|
|
|
|
*/
|
2018-01-22 14:26:04 +05:30
|
|
|
if ((rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) !=
|
2022-11-02 19:33:14 +01:00
|
|
|
0)
|
|
|
|
{
|
2024-11-14 12:34:11 +11:00
|
|
|
update_log(
|
|
|
|
client, zone, LOGLEVEL_PROTOCOL,
|
|
|
|
"attempt to add NSEC3PARAM "
|
|
|
|
"record with non OPTOUT flag");
|
2008-09-24 02:46:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-01-09 23:40:04 +00:00
|
|
|
if ((options & DNS_ZONEOPT_CHECKWILDCARD) != 0 &&
|
|
|
|
dns_name_internalwildcard(name))
|
|
|
|
{
|
|
|
|
char namestr[DNS_NAME_FORMATSIZE];
|
|
|
|
dns_name_format(name, namestr, sizeof(namestr));
|
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
2024-11-14 12:34:11 +11:00
|
|
|
"warning: ownername '%s' contains a "
|
|
|
|
"non-terminal wildcard",
|
2005-01-09 23:40:04 +00:00
|
|
|
namestr);
|
|
|
|
}
|
2002-01-22 16:05:51 +00:00
|
|
|
|
2018-04-03 23:56:04 +02:00
|
|
|
if ((options & DNS_ZONEOPT_CHECKTTL) != 0) {
|
2014-02-18 23:26:50 -08:00
|
|
|
maxttl = dns_zone_getmaxttl(zone);
|
|
|
|
if (ttl > maxttl) {
|
|
|
|
ttl = maxttl;
|
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
|
|
|
"reducing TTL to the "
|
|
|
|
"configured max-zone-ttl %d",
|
|
|
|
maxttl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-20 01:20:42 +11:00
|
|
|
if (maxbytype != NULL && maxbytype[update] != 0) {
|
2019-07-04 15:24:20 +10:00
|
|
|
unsigned int count = 0;
|
|
|
|
CHECK(foreach_rr(db, ver, name, rdata.type,
|
|
|
|
covers, count_action, &count));
|
2024-11-20 01:20:42 +11:00
|
|
|
if (count >= maxbytype[update]) {
|
2019-07-04 15:24:20 +10:00
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
|
|
|
"attempt to add more "
|
|
|
|
"records than permitted by "
|
|
|
|
"policy max=%u",
|
2024-11-20 01:20:42 +11:00
|
|
|
maxbytype[update]);
|
2019-07-04 15:24:20 +10:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-13 18:20:26 +02:00
|
|
|
if (isc_log_wouldlog(LOGLEVEL_PROTOCOL)) {
|
2002-01-22 16:05:51 +00:00
|
|
|
char namestr[DNS_NAME_FORMATSIZE];
|
|
|
|
char typestr[DNS_RDATATYPE_FORMATSIZE];
|
2013-02-20 13:54:52 -08:00
|
|
|
char rdstr[2048];
|
2013-01-24 16:35:23 -06:00
|
|
|
isc_buffer_t buf;
|
|
|
|
int len = 0;
|
2013-02-20 13:54:52 -08:00
|
|
|
const char *truncated = "";
|
|
|
|
|
2013-01-24 16:35:23 -06:00
|
|
|
dns_name_format(name, namestr, sizeof(namestr));
|
2002-01-22 16:05:51 +00:00
|
|
|
dns_rdatatype_format(rdata.type, typestr,
|
|
|
|
sizeof(typestr));
|
2013-01-24 16:35:23 -06:00
|
|
|
isc_buffer_init(&buf, rdstr, sizeof(rdstr));
|
|
|
|
result = dns_rdata_totext(&rdata, NULL, &buf);
|
2013-02-20 13:54:52 -08:00
|
|
|
if (result == ISC_R_NOSPACE) {
|
|
|
|
len = (int)isc_buffer_usedlength(&buf);
|
|
|
|
truncated = " [TRUNCATED]";
|
|
|
|
} else if (result != ISC_R_SUCCESS) {
|
2024-11-14 12:34:11 +11:00
|
|
|
snprintf(
|
|
|
|
rdstr, sizeof(rdstr),
|
|
|
|
"[dns_rdata_totext failed: %s]",
|
|
|
|
isc_result_totext(result));
|
2013-02-20 13:54:52 -08:00
|
|
|
len = strlen(rdstr);
|
|
|
|
} else {
|
|
|
|
len = (int)isc_buffer_usedlength(&buf);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2006-12-04 01:54:53 +00:00
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
2013-02-20 13:54:52 -08:00
|
|
|
"adding an RR at '%s' %s %.*s%s",
|
|
|
|
namestr, typestr, len, rdstr,
|
|
|
|
truncated);
|
2002-01-22 16:05:51 +00:00
|
|
|
}
|
2000-12-16 00:58:03 +00:00
|
|
|
|
|
|
|
/* Prepare the affected RRset for the addition. */
|
|
|
|
{
|
|
|
|
add_rr_prepare_ctx_t ctx;
|
|
|
|
ctx.db = db;
|
|
|
|
ctx.ver = ver;
|
|
|
|
ctx.diff = &diff;
|
|
|
|
ctx.name = name;
|
2015-02-27 15:08:38 +11:00
|
|
|
ctx.oldname = name;
|
2000-12-16 00:58:03 +00:00
|
|
|
ctx.update_rr = &rdata;
|
|
|
|
ctx.update_rr_ttl = ttl;
|
2018-04-17 08:29:14 -07:00
|
|
|
ctx.ignore_add = false;
|
2000-12-16 00:58:03 +00:00
|
|
|
dns_diff_init(mctx, &ctx.del_diff);
|
|
|
|
dns_diff_init(mctx, &ctx.add_diff);
|
2004-05-05 01:32:58 +00:00
|
|
|
CHECK(foreach_rr(db, ver, name, rdata.type,
|
|
|
|
covers, add_rr_prepare_action,
|
|
|
|
&ctx));
|
2000-12-16 00:58:03 +00:00
|
|
|
|
|
|
|
if (ctx.ignore_add) {
|
|
|
|
dns_diff_clear(&ctx.del_diff);
|
|
|
|
dns_diff_clear(&ctx.add_diff);
|
|
|
|
} else {
|
2014-06-18 11:21:31 +10:00
|
|
|
result = do_diff(&ctx.del_diff, db, ver,
|
|
|
|
&diff);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
result = do_diff(&ctx.add_diff,
|
|
|
|
db, ver,
|
|
|
|
&diff);
|
|
|
|
}
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
dns_diff_clear(&ctx.del_diff);
|
|
|
|
dns_diff_clear(&ctx.add_diff);
|
|
|
|
goto failure;
|
|
|
|
}
|
2024-06-03 07:56:21 +02:00
|
|
|
result = update_one_rr(
|
|
|
|
db, ver, &diff, DNS_DIFFOP_ADD,
|
|
|
|
name, ttl, &rdata);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
|
|
|
"adding an RR "
|
|
|
|
"failed: %s",
|
|
|
|
isc_result_totext(
|
|
|
|
result));
|
|
|
|
goto failure;
|
|
|
|
}
|
2000-12-16 00:58:03 +00:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
} else if (update_class == dns_rdataclass_any) {
|
|
|
|
if (rdata.type == dns_rdatatype_any) {
|
2024-08-13 18:20:26 +02:00
|
|
|
if (isc_log_wouldlog(LOGLEVEL_PROTOCOL)) {
|
2002-01-22 16:05:51 +00:00
|
|
|
char namestr[DNS_NAME_FORMATSIZE];
|
|
|
|
dns_name_format(name, namestr,
|
|
|
|
sizeof(namestr));
|
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
|
|
|
"delete all rrsets from "
|
2002-01-22 21:45:07 +00:00
|
|
|
"name '%s'",
|
|
|
|
namestr);
|
2002-01-22 16:05:51 +00:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
if (dns_name_equal(name, zonename)) {
|
|
|
|
CHECK(delete_if(type_not_soa_nor_ns_p,
|
|
|
|
db, ver, name,
|
2025-08-11 07:17:43 +02:00
|
|
|
dns_rdatatype_any,
|
|
|
|
dns_rdatatype_none,
|
1999-08-20 05:35:16 +00:00
|
|
|
&rdata, &diff));
|
|
|
|
} else {
|
2004-10-21 01:29:29 +00:00
|
|
|
CHECK(delete_if(type_not_dnssec, db,
|
|
|
|
ver, name,
|
2025-08-11 07:17:43 +02:00
|
|
|
dns_rdatatype_any,
|
|
|
|
dns_rdatatype_none,
|
1999-08-20 05:35:16 +00:00
|
|
|
&rdata, &diff));
|
|
|
|
}
|
|
|
|
} else if (dns_name_equal(name, zonename) &&
|
|
|
|
(rdata.type == dns_rdatatype_soa ||
|
|
|
|
rdata.type == dns_rdatatype_ns))
|
|
|
|
{
|
2006-12-04 01:54:53 +00:00
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
2024-11-14 12:34:11 +11:00
|
|
|
"attempt to delete all SOA or NS "
|
|
|
|
"records ignored");
|
1999-08-20 05:35:16 +00:00
|
|
|
continue;
|
|
|
|
} else {
|
2024-08-13 18:20:26 +02:00
|
|
|
if (isc_log_wouldlog(LOGLEVEL_PROTOCOL)) {
|
2002-01-22 16:05:51 +00:00
|
|
|
char namestr[DNS_NAME_FORMATSIZE];
|
|
|
|
char typestr[DNS_RDATATYPE_FORMATSIZE];
|
|
|
|
dns_name_format(name, namestr,
|
|
|
|
sizeof(namestr));
|
|
|
|
dns_rdatatype_format(rdata.type,
|
|
|
|
typestr,
|
|
|
|
sizeof(typestr));
|
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
|
|
|
"deleting rrset at '%s' %s",
|
|
|
|
namestr, typestr);
|
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
CHECK(delete_if(true_p, db, ver, name,
|
1999-08-31 22:14:06 +00:00
|
|
|
rdata.type, covers, &rdata,
|
|
|
|
&diff));
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
} else if (update_class == dns_rdataclass_none) {
|
2009-08-17 07:18:41 +00:00
|
|
|
char namestr[DNS_NAME_FORMATSIZE];
|
|
|
|
char typestr[DNS_RDATATYPE_FORMATSIZE];
|
|
|
|
|
1999-11-19 19:07:16 +00:00
|
|
|
/*
|
|
|
|
* The (name == zonename) condition appears in
|
|
|
|
* RFC2136 3.4.2.4 but is missing from the pseudocode.
|
|
|
|
*/
|
|
|
|
if (dns_name_equal(name, zonename)) {
|
|
|
|
if (rdata.type == dns_rdatatype_soa) {
|
2001-01-18 23:31:03 +00:00
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
|
|
|
"attempt to delete SOA "
|
|
|
|
"ignored");
|
1999-11-19 19:07:16 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (rdata.type == dns_rdatatype_ns) {
|
|
|
|
int count;
|
2025-08-11 07:17:43 +02:00
|
|
|
CHECK(rr_count(
|
|
|
|
db, ver, name, dns_rdatatype_ns,
|
|
|
|
dns_rdatatype_none, &count));
|
1999-11-19 19:07:16 +00:00
|
|
|
if (count == 1) {
|
2001-01-18 23:31:03 +00:00
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
2024-11-14 12:34:11 +11:00
|
|
|
"attempt to delete "
|
|
|
|
"last NS ignored");
|
1999-11-19 19:07:16 +00:00
|
|
|
continue;
|
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2022-10-05 13:36:42 +02:00
|
|
|
/*
|
|
|
|
* Don't remove DNSKEY, CDNSKEY, CDS records
|
|
|
|
* that are in use (under our control).
|
|
|
|
*/
|
2023-03-15 11:51:33 +01:00
|
|
|
if (dns_rdatatype_iskeymaterial(rdata.type)) {
|
2022-10-05 13:36:42 +02:00
|
|
|
isc_result_t r;
|
|
|
|
bool inuse = false;
|
|
|
|
r = dns_zone_dnskey_inuse(zone, &rdata,
|
|
|
|
&inuse);
|
|
|
|
if (r != ISC_R_SUCCESS) {
|
|
|
|
FAIL(r);
|
|
|
|
}
|
|
|
|
if (inuse) {
|
2024-01-05 11:08:33 +11:00
|
|
|
char typebuf
|
|
|
|
[DNS_RDATATYPE_FORMATSIZE];
|
|
|
|
|
|
|
|
dns_rdatatype_format(
|
|
|
|
rdata.type, typebuf,
|
|
|
|
sizeof(typebuf));
|
2022-10-05 13:36:42 +02:00
|
|
|
update_log(client, zone,
|
|
|
|
LOGLEVEL_PROTOCOL,
|
2024-11-14 12:34:11 +11:00
|
|
|
"attempt to delete "
|
|
|
|
"in use %s ignored",
|
2024-01-05 11:08:33 +11:00
|
|
|
typebuf);
|
2022-10-05 13:36:42 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2009-08-17 07:18:41 +00:00
|
|
|
dns_name_format(name, namestr, sizeof(namestr));
|
|
|
|
dns_rdatatype_format(rdata.type, typestr,
|
|
|
|
sizeof(typestr));
|
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
|
|
|
"deleting an RR at %s %s", namestr, typestr);
|
|
|
|
CHECK(delete_if(rr_equal_p, db, ver, name, rdata.type,
|
|
|
|
covers, &rdata, &diff));
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2025-03-19 20:29:17 +01:00
|
|
|
|
|
|
|
++update;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2008-09-24 02:46:23 +00:00
|
|
|
/*
|
|
|
|
* Check that any changes to DNSKEY/NSEC3PARAM records make sense.
|
|
|
|
* If they don't then back out all changes to DNSKEY/NSEC3PARAM
|
|
|
|
* records.
|
|
|
|
*/
|
|
|
|
if (!ISC_LIST_EMPTY(diff.tuples)) {
|
|
|
|
CHECK(check_dnssec(client, zone, db, ver, &diff));
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2009-12-04 03:33:15 +00:00
|
|
|
if (!ISC_LIST_EMPTY(diff.tuples)) {
|
|
|
|
unsigned int errors = 0;
|
|
|
|
CHECK(dns_zone_nscheck(zone, db, ver, &errors));
|
|
|
|
if (errors != 0) {
|
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
|
|
|
"update rejected: post update name server "
|
|
|
|
"sanity check failed");
|
|
|
|
result = DNS_R_REFUSED;
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
}
|
2022-10-12 17:01:57 +11:00
|
|
|
if (!ISC_LIST_EMPTY(diff.tuples) && is_signing) {
|
2015-05-27 15:25:45 +10:00
|
|
|
result = dns_zone_cdscheck(zone, db, ver);
|
|
|
|
if (result == DNS_R_BADCDS || result == DNS_R_BADCDNSKEY) {
|
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
|
|
|
"update rejected: bad %s RRset",
|
|
|
|
result == DNS_R_BADCDS ? "CDS" : "CDNSKEY");
|
|
|
|
result = DNS_R_REFUSED;
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
goto failure;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2015-05-27 15:25:45 +10:00
|
|
|
}
|
2009-12-04 03:33:15 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/*
|
1999-09-09 08:58:00 +00:00
|
|
|
* If any changes were made, increment the SOA serial number,
|
2003-09-30 06:00:40 +00:00
|
|
|
* update RRSIGs and NSECs (if zone is secure), and write the update
|
1999-09-09 08:58:00 +00:00
|
|
|
* to the journal.
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
|
|
|
if (!ISC_LIST_EMPTY(diff.tuples)) {
|
2001-02-22 19:15:01 +00:00
|
|
|
char *journalfile;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_journal_t *journal;
|
2018-04-17 08:29:14 -07:00
|
|
|
bool has_dnskey;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/*
|
1999-09-09 08:58:00 +00:00
|
|
|
* Increment the SOA serial, but only if it was not
|
|
|
|
* changed as a result of an update operation.
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
|
|
|
if (!soa_serial_changed) {
|
2011-07-01 02:25:48 +00:00
|
|
|
CHECK(update_soa_serial(
|
|
|
|
db, ver, &diff, mctx,
|
|
|
|
dns_zone_getserialupdatemethod(zone)));
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
2005-05-19 04:59:05 +00:00
|
|
|
CHECK(check_mx(client, zone, db, ver, &diff));
|
|
|
|
|
2002-11-12 21:52:44 +00:00
|
|
|
CHECK(remove_orphaned_ds(db, ver, &diff));
|
|
|
|
|
2025-08-11 07:17:43 +02:00
|
|
|
CHECK(rrset_exists(db, ver, zonename, dns_rdatatype_dnskey,
|
|
|
|
dns_rdatatype_none, &has_dnskey));
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2014-06-10 09:17:15 +10:00
|
|
|
CHECK(rrset_exists(db, oldver, zonename, dns_rdatatype_dnskey,
|
2025-08-11 07:17:43 +02:00
|
|
|
dns_rdatatype_none, &had_dnskey));
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2009-10-08 23:13:07 +00:00
|
|
|
CHECK(rollback_private(db, privatetype, ver, &diff));
|
2008-04-01 01:37:25 +00:00
|
|
|
|
2009-10-08 23:13:07 +00:00
|
|
|
CHECK(add_nsec3param_records(client, zone, db, ver, &diff));
|
2008-04-01 01:37:25 +00:00
|
|
|
|
2022-10-12 17:01:57 +11:00
|
|
|
if (is_signing && had_dnskey && !has_dnskey) {
|
2008-09-24 02:46:23 +00:00
|
|
|
/*
|
|
|
|
* We are transitioning from secure to insecure.
|
|
|
|
* Cause all NSEC3 chains to be deleted. When the
|
|
|
|
* the last signature for the DNSKEY records are
|
|
|
|
* remove any NSEC chain present will also be removed.
|
|
|
|
*/
|
2010-05-18 01:39:41 +00:00
|
|
|
CHECK(dns_nsec3param_deletechains(db, ver, zone, true,
|
2018-04-17 08:29:14 -07:00
|
|
|
&diff));
|
2009-10-08 23:13:07 +00:00
|
|
|
} else if (has_dnskey && isdnssec(db, ver, privatetype)) {
|
2011-08-30 05:16:15 +00:00
|
|
|
dns_update_log_t log;
|
2019-10-09 17:38:44 +02:00
|
|
|
uint32_t interval =
|
|
|
|
dns_zone_getsigvalidityinterval(zone);
|
2011-08-30 05:16:15 +00:00
|
|
|
|
|
|
|
log.func = update_log_cb;
|
|
|
|
log.arg = client;
|
|
|
|
result = dns_update_signatures(&log, zone, db, oldver,
|
2019-10-09 17:38:44 +02:00
|
|
|
ver, &diff, interval);
|
2011-08-30 05:16:15 +00:00
|
|
|
|
2000-04-06 22:03:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2001-01-18 23:31:03 +00:00
|
|
|
update_log(client, zone, ISC_LOG_ERROR,
|
2008-09-24 02:46:23 +00:00
|
|
|
"RRSIG/NSEC/NSEC3 update failed: %s",
|
2001-01-18 23:31:03 +00:00
|
|
|
isc_result_totext(result));
|
1999-10-25 20:23:13 +00:00
|
|
|
goto failure;
|
|
|
|
}
|
1999-09-09 08:58:00 +00:00
|
|
|
}
|
|
|
|
|
2016-11-02 17:31:27 +11:00
|
|
|
maxrecords = dns_zone_getmaxrecords(zone);
|
|
|
|
if (maxrecords != 0U) {
|
|
|
|
result = dns_db_getsize(db, ver, &records, NULL);
|
|
|
|
if (result == ISC_R_SUCCESS && records > maxrecords) {
|
|
|
|
update_log(client, zone, ISC_LOG_ERROR,
|
2024-11-14 12:34:11 +11:00
|
|
|
"records in zone (%" PRIu64
|
|
|
|
") exceeds max-records (%u)",
|
2016-11-02 17:31:27 +11:00
|
|
|
records, maxrecords);
|
|
|
|
result = DNS_R_TOOMANYRECORDS;
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-02-22 19:15:01 +00:00
|
|
|
journalfile = dns_zone_getjournal(zone);
|
|
|
|
if (journalfile != NULL) {
|
|
|
|
update_log(client, zone, LOGLEVEL_DEBUG,
|
|
|
|
"writing journal %s", journalfile);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2001-02-22 19:15:01 +00:00
|
|
|
journal = NULL;
|
|
|
|
result = dns_journal_open(mctx, journalfile,
|
2011-12-22 07:32:41 +00:00
|
|
|
DNS_JOURNAL_CREATE, &journal);
|
2001-02-22 19:15:01 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
FAILS(result, "journal open failed");
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2001-02-22 19:15:01 +00:00
|
|
|
|
|
|
|
result = dns_journal_write_transaction(journal, &diff);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
dns_journal_destroy(&journal);
|
|
|
|
FAILS(result, "journal write failed");
|
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
dns_journal_destroy(&journal);
|
|
|
|
}
|
|
|
|
|
2004-07-20 07:39:31 +00:00
|
|
|
/*
|
|
|
|
* XXXRTH Just a note that this committing code will have
|
|
|
|
* to change to handle databases that need two-phase
|
|
|
|
* commit, but this isn't a priority.
|
|
|
|
*/
|
|
|
|
update_log(client, zone, LOGLEVEL_DEBUG,
|
|
|
|
"committing update transaction");
|
2008-04-01 01:37:25 +00:00
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
dns_db_closeversion(db, &ver, true);
|
2000-05-22 16:01:58 +00:00
|
|
|
|
2004-07-20 07:39:31 +00:00
|
|
|
/*
|
|
|
|
* Mark the zone as dirty so that it will be written to disk.
|
|
|
|
*/
|
|
|
|
dns_zone_markdirty(zone);
|
2000-05-22 16:01:58 +00:00
|
|
|
|
2004-07-20 07:39:31 +00:00
|
|
|
/*
|
2021-10-05 11:07:44 +02:00
|
|
|
* Notify secondaries of the change we just made.
|
2004-07-20 07:39:31 +00:00
|
|
|
*/
|
2025-04-22 13:33:48 +00:00
|
|
|
dns_zone_notify(zone, false);
|
2004-07-20 07:39:31 +00:00
|
|
|
} else {
|
|
|
|
update_log(client, zone, LOGLEVEL_DEBUG, "redundant request");
|
2018-04-17 08:29:14 -07:00
|
|
|
dns_db_closeversion(db, &ver, true);
|
2004-07-20 07:39:31 +00:00
|
|
|
}
|
2000-04-06 22:03:35 +00:00
|
|
|
result = ISC_R_SUCCESS;
|
1999-08-20 05:35:16 +00:00
|
|
|
goto common;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
failure:
|
2000-05-24 03:26:35 +00:00
|
|
|
/*
|
|
|
|
* The reason for failure should have been logged at this point.
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
if (ver != NULL) {
|
2008-01-18 23:46:58 +00:00
|
|
|
update_log(client, zone, LOGLEVEL_DEBUG, "rolling back");
|
2018-04-17 08:29:14 -07:00
|
|
|
dns_db_closeversion(db, &ver, false);
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
common:
|
|
|
|
dns_diff_clear(&temp);
|
|
|
|
dns_diff_clear(&diff);
|
|
|
|
|
1999-09-09 08:58:00 +00:00
|
|
|
if (oldver != NULL) {
|
2018-04-17 08:29:14 -07:00
|
|
|
dns_db_closeversion(db, &oldver, false);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
if (db != NULL) {
|
|
|
|
dns_db_detach(&db);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-10-14 01:37:00 +00:00
|
|
|
|
2024-11-20 01:20:42 +11:00
|
|
|
if (maxbytype != NULL) {
|
|
|
|
isc_mem_cput(mctx, maxbytype, maxbytypelen, sizeof(*maxbytype));
|
2019-07-03 17:03:13 +10:00
|
|
|
}
|
|
|
|
|
2000-10-13 23:21:17 +00:00
|
|
|
if (ssutable != NULL) {
|
|
|
|
dns_ssutable_detach(&ssutable);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-10-13 23:21:17 +00:00
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
uev->result = result;
|
2008-04-03 05:55:52 +00:00
|
|
|
if (zone != NULL) {
|
|
|
|
INSIST(uev->zone == zone); /* we use this later */
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2014-10-03 07:50:09 +10:00
|
|
|
|
2022-10-26 23:35:47 -07:00
|
|
|
isc_async_run(client->manager->loop, updatedone_action, uev);
|
2014-10-03 07:50:09 +10:00
|
|
|
INSIST(ver == NULL);
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
static void
|
2022-10-26 23:35:47 -07:00
|
|
|
updatedone_action(void *arg) {
|
|
|
|
update_t *uev = (update_t *)arg;
|
|
|
|
ns_client_t *client = uev->client;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2025-05-14 15:32:53 +02:00
|
|
|
REQUIRE(client->inner.updatehandle == client->inner.handle);
|
1999-10-14 00:03:04 +00:00
|
|
|
|
2008-04-03 05:55:52 +00:00
|
|
|
switch (uev->result) {
|
|
|
|
case ISC_R_SUCCESS:
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, uev->zone, ns_statscounter_updatedone);
|
2008-04-03 05:55:52 +00:00
|
|
|
break;
|
|
|
|
case DNS_R_REFUSED:
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, uev->zone, ns_statscounter_updaterej);
|
2008-04-03 05:55:52 +00:00
|
|
|
break;
|
|
|
|
default:
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, uev->zone, ns_statscounter_updatefail);
|
2008-04-03 05:55:52 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-09-03 13:31:27 -07:00
|
|
|
|
1999-10-14 00:03:04 +00:00
|
|
|
respond(client, uev->result);
|
2020-09-03 13:31:27 -07:00
|
|
|
|
2023-04-11 07:54:58 +02:00
|
|
|
isc_quota_release(&client->manager->sctx->updquota);
|
2022-10-26 23:35:47 -07:00
|
|
|
if (uev->zone != NULL) {
|
|
|
|
dns_zone_detach(&uev->zone);
|
|
|
|
}
|
|
|
|
isc_mem_put(client->manager->mctx, uev, sizeof(*uev));
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_nmhandle_detach(&client->inner.updatehandle);
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2000-09-13 01:30:34 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-09-13 01:30:34 +00:00
|
|
|
* Update forwarding support.
|
|
|
|
*/
|
|
|
|
static void
|
2022-10-26 23:35:47 -07:00
|
|
|
forward_fail(void *arg) {
|
|
|
|
update_t *uev = (update_t *)arg;
|
|
|
|
ns_client_t *client = uev->client;
|
2000-12-28 01:29:09 +00:00
|
|
|
|
2000-09-13 01:30:34 +00:00
|
|
|
respond(client, DNS_R_SERVFAIL);
|
2022-09-01 16:05:04 -07:00
|
|
|
|
2023-04-11 07:54:58 +02:00
|
|
|
isc_quota_release(&client->manager->sctx->updquota);
|
2022-10-26 23:35:47 -07:00
|
|
|
isc_mem_put(client->manager->mctx, uev, sizeof(*uev));
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_nmhandle_detach(&client->inner.updatehandle);
|
2000-09-13 01:30:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
forward_callback(void *arg, isc_result_t result, dns_message_t *answer) {
|
2022-10-26 23:35:47 -07:00
|
|
|
update_t *uev = (update_t *)arg;
|
|
|
|
ns_client_t *client = uev->client;
|
2008-04-03 05:55:52 +00:00
|
|
|
dns_zone_t *zone = uev->zone;
|
2000-09-13 01:30:34 +00:00
|
|
|
|
2000-12-28 01:29:09 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
INSIST(answer == NULL);
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, zone, ns_statscounter_updatefwdfail);
|
2022-10-26 23:35:47 -07:00
|
|
|
isc_async_run(client->manager->loop, forward_fail, uev);
|
2000-12-28 01:29:09 +00:00
|
|
|
} else {
|
|
|
|
uev->answer = answer;
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, zone, ns_statscounter_updaterespfwd);
|
2022-10-26 23:35:47 -07:00
|
|
|
isc_async_run(client->manager->loop, forward_done, uev);
|
2000-12-28 01:29:09 +00:00
|
|
|
}
|
2020-09-03 13:31:27 -07:00
|
|
|
|
2008-04-03 05:55:52 +00:00
|
|
|
dns_zone_detach(&zone);
|
2000-12-28 01:29:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2022-10-26 23:35:47 -07:00
|
|
|
forward_done(void *arg) {
|
|
|
|
update_t *uev = (update_t *)arg;
|
|
|
|
ns_client_t *client = uev->client;
|
2000-12-28 01:29:09 +00:00
|
|
|
|
|
|
|
ns_client_sendraw(client, uev->answer);
|
2020-09-21 16:16:15 -03:00
|
|
|
dns_message_detach(&uev->answer);
|
2022-09-01 16:05:04 -07:00
|
|
|
|
2023-04-11 07:54:58 +02:00
|
|
|
isc_quota_release(&client->manager->sctx->updquota);
|
2022-10-26 23:35:47 -07:00
|
|
|
isc_mem_put(client->manager->mctx, uev, sizeof(*uev));
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_nmhandle_detach(&client->inner.reqhandle);
|
|
|
|
isc_nmhandle_detach(&client->inner.updatehandle);
|
2000-09-13 01:30:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2022-10-26 23:35:47 -07:00
|
|
|
forward_action(void *arg) {
|
|
|
|
update_t *uev = (update_t *)arg;
|
2000-09-13 01:30:34 +00:00
|
|
|
dns_zone_t *zone = uev->zone;
|
2022-10-26 23:35:47 -07:00
|
|
|
ns_client_t *client = uev->client;
|
2000-09-13 01:30:34 +00:00
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
result = dns_zone_forwardupdate(zone, client->message, forward_callback,
|
2022-10-26 23:35:47 -07:00
|
|
|
uev);
|
2000-09-13 01:30:34 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2022-10-26 23:35:47 -07:00
|
|
|
isc_async_run(client->manager->loop, forward_fail, uev);
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, zone, ns_statscounter_updatefwdfail);
|
2008-04-03 05:55:52 +00:00
|
|
|
dns_zone_detach(&zone);
|
2019-11-05 15:34:35 -08:00
|
|
|
} else {
|
2017-09-08 13:39:09 -07:00
|
|
|
inc_stats(client, zone, ns_statscounter_updatereqfwd);
|
2019-11-05 15:34:35 -08:00
|
|
|
}
|
2000-09-13 01:30:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
2022-10-26 23:35:47 -07:00
|
|
|
send_forward(ns_client_t *client, dns_zone_t *zone) {
|
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
2013-05-03 14:05:32 -07:00
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char classbuf[DNS_RDATACLASS_FORMATSIZE];
|
2022-10-26 23:35:47 -07:00
|
|
|
update_t *uev = NULL;
|
2011-08-31 06:49:10 +00:00
|
|
|
|
2022-11-08 17:32:41 -08:00
|
|
|
result = checkupdateacl(client, dns_zone_getforwardacl(zone),
|
|
|
|
"update forwarding", dns_zone_getorigin(zone),
|
|
|
|
true, false);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-04-11 07:54:58 +02:00
|
|
|
result = isc_quota_acquire(&client->manager->sctx->updquota);
|
2022-09-01 16:05:04 -07:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2023-04-11 07:54:58 +02:00
|
|
|
if (result == ISC_R_SOFTQUOTA) {
|
|
|
|
isc_quota_release(&client->manager->sctx->updquota);
|
|
|
|
}
|
2022-09-01 16:05:04 -07:00
|
|
|
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
|
|
|
"update failed: too many DNS UPDATEs queued (%s)",
|
|
|
|
isc_result_totext(result));
|
|
|
|
ns_stats_increment(client->manager->sctx->nsstats,
|
|
|
|
ns_statscounter_updatequota);
|
|
|
|
return DNS_R_DROP;
|
|
|
|
}
|
|
|
|
|
2022-10-26 23:35:47 -07:00
|
|
|
uev = isc_mem_get(client->manager->mctx, sizeof(*uev));
|
|
|
|
*uev = (update_t){
|
|
|
|
.zone = zone,
|
|
|
|
.client = client,
|
|
|
|
.result = ISC_R_SUCCESS,
|
|
|
|
};
|
2000-09-13 01:30:34 +00:00
|
|
|
|
2013-05-03 14:05:32 -07:00
|
|
|
dns_name_format(dns_zone_getorigin(zone), namebuf, sizeof(namebuf));
|
|
|
|
dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
|
|
|
|
sizeof(classbuf));
|
|
|
|
|
|
|
|
ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
|
|
|
|
LOGLEVEL_PROTOCOL, "forwarding update for zone '%s/%s'",
|
|
|
|
namebuf, classbuf);
|
|
|
|
|
2025-05-14 15:32:53 +02:00
|
|
|
isc_nmhandle_attach(client->inner.handle, &client->inner.updatehandle);
|
2022-10-26 23:35:47 -07:00
|
|
|
isc_async_run(dns_zone_getloop(zone), forward_action, uev);
|
2000-09-13 01:30:34 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|