xfrout.c 45.7 KB
Newer Older
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2013  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
 */

Evan Hunt's avatar
Evan Hunt committed
18
/* $Id$ */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
19 20 21

#include <config.h>

22
#include <isc/formatcheck.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
23
#include <isc/mem.h>
24
#include <isc/timer.h>
25
#include <isc/print.h>
26
#include <isc/stats.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
27
#include <isc/util.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
28

Mark Andrews's avatar
Mark Andrews committed
29 30
#include <dns/db.h>
#include <dns/dbiterator.h>
31
#include <dns/dlz.h>
32
#include <dns/fixedname.h>
Mark Andrews's avatar
Mark Andrews committed
33 34
#include <dns/journal.h>
#include <dns/message.h>
35
#include <dns/peer.h>
36
#include <dns/rdataclass.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
37 38 39
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
Mark Andrews's avatar
Mark Andrews committed
40
#include <dns/result.h>
41
#include <dns/rriterator.h>
Brian Wellington's avatar
Brian Wellington committed
42
#include <dns/soa.h>
43
#include <dns/stats.h>
44
#include <dns/timer.h>
45
#include <dns/tsig.h>
Mark Andrews's avatar
Mark Andrews committed
46
#include <dns/view.h>
47 48
#include <dns/zone.h>
#include <dns/zt.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
49 50

#include <named/client.h>
51
#include <named/log.h>
52
#include <named/server.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
53 54
#include <named/xfrout.h>

Automatic Updater's avatar
Automatic Updater committed
55
/*! \file
56
 * \brief
Andreas Gustafsson's avatar
Andreas Gustafsson committed
57 58 59 60 61
 * Outgoing AXFR and IXFR.
 */

/*
 * TODO:
62
 *  - IXFR over UDP
Andreas Gustafsson's avatar
Andreas Gustafsson committed
63 64
 */

65
#define XFROUT_COMMON_LOGARGS \
66
	ns_g_lctx, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT
67 68 69 70 71 72 73

#define XFROUT_PROTOCOL_LOGARGS \
	XFROUT_COMMON_LOGARGS, ISC_LOG_INFO

#define XFROUT_DEBUG_LOGARGS(n) \
	XFROUT_COMMON_LOGARGS, ISC_LOG_DEBUG(n)

74 75 76 77 78
#define XFROUT_RR_LOGARGS \
	XFROUT_COMMON_LOGARGS, XFROUT_RR_LOGLEVEL

#define XFROUT_RR_LOGLEVEL	ISC_LOG_DEBUG(8)

79
/*%
80
 * Fail unconditionally and log as a client error.
81 82
 * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
 * from complaining about "end-of-loop code not reached".
83 84 85
 */
#define FAILC(code, msg) \
	do {							\
86
		result = (code);				\
87 88 89
		ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
			   NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
			   "bad zone transfer request: %s (%s)", \
Automatic Updater's avatar
Automatic Updater committed
90
			   msg, isc_result_totext(code));	\
91
		if (result != ISC_R_SUCCESS) goto failure;	\
92 93
	} while (0)

94 95 96 97 98 99 100 101 102
#define FAILQ(code, msg, question, rdclass) \
	do {							\
		char _buf1[DNS_NAME_FORMATSIZE];		\
		char _buf2[DNS_RDATACLASS_FORMATSIZE]; 		\
		result = (code);				\
		dns_name_format(question, _buf1, sizeof(_buf1));  \
		dns_rdataclass_format(rdclass, _buf2, sizeof(_buf2)); \
		ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
			   NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
103
			   "bad zone transfer request: '%s/%s': %s (%s)", \
Automatic Updater's avatar
Automatic Updater committed
104
			   _buf1, _buf2, msg, isc_result_totext(code));	\
105 106 107
		if (result != ISC_R_SUCCESS) goto failure;	\
	} while (0)

108
#define CHECK(op) \
Automatic Updater's avatar
Automatic Updater committed
109
	do { result = (op); 					\
110
		if (result != ISC_R_SUCCESS) goto failure; 	\
111
	} while (0)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
112 113 114

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

115
static inline void
116 117
inc_stats(dns_zone_t *zone, isc_statscounter_t counter) {
	isc_stats_increment(ns_g_server->nsstats, counter);
118
	if (zone != NULL) {
119
		isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
120
		if (zonestats != NULL)
121
			isc_stats_increment(zonestats, counter);
122 123 124
	}
}

