update.c 123 KB
Newer Older
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2011  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
Andreas Gustafsson's avatar
Andreas Gustafsson committed
6 7
 * 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.190 2011/03/11 06:11:21 marka Exp $ */
David Lawrence's avatar
David Lawrence committed
19

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

22
#include <isc/netaddr.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
23
#include <isc/print.h>
24
#include <isc/serial.h>
25
#include <isc/stats.h>
26
#include <isc/string.h>
27
#include <isc/taskpool.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
28
#include <isc/util.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
29

Bob Halley's avatar
Bob Halley committed
30 31
#include <dns/db.h>
#include <dns/dbiterator.h>
32
#include <dns/diff.h>
Bob Halley's avatar
Bob Halley committed
33 34
#include <dns/dnssec.h>
#include <dns/events.h>
Brian Wellington's avatar
Brian Wellington committed
35
#include <dns/fixedname.h>
Bob Halley's avatar
Bob Halley committed
36
#include <dns/journal.h>
37
#include <dns/keyvalues.h>
Bob Halley's avatar
Bob Halley committed
38
#include <dns/message.h>
39
#include <dns/nsec.h>
40
#include <dns/nsec3.h>
41
#include <dns/private.h>
42
#include <dns/rdataclass.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
43 44
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
45
#include <dns/rdatastruct.h>
46
#include <dns/rdatatype.h>
47
#include <dns/soa.h>
48
#include <dns/ssu.h>
49
#include <dns/tsig.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
50
#include <dns/view.h>
Bob Halley's avatar
Bob Halley committed
51 52
#include <dns/zone.h>
#include <dns/zt.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
53 54

#include <named/client.h>
55
#include <named/log.h>
56
#include <named/server.h>
57
#include <named/update.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
58

59 60
/*! \file
 * \brief
Andreas Gustafsson's avatar
Andreas Gustafsson committed
61 62
 * This module implements dynamic update as in RFC2136.
 */
63

Andreas Gustafsson's avatar
Andreas Gustafsson committed
64
/*
65 66 67
 *  XXX TODO:
 * - document strict minimality
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
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 83 84 85
 * Check an operation for failure.  These macros all assume that
 * the function using them has a 'result' variable and a 'failure'
 * label.
 */
86
#define CHECK(op) \
87 88
	do { result = (op); \
		if (result != ISC_R_SUCCESS) goto failure; \
89
	} while (0)
90

91
/*%
92
 * Fail unconditionally with result 'code', which must not
93
 * be ISC_R_SUCCESS.  The reason for failure presumably has
94
 * been logged already.
95 96 97
 *
 * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
 * from complaining about "end-of-loop code not reached".
98 99 100 101 102
 */

#define FAIL(code) \
	do {							\
		result = (code);				\
103
		if (result != ISC_R_SUCCESS) goto failure;	\
104 105
	} while (0)

106
/*%
107
 * Fail unconditionally and log as a client error.
108 109
 * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
 * from complaining about "end-of-loop code not reached".
110
 */
111 112
#define FAILC(code, msg) \
	do {							\
113
		const char *_what = "failed";			\
114
		result = (code);				\
115 116 117 118 119 120 121
		switch (result) {				\
		case DNS_R_NXDOMAIN:				\
		case DNS_R_YXDOMAIN:				\
		case DNS_R_YXRRSET:				\
		case DNS_R_NXRRSET:				\
			_what = "unsuccessful";			\
		}						\
122 123 124
		update_log(client, zone, LOGLEVEL_PROTOCOL,	\
			   "update %s: %s (%s)", _what,		\
			   msg, isc_result_totext(result));	\
125
		if (result != ISC_R_SUCCESS) goto failure;	\
126
	} while (0)
127 128 129 130 131
#define PREREQFAILC(code, msg) \
	do {							\
		inc_stats(zone, dns_nsstatscounter_updatebadprereq); \
		FAILC(code, msg);				\
	} while (0)
132

133 134
#define FAILN(code, name, msg) \
	do {								\
135
		const char *_what = "failed";				\
136
		result = (code);					\
137 138 139 140 141 142 143
		switch (result) {					\
		case DNS_R_NXDOMAIN:					\
		case DNS_R_YXDOMAIN:					\
		case DNS_R_YXRRSET:					\
		case DNS_R_NXRRSET:					\
			_what = "unsuccessful";				\
		}							\
144 145 146
		if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) {	\
			char _nbuf[DNS_NAME_FORMATSIZE];		\
			dns_name_format(name, _nbuf, sizeof(_nbuf));	\
147
			update_log(client, zone, LOGLEVEL_PROTOCOL,	\
148
				   "update %s: %s: %s (%s)", _what, _nbuf, \
149 150 151 152
				   msg, isc_result_totext(result));	\
		}							\
		if (result != ISC_R_SUCCESS) goto failure;		\
	} while (0)
