update.c 75.3 KB
Newer Older
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2003  Internet Software Consortium.
4
 *
Andreas Gustafsson's avatar
Andreas Gustafsson committed
5 6 7
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
Andreas Gustafsson's avatar
Andreas Gustafsson committed
16 17
 */

Mark Andrews's avatar
Mark Andrews committed
18
/* $Id: update.c,v 1.120 2005/01/10 23:43:17 marka Exp $ */
David Lawrence's avatar
David Lawrence committed
19

Andreas Gustafsson's avatar
Andreas Gustafsson committed
20 21
#include <config.h>

Andreas Gustafsson's avatar
Andreas Gustafsson committed
22
#include <isc/print.h>
23
#include <isc/string.h>
24
#include <isc/taskpool.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
25
#include <isc/util.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
26

Bob Halley's avatar
Bob Halley committed
27 28
#include <dns/db.h>
#include <dns/dbiterator.h>
29
#include <dns/diff.h>
Bob Halley's avatar
Bob Halley committed
30 31
#include <dns/dnssec.h>
#include <dns/events.h>
Brian Wellington's avatar
Brian Wellington committed
32
#include <dns/fixedname.h>
Bob Halley's avatar
Bob Halley committed
33 34
#include <dns/journal.h>
#include <dns/message.h>
35
#include <dns/nsec.h>
36
#include <dns/rdataclass.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
37 38
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
39
#include <dns/rdatatype.h>
40
#include <dns/soa.h>
41
#include <dns/ssu.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
42
#include <dns/view.h>
Bob Halley's avatar
Bob Halley committed
43 44
#include <dns/zone.h>
#include <dns/zt.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
45 46

#include <named/client.h>
47
#include <named/log.h>
48
#include <named/update.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
49 50 51 52

/*
 * This module implements dynamic update as in RFC2136.
 */
53

Andreas Gustafsson's avatar
Andreas Gustafsson committed
54 55 56 57 58
/*
  XXX TODO:
  - document strict minimality
*/

59
/**************************************************************************/
60 61

/*
62
 * Log level for tracing dynamic update protocol requests.
63
 */
64
#define LOGLEVEL_PROTOCOL	ISC_LOG_INFO
65 66

/*
67
 * Log level for low-level debug tracing.
68
 */
69
#define LOGLEVEL_DEBUG 		ISC_LOG_DEBUG(8)
70

71 72 73 74 75
/*
 * Check an operation for failure.  These macros all assume that
 * the function using them has a 'result' variable and a 'failure'
 * label.
 */
76 77
#define CHECK(op) \
	do { result = (op); 				  	 \
78
	       if (result != ISC_R_SUCCESS) goto failure; 	 \
79
	} while (0)
80 81 82

/*
 * Fail unconditionally with result 'code', which must not
83
 * be ISC_R_SUCCESS.  The reason for failure presumably has
84
 * been logged already.
85 86 87
 *
 * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
 * from complaining about "end-of-loop code not reached".
88 89 90 91 92
 */

#define FAIL(code) \
	do {							\
		result = (code);				\
93
		if (result != ISC_R_SUCCESS) goto failure;	\
94 95 96 97
	} while (0)

/*
 * Fail unconditionally and log as a client error.
98 99
 * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
 * from complaining about "end-of-loop code not reached".
100
 */
101 102
#define FAILC(code, msg) \
	do {							\
103
		const char *_what = "failed";			\
104
		result = (code);				\
105 106 107 108 109 110 111
		switch (result) {				\
		case DNS_R_NXDOMAIN:				\
		case DNS_R_YXDOMAIN:				\
		case DNS_R_YXRRSET:				\
		case DNS_R_NXRRSET:				\
			_what = "unsuccessful";			\
		}						\
112
		update_log(client, zone, LOGLEVEL_PROTOCOL,   	\
113
			      "update %s: %s (%s)", _what,	\
114
		      	      msg, isc_result_totext(result));	\
115
		if (result != ISC_R_SUCCESS) goto failure;	\
116 117
	} while (0)