Andreas Gustafsson's avatar
Andreas Gustafsson committed
125 126
/**************************************************************************/

127
/*% Log an RR (for debugging) */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
128 129

static void
130
log_rr(dns_name_t *name, dns_rdata_t *rdata, isc_uint32_t ttl) {
131
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
132 133 134 135
	isc_buffer_t buf;
	char mem[2000];
	dns_rdatalist_t rdl;
	dns_rdataset_t rds;
Mark Andrews's avatar
Mark Andrews committed
136
	dns_rdata_t rd = DNS_RDATA_INIT;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
137 138 139 140

	rdl.type = rdata->type;
	rdl.rdclass = rdata->rdclass;
	rdl.ttl = ttl;
141 142 143 144 145
	if (rdata->type == dns_rdatatype_sig ||
	    rdata->type == dns_rdatatype_rrsig)
		rdl.covers = dns_rdata_covers(rdata);
	else
		rdl.covers = dns_rdatatype_none;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
146 147 148
	ISC_LIST_INIT(rdl.rdata);
	ISC_LINK_INIT(&rdl, link);
	dns_rdataset_init(&rds);
Mark Andrews's avatar
Mark Andrews committed
149 150 151
	dns_rdata_init(&rd);
	dns_rdata_clone(rdata, &rd);
	ISC_LIST_APPEND(rdl.rdata, &rd, link);
152
	RUNTIME_CHECK(dns_rdatalist_tordataset(&rdl, &rds) == ISC_R_SUCCESS);
153

154
	isc_buffer_init(&buf, mem, sizeof(mem));
Andreas Gustafsson's avatar
Andreas Gustafsson committed
155 156 157
	result = dns_rdataset_totext(&rds, name,
				     ISC_FALSE, ISC_FALSE, &buf);

158 159 160 161
	/*
	 * We could use xfrout_log(), but that would produce
	 * very long lines with a repetitive prefix.
	 */
162
	if (result == ISC_R_SUCCESS) {
163 164 165
		/*
		 * Get rid of final newline.
		 */
166
		INSIST(buf.used >= 1 &&
167
		       ((char *) buf.base)[buf.used - 1] == '\n');
168
		buf.used--;
Automatic Updater's avatar
Automatic Updater committed
169

170 171
		isc_log_write(XFROUT_RR_LOGARGS, "%.*s",
			      (int)isc_buffer_usedlength(&buf),
172
			      (char *)isc_buffer_base(&buf));
Andreas Gustafsson's avatar
Andreas Gustafsson committed
173
	} else {
174
		isc_log_write(XFROUT_RR_LOGARGS, "<RR too large to print>");
Andreas Gustafsson's avatar
Andreas Gustafsson committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
	}
}

/**************************************************************************/
/*
 * An 'rrstream_t' is a polymorphic iterator that returns
 * a stream of resource records.  There are multiple implementations,
 * e.g. for generating AXFR and IXFR records streams.
 */

typedef struct rrstream_methods rrstream_methods_t;

typedef struct rrstream {
	isc_mem_t 		*mctx;
	rrstream_methods_t	*methods;
} rrstream_t;

192
struct rrstream_methods {
193 194
	isc_result_t 		(*first)(rrstream_t *);
	isc_result_t 		(*next)(rrstream_t *);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
195 196 197 198
	void			(*current)(rrstream_t *,
					   dns_name_t **,
					   isc_uint32_t *,
					   dns_rdata_t **);
199
	void	 		(*pause)(rrstream_t *);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
200 201 202
	void 			(*destroy)(rrstream_t **);
};

203 204 205 206 207
static void
rrstream_noop_pause(rrstream_t *rs) {
	UNUSED(rs);
}

Andreas Gustafsson's avatar
Andreas Gustafsson committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
/**************************************************************************/
/*
 * An 'ixfr_rrstream_t' is an 'rrstream_t' that returns
 * an IXFR-like RR stream from a journal file.
 *
 * The SOA at the beginning of each sequence of additions
 * or deletions are included in the stream, but the extra
 * SOAs at the beginning and end of the entire transfer are
 * not included.
 */

typedef struct ixfr_rrstream {
	rrstream_t		common;
	dns_journal_t 		*journal;
} ixfr_rrstream_t;