153 154 155 156 157
#define PREREQFAILN(code, name, msg) \
	do {								\
		inc_stats(zone, dns_nsstatscounter_updatebadprereq); \
		FAILN(code, name, msg);					\
	} while (0)
158 159 160

#define FAILNT(code, name, type, msg) \
	do {								\
161
		const char *_what = "failed";				\
162
		result = (code);					\
163 164 165 166 167 168 169
		switch (result) {					\
		case DNS_R_NXDOMAIN:					\
		case DNS_R_YXDOMAIN:					\
		case DNS_R_YXRRSET:					\
		case DNS_R_NXRRSET:					\
			_what = "unsuccessful";				\
		}							\
170 171 172 173 174
		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)); \
175
			update_log(client, zone, LOGLEVEL_PROTOCOL,	\
176 177
				   "update %s: %s/%s: %s (%s)",		\
				   _what, _nbuf, _tbuf, msg,		\
178 179 180 181
				   isc_result_totext(result));		\
		}							\
		if (result != ISC_R_SUCCESS) goto failure;		\
	} while (0)
182 183 184 185 186 187
#define PREREQFAILNT(code, name, type, msg)				\
	do {								\
		inc_stats(zone, dns_nsstatscounter_updatebadprereq); \
		FAILNT(code, name, type, msg);				\
	} while (0)

188
/*%
189
 * Fail unconditionally and log as a server error.
190 191
 * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
 * from complaining about "end-of-loop code not reached".
192 193 194
 */
#define FAILS(code, msg) \
	do {							\
195
		result = (code);				\
196
		update_log(client, zone, LOGLEVEL_PROTOCOL,	\
197 198
			   "error: %s: %s",			\
			   msg, isc_result_totext(result));	\
199
		if (result != ISC_R_SUCCESS) goto failure;	\
200
	} while (0)
201

202
/*
Francis Dupont's avatar
Francis Dupont committed
203
 * Return TRUE if NS_CLIENTATTR_TCP is set in the attributes other FALSE.
204 205 206
 */
#define TCPCLIENT(client) (((client)->attributes & NS_CLIENTATTR_TCP) != 0)

207 208
/**************************************************************************/

Andreas Gustafsson's avatar
Andreas Gustafsson committed
209 210 211 212
typedef struct rr rr_t;

struct rr {
	/* dns_name_t name; */
213 214
	isc_uint32_t		ttl;
	dns_rdata_t		rdata;
215 216 217 218 219 220
};

typedef struct update_event update_event_t;

struct update_event {
	ISC_EVENT_COMMON(update_event_t);
221
	dns_zone_t		*zone;
222
	isc_result_t		result;
223
	dns_message_t		*answer;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
224 225
};

226 227 228 229 230 231 232
/**************************************************************************/
/*
 * 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);
233
static isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone);
234
static void forward_done(isc_task_t *task, isc_event_t *event);
235

Andreas Gustafsson's avatar
Andreas Gustafsson committed
236
/**************************************************************************/
237

238 239 240 241
static void
update_log(ns_client_t *client, dns_zone_t *zone,
	   int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
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
263
	vsnprintf(message, sizeof(message), fmt, ap);
264 265 266
	va_end(ap);

	ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
267
		      level, "updating zone '%s/%s': %s",
268 269 270
		      namebuf, classbuf, message);
}

271 272 273 274
/*%
 * Increment updated-related statistics counters.
 */
static inline void
275 276
inc_stats(dns_zone_t *zone, isc_statscounter_t counter) {
	isc_stats_increment(ns_g_server->nsstats, counter);
277 278

	if (zone != NULL) {
279
		isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
280
		if (zonestats != NULL)
281
			isc_stats_increment(zonestats, counter);
282 283 284
	}
}

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
/*%
 * Check if we could have queried for the contents of this zone or
 * if the zone is potentially updateable.
 * If the zone can potentially be updated and the check failed then
 * log a error otherwise we log a informational message.
 */
