update.c 80.8 KB
Newer Older
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 2004-2006  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
 */

18
/* $Id: update.c,v 1.130 2006/12/04 01:52:45 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
#include <dns/journal.h>
34
#include <dns/keyvalues.h>
Bob Halley's avatar
Bob Halley committed
35
#include <dns/message.h>
36
#include <dns/nsec.h>
37
#include <dns/rdataclass.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
38 39
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
40
#include <dns/rdatastruct.h>
41
#include <dns/rdatatype.h>
42
#include <dns/soa.h>
43
#include <dns/ssu.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
44
#include <dns/view.h>
Bob Halley's avatar
Bob Halley committed
45 46
#include <dns/zone.h>
#include <dns/zt.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
47 48

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

52 53
/*! \file
 * \brief
Andreas Gustafsson's avatar
Andreas Gustafsson committed
54 55
 * This module implements dynamic update as in RFC2136.
 */
56

Andreas Gustafsson's avatar
Andreas Gustafsson committed
57
/*
58 59 60
 *  XXX TODO:
 * - document strict minimality
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
61

62
/**************************************************************************/
63

64
/*%
65
 * Log level for tracing dynamic update protocol requests.
66
 */
67
#define LOGLEVEL_PROTOCOL	ISC_LOG_INFO
68

69
/*%
70
 * Log level for low-level debug tracing.
71
 */
72
#define LOGLEVEL_DEBUG		ISC_LOG_DEBUG(8)
73

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

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

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

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

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

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

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
181 182 183 184
typedef struct rr rr_t;

struct rr {
	/* dns_name_t name; */
185 186
	isc_uint32_t		ttl;
	dns_rdata_t		rdata;
187 188 189 190 191 192
};

typedef struct update_event update_event_t;

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

198 199 200 201 202 203 204
/**************************************************************************/
/*
 * 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);
205
static isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone);
206
static void forward_done(isc_task_t *task, isc_event_t *event);
207

Andreas Gustafsson's avatar
Andreas Gustafsson committed
208
/**************************************************************************/
209

210 211 212 213
static void
update_log(ns_client_t *client, dns_zone_t *zone,
	   int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
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
235
	vsnprintf(message, sizeof(message), fmt, ap);
236 237 238
	va_end(ap);

	ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
239
		      level, "updating zone '%s/%s': %s",
240 241 242
		      namebuf, classbuf, message);
}

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

253 254 255 256 257 258
	if (slave && acl == NULL) {
		result = DNS_R_NOTIMP;
		level = ISC_LOG_DEBUG(3);
		msg = "disabled";
	} else
		result = ns_client_checkaclsilent(client, acl, ISC_FALSE);
259 260 261 262 263 264

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

265 266 267 268 269 270 271
	if (client->signer != NULL) {
		dns_name_format(client->signer, namebuf, sizeof(namebuf));
		ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
			      NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
			      "signer \"%s\" %s", namebuf, msg);
	}

272 273 274 275 276
	dns_name_format(zonename, namebuf, sizeof(namebuf));
	dns_rdataclass_format(client->view->rdclass, classbuf,
			      sizeof(classbuf));

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

282
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
283 284
 * Update a single RR in version 'ver' of 'db' and log the
 * update in 'diff'.
285
 *
Andreas Gustafsson's avatar
Andreas Gustafsson committed
286
 * Ensures:
287 288
 * \li	'*tuple' == NULL.  Either the tuple is freed, or its
 *	ownership has been transferred to the diff.
Andreas Gustafsson's avatar
Andreas Gustafsson committed
289
 */
290
static isc_result_t
291
do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
292 293 294
	     dns_diff_t *diff)
{
	dns_diff_t temp_diff;
295
	isc_result_t result;
296

297 298 299
	/*
	 * Create a singleton diff.
	 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
300 301 302
	dns_diff_init(diff->mctx, &temp_diff);
	ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);

303 304 305
	/*
	 * Apply it to the database.
	 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
306
	result = dns_diff_apply(&temp_diff, db, ver);
Mark Andrews's avatar
Mark Andrews committed
307
	ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
308
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
309 310 311 312
		dns_difftuple_free(tuple);
		return (result);
	}

313 314 315
	/*
	 * Merge it into the current pending journal entry.
	 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
316 317
	dns_diff_appendminimal(diff, tuple);

318 319 320
	/*
	 * Do not clear temp_diff.
	 */
321
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
322
}
323

324
/*%
325 326 327 328
 * Perform the updates in 'updates' in version 'ver' of 'db' and log the
 * update in 'diff'.
 *
 * Ensures:
329
 * \li	'updates' is empty.
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
 */
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);
}