/* Forward declarations. */
225 226 227
static void
ixfr_rrstream_destroy(rrstream_t **sp);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
228 229 230 231 232 233 234
static rrstream_methods_t ixfr_rrstream_methods;

/*
 * Returns: anything dns_journal_open() or dns_journal_iter_init()
 * may return.
 */

235
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
236
ixfr_rrstream_create(isc_mem_t *mctx,
237 238 239 240
		     const char *journal_filename,
		     isc_uint32_t begin_serial,
		     isc_uint32_t end_serial,
		     rrstream_t **sp)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
241 242
{
	ixfr_rrstream_t *s;
243
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
244 245 246 247 248

	INSIST(sp != NULL && *sp == NULL);

	s = isc_mem_get(mctx, sizeof(*s));
	if (s == NULL)
249
		return (ISC_R_NOMEMORY);
250 251
	s->common.mctx = NULL;
	isc_mem_attach(mctx, &s->common.mctx);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
252 253
	s->common.methods = &ixfr_rrstream_methods;
	s->journal = NULL;
254

255
	CHECK(dns_journal_open(mctx, journal_filename,
256
			       DNS_JOURNAL_READ, &s->journal));
Andreas Gustafsson's avatar
Andreas Gustafsson committed
257 258 259
	CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial));

	*sp = (rrstream_t *) s;
260
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
261 262

 failure:
Mark Andrews's avatar
Mark Andrews committed
263
	ixfr_rrstream_destroy((rrstream_t **) (void *)&s);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
264 265 266
	return (result);
}

267
static isc_result_t
268
ixfr_rrstream_first(rrstream_t *rs) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
269 270 271 272
	ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
	return (dns_journal_first_rr(s->journal));
}

273
static isc_result_t
274
ixfr_rrstream_next(rrstream_t *rs) {
275
	ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
276 277 278 279 280 281 282 283
	return (dns_journal_next_rr(s->journal));
}

static void
ixfr_rrstream_current(rrstream_t *rs,
		       dns_name_t **name, isc_uint32_t *ttl,
		       dns_rdata_t **rdata)
{
284
	ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
285 286 287 288 289 290 291 292
	dns_journal_current_rr(s->journal, name, ttl, rdata);
}

static void
ixfr_rrstream_destroy(rrstream_t **rsp) {
	ixfr_rrstream_t *s = (ixfr_rrstream_t *) *rsp;
	if (s->journal != 0)
		dns_journal_destroy(&s->journal);
293
	isc_mem_putanddetach(&s->common.mctx, s, sizeof(*s));
Andreas Gustafsson's avatar
Andreas Gustafsson committed
294 295
}

296
static rrstream_methods_t ixfr_rrstream_methods = {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
297 298
	ixfr_rrstream_first,
	ixfr_rrstream_next,
299
	ixfr_rrstream_current,
300
	rrstream_noop_pause,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
301 302 303 304 305
	ixfr_rrstream_destroy
};

/**************************************************************************/
/*
306
 * An 'axfr_rrstream_t' is an 'rrstream_t' that returns
Andreas Gustafsson's avatar
Andreas Gustafsson committed
307 308 309 310 311 312 313 314
 * an AXFR-like RR stream from a database.
 *
 * The SOAs at the beginning and end of the transfer are
 * not included in the stream.
 */

typedef struct axfr_rrstream {
	rrstream_t		common;
315
	dns_rriterator_t	it;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
316 317 318
	isc_boolean_t		it_valid;
} axfr_rrstream_t;

319 320 321 322 323 324
/*
 * Forward declarations.
 */
static void
axfr_rrstream_destroy(rrstream_t **rsp);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
325 326
static rrstream_methods_t axfr_rrstream_methods;

327
static isc_result_t
328 329
axfr_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
		     rrstream_t **sp)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
330 331
{
	axfr_rrstream_t *s;
332
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
333 334 335 336 337

	INSIST(sp != NULL && *sp == NULL);

	s = isc_mem_get(mctx, sizeof(*s));
	if (s == NULL)
338
		return (ISC_R_NOMEMORY);
339 340
	s->common.mctx = NULL;
	isc_mem_attach(mctx, &s->common.mctx);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
341 342 343
	s->common.methods = &axfr_rrstream_methods;
	s->it_valid = ISC_FALSE;

344
	CHECK(dns_rriterator_init(&s->it, db, ver, 0));
Andreas Gustafsson's avatar
Andreas Gustafsson committed
345 346 347
	s->it_valid = ISC_TRUE;

	*sp = (rrstream_t *) s;
348
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
349 350

 failure:
Mark Andrews's avatar
Mark Andrews committed
351
	axfr_rrstream_destroy((rrstream_t **) (void *)&s);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
352 353 354
	return (result);
}