static isc_result_t
checkqueryacl(ns_client_t *client, dns_acl_t *queryacl, dns_name_t *zonename,
	      dns_acl_t *updateacl, dns_ssutable_t *ssutable)
{
	char namebuf[DNS_NAME_FORMATSIZE];
	char classbuf[DNS_RDATACLASS_FORMATSIZE];
	int level;
	isc_result_t result;

	result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE);
	if (result != ISC_R_SUCCESS) {
		dns_name_format(zonename, namebuf, sizeof(namebuf));
		dns_rdataclass_format(client->view->rdclass, classbuf,
				      sizeof(classbuf));

		level = (updateacl == NULL && ssutable == NULL) ?
				ISC_LOG_INFO : ISC_LOG_ERROR;

		ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
			      NS_LOGMODULE_UPDATE, level,
			      "update '%s/%s' denied due to allow-query",
			      namebuf, classbuf);
	} else if (updateacl == NULL && ssutable == NULL) {
314 315 316 317
		dns_name_format(zonename, namebuf, sizeof(namebuf));
		dns_rdataclass_format(client->view->rdclass, classbuf,
				      sizeof(classbuf));

318 319 320 321 322 323 324 325
		result = DNS_R_REFUSED;
		ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
			      NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
			      "update '%s/%s' denied", namebuf, classbuf);
	}
	return (result);
}

326 327 328 329 330 331 332 333 334 335 336 337 338 339
/*%
 * Override the default acl logging when checking whether a client
 * can update the zone or whether we can forward the request to the
 * master based on IP address.
 *
 * 'message' contains the type of operation that is being attempted.
 * 'slave' indicates if this is a slave zone.  If 'acl' is NULL then
 * log at debug=3.
 * If the zone has no access controls configured ('acl' == NULL &&
 * 'has_ssutable == ISC_FALS) log the attempt at info, otherwise
 * at error.
 *
 * If the request was signed log that we received it.
 */
340 341
static isc_result_t
checkupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message,
342 343
	       dns_name_t *zonename, isc_boolean_t slave,
	       isc_boolean_t has_ssutable)
344 345 346 347 348 349 350
{
	char namebuf[DNS_NAME_FORMATSIZE];
	char classbuf[DNS_RDATACLASS_FORMATSIZE];
	int level = ISC_LOG_ERROR;
	const char *msg = "denied";
	isc_result_t result;

351 352 353 354
	if (slave && acl == NULL) {
		result = DNS_R_NOTIMP;
		level = ISC_LOG_DEBUG(3);
		msg = "disabled";
355
	} else {
356
		result = ns_client_checkaclsilent(client, NULL, acl, ISC_FALSE);
357 358 359 360 361 362
		if (result == ISC_R_SUCCESS) {
			level = ISC_LOG_DEBUG(3);
			msg = "approved";
		} else if (acl == NULL && !has_ssutable) {
			level = ISC_LOG_INFO;
		}
363 364
	}

365 366 367 368 369 370 371
	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);
	}

372 373 374 375 376
	dns_name_format(zonename, namebuf, sizeof(namebuf));
	dns_rdataclass_format(client->view->rdclass, classbuf,
			      sizeof(classbuf));

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

382
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
383 384
 * Update a single RR in version 'ver' of 'db' and log the
 * update in 'diff'.
385
 *
Andreas Gustafsson's avatar
Andreas Gustafsson committed
386
 * Ensures:
387 388
 * \li	'*tuple' == NULL.  Either the tuple is freed, or its
 *	ownership has been transferred to the diff.
Andreas Gustafsson's avatar
Andreas Gustafsson committed
389
 */
390
static isc_result_t
391
do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
392 393 394
	     dns_diff_t *diff)
{
	dns_diff_t temp_diff;
395
	isc_result_t result;
396

397 398 399
	/*
	 * Create a singleton diff.
	 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
400
	dns_diff_init(diff->mctx, &temp_diff);
401
	temp_diff.resign = diff->resign;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
402 403
	ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);

404 405 406
	/*
	 * Apply it to the database.
	 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
407
	result = dns_diff_apply(&temp_diff, db, ver);
Mark Andrews's avatar
Mark Andrews committed
408
	ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
409
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
410 411 412 413
		dns_difftuple_free(tuple);
		return (result);
	}

414 415 416
	/*
	 * Merge it into the current pending journal entry.
	 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
417 418
	dns_diff_appendminimal(diff, tuple);

419 420 421
	/*
	 * Do not clear temp_diff.
	 */
422
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
423
}
424

425
/*%
426 427 428 429
 * Perform the updates in 'updates' in version 'ver' of 'db' and log the
 * update in 'diff'.
 *
 * Ensures:
430
 * \li	'updates' is empty.
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
 */
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);
}

