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