118 119
#define FAILN(code, name, msg) \
	do {								\
120
		const char *_what = "failed";				\
121
		result = (code);					\
122 123 124 125 126 127 128
		switch (result) {					\
		case DNS_R_NXDOMAIN:					\
		case DNS_R_YXDOMAIN:					\
		case DNS_R_YXRRSET:					\
		case DNS_R_NXRRSET:					\
			_what = "unsuccessful";				\
		}							\
129 130 131 132
		if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) {	\
			char _nbuf[DNS_NAME_FORMATSIZE];		\
			dns_name_format(name, _nbuf, sizeof(_nbuf));	\
			update_log(client, zone, LOGLEVEL_PROTOCOL,   	\
133
				   "update %s: %s: %s (%s)", _what, _nbuf, \
134 135 136 137 138 139 140
				   msg, isc_result_totext(result));	\
		}							\
		if (result != ISC_R_SUCCESS) goto failure;		\
	} while (0)

#define FAILNT(code, name, type, msg) \
	do {								\
141
		const char *_what = "failed";				\
142
		result = (code);					\
143 144 145 146 147 148 149
		switch (result) {					\
		case DNS_R_NXDOMAIN:					\
		case DNS_R_YXDOMAIN:					\
		case DNS_R_YXRRSET:					\
		case DNS_R_NXRRSET:					\
			_what = "unsuccessful";				\
		}							\
150 151 152 153 154 155
		if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) {	\
			char _nbuf[DNS_NAME_FORMATSIZE];		\
			char _tbuf[DNS_RDATATYPE_FORMATSIZE];		\
			dns_name_format(name, _nbuf, sizeof(_nbuf));	\
			dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \
			update_log(client, zone, LOGLEVEL_PROTOCOL,   	\
156 157
				   "update %s: %s/%s: %s (%s)",		\
				   _what, _nbuf, _tbuf, msg,		\
158 159 160 161
				   isc_result_totext(result));		\
		}							\
		if (result != ISC_R_SUCCESS) goto failure;		\
	} while (0)
162 163
/*
 * Fail unconditionally and log as a server error.
164 165
 * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
 * from complaining about "end-of-loop code not reached".
166 167 168
 */
#define FAILS(code, msg) \
	do {							\
169
		result = (code);				\
170
		update_log(client, zone, LOGLEVEL_PROTOCOL,	\
171
			      "error: %s: %s", 			\
172
			      msg, isc_result_totext(result));	\
173
		if (result != ISC_R_SUCCESS) goto failure;	\
174
	} while (0)
175 176 177

/**************************************************************************/

Andreas Gustafsson's avatar
Andreas Gustafsson committed
178 179 180 181
typedef struct rr rr_t;

struct rr {
	/* dns_name_t name; */
182 183 184 185 186 187 188 189 190
	isc_uint32_t 		ttl;
	dns_rdata_t 		rdata;
};

typedef struct update_event update_event_t;

struct update_event {
	ISC_EVENT_COMMON(update_event_t);
	dns_zone_t 		*zone;
191
	isc_result_t		result;
192
	dns_message_t		*answer;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
193 194
};

195 196 197 198 199 200 201
/**************************************************************************/
/*
 * Forward declarations.
 */

static void update_action(isc_task_t *task, isc_event_t *event);
static void updatedone_action(isc_task_t *task, isc_event_t *event);
202
static isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone);
203
static void forward_done(isc_task_t *task, isc_event_t *event);
204

Andreas Gustafsson's avatar
Andreas Gustafsson committed
205
/**************************************************************************/
206

207 208 209 210
static void
update_log(ns_client_t *client, dns_zone_t *zone,
	   int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
static void
update_log(ns_client_t *client, dns_zone_t *zone,
	   int level, const char *fmt, ...)
{
	va_list ap;
	char message[4096];
	char namebuf[DNS_NAME_FORMATSIZE];
	char classbuf[DNS_RDATACLASS_FORMATSIZE];

	if (client == NULL || zone == NULL)
		return;

	if (isc_log_wouldlog(ns_g_lctx, level) == ISC_FALSE)
		return;

	dns_name_format(dns_zone_getorigin(zone), namebuf,
			sizeof(namebuf));
	dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
			      sizeof(classbuf));

	va_start(ap, fmt);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
232
	vsnprintf(message, sizeof(message), fmt, ap);
233 234 235
	va_end(ap);

	ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
236
		      level, "updating zone '%s/%s': %s",
237 238 239
		      namebuf, classbuf, message);
}