449
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
450
update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
451 452
	      dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
	      dns_rdata_t *rdata)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
453 454
{
	dns_difftuple_t *tuple = NULL;
455
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
456 457
	result = dns_difftuple_create(diff->mctx, op,
				      name, ttl, rdata, &tuple);
458
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
459 460 461 462 463 464 465 466
		return (result);
	return (do_one_tuple(&tuple, db, ver, diff));
}

/**************************************************************************/
/*
 * Callback-style iteration over rdatasets and rdatas.
 *
467
 * foreach_rrset() can be used to iterate over the RRsets
Andreas Gustafsson's avatar
Andreas Gustafsson committed
468 469
 * of a name and call a callback function with each
 * one.  Similarly, foreach_rr() can be used to iterate
470
 * over the individual RRs at name, optionally restricted
Andreas Gustafsson's avatar
Andreas Gustafsson committed
471 472 473 474 475 476 477 478 479 480 481 482
 * 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.
 */

483
/*%
484 485
 * Function type for foreach_rrset() iterator actions.
 */
486
typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
487

488
/*%
489 490
 * Function type for foreach_rr() iterator actions.
 */
491
typedef isc_result_t rr_func(void *data, rr_t *rr);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
492

493
/*%
494 495
 * Internal context struct for foreach_node_rr().
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
496 497 498 499 500
typedef struct {
	rr_func *	rr_action;
	void *		rr_action_data;
} foreach_node_rr_ctx_t;

501
/*%
502 503
 * Internal helper function for foreach_node_rr().
 */
504
static isc_result_t
505
foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) {
506
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
507 508
	foreach_node_rr_ctx_t *ctx = data;
	for (result = dns_rdataset_first(rdataset);
509
	     result == ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
510 511
	     result = dns_rdataset_next(rdataset))
	{
512
		rr_t rr = { 0, DNS_RDATA_INIT };
Automatic Updater's avatar
Automatic Updater committed
513

Andreas Gustafsson's avatar
Andreas Gustafsson committed
514 515 516
		dns_rdataset_current(rdataset, &rr.rdata);
		rr.ttl = rdataset->ttl;
		result = (*ctx->rr_action)(ctx->rr_action_data, &rr);
517
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
518 519
			return (result);
	}
520
	if (result != ISC_R_NOMORE)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
521
		return (result);
522
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
523 524
}

525
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
526 527
 * For each rdataset of 'name' in 'ver' of 'db', call 'action'
 * with the rdataset and 'action_data' as arguments.  If the name
528
 * does not exist, do nothing.
Andreas Gustafsson's avatar
Andreas Gustafsson committed
529 530 531
 *
 * If 'action' returns an error, abort iteration and return the error.
 */
532
static isc_result_t
533 534
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
535
{
536
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
537 538
	dns_dbnode_t *node;
	dns_rdatasetiter_t *iter;
539

Andreas Gustafsson's avatar
Andreas Gustafsson committed
540 541
	node = NULL;
	result = dns_db_findnode(db, name, ISC_FALSE, &node);
542 543 544
	if (result == ISC_R_NOTFOUND)
		return (ISC_R_SUCCESS);
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
545
		return (result);
546

Andreas Gustafsson's avatar
Andreas Gustafsson committed
547 548 549
	iter = NULL;
	result = dns_db_allrdatasets(db, node, ver,
				     (isc_stdtime_t) 0, &iter);
550
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
551 552 553
		goto cleanup_node;

	for (result = dns_rdatasetiter_first(iter);
554
	     result == ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
555 556 557 558 559 560 561 562 563 564
	     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);
565
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
566 567
			goto cleanup_iterator;
	}
568 569
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
570 571 572 573 574

 cleanup_iterator:
	dns_rdatasetiter_destroy(&iter);

 cleanup_node:
575
	dns_db_detachnode(db, &node);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
576 577 578 579

	return (result);
}

580
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
581 582 583 584 585 586 587
 * 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.
 */
588
static isc_result_t
589 590
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
591 592 593 594 595 596 597 598 599
{
	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));
}


600
/*%
Bob Halley's avatar
Bob Halley committed
601 602
 * For each of the RRs specified by 'db', 'ver', 'name', 'type',
 * (which can be dns_rdatatype_any to match any type), and 'covers', call
603
 * 'action' with the RR and 'action_data' as arguments. If the name
Andreas Gustafsson's avatar
Andreas Gustafsson committed
604 605
 * does not exist, or if no RRset of the given type exists at the name,
 * do nothing.
606
 *
Andreas Gustafsson's avatar
Andreas Gustafsson committed
607 608
 * If 'action' returns an error, abort iteration and return the error.
 */