355
static isc_result_t
356
axfr_rrstream_first(rrstream_t *rs) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
357
	axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
358
	isc_result_t result;
359
	result = dns_rriterator_first(&s->it);
360
	if (result != ISC_R_SUCCESS)
361
		return (result);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
362 363 364 365 366
	/* Skip SOA records. */
	for (;;) {
		dns_name_t *name_dummy = NULL;
		isc_uint32_t ttl_dummy;
		dns_rdata_t *rdata = NULL;
367 368
		dns_rriterator_current(&s->it, &name_dummy,
				       &ttl_dummy, NULL, &rdata);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
369 370
		if (rdata->type != dns_rdatatype_soa)
			break;
371
		result = dns_rriterator_next(&s->it);
372
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
373 374 375 376 377
			break;
	}
	return (result);
}

378
static isc_result_t
379
axfr_rrstream_next(rrstream_t *rs) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
380
	axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
381
	isc_result_t result;
382

Andreas Gustafsson's avatar
Andreas Gustafsson committed
383 384 385 386 387
	/* Skip SOA records. */
	for (;;) {
		dns_name_t *name_dummy = NULL;
		isc_uint32_t ttl_dummy;
		dns_rdata_t *rdata = NULL;
388
		result = dns_rriterator_next(&s->it);
389
		if (result != ISC_R_SUCCESS)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
390
			break;
391 392
		dns_rriterator_current(&s->it, &name_dummy,
				       &ttl_dummy, NULL, &rdata);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
393 394 395 396 397 398 399
		if (rdata->type != dns_rdatatype_soa)
			break;
	}
	return (result);
}

static void
400 401
axfr_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
		      dns_rdata_t **rdata)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
402 403
{
	axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
404
	dns_rriterator_current(&s->it, name, ttl, NULL, rdata);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
405 406
}

407 408 409
static void
axfr_rrstream_pause(rrstream_t *rs) {
	axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
410
	dns_rriterator_pause(&s->it);
411 412
}

Andreas Gustafsson's avatar
Andreas Gustafsson committed
413 414 415 416
static void
axfr_rrstream_destroy(rrstream_t **rsp) {
	axfr_rrstream_t *s = (axfr_rrstream_t *) *rsp;
	if (s->it_valid)
417
		dns_rriterator_destroy(&s->it);
418
	isc_mem_putanddetach(&s->common.mctx, s, sizeof(*s));
Andreas Gustafsson's avatar
Andreas Gustafsson committed
419 420
}

421
static rrstream_methods_t axfr_rrstream_methods = {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
422 423
	axfr_rrstream_first,
	axfr_rrstream_next,
424
	axfr_rrstream_current,
425
	axfr_rrstream_pause,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439
	axfr_rrstream_destroy
};

/**************************************************************************/
/*
 * An 'soa_rrstream_t' is a degenerate 'rrstream_t' that returns
 * a single SOA record.
 */

typedef struct soa_rrstream {
	rrstream_t		common;
	dns_difftuple_t 	*soa_tuple;
} soa_rrstream_t;

440 441 442 443 444 445
/*
 * Forward declarations.
 */
static void
soa_rrstream_destroy(rrstream_t **rsp);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
446 447
static rrstream_methods_t soa_rrstream_methods;

448
static isc_result_t
449
soa_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
450 451 452
		    rrstream_t **sp)
{
	soa_rrstream_t *s;
453
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
454 455 456 457 458

	INSIST(sp != NULL && *sp == NULL);

	s = isc_mem_get(mctx, sizeof(*s));
	if (s == NULL)
459
		return (ISC_R_NOMEMORY);
460 461
	s->common.mctx = NULL;
	isc_mem_attach(mctx, &s->common.mctx);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
462 463
	s->common.methods = &soa_rrstream_methods;
	s->soa_tuple = NULL;
464

Andreas Gustafsson's avatar
Andreas Gustafsson committed
465 466 467 468
	CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
				    &s->soa_tuple));

	*sp = (rrstream_t *) s;
469
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
470 471

 failure:
