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