609
static isc_result_t
610 611
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
612 613 614
	   void *rr_action_data)
{

615
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
616 617 618 619
	dns_dbnode_t *node;
	dns_rdataset_t rdataset;

	if (type == dns_rdatatype_any)
620
		return (foreach_node_rr(db, ver, name,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
621
					rr_action, rr_action_data));
622

Andreas Gustafsson's avatar
Andreas Gustafsson committed
623
	node = NULL;
624 625 626 627 628
	if (type == dns_rdatatype_nsec3 ||
	    (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3))
		result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
	else
		result = dns_db_findnode(db, name, ISC_FALSE, &node);
629 630 631
	if (result == ISC_R_NOTFOUND)
		return (ISC_R_SUCCESS);
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
632 633 634
		return (result);

	dns_rdataset_init(&rdataset);
Bob Halley's avatar
Bob Halley committed
635
	result = dns_db_findrdataset(db, node, ver, type, covers,
Bob Halley's avatar
Bob Halley committed
636
				     (isc_stdtime_t) 0, &rdataset, NULL);
637 638
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
639 640
		goto cleanup_node;
	}
641
	if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
642 643 644
		goto cleanup_node;

	for (result = dns_rdataset_first(&rdataset);
645
	     result == ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
646 647
	     result = dns_rdataset_next(&rdataset))
	{
648
		rr_t rr = { 0, DNS_RDATA_INIT };
Andreas Gustafsson's avatar
Andreas Gustafsson committed
649 650 651
		dns_rdataset_current(&rdataset, &rr.rdata);
		rr.ttl = rdataset.ttl;
		result = (*rr_action)(rr_action_data, &rr);
652
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
653 654
			goto cleanup_rdataset;
	}
655
	if (result != ISC_R_NOMORE)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
656
		goto cleanup_rdataset;
657
	result = ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
658 659 660 661

 cleanup_rdataset:
	dns_rdataset_disassociate(&rdataset);
 cleanup_node:
662
	dns_db_detachnode(db, &node);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
663 664 665 666 667 668 669 670 671

	return (result);
}

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

672
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
673 674 675 676 677
 * 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);

678
/*%
679 680
 * Helper function for rrset_exists().
 */
681
static isc_result_t
682
rrset_exists_action(void *data, rr_t *rr) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
683 684
	UNUSED(data);
	UNUSED(rr);
685
	return (ISC_R_EXISTS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
686 687
}

688
/*%
689 690
 * Utility macro for RR existence checking functions.
 *
691 692
 * If the variable 'result' has the value ISC_R_EXISTS or
 * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE,
693 694 695 696 697 698 699 700
 * 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.
 */
701 702 703
#define RETURN_EXISTENCE_FLAG				\
	return ((result == ISC_R_EXISTS) ?		\
		(*exists = ISC_TRUE, ISC_R_SUCCESS) :	\
704 705
		((result == ISC_R_SUCCESS) ?		\
		 (*exists = ISC_FALSE, ISC_R_SUCCESS) :	\
706
		 result))
707

708
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
709 710 711
 * Set '*exists' to true iff an rrset of the given type exists,
 * to false otherwise.
 */
712
static isc_result_t
713 714
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
715
	     isc_boolean_t *exists)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
716
{
717
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
718
	result = foreach_rr(db, ver, name, type, covers,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
719 720 721 722
			    rrset_exists_action, NULL);
	RETURN_EXISTENCE_FLAG;
}

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
/*%
 * Set '*visible' to true if the RRset exists and is part of the
 * visible zone.  Otherwise '*visible' is set to false unless a
 * error occurs.
 */
static isc_result_t
rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
	      dns_rdatatype_t type, isc_boolean_t *visible)
{
	isc_result_t result;
	dns_fixedname_t fixed;

	dns_fixedname_init(&fixed);
	result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD,
			     (isc_stdtime_t) 0, NULL,
			     dns_fixedname_name(&fixed), NULL, NULL);
	switch (result) {
	case ISC_R_SUCCESS:
		*visible = ISC_TRUE;
		break;
	/*
Francis Dupont's avatar
Francis Dupont committed
744
	 * Glue, obscured, deleted or replaced records.
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
	 */
	case DNS_R_DELEGATION:
	case DNS_R_DNAME:
	case DNS_R_CNAME:
	case DNS_R_NXDOMAIN:
	case DNS_R_NXRRSET:
	case DNS_R_EMPTYNAME:
	case DNS_R_COVERINGNSEC:
		*visible = ISC_FALSE;
		result = ISC_R_SUCCESS;
		break;
	default:
		break;
	}
	return (result);
}

