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