Mark Andrews's avatar
Mark Andrews committed
472
	soa_rrstream_destroy((rrstream_t **) (void *)&s);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
473 474 475
	return (result);
}

476
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
477
soa_rrstream_first(rrstream_t *rs) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
478
	UNUSED(rs);
479
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
480 481
}

482
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
483
soa_rrstream_next(rrstream_t *rs) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
484
	UNUSED(rs);
485
	return (ISC_R_NOMORE);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
486 487 488
}

static void
489
soa_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
490 491
		     dns_rdata_t **rdata)
{
492
	soa_rrstream_t *s = (soa_rrstream_t *) rs;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
493 494 495 496 497 498 499 500 501 502
	*name = &s->soa_tuple->name;
	*ttl = s->soa_tuple->ttl;
	*rdata = &s->soa_tuple->rdata;
}

static void
soa_rrstream_destroy(rrstream_t **rsp) {
	soa_rrstream_t *s = (soa_rrstream_t *) *rsp;
	if (s->soa_tuple != NULL)
		dns_difftuple_free(&s->soa_tuple);
503
	isc_mem_putanddetach(&s->common.mctx, s, sizeof(*s));
Andreas Gustafsson's avatar
Andreas Gustafsson committed
504 505
}

506
static rrstream_methods_t soa_rrstream_methods = {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
507 508
	soa_rrstream_first,
	soa_rrstream_next,
509
	soa_rrstream_current,
510
	rrstream_noop_pause,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
511 512 513 514 515 516 517 518 519 520
	soa_rrstream_destroy
};

/**************************************************************************/
/*
 * A 'compound_rrstream_t' objects owns a soa_rrstream
 * and another rrstream, the "data stream".  It returns
 * a concatenated stream consisting of the soa_rrstream, then
 * the data stream, then the soa_rrstream again.
 *
521
 * The component streams are owned by the compound_rrstream_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
522 523 524 525 526 527 528 529 530 531
 * and are destroyed with it.
 */

typedef struct compound_rrstream {
	rrstream_t		common;
	rrstream_t		*components[3];
	int			state;
	isc_result_t		result;
} compound_rrstream_t;

532 533 534 535 536 537 538 539 540
/*
 * Forward declarations.
 */
static void
compound_rrstream_destroy(rrstream_t **rsp);

static isc_result_t
compound_rrstream_next(rrstream_t *rs);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
static rrstream_methods_t compound_rrstream_methods;

/*
 * Requires:
 *	soa_stream != NULL && *soa_stream != NULL
 *	data_stream != NULL && *data_stream != NULL
 *	sp != NULL && *sp == NULL
 *
 * Ensures:
 *	*soa_stream == NULL
 *	*data_stream == NULL
 *	*sp points to a valid compound_rrstream_t
 *	The soa and data streams will be destroyed
 *	when the compound_rrstream_t is destroyed.
 */
556
static isc_result_t
557 558
compound_rrstream_create(isc_mem_t *mctx, rrstream_t **soa_stream,
			 rrstream_t **data_stream, rrstream_t **sp)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
559 560 561 562 563 564 565
{
	compound_rrstream_t *s;

	INSIST(sp != NULL && *sp == NULL);

	s = isc_mem_get(mctx, sizeof(*s));
	if (s == NULL)
566
		return (ISC_R_NOMEMORY);
567 568
	s->common.mctx = NULL;
	isc_mem_attach(mctx, &s->common.mctx);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
569 570 571 572 573 574 575 576 577 578
	s->common.methods = &compound_rrstream_methods;
	s->components[0] = *soa_stream;
	s->components[1] = *data_stream;
	s->components[2] = *soa_stream;
	s->state = -1;
	s->result = ISC_R_FAILURE;

	*soa_stream = NULL;
	*data_stream = NULL;
	*sp = (rrstream_t *) s;
579
	return (ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
580 581
}

582
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
583 584 585 586 587 588
compound_rrstream_first(rrstream_t *rs) {
	compound_rrstream_t *s = (compound_rrstream_t *) rs;
	s->state = 0;
	do {
		rrstream_t *curstream = s->components[s->state];
		s->result = curstream->methods->first(curstream);
589
	} while (s->result == ISC_R_NOMORE && s->state < 2);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
590 591 592
	return (s->result);
}

593
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
594 595
compound_rrstream_next(rrstream_t *rs) {
	compound_rrstream_t *s = (compound_rrstream_t *) rs;
596
	rrstream_t *curstream = s->components[s->state];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
597
	s->result = curstream->methods->next(curstream);
598
	while (s->result == ISC_R_NOMORE) {
599 600 601 602 603
		/*
		 * Make sure locks held by the current stream
		 * are released before we switch streams.
		 */
		curstream->methods->pause(curstream);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
604
		if (s->state == 2)
605
			return (ISC_R_NOMORE);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
606 607 608 609 610 611 612 613
		s->state++;
		curstream = s->components[s->state];
		s->result = curstream->methods->first(curstream);
	}
	return (s->result);
}

static void
614 615
compound_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
			  dns_rdata_t **rdata)