762
/*%
763 764
 * Helper function for cname_incompatible_rrset_exists.
 */
765
static isc_result_t
766
cname_compatibility_action(void *data, dns_rdataset_t *rrset) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
767
	UNUSED(data);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
768
	if (rrset->type != dns_rdatatype_cname &&
769
	    ! dns_rdatatype_isdnssec(rrset->type))
770 771
		return (ISC_R_EXISTS);
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
772 773
}

774
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
775 776 777 778 779 780 781
 * 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.
 */
782
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
783 784
cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
				dns_name_t *name, isc_boolean_t *exists) {
785 786
	isc_result_t result;
	result = foreach_rrset(db, ver, name,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
787 788 789 790
			       cname_compatibility_action, NULL);
	RETURN_EXISTENCE_FLAG;
}

791
/*%
792 793
 * Helper function for rr_count().
 */
794
static isc_result_t
795
count_rr_action(void *data, rr_t *rr) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
796
	int *countp = data;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
797
	UNUSED(rr);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
798
	(*countp)++;
799
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
800 801
}

802
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
803 804
 * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'.
 */
805
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
806
rr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
Bob Halley's avatar
Bob Halley committed
807
	 dns_rdatatype_t type, dns_rdatatype_t covers, int *countp)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
808 809
{
	*countp = 0;
Bob Halley's avatar
Bob Halley committed
810
	return (foreach_rr(db, ver, name, type, covers,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
811 812 813
			   count_rr_action, countp));
}

814
/*%
815 816
 * Context struct and helper function for name_exists().
 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
817

818
static isc_result_t
819
name_exists_action(void *data, dns_rdataset_t *rrset) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
820 821
	UNUSED(data);
	UNUSED(rrset);
822
	return (ISC_R_EXISTS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
823 824
}

825
/*%
Andreas Gustafsson's avatar
Andreas Gustafsson committed
826 827
 * Set '*exists' to true iff the given name exists, to false otherwise.
 */
828
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
829 830 831
name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
	    isc_boolean_t *exists)
{
832
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
833 834 835 836 837
	result = foreach_rrset(db, ver, name,
			       name_exists_action, NULL);
	RETURN_EXISTENCE_FLAG;
}

838
/*
Francis Dupont's avatar
Francis Dupont committed
839
 *	'ssu_check_t' is used to pass the arguments to
840 841 842
 *	dns_ssutable_checkrules() to the callback function
 *	ssu_checkrule().
 */
843
typedef struct {
844
	/* The ownername of the record to be updated. */
845
	dns_name_t *name;
846 847

	/* The signature's name if the request was signed. */
848
	dns_name_t *signer;
849 850 851 852 853

	/* The address of the client if the request was received via TCP. */
	isc_netaddr_t *tcpaddr;

	/* The ssu table to check against. */
854
	dns_ssutable_t *table;
855 856 857

	/* the key used for TKEY requests */
	dst_key_t *key;
858 859 860
} ssu_check_t;

static isc_result_t
861
ssu_checkrule(void *data, dns_rdataset_t *rrset) {
862 863 864 865
	ssu_check_t *ssuinfo = data;
	isc_boolean_t result;

	/*
866
	 * If we're deleting all records, it's ok to delete RRSIG and NSEC even
867 868
	 * if we're normally not allowed to.
	 */
869 870
	if (rrset->type == dns_rdatatype_rrsig ||
	    rrset->type == dns_rdatatype_nsec)
871
		return (ISC_R_SUCCESS);
872
	result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer,
873
					 ssuinfo->name, ssuinfo->tcpaddr,
874
					 rrset->type, ssuinfo->key);
875 876 877 878 879
	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,
880
	     dns_ssutable_t *ssutable, dns_name_t *signer,
881
	     isc_netaddr_t *tcpaddr, dst_key_t *key)
882 883 884 885 886 887 888
{
	isc_result_t result;
	ssu_check_t ssuinfo;

	ssuinfo.name = name;
	ssuinfo.table = ssutable;
	ssuinfo.signer = signer;
889
	ssuinfo.tcpaddr = tcpaddr;
890
	ssuinfo.key = key;