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