240 241
static isc_result_t
checkupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message,
242
	       dns_name_t *zonename, isc_boolean_t slave)
243 244 245 246 247 248 249
{
	char namebuf[DNS_NAME_FORMATSIZE];
	char classbuf[DNS_RDATACLASS_FORMATSIZE];
	int level = ISC_LOG_ERROR;
	const char *msg = "denied";
	isc_result_t result;

250 251 252 253 254 255
	if (slave && acl == NULL) {
		result = DNS_R_NOTIMP;
		level = ISC_LOG_DEBUG(3);
		msg = "disabled";
	} else
		result = ns_client_checkaclsilent(client, acl, ISC_FALSE);
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271

	if (result == ISC_R_SUCCESS) {
		level = ISC_LOG_DEBUG(3);
		msg = "approved";
	}

	dns_name_format(zonename, namebuf, sizeof(namebuf));
	dns_rdataclass_format(client->view->rdclass, classbuf,
			      sizeof(classbuf));

	ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
			      NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s",
			      message, namebuf, classbuf, msg);
	return (result);
}

Andreas Gustafsson's avatar
Andreas Gustafsson committed
272 273 274
/*
 * Update a single RR in version 'ver' of 'db' and log the
 * update in 'diff'.
275
 *
Andreas Gustafsson's avatar
Andreas Gustafsson committed
276 277 278 279
 * Ensures:
 *   '*tuple' == NULL.  Either the tuple is freed, or its
 *         ownership has been transferred to the diff.
 */
280
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
281 282 283 284 285
do_one_tuple(dns_difftuple_t **tuple,
	     dns_db_t *db, dns_dbversion_t *ver,
	     dns_diff_t *diff)
{
	dns_diff_t temp_diff;
286
	isc_result_t result;
287

288 289 290
	/*
	 * Create a singleton diff.
	 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
291 292 293
	dns_diff_init(diff->mctx, &temp_diff);
	ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);

294 295 296
	/*
	 * Apply it to the database.
	 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
297
	result = dns_diff_apply(&temp_diff, db, ver);
Mark Andrews's avatar
Mark Andrews committed
298
	ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
299
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
300 301 302 303
		dns_difftuple_free(tuple);
		return (result);
	}

304 305 306
	/*
	 * Merge it into the current pending journal entry.
	 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
307 308
	dns_diff_appendminimal(diff, tuple);

309 310 311
	/*
	 * Do not clear temp_diff.
	 */
312
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
313
}
314

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
/*
 * Perform the updates in 'updates' in version 'ver' of 'db' and log the
 * update in 'diff'.
 *
 * Ensures:
 *   'updates' is empty.
 */
static isc_result_t
do_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver,
	dns_diff_t *diff)
{
	isc_result_t result;
	while (! ISC_LIST_EMPTY(updates->tuples)) {
		dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples);
		ISC_LIST_UNLINK(updates->tuples, t, link);
		CHECK(do_one_tuple(&t, db, ver, diff));
	}
	return (ISC_R_SUCCESS);

 failure:
	dns_diff_clear(diff);
	return (result);
}

339
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
340
update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
341
	      dns_diffop_t op, dns_name_t *name,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
342 343 344
	      dns_ttl_t ttl, dns_rdata_t *rdata)
{
	dns_difftuple_t *tuple = NULL;
345
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
346 347
	result = dns_difftuple_create(diff->mctx, op,
				      name, ttl, rdata, &tuple);
348
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
349 350 351 352 353 354 355 356
		return (result);
	return (do_one_tuple(&tuple, db, ver, diff));
}

/**************************************************************************/
/*
 * Callback-style iteration over rdatasets and rdatas.
 *
357
 * foreach_rrset() can be used to iterate over the RRsets
Andreas Gustafsson's avatar
Andreas Gustafsson committed
358 359
 * of a name and call a callback function with each
 * one.  Similarly, foreach_rr() can be used to iterate
360
 * over the individual RRs at name, optionally restricted
Andreas Gustafsson's avatar
Andreas Gustafsson committed
361 362 363 364 365 366 367 368 369 370 371 372
 * to RRs of a given type.
 *
 * The callback functions are called "actions" and take
 * two arguments: a void pointer for passing arbitrary
 * context information, and a pointer to the current RRset
 * or RR.  By convention, their names end in "_action".
 */