348
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
349
update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
350 351
	      dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
	      dns_rdata_t *rdata)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
352 353
{
	dns_difftuple_t *tuple = NULL;
354
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
355 356
	result = dns_difftuple_create(diff->mctx, op,
				      name, ttl, rdata, &tuple);
357
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
358 359 360 361 362 363 364 365
		return (result);
	return (do_one_tuple(&tuple, db, ver, diff));
}

/**************************************************************************/
/*
 * Callback-style iteration over rdatasets and rdatas.
 *
366
 * foreach_rrset() can be used to iterate over the RRsets
Andreas Gustafsson's avatar
Andreas Gustafsson committed
367 368
 * of a name and call a callback function with each
 * one.  Similarly, foreach_rr() can be used to iterate
369
 * over the individual RRs at name, optionally restricted
Andreas Gustafsson's avatar
Andreas Gustafsson committed
370 371 372 373 374 375 376 377 378 379 380 381
 * 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.
 */

382
/*%
383 384
 * Function type for foreach_rrset() iterator actions.
 */
385
typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
386

387
/*%
388 389
 * Function type for foreach_rr() iterator actions.
 */
390
typedef isc_result_t rr_func(void *data, rr_t *rr);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
391

392
/*%
393 394
 * Internal context struct for foreach_node_rr().
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
395 396 397 398 399
typedef struct {
	rr_func *	rr_action;
	void *		rr_action_data;
} foreach_node_rr_ctx_t;

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

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
439 440
	node = NULL;
	result = dns_db_findnode(db, name, ISC_FALSE, &node);
441 442 443
	if (result == ISC_R_NOTFOUND)
		return (ISC_R_SUCCESS);
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
444
		return (result);
445

Andreas Gustafsson's avatar
Andreas Gustafsson committed
446 447 448
	iter = NULL;
	result = dns_db_allrdatasets(db, node, ver,
				     (isc_stdtime_t) 0, &iter);
449
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
450 451 452
		goto cleanup_node;

	for (result = dns_rdatasetiter_first(iter);
453
	     result == ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
454 455 456 457 458 459 460 461 462 463
	     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);
464
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
465 466
			goto cleanup_iterator;
	}
467 468
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
469 470 471 472 473

 cleanup_iterator:
	dns_rdatasetiter_destroy(&iter);

 cleanup_node:
474
	dns_db_detachnode(db, &node);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
475 476 477 478

	return (result);
}

479
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
480 481 482 483 484 485 486
 * 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.
 */
487
static isc_result_t
488 489
foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
		rr_func *rr_action, void *rr_action_data)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
490 491 492 493 494 495 496 497 498
{
	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));
}


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

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

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

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

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

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

 cleanup_rdataset:
	dns_rdataset_disassociate(&rdataset);
 cleanup_node:
557
	dns_db_detachnode(db, &node);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
558 559 560 561 562 563 564 565 566

	return (result);
}

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

567
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
568 569 570 571 572
 * 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);

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

583
/*%
584 585
 * Utility macro for RR existence checking functions.
 *
586 587
 * If the variable 'result' has the value ISC_R_EXISTS or
 * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE,
588 589 590 591 592 593 594 595
 * 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.
 */
596 597 598
#define RETURN_EXISTENCE_FLAG				\
	return ((result == ISC_R_EXISTS) ?		\
		(*exists = ISC_TRUE, ISC_R_SUCCESS) :	\
599 600
		((result == ISC_R_SUCCESS) ?		\
		 (*exists = ISC_FALSE, ISC_R_SUCCESS) :	\
601
		 result))
602

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

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

630
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
631 632 633 634 635 636 637
 * 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.
 */
638
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
639 640
cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
				dns_name_t *name, isc_boolean_t *exists) {
641 642
	isc_result_t result;
	result = foreach_rrset(db, ver, name,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
643 644 645 646
			       cname_compatibility_action, NULL);
	RETURN_EXISTENCE_FLAG;
}

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

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

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

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

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

694
typedef struct {
695 696
	dns_name_t *name;
	dns_name_t *signer;
697 698 699 700
	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
/**************************************************************************/
/*
 * 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.
 */


743
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
744 745
 * 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
	CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS,
754
				   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
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
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
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
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
	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);
}

807
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
808 809 810
 * 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
						this name and type */
854
			dns_diff_t u_rrs; /* Update RRs with
Andreas Gustafsson's avatar
Andreas Gustafsson committed
855 856
						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
<