Andreas Gustafsson's avatar
Andreas Gustafsson committed
616 617 618 619
{
	compound_rrstream_t *s = (compound_rrstream_t *) rs;
	rrstream_t *curstream;
	INSIST(0 <= s->state && s->state < 3);
620
	INSIST(s->result == ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
621 622 623 624
	curstream = s->components[s->state];
	curstream->methods->current(curstream, name, ttl, rdata);
}

625 626 627 628 629 630 631 632 633 634
static void
compound_rrstream_pause(rrstream_t *rs)
{
	compound_rrstream_t *s = (compound_rrstream_t *) rs;
	rrstream_t *curstream;
	INSIST(0 <= s->state && s->state < 3);
	curstream = s->components[s->state];
	curstream->methods->pause(curstream);
}

Andreas Gustafsson's avatar
Andreas Gustafsson committed
635 636 637 638 639 640
static void
compound_rrstream_destroy(rrstream_t **rsp) {
	compound_rrstream_t *s = (compound_rrstream_t *) *rsp;
	s->components[0]->methods->destroy(&s->components[0]);
	s->components[1]->methods->destroy(&s->components[1]);
	s->components[2] = NULL; /* Copy of components[0]. */
641
	isc_mem_putanddetach(&s->common.mctx, s, sizeof(*s));
Andreas Gustafsson's avatar
Andreas Gustafsson committed
642 643
}

644
static rrstream_methods_t compound_rrstream_methods = {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
645 646
	compound_rrstream_first,
	compound_rrstream_next,
647
	compound_rrstream_current,
648
	compound_rrstream_pause,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
649 650 651 652 653
	compound_rrstream_destroy
};

/**************************************************************************/
/*
Andreas Gustafsson's avatar
typo  
Andreas Gustafsson committed
654
 * An 'xfrout_ctx_t' contains the state of an outgoing AXFR or IXFR
Andreas Gustafsson's avatar
Andreas Gustafsson committed
655 656 657 658 659 660 661 662 663
 * in progress.
 */

typedef struct {
	isc_mem_t 		*mctx;
	ns_client_t		*client;
	unsigned int 		id;		/* ID of request */
	dns_name_t		*qname;		/* Question name of request */
	dns_rdatatype_t		qtype;		/* dns_rdatatype_{a,i}xfr */
664
	dns_rdataclass_t	qclass;
665
	dns_zone_t 		*zone;		/* (necessary for stats) */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
666 667
	dns_db_t 		*db;
	dns_dbversion_t 	*ver;
668
	isc_quota_t		*quota;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
669
	rrstream_t 		*stream;	/* The XFR RR stream */
670
	isc_boolean_t		end_of_stream;	/* EOS has been reached */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
671 672 673 674 675 676 677
	isc_buffer_t 		buf;		/* Buffer for message owner
						   names and rdatas */
	isc_buffer_t 		txlenbuf;	/* Transmit length buffer */
	isc_buffer_t		txbuf;		/* Transmit message buffer */
	void 			*txmem;
	unsigned int 		txmemlen;
	unsigned int		nmsg;		/* Number of messages sent */
678
	dns_tsigkey_t		*tsigkey;	/* Key used to create TSIG */
679
	isc_buffer_t		*lasttsig;	/* the last TSIG */
680
	isc_boolean_t		many_answers;
681 682
	int			sends;		/* Send in progress */
	isc_boolean_t		shuttingdown;
683
	const char		*mnemonic;	/* Style of transfer */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
684 685
} xfrout_ctx_t;

686
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
687 688
xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client,
		  unsigned int id, dns_name_t *qname, dns_rdatatype_t qtype,
689
		  dns_rdataclass_t qclass, dns_zone_t *zone,
690
		  dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
691
		  rrstream_t *stream, dns_tsigkey_t *tsigkey,
692
		  isc_buffer_t *lasttsig,
693 694
		  unsigned int maxtime,
		  unsigned int idletime,
695
		  isc_boolean_t many_answers,
696
		  xfrout_ctx_t **xfrp);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