/*
 * XXXRTH  We might want to make this public somewhere in libdns.
 */

373 374 375
/*
 * Function type for foreach_rrset() iterator actions.
 */
376
typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
377

378 379 380
/*
 * Function type for foreach_rr() iterator actions.
 */
381
typedef isc_result_t rr_func(void *data, rr_t *rr);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
382

383 384 385
/*
 * Internal context struct for foreach_node_rr().
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
386 387 388 389 390
typedef struct {
	rr_func *	rr_action;
	void *		rr_action_data;
} foreach_node_rr_ctx_t;

391 392 393
/*
 * Internal helper function for foreach_node_rr().
 */
394
static isc_result_t
395
foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) {
396
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
397 398
	foreach_node_rr_ctx_t *ctx = data;
	for (result = dns_rdataset_first(rdataset);
399
	     result == ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
400 401
	     result = dns_rdataset_next(rdataset))
	{
402 403
		rr_t rr = { 0, DNS_RDATA_INIT };
		
Andreas Gustafsson's avatar
Andreas Gustafsson committed
404 405 406
		dns_rdataset_current(rdataset, &rr.rdata);
		rr.ttl = rdataset->ttl;
		result = (*ctx->rr_action)(ctx->rr_action_data, &rr);
407
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
408 409
			return (result);
	}
410
	if (result != ISC_R_NOMORE)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
411
		return (result);
412
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
413 414 415 416 417
}

/*
 * For each rdataset of 'name' in 'ver' of 'db', call 'action'
 * with the rdataset and 'action_data' as arguments.  If the name
418
 * does not exist, do nothing.
Andreas Gustafsson's avatar
Andreas Gustafsson committed
419 420 421
 *
 * If 'action' returns an error, abort iteration and return the error.
 */
422
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
423 424 425 426 427 428
foreach_rrset(dns_db_t *db,
	      dns_dbversion_t *ver,
	      dns_name_t *name,
	      rrset_func *action,
	      void *action_data)
{
429
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
430 431
	dns_dbnode_t *node;
	dns_rdatasetiter_t *iter;
432

Andreas Gustafsson's avatar
Andreas Gustafsson committed
433 434
	node = NULL;
	result = dns_db_findnode(db, name, ISC_FALSE, &node);
435 436 437
	if (result == ISC_R_NOTFOUND)
		return (ISC_R_SUCCESS);
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
438
		return (result);
439

Andreas Gustafsson's avatar
Andreas Gustafsson committed
440 441 442
	iter = NULL;
	result = dns_db_allrdatasets(db, node, ver,
				     (isc_stdtime_t) 0, &iter);
443
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
444 445 446
		goto cleanup_node;

	for (result = dns_rdatasetiter_first(iter);
447
	     result == ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
448 449 450 451 452 453 454 455 456 457
	     result = dns_rdatasetiter_next(iter))
	{
		dns_rdataset_t rdataset;

		dns_rdataset_init(&rdataset);
		dns_rdatasetiter_current(iter, &rdataset);

		result = (*action)(action_data, &rdataset);

		dns_rdataset_disassociate(&rdataset);
458
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
459 460
			goto cleanup_iterator;
	}
461 462
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
463 464 465 466 467

 cleanup_iterator:
	dns_rdatasetiter_destroy(&iter);

 cleanup_node:
468
	dns_db_detachnode(db, &node);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
469 470 471 472 473 474 475 476 477 478 479 480

	return (result);
}

/*
 * For each RR of 'name' in 'ver' of 'db', call 'action'
 * with the RR and 'action_data' as arguments.  If the name
 * does not exist, do nothing.
 *
 * If 'action' returns an error, abort iteration
 * and return the error.
 */
481
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
foreach_node_rr(dns_db_t *db,
	    dns_dbversion_t *ver,
	    dns_name_t *name,
	    rr_func *rr_action,
	    void *rr_action_data)
{
	foreach_node_rr_ctx_t ctx;
	ctx.rr_action = rr_action;
	ctx.rr_action_data = rr_action_data;
	return (foreach_rrset(db, ver, name,
			      foreach_node_rr_action, &ctx));
}


