Home | History | Annotate | Line # | Download | only in ns
      1 /*	$NetBSD: update.c,v 1.22 2026/08/29 14:55:20 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #include <inttypes.h>
     17 #include <stdbool.h>
     18 
     19 #include <isc/async.h>
     20 #include <isc/netaddr.h>
     21 #include <isc/serial.h>
     22 #include <isc/stats.h>
     23 #include <isc/string.h>
     24 #include <isc/util.h>
     25 
     26 #include <dns/db.h>
     27 #include <dns/dbiterator.h>
     28 #include <dns/diff.h>
     29 #include <dns/dnssec.h>
     30 #include <dns/fixedname.h>
     31 #include <dns/journal.h>
     32 #include <dns/keyvalues.h>
     33 #include <dns/message.h>
     34 #include <dns/nsec.h>
     35 #include <dns/nsec3.h>
     36 #include <dns/private.h>
     37 #include <dns/rdataclass.h>
     38 #include <dns/rdataset.h>
     39 #include <dns/rdatasetiter.h>
     40 #include <dns/rdatastruct.h>
     41 #include <dns/rdatatype.h>
     42 #include <dns/result.h>
     43 #include <dns/soa.h>
     44 #include <dns/ssu.h>
     45 #include <dns/tsig.h>
     46 #include <dns/update.h>
     47 #include <dns/view.h>
     48 #include <dns/zone.h>
     49 #include <dns/zt.h>
     50 
     51 #include <ns/client.h>
     52 #include <ns/interfacemgr.h>
     53 #include <ns/log.h>
     54 #include <ns/server.h>
     55 #include <ns/stats.h>
     56 #include <ns/update.h>
     57 
     58 /*! \file
     59  * \brief
     60  * This module implements dynamic update as in RFC2136.
     61  */
     62 
     63 /*
     64  *  XXX TODO:
     65  * - document strict minimality
     66  */
     67 
     68 /**************************************************************************/
     69 
     70 /*%
     71  * Log level for tracing dynamic update protocol requests.
     72  */
     73 #define LOGLEVEL_PROTOCOL ISC_LOG_INFO
     74 
     75 /*%
     76  * Log level for low-level debug tracing.
     77  */
     78 #define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8)
     79 
     80 /*%
     81  * Fail unconditionally and log as a client error.
     82  * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
     83  * from complaining about "end-of-loop code not reached".
     84  */
     85 #define FAILC(code, msg)                                     \
     86 	do {                                                 \
     87 		const char *_what = "failed";                \
     88 		result = (code);                             \
     89 		switch (result) {                            \
     90 		case DNS_R_NXDOMAIN:                         \
     91 		case DNS_R_YXDOMAIN:                         \
     92 		case DNS_R_YXRRSET:                          \
     93 		case DNS_R_NXRRSET:                          \
     94 			_what = "unsuccessful";              \
     95 		default:                                     \
     96 			break;                               \
     97 		}                                            \
     98 		update_log(client, zone, LOGLEVEL_PROTOCOL,  \
     99 			   "update %s: %s (%s)", _what, msg, \
    100 			   isc_result_totext(result));       \
    101 		if (result != ISC_R_SUCCESS)                 \
    102 			goto cleanup;                        \
    103 	} while (0)
    104 #define PREREQFAILC(code, msg)                                            \
    105 	do {                                                              \
    106 		inc_stats(client, zone, ns_statscounter_updatebadprereq); \
    107 		FAILC(code, msg);                                         \
    108 	} while (0)
    109 
    110 #define FAILN(code, name, msg)                                             \
    111 	do {                                                               \
    112 		const char *_what = "failed";                              \
    113 		result = (code);                                           \
    114 		switch (result) {                                          \
    115 		case DNS_R_NXDOMAIN:                                       \
    116 		case DNS_R_YXDOMAIN:                                       \
    117 		case DNS_R_YXRRSET:                                        \
    118 		case DNS_R_NXRRSET:                                        \
    119 			_what = "unsuccessful";                            \
    120 		default:                                                   \
    121 			break;                                             \
    122 		}                                                          \
    123 		if (isc_log_wouldlog(ns_lctx, LOGLEVEL_PROTOCOL)) {        \
    124 			char _nbuf[DNS_NAME_FORMATSIZE];                   \
    125 			dns_name_format(name, _nbuf, sizeof(_nbuf));       \
    126 			update_log(client, zone, LOGLEVEL_PROTOCOL,        \
    127 				   "update %s: %s: %s (%s)", _what, _nbuf, \
    128 				   msg, isc_result_totext(result));        \
    129 		}                                                          \
    130 		if (result != ISC_R_SUCCESS)                               \
    131 			goto cleanup;                                      \
    132 	} while (0)
    133 #define PREREQFAILN(code, name, msg)                                      \
    134 	do {                                                              \
    135 		inc_stats(client, zone, ns_statscounter_updatebadprereq); \
    136 		FAILN(code, name, msg);                                   \
    137 	} while (0)
    138 
    139 #define FAILNT(code, name, type, msg)                                         \
    140 	do {                                                                  \
    141 		const char *_what = "failed";                                 \
    142 		result = (code);                                              \
    143 		switch (result) {                                             \
    144 		case DNS_R_NXDOMAIN:                                          \
    145 		case DNS_R_YXDOMAIN:                                          \
    146 		case DNS_R_YXRRSET:                                           \
    147 		case DNS_R_NXRRSET:                                           \
    148 			_what = "unsuccessful";                               \
    149 		default:                                                      \
    150 			break;                                                \
    151 		}                                                             \
    152 		if (isc_log_wouldlog(ns_lctx, LOGLEVEL_PROTOCOL)) {           \
    153 			char _nbuf[DNS_NAME_FORMATSIZE];                      \
    154 			char _tbuf[DNS_RDATATYPE_FORMATSIZE];                 \
    155 			dns_name_format(name, _nbuf, sizeof(_nbuf));          \
    156 			dns_rdatatype_format(type, _tbuf, sizeof(_tbuf));     \
    157 			update_log(client, zone, LOGLEVEL_PROTOCOL,           \
    158 				   "update %s: %s/%s: %s (%s)", _what, _nbuf, \
    159 				   _tbuf, msg, isc_result_totext(result));    \
    160 		}                                                             \
    161 		if (result != ISC_R_SUCCESS)                                  \
    162 			goto cleanup;                                         \
    163 	} while (0)
    164 #define PREREQFAILNT(code, name, type, msg)                               \
    165 	do {                                                              \
    166 		inc_stats(client, zone, ns_statscounter_updatebadprereq); \
    167 		FAILNT(code, name, type, msg);                            \
    168 	} while (0)
    169 
    170 /*%
    171  * Fail unconditionally and log as a server error.
    172  * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
    173  * from complaining about "end-of-loop code not reached".
    174  */
    175 #define FAILS(code, msg)                                                     \
    176 	do {                                                                 \
    177 		result = (code);                                             \
    178 		update_log(client, zone, LOGLEVEL_PROTOCOL, "error: %s: %s", \
    179 			   msg, isc_result_totext(result));                  \
    180 		if (result != ISC_R_SUCCESS)                                 \
    181 			goto cleanup;                                        \
    182 	} while (0)
    183 
    184 /*
    185  * Return TRUE if NS_CLIENTATTR_TCP is set in the attributes other FALSE.
    186  */
    187 #define TCPCLIENT(client) (((client)->attributes & NS_CLIENTATTR_TCP) != 0)
    188 
    189 /**************************************************************************/
    190 
    191 typedef struct rr rr_t;
    192 
    193 struct rr {
    194 	/* dns_name_t name; */
    195 	uint32_t ttl;
    196 	dns_rdata_t rdata;
    197 };
    198 
    199 typedef struct update update_t;
    200 
    201 struct update {
    202 	dns_zone_t *zone;
    203 	ns_client_t *client;
    204 	isc_result_t result;
    205 	dns_message_t *answer;
    206 	dns_ssutable_t *ssutable;
    207 	unsigned int *maxbytype;
    208 	size_t maxbytypelen;
    209 };
    210 
    211 /*%
    212  * Prepare an RR for the addition of the new RR 'ctx->update_rr',
    213  * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting
    214  * the RRs if it is replaced by the new RR or has a conflicting TTL.
    215  * The necessary changes are appended to ctx->del_diff and ctx->add_diff;
    216  * we need to do all deletions before any additions so that we don't run
    217  * into transient states with conflicting TTLs.
    218  */
    219 
    220 typedef struct {
    221 	dns_zone_t *zone;
    222 	dns_db_t *db;
    223 	dns_dbversion_t *ver;
    224 	dns_diff_t *diff;
    225 	dns_name_t *name;
    226 	dns_name_t *oldname;
    227 	dns_rdata_t *update_rr;
    228 	dns_ttl_t update_rr_ttl;
    229 	bool ignore_add;
    230 	dns_diff_t del_diff;
    231 	dns_diff_t add_diff;
    232 } add_rr_prepare_ctx_t;
    233 
    234 /**************************************************************************/
    235 /*
    236  * Forward declarations.
    237  */
    238 
    239 static void
    240 update_action(void *arg);
    241 static void
    242 updatedone_action(void *arg);
    243 static isc_result_t
    244 send_forward(ns_client_t *client, dns_zone_t *zone);
    245 static void
    246 forward_done(void *arg);
    247 static isc_result_t
    248 add_rr_prepare_action(void *data, rr_t *rr);
    249 static isc_result_t
    250 rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
    251 	  const dns_rdata_t *rdata, bool *flag);
    252 
    253 /**************************************************************************/
    254 
    255 static void
    256 update_log(ns_client_t *client, dns_zone_t *zone, int level, const char *fmt,
    257 	   ...) ISC_FORMAT_PRINTF(4, 5);
    258 
    259 static void
    260 update_log(ns_client_t *client, dns_zone_t *zone, int level, const char *fmt,
    261 	   ...) {
    262 	va_list ap;
    263 	char message[4096];
    264 	char namebuf[DNS_NAME_FORMATSIZE];
    265 	char classbuf[DNS_RDATACLASS_FORMATSIZE];
    266 
    267 	if (client == NULL) {
    268 		return;
    269 	}
    270 
    271 	if (!isc_log_wouldlog(ns_lctx, level)) {
    272 		return;
    273 	}
    274 
    275 	va_start(ap, fmt);
    276 	vsnprintf(message, sizeof(message), fmt, ap);
    277 	va_end(ap);
    278 
    279 	if (zone != NULL) {
    280 		dns_name_format(dns_zone_getorigin(zone), namebuf,
    281 				sizeof(namebuf));
    282 		dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
    283 				      sizeof(classbuf));
    284 
    285 		ns_client_log(client, NS_LOGCATEGORY_UPDATE,
    286 			      NS_LOGMODULE_UPDATE, level,
    287 			      "updating zone '%s/%s': %s", namebuf, classbuf,
    288 			      message);
    289 	} else {
    290 		ns_client_log(client, NS_LOGCATEGORY_UPDATE,
    291 			      NS_LOGMODULE_UPDATE, level, "%s", message);
    292 	}
    293 }
    294 
    295 static void
    296 update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) {
    297 	update_log(arg, zone, level, "%s", message);
    298 }
    299 
    300 /*%
    301  * Increment updated-related statistics counters.
    302  */
    303 static void
    304 inc_stats(ns_client_t *client, dns_zone_t *zone, isc_statscounter_t counter) {
    305 	ns_stats_increment(client->manager->sctx->nsstats, counter);
    306 
    307 	if (zone != NULL) {
    308 		isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
    309 		if (zonestats != NULL) {
    310 			isc_stats_increment(zonestats, counter);
    311 		}
    312 	}
    313 }
    314 
    315 /*%
    316  * Check if we could have queried for the contents of this zone or
    317  * if the zone is potentially updateable.
    318  * If the zone can potentially be updated and the check failed then
    319  * log a error otherwise we log a informational message.
    320  */
    321 static isc_result_t
    322 checkqueryacl(ns_client_t *client, dns_acl_t *queryacl, dns_name_t *zonename,
    323 	      dns_acl_t *updateacl, dns_ssutable_t *ssutable) {
    324 	isc_result_t result;
    325 	char namebuf[DNS_NAME_FORMATSIZE];
    326 	char classbuf[DNS_RDATACLASS_FORMATSIZE];
    327 	bool update_possible =
    328 		((updateacl != NULL && !dns_acl_isnone(updateacl)) ||
    329 		 ssutable != NULL);
    330 
    331 	result = ns_client_checkaclsilent(client, NULL, queryacl, true);
    332 	if (result != ISC_R_SUCCESS) {
    333 		int level = update_possible ? ISC_LOG_ERROR : ISC_LOG_INFO;
    334 
    335 		dns_name_format(zonename, namebuf, sizeof(namebuf));
    336 		dns_rdataclass_format(client->view->rdclass, classbuf,
    337 				      sizeof(classbuf));
    338 
    339 		ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
    340 			      NS_LOGMODULE_UPDATE, level,
    341 			      "update '%s/%s' denied due to allow-query",
    342 			      namebuf, classbuf);
    343 	} else if (!update_possible) {
    344 		dns_name_format(zonename, namebuf, sizeof(namebuf));
    345 		dns_rdataclass_format(client->view->rdclass, classbuf,
    346 				      sizeof(classbuf));
    347 
    348 		result = DNS_R_REFUSED;
    349 		ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
    350 			      NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
    351 			      "update '%s/%s' denied", namebuf, classbuf);
    352 	}
    353 	return result;
    354 }
    355 
    356 /*%
    357  * Override the default acl logging when checking whether a client
    358  * can update the zone or whether we can forward the request to the
    359  * primary server based on IP address.
    360  *
    361  * 'message' contains the type of operation that is being attempted.
    362  *
    363  * 'secondary' indicates whether this is a secondary zone.
    364  *
    365  * If the zone has no access controls configured ('acl' == NULL &&
    366  * 'has_ssutable == false`), log the attempt at info, otherwise at error.
    367  * If 'secondary' is true, log at debug=3.
    368  *
    369  * If the request was signed, log that we received it.
    370  */
    371 static isc_result_t
    372 checkupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message,
    373 	       dns_name_t *zonename, bool secondary, bool has_ssutable) {
    374 	char namebuf[DNS_NAME_FORMATSIZE];
    375 	char classbuf[DNS_RDATACLASS_FORMATSIZE];
    376 	int level = ISC_LOG_ERROR;
    377 	const char *msg = "denied";
    378 	isc_result_t result;
    379 
    380 	if (secondary && acl == NULL) {
    381 		result = DNS_R_NOTIMP;
    382 		level = ISC_LOG_DEBUG(3);
    383 		msg = "disabled";
    384 	} else {
    385 		result = ns_client_checkaclsilent(client, NULL, acl, false);
    386 		if (result == ISC_R_SUCCESS) {
    387 			level = ISC_LOG_DEBUG(3);
    388 			msg = "approved";
    389 		} else if (acl == NULL && !has_ssutable) {
    390 			level = ISC_LOG_INFO;
    391 		}
    392 	}
    393 
    394 	if (client->signer != NULL) {
    395 		dns_name_format(client->signer, namebuf, sizeof(namebuf));
    396 		ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
    397 			      NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
    398 			      "signer \"%s\" %s", namebuf, msg);
    399 	}
    400 
    401 	dns_name_format(zonename, namebuf, sizeof(namebuf));
    402 	dns_rdataclass_format(client->view->rdclass, classbuf,
    403 			      sizeof(classbuf));
    404 
    405 	ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
    406 		      NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s", message,
    407 		      namebuf, classbuf, msg);
    408 	return result;
    409 }
    410 
    411 /*%
    412  * Update a single RR in version 'ver' of 'db' and log the
    413  * update in 'diff'.
    414  *
    415  * Ensures:
    416  * \li	'*tuple' == NULL.  Either the tuple is freed, or its
    417  *	ownership has been transferred to the diff.
    418  */
    419 static isc_result_t
    420 do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
    421 	     dns_diff_t *diff) {
    422 	dns_diff_t temp_diff;
    423 	isc_result_t result;
    424 
    425 	/*
    426 	 * Create a singleton diff.
    427 	 */
    428 	dns_diff_init(diff->mctx, &temp_diff);
    429 	ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
    430 
    431 	/*
    432 	 * Apply it to the database.
    433 	 */
    434 	result = dns_diff_apply(&temp_diff, db, ver);
    435 	ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
    436 	if (result != ISC_R_SUCCESS) {
    437 		dns_difftuple_free(tuple);
    438 		return result;
    439 	}
    440 
    441 	/*
    442 	 * Merge it into the current pending journal entry.
    443 	 */
    444 	dns_diff_appendminimal(diff, tuple);
    445 
    446 	/*
    447 	 * Do not clear temp_diff.
    448 	 */
    449 	return ISC_R_SUCCESS;
    450 }
    451 
    452 /*%
    453  * Perform the updates in 'updates' in version 'ver' of 'db' and log the
    454  * update in 'diff'.
    455  *
    456  * Ensures:
    457  * \li	'updates' is empty.
    458  */
    459 static isc_result_t
    460 do_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver,
    461 	dns_diff_t *diff) {
    462 	isc_result_t result;
    463 	while (!ISC_LIST_EMPTY(updates->tuples)) {
    464 		dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples);
    465 		ISC_LIST_UNLINK(updates->tuples, t, link);
    466 		CHECK(do_one_tuple(&t, db, ver, diff));
    467 	}
    468 	return ISC_R_SUCCESS;
    469 
    470 cleanup:
    471 	dns_diff_clear(diff);
    472 	return result;
    473 }
    474 
    475 static isc_result_t
    476 update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
    477 	      dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
    478 	      dns_rdata_t *rdata) {
    479 	dns_difftuple_t *tuple = NULL;
    480 	isc_result_t result;
    481 	result = dns_difftuple_create(diff->mctx, op, name, ttl, rdata, &tuple);
    482 	if (result != ISC_R_SUCCESS) {
    483 		return result;
    484 	}
    485 	return do_one_tuple(&tuple, db, ver, diff);
    486 }
    487 
    488 /**************************************************************************/
    489 /*
    490  * Callback-style iteration over rdatasets and rdatas.
    491  *
    492  * foreach_rrset() can be used to iterate over the RRsets
    493  * of a name and call a callback function with each
    494  * one.  Similarly, foreach_rr() can be used to iterate
    495  * over the individual RRs at name, optionally restricted
    496  * to RRs of a given type.
    497  *
    498  * The callback functions are called "actions" and take
    499  * two arguments: a void pointer for passing arbitrary
    500  * context information, and a pointer to the current RRset
    501  * or RR.  By convention, their names end in "_action".
    502  */
    503 
    504 /*
    505  * XXXRTH  We might want to make this public somewhere in libdns.
    506  */
    507 
    508 /*%
    509  * Function type for foreach_rrset() iterator actions.
    510  */
    511 typedef isc_result_t
    512 rrset_func(void *data, dns_rdataset_t *rrset);
    513 
    514 /*%
    515  * Function type for foreach_rr() iterator actions.
    516  */
    517 typedef isc_result_t
    518 rr_func(void *data, rr_t *rr);
    519 
    520 /*%
    521  * Internal context struct for foreach_node_rr().
    522  */
    523 typedef struct {
    524 	rr_func *rr_action;
    525 	void *rr_action_data;
    526 } foreach_node_rr_ctx_t;
    527 
    528 /*%
    529  * Internal helper function for foreach_node_rr().
    530  */
    531 static isc_result_t
    532 foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) {
    533 	isc_result_t result;
    534 	foreach_node_rr_ctx_t *ctx = data;
    535 	for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS;
    536 	     result = dns_rdataset_next(rdataset))
    537 	{
    538 		rr_t rr = { 0, DNS_RDATA_INIT };
    539 
    540 		dns_rdataset_current(rdataset, &rr.rdata);
    541 		rr.ttl = rdataset->ttl;
    542 		result = (*ctx->rr_action)(ctx->rr_action_data, &rr);
    543 		if (result != ISC_R_SUCCESS) {
    544 			return result;
    545 		}
    546 	}
    547 	if (result != ISC_R_NOMORE) {
    548 		return result;
    549 	}
    550 	return ISC_R_SUCCESS;
    551 }
    552 
    553 /*%
    554  * For each rdataset of 'name' in 'ver' of 'db', call 'action'
    555  * with the rdataset and 'action_data' as arguments.  If the name
    556  * does not exist, do nothing.
    557  *
    558  * If 'action' returns an error, abort iteration and return the error.
    559  */
    560 static isc_result_t
    561 foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
    562 	      rrset_func *action, void *action_data) {
    563 	isc_result_t result;
    564 	dns_dbnode_t *node;
    565 	dns_rdatasetiter_t *iter;
    566 	dns_clientinfomethods_t cm;
    567 	dns_clientinfo_t ci;
    568 	dns_dbversion_t *oldver = NULL;
    569 
    570 	dns_clientinfomethods_init(&cm, ns_client_sourceip);
    571 
    572 	/*
    573 	 * Only set the clientinfo 'versionp' if the new version is
    574 	 * different from the current version
    575 	 */
    576 	dns_db_currentversion(db, &oldver);
    577 	dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
    578 	dns_db_closeversion(db, &oldver, false);
    579 
    580 	node = NULL;
    581 	result = dns_db_findnodeext(db, name, false, &cm, &ci, &node);
    582 	if (result == ISC_R_NOTFOUND) {
    583 		return ISC_R_SUCCESS;
    584 	}
    585 	if (result != ISC_R_SUCCESS) {
    586 		return result;
    587 	}
    588 
    589 	iter = NULL;
    590 	result = dns_db_allrdatasets(db, node, ver, 0, (isc_stdtime_t)0, &iter);
    591 	if (result != ISC_R_SUCCESS) {
    592 		goto cleanup_node;
    593 	}
    594 
    595 	for (result = dns_rdatasetiter_first(iter); result == ISC_R_SUCCESS;
    596 	     result = dns_rdatasetiter_next(iter))
    597 	{
    598 		dns_rdataset_t rdataset;
    599 
    600 		dns_rdataset_init(&rdataset);
    601 		dns_rdatasetiter_current(iter, &rdataset);
    602 
    603 		result = (*action)(action_data, &rdataset);
    604 
    605 		dns_rdataset_disassociate(&rdataset);
    606 		if (result != ISC_R_SUCCESS) {
    607 			goto cleanup_iterator;
    608 		}
    609 	}
    610 	if (result == ISC_R_NOMORE) {
    611 		result = ISC_R_SUCCESS;
    612 	}
    613 
    614 cleanup_iterator:
    615 	dns_rdatasetiter_destroy(&iter);
    616 
    617 cleanup_node:
    618 	dns_db_detachnode(db, &node);
    619 
    620 	return result;
    621 }
    622 
    623 /*%
    624  * For each RR of 'name' in 'ver' of 'db', call 'action'
    625  * with the RR and 'action_data' as arguments.  If the name
    626  * does not exist, do nothing.
    627  *
    628  * If 'action' returns an error, abort iteration
    629  * and return the error.
    630  */
    631 static isc_result_t
    632 foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
    633 		rr_func *rr_action, void *rr_action_data) {
    634 	foreach_node_rr_ctx_t ctx;
    635 	ctx.rr_action = rr_action;
    636 	ctx.rr_action_data = rr_action_data;
    637 	return foreach_rrset(db, ver, name, foreach_node_rr_action, &ctx);
    638 }
    639 
    640 /*%
    641  * For each of the RRs specified by 'db', 'ver', 'name', 'type',
    642  * (which can be dns_rdatatype_any to match any type), and 'covers', call
    643  * 'action' with the RR and 'action_data' as arguments. If the name
    644  * does not exist, or if no RRset of the given type exists at the name,
    645  * do nothing.
    646  *
    647  * If 'action' returns an error, abort iteration and return the error.
    648  */
    649 static isc_result_t
    650 foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
    651 	   dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action,
    652 	   void *rr_action_data) {
    653 	isc_result_t result;
    654 	dns_dbnode_t *node;
    655 	dns_rdataset_t rdataset;
    656 	dns_clientinfomethods_t cm;
    657 	dns_clientinfo_t ci;
    658 	dns_dbversion_t *oldver = NULL;
    659 	dns_fixedname_t fixed;
    660 
    661 	dns_clientinfomethods_init(&cm, ns_client_sourceip);
    662 
    663 	/*
    664 	 * Only set the clientinfo 'versionp' if the new version is
    665 	 * different from the current version
    666 	 */
    667 	dns_db_currentversion(db, &oldver);
    668 	dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
    669 	dns_db_closeversion(db, &oldver, false);
    670 
    671 	if (type == dns_rdatatype_any) {
    672 		return foreach_node_rr(db, ver, name, rr_action,
    673 				       rr_action_data);
    674 	}
    675 
    676 	node = NULL;
    677 	if (type == dns_rdatatype_nsec3 ||
    678 	    (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3))
    679 	{
    680 		result = dns_db_findnsec3node(db, name, false, &node);
    681 	} else {
    682 		result = dns_db_findnodeext(db, name, false, &cm, &ci, &node);
    683 	}
    684 	if (result == ISC_R_NOTFOUND) {
    685 		return ISC_R_SUCCESS;
    686 	}
    687 	if (result != ISC_R_SUCCESS) {
    688 		return result;
    689 	}
    690 
    691 	dns_rdataset_init(&rdataset);
    692 	result = dns_db_findrdataset(db, node, ver, type, covers,
    693 				     (isc_stdtime_t)0, &rdataset, NULL);
    694 	if (result == ISC_R_NOTFOUND) {
    695 		result = ISC_R_SUCCESS;
    696 		goto cleanup_node;
    697 	}
    698 	if (result != ISC_R_SUCCESS) {
    699 		goto cleanup_node;
    700 	}
    701 
    702 	if (rr_action == add_rr_prepare_action) {
    703 		add_rr_prepare_ctx_t *ctx = rr_action_data;
    704 
    705 		ctx->oldname = dns_fixedname_initname(&fixed);
    706 		dns_name_copy(name, ctx->oldname);
    707 		dns_rdataset_getownercase(&rdataset, ctx->oldname);
    708 	}
    709 
    710 	for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS;
    711 	     result = dns_rdataset_next(&rdataset))
    712 	{
    713 		rr_t rr = { 0, DNS_RDATA_INIT };
    714 		dns_rdataset_current(&rdataset, &rr.rdata);
    715 		rr.ttl = rdataset.ttl;
    716 		result = (*rr_action)(rr_action_data, &rr);
    717 		if (result != ISC_R_SUCCESS) {
    718 			goto cleanup_rdataset;
    719 		}
    720 	}
    721 	if (result != ISC_R_NOMORE) {
    722 		goto cleanup_rdataset;
    723 	}
    724 	result = ISC_R_SUCCESS;
    725 
    726 cleanup_rdataset:
    727 	dns_rdataset_disassociate(&rdataset);
    728 cleanup_node:
    729 	dns_db_detachnode(db, &node);
    730 
    731 	return result;
    732 }
    733 
    734 /**************************************************************************/
    735 /*
    736  * Various tests on the database contents (for prerequisites, etc).
    737  */
    738 
    739 /*%
    740  * Function type for predicate functions that compare a database RR 'db_rr'
    741  * against an update RR 'update_rr'.
    742  */
    743 typedef bool
    744 rr_predicate(dns_zone_t *zone, dns_rdata_t *update_rr, dns_rdata_t *db_rr);
    745 
    746 static isc_result_t
    747 count_action(void *data, rr_t *rr ISC_ATTR_UNUSED) {
    748 	unsigned int *ui = (unsigned int *)data;
    749 
    750 	(*ui)++;
    751 
    752 	return ISC_R_SUCCESS;
    753 }
    754 
    755 /*%
    756  * Helper function for rrset_exists().
    757  */
    758 static isc_result_t
    759 rrset_exists_action(void *data ISC_ATTR_UNUSED, rr_t *rr ISC_ATTR_UNUSED) {
    760 	return ISC_R_EXISTS;
    761 }
    762 
    763 /*%
    764  * Utility macro for RR existence checking functions.
    765  *
    766  * If the variable 'result' has the value ISC_R_EXISTS or
    767  * ISC_R_SUCCESS, set *exists to true or false,
    768  * respectively, and return success.
    769  *
    770  * If 'result' has any other value, there was a failure.
    771  * Return the failure result code and do not set *exists.
    772  *
    773  * This would be more readable as "do { if ... } while(0)",
    774  * but that form generates tons of warnings on Solaris 2.6.
    775  */
    776 #define RETURN_EXISTENCE_FLAG                                         \
    777 	return ((result == ISC_R_EXISTS)                              \
    778 			? (*exists = true, ISC_R_SUCCESS)             \
    779 			: ((result == ISC_R_SUCCESS)                  \
    780 				   ? (*exists = false, ISC_R_SUCCESS) \
    781 				   : result))
    782 
    783 /*%
    784  * Set '*exists' to true iff an rrset of the given type exists,
    785  * to false otherwise.
    786  */
    787 static isc_result_t
    788 rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
    789 	     dns_rdatatype_t type, dns_rdatatype_t covers, bool *exists) {
    790 	isc_result_t result;
    791 	result = foreach_rr(db, ver, name, type, covers, rrset_exists_action,
    792 			    NULL);
    793 	RETURN_EXISTENCE_FLAG;
    794 }
    795 
    796 /*%
    797  * Helper function for cname_incompatible_rrset_exists.
    798  */
    799 static isc_result_t
    800 cname_compatibility_action(void *data ISC_ATTR_UNUSED, dns_rdataset_t *rrset) {
    801 	if (rrset->type != dns_rdatatype_cname &&
    802 	    !dns_rdatatype_atcname(rrset->type))
    803 	{
    804 		return ISC_R_EXISTS;
    805 	}
    806 	return ISC_R_SUCCESS;
    807 }
    808 
    809 /*%
    810  * Check whether there is an rrset incompatible with adding a CNAME RR,
    811  * i.e., anything but another CNAME (which can be replaced) or a
    812  * DNSSEC RR (which can coexist).
    813  *
    814  * If such an incompatible rrset exists, set '*exists' to true.
    815  * Otherwise, set it to false.
    816  */
    817 static isc_result_t
    818 cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
    819 				dns_name_t *name, bool *exists) {
    820 	isc_result_t result;
    821 	result = foreach_rrset(db, ver, name, cname_compatibility_action, NULL);
    822 	RETURN_EXISTENCE_FLAG;
    823 }
    824 
    825 /*%
    826  * Helper function for rr_count().
    827  */
    828 static isc_result_t
    829 count_rr_action(void *data, rr_t *rr ISC_ATTR_UNUSED) {
    830 	int *countp = data;
    831 	(*countp)++;
    832 	return ISC_R_SUCCESS;
    833 }
    834 
    835 /*%
    836  * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'.
    837  */
    838 static isc_result_t
    839 rr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
    840 	 dns_rdatatype_t type, dns_rdatatype_t covers, int *countp) {
    841 	*countp = 0;
    842 	return foreach_rr(db, ver, name, type, covers, count_rr_action, countp);
    843 }
    844 
    845 /*%
    846  * Context struct and helper function for name_exists().
    847  */
    848 
    849 static isc_result_t
    850 name_exists_action(void *data ISC_ATTR_UNUSED,
    851 		   dns_rdataset_t *rrset ISC_ATTR_UNUSED) {
    852 	return ISC_R_EXISTS;
    853 }
    854 
    855 /*%
    856  * Set '*exists' to true iff the given name exists, to false otherwise.
    857  */
    858 static isc_result_t
    859 name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
    860 	    bool *exists) {
    861 	isc_result_t result;
    862 	result = foreach_rrset(db, ver, name, name_exists_action, NULL);
    863 	RETURN_EXISTENCE_FLAG;
    864 }
    865 
    866 /*
    867  *	'ssu_check_t' is used to pass the arguments to
    868  *	dns_ssutable_checkrules() to the callback function
    869  *	ssu_checkrule().
    870  */
    871 typedef struct {
    872 	/* The ownername of the record to be updated. */
    873 	dns_name_t *name;
    874 
    875 	/* The signature's name if the request was signed. */
    876 	dns_name_t *signer;
    877 
    878 	/* The address of the client. */
    879 	isc_netaddr_t *addr;
    880 
    881 	/* The ACL environment */
    882 	dns_aclenv_t *aclenv;
    883 
    884 	/* Whether the request was sent via TCP. */
    885 	bool tcp;
    886 
    887 	/* The ssu table to check against. */
    888 	dns_ssutable_t *table;
    889 
    890 	/* the key used for TKEY requests */
    891 	dst_key_t *key;
    892 } ssu_check_t;
    893 
    894 static isc_result_t
    895 ssu_checkrule(void *data, dns_rdataset_t *rrset) {
    896 	ssu_check_t *ssuinfo = data;
    897 	bool rule_ok = false;
    898 
    899 	/*
    900 	 * If we're deleting all records, it's ok to delete RRSIG and NSEC even
    901 	 * if we're normally not allowed to.
    902 	 */
    903 	if (rrset->type == dns_rdatatype_rrsig ||
    904 	    rrset->type == dns_rdatatype_nsec)
    905 	{
    906 		return ISC_R_SUCCESS;
    907 	}
    908 
    909 	/*
    910 	 * krb5-subdomain-self-rhs and ms-subdomain-self-rhs need
    911 	 * to check the PTR and SRV target names so extract them
    912 	 * from the resource records.
    913 	 */
    914 	if (rrset->rdclass == dns_rdataclass_in &&
    915 	    (rrset->type == dns_rdatatype_srv ||
    916 	     rrset->type == dns_rdatatype_ptr))
    917 	{
    918 		dns_name_t *target = NULL;
    919 		dns_rdata_ptr_t ptr;
    920 		dns_rdata_in_srv_t srv;
    921 		dns_rdataset_t rdataset;
    922 		isc_result_t result;
    923 
    924 		dns_rdataset_init(&rdataset);
    925 		dns_rdataset_clone(rrset, &rdataset);
    926 
    927 		for (result = dns_rdataset_first(&rdataset);
    928 		     result == ISC_R_SUCCESS;
    929 		     result = dns_rdataset_next(&rdataset))
    930 		{
    931 			dns_rdata_t rdata = DNS_RDATA_INIT;
    932 			dns_rdataset_current(&rdataset, &rdata);
    933 			if (rrset->type == dns_rdatatype_ptr) {
    934 				result = dns_rdata_tostruct(&rdata, &ptr, NULL);
    935 				RUNTIME_CHECK(result == ISC_R_SUCCESS);
    936 				target = &ptr.ptr;
    937 			}
    938 			if (rrset->type == dns_rdatatype_srv) {
    939 				result = dns_rdata_tostruct(&rdata, &srv, NULL);
    940 				RUNTIME_CHECK(result == ISC_R_SUCCESS);
    941 				target = &srv.target;
    942 			}
    943 			rule_ok = dns_ssutable_checkrules(
    944 				ssuinfo->table, ssuinfo->signer, ssuinfo->name,
    945 				ssuinfo->addr, ssuinfo->tcp, ssuinfo->aclenv,
    946 				rrset->type, target, ssuinfo->key, NULL);
    947 			if (!rule_ok) {
    948 				break;
    949 			}
    950 		}
    951 		if (result != ISC_R_NOMORE) {
    952 			rule_ok = false;
    953 		}
    954 		dns_rdataset_disassociate(&rdataset);
    955 	} else {
    956 		rule_ok = dns_ssutable_checkrules(
    957 			ssuinfo->table, ssuinfo->signer, ssuinfo->name,
    958 			ssuinfo->addr, ssuinfo->tcp, ssuinfo->aclenv,
    959 			rrset->type, NULL, ssuinfo->key, NULL);
    960 	}
    961 	return rule_ok ? ISC_R_SUCCESS : ISC_R_FAILURE;
    962 }
    963 
    964 static bool
    965 ssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
    966 	     dns_ssutable_t *ssutable, dns_name_t *signer, isc_netaddr_t *addr,
    967 	     dns_aclenv_t *aclenv, bool tcp, dst_key_t *key) {
    968 	isc_result_t result;
    969 	ssu_check_t ssuinfo;
    970 
    971 	ssuinfo.name = name;
    972 	ssuinfo.table = ssutable;
    973 	ssuinfo.signer = signer;
    974 	ssuinfo.addr = addr;
    975 	ssuinfo.aclenv = aclenv;
    976 	ssuinfo.tcp = tcp;
    977 	ssuinfo.key = key;
    978 	result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo);
    979 	return result == ISC_R_SUCCESS;
    980 }
    981 
    982 static isc_result_t
    983 ssu_checkrr(void *data, rr_t *rr) {
    984 	isc_result_t result;
    985 	ssu_check_t *ssuinfo = data;
    986 	dns_name_t *target = NULL;
    987 	dns_rdata_ptr_t ptr;
    988 	dns_rdata_in_srv_t srv;
    989 	bool answer;
    990 
    991 	if (rr->rdata.type == dns_rdatatype_ptr) {
    992 		result = dns_rdata_tostruct(&rr->rdata, &ptr, NULL);
    993 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    994 		target = &ptr.ptr;
    995 	}
    996 	if (rr->rdata.rdclass == dns_rdataclass_in &&
    997 	    rr->rdata.type == dns_rdatatype_srv)
    998 	{
    999 		result = dns_rdata_tostruct(&rr->rdata, &srv, NULL);
   1000 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1001 		target = &srv.target;
   1002 	}
   1003 
   1004 	answer = dns_ssutable_checkrules(
   1005 		ssuinfo->table, ssuinfo->signer, ssuinfo->name, ssuinfo->addr,
   1006 		ssuinfo->tcp, ssuinfo->aclenv, rr->rdata.type, target,
   1007 		ssuinfo->key, NULL);
   1008 	return answer ? ISC_R_SUCCESS : ISC_R_FAILURE;
   1009 }
   1010 
   1011 /**************************************************************************/
   1012 /*
   1013  * Checking of "RRset exists (value dependent)" prerequisites.
   1014  *
   1015  * In the RFC2136 section 3.2.5, this is the pseudocode involving
   1016  * a variable called "temp", a mapping of <name, type> tuples to rrsets.
   1017  *
   1018  * Here, we represent the "temp" data structure as (non-minimal) "dns_diff_t"
   1019  * where each tuple has op==DNS_DIFFOP_EXISTS.
   1020  */
   1021 
   1022 /*%
   1023  * Append a tuple asserting the existence of the RR with
   1024  * 'name' and 'rdata' to 'diff'.
   1025  */
   1026 static isc_result_t
   1027 temp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) {
   1028 	isc_result_t result;
   1029 	dns_difftuple_t *tuple = NULL;
   1030 
   1031 	REQUIRE(DNS_DIFF_VALID(diff));
   1032 	CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS, name, 0,
   1033 				   rdata, &tuple));
   1034 	ISC_LIST_APPEND(diff->tuples, tuple, link);
   1035 cleanup:
   1036 	return result;
   1037 }
   1038 
   1039 /*%
   1040  * Compare two rdatasets represented as sorted lists of tuples.
   1041  * All list elements must have the same owner name and type.
   1042  * Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset)
   1043  * if not.
   1044  */
   1045 static isc_result_t
   1046 temp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) {
   1047 	for (;;) {
   1048 		if (a == NULL || b == NULL) {
   1049 			break;
   1050 		}
   1051 		INSIST(a->op == DNS_DIFFOP_EXISTS &&
   1052 		       b->op == DNS_DIFFOP_EXISTS);
   1053 		INSIST(a->rdata.type == b->rdata.type);
   1054 		INSIST(dns_name_equal(&a->name, &b->name));
   1055 		if (dns_rdata_casecompare(&a->rdata, &b->rdata) != 0) {
   1056 			return DNS_R_NXRRSET;
   1057 		}
   1058 		a = ISC_LIST_NEXT(a, link);
   1059 		b = ISC_LIST_NEXT(b, link);
   1060 	}
   1061 	if (a != NULL || b != NULL) {
   1062 		return DNS_R_NXRRSET;
   1063 	}
   1064 	return ISC_R_SUCCESS;
   1065 }
   1066 
   1067 /*%
   1068  * A comparison function defining the sorting order for the entries
   1069  * in the "temp" data structure.  The major sort key is the owner name,
   1070  * followed by the type and rdata.
   1071  */
   1072 static int
   1073 temp_order(const void *av, const void *bv) {
   1074 	dns_difftuple_t const *const *ap = av;
   1075 	dns_difftuple_t const *const *bp = bv;
   1076 	dns_difftuple_t const *a = *ap;
   1077 	dns_difftuple_t const *b = *bp;
   1078 	int r;
   1079 	r = dns_name_compare(&a->name, &b->name);
   1080 	if (r != 0) {
   1081 		return r;
   1082 	}
   1083 	r = (b->rdata.type - a->rdata.type);
   1084 	if (r != 0) {
   1085 		return r;
   1086 	}
   1087 	r = dns_rdata_casecompare(&a->rdata, &b->rdata);
   1088 	return r;
   1089 }
   1090 
   1091 /*%
   1092  * Check the "RRset exists (value dependent)" prerequisite information
   1093  * in 'temp' against the contents of the database 'db'.
   1094  *
   1095  * Return ISC_R_SUCCESS if the prerequisites are satisfied,
   1096  * rcode(dns_rcode_nxrrset) if not.
   1097  *
   1098  * 'temp' must be pre-sorted.
   1099  */
   1100 
   1101 static isc_result_t
   1102 temp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db,
   1103 	   dns_dbversion_t *ver, dns_name_t *tmpname, dns_rdatatype_t *typep) {
   1104 	isc_result_t result;
   1105 	dns_name_t *name;
   1106 	dns_dbnode_t *node;
   1107 	dns_difftuple_t *t;
   1108 	dns_diff_t trash;
   1109 
   1110 	dns_diff_init(mctx, &trash);
   1111 
   1112 	/*
   1113 	 * For each name and type in the prerequisites,
   1114 	 * construct a sorted rdata list of the corresponding
   1115 	 * database contents, and compare the lists.
   1116 	 */
   1117 	t = ISC_LIST_HEAD(temp->tuples);
   1118 	while (t != NULL) {
   1119 		name = &t->name;
   1120 		dns_name_copy(name, tmpname);
   1121 		*typep = t->rdata.type;
   1122 
   1123 		/* A new unique name begins here. */
   1124 		node = NULL;
   1125 		result = dns_db_findnode(db, name, false, &node);
   1126 		if (result == ISC_R_NOTFOUND) {
   1127 			dns_diff_clear(&trash);
   1128 			return DNS_R_NXRRSET;
   1129 		}
   1130 		if (result != ISC_R_SUCCESS) {
   1131 			dns_diff_clear(&trash);
   1132 			return result;
   1133 		}
   1134 
   1135 		/* A new unique type begins here. */
   1136 		while (t != NULL && dns_name_equal(&t->name, name)) {
   1137 			dns_rdatatype_t type, covers;
   1138 			dns_rdataset_t rdataset;
   1139 			dns_diff_t d_rrs; /* Database RRs with
   1140 					   *    this name and type */
   1141 			dns_diff_t u_rrs; /* Update RRs with
   1142 					   *    this name and type */
   1143 
   1144 			*typep = type = t->rdata.type;
   1145 			if (type == dns_rdatatype_rrsig ||
   1146 			    type == dns_rdatatype_sig)
   1147 			{
   1148 				covers = dns_rdata_covers(&t->rdata);
   1149 			} else if (type == dns_rdatatype_any) {
   1150 				dns_db_detachnode(db, &node);
   1151 				dns_diff_clear(&trash);
   1152 				return DNS_R_NXRRSET;
   1153 			} else {
   1154 				covers = 0;
   1155 			}
   1156 
   1157 			/*
   1158 			 * Collect all database RRs for this name and type
   1159 			 * onto d_rrs and sort them.
   1160 			 */
   1161 			dns_rdataset_init(&rdataset);
   1162 			result = dns_db_findrdataset(db, node, ver, type,
   1163 						     covers, (isc_stdtime_t)0,
   1164 						     &rdataset, NULL);
   1165 			if (result != ISC_R_SUCCESS) {
   1166 				dns_db_detachnode(db, &node);
   1167 				dns_diff_clear(&trash);
   1168 				return DNS_R_NXRRSET;
   1169 			}
   1170 
   1171 			dns_diff_init(mctx, &d_rrs);
   1172 			dns_diff_init(mctx, &u_rrs);
   1173 
   1174 			for (result = dns_rdataset_first(&rdataset);
   1175 			     result == ISC_R_SUCCESS;
   1176 			     result = dns_rdataset_next(&rdataset))
   1177 			{
   1178 				dns_rdata_t rdata = DNS_RDATA_INIT;
   1179 				dns_rdataset_current(&rdataset, &rdata);
   1180 				CHECK(temp_append(&d_rrs, name, &rdata));
   1181 			}
   1182 			if (result != ISC_R_NOMORE) {
   1183 				goto cleanup;
   1184 			}
   1185 			CHECK(dns_diff_sort(&d_rrs, temp_order));
   1186 
   1187 			/*
   1188 			 * Collect all update RRs for this name and type
   1189 			 * onto u_rrs.  No need to sort them here -
   1190 			 * they are already sorted.
   1191 			 */
   1192 			while (t != NULL && dns_name_equal(&t->name, name) &&
   1193 			       t->rdata.type == type)
   1194 			{
   1195 				dns_difftuple_t *next = ISC_LIST_NEXT(t, link);
   1196 				ISC_LIST_UNLINK(temp->tuples, t, link);
   1197 				ISC_LIST_APPEND(u_rrs.tuples, t, link);
   1198 				t = next;
   1199 			}
   1200 
   1201 			/* Compare the two sorted lists. */
   1202 			CHECK(temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples),
   1203 					       ISC_LIST_HEAD(d_rrs.tuples)));
   1204 
   1205 			/*
   1206 			 * We are done with the tuples, but we can't free
   1207 			 * them yet because "name" still points into one
   1208 			 * of them.  Move them on a temporary list.
   1209 			 */
   1210 			ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link);
   1211 			ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link);
   1212 			dns_rdataset_disassociate(&rdataset);
   1213 
   1214 			continue;
   1215 
   1216 		cleanup:
   1217 			dns_diff_clear(&d_rrs);
   1218 			dns_diff_clear(&u_rrs);
   1219 			dns_diff_clear(&trash);
   1220 			dns_rdataset_disassociate(&rdataset);
   1221 			dns_db_detachnode(db, &node);
   1222 			return result;
   1223 		}
   1224 
   1225 		dns_db_detachnode(db, &node);
   1226 	}
   1227 
   1228 	dns_diff_clear(&trash);
   1229 	return ISC_R_SUCCESS;
   1230 }
   1231 
   1232 /**************************************************************************/
   1233 /*
   1234  * Conditional deletion of RRs.
   1235  */
   1236 
   1237 /*%
   1238  * Context structure for delete_if().
   1239  */
   1240 
   1241 typedef struct {
   1242 	rr_predicate *predicate;
   1243 	dns_zone_t *zone;
   1244 	dns_db_t *db;
   1245 	dns_dbversion_t *ver;
   1246 	dns_diff_t *diff;
   1247 	dns_name_t *name;
   1248 	dns_rdata_t *update_rr;
   1249 } conditional_delete_ctx_t;
   1250 
   1251 /*%
   1252  * Predicate functions for delete_if().
   1253  */
   1254 
   1255 /*%
   1256  * Return true iff 'db_rr' is neither a SOA nor an NS RR nor
   1257  * an RRSIG nor an NSEC3PARAM nor a NSEC.
   1258  */
   1259 static bool
   1260 type_not_soa_nor_ns_p(dns_zone_t *zone ISC_ATTR_UNUSED,
   1261 		      dns_rdata_t *update_rr ISC_ATTR_UNUSED,
   1262 		      dns_rdata_t *db_rr) {
   1263 	return (db_rr->type != dns_rdatatype_soa &&
   1264 		db_rr->type != dns_rdatatype_ns &&
   1265 		db_rr->type != dns_rdatatype_nsec3param &&
   1266 		db_rr->type != dns_rdatatype_rrsig &&
   1267 		db_rr->type != dns_rdatatype_nsec)
   1268 		       ? true
   1269 		       : false;
   1270 }
   1271 
   1272 /*%
   1273  * Return true iff 'db_rr' is neither a RRSIG nor a NSEC.
   1274  */
   1275 static bool
   1276 type_not_dnssec(dns_zone_t *zone ISC_ATTR_UNUSED,
   1277 		dns_rdata_t *update_rr ISC_ATTR_UNUSED, dns_rdata_t *db_rr) {
   1278 	return (db_rr->type != dns_rdatatype_rrsig &&
   1279 		db_rr->type != dns_rdatatype_nsec)
   1280 		       ? true
   1281 		       : false;
   1282 }
   1283 
   1284 /*%
   1285  * Return true always.
   1286  */
   1287 static bool
   1288 true_p(dns_zone_t *zone ISC_ATTR_UNUSED, dns_rdata_t *update_rr ISC_ATTR_UNUSED,
   1289        dns_rdata_t *db_rr ISC_ATTR_UNUSED) {
   1290 	return true;
   1291 }
   1292 
   1293 /*%
   1294  * Return true iff 'db_rr' is not a DNSKEY or derivative (CDNSKEY, CDS)
   1295  * of a key that is being used for signing.
   1296  */
   1297 static bool
   1298 rr_not_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *update_rr ISC_ATTR_UNUSED,
   1299 		    dns_rdata_t *db_rr) {
   1300 	isc_result_t result;
   1301 	bool dnskey_inuse = false;
   1302 
   1303 	if (dns_rdatatype_iskeymaterial(db_rr->type)) {
   1304 		/*
   1305 		 * If the check fails, we couldn't convert the rdata into a
   1306 		 * key. This shouldn't happen and it is unclear what the
   1307 		 * result action of the update should be.  In the case of
   1308 		 * deleting a single RR we treat such failure by rolling
   1309 		 * back the complete update.
   1310 		 *
   1311 		 * With RR predicates this is difficult because a predicate
   1312 		 * returns true or false. We could change the API to make
   1313 		 * it return a different type (e.g. isc_result_t) and
   1314 		 * treat ISC_R_SUCCESS as true, and a specific other
   1315 		 * value oas false, any other values would mean an error
   1316 		 * and requires rolling back the update.
   1317 		 *
   1318 		 * Currently we treat a check failure as the key was not
   1319 		 * in use.
   1320 		 */
   1321 		CHECK(dns_zone_dnskey_inuse(zone, db_rr, &dnskey_inuse));
   1322 	}
   1323 
   1324 cleanup:
   1325 	return !dnskey_inuse;
   1326 }
   1327 
   1328 /*%
   1329  * Return true iff the two RRs have identical rdata.
   1330  */
   1331 static bool
   1332 rr_equal_p(dns_zone_t *zone ISC_ATTR_UNUSED, dns_rdata_t *update_rr,
   1333 	   dns_rdata_t *db_rr) {
   1334 	/*
   1335 	 * XXXRTH  This is not a problem, but we should consider creating
   1336 	 *         dns_rdata_equal() (that used dns_name_equal()), since it
   1337 	 *         would be faster.  Not a priority.
   1338 	 */
   1339 	return dns_rdata_casecompare(update_rr, db_rr) == 0 ? true : false;
   1340 }
   1341 
   1342 /*%
   1343  * Return true iff 'update_rr' should replace 'db_rr' according
   1344  * to the special RFC2136 rules for CNAME, SOA, and WKS records.
   1345  *
   1346  * RFC2136 does not mention NSEC or DNAME, but multiple NSECs or DNAMEs
   1347  * make little sense, so we replace those, too.
   1348  *
   1349  * Additionally replace RRSIG that have been generated by the same key
   1350  * for the same type.  This simplifies refreshing a offline KSK by not
   1351  * requiring that the old RRSIG be deleted.  It also simplifies key
   1352  * rollover by only requiring that the new RRSIG be added.
   1353  */
   1354 static bool
   1355 replaces_p(dns_zone_t *zone ISC_ATTR_UNUSED, dns_rdata_t *update_rr,
   1356 	   dns_rdata_t *db_rr) {
   1357 	dns_rdata_rrsig_t updatesig, dbsig;
   1358 	isc_result_t result;
   1359 
   1360 	if (db_rr->type != update_rr->type) {
   1361 		return false;
   1362 	}
   1363 	if (db_rr->type == dns_rdatatype_cname) {
   1364 		return true;
   1365 	}
   1366 	if (db_rr->type == dns_rdatatype_dname) {
   1367 		return true;
   1368 	}
   1369 	if (db_rr->type == dns_rdatatype_soa) {
   1370 		return true;
   1371 	}
   1372 	if (db_rr->type == dns_rdatatype_nsec) {
   1373 		return true;
   1374 	}
   1375 	if (db_rr->type == dns_rdatatype_rrsig) {
   1376 		/*
   1377 		 * Replace existing RRSIG with the same keyid,
   1378 		 * covered and algorithm.
   1379 		 */
   1380 		result = dns_rdata_tostruct(db_rr, &dbsig, NULL);
   1381 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1382 		result = dns_rdata_tostruct(update_rr, &updatesig, NULL);
   1383 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1384 		if (dbsig.keyid == updatesig.keyid &&
   1385 		    dbsig.covered == updatesig.covered &&
   1386 		    dbsig.algorithm == updatesig.algorithm)
   1387 		{
   1388 			return true;
   1389 		}
   1390 	}
   1391 
   1392 	if (db_rr->rdclass == dns_rdataclass_in &&
   1393 	    db_rr->type == dns_rdatatype_wks)
   1394 	{
   1395 		/*
   1396 		 * Compare the address and protocol fields only.  These
   1397 		 * form the first five bytes of the RR data.  Do a
   1398 		 * raw binary comparison; unpacking the WKS RRs using
   1399 		 * dns_rdata_tostruct() might be cleaner in some ways.
   1400 		 */
   1401 		INSIST(db_rr->length >= 5 && update_rr->length >= 5);
   1402 		return memcmp(db_rr->data, update_rr->data, 5) == 0 ? true
   1403 								    : false;
   1404 	}
   1405 
   1406 	if (db_rr->type == dns_rdatatype_nsec3param) {
   1407 		if (db_rr->length != update_rr->length) {
   1408 			return false;
   1409 		}
   1410 		INSIST(db_rr->length >= 4 && update_rr->length >= 4);
   1411 		/*
   1412 		 * Replace NSEC3PARAM records that only differ by the
   1413 		 * flags field.
   1414 		 */
   1415 		if (db_rr->data[0] == update_rr->data[0] &&
   1416 		    memcmp(db_rr->data + 2, update_rr->data + 2,
   1417 			   update_rr->length - 2) == 0)
   1418 		{
   1419 			return true;
   1420 		}
   1421 	}
   1422 	return false;
   1423 }
   1424 
   1425 static bool
   1426 apex_special_processing_any(dns_zone_t *zone, dns_rdata_t *update_rr,
   1427 			    dns_rdata_t *db_rr) {
   1428 	return rr_not_dnskey_inuse(zone, update_rr, db_rr) &&
   1429 	       type_not_soa_nor_ns_p(zone, update_rr, db_rr);
   1430 }
   1431 
   1432 static bool
   1433 apex_special_processing(dns_zone_t *zone, dns_rdata_t *update_rr,
   1434 			dns_rdata_t *db_rr) {
   1435 	if (db_rr->type != update_rr->type) {
   1436 		return false;
   1437 	}
   1438 	return apex_special_processing_any(zone, update_rr, db_rr);
   1439 }
   1440 
   1441 /*%
   1442  * Internal helper function for delete_if().
   1443  */
   1444 static isc_result_t
   1445 delete_if_action(void *data, rr_t *rr) {
   1446 	conditional_delete_ctx_t *ctx = data;
   1447 	if ((*ctx->predicate)(ctx->zone, ctx->update_rr, &rr->rdata)) {
   1448 		isc_result_t result;
   1449 		result = update_one_rr(ctx->db, ctx->ver, ctx->diff,
   1450 				       DNS_DIFFOP_DEL, ctx->name, rr->ttl,
   1451 				       &rr->rdata);
   1452 		return result;
   1453 	}
   1454 
   1455 	return ISC_R_SUCCESS;
   1456 }
   1457 
   1458 /*%
   1459  * Conditionally delete RRs.  Apply 'predicate' to the RRs
   1460  * specified by 'db', 'ver', 'name', and 'type' (which can
   1461  * be dns_rdatatype_any to match any type).  Delete those
   1462  * RRs for which the predicate returns true, and log the
   1463  * deletions in 'diff'.
   1464  */
   1465 static isc_result_t
   1466 delete_if(rr_predicate *predicate, dns_zone_t *zone, dns_db_t *db,
   1467 	  dns_dbversion_t *ver, dns_name_t *name, dns_rdatatype_t type,
   1468 	  dns_rdatatype_t covers, dns_rdata_t *update_rr, dns_diff_t *diff) {
   1469 	conditional_delete_ctx_t ctx;
   1470 	ctx.predicate = predicate;
   1471 	ctx.zone = zone;
   1472 	ctx.db = db;
   1473 	ctx.ver = ver;
   1474 	ctx.diff = diff;
   1475 	ctx.name = name;
   1476 	ctx.update_rr = update_rr;
   1477 	return foreach_rr(db, ver, name, type, covers, delete_if_action, &ctx);
   1478 }
   1479 
   1480 /**************************************************************************/
   1481 
   1482 static isc_result_t
   1483 add_rr_prepare_action(void *data, rr_t *rr) {
   1484 	isc_result_t result = ISC_R_SUCCESS;
   1485 	add_rr_prepare_ctx_t *ctx = data;
   1486 	dns_difftuple_t *tuple = NULL;
   1487 	bool equal, case_equal, ttl_equal;
   1488 
   1489 	/*
   1490 	 * Are the new and old cases equal?
   1491 	 */
   1492 	case_equal = dns_name_caseequal(ctx->name, ctx->oldname);
   1493 
   1494 	/*
   1495 	 * Are the ttl's equal?
   1496 	 */
   1497 	ttl_equal = rr->ttl == ctx->update_rr_ttl;
   1498 
   1499 	/*
   1500 	 * If the update RR is a "duplicate" of a existing RR,
   1501 	 * the update should be silently ignored.
   1502 	 */
   1503 	equal = (dns_rdata_casecompare(&rr->rdata, ctx->update_rr) == 0);
   1504 	if (equal && case_equal && ttl_equal) {
   1505 		ctx->ignore_add = true;
   1506 		return ISC_R_SUCCESS;
   1507 	}
   1508 
   1509 	/*
   1510 	 * If this RR is "equal" to the update RR, it should
   1511 	 * be deleted before the update RR is added.
   1512 	 */
   1513 	if (replaces_p(ctx->zone, ctx->update_rr, &rr->rdata)) {
   1514 		CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL,
   1515 					   ctx->oldname, rr->ttl, &rr->rdata,
   1516 					   &tuple));
   1517 		dns_diff_append(&ctx->del_diff, &tuple);
   1518 		return ISC_R_SUCCESS;
   1519 	}
   1520 
   1521 	/*
   1522 	 * If this RR differs in TTL or case from the update RR,
   1523 	 * its TTL and case must be adjusted.
   1524 	 */
   1525 	if (!ttl_equal || !case_equal) {
   1526 		CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL,
   1527 					   ctx->oldname, rr->ttl, &rr->rdata,
   1528 					   &tuple));
   1529 		dns_diff_append(&ctx->del_diff, &tuple);
   1530 		if (!equal) {
   1531 			CHECK(dns_difftuple_create(
   1532 				ctx->add_diff.mctx, DNS_DIFFOP_ADD, ctx->name,
   1533 				ctx->update_rr_ttl, &rr->rdata, &tuple));
   1534 			dns_diff_append(&ctx->add_diff, &tuple);
   1535 		}
   1536 	}
   1537 cleanup:
   1538 	return result;
   1539 }
   1540 
   1541 /**************************************************************************/
   1542 /*
   1543  * Miscellaneous subroutines.
   1544  */
   1545 
   1546 /*%
   1547  * Extract a single update RR from 'section' of dynamic update message
   1548  * 'msg', with consistency checking.
   1549  *
   1550  * Stores the owner name, rdata, and TTL of the update RR at 'name',
   1551  * 'rdata', and 'ttl', respectively.
   1552  */
   1553 static void
   1554 get_current_rr(dns_message_t *msg, dns_section_t section, dns_name_t **name,
   1555 	       dns_rdata_t *rdata, dns_rdatatype_t *covers, dns_ttl_t *ttl,
   1556 	       dns_rdataclass_t *update_class) {
   1557 	dns_rdataset_t *rdataset;
   1558 	isc_result_t result;
   1559 	dns_message_currentname(msg, section, name);
   1560 	rdataset = ISC_LIST_HEAD((*name)->list);
   1561 	INSIST(rdataset != NULL);
   1562 	INSIST(ISC_LIST_NEXT(rdataset, link) == NULL);
   1563 	*covers = rdataset->covers;
   1564 	*ttl = rdataset->ttl;
   1565 	result = dns_rdataset_first(rdataset);
   1566 	INSIST(result == ISC_R_SUCCESS);
   1567 	dns_rdataset_current(rdataset, rdata);
   1568 	INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
   1569 	*update_class = rdata->rdclass;
   1570 	rdata->rdclass = dns_rdataclass_in;
   1571 }
   1572 
   1573 /*%
   1574  * Increment the SOA serial number of database 'db', version 'ver'.
   1575  * Replace the SOA record in the database, and log the
   1576  * change in 'diff'.
   1577  */
   1578 
   1579 /*
   1580  * XXXRTH  Failures in this routine will be worth logging, when
   1581  *         we have a logging system.  Failure to find the zonename
   1582  *	   or the SOA rdataset warrant at least an UNEXPECTED_ERROR().
   1583  */
   1584 
   1585 static isc_result_t
   1586 update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
   1587 		  isc_mem_t *mctx, dns_updatemethod_t method) {
   1588 	dns_difftuple_t *deltuple = NULL;
   1589 	dns_difftuple_t *addtuple = NULL;
   1590 	uint32_t serial;
   1591 	isc_result_t result;
   1592 
   1593 	CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
   1594 	CHECK(dns_difftuple_copy(deltuple, &addtuple));
   1595 	addtuple->op = DNS_DIFFOP_ADD;
   1596 
   1597 	serial = dns_soa_getserial(&addtuple->rdata);
   1598 	serial = dns_update_soaserial(serial, method, NULL);
   1599 	dns_soa_setserial(serial, &addtuple->rdata);
   1600 	CHECK(do_one_tuple(&deltuple, db, ver, diff));
   1601 	CHECK(do_one_tuple(&addtuple, db, ver, diff));
   1602 	result = ISC_R_SUCCESS;
   1603 
   1604 cleanup:
   1605 	if (addtuple != NULL) {
   1606 		dns_difftuple_free(&addtuple);
   1607 	}
   1608 	if (deltuple != NULL) {
   1609 		dns_difftuple_free(&deltuple);
   1610 	}
   1611 	return result;
   1612 }
   1613 
   1614 /*%
   1615  * Check that the new SOA record at 'update_rdata' does not
   1616  * illegally cause the SOA serial number to decrease or stay
   1617  * unchanged relative to the existing SOA in 'db'.
   1618  *
   1619  * Sets '*ok' to true if the update is legal, false if not.
   1620  *
   1621  * William King points out that RFC2136 is inconsistent about
   1622  * the case where the serial number stays unchanged:
   1623  *
   1624  *   section 3.4.2.2 requires a server to ignore a SOA update request
   1625  *   if the serial number on the update SOA is less_than_or_equal to
   1626  *   the zone SOA serial.
   1627  *
   1628  *   section 3.6 requires a server to ignore a SOA update request if
   1629  *   the serial is less_than the zone SOA serial.
   1630  *
   1631  * Paul says 3.4.2.2 is correct.
   1632  *
   1633  */
   1634 static isc_result_t
   1635 check_soa_increment(dns_db_t *db, dns_dbversion_t *ver,
   1636 		    dns_rdata_t *update_rdata, bool *ok) {
   1637 	uint32_t db_serial;
   1638 	uint32_t update_serial;
   1639 	isc_result_t result;
   1640 
   1641 	update_serial = dns_soa_getserial(update_rdata);
   1642 
   1643 	result = dns_db_getsoaserial(db, ver, &db_serial);
   1644 	if (result != ISC_R_SUCCESS) {
   1645 		return result;
   1646 	}
   1647 
   1648 	if (DNS_SERIAL_GE(db_serial, update_serial)) {
   1649 		*ok = false;
   1650 	} else {
   1651 		*ok = true;
   1652 	}
   1653 
   1654 	return ISC_R_SUCCESS;
   1655 }
   1656 
   1657 /**************************************************************************/
   1658 /*%
   1659  * The actual update code in all its glory.  We try to follow
   1660  * the RFC2136 pseudocode as closely as possible.
   1661  */
   1662 
   1663 static isc_result_t
   1664 send_update(ns_client_t *client, dns_zone_t *zone) {
   1665 	isc_result_t result = ISC_R_SUCCESS;
   1666 	dns_ssutable_t *ssutable = NULL;
   1667 	dns_message_t *request = client->message;
   1668 	isc_mem_t *mctx = client->manager->mctx;
   1669 	dns_aclenv_t *env = client->manager->aclenv;
   1670 	dns_rdatatype_t covers;
   1671 	dns_name_t *zonename = NULL;
   1672 	unsigned int *maxbytype = NULL;
   1673 	size_t update = 0, maxbytypelen = 0;
   1674 	dns_zoneopt_t options;
   1675 	dns_db_t *db = NULL;
   1676 	dns_dbversion_t *ver = NULL;
   1677 	update_t *uev = NULL;
   1678 
   1679 	CHECK(dns_zone_getdb(zone, &db));
   1680 	zonename = dns_db_origin(db);
   1681 	dns_zone_getssutable(zone, &ssutable);
   1682 	options = dns_zone_getoptions(zone);
   1683 	dns_db_currentversion(db, &ver);
   1684 
   1685 	/* Updates are only supported for class IN. */
   1686 	INSIST(dns_zone_getclass(zone) == dns_rdataclass_in);
   1687 
   1688 	/*
   1689 	 * Update message processing can leak record existence information
   1690 	 * so check that we are allowed to query this zone.  Additionally,
   1691 	 * if we would refuse all updates for this zone, we bail out here.
   1692 	 */
   1693 	CHECK(checkqueryacl(client, dns_zone_getqueryacl(zone),
   1694 			    dns_zone_getorigin(zone),
   1695 			    dns_zone_getupdateacl(zone), ssutable));
   1696 
   1697 	/*
   1698 	 * Check requestor's permissions.
   1699 	 */
   1700 	if (ssutable == NULL) {
   1701 		CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone),
   1702 				     "update", dns_zone_getorigin(zone), false,
   1703 				     false));
   1704 	} else if (client->signer == NULL && !TCPCLIENT(client)) {
   1705 		CHECK(checkupdateacl(client, NULL, "update",
   1706 				     dns_zone_getorigin(zone), false, true));
   1707 	}
   1708 
   1709 	if (dns_zone_getupdatedisabled(zone)) {
   1710 		FAILC(DNS_R_REFUSED,
   1711 		      "dynamic update temporarily disabled because the zone is "
   1712 		      "frozen.  Use 'rndc thaw' to re-enable updates.");
   1713 	}
   1714 
   1715 	/*
   1716 	 * Prescan the update section, checking for updates that
   1717 	 * are illegal or violate policy.
   1718 	 */
   1719 	if (ssutable != NULL) {
   1720 		maxbytypelen = request->counts[DNS_SECTION_UPDATE];
   1721 		maxbytype = isc_mem_cget(mctx, maxbytypelen,
   1722 					 sizeof(*maxbytype));
   1723 	}
   1724 
   1725 	for (update = 0,
   1726 	    result = dns_message_firstname(request, DNS_SECTION_UPDATE);
   1727 	     result == ISC_R_SUCCESS; update++,
   1728 	    result = dns_message_nextname(request, DNS_SECTION_UPDATE))
   1729 	{
   1730 		dns_name_t *name = NULL;
   1731 		dns_rdata_t rdata = DNS_RDATA_INIT;
   1732 		dns_ttl_t ttl;
   1733 		dns_rdataclass_t update_class;
   1734 
   1735 		INSIST(ssutable == NULL || update < maxbytypelen);
   1736 
   1737 		get_current_rr(request, DNS_SECTION_UPDATE, &name, &rdata,
   1738 			       &covers, &ttl, &update_class);
   1739 
   1740 		if (!dns_name_issubdomain(name, zonename)) {
   1741 			FAILC(DNS_R_NOTZONE, "update RR is outside zone");
   1742 		}
   1743 		if (update_class == dns_rdataclass_in) {
   1744 			/*
   1745 			 * Check for meta-RRs.  The RFC2136 pseudocode says
   1746 			 * check for ANY|AXFR|MAILA|MAILB, but the text adds
   1747 			 * "or any other QUERY metatype"
   1748 			 */
   1749 			if (dns_rdatatype_ismeta(rdata.type)) {
   1750 				FAILC(DNS_R_FORMERR, "meta-RR in update");
   1751 			}
   1752 			result = dns_zone_checknames(zone, name, &rdata);
   1753 			if (result != ISC_R_SUCCESS) {
   1754 				CHECK(DNS_R_REFUSED);
   1755 			}
   1756 			if ((options & DNS_ZONEOPT_CHECKSVCB) != 0 &&
   1757 			    rdata.rdclass == dns_rdataclass_in &&
   1758 			    rdata.type == dns_rdatatype_svcb)
   1759 			{
   1760 				result = dns_rdata_checksvcb(name, &rdata);
   1761 				if (result != ISC_R_SUCCESS) {
   1762 					const char *reason =
   1763 						isc_result_totext(result);
   1764 					FAILNT(DNS_R_REFUSED, name, rdata.type,
   1765 					       reason);
   1766 				}
   1767 			}
   1768 		} else if (update_class == dns_rdataclass_any) {
   1769 			if (ttl != 0 || rdata.length != 0 ||
   1770 			    (dns_rdatatype_ismeta(rdata.type) &&
   1771 			     rdata.type != dns_rdatatype_any))
   1772 			{
   1773 				FAILC(DNS_R_FORMERR, "meta-RR in update");
   1774 			}
   1775 		} else if (update_class == dns_rdataclass_none) {
   1776 			if (ttl != 0 || dns_rdatatype_ismeta(rdata.type)) {
   1777 				FAILC(DNS_R_FORMERR, "meta-RR in update");
   1778 			}
   1779 		} else {
   1780 			update_log(client, zone, ISC_LOG_WARNING,
   1781 				   "update RR has incorrect class %d",
   1782 				   update_class);
   1783 			CHECK(DNS_R_FORMERR);
   1784 		}
   1785 
   1786 		/*
   1787 		 * draft-ietf-dnsind-simple-secure-update-01 says
   1788 		 * "Unlike traditional dynamic update, the client
   1789 		 * is forbidden from updating NSEC records."
   1790 		 */
   1791 		if (rdata.type == dns_rdatatype_nsec3) {
   1792 			FAILC(DNS_R_REFUSED, "explicit NSEC3 updates are not "
   1793 					     "allowed in secure zones");
   1794 		} else if (rdata.type == dns_rdatatype_nsec) {
   1795 			FAILC(DNS_R_REFUSED, "explicit NSEC updates are not "
   1796 					     "allowed in secure zones");
   1797 		} else if (rdata.type == dns_rdatatype_sig) {
   1798 			FAILC(DNS_R_REFUSED, "SIG updates are not "
   1799 					     "allowed");
   1800 		} else if (rdata.type == dns_rdatatype_nxt) {
   1801 			FAILC(DNS_R_REFUSED, "NXT updates are not "
   1802 					     "allowed");
   1803 		} else if (rdata.type == dns_rdatatype_rrsig &&
   1804 			   !dns_name_equal(name, zonename))
   1805 		{
   1806 			FAILC(DNS_R_REFUSED,
   1807 			      "explicit RRSIG updates are currently not "
   1808 			      "supported in secure zones except at the apex");
   1809 		}
   1810 
   1811 		if (ssutable != NULL) {
   1812 			isc_netaddr_t netaddr;
   1813 			dns_name_t *target = NULL;
   1814 			dst_key_t *tsigkey = NULL;
   1815 			dns_rdata_ptr_t ptr;
   1816 			dns_rdata_in_srv_t srv;
   1817 
   1818 			maxbytype[update] = 0;
   1819 
   1820 			isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
   1821 
   1822 			if (client->message->tsigkey != NULL) {
   1823 				tsigkey = client->message->tsigkey->key;
   1824 			}
   1825 
   1826 			if ((update_class == dns_rdataclass_in ||
   1827 			     update_class == dns_rdataclass_none) &&
   1828 			    rdata.type == dns_rdatatype_ptr)
   1829 			{
   1830 				result = dns_rdata_tostruct(&rdata, &ptr, NULL);
   1831 				RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1832 				target = &ptr.ptr;
   1833 			}
   1834 
   1835 			if ((update_class == dns_rdataclass_in ||
   1836 			     update_class == dns_rdataclass_none) &&
   1837 			    rdata.type == dns_rdatatype_srv)
   1838 			{
   1839 				result = dns_rdata_tostruct(&rdata, &srv, NULL);
   1840 				RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1841 				target = &srv.target;
   1842 			}
   1843 
   1844 			if (update_class == dns_rdataclass_any &&
   1845 			    (rdata.type == dns_rdatatype_ptr ||
   1846 			     rdata.type == dns_rdatatype_srv))
   1847 			{
   1848 				ssu_check_t ssuinfo;
   1849 
   1850 				ssuinfo.name = name;
   1851 				ssuinfo.table = ssutable;
   1852 				ssuinfo.signer = client->signer;
   1853 				ssuinfo.addr = &netaddr;
   1854 				ssuinfo.aclenv = env;
   1855 				ssuinfo.tcp = TCPCLIENT(client);
   1856 				ssuinfo.key = tsigkey;
   1857 
   1858 				result = foreach_rr(db, ver, name, rdata.type,
   1859 						    dns_rdatatype_none,
   1860 						    ssu_checkrr, &ssuinfo);
   1861 				if (result != ISC_R_SUCCESS) {
   1862 					FAILC(DNS_R_REFUSED,
   1863 					      "rejected by secure update");
   1864 				}
   1865 			} else if (target != NULL &&
   1866 				   update_class == dns_rdataclass_none)
   1867 			{
   1868 				bool flag;
   1869 				CHECK(rr_exists(db, ver, name, &rdata, &flag));
   1870 				if (flag &&
   1871 				    !dns_ssutable_checkrules(
   1872 					    ssutable, client->signer, name,
   1873 					    &netaddr, TCPCLIENT(client), env,
   1874 					    rdata.type, target, tsigkey, NULL))
   1875 				{
   1876 					FAILC(DNS_R_REFUSED,
   1877 					      "rejected by secure update");
   1878 				}
   1879 			} else if (rdata.type != dns_rdatatype_any) {
   1880 				const dns_ssurule_t *ssurule = NULL;
   1881 				if (!dns_ssutable_checkrules(
   1882 					    ssutable, client->signer, name,
   1883 					    &netaddr, TCPCLIENT(client), env,
   1884 					    rdata.type, target, tsigkey,
   1885 					    &ssurule))
   1886 				{
   1887 					FAILC(DNS_R_REFUSED,
   1888 					      "rejected by secure update");
   1889 				}
   1890 				maxbytype[update] = dns_ssurule_max(ssurule,
   1891 								    rdata.type);
   1892 			} else {
   1893 				if (!ssu_checkall(db, ver, name, ssutable,
   1894 						  client->signer, &netaddr, env,
   1895 						  TCPCLIENT(client), tsigkey))
   1896 				{
   1897 					FAILC(DNS_R_REFUSED,
   1898 					      "rejected by secure update");
   1899 				}
   1900 			}
   1901 		}
   1902 	}
   1903 	if (result != ISC_R_NOMORE) {
   1904 		CHECK(result);
   1905 	}
   1906 
   1907 	update_log(client, zone, LOGLEVEL_DEBUG, "update section prescan OK");
   1908 
   1909 	result = isc_quota_acquire(&client->manager->sctx->updquota);
   1910 	if (result != ISC_R_SUCCESS) {
   1911 		update_log(client, zone, LOGLEVEL_PROTOCOL,
   1912 			   "update failed: too many DNS UPDATEs queued (%s)",
   1913 			   isc_result_totext(result));
   1914 		ns_stats_increment(client->manager->sctx->nsstats,
   1915 				   ns_statscounter_updatequota);
   1916 		CHECK(DNS_R_DROP);
   1917 	}
   1918 
   1919 	uev = isc_mem_get(client->manager->mctx, sizeof(*uev));
   1920 	*uev = (update_t){
   1921 		.zone = zone,
   1922 		.client = client,
   1923 		.ssutable = MOVE_OWNERSHIP(ssutable),
   1924 		.maxbytype = MOVE_OWNERSHIP(maxbytype),
   1925 		.maxbytypelen = maxbytypelen,
   1926 		.result = ISC_R_SUCCESS,
   1927 	};
   1928 
   1929 	isc_nmhandle_attach(client->handle, &client->updatehandle);
   1930 	isc_async_run(dns_zone_getloop(zone), update_action, uev);
   1931 
   1932 cleanup:
   1933 	if (db != NULL) {
   1934 		dns_db_closeversion(db, &ver, false);
   1935 		dns_db_detach(&db);
   1936 	}
   1937 
   1938 	if (maxbytype != NULL) {
   1939 		isc_mem_cput(mctx, maxbytype, maxbytypelen, sizeof(*maxbytype));
   1940 	}
   1941 
   1942 	if (ssutable != NULL) {
   1943 		dns_ssutable_detach(&ssutable);
   1944 	}
   1945 
   1946 	return result;
   1947 }
   1948 
   1949 static void
   1950 respond(ns_client_t *client, isc_result_t result) {
   1951 	isc_result_t msg_result;
   1952 
   1953 	msg_result = dns_message_reply(client->message, true);
   1954 	if (msg_result != ISC_R_SUCCESS) {
   1955 		isc_log_write(ns_lctx, NS_LOGCATEGORY_UPDATE,
   1956 			      NS_LOGMODULE_UPDATE, ISC_LOG_ERROR,
   1957 			      "could not create update response message: %s",
   1958 			      isc_result_totext(msg_result));
   1959 		ns_client_drop(client, msg_result);
   1960 		isc_nmhandle_detach(&client->reqhandle);
   1961 		return;
   1962 	}
   1963 
   1964 	client->message->rcode = dns_result_torcode(result);
   1965 	ns_client_send(client);
   1966 	isc_nmhandle_detach(&client->reqhandle);
   1967 }
   1968 
   1969 void
   1970 ns_update_start(ns_client_t *client, isc_nmhandle_t *handle,
   1971 		isc_result_t sigresult) {
   1972 	dns_message_t *request = client->message;
   1973 	isc_result_t result;
   1974 	dns_name_t *zonename;
   1975 	dns_rdataset_t *zone_rdataset;
   1976 	dns_zone_t *zone = NULL, *raw = NULL;
   1977 
   1978 	/*
   1979 	 * Attach to the request handle. This will be held until
   1980 	 * we respond, or drop the request.
   1981 	 */
   1982 	isc_nmhandle_attach(handle, &client->reqhandle);
   1983 
   1984 	/*
   1985 	 * Interpret the zone section.
   1986 	 */
   1987 	result = dns_message_firstname(request, DNS_SECTION_ZONE);
   1988 	if (result != ISC_R_SUCCESS) {
   1989 		FAILC(DNS_R_FORMERR, "update zone section empty");
   1990 	}
   1991 
   1992 	/*
   1993 	 * The zone section must contain exactly one "question", and
   1994 	 * it must be of type SOA.
   1995 	 */
   1996 	zonename = NULL;
   1997 	dns_message_currentname(request, DNS_SECTION_ZONE, &zonename);
   1998 	zone_rdataset = ISC_LIST_HEAD(zonename->list);
   1999 	if (zone_rdataset->type != dns_rdatatype_soa) {
   2000 		FAILC(DNS_R_FORMERR, "update zone section contains non-SOA");
   2001 	}
   2002 	if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) {
   2003 		FAILC(DNS_R_FORMERR,
   2004 		      "update zone section contains multiple RRs");
   2005 	}
   2006 
   2007 	/* The zone section must have exactly one name. */
   2008 	result = dns_message_nextname(request, DNS_SECTION_ZONE);
   2009 	if (result != ISC_R_NOMORE) {
   2010 		FAILC(DNS_R_FORMERR,
   2011 		      "update zone section contains multiple RRs");
   2012 	}
   2013 
   2014 	result = dns_view_findzone(client->view, zonename, DNS_ZTFIND_EXACT,
   2015 				   &zone);
   2016 	if (result != ISC_R_SUCCESS) {
   2017 		FAILN(DNS_R_NOTAUTH, zonename,
   2018 		      "not authoritative for update zone");
   2019 	}
   2020 
   2021 	/*
   2022 	 * If there is a raw (unsigned) zone associated with this
   2023 	 * zone then it processes the UPDATE request.
   2024 	 */
   2025 	dns_zone_getraw(zone, &raw);
   2026 	if (raw != NULL) {
   2027 		dns_zone_detach(&zone);
   2028 		dns_zone_attach(raw, &zone);
   2029 		dns_zone_detach(&raw);
   2030 	}
   2031 
   2032 	switch (dns_zone_gettype(zone)) {
   2033 	case dns_zone_primary:
   2034 	case dns_zone_dlz:
   2035 		/*
   2036 		 * We can now fail due to a bad signature as we now know
   2037 		 * that we are the primary.
   2038 		 */
   2039 		CHECK(sigresult);
   2040 		dns_message_clonebuffer(client->message);
   2041 		CHECK(send_update(client, zone));
   2042 		break;
   2043 	case dns_zone_secondary:
   2044 	case dns_zone_mirror:
   2045 		dns_message_clonebuffer(client->message);
   2046 		CHECK(send_forward(client, zone));
   2047 		break;
   2048 	default:
   2049 		FAILC(DNS_R_NOTAUTH, "not authoritative for update zone");
   2050 	}
   2051 	return;
   2052 
   2053 cleanup:
   2054 	if (result == DNS_R_REFUSED) {
   2055 		inc_stats(client, zone, ns_statscounter_updaterej);
   2056 	}
   2057 
   2058 	/*
   2059 	 * We failed without having sent an update event to the zone.
   2060 	 * We are still in the client context, so we can
   2061 	 * simply give an error response without switching tasks.
   2062 	 */
   2063 	if (result == DNS_R_DROP) {
   2064 		ns_client_drop(client, result);
   2065 		isc_nmhandle_detach(&client->reqhandle);
   2066 	} else {
   2067 		respond(client, result);
   2068 	}
   2069 
   2070 	if (zone != NULL) {
   2071 		dns_zone_detach(&zone);
   2072 	}
   2073 }
   2074 
   2075 /*%
   2076  * DS records are not allowed to exist without corresponding NS records,
   2077  * RFC 3658, 2.2 Protocol Change,
   2078  * "DS RRsets MUST NOT appear at non-delegation points or at a zone's apex".
   2079  */
   2080 
   2081 static isc_result_t
   2082 remove_orphaned_ds(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *newver,
   2083 		   dns_diff_t *diff) {
   2084 	isc_result_t result;
   2085 	bool ns_exists;
   2086 	dns_difftuple_t *tuple;
   2087 	dns_diff_t temp_diff;
   2088 
   2089 	dns_diff_init(diff->mctx, &temp_diff);
   2090 
   2091 	for (tuple = ISC_LIST_HEAD(diff->tuples); tuple != NULL;
   2092 	     tuple = ISC_LIST_NEXT(tuple, link))
   2093 	{
   2094 		if (!((tuple->op == DNS_DIFFOP_DEL &&
   2095 		       tuple->rdata.type == dns_rdatatype_ns) ||
   2096 		      (tuple->op == DNS_DIFFOP_ADD &&
   2097 		       tuple->rdata.type == dns_rdatatype_ds)))
   2098 		{
   2099 			continue;
   2100 		}
   2101 		CHECK(rrset_exists(db, newver, &tuple->name, dns_rdatatype_ns,
   2102 				   0, &ns_exists));
   2103 		if (ns_exists &&
   2104 		    !dns_name_equal(&tuple->name, dns_db_origin(db)))
   2105 		{
   2106 			continue;
   2107 		}
   2108 		CHECK(delete_if(true_p, zone, db, newver, &tuple->name,
   2109 				dns_rdatatype_ds, 0, NULL, &temp_diff));
   2110 	}
   2111 	result = ISC_R_SUCCESS;
   2112 
   2113 cleanup:
   2114 	for (tuple = ISC_LIST_HEAD(temp_diff.tuples); tuple != NULL;
   2115 	     tuple = ISC_LIST_HEAD(temp_diff.tuples))
   2116 	{
   2117 		ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
   2118 		dns_diff_appendminimal(diff, &tuple);
   2119 	}
   2120 	return result;
   2121 }
   2122 
   2123 /*
   2124  * This implements the post load integrity checks for mx records.
   2125  */
   2126 static isc_result_t
   2127 check_mx(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
   2128 	 dns_dbversion_t *newver, dns_diff_t *diff) {
   2129 	char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")];
   2130 	char ownerbuf[DNS_NAME_FORMATSIZE];
   2131 	char namebuf[DNS_NAME_FORMATSIZE];
   2132 	char altbuf[DNS_NAME_FORMATSIZE];
   2133 	dns_difftuple_t *t;
   2134 	dns_fixedname_t fixed;
   2135 	dns_name_t *foundname;
   2136 	dns_rdata_mx_t mx;
   2137 	dns_rdata_t rdata;
   2138 	bool ok = true;
   2139 	bool isaddress;
   2140 	isc_result_t result;
   2141 	struct in6_addr addr6;
   2142 	struct in_addr addr;
   2143 	dns_zoneopt_t options;
   2144 
   2145 	foundname = dns_fixedname_initname(&fixed);
   2146 	dns_rdata_init(&rdata);
   2147 	options = dns_zone_getoptions(zone);
   2148 
   2149 	for (t = ISC_LIST_HEAD(diff->tuples); t != NULL;
   2150 	     t = ISC_LIST_NEXT(t, link))
   2151 	{
   2152 		if (t->op != DNS_DIFFOP_ADD ||
   2153 		    t->rdata.type != dns_rdatatype_mx)
   2154 		{
   2155 			continue;
   2156 		}
   2157 
   2158 		result = dns_rdata_tostruct(&t->rdata, &mx, NULL);
   2159 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   2160 		/*
   2161 		 * Check if we will error out if we attempt to reload the
   2162 		 * zone.
   2163 		 */
   2164 		dns_name_format(&mx.mx, namebuf, sizeof(namebuf));
   2165 		dns_name_format(&t->name, ownerbuf, sizeof(ownerbuf));
   2166 		isaddress = false;
   2167 		if ((options & DNS_ZONEOPT_CHECKMX) != 0 &&
   2168 		    strlcpy(tmp, namebuf, sizeof(tmp)) < sizeof(tmp))
   2169 		{
   2170 			if (tmp[strlen(tmp) - 1] == '.') {
   2171 				tmp[strlen(tmp) - 1] = '\0';
   2172 			}
   2173 			if (inet_pton(AF_INET, tmp, &addr) == 1 ||
   2174 			    inet_pton(AF_INET6, tmp, &addr6) == 1)
   2175 			{
   2176 				isaddress = true;
   2177 			}
   2178 		}
   2179 
   2180 		if (isaddress && (options & DNS_ZONEOPT_CHECKMXFAIL) != 0) {
   2181 			update_log(client, zone, ISC_LOG_ERROR,
   2182 				   "%s/MX: '%s': %s", ownerbuf, namebuf,
   2183 				   isc_result_totext(DNS_R_MXISADDRESS));
   2184 			ok = false;
   2185 		} else if (isaddress) {
   2186 			update_log(client, zone, ISC_LOG_WARNING,
   2187 				   "%s/MX: warning: '%s': %s", ownerbuf,
   2188 				   namebuf,
   2189 				   isc_result_totext(DNS_R_MXISADDRESS));
   2190 		}
   2191 
   2192 		/*
   2193 		 * Check zone integrity checks.
   2194 		 */
   2195 		if ((options & DNS_ZONEOPT_CHECKINTEGRITY) == 0) {
   2196 			continue;
   2197 		}
   2198 		result = dns_db_find(db, &mx.mx, newver, dns_rdatatype_a, 0, 0,
   2199 				     NULL, foundname, NULL, NULL);
   2200 		if (result == ISC_R_SUCCESS) {
   2201 			continue;
   2202 		}
   2203 
   2204 		if (result == DNS_R_NXRRSET) {
   2205 			result = dns_db_find(db, &mx.mx, newver,
   2206 					     dns_rdatatype_aaaa, 0, 0, NULL,
   2207 					     foundname, NULL, NULL);
   2208 			if (result == ISC_R_SUCCESS) {
   2209 				continue;
   2210 			}
   2211 		}
   2212 
   2213 		if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN) {
   2214 			update_log(
   2215 				client, zone, ISC_LOG_ERROR,
   2216 				"%s/MX '%s' has no address records (A or AAAA)",
   2217 				ownerbuf, namebuf);
   2218 			ok = false;
   2219 		} else if (result == DNS_R_CNAME) {
   2220 			update_log(client, zone, ISC_LOG_ERROR,
   2221 				   "%s/MX '%s' is a CNAME (illegal)", ownerbuf,
   2222 				   namebuf);
   2223 			ok = false;
   2224 		} else if (result == DNS_R_DNAME) {
   2225 			dns_name_format(foundname, altbuf, sizeof altbuf);
   2226 			update_log(client, zone, ISC_LOG_ERROR,
   2227 				   "%s/MX '%s' is below a DNAME '%s' (illegal)",
   2228 				   ownerbuf, namebuf, altbuf);
   2229 			ok = false;
   2230 		}
   2231 	}
   2232 	return ok ? ISC_R_SUCCESS : DNS_R_REFUSED;
   2233 }
   2234 
   2235 static isc_result_t
   2236 rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
   2237 	  const dns_rdata_t *rdata, bool *flag) {
   2238 	dns_rdataset_t rdataset;
   2239 	dns_dbnode_t *node = NULL;
   2240 	isc_result_t result;
   2241 
   2242 	dns_rdataset_init(&rdataset);
   2243 	if (rdata->type == dns_rdatatype_nsec3) {
   2244 		result = dns_db_findnsec3node(db, name, false, &node);
   2245 	} else {
   2246 		result = dns_db_findnode(db, name, false, &node);
   2247 	}
   2248 	if (result == ISC_R_NOTFOUND) {
   2249 		*flag = false;
   2250 		result = ISC_R_SUCCESS;
   2251 		goto cleanup;
   2252 	} else {
   2253 		CHECK(result);
   2254 	}
   2255 	result = dns_db_findrdataset(db, node, ver, rdata->type, 0,
   2256 				     (isc_stdtime_t)0, &rdataset, NULL);
   2257 	if (result == ISC_R_NOTFOUND) {
   2258 		*flag = false;
   2259 		result = ISC_R_SUCCESS;
   2260 		goto cleanup;
   2261 	}
   2262 
   2263 	for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS;
   2264 	     result = dns_rdataset_next(&rdataset))
   2265 	{
   2266 		dns_rdata_t myrdata = DNS_RDATA_INIT;
   2267 		dns_rdataset_current(&rdataset, &myrdata);
   2268 		if (!dns_rdata_casecompare(&myrdata, rdata)) {
   2269 			break;
   2270 		}
   2271 	}
   2272 	dns_rdataset_disassociate(&rdataset);
   2273 	if (result == ISC_R_SUCCESS) {
   2274 		*flag = true;
   2275 	} else if (result == ISC_R_NOMORE) {
   2276 		*flag = false;
   2277 		result = ISC_R_SUCCESS;
   2278 	}
   2279 
   2280 cleanup:
   2281 	if (node != NULL) {
   2282 		dns_db_detachnode(db, &node);
   2283 	}
   2284 	return result;
   2285 }
   2286 
   2287 static isc_result_t
   2288 get_iterations(dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_t privatetype,
   2289 	       unsigned int *iterationsp) {
   2290 	dns_dbnode_t *node = NULL;
   2291 	dns_rdata_nsec3param_t nsec3param;
   2292 	dns_rdataset_t rdataset;
   2293 	isc_result_t result;
   2294 	unsigned int iterations = 0;
   2295 
   2296 	dns_rdataset_init(&rdataset);
   2297 
   2298 	result = dns_db_getoriginnode(db, &node);
   2299 	if (result != ISC_R_SUCCESS) {
   2300 		return result;
   2301 	}
   2302 	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0,
   2303 				     (isc_stdtime_t)0, &rdataset, NULL);
   2304 	if (result == ISC_R_NOTFOUND) {
   2305 		goto try_private;
   2306 	}
   2307 	CHECK(result);
   2308 
   2309 	for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS;
   2310 	     result = dns_rdataset_next(&rdataset))
   2311 	{
   2312 		dns_rdata_t rdata = DNS_RDATA_INIT;
   2313 		dns_rdataset_current(&rdataset, &rdata);
   2314 		CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
   2315 		if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) {
   2316 			continue;
   2317 		}
   2318 		if (nsec3param.iterations > iterations) {
   2319 			iterations = nsec3param.iterations;
   2320 		}
   2321 	}
   2322 	if (result != ISC_R_NOMORE) {
   2323 		goto cleanup;
   2324 	}
   2325 
   2326 	dns_rdataset_disassociate(&rdataset);
   2327 
   2328 try_private:
   2329 	if (privatetype == 0) {
   2330 		goto success;
   2331 	}
   2332 
   2333 	result = dns_db_findrdataset(db, node, ver, privatetype, 0,
   2334 				     (isc_stdtime_t)0, &rdataset, NULL);
   2335 	if (result == ISC_R_NOTFOUND) {
   2336 		goto success;
   2337 	}
   2338 	CHECK(result);
   2339 
   2340 	for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS;
   2341 	     result = dns_rdataset_next(&rdataset))
   2342 	{
   2343 		unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
   2344 		dns_rdata_t private = DNS_RDATA_INIT;
   2345 		dns_rdata_t rdata = DNS_RDATA_INIT;
   2346 
   2347 		dns_rdataset_current(&rdataset, &rdata);
   2348 		if (!dns_nsec3param_fromprivate(&private, &rdata, buf,
   2349 						sizeof(buf)))
   2350 		{
   2351 			continue;
   2352 		}
   2353 		CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
   2354 		if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) {
   2355 			continue;
   2356 		}
   2357 		if (nsec3param.iterations > iterations) {
   2358 			iterations = nsec3param.iterations;
   2359 		}
   2360 	}
   2361 	if (result != ISC_R_NOMORE) {
   2362 		goto cleanup;
   2363 	}
   2364 
   2365 success:
   2366 	*iterationsp = iterations;
   2367 	result = ISC_R_SUCCESS;
   2368 
   2369 cleanup:
   2370 	if (node != NULL) {
   2371 		dns_db_detachnode(db, &node);
   2372 	}
   2373 	if (dns_rdataset_isassociated(&rdataset)) {
   2374 		dns_rdataset_disassociate(&rdataset);
   2375 	}
   2376 	return result;
   2377 }
   2378 
   2379 /*
   2380  * Prevent the zone entering a inconsistent state where
   2381  * NSEC only DNSKEYs are present with NSEC3 chains.
   2382  */
   2383 static isc_result_t
   2384 check_dnssec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
   2385 	     dns_dbversion_t *ver, dns_diff_t *diff) {
   2386 	isc_result_t result;
   2387 	unsigned int iterations = 0;
   2388 	dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
   2389 
   2390 	/* Refuse to allow NSEC3 with NSEC-only keys */
   2391 	if (!dns_zone_check_dnskey_nsec3(zone, db, ver, diff, NULL, 0)) {
   2392 		update_log(client, zone, ISC_LOG_ERROR,
   2393 			   "NSEC only DNSKEYs and NSEC3 chains not allowed");
   2394 		CHECK(DNS_R_REFUSED);
   2395 	}
   2396 
   2397 	/* Verify NSEC3 params */
   2398 	CHECK(get_iterations(db, ver, privatetype, &iterations));
   2399 	if (iterations > dns_nsec3_maxiterations()) {
   2400 		update_log(client, zone, ISC_LOG_ERROR,
   2401 			   "too many NSEC3 iterations (%u)", iterations);
   2402 		CHECK(DNS_R_REFUSED);
   2403 	}
   2404 
   2405 cleanup:
   2406 	return result;
   2407 }
   2408 
   2409 /*
   2410  * Delay NSEC3PARAM changes as they need to be applied to the whole zone.
   2411  */
   2412 static isc_result_t
   2413 add_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
   2414 		       dns_dbversion_t *ver, dns_diff_t *diff) {
   2415 	isc_result_t result = ISC_R_SUCCESS;
   2416 	dns_difftuple_t *tuple, *newtuple = NULL, *next;
   2417 	dns_rdata_t rdata = DNS_RDATA_INIT;
   2418 	unsigned char buf[DNS_PRIVATE_BUFFERSIZE];
   2419 	dns_diff_t temp_diff;
   2420 	dns_diffop_t op;
   2421 	bool flag;
   2422 	dns_name_t *name = dns_zone_getorigin(zone);
   2423 	dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
   2424 	uint32_t ttl = 0;
   2425 	bool ttl_good = false;
   2426 
   2427 	update_log(client, zone, ISC_LOG_DEBUG(3),
   2428 		   "checking for NSEC3PARAM changes");
   2429 
   2430 	dns_diff_init(diff->mctx, &temp_diff);
   2431 
   2432 	/*
   2433 	 * Extract NSEC3PARAM tuples from list.
   2434 	 */
   2435 	for (tuple = ISC_LIST_HEAD(diff->tuples); tuple != NULL; tuple = next) {
   2436 		next = ISC_LIST_NEXT(tuple, link);
   2437 
   2438 		if (tuple->rdata.type != dns_rdatatype_nsec3param ||
   2439 		    !dns_name_equal(name, &tuple->name))
   2440 		{
   2441 			continue;
   2442 		}
   2443 		ISC_LIST_UNLINK(diff->tuples, tuple, link);
   2444 		ISC_LIST_APPEND(temp_diff.tuples, tuple, link);
   2445 	}
   2446 
   2447 	/*
   2448 	 * Extract TTL changes pairs, we don't need to convert these to
   2449 	 * delayed changes.
   2450 	 */
   2451 	for (tuple = ISC_LIST_HEAD(temp_diff.tuples); tuple != NULL;
   2452 	     tuple = next)
   2453 	{
   2454 		if (tuple->op == DNS_DIFFOP_ADD) {
   2455 			if (!ttl_good) {
   2456 				/*
   2457 				 * Any adds here will contain the final
   2458 				 * NSEC3PARAM RRset TTL.
   2459 				 */
   2460 				ttl = tuple->ttl;
   2461 				ttl_good = true;
   2462 			}
   2463 			/*
   2464 			 * Walk the temp_diff list looking for the
   2465 			 * corresponding delete.
   2466 			 */
   2467 			next = ISC_LIST_HEAD(temp_diff.tuples);
   2468 			while (next != NULL) {
   2469 				unsigned char *next_data = next->rdata.data;
   2470 				unsigned char *tuple_data = tuple->rdata.data;
   2471 				if (next->op == DNS_DIFFOP_DEL &&
   2472 				    next->rdata.length == tuple->rdata.length &&
   2473 				    !memcmp(next_data, tuple_data,
   2474 					    next->rdata.length))
   2475 				{
   2476 					ISC_LIST_UNLINK(temp_diff.tuples, next,
   2477 							link);
   2478 					ISC_LIST_APPEND(diff->tuples, next,
   2479 							link);
   2480 					break;
   2481 				}
   2482 				next = ISC_LIST_NEXT(next, link);
   2483 			}
   2484 			/*
   2485 			 * If we have not found a pair move onto the next
   2486 			 * tuple.
   2487 			 */
   2488 			if (next == NULL) {
   2489 				next = ISC_LIST_NEXT(tuple, link);
   2490 				continue;
   2491 			}
   2492 			/*
   2493 			 * Find the next tuple to be processed before
   2494 			 * unlinking then complete moving the pair to 'diff'.
   2495 			 */
   2496 			next = ISC_LIST_NEXT(tuple, link);
   2497 			ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
   2498 			ISC_LIST_APPEND(diff->tuples, tuple, link);
   2499 		} else {
   2500 			next = ISC_LIST_NEXT(tuple, link);
   2501 		}
   2502 	}
   2503 
   2504 	/*
   2505 	 * Preserve any ongoing changes from a BIND 9.6.x upgrade.
   2506 	 *
   2507 	 * Any NSEC3PARAM records with flags other than OPTOUT named
   2508 	 * in managing and should not be touched so revert such changes
   2509 	 * taking into account any TTL change of the NSEC3PARAM RRset.
   2510 	 */
   2511 	for (tuple = ISC_LIST_HEAD(temp_diff.tuples); tuple != NULL;
   2512 	     tuple = next)
   2513 	{
   2514 		next = ISC_LIST_NEXT(tuple, link);
   2515 		if ((tuple->rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) != 0) {
   2516 			/*
   2517 			 * If we haven't had any adds then the tuple->ttl must
   2518 			 * be the original ttl and should be used for any
   2519 			 * future changes.
   2520 			 */
   2521 			if (!ttl_good) {
   2522 				ttl = tuple->ttl;
   2523 				ttl_good = true;
   2524 			}
   2525 			op = (tuple->op == DNS_DIFFOP_DEL) ? DNS_DIFFOP_ADD
   2526 							   : DNS_DIFFOP_DEL;
   2527 			CHECK(dns_difftuple_create(diff->mctx, op, name, ttl,
   2528 						   &tuple->rdata, &newtuple));
   2529 			CHECK(do_one_tuple(&newtuple, db, ver, diff));
   2530 			ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
   2531 			dns_diff_appendminimal(diff, &tuple);
   2532 		}
   2533 	}
   2534 
   2535 	/*
   2536 	 * We now have just the actual changes to the NSEC3PARAM RRset.
   2537 	 * Convert the adds to delayed adds and the deletions into delayed
   2538 	 * deletions.
   2539 	 */
   2540 	for (tuple = ISC_LIST_HEAD(temp_diff.tuples); tuple != NULL;
   2541 	     tuple = next)
   2542 	{
   2543 		/*
   2544 		 * If we haven't had any adds then the tuple->ttl must be the
   2545 		 * original ttl and should be used for any future changes.
   2546 		 */
   2547 		if (!ttl_good) {
   2548 			ttl = tuple->ttl;
   2549 			ttl_good = true;
   2550 		}
   2551 		if (tuple->op == DNS_DIFFOP_ADD) {
   2552 			bool nseconly = false;
   2553 
   2554 			/*
   2555 			 * Look for any deletes which match this ADD ignoring
   2556 			 * flags.  We don't need to explicitly remove them as
   2557 			 * they will be removed a side effect of processing
   2558 			 * the add.
   2559 			 */
   2560 			next = ISC_LIST_HEAD(temp_diff.tuples);
   2561 			while (next != NULL) {
   2562 				unsigned char *next_data = next->rdata.data;
   2563 				unsigned char *tuple_data = tuple->rdata.data;
   2564 				if (next->op != DNS_DIFFOP_DEL ||
   2565 				    next->rdata.length != tuple->rdata.length ||
   2566 				    next_data[0] != tuple_data[0] ||
   2567 				    next_data[2] != tuple_data[2] ||
   2568 				    next_data[3] != tuple_data[3] ||
   2569 				    memcmp(next_data + 4, tuple_data + 4,
   2570 					   tuple->rdata.length - 4))
   2571 				{
   2572 					next = ISC_LIST_NEXT(next, link);
   2573 					continue;
   2574 				}
   2575 				ISC_LIST_UNLINK(temp_diff.tuples, next, link);
   2576 				ISC_LIST_APPEND(diff->tuples, next, link);
   2577 				next = ISC_LIST_HEAD(temp_diff.tuples);
   2578 			}
   2579 
   2580 			/*
   2581 			 * Create a private-type record to signal that
   2582 			 * we want a delayed NSEC3 chain add/delete
   2583 			 */
   2584 			dns_nsec3param_toprivate(&tuple->rdata, &rdata,
   2585 						 privatetype, buf, sizeof(buf));
   2586 			buf[2] |= DNS_NSEC3FLAG_CREATE;
   2587 
   2588 			/*
   2589 			 * If the zone is not currently capable of
   2590 			 * supporting an NSEC3 chain, then we set the
   2591 			 * INITIAL flag to indicate that these parameters
   2592 			 * are to be used later.
   2593 			 *
   2594 			 * Don't provide a 'diff' here because we want to
   2595 			 * know the capability of the current database.
   2596 			 */
   2597 			result = dns_nsec_nseconly(db, ver, NULL, &nseconly);
   2598 			if (result == ISC_R_NOTFOUND || nseconly) {
   2599 				buf[2] |= DNS_NSEC3FLAG_INITIAL;
   2600 			}
   2601 
   2602 			/*
   2603 			 * See if this CREATE request already exists.
   2604 			 */
   2605 			CHECK(rr_exists(db, ver, name, &rdata, &flag));
   2606 
   2607 			if (!flag) {
   2608 				CHECK(dns_difftuple_create(
   2609 					diff->mctx, DNS_DIFFOP_ADD, name, 0,
   2610 					&rdata, &newtuple));
   2611 				CHECK(do_one_tuple(&newtuple, db, ver, diff));
   2612 			}
   2613 
   2614 			/*
   2615 			 * Remove any existing CREATE request to add an
   2616 			 * otherwise identical chain with a reversed
   2617 			 * OPTOUT state.
   2618 			 */
   2619 			buf[2] ^= DNS_NSEC3FLAG_OPTOUT;
   2620 			CHECK(rr_exists(db, ver, name, &rdata, &flag));
   2621 
   2622 			if (flag) {
   2623 				CHECK(dns_difftuple_create(
   2624 					diff->mctx, DNS_DIFFOP_DEL, name, 0,
   2625 					&rdata, &newtuple));
   2626 				CHECK(do_one_tuple(&newtuple, db, ver, diff));
   2627 			}
   2628 
   2629 			/*
   2630 			 * Find the next tuple to be processed and remove the
   2631 			 * temporary add record.
   2632 			 */
   2633 			next = ISC_LIST_NEXT(tuple, link);
   2634 			CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
   2635 						   name, ttl, &tuple->rdata,
   2636 						   &newtuple));
   2637 			CHECK(do_one_tuple(&newtuple, db, ver, diff));
   2638 			ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
   2639 			dns_diff_appendminimal(diff, &tuple);
   2640 			dns_rdata_reset(&rdata);
   2641 		} else {
   2642 			next = ISC_LIST_NEXT(tuple, link);
   2643 		}
   2644 	}
   2645 
   2646 	for (tuple = ISC_LIST_HEAD(temp_diff.tuples); tuple != NULL;
   2647 	     tuple = next)
   2648 	{
   2649 		INSIST(ttl_good);
   2650 
   2651 		next = ISC_LIST_NEXT(tuple, link);
   2652 		/*
   2653 		 * See if we already have a REMOVE request in progress.
   2654 		 */
   2655 		dns_nsec3param_toprivate(&tuple->rdata, &rdata, privatetype,
   2656 					 buf, sizeof(buf));
   2657 
   2658 		buf[2] |= DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC;
   2659 
   2660 		CHECK(rr_exists(db, ver, name, &rdata, &flag));
   2661 		if (!flag) {
   2662 			buf[2] &= ~DNS_NSEC3FLAG_NONSEC;
   2663 			CHECK(rr_exists(db, ver, name, &rdata, &flag));
   2664 		}
   2665 
   2666 		if (!flag) {
   2667 			CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
   2668 						   name, 0, &rdata, &newtuple));
   2669 			CHECK(do_one_tuple(&newtuple, db, ver, diff));
   2670 		}
   2671 		CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
   2672 					   ttl, &tuple->rdata, &newtuple));
   2673 		CHECK(do_one_tuple(&newtuple, db, ver, diff));
   2674 		ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
   2675 		dns_diff_appendminimal(diff, &tuple);
   2676 		dns_rdata_reset(&rdata);
   2677 	}
   2678 
   2679 	result = ISC_R_SUCCESS;
   2680 cleanup:
   2681 	dns_diff_clear(&temp_diff);
   2682 	return result;
   2683 }
   2684 
   2685 static isc_result_t
   2686 rollback_private(dns_db_t *db, dns_rdatatype_t privatetype,
   2687 		 dns_dbversion_t *ver, dns_diff_t *diff) {
   2688 	dns_diff_t temp_diff;
   2689 	dns_diffop_t op;
   2690 	dns_difftuple_t *tuple, *newtuple = NULL, *next;
   2691 	dns_name_t *name = dns_db_origin(db);
   2692 	isc_mem_t *mctx = diff->mctx;
   2693 	isc_result_t result;
   2694 
   2695 	if (privatetype == 0) {
   2696 		return ISC_R_SUCCESS;
   2697 	}
   2698 
   2699 	dns_diff_init(mctx, &temp_diff);
   2700 
   2701 	/*
   2702 	 * Extract the changes to be rolled back.
   2703 	 */
   2704 	for (tuple = ISC_LIST_HEAD(diff->tuples); tuple != NULL; tuple = next) {
   2705 		next = ISC_LIST_NEXT(tuple, link);
   2706 
   2707 		if (tuple->rdata.type != privatetype ||
   2708 		    !dns_name_equal(name, &tuple->name))
   2709 		{
   2710 			continue;
   2711 		}
   2712 
   2713 		/*
   2714 		 * Allow records which indicate that a zone has been
   2715 		 * signed with a DNSKEY to be removed.
   2716 		 */
   2717 		if (tuple->op == DNS_DIFFOP_DEL && tuple->rdata.length == 5 &&
   2718 		    tuple->rdata.data[0] != 0 && tuple->rdata.data[4] != 0)
   2719 		{
   2720 			continue;
   2721 		}
   2722 
   2723 		ISC_LIST_UNLINK(diff->tuples, tuple, link);
   2724 		ISC_LIST_PREPEND(temp_diff.tuples, tuple, link);
   2725 	}
   2726 
   2727 	/*
   2728 	 * Rollback the changes.
   2729 	 */
   2730 	while ((tuple = ISC_LIST_HEAD(temp_diff.tuples)) != NULL) {
   2731 		op = (tuple->op == DNS_DIFFOP_DEL) ? DNS_DIFFOP_ADD
   2732 						   : DNS_DIFFOP_DEL;
   2733 		CHECK(dns_difftuple_create(mctx, op, name, tuple->ttl,
   2734 					   &tuple->rdata, &newtuple));
   2735 		CHECK(do_one_tuple(&newtuple, db, ver, &temp_diff));
   2736 	}
   2737 	result = ISC_R_SUCCESS;
   2738 
   2739 cleanup:
   2740 	dns_diff_clear(&temp_diff);
   2741 	return result;
   2742 }
   2743 
   2744 static bool
   2745 isdnssec(dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_t privatetype) {
   2746 	isc_result_t result;
   2747 	bool build_nsec, build_nsec3;
   2748 
   2749 	if (dns_db_issecure(db)) {
   2750 		return true;
   2751 	}
   2752 
   2753 	result = dns_private_chains(db, ver, privatetype, &build_nsec,
   2754 				    &build_nsec3);
   2755 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   2756 	return build_nsec || build_nsec3;
   2757 }
   2758 
   2759 static void
   2760 update_action(void *arg) {
   2761 	update_t *uev = (update_t *)arg;
   2762 	dns_zone_t *zone = uev->zone;
   2763 	ns_client_t *client = uev->client;
   2764 	dns_ssutable_t *ssutable = uev->ssutable;
   2765 	unsigned int *maxbytype = uev->maxbytype;
   2766 	size_t update = 0, maxbytypelen = uev->maxbytypelen;
   2767 	isc_result_t result;
   2768 	dns_db_t *db = NULL;
   2769 	dns_dbversion_t *oldver = NULL;
   2770 	dns_dbversion_t *ver = NULL;
   2771 	dns_diff_t diff; /* Pending updates. */
   2772 	dns_diff_t temp; /* Pending RR existence assertions. */
   2773 	bool soa_serial_changed = false;
   2774 	isc_mem_t *mctx = client->manager->mctx;
   2775 	dns_rdatatype_t covers;
   2776 	dns_message_t *request = client->message;
   2777 	dns_name_t *zonename = NULL;
   2778 	dns_fixedname_t tmpnamefixed;
   2779 	dns_name_t *tmpname = NULL;
   2780 	dns_zoneopt_t options;
   2781 	bool had_dnskey;
   2782 	dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
   2783 	dns_ttl_t maxttl = 0;
   2784 	uint32_t maxrecords;
   2785 	uint64_t records;
   2786 	bool is_inline, is_maintain, is_signing;
   2787 
   2788 	dns_diff_init(mctx, &diff);
   2789 	dns_diff_init(mctx, &temp);
   2790 
   2791 	CHECK(dns_zone_getdb(zone, &db));
   2792 	zonename = dns_db_origin(db);
   2793 	options = dns_zone_getoptions(zone);
   2794 
   2795 	INSIST(dns_zone_getclass(zone) == dns_rdataclass_in);
   2796 
   2797 	is_inline = (!dns_zone_israw(zone) && dns_zone_issecure(zone));
   2798 	is_maintain = ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0);
   2799 	is_signing = is_inline || (!is_inline && is_maintain);
   2800 
   2801 	/*
   2802 	 * Get old and new versions now that queryacl has been checked.
   2803 	 */
   2804 	dns_db_currentversion(db, &oldver);
   2805 	CHECK(dns_db_newversion(db, &ver));
   2806 
   2807 	/*
   2808 	 * Check prerequisites.
   2809 	 */
   2810 
   2811 	for (result = dns_message_firstname(request, DNS_SECTION_PREREQUISITE);
   2812 	     result == ISC_R_SUCCESS;
   2813 	     result = dns_message_nextname(request, DNS_SECTION_PREREQUISITE))
   2814 	{
   2815 		dns_name_t *name = NULL;
   2816 		dns_rdata_t rdata = DNS_RDATA_INIT;
   2817 		dns_ttl_t ttl;
   2818 		dns_rdataclass_t update_class;
   2819 		bool flag;
   2820 
   2821 		get_current_rr(request, DNS_SECTION_PREREQUISITE, &name, &rdata,
   2822 			       &covers, &ttl, &update_class);
   2823 
   2824 		if (ttl != 0) {
   2825 			PREREQFAILC(DNS_R_FORMERR,
   2826 				    "prerequisite TTL is not zero");
   2827 		}
   2828 
   2829 		if (!dns_name_issubdomain(name, zonename)) {
   2830 			PREREQFAILN(DNS_R_NOTZONE, name,
   2831 				    "prerequisite name is out of zone");
   2832 		}
   2833 
   2834 		if (update_class == dns_rdataclass_any) {
   2835 			if (rdata.length != 0) {
   2836 				PREREQFAILC(DNS_R_FORMERR,
   2837 					    "class ANY prerequisite RDATA is "
   2838 					    "not empty");
   2839 			}
   2840 			if (rdata.type == dns_rdatatype_any) {
   2841 				CHECK(name_exists(db, ver, name, &flag));
   2842 				if (!flag) {
   2843 					PREREQFAILN(
   2844 						DNS_R_NXDOMAIN, name,
   2845 						"'name in use' prerequisite "
   2846 						"not satisfied");
   2847 				}
   2848 			} else {
   2849 				CHECK(rrset_exists(db, ver, name, rdata.type,
   2850 						   covers, &flag));
   2851 				if (!flag) {
   2852 					/* RRset does not exist. */
   2853 					PREREQFAILNT(
   2854 						DNS_R_NXRRSET, name, rdata.type,
   2855 						"'rrset exists (value "
   2856 						"independent)' prerequisite "
   2857 						"not satisfied");
   2858 				}
   2859 			}
   2860 		} else if (update_class == dns_rdataclass_none) {
   2861 			if (rdata.length != 0) {
   2862 				PREREQFAILC(DNS_R_FORMERR,
   2863 					    "class NONE prerequisite RDATA is "
   2864 					    "not empty");
   2865 			}
   2866 			if (rdata.type == dns_rdatatype_any) {
   2867 				CHECK(name_exists(db, ver, name, &flag));
   2868 				if (flag) {
   2869 					PREREQFAILN(
   2870 						DNS_R_YXDOMAIN, name,
   2871 						"'name not in use' "
   2872 						"prerequisite not satisfied");
   2873 				}
   2874 			} else {
   2875 				CHECK(rrset_exists(db, ver, name, rdata.type,
   2876 						   covers, &flag));
   2877 				if (flag) {
   2878 					/* RRset exists. */
   2879 					PREREQFAILNT(
   2880 						DNS_R_YXRRSET, name, rdata.type,
   2881 						"'rrset does not exist' "
   2882 						"prerequisite not satisfied");
   2883 				}
   2884 			}
   2885 		} else if (update_class == dns_rdataclass_in) {
   2886 			/* "temp<rr.name, rr.type> += rr;" */
   2887 			result = temp_append(&temp, name, &rdata);
   2888 			if (result != ISC_R_SUCCESS) {
   2889 				UNEXPECTED_ERROR(
   2890 					"temp entry creation failed: %s",
   2891 					isc_result_totext(result));
   2892 				CHECK(ISC_R_UNEXPECTED);
   2893 			}
   2894 		} else {
   2895 			PREREQFAILC(DNS_R_FORMERR, "malformed prerequisite");
   2896 		}
   2897 	}
   2898 	if (result != ISC_R_NOMORE) {
   2899 		CHECK(result);
   2900 	}
   2901 
   2902 	/*
   2903 	 * Perform the final check of the "rrset exists (value dependent)"
   2904 	 * prerequisites.
   2905 	 */
   2906 	if (ISC_LIST_HEAD(temp.tuples) != NULL) {
   2907 		dns_rdatatype_t type;
   2908 
   2909 		/*
   2910 		 * Sort the prerequisite records by owner name,
   2911 		 * type, and rdata.
   2912 		 */
   2913 		result = dns_diff_sort(&temp, temp_order);
   2914 		if (result != ISC_R_SUCCESS) {
   2915 			FAILC(result, "'RRset exists (value dependent)' "
   2916 				      "prerequisite not satisfied");
   2917 		}
   2918 
   2919 		tmpname = dns_fixedname_initname(&tmpnamefixed);
   2920 		result = temp_check(mctx, &temp, db, ver, tmpname, &type);
   2921 		if (result != ISC_R_SUCCESS) {
   2922 			FAILNT(result, tmpname, type,
   2923 			       "'RRset exists (value dependent)' prerequisite "
   2924 			       "not satisfied");
   2925 		}
   2926 	}
   2927 
   2928 	update_log(client, zone, LOGLEVEL_DEBUG, "prerequisites are OK");
   2929 
   2930 	/*
   2931 	 * Process the Update Section.
   2932 	 */
   2933 	INSIST(ssutable == NULL || maxbytype != NULL);
   2934 	for (update = 0,
   2935 	    result = dns_message_firstname(request, DNS_SECTION_UPDATE);
   2936 	     result == ISC_R_SUCCESS; update++,
   2937 	    result = dns_message_nextname(request, DNS_SECTION_UPDATE))
   2938 	{
   2939 		dns_name_t *name = NULL;
   2940 		dns_rdata_t rdata = DNS_RDATA_INIT;
   2941 		dns_ttl_t ttl;
   2942 		dns_rdataclass_t update_class;
   2943 		bool flag;
   2944 
   2945 		INSIST(ssutable == NULL || update < maxbytypelen);
   2946 
   2947 		get_current_rr(request, DNS_SECTION_UPDATE, &name, &rdata,
   2948 			       &covers, &ttl, &update_class);
   2949 
   2950 		if (update_class == dns_rdataclass_in) {
   2951 			/*
   2952 			 * RFC1123 doesn't allow MF and MD in master files.
   2953 			 */
   2954 			if (rdata.type == dns_rdatatype_md ||
   2955 			    rdata.type == dns_rdatatype_mf)
   2956 			{
   2957 				char typebuf[DNS_RDATATYPE_FORMATSIZE];
   2958 
   2959 				dns_rdatatype_format(rdata.type, typebuf,
   2960 						     sizeof(typebuf));
   2961 				update_log(client, zone, LOGLEVEL_PROTOCOL,
   2962 					   "attempt to add %s ignored",
   2963 					   typebuf);
   2964 				continue;
   2965 			}
   2966 			if ((rdata.type == dns_rdatatype_ns ||
   2967 			     rdata.type == dns_rdatatype_dname) &&
   2968 			    dns_name_iswildcard(name))
   2969 			{
   2970 				char typebuf[DNS_RDATATYPE_FORMATSIZE];
   2971 
   2972 				dns_rdatatype_format(rdata.type, typebuf,
   2973 						     sizeof(typebuf));
   2974 				update_log(client, zone, LOGLEVEL_PROTOCOL,
   2975 					   "attempt to add wildcard %s record "
   2976 					   "ignored",
   2977 					   typebuf);
   2978 				continue;
   2979 			}
   2980 			if (rdata.type == dns_rdatatype_cname) {
   2981 				CHECK(cname_incompatible_rrset_exists(
   2982 					db, ver, name, &flag));
   2983 				if (flag) {
   2984 					update_log(
   2985 						client, zone, LOGLEVEL_PROTOCOL,
   2986 						"attempt to add CNAME "
   2987 						"alongside non-CNAME ignored");
   2988 					continue;
   2989 				}
   2990 			} else {
   2991 				CHECK(rrset_exists(db, ver, name,
   2992 						   dns_rdatatype_cname, 0,
   2993 						   &flag));
   2994 				if (flag && !dns_rdatatype_atcname(rdata.type))
   2995 				{
   2996 					update_log(client, zone,
   2997 						   LOGLEVEL_PROTOCOL,
   2998 						   "attempt to add non-CNAME "
   2999 						   "alongside CNAME ignored");
   3000 					continue;
   3001 				}
   3002 			}
   3003 			if (rdata.type == dns_rdatatype_soa) {
   3004 				bool ok;
   3005 				CHECK(rrset_exists(db, ver, name,
   3006 						   dns_rdatatype_soa, 0,
   3007 						   &flag));
   3008 				if (!flag) {
   3009 					update_log(client, zone,
   3010 						   LOGLEVEL_PROTOCOL,
   3011 						   "attempt to create 2nd SOA "
   3012 						   "ignored");
   3013 					continue;
   3014 				}
   3015 				CHECK(check_soa_increment(db, ver, &rdata,
   3016 							  &ok));
   3017 				if (!ok) {
   3018 					update_log(client, zone,
   3019 						   LOGLEVEL_PROTOCOL,
   3020 						   "SOA update failed to "
   3021 						   "increment serial, ignoring "
   3022 						   "it");
   3023 					continue;
   3024 				}
   3025 				soa_serial_changed = true;
   3026 			}
   3027 
   3028 			if (dns_rdatatype_atparent(rdata.type) &&
   3029 			    dns_name_equal(name, zonename))
   3030 			{
   3031 				char typebuf[DNS_RDATATYPE_FORMATSIZE];
   3032 
   3033 				dns_rdatatype_format(rdata.type, typebuf,
   3034 						     sizeof(typebuf));
   3035 				update_log(client, zone, LOGLEVEL_PROTOCOL,
   3036 					   "attempt to add a %s record at zone "
   3037 					   "apex ignored",
   3038 					   typebuf);
   3039 				continue;
   3040 			}
   3041 
   3042 			if (rdata.type == privatetype) {
   3043 				update_log(client, zone, LOGLEVEL_PROTOCOL,
   3044 					   "attempt to add a private type (%u) "
   3045 					   "record rejected internal use only",
   3046 					   privatetype);
   3047 				continue;
   3048 			}
   3049 
   3050 			if (rdata.type == dns_rdatatype_nsec3param) {
   3051 				/*
   3052 				 * Ignore attempts to add NSEC3PARAM records
   3053 				 * with any flags other than OPTOUT.
   3054 				 */
   3055 				if ((rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) !=
   3056 				    0)
   3057 				{
   3058 					update_log(
   3059 						client, zone, LOGLEVEL_PROTOCOL,
   3060 						"attempt to add NSEC3PARAM "
   3061 						"record with non OPTOUT flag");
   3062 					continue;
   3063 				}
   3064 			}
   3065 
   3066 			if ((options & DNS_ZONEOPT_CHECKWILDCARD) != 0 &&
   3067 			    dns_name_internalwildcard(name))
   3068 			{
   3069 				char namestr[DNS_NAME_FORMATSIZE];
   3070 				dns_name_format(name, namestr, sizeof(namestr));
   3071 				update_log(client, zone, LOGLEVEL_PROTOCOL,
   3072 					   "warning: ownername '%s' contains a "
   3073 					   "non-terminal wildcard",
   3074 					   namestr);
   3075 			}
   3076 
   3077 			if ((options & DNS_ZONEOPT_CHECKTTL) != 0) {
   3078 				maxttl = dns_zone_getmaxttl(zone);
   3079 				if (ttl > maxttl) {
   3080 					ttl = maxttl;
   3081 					update_log(client, zone,
   3082 						   LOGLEVEL_PROTOCOL,
   3083 						   "reducing TTL to the "
   3084 						   "configured max-zone-ttl %d",
   3085 						   maxttl);
   3086 				}
   3087 			}
   3088 
   3089 			if (maxbytype != NULL && maxbytype[update] != 0) {
   3090 				unsigned int count = 0;
   3091 				CHECK(foreach_rr(db, ver, name, rdata.type,
   3092 						 covers, count_action, &count));
   3093 				if (count >= maxbytype[update]) {
   3094 					update_log(client, zone,
   3095 						   LOGLEVEL_PROTOCOL,
   3096 						   "attempt to add more "
   3097 						   "records than permitted by "
   3098 						   "policy max=%u",
   3099 						   maxbytype[update]);
   3100 					continue;
   3101 				}
   3102 			}
   3103 
   3104 			if (isc_log_wouldlog(ns_lctx, LOGLEVEL_PROTOCOL)) {
   3105 				char namestr[DNS_NAME_FORMATSIZE];
   3106 				char typestr[DNS_RDATATYPE_FORMATSIZE];
   3107 				char rdstr[2048];
   3108 				isc_buffer_t buf;
   3109 				int len = 0;
   3110 				const char *truncated = "";
   3111 
   3112 				dns_name_format(name, namestr, sizeof(namestr));
   3113 				dns_rdatatype_format(rdata.type, typestr,
   3114 						     sizeof(typestr));
   3115 				isc_buffer_init(&buf, rdstr, sizeof(rdstr));
   3116 				result = dns_rdata_totext(&rdata, NULL, &buf);
   3117 				if (result == ISC_R_NOSPACE) {
   3118 					len = (int)isc_buffer_usedlength(&buf);
   3119 					truncated = " [TRUNCATED]";
   3120 				} else if (result != ISC_R_SUCCESS) {
   3121 					snprintf(
   3122 						rdstr, sizeof(rdstr),
   3123 						"[dns_rdata_totext failed: %s]",
   3124 						isc_result_totext(result));
   3125 					len = strlen(rdstr);
   3126 				} else {
   3127 					len = (int)isc_buffer_usedlength(&buf);
   3128 				}
   3129 				update_log(client, zone, LOGLEVEL_PROTOCOL,
   3130 					   "adding an RR at '%s' %s %.*s%s",
   3131 					   namestr, typestr, len, rdstr,
   3132 					   truncated);
   3133 			}
   3134 
   3135 			/* Prepare the affected RRset for the addition. */
   3136 			{
   3137 				add_rr_prepare_ctx_t ctx;
   3138 				ctx.zone = zone;
   3139 				ctx.db = db;
   3140 				ctx.ver = ver;
   3141 				ctx.diff = &diff;
   3142 				ctx.name = name;
   3143 				ctx.oldname = name;
   3144 				ctx.update_rr = &rdata;
   3145 				ctx.update_rr_ttl = ttl;
   3146 				ctx.ignore_add = false;
   3147 				dns_diff_init(mctx, &ctx.del_diff);
   3148 				dns_diff_init(mctx, &ctx.add_diff);
   3149 				CHECK(foreach_rr(db, ver, name, rdata.type,
   3150 						 covers, add_rr_prepare_action,
   3151 						 &ctx));
   3152 
   3153 				if (ctx.ignore_add) {
   3154 					dns_diff_clear(&ctx.del_diff);
   3155 					dns_diff_clear(&ctx.add_diff);
   3156 				} else {
   3157 					result = do_diff(&ctx.del_diff, db, ver,
   3158 							 &diff);
   3159 					if (result == ISC_R_SUCCESS) {
   3160 						result = do_diff(&ctx.add_diff,
   3161 								 db, ver,
   3162 								 &diff);
   3163 					}
   3164 					if (result != ISC_R_SUCCESS) {
   3165 						dns_diff_clear(&ctx.del_diff);
   3166 						dns_diff_clear(&ctx.add_diff);
   3167 						goto cleanup;
   3168 					}
   3169 					result = update_one_rr(
   3170 						db, ver, &diff, DNS_DIFFOP_ADD,
   3171 						name, ttl, &rdata);
   3172 					if (result != ISC_R_SUCCESS) {
   3173 						update_log(client, zone,
   3174 							   LOGLEVEL_PROTOCOL,
   3175 							   "adding an RR "
   3176 							   "failed: %s",
   3177 							   isc_result_totext(
   3178 								   result));
   3179 						goto cleanup;
   3180 					}
   3181 				}
   3182 			}
   3183 		} else if (update_class == dns_rdataclass_any) {
   3184 			if (rdata.type == dns_rdatatype_any) {
   3185 				if (isc_log_wouldlog(ns_lctx,
   3186 						     LOGLEVEL_PROTOCOL))
   3187 				{
   3188 					char namestr[DNS_NAME_FORMATSIZE];
   3189 					dns_name_format(name, namestr,
   3190 							sizeof(namestr));
   3191 					update_log(client, zone,
   3192 						   LOGLEVEL_PROTOCOL,
   3193 						   "delete all rrsets from "
   3194 						   "name '%s'",
   3195 						   namestr);
   3196 				}
   3197 				if (dns_name_equal(name, zonename)) {
   3198 					CHECK(delete_if(
   3199 						apex_special_processing_any,
   3200 						zone, db, ver, name,
   3201 						dns_rdatatype_any,
   3202 						dns_rdatatype_none, &rdata,
   3203 						&diff));
   3204 				} else {
   3205 					CHECK(delete_if(type_not_dnssec, zone,
   3206 							db, ver, name,
   3207 							dns_rdatatype_any, 0,
   3208 							&rdata, &diff));
   3209 				}
   3210 			} else if (dns_name_equal(name, zonename)) {
   3211 				CHECK(delete_if(
   3212 					apex_special_processing, zone, db, ver,
   3213 					name, dns_rdatatype_any,
   3214 					dns_rdatatype_none, &rdata, &diff));
   3215 			} else {
   3216 				if (isc_log_wouldlog(ns_lctx,
   3217 						     LOGLEVEL_PROTOCOL))
   3218 				{
   3219 					char namestr[DNS_NAME_FORMATSIZE];
   3220 					char typestr[DNS_RDATATYPE_FORMATSIZE];
   3221 					dns_name_format(name, namestr,
   3222 							sizeof(namestr));
   3223 					dns_rdatatype_format(rdata.type,
   3224 							     typestr,
   3225 							     sizeof(typestr));
   3226 					update_log(client, zone,
   3227 						   LOGLEVEL_PROTOCOL,
   3228 						   "deleting rrset at '%s' %s",
   3229 						   namestr, typestr);
   3230 				}
   3231 				CHECK(delete_if(true_p, zone, db, ver, name,
   3232 						rdata.type, covers, &rdata,
   3233 						&diff));
   3234 			}
   3235 		} else if (update_class == dns_rdataclass_none) {
   3236 			char namestr[DNS_NAME_FORMATSIZE];
   3237 			char typestr[DNS_RDATATYPE_FORMATSIZE];
   3238 
   3239 			/*
   3240 			 * The (name == zonename) condition appears in
   3241 			 * RFC2136 3.4.2.4 but is missing from the pseudocode.
   3242 			 */
   3243 			if (dns_name_equal(name, zonename)) {
   3244 				if (rdata.type == dns_rdatatype_soa) {
   3245 					update_log(client, zone,
   3246 						   LOGLEVEL_PROTOCOL,
   3247 						   "attempt to delete SOA "
   3248 						   "ignored");
   3249 					continue;
   3250 				}
   3251 				if (rdata.type == dns_rdatatype_ns) {
   3252 					int count;
   3253 					CHECK(rr_count(db, ver, name,
   3254 						       dns_rdatatype_ns, 0,
   3255 						       &count));
   3256 					if (count == 1) {
   3257 						update_log(client, zone,
   3258 							   LOGLEVEL_PROTOCOL,
   3259 							   "attempt to delete "
   3260 							   "last NS ignored");
   3261 						continue;
   3262 					}
   3263 				}
   3264 				/*
   3265 				 * Don't remove DNSKEY, CDNSKEY, CDS records
   3266 				 * that are in use (under our control).
   3267 				 */
   3268 				if (dns_rdatatype_iskeymaterial(rdata.type)) {
   3269 					bool inuse = false;
   3270 					CHECK(dns_zone_dnskey_inuse(
   3271 						zone, &rdata, &inuse));
   3272 					if (inuse) {
   3273 						char typebuf
   3274 							[DNS_RDATATYPE_FORMATSIZE];
   3275 
   3276 						dns_rdatatype_format(
   3277 							rdata.type, typebuf,
   3278 							sizeof(typebuf));
   3279 						update_log(client, zone,
   3280 							   LOGLEVEL_PROTOCOL,
   3281 							   "attempt to delete "
   3282 							   "in use %s ignored",
   3283 							   typebuf);
   3284 						continue;
   3285 					}
   3286 				}
   3287 			}
   3288 			dns_name_format(name, namestr, sizeof(namestr));
   3289 			dns_rdatatype_format(rdata.type, typestr,
   3290 					     sizeof(typestr));
   3291 			update_log(client, zone, LOGLEVEL_PROTOCOL,
   3292 				   "deleting an RR at %s %s", namestr, typestr);
   3293 			CHECK(delete_if(rr_equal_p, zone, db, ver, name,
   3294 					rdata.type, covers, &rdata, &diff));
   3295 		}
   3296 	}
   3297 	if (result != ISC_R_NOMORE) {
   3298 		CHECK(result);
   3299 	}
   3300 
   3301 	/*
   3302 	 * Check that any changes to DNSKEY/NSEC3PARAM records make sense.
   3303 	 * If they don't then back out all changes to DNSKEY/NSEC3PARAM
   3304 	 * records.
   3305 	 */
   3306 	if (!ISC_LIST_EMPTY(diff.tuples)) {
   3307 		CHECK(check_dnssec(client, zone, db, ver, &diff));
   3308 	}
   3309 
   3310 	if (!ISC_LIST_EMPTY(diff.tuples)) {
   3311 		unsigned int errors = 0;
   3312 		CHECK(dns_zone_nscheck(zone, db, ver, &errors));
   3313 		if (errors != 0) {
   3314 			update_log(client, zone, LOGLEVEL_PROTOCOL,
   3315 				   "update rejected: post update name server "
   3316 				   "sanity check failed");
   3317 			CHECK(DNS_R_REFUSED);
   3318 		}
   3319 	}
   3320 	if (!ISC_LIST_EMPTY(diff.tuples) && is_signing) {
   3321 		result = dns_zone_cdscheck(zone, db, ver);
   3322 		if (result == DNS_R_BADCDS || result == DNS_R_BADCDNSKEY) {
   3323 			update_log(client, zone, LOGLEVEL_PROTOCOL,
   3324 				   "update rejected: bad %s RRset",
   3325 				   result == DNS_R_BADCDS ? "CDS" : "CDNSKEY");
   3326 			CHECK(DNS_R_REFUSED);
   3327 		}
   3328 		CHECK(result);
   3329 	}
   3330 
   3331 	/*
   3332 	 * If any changes were made, increment the SOA serial number,
   3333 	 * update RRSIGs and NSECs (if zone is secure), and write the update
   3334 	 * to the journal.
   3335 	 */
   3336 	if (!ISC_LIST_EMPTY(diff.tuples)) {
   3337 		char *journalfile;
   3338 		dns_journal_t *journal;
   3339 		bool has_dnskey;
   3340 
   3341 		/*
   3342 		 * Increment the SOA serial, but only if it was not
   3343 		 * changed as a result of an update operation.
   3344 		 */
   3345 		if (!soa_serial_changed) {
   3346 			CHECK(update_soa_serial(
   3347 				db, ver, &diff, mctx,
   3348 				dns_zone_getserialupdatemethod(zone)));
   3349 		}
   3350 
   3351 		CHECK(check_mx(client, zone, db, ver, &diff));
   3352 
   3353 		CHECK(remove_orphaned_ds(zone, db, ver, &diff));
   3354 
   3355 		CHECK(rrset_exists(db, ver, zonename, dns_rdatatype_dnskey, 0,
   3356 				   &has_dnskey));
   3357 
   3358 		CHECK(rrset_exists(db, oldver, zonename, dns_rdatatype_dnskey,
   3359 				   0, &had_dnskey));
   3360 
   3361 		CHECK(rollback_private(db, privatetype, ver, &diff));
   3362 
   3363 		CHECK(add_nsec3param_records(client, zone, db, ver, &diff));
   3364 
   3365 		if (is_signing && had_dnskey && !has_dnskey) {
   3366 			/*
   3367 			 * We are transitioning from secure to insecure.
   3368 			 * Cause all NSEC3 chains to be deleted.  When the
   3369 			 * the last signature for the DNSKEY records are
   3370 			 * remove any NSEC chain present will also be removed.
   3371 			 */
   3372 			CHECK(dns_nsec3param_deletechains(db, ver, zone, true,
   3373 							  &diff));
   3374 		} else if (has_dnskey && isdnssec(db, ver, privatetype)) {
   3375 			dns_update_log_t log;
   3376 			uint32_t interval =
   3377 				dns_zone_getsigvalidityinterval(zone);
   3378 
   3379 			log.func = update_log_cb;
   3380 			log.arg = client;
   3381 			result = dns_update_signatures(&log, zone, db, oldver,
   3382 						       ver, &diff, interval);
   3383 
   3384 			if (result != ISC_R_SUCCESS) {
   3385 				update_log(client, zone, ISC_LOG_ERROR,
   3386 					   "RRSIG/NSEC/NSEC3 update failed: %s",
   3387 					   isc_result_totext(result));
   3388 				goto cleanup;
   3389 			}
   3390 		}
   3391 
   3392 		maxrecords = dns_zone_getmaxrecords(zone);
   3393 		if (maxrecords != 0U) {
   3394 			result = dns_db_getsize(db, ver, &records, NULL);
   3395 			if (result == ISC_R_SUCCESS && records > maxrecords) {
   3396 				update_log(client, zone, ISC_LOG_ERROR,
   3397 					   "records in zone (%" PRIu64
   3398 					   ") exceeds max-records (%u)",
   3399 					   records, maxrecords);
   3400 				CHECK(DNS_R_TOOMANYRECORDS);
   3401 			}
   3402 		}
   3403 
   3404 		journalfile = dns_zone_getjournal(zone);
   3405 		if (journalfile != NULL) {
   3406 			update_log(client, zone, LOGLEVEL_DEBUG,
   3407 				   "writing journal %s", journalfile);
   3408 
   3409 			journal = NULL;
   3410 			result = dns_journal_open(mctx, journalfile,
   3411 						  DNS_JOURNAL_CREATE, &journal);
   3412 			if (result != ISC_R_SUCCESS) {
   3413 				FAILS(result, "journal open failed");
   3414 			}
   3415 
   3416 			result = dns_journal_write_transaction(journal, &diff);
   3417 			if (result != ISC_R_SUCCESS) {
   3418 				dns_journal_destroy(&journal);
   3419 				FAILS(result, "journal write failed");
   3420 			}
   3421 
   3422 			dns_journal_destroy(&journal);
   3423 		}
   3424 
   3425 		/*
   3426 		 * XXXRTH  Just a note that this committing code will have
   3427 		 *	   to change to handle databases that need two-phase
   3428 		 *	   commit, but this isn't a priority.
   3429 		 */
   3430 		update_log(client, zone, LOGLEVEL_DEBUG,
   3431 			   "committing update transaction");
   3432 
   3433 		dns_db_closeversion(db, &ver, true);
   3434 
   3435 		/*
   3436 		 * Mark the zone as dirty so that it will be written to disk.
   3437 		 */
   3438 		dns_zone_markdirty(zone);
   3439 
   3440 		/*
   3441 		 * Notify secondaries of the change we just made.
   3442 		 */
   3443 		dns_zone_notify(zone, false);
   3444 	} else {
   3445 		update_log(client, zone, LOGLEVEL_DEBUG, "redundant request");
   3446 		dns_db_closeversion(db, &ver, true);
   3447 	}
   3448 	result = ISC_R_SUCCESS;
   3449 	goto common;
   3450 
   3451 cleanup:
   3452 	/*
   3453 	 * The reason for failure should have been logged at this point.
   3454 	 */
   3455 	if (ver != NULL) {
   3456 		update_log(client, zone, LOGLEVEL_DEBUG, "rolling back");
   3457 		dns_db_closeversion(db, &ver, false);
   3458 	}
   3459 
   3460 common:
   3461 	dns_diff_clear(&temp);
   3462 	dns_diff_clear(&diff);
   3463 
   3464 	if (oldver != NULL) {
   3465 		dns_db_closeversion(db, &oldver, false);
   3466 	}
   3467 
   3468 	if (db != NULL) {
   3469 		dns_db_detach(&db);
   3470 	}
   3471 
   3472 	if (maxbytype != NULL) {
   3473 		isc_mem_cput(mctx, maxbytype, maxbytypelen, sizeof(*maxbytype));
   3474 	}
   3475 
   3476 	if (ssutable != NULL) {
   3477 		dns_ssutable_detach(&ssutable);
   3478 	}
   3479 
   3480 	uev->result = result;
   3481 	if (zone != NULL) {
   3482 		INSIST(uev->zone == zone); /* we use this later */
   3483 	}
   3484 
   3485 	isc_async_run(client->manager->loop, updatedone_action, uev);
   3486 	INSIST(ver == NULL);
   3487 }
   3488 
   3489 static void
   3490 updatedone_action(void *arg) {
   3491 	update_t *uev = (update_t *)arg;
   3492 	ns_client_t *client = uev->client;
   3493 
   3494 	REQUIRE(client->updatehandle == client->handle);
   3495 
   3496 	switch (uev->result) {
   3497 	case ISC_R_SUCCESS:
   3498 		inc_stats(client, uev->zone, ns_statscounter_updatedone);
   3499 		break;
   3500 	case DNS_R_REFUSED:
   3501 		inc_stats(client, uev->zone, ns_statscounter_updaterej);
   3502 		break;
   3503 	default:
   3504 		inc_stats(client, uev->zone, ns_statscounter_updatefail);
   3505 		break;
   3506 	}
   3507 
   3508 	respond(client, uev->result);
   3509 
   3510 	isc_quota_release(&client->manager->sctx->updquota);
   3511 	if (uev->zone != NULL) {
   3512 		dns_zone_detach(&uev->zone);
   3513 	}
   3514 	isc_mem_put(client->manager->mctx, uev, sizeof(*uev));
   3515 	isc_nmhandle_detach(&client->updatehandle);
   3516 }
   3517 
   3518 /*%
   3519  * Update forwarding support.
   3520  */
   3521 static void
   3522 forward_fail(void *arg) {
   3523 	update_t *uev = (update_t *)arg;
   3524 	ns_client_t *client = uev->client;
   3525 
   3526 	respond(client, DNS_R_SERVFAIL);
   3527 
   3528 	isc_quota_release(&client->manager->sctx->updquota);
   3529 	isc_mem_put(client->manager->mctx, uev, sizeof(*uev));
   3530 	isc_nmhandle_detach(&client->updatehandle);
   3531 }
   3532 
   3533 static void
   3534 forward_callback(void *arg, isc_result_t result, dns_message_t *answer) {
   3535 	update_t *uev = (update_t *)arg;
   3536 	ns_client_t *client = uev->client;
   3537 	dns_zone_t *zone = uev->zone;
   3538 
   3539 	if (result != ISC_R_SUCCESS) {
   3540 		INSIST(answer == NULL);
   3541 		inc_stats(client, zone, ns_statscounter_updatefwdfail);
   3542 		isc_async_run(client->manager->loop, forward_fail, uev);
   3543 	} else {
   3544 		uev->answer = answer;
   3545 		inc_stats(client, zone, ns_statscounter_updaterespfwd);
   3546 		isc_async_run(client->manager->loop, forward_done, uev);
   3547 	}
   3548 
   3549 	dns_zone_detach(&zone);
   3550 }
   3551 
   3552 static void
   3553 forward_done(void *arg) {
   3554 	update_t *uev = (update_t *)arg;
   3555 	ns_client_t *client = uev->client;
   3556 
   3557 	ns_client_sendraw(client, uev->answer);
   3558 	dns_message_detach(&uev->answer);
   3559 
   3560 	isc_quota_release(&client->manager->sctx->updquota);
   3561 	isc_mem_put(client->manager->mctx, uev, sizeof(*uev));
   3562 	isc_nmhandle_detach(&client->reqhandle);
   3563 	isc_nmhandle_detach(&client->updatehandle);
   3564 }
   3565 
   3566 static void
   3567 forward_action(void *arg) {
   3568 	update_t *uev = (update_t *)arg;
   3569 	dns_zone_t *zone = uev->zone;
   3570 	ns_client_t *client = uev->client;
   3571 	isc_result_t result;
   3572 
   3573 	result = dns_zone_forwardupdate(zone, client->message, forward_callback,
   3574 					uev);
   3575 	if (result != ISC_R_SUCCESS) {
   3576 		isc_async_run(client->manager->loop, forward_fail, uev);
   3577 		inc_stats(client, zone, ns_statscounter_updatefwdfail);
   3578 		dns_zone_detach(&zone);
   3579 	} else {
   3580 		inc_stats(client, zone, ns_statscounter_updatereqfwd);
   3581 	}
   3582 }
   3583 
   3584 static isc_result_t
   3585 send_forward(ns_client_t *client, dns_zone_t *zone) {
   3586 	isc_result_t result = ISC_R_SUCCESS;
   3587 	char namebuf[DNS_NAME_FORMATSIZE];
   3588 	char classbuf[DNS_RDATACLASS_FORMATSIZE];
   3589 	update_t *uev = NULL;
   3590 
   3591 	result = checkupdateacl(client, dns_zone_getforwardacl(zone),
   3592 				"update forwarding", dns_zone_getorigin(zone),
   3593 				true, false);
   3594 	if (result != ISC_R_SUCCESS) {
   3595 		return result;
   3596 	}
   3597 
   3598 	result = isc_quota_acquire(&client->manager->sctx->updquota);
   3599 	if (result != ISC_R_SUCCESS) {
   3600 		if (result == ISC_R_SOFTQUOTA) {
   3601 			isc_quota_release(&client->manager->sctx->updquota);
   3602 		}
   3603 		update_log(client, zone, LOGLEVEL_PROTOCOL,
   3604 			   "update failed: too many DNS UPDATEs queued (%s)",
   3605 			   isc_result_totext(result));
   3606 		ns_stats_increment(client->manager->sctx->nsstats,
   3607 				   ns_statscounter_updatequota);
   3608 		return DNS_R_DROP;
   3609 	}
   3610 
   3611 	uev = isc_mem_get(client->manager->mctx, sizeof(*uev));
   3612 	*uev = (update_t){
   3613 		.zone = zone,
   3614 		.client = client,
   3615 		.result = ISC_R_SUCCESS,
   3616 	};
   3617 
   3618 	dns_name_format(dns_zone_getorigin(zone), namebuf, sizeof(namebuf));
   3619 	dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
   3620 			      sizeof(classbuf));
   3621 
   3622 	ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
   3623 		      LOGLEVEL_PROTOCOL, "forwarding update for zone '%s/%s'",
   3624 		      namebuf, classbuf);
   3625 
   3626 	isc_nmhandle_attach(client->handle, &client->updatehandle);
   3627 	isc_async_run(dns_zone_getloop(zone), forward_action, uev);
   3628 
   3629 	return result;
   3630 }
   3631