697

698 699 700 701 702 703 704
static void
sendstream(xfrout_ctx_t *xfr);

static void
xfrout_senddone(isc_task_t *task, isc_event_t *event);

static void
David Lawrence's avatar
David Lawrence committed
705
xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg);
706 707 708 709 710 711 712 713 714

static void
xfrout_maybe_destroy(xfrout_ctx_t *xfr);

static void
xfrout_ctx_destroy(xfrout_ctx_t **xfrp);

static void
xfrout_client_shutdown(void *arg, isc_result_t result);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
715

716
static void
717 718 719
xfrout_log1(ns_client_t *client, dns_name_t *zonename,
	    dns_rdataclass_t rdclass, int level,
	    const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6);
720 721

static void
722
xfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...)
723
	   ISC_FORMAT_PRINTF(3, 4);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
724 725 726 727

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

void
728
ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
729
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
730 731
	dns_name_t *question_name;
	dns_rdataset_t *question_rdataset;
732
	dns_zone_t *zone = NULL, *raw = NULL, *mayberaw;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
733 734 735 736 737 738 739 740 741
	dns_db_t *db = NULL;
	dns_dbversion_t *ver = NULL;
	dns_rdataclass_t question_class;
	rrstream_t *soa_stream = NULL;
	rrstream_t *data_stream = NULL;
	rrstream_t *stream = NULL;
	dns_difftuple_t *current_soa_tuple = NULL;
	dns_name_t *soa_name;
	dns_rdataset_t *soa_rdataset;
742
	dns_rdata_t soa_rdata = DNS_RDATA_INIT;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
743
	isc_boolean_t have_soa = ISC_FALSE;
744
	const char *mnemonic = NULL;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
745 746 747
	isc_mem_t *mctx = client->mctx;
	dns_message_t *request = client->message;
	xfrout_ctx_t *xfr = NULL;
748
	isc_quota_t *quota = NULL;
749
	dns_transfer_format_t format = client->view->transfer_format;
750 751
	isc_netaddr_t na;
	dns_peer_t *peer = NULL;
752
	isc_buffer_t *tsigbuf = NULL;
753
	char *journalfile;
754
	char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")];
755
	char keyname[DNS_NAME_FORMATSIZE];
756
	isc_boolean_t is_poll = ISC_FALSE;
757
	isc_boolean_t is_dlz = ISC_FALSE;
758 759
	isc_boolean_t is_ixfr = ISC_FALSE;
	isc_uint32_t begin_serial = 0, current_serial;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
760 761 762 763 764 765 766 767 768 769 770 771

	switch (reqtype) {
	case dns_rdatatype_axfr:
		mnemonic = "AXFR";
		break;
	case dns_rdatatype_ixfr:
		mnemonic = "IXFR";
		break;
	default:
		INSIST(0);
		break;
	}
772

773
	ns_client_log(client,
774 775
		      DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT,
		      ISC_LOG_DEBUG(6), "%s request", mnemonic);
776
	/*
777
	 * Apply quota.
778
	 */
779
	result = isc_quota_attach(&ns_g_server->xfroutquota, &quota);
780
	if (result != ISC_R_SUCCESS) {
781
		isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING,
782
			      "%s request denied: %s", mnemonic,
783 784 785 786
			      isc_result_totext(result));
		goto failure;
	}

Andreas Gustafsson's avatar
Andreas Gustafsson committed
787 788 789 790
	/*
	 * Interpret the question section.
	 */
	result = dns_message_firstname(request, DNS_SECTION_QUESTION);
791
	INSIST(result == ISC_R_SUCCESS);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
792 793 794 795 796 797 798 799 800 801 802

	/*
	 * The question section must contain exactly one question, and
	 * it must be for AXFR/IXFR as appropriate.
	 */
	question_name = NULL;
	dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name);
	question_rdataset = ISC_LIST_HEAD(question_name->list);
	question_class = question_rdataset->rdclass;
	INSIST(question_rdataset->type == reqtype);
	if (ISC_LIST_NEXT(question_rdataset, link) != NULL)