/*
Bob Halley's avatar
Bob Halley committed
497 498
 * For each of the RRs specified by 'db', 'ver', 'name', 'type',
 * (which can be dns_rdatatype_any to match any type), and 'covers', call
499
 * 'action' with the RR and 'action_data' as arguments. If the name
Andreas Gustafsson's avatar
Andreas Gustafsson committed
500 501
 * does not exist, or if no RRset of the given type exists at the name,
 * do nothing.
502
 *
Andreas Gustafsson's avatar
Andreas Gustafsson committed
503 504
 * If 'action' returns an error, abort iteration and return the error.
 */
505
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
506 507 508 509
foreach_rr(dns_db_t *db,
	   dns_dbversion_t *ver,
	   dns_name_t *name,
	   dns_rdatatype_t type,
Bob Halley's avatar
Bob Halley committed
510
	   dns_rdatatype_t covers,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
511 512 513 514
	   rr_func *rr_action,
	   void *rr_action_data)
{

515
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
516 517 518 519
	dns_dbnode_t *node;
	dns_rdataset_t rdataset;

	if (type == dns_rdatatype_any)
520
		return (foreach_node_rr(db, ver, name,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
521
					rr_action, rr_action_data));
522

Andreas Gustafsson's avatar
Andreas Gustafsson committed
523 524
	node = NULL;
	result = dns_db_findnode(db, name, ISC_FALSE, &node);
525 526 527
	if (result == ISC_R_NOTFOUND)
		return (ISC_R_SUCCESS);
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
528 529 530
		return (result);

	dns_rdataset_init(&rdataset);
Bob Halley's avatar
Bob Halley committed
531
	result = dns_db_findrdataset(db, node, ver, type, covers,
Bob Halley's avatar
Bob Halley committed
532
				     (isc_stdtime_t) 0, &rdataset, NULL);
533 534
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
535 536
		goto cleanup_node;
	}
537
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
538 539 540
		goto cleanup_node;

	for (result = dns_rdataset_first(&rdataset);
541
	     result == ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
542 543
	     result = dns_rdataset_next(&rdataset))
	{
544
		rr_t rr = { 0, DNS_RDATA_INIT };
Andreas Gustafsson's avatar
Andreas Gustafsson committed
545 546 547
		dns_rdataset_current(&rdataset, &rr.rdata);
		rr.ttl = rdataset.ttl;
		result = (*rr_action)(rr_action_data, &rr);
548
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
549 550
			goto cleanup_rdataset;
	}
551
	if (result != ISC_R_NOMORE)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
552
		goto cleanup_rdataset;
553
	result = ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
554 555 556 557

 cleanup_rdataset:
	dns_rdataset_disassociate(&rdataset);
 cleanup_node:
558
	dns_db_detachnode(db, &node);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573

	return (result);
}

/**************************************************************************/
/*
 * Various tests on the database contents (for prerequisites, etc).
 */

/*
 * Function type for predicate functions that compare a database RR 'db_rr'
 * against an update RR 'update_rr'.
 */
typedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr);

574 575 576
/*
 * Helper function for rrset_exists().
 */
577
static isc_result_t
578
rrset_exists_action(void *data, rr_t *rr) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
579 580
	UNUSED(data);
	UNUSED(rr);
581
	return (ISC_R_EXISTS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
582 583
}

584 585 586
/*
 * Utility macro for RR existence checking functions.
 *
587 588
 * If the variable 'result' has the value ISC_R_EXISTS or
 * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE,
589 590 591 592 593 594 595 596 597
 * respectively, and return success.
 *
 * If 'result' has any other value, there was a failure.
 * Return the failure result code and do not set *exists.
 *
 * This would be more readable as "do { if ... } while(0)",
 * but that form generates tons of warnings on Solaris 2.6.
 */
#define RETURN_EXISTENCE_FLAG 				\
598 599 600 601
	return ((result == ISC_R_EXISTS) ? 		\
		(*exists = ISC_TRUE, ISC_R_SUCCESS) : 	\
		((result == ISC_R_SUCCESS) ?		\
		 (*exists = ISC_FALSE, ISC_R_SUCCESS) :	\
602
		 result))
603

Andreas Gustafsson's avatar
Andreas Gustafsson committed
604 605 606 607
/*
 * Set '*exists' to true iff an rrset of the given type exists,
 * to false otherwise.
 */
608
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
609
rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
Bob Halley's avatar
Bob Halley committed
610 611
	     dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers,
	     isc_boolean_t *exists)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
612
{
613
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
614
	result = foreach_rr(db, ver, name, type, covers,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
615 616 617 618
			    rrset_exists_action, NULL);
	RETURN_EXISTENCE_FLAG;
}

619 620 621
/*
 * Helper function for cname_incompatible_rrset_exists.
 */
622
static isc_result_t
623
cname_compatibility_action(void *data, dns_rdataset_t *rrset) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
624
	UNUSED(data);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
625
	if (rrset->type != dns_rdatatype_cname &&
626
	    ! dns_rdatatype_isdnssec(rrset->type))
627 628
		return (ISC_R_EXISTS);
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
629 630 631 632 633 634 635 636 637 638
}

/*
 * Check whether there is an rrset incompatible with adding a CNAME RR,
 * i.e., anything but another CNAME (which can be replaced) or a
 * DNSSEC RR (which can coexist).
 *
 * If such an incompatible rrset exists, set '*exists' to ISC_TRUE.
 * Otherwise, set it to ISC_FALSE.
 */
639
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
640 641
cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
				dns_name_t *name, isc_boolean_t *exists) {
642 643
	isc_result_t result;
	result = foreach_rrset(db, ver, name,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
644 645 646 647
			       cname_compatibility_action, NULL);
	RETURN_EXISTENCE_FLAG;
}

648 649 650
/*
 * Helper function for rr_count().
 */
651
static isc_result_t
652
count_rr_action(void *data, rr_t *rr) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
653
	int *countp = data;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
654
	UNUSED(rr);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
655
	(*countp)++;
656
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
657 658 659 660 661
}

/*
 * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'.
 */
662
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
663
rr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
Bob Halley's avatar
Bob Halley committed
664
	 dns_rdatatype_t type, dns_rdatatype_t covers, int *countp)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
665 666
{
	*countp = 0;
Bob Halley's avatar
Bob Halley committed
667
	return (foreach_rr(db, ver, name, type, covers,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
668 669 670
			   count_rr_action, countp));
}

671 672 673
/*
 * Context struct and helper function for name_exists().
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
674

675
static isc_result_t
676
name_exists_action(void *data, dns_rdataset_t *rrset) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
677 678
	UNUSED(data);
	UNUSED(rrset);
679
	return (ISC_R_EXISTS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
680 681 682 683 684
}

/*
 * Set '*exists' to true iff the given name exists, to false otherwise.
 */
685
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
686 687 688
name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
	    isc_boolean_t *exists)
{
689
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
690 691 692 693 694
	result = foreach_rrset(db, ver, name,
			       name_exists_action, NULL);
	RETURN_EXISTENCE_FLAG;
}

695 696 697 698 699 700
typedef struct {
	dns_name_t *name, *signer;
	dns_ssutable_t *table;
} ssu_check_t;

static isc_result_t
701
ssu_checkrule(void *data, dns_rdataset_t *rrset) {
702 703 704 705
	ssu_check_t *ssuinfo = data;
	isc_boolean_t result;

	/*
706
	 * If we're deleting all records, it's ok to delete RRSIG and NSEC even
707 708
	 * if we're normally not allowed to.
	 */
709 710
	if (rrset->type == dns_rdatatype_rrsig ||
	    rrset->type == dns_rdatatype_nsec)
711
		return (ISC_R_SUCCESS);
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
	result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer,
					 ssuinfo->name, rrset->type);
	return (result == ISC_TRUE ? ISC_R_SUCCESS : ISC_R_FAILURE);
}

static isc_boolean_t
ssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
	     dns_ssutable_t *ssutable, dns_name_t *signer)
{
	isc_result_t result;
	ssu_check_t ssuinfo;

	ssuinfo.name = name;
	ssuinfo.table = ssutable;
	ssuinfo.signer = signer;
	result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo);
	return (ISC_TF(result == ISC_R_SUCCESS));
}