803
		FAILC(DNS_R_FORMERR, "multiple questions");
Andreas Gustafsson's avatar
Andreas Gustafsson committed
804
	result = dns_message_nextname(request, DNS_SECTION_QUESTION);
805
	if (result != ISC_R_NOMORE)
806
		FAILC(DNS_R_FORMERR, "multiple questions");
Andreas Gustafsson's avatar
Andreas Gustafsson committed
807

Bob Halley's avatar
Bob Halley committed
808 809
	result = dns_zt_find(client->view->zonetable, question_name, 0, NULL,
			     &zone);
810

811
	if (result != ISC_R_SUCCESS) {
812
		/*
Mark Andrews's avatar
Mark Andrews committed
813 814
		 * Normal zone table does not have a match.
		 * Try the DLZ database
815
		 */
Evan Hunt's avatar
Evan Hunt committed
816 817
		// Temporary: only searching the first DLZ database
		if (! ISC_LIST_EMPTY(client->view->dlz_searched)) {
818
			result = dns_dlzallowzonexfr(client->view,
Mark Andrews's avatar
Mark Andrews committed
819 820
						     question_name,
						     &client->peeraddr,
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
						     &db);

			if (result == ISC_R_NOPERM) {
				char _buf1[DNS_NAME_FORMATSIZE];
				char _buf2[DNS_RDATACLASS_FORMATSIZE];

				result = DNS_R_REFUSED;
				dns_name_format(question_name, _buf1,
						sizeof(_buf1));
				dns_rdataclass_format(question_class,
						      _buf2, sizeof(_buf2));
				ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
					      NS_LOGMODULE_XFER_OUT,
					      ISC_LOG_ERROR,
					      "zone transfer '%s/%s' denied",
					      _buf1, _buf2);
				goto failure;
			}
			if (result != ISC_R_SUCCESS)
840 841
				FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
				      question_name, question_class);
842 843 844
			is_dlz = ISC_TRUE;
		} else {
			/*
Automatic Updater's avatar
Automatic Updater committed
845
			 * not DLZ and not in normal zone table, we are
846 847 848 849 850 851 852 853
			 * not authoritative
			 */
			FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
			      question_name, question_class);
		}
	} else {
		/* zone table has a match */
		switch(dns_zone_gettype(zone)) {
854
			/* Master and slave zones are OK for transfer. */
855 856
			case dns_zone_master:
			case dns_zone_slave:
857
			case dns_zone_dlz:
858
				break;
859
			default:
860 861
				FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
				      question_name, question_class);
862 863 864
			}
		CHECK(dns_zone_getdb(zone, &db));
		dns_db_currentversion(db, &ver);
865
	}
866

867
	xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
868
		    "%s question section OK", mnemonic);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
869 870 871 872 873 874

	/*
	 * Check the authority section.  Look for a SOA record with
	 * the same name and class as the question.
	 */
	for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY);
875
	     result == ISC_R_SUCCESS;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
876 877 878
	     result = dns_message_nextname(request, DNS_SECTION_AUTHORITY))
	{
		soa_name = NULL;
879 880
		dns_message_currentname(request, DNS_SECTION_AUTHORITY,
					&soa_name);
881

882 883 884
		/*
		 * Ignore data whose owner name is not the zone apex.
		 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
885 886
		if (! dns_name_equal(soa_name, question_name))
			continue;
887

Andreas Gustafsson's avatar
Andreas Gustafsson committed
888 889 890 891
		for (soa_rdataset = ISC_LIST_HEAD(soa_name->list);
		     soa_rdataset != NULL;
		     soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link))
		{
892 893 894
			/*
			 * Ignore non-SOA data.
			 */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
895 896 897 898 899 900 901 902
			if (soa_rdataset->type != dns_rdatatype_soa)
				continue;
			if (soa_rdataset->rdclass != question_class)
				continue;

			CHECK(dns_rdataset_first(soa_rdataset));
			dns_rdataset_current(soa_rdataset, &soa_rdata);
			result = dns_rdataset_next(soa_rdataset);
903
			if (result == ISC_R_SUCCESS)
904 905 906
				FAILC(DNS_R_FORMERR,
				      "IXFR authority section "
				      "has multiple SOAs");
Andreas Gustafsson's avatar
Andreas Gustafsson committed