Andreas Gustafsson's avatar
Andreas Gustafsson committed
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
/**************************************************************************/
/*
 * Checking of "RRset exists (value dependent)" prerequisites.
 *
 * In the RFC2136 section 3.2.5, this is the pseudocode involving
 * a variable called "temp", a mapping of <name, type> tuples to rrsets.
 *
 * Here, we represent the "temp" data structure as (non-minimial) "dns_diff_t"
 * where each typle has op==DNS_DIFFOP_EXISTS.
 */


/*
 * Append a tuple asserting the existence of the RR with
 * 'name' and 'rdata' to 'diff'.
746
 */
747
static isc_result_t
748
temp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) {
749
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
750
	dns_difftuple_t *tuple = NULL;
751

Bob Halley's avatar
Bob Halley committed
752
	REQUIRE(DNS_DIFF_VALID(diff));
753 754
	CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS,
				      name, 0, rdata, &tuple));
Andreas Gustafsson's avatar
Andreas Gustafsson committed
755
	ISC_LIST_APPEND(diff->tuples, tuple, link);
756 757
 failure:
	return (result);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
758 759 760 761 762
}

/*
 * Compare two rdatasets represented as sorted lists of tuples.
 * All list elements must have the same owner name and type.
763
 * Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
764 765
 * if not.
 */
766
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
767 768 769 770
temp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) {
	for (;;) {
		if (a == NULL || b == NULL)
			break;
771 772
		INSIST(a->op == DNS_DIFFOP_EXISTS &&
		       b->op == DNS_DIFFOP_EXISTS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
773 774 775 776 777 778 779 780 781
		INSIST(a->rdata.type == b->rdata.type);
		INSIST(dns_name_equal(&a->name, &b->name));
		if (dns_rdata_compare(&a->rdata, &b->rdata) != 0)
			return (DNS_R_NXRRSET);
		a = ISC_LIST_NEXT(a, link);
		b = ISC_LIST_NEXT(b, link);
	}
	if (a != NULL || b != NULL)
		return (DNS_R_NXRRSET);
782
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
783 784 785 786 787 788 789
}

/*
 * A comparison function defining the sorting order for the entries
 * in the "temp" data structure.  The major sort key is the owner name,
 * followed by the type and rdata.
 */
790
static int
791
temp_order(const void *av, const void *bv) {
Brian Wellington's avatar
Brian Wellington committed
792 793 794 795
	dns_difftuple_t const * const *ap = av;
	dns_difftuple_t const * const *bp = bv;
	dns_difftuple_t const *a = *ap;
	dns_difftuple_t const *b = *bp;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
	int r;
	r = dns_name_compare(&a->name, &b->name);
	if (r != 0)
		return (r);
	r = (b->rdata.type - a->rdata.type);
	if (r != 0)
		return (r);
	r = dns_rdata_compare(&a->rdata, &b->rdata);
	return (r);
}

/*
 * Check the "RRset exists (value dependent)" prerequisite information
 * in 'temp' against the contents of the database 'db'.
 *
811
 * Return ISC_R_SUCCESS if the prerequisites are satisfied,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
812
 * rcode(dns_rcode_nxrrset) if not.
813 814
 *
 * 'temp' must be pre-sorted.
Andreas Gustafsson's avatar
Andreas Gustafsson committed
815 816
 */

817
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
818
temp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db,
819
	   dns_dbversion_t *ver, dns_name_t *tmpname, dns_rdatatype_t *typep)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
820
{
821
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
822 823 824 825
	dns_name_t *name;
	dns_dbnode_t *node;
	dns_difftuple_t *t;
	dns_diff_t trash;
826

Andreas Gustafsson's avatar
Andreas Gustafsson committed
827
	dns_diff_init(mctx, &trash);
828

Andreas Gustafsson's avatar
Andreas Gustafsson committed
829 830 831 832 833 834 835 836
	/*
	 * For each name and type in the prerequisites,
	 * construct a sorted rdata list of the corresponding
	 * database contents, and compare the lists.
	 */
	t = ISC_LIST_HEAD(temp->tuples);
	while (t != NULL) {
		name = &t->name;
837 838
		(void)dns_name_copy(name, tmpname, NULL);
		*typep = t->rdata.type;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
839 840 841 842

		/* A new unique name begins here. */
		node = NULL;
		result = dns_db_findnode(db, name, ISC_FALSE, &node);
843
		if (result == ISC_R_NOTFOUND)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
844
			return (DNS_R_NXRRSET);
845
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
846 847
			return (result);

848
		/* A new unique type begins here. */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
849
		while (t != NULL && dns_name_equal(&t->name, name)) {
Bob Halley's avatar
Bob Halley committed
850
			dns_rdatatype_t type, covers;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
851
			dns_rdataset_t rdataset;
852
			dns_diff_t d_rrs; /* Database RRs with
Andreas Gustafsson's avatar
Andreas Gustafsson committed
853 854 855 856
						this name and type */
 			dns_diff_t u_rrs; /* Update RRs with
						this name and type */

857
			*typep = type = t->rdata.type;
858 859
			if (type == dns_rdatatype_rrsig ||
			    type == dns_rdatatype_sig)
Bob Halley's avatar
Bob Halley committed
860 861 862
				covers = dns_rdata_covers(&t->rdata);
			else
				covers = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
863 864 865 866 867 868 869

			/*
			 * Collect all database RRs for this name and type
			 * onto d_rrs and sort them.
			 */
			dns_rdataset_init(&rdataset);
			result = dns_db_findrdataset(db, node, ver, type,
Bob Halley's avatar
Bob Halley committed
870
						     covers, (isc_stdtime_t) 0,
Bob Halley's avatar
Bob Halley committed
871
						     &rdataset, NULL);
872
			if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
873 874 875 876 877 878 879 880
				dns_db_detachnode(db, &node);
				return (DNS_R_NXRRSET);
			}

			dns_diff_init(mctx, &d_rrs);
			dns_diff_init(mctx, &u_rrs);

			for (result = dns_rdataset_first(&rdataset);
881
			     result == ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
882 883
			     result = dns_rdataset_next(&rdataset))
			{
884
				dns_rdata_t rdata = DNS_RDATA_INIT;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
885 886
				dns_rdataset_current(&rdataset, &rdata);
				result = temp_append(&d_rrs, name, &rdata);
887
				if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
888 889
					goto failure;
			}
890
			if (result != ISC_R_NOMORE)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
891 892
				goto failure;
			result = dns_diff_sort(&d_rrs, temp_order);
893
			if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
894 895 896 897
				goto failure;

			/*
			 * Collect all update RRs for this name and type
898
			 * onto u_rrs.  No need to sort them here -
Andreas Gustafsson's avatar
Andreas Gustafsson committed
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
			 * they are already sorted.
			 */
			while (t != NULL &&
			       dns_name_equal(&t->name, name) &&
			       t->rdata.type == type)
			{
				dns_difftuple_t *next =
					ISC_LIST_NEXT(t, link);
				ISC_LIST_UNLINK(temp->tuples, t, link);
				ISC_LIST_APPEND(u_rrs.tuples, t, link);
				t = next;
			}

			/* Compare the two sorted lists. */
			result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples),
						  ISC_LIST_HEAD(d_rrs.tuples));
915
			if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
				goto failure;

			/*
			 * We are done with the tuples, but we can't free
			 * them yet because "name" still points into one
			 * of them.  Move them on a temporary list.
			 */
			ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link);
			ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link);
			dns_rdataset_disassociate(&rdataset);

			continue;

		    failure:
			dns_diff_clear(&d_rrs);
			dns_diff_clear(&u_rrs);
			dns_diff_clear(&trash);
			dns_rdataset_disassociate(&rdataset);
			dns_db_detachnode(db, &node);
			return (result);
		}

		dns_db_detachnode(db, &node);
	}
940 941

	dns_diff_clear(&trash);
942
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
943 944 945 946 947 948 949
}

/**************************************************************************/
/*
 * Conditional deletion of RRs.
 */

950 951 952
/*
 * Context structure for delete_if().
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
953 954 955 956 957 958 959 960 961 962

typedef struct {
	rr_predicate *predicate;
	dns_db_t *db;
	dns_dbversion_t *ver;
	dns_diff_t *diff;
	dns_name_t *name;
	dns_rdata_t *update_rr;
} conditional_delete_ctx_t;

963 964 965
/*
 * Predicate functions for delete_if().
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
966

967
/*
968 969
 * Return true iff 'db_rr' is neither a SOA nor an NS RR nor
 * an RRSIG nor a NSEC.
970
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
971
static isc_boolean_t
972