xfrout.c revision 218384
1135446Strhodes/*
2218384Sdougb * Copyright (C) 2004-2010  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2003  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18218384Sdougb/* $Id: xfrout.c,v 1.131.26.6 2010-05-27 23:48:18 tbox Exp $ */
19135446Strhodes
20135446Strhodes#include <config.h>
21135446Strhodes
22135446Strhodes#include <isc/formatcheck.h>
23135446Strhodes#include <isc/mem.h>
24135446Strhodes#include <isc/timer.h>
25135446Strhodes#include <isc/print.h>
26193149Sdougb#include <isc/stats.h>
27135446Strhodes#include <isc/util.h>
28135446Strhodes
29135446Strhodes#include <dns/db.h>
30135446Strhodes#include <dns/dbiterator.h>
31170222Sdougb#ifdef DLZ
32170222Sdougb#include <dns/dlz.h>
33170222Sdougb#endif
34135446Strhodes#include <dns/fixedname.h>
35135446Strhodes#include <dns/journal.h>
36135446Strhodes#include <dns/message.h>
37135446Strhodes#include <dns/peer.h>
38135446Strhodes#include <dns/rdataclass.h>
39135446Strhodes#include <dns/rdatalist.h>
40135446Strhodes#include <dns/rdataset.h>
41135446Strhodes#include <dns/rdatasetiter.h>
42135446Strhodes#include <dns/result.h>
43135446Strhodes#include <dns/soa.h>
44193149Sdougb#include <dns/stats.h>
45135446Strhodes#include <dns/timer.h>
46135446Strhodes#include <dns/tsig.h>
47135446Strhodes#include <dns/view.h>
48135446Strhodes#include <dns/zone.h>
49135446Strhodes#include <dns/zt.h>
50135446Strhodes
51135446Strhodes#include <named/client.h>
52135446Strhodes#include <named/log.h>
53135446Strhodes#include <named/server.h>
54135446Strhodes#include <named/xfrout.h>
55135446Strhodes
56193149Sdougb/*! \file
57170222Sdougb * \brief
58135446Strhodes * Outgoing AXFR and IXFR.
59135446Strhodes */
60135446Strhodes
61135446Strhodes/*
62135446Strhodes * TODO:
63135446Strhodes *  - IXFR over UDP
64135446Strhodes */
65135446Strhodes
66135446Strhodes#define XFROUT_COMMON_LOGARGS \
67135446Strhodes	ns_g_lctx, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT
68135446Strhodes
69135446Strhodes#define XFROUT_PROTOCOL_LOGARGS \
70135446Strhodes	XFROUT_COMMON_LOGARGS, ISC_LOG_INFO
71135446Strhodes
72135446Strhodes#define XFROUT_DEBUG_LOGARGS(n) \
73135446Strhodes	XFROUT_COMMON_LOGARGS, ISC_LOG_DEBUG(n)
74135446Strhodes
75135446Strhodes#define XFROUT_RR_LOGARGS \
76135446Strhodes	XFROUT_COMMON_LOGARGS, XFROUT_RR_LOGLEVEL
77135446Strhodes
78135446Strhodes#define XFROUT_RR_LOGLEVEL	ISC_LOG_DEBUG(8)
79135446Strhodes
80170222Sdougb/*%
81135446Strhodes * Fail unconditionally and log as a client error.
82135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
83135446Strhodes * from complaining about "end-of-loop code not reached".
84135446Strhodes */
85135446Strhodes#define FAILC(code, msg) \
86135446Strhodes	do {							\
87135446Strhodes		result = (code);				\
88135446Strhodes		ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
89135446Strhodes			   NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
90135446Strhodes			   "bad zone transfer request: %s (%s)", \
91193149Sdougb			   msg, isc_result_totext(code));	\
92135446Strhodes		if (result != ISC_R_SUCCESS) goto failure;	\
93135446Strhodes	} while (0)
94135446Strhodes
95135446Strhodes#define FAILQ(code, msg, question, rdclass) \
96135446Strhodes	do {							\
97135446Strhodes		char _buf1[DNS_NAME_FORMATSIZE];		\
98135446Strhodes		char _buf2[DNS_RDATACLASS_FORMATSIZE]; 		\
99135446Strhodes		result = (code);				\
100135446Strhodes		dns_name_format(question, _buf1, sizeof(_buf1));  \
101135446Strhodes		dns_rdataclass_format(rdclass, _buf2, sizeof(_buf2)); \
102135446Strhodes		ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
103135446Strhodes			   NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
104135446Strhodes			   "bad zone transfer request: '%s/%s': %s (%s)", \
105193149Sdougb			   _buf1, _buf2, msg, isc_result_totext(code));	\
106135446Strhodes		if (result != ISC_R_SUCCESS) goto failure;	\
107135446Strhodes	} while (0)
108135446Strhodes
109135446Strhodes#define CHECK(op) \
110193149Sdougb	do { result = (op); 					\
111135446Strhodes		if (result != ISC_R_SUCCESS) goto failure; 	\
112135446Strhodes	} while (0)
113135446Strhodes
114135446Strhodes/**************************************************************************/
115170222Sdougb/*%
116135446Strhodes * A db_rr_iterator_t is an iterator that iterates over an entire database,
117135446Strhodes * returning one RR at a time, in some arbitrary order.
118135446Strhodes */
119135446Strhodes
120135446Strhodestypedef struct db_rr_iterator db_rr_iterator_t;
121135446Strhodes
122170222Sdougb/*% db_rr_iterator structure */
123135446Strhodesstruct db_rr_iterator {
124135446Strhodes	isc_result_t		result;
125135446Strhodes	dns_db_t		*db;
126193149Sdougb	dns_dbiterator_t 	*dbit;
127135446Strhodes	dns_dbversion_t 	*ver;
128135446Strhodes	isc_stdtime_t		now;
129135446Strhodes	dns_dbnode_t		*node;
130135446Strhodes	dns_fixedname_t		fixedname;
131193149Sdougb	dns_rdatasetiter_t 	*rdatasetit;
132135446Strhodes	dns_rdataset_t 		rdataset;
133135446Strhodes	dns_rdata_t		rdata;
134135446Strhodes};
135135446Strhodes
136135446Strhodesstatic isc_result_t
137135446Strhodesdb_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
138135446Strhodes		    isc_stdtime_t now);
139135446Strhodes
140135446Strhodesstatic isc_result_t
141135446Strhodesdb_rr_iterator_first(db_rr_iterator_t *it);
142135446Strhodes
143135446Strhodesstatic isc_result_t
144135446Strhodesdb_rr_iterator_next(db_rr_iterator_t *it);
145135446Strhodes
146135446Strhodesstatic void
147135446Strhodesdb_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name,
148135446Strhodes		       isc_uint32_t *ttl, dns_rdata_t **rdata);
149135446Strhodes
150135446Strhodesstatic void
151135446Strhodesdb_rr_iterator_destroy(db_rr_iterator_t *it);
152135446Strhodes
153193149Sdougbstatic inline void
154193149Sdougbinc_stats(dns_zone_t *zone, isc_statscounter_t counter) {
155193149Sdougb	isc_stats_increment(ns_g_server->nsstats, counter);
156193149Sdougb	if (zone != NULL) {
157193149Sdougb		isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
158193149Sdougb		if (zonestats != NULL)
159193149Sdougb			isc_stats_increment(zonestats, counter);
160193149Sdougb	}
161193149Sdougb}
162193149Sdougb
163135446Strhodesstatic isc_result_t
164135446Strhodesdb_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
165135446Strhodes		    isc_stdtime_t now)
166135446Strhodes{
167135446Strhodes	isc_result_t result;
168135446Strhodes	it->db = db;
169135446Strhodes	it->dbit = NULL;
170135446Strhodes	it->ver = ver;
171135446Strhodes	it->now = now;
172135446Strhodes	it->node = NULL;
173193149Sdougb	result = dns_db_createiterator(it->db, 0, &it->dbit);
174135446Strhodes	if (result != ISC_R_SUCCESS)
175135446Strhodes		return (result);
176135446Strhodes	it->rdatasetit = NULL;
177135446Strhodes	dns_rdata_init(&it->rdata);
178135446Strhodes	dns_rdataset_init(&it->rdataset);
179135446Strhodes	dns_fixedname_init(&it->fixedname);
180135446Strhodes	INSIST(! dns_rdataset_isassociated(&it->rdataset));
181135446Strhodes	it->result = ISC_R_SUCCESS;
182135446Strhodes	return (it->result);
183135446Strhodes}
184135446Strhodes
185135446Strhodesstatic isc_result_t
186135446Strhodesdb_rr_iterator_first(db_rr_iterator_t *it) {
187135446Strhodes	it->result = dns_dbiterator_first(it->dbit);
188135446Strhodes	/*
189135446Strhodes	 * The top node may be empty when out of zone glue exists.
190135446Strhodes	 * Walk the tree to find the first node with data.
191135446Strhodes	 */
192135446Strhodes	while (it->result == ISC_R_SUCCESS) {
193135446Strhodes		it->result = dns_dbiterator_current(it->dbit, &it->node,
194135446Strhodes				    dns_fixedname_name(&it->fixedname));
195135446Strhodes		if (it->result != ISC_R_SUCCESS)
196135446Strhodes			return (it->result);
197135446Strhodes
198135446Strhodes		it->result = dns_db_allrdatasets(it->db, it->node,
199135446Strhodes						 it->ver, it->now,
200135446Strhodes						 &it->rdatasetit);
201135446Strhodes		if (it->result != ISC_R_SUCCESS)
202135446Strhodes			return (it->result);
203135446Strhodes
204135446Strhodes		it->result = dns_rdatasetiter_first(it->rdatasetit);
205135446Strhodes		if (it->result != ISC_R_SUCCESS) {
206135446Strhodes			/*
207135446Strhodes			 * This node is empty. Try next node.
208135446Strhodes			 */
209135446Strhodes			dns_rdatasetiter_destroy(&it->rdatasetit);
210135446Strhodes			dns_db_detachnode(it->db, &it->node);
211135446Strhodes			it->result = dns_dbiterator_next(it->dbit);
212135446Strhodes			continue;
213135446Strhodes		}
214135446Strhodes		dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
215170222Sdougb		it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
216135446Strhodes		it->result = dns_rdataset_first(&it->rdataset);
217135446Strhodes		return (it->result);
218135446Strhodes	}
219135446Strhodes	return (it->result);
220135446Strhodes}
221135446Strhodes
222135446Strhodes
223135446Strhodesstatic isc_result_t
224135446Strhodesdb_rr_iterator_next(db_rr_iterator_t *it) {
225135446Strhodes	if (it->result != ISC_R_SUCCESS)
226135446Strhodes		return (it->result);
227135446Strhodes
228135446Strhodes	INSIST(it->dbit != NULL);
229135446Strhodes	INSIST(it->node != NULL);
230135446Strhodes	INSIST(it->rdatasetit != NULL);
231135446Strhodes
232135446Strhodes	it->result = dns_rdataset_next(&it->rdataset);
233135446Strhodes	if (it->result == ISC_R_NOMORE) {
234135446Strhodes		dns_rdataset_disassociate(&it->rdataset);
235135446Strhodes		it->result = dns_rdatasetiter_next(it->rdatasetit);
236135446Strhodes		/*
237135446Strhodes		 * The while loop body is executed more than once
238135446Strhodes		 * only when an empty dbnode needs to be skipped.
239135446Strhodes		 */
240135446Strhodes		while (it->result == ISC_R_NOMORE) {
241135446Strhodes			dns_rdatasetiter_destroy(&it->rdatasetit);
242135446Strhodes			dns_db_detachnode(it->db, &it->node);
243135446Strhodes			it->result = dns_dbiterator_next(it->dbit);
244135446Strhodes			if (it->result == ISC_R_NOMORE) {
245135446Strhodes				/* We are at the end of the entire database. */
246135446Strhodes				return (it->result);
247135446Strhodes			}
248135446Strhodes			if (it->result != ISC_R_SUCCESS)
249135446Strhodes				return (it->result);
250135446Strhodes			it->result = dns_dbiterator_current(it->dbit,
251135446Strhodes				    &it->node,
252135446Strhodes				    dns_fixedname_name(&it->fixedname));
253135446Strhodes			if (it->result != ISC_R_SUCCESS)
254135446Strhodes				return (it->result);
255135446Strhodes			it->result = dns_db_allrdatasets(it->db, it->node,
256135446Strhodes					 it->ver, it->now,
257135446Strhodes					 &it->rdatasetit);
258135446Strhodes			if (it->result != ISC_R_SUCCESS)
259135446Strhodes				return (it->result);
260135446Strhodes			it->result = dns_rdatasetiter_first(it->rdatasetit);
261135446Strhodes		}
262135446Strhodes		if (it->result != ISC_R_SUCCESS)
263135446Strhodes			return (it->result);
264135446Strhodes		dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
265170222Sdougb		it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
266135446Strhodes		it->result = dns_rdataset_first(&it->rdataset);
267135446Strhodes		if (it->result != ISC_R_SUCCESS)
268135446Strhodes			return (it->result);
269135446Strhodes	}
270135446Strhodes	return (it->result);
271135446Strhodes}
272135446Strhodes
273135446Strhodesstatic void
274135446Strhodesdb_rr_iterator_pause(db_rr_iterator_t *it) {
275135446Strhodes	RUNTIME_CHECK(dns_dbiterator_pause(it->dbit) == ISC_R_SUCCESS);
276135446Strhodes}
277135446Strhodes
278135446Strhodesstatic void
279135446Strhodesdb_rr_iterator_destroy(db_rr_iterator_t *it) {
280135446Strhodes	if (dns_rdataset_isassociated(&it->rdataset))
281135446Strhodes		dns_rdataset_disassociate(&it->rdataset);
282135446Strhodes	if (it->rdatasetit != NULL)
283135446Strhodes		dns_rdatasetiter_destroy(&it->rdatasetit);
284135446Strhodes	if (it->node != NULL)
285135446Strhodes		dns_db_detachnode(it->db, &it->node);
286135446Strhodes	dns_dbiterator_destroy(&it->dbit);
287135446Strhodes}
288135446Strhodes
289135446Strhodesstatic void
290135446Strhodesdb_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name,
291135446Strhodes		      isc_uint32_t *ttl, dns_rdata_t **rdata)
292135446Strhodes{
293135446Strhodes	REQUIRE(name != NULL && *name == NULL);
294135446Strhodes	REQUIRE(it->result == ISC_R_SUCCESS);
295135446Strhodes	*name = dns_fixedname_name(&it->fixedname);
296135446Strhodes	*ttl = it->rdataset.ttl;
297135446Strhodes	dns_rdata_reset(&it->rdata);
298135446Strhodes	dns_rdataset_current(&it->rdataset, &it->rdata);
299135446Strhodes	*rdata = &it->rdata;
300135446Strhodes}
301135446Strhodes
302135446Strhodes/**************************************************************************/
303135446Strhodes
304170222Sdougb/*% Log an RR (for debugging) */
305135446Strhodes
306135446Strhodesstatic void
307135446Strhodeslog_rr(dns_name_t *name, dns_rdata_t *rdata, isc_uint32_t ttl) {
308135446Strhodes	isc_result_t result;
309135446Strhodes	isc_buffer_t buf;
310135446Strhodes	char mem[2000];
311135446Strhodes	dns_rdatalist_t rdl;
312135446Strhodes	dns_rdataset_t rds;
313135446Strhodes	dns_rdata_t rd = DNS_RDATA_INIT;
314135446Strhodes
315135446Strhodes	rdl.type = rdata->type;
316135446Strhodes	rdl.rdclass = rdata->rdclass;
317135446Strhodes	rdl.ttl = ttl;
318193149Sdougb	if (rdata->type == dns_rdatatype_sig ||
319193149Sdougb	    rdata->type == dns_rdatatype_rrsig)
320193149Sdougb		rdl.covers = dns_rdata_covers(rdata);
321193149Sdougb	else
322193149Sdougb		rdl.covers = dns_rdatatype_none;
323135446Strhodes	ISC_LIST_INIT(rdl.rdata);
324135446Strhodes	ISC_LINK_INIT(&rdl, link);
325135446Strhodes	dns_rdataset_init(&rds);
326135446Strhodes	dns_rdata_init(&rd);
327135446Strhodes	dns_rdata_clone(rdata, &rd);
328135446Strhodes	ISC_LIST_APPEND(rdl.rdata, &rd, link);
329135446Strhodes	RUNTIME_CHECK(dns_rdatalist_tordataset(&rdl, &rds) == ISC_R_SUCCESS);
330135446Strhodes
331135446Strhodes	isc_buffer_init(&buf, mem, sizeof(mem));
332135446Strhodes	result = dns_rdataset_totext(&rds, name,
333135446Strhodes				     ISC_FALSE, ISC_FALSE, &buf);
334135446Strhodes
335135446Strhodes	/*
336135446Strhodes	 * We could use xfrout_log(), but that would produce
337135446Strhodes	 * very long lines with a repetitive prefix.
338135446Strhodes	 */
339135446Strhodes	if (result == ISC_R_SUCCESS) {
340135446Strhodes		/*
341135446Strhodes		 * Get rid of final newline.
342135446Strhodes		 */
343135446Strhodes		INSIST(buf.used >= 1 &&
344135446Strhodes		       ((char *) buf.base)[buf.used - 1] == '\n');
345135446Strhodes		buf.used--;
346193149Sdougb
347135446Strhodes		isc_log_write(XFROUT_RR_LOGARGS, "%.*s",
348135446Strhodes			      (int)isc_buffer_usedlength(&buf),
349135446Strhodes			      (char *)isc_buffer_base(&buf));
350135446Strhodes	} else {
351135446Strhodes		isc_log_write(XFROUT_RR_LOGARGS, "<RR too large to print>");
352135446Strhodes	}
353135446Strhodes}
354135446Strhodes
355135446Strhodes/**************************************************************************/
356135446Strhodes/*
357135446Strhodes * An 'rrstream_t' is a polymorphic iterator that returns
358135446Strhodes * a stream of resource records.  There are multiple implementations,
359135446Strhodes * e.g. for generating AXFR and IXFR records streams.
360135446Strhodes */
361135446Strhodes
362135446Strhodestypedef struct rrstream_methods rrstream_methods_t;
363135446Strhodes
364135446Strhodestypedef struct rrstream {
365135446Strhodes	isc_mem_t 		*mctx;
366135446Strhodes	rrstream_methods_t	*methods;
367135446Strhodes} rrstream_t;
368135446Strhodes
369135446Strhodesstruct rrstream_methods {
370135446Strhodes	isc_result_t 		(*first)(rrstream_t *);
371135446Strhodes	isc_result_t 		(*next)(rrstream_t *);
372135446Strhodes	void			(*current)(rrstream_t *,
373135446Strhodes					   dns_name_t **,
374135446Strhodes					   isc_uint32_t *,
375135446Strhodes					   dns_rdata_t **);
376135446Strhodes	void	 		(*pause)(rrstream_t *);
377135446Strhodes	void 			(*destroy)(rrstream_t **);
378135446Strhodes};
379135446Strhodes
380135446Strhodesstatic void
381135446Strhodesrrstream_noop_pause(rrstream_t *rs) {
382135446Strhodes	UNUSED(rs);
383135446Strhodes}
384135446Strhodes
385135446Strhodes/**************************************************************************/
386135446Strhodes/*
387135446Strhodes * An 'ixfr_rrstream_t' is an 'rrstream_t' that returns
388135446Strhodes * an IXFR-like RR stream from a journal file.
389135446Strhodes *
390135446Strhodes * The SOA at the beginning of each sequence of additions
391135446Strhodes * or deletions are included in the stream, but the extra
392135446Strhodes * SOAs at the beginning and end of the entire transfer are
393135446Strhodes * not included.
394135446Strhodes */
395135446Strhodes
396135446Strhodestypedef struct ixfr_rrstream {
397135446Strhodes	rrstream_t		common;
398135446Strhodes	dns_journal_t 		*journal;
399135446Strhodes} ixfr_rrstream_t;
400135446Strhodes
401135446Strhodes/* Forward declarations. */
402135446Strhodesstatic void
403135446Strhodesixfr_rrstream_destroy(rrstream_t **sp);
404135446Strhodes
405135446Strhodesstatic rrstream_methods_t ixfr_rrstream_methods;
406135446Strhodes
407135446Strhodes/*
408135446Strhodes * Returns: anything dns_journal_open() or dns_journal_iter_init()
409135446Strhodes * may return.
410135446Strhodes */
411135446Strhodes
412135446Strhodesstatic isc_result_t
413135446Strhodesixfr_rrstream_create(isc_mem_t *mctx,
414135446Strhodes		     const char *journal_filename,
415135446Strhodes		     isc_uint32_t begin_serial,
416135446Strhodes		     isc_uint32_t end_serial,
417135446Strhodes		     rrstream_t **sp)
418135446Strhodes{
419135446Strhodes	ixfr_rrstream_t *s;
420135446Strhodes	isc_result_t result;
421135446Strhodes
422135446Strhodes	INSIST(sp != NULL && *sp == NULL);
423135446Strhodes
424135446Strhodes	s = isc_mem_get(mctx, sizeof(*s));
425135446Strhodes	if (s == NULL)
426135446Strhodes		return (ISC_R_NOMEMORY);
427135446Strhodes	s->common.mctx = mctx;
428135446Strhodes	s->common.methods = &ixfr_rrstream_methods;
429135446Strhodes	s->journal = NULL;
430135446Strhodes
431135446Strhodes	CHECK(dns_journal_open(mctx, journal_filename,
432135446Strhodes			       ISC_FALSE, &s->journal));
433135446Strhodes	CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial));
434135446Strhodes
435135446Strhodes	*sp = (rrstream_t *) s;
436135446Strhodes	return (ISC_R_SUCCESS);
437135446Strhodes
438135446Strhodes failure:
439135446Strhodes	ixfr_rrstream_destroy((rrstream_t **) (void *)&s);
440135446Strhodes	return (result);
441135446Strhodes}
442135446Strhodes
443135446Strhodesstatic isc_result_t
444135446Strhodesixfr_rrstream_first(rrstream_t *rs) {
445135446Strhodes	ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
446135446Strhodes	return (dns_journal_first_rr(s->journal));
447135446Strhodes}
448135446Strhodes
449135446Strhodesstatic isc_result_t
450135446Strhodesixfr_rrstream_next(rrstream_t *rs) {
451135446Strhodes	ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
452135446Strhodes	return (dns_journal_next_rr(s->journal));
453135446Strhodes}
454135446Strhodes
455135446Strhodesstatic void
456135446Strhodesixfr_rrstream_current(rrstream_t *rs,
457135446Strhodes		       dns_name_t **name, isc_uint32_t *ttl,
458135446Strhodes		       dns_rdata_t **rdata)
459135446Strhodes{
460135446Strhodes	ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
461135446Strhodes	dns_journal_current_rr(s->journal, name, ttl, rdata);
462135446Strhodes}
463135446Strhodes
464135446Strhodesstatic void
465135446Strhodesixfr_rrstream_destroy(rrstream_t **rsp) {
466135446Strhodes	ixfr_rrstream_t *s = (ixfr_rrstream_t *) *rsp;
467135446Strhodes	if (s->journal != 0)
468135446Strhodes		dns_journal_destroy(&s->journal);
469135446Strhodes	isc_mem_put(s->common.mctx, s, sizeof(*s));
470135446Strhodes}
471135446Strhodes
472135446Strhodesstatic rrstream_methods_t ixfr_rrstream_methods = {
473135446Strhodes	ixfr_rrstream_first,
474135446Strhodes	ixfr_rrstream_next,
475135446Strhodes	ixfr_rrstream_current,
476135446Strhodes	rrstream_noop_pause,
477135446Strhodes	ixfr_rrstream_destroy
478135446Strhodes};
479135446Strhodes
480135446Strhodes/**************************************************************************/
481135446Strhodes/*
482135446Strhodes * An 'axfr_rrstream_t' is an 'rrstream_t' that returns
483135446Strhodes * an AXFR-like RR stream from a database.
484135446Strhodes *
485135446Strhodes * The SOAs at the beginning and end of the transfer are
486135446Strhodes * not included in the stream.
487135446Strhodes */
488135446Strhodes
489135446Strhodestypedef struct axfr_rrstream {
490135446Strhodes	rrstream_t		common;
491135446Strhodes	db_rr_iterator_t	it;
492135446Strhodes	isc_boolean_t		it_valid;
493135446Strhodes} axfr_rrstream_t;
494135446Strhodes
495135446Strhodes/*
496135446Strhodes * Forward declarations.
497135446Strhodes */
498135446Strhodesstatic void
499135446Strhodesaxfr_rrstream_destroy(rrstream_t **rsp);
500135446Strhodes
501135446Strhodesstatic rrstream_methods_t axfr_rrstream_methods;
502135446Strhodes
503135446Strhodesstatic isc_result_t
504135446Strhodesaxfr_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
505135446Strhodes		     rrstream_t **sp)
506135446Strhodes{
507135446Strhodes	axfr_rrstream_t *s;
508135446Strhodes	isc_result_t result;
509135446Strhodes
510135446Strhodes	INSIST(sp != NULL && *sp == NULL);
511135446Strhodes
512135446Strhodes	s = isc_mem_get(mctx, sizeof(*s));
513135446Strhodes	if (s == NULL)
514135446Strhodes		return (ISC_R_NOMEMORY);
515135446Strhodes	s->common.mctx = mctx;
516135446Strhodes	s->common.methods = &axfr_rrstream_methods;
517135446Strhodes	s->it_valid = ISC_FALSE;
518135446Strhodes
519135446Strhodes	CHECK(db_rr_iterator_init(&s->it, db, ver, 0));
520135446Strhodes	s->it_valid = ISC_TRUE;
521135446Strhodes
522135446Strhodes	*sp = (rrstream_t *) s;
523135446Strhodes	return (ISC_R_SUCCESS);
524135446Strhodes
525135446Strhodes failure:
526135446Strhodes	axfr_rrstream_destroy((rrstream_t **) (void *)&s);
527135446Strhodes	return (result);
528135446Strhodes}
529135446Strhodes
530135446Strhodesstatic isc_result_t
531135446Strhodesaxfr_rrstream_first(rrstream_t *rs) {
532135446Strhodes	axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
533135446Strhodes	isc_result_t result;
534135446Strhodes	result = db_rr_iterator_first(&s->it);
535135446Strhodes	if (result != ISC_R_SUCCESS)
536135446Strhodes		return (result);
537135446Strhodes	/* Skip SOA records. */
538135446Strhodes	for (;;) {
539135446Strhodes		dns_name_t *name_dummy = NULL;
540135446Strhodes		isc_uint32_t ttl_dummy;
541135446Strhodes		dns_rdata_t *rdata = NULL;
542135446Strhodes		db_rr_iterator_current(&s->it, &name_dummy,
543135446Strhodes				      &ttl_dummy, &rdata);
544135446Strhodes		if (rdata->type != dns_rdatatype_soa)
545135446Strhodes			break;
546135446Strhodes		result = db_rr_iterator_next(&s->it);
547135446Strhodes		if (result != ISC_R_SUCCESS)
548135446Strhodes			break;
549135446Strhodes	}
550135446Strhodes	return (result);
551135446Strhodes}
552135446Strhodes
553135446Strhodesstatic isc_result_t
554135446Strhodesaxfr_rrstream_next(rrstream_t *rs) {
555135446Strhodes	axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
556135446Strhodes	isc_result_t result;
557135446Strhodes
558135446Strhodes	/* Skip SOA records. */
559135446Strhodes	for (;;) {
560135446Strhodes		dns_name_t *name_dummy = NULL;
561135446Strhodes		isc_uint32_t ttl_dummy;
562135446Strhodes		dns_rdata_t *rdata = NULL;
563135446Strhodes		result = db_rr_iterator_next(&s->it);
564135446Strhodes		if (result != ISC_R_SUCCESS)
565135446Strhodes			break;
566135446Strhodes		db_rr_iterator_current(&s->it, &name_dummy,
567135446Strhodes				      &ttl_dummy, &rdata);
568135446Strhodes		if (rdata->type != dns_rdatatype_soa)
569135446Strhodes			break;
570135446Strhodes	}
571135446Strhodes	return (result);
572135446Strhodes}
573135446Strhodes
574135446Strhodesstatic void
575135446Strhodesaxfr_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
576135446Strhodes		      dns_rdata_t **rdata)
577135446Strhodes{
578135446Strhodes	axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
579135446Strhodes	db_rr_iterator_current(&s->it, name, ttl, rdata);
580135446Strhodes}
581135446Strhodes
582135446Strhodesstatic void
583135446Strhodesaxfr_rrstream_pause(rrstream_t *rs) {
584135446Strhodes	axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
585135446Strhodes	db_rr_iterator_pause(&s->it);
586135446Strhodes}
587135446Strhodes
588135446Strhodesstatic void
589135446Strhodesaxfr_rrstream_destroy(rrstream_t **rsp) {
590135446Strhodes	axfr_rrstream_t *s = (axfr_rrstream_t *) *rsp;
591135446Strhodes	if (s->it_valid)
592135446Strhodes		db_rr_iterator_destroy(&s->it);
593135446Strhodes	isc_mem_put(s->common.mctx, s, sizeof(*s));
594135446Strhodes}
595135446Strhodes
596135446Strhodesstatic rrstream_methods_t axfr_rrstream_methods = {
597135446Strhodes	axfr_rrstream_first,
598135446Strhodes	axfr_rrstream_next,
599135446Strhodes	axfr_rrstream_current,
600135446Strhodes	axfr_rrstream_pause,
601135446Strhodes	axfr_rrstream_destroy
602135446Strhodes};
603135446Strhodes
604135446Strhodes/**************************************************************************/
605135446Strhodes/*
606135446Strhodes * An 'soa_rrstream_t' is a degenerate 'rrstream_t' that returns
607135446Strhodes * a single SOA record.
608135446Strhodes */
609135446Strhodes
610135446Strhodestypedef struct soa_rrstream {
611135446Strhodes	rrstream_t		common;
612135446Strhodes	dns_difftuple_t 	*soa_tuple;
613135446Strhodes} soa_rrstream_t;
614135446Strhodes
615135446Strhodes/*
616135446Strhodes * Forward declarations.
617135446Strhodes */
618135446Strhodesstatic void
619135446Strhodessoa_rrstream_destroy(rrstream_t **rsp);
620135446Strhodes
621135446Strhodesstatic rrstream_methods_t soa_rrstream_methods;
622135446Strhodes
623135446Strhodesstatic isc_result_t
624135446Strhodessoa_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
625135446Strhodes		    rrstream_t **sp)
626135446Strhodes{
627135446Strhodes	soa_rrstream_t *s;
628135446Strhodes	isc_result_t result;
629135446Strhodes
630135446Strhodes	INSIST(sp != NULL && *sp == NULL);
631135446Strhodes
632135446Strhodes	s = isc_mem_get(mctx, sizeof(*s));
633135446Strhodes	if (s == NULL)
634135446Strhodes		return (ISC_R_NOMEMORY);
635135446Strhodes	s->common.mctx = mctx;
636135446Strhodes	s->common.methods = &soa_rrstream_methods;
637135446Strhodes	s->soa_tuple = NULL;
638135446Strhodes
639135446Strhodes	CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
640135446Strhodes				    &s->soa_tuple));
641135446Strhodes
642135446Strhodes	*sp = (rrstream_t *) s;
643135446Strhodes	return (ISC_R_SUCCESS);
644135446Strhodes
645135446Strhodes failure:
646135446Strhodes	soa_rrstream_destroy((rrstream_t **) (void *)&s);
647135446Strhodes	return (result);
648135446Strhodes}
649135446Strhodes
650135446Strhodesstatic isc_result_t
651135446Strhodessoa_rrstream_first(rrstream_t *rs) {
652135446Strhodes	UNUSED(rs);
653135446Strhodes	return (ISC_R_SUCCESS);
654135446Strhodes}
655135446Strhodes
656135446Strhodesstatic isc_result_t
657135446Strhodessoa_rrstream_next(rrstream_t *rs) {
658135446Strhodes	UNUSED(rs);
659135446Strhodes	return (ISC_R_NOMORE);
660135446Strhodes}
661135446Strhodes
662135446Strhodesstatic void
663135446Strhodessoa_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
664135446Strhodes		     dns_rdata_t **rdata)
665135446Strhodes{
666135446Strhodes	soa_rrstream_t *s = (soa_rrstream_t *) rs;
667135446Strhodes	*name = &s->soa_tuple->name;
668135446Strhodes	*ttl = s->soa_tuple->ttl;
669135446Strhodes	*rdata = &s->soa_tuple->rdata;
670135446Strhodes}
671135446Strhodes
672135446Strhodesstatic void
673135446Strhodessoa_rrstream_destroy(rrstream_t **rsp) {
674135446Strhodes	soa_rrstream_t *s = (soa_rrstream_t *) *rsp;
675135446Strhodes	if (s->soa_tuple != NULL)
676135446Strhodes		dns_difftuple_free(&s->soa_tuple);
677135446Strhodes	isc_mem_put(s->common.mctx, s, sizeof(*s));
678135446Strhodes}
679135446Strhodes
680135446Strhodesstatic rrstream_methods_t soa_rrstream_methods = {
681135446Strhodes	soa_rrstream_first,
682135446Strhodes	soa_rrstream_next,
683135446Strhodes	soa_rrstream_current,
684135446Strhodes	rrstream_noop_pause,
685135446Strhodes	soa_rrstream_destroy
686135446Strhodes};
687135446Strhodes
688135446Strhodes/**************************************************************************/
689135446Strhodes/*
690135446Strhodes * A 'compound_rrstream_t' objects owns a soa_rrstream
691135446Strhodes * and another rrstream, the "data stream".  It returns
692135446Strhodes * a concatenated stream consisting of the soa_rrstream, then
693135446Strhodes * the data stream, then the soa_rrstream again.
694135446Strhodes *
695135446Strhodes * The component streams are owned by the compound_rrstream_t
696135446Strhodes * and are destroyed with it.
697135446Strhodes */
698135446Strhodes
699135446Strhodestypedef struct compound_rrstream {
700135446Strhodes	rrstream_t		common;
701135446Strhodes	rrstream_t		*components[3];
702135446Strhodes	int			state;
703135446Strhodes	isc_result_t		result;
704135446Strhodes} compound_rrstream_t;
705135446Strhodes
706135446Strhodes/*
707135446Strhodes * Forward declarations.
708135446Strhodes */
709135446Strhodesstatic void
710135446Strhodescompound_rrstream_destroy(rrstream_t **rsp);
711135446Strhodes
712135446Strhodesstatic isc_result_t
713135446Strhodescompound_rrstream_next(rrstream_t *rs);
714135446Strhodes
715135446Strhodesstatic rrstream_methods_t compound_rrstream_methods;
716135446Strhodes
717135446Strhodes/*
718135446Strhodes * Requires:
719135446Strhodes *	soa_stream != NULL && *soa_stream != NULL
720135446Strhodes *	data_stream != NULL && *data_stream != NULL
721135446Strhodes *	sp != NULL && *sp == NULL
722135446Strhodes *
723135446Strhodes * Ensures:
724135446Strhodes *	*soa_stream == NULL
725135446Strhodes *	*data_stream == NULL
726135446Strhodes *	*sp points to a valid compound_rrstream_t
727135446Strhodes *	The soa and data streams will be destroyed
728135446Strhodes *	when the compound_rrstream_t is destroyed.
729135446Strhodes */
730135446Strhodesstatic isc_result_t
731135446Strhodescompound_rrstream_create(isc_mem_t *mctx, rrstream_t **soa_stream,
732135446Strhodes			 rrstream_t **data_stream, rrstream_t **sp)
733135446Strhodes{
734135446Strhodes	compound_rrstream_t *s;
735135446Strhodes
736135446Strhodes	INSIST(sp != NULL && *sp == NULL);
737135446Strhodes
738135446Strhodes	s = isc_mem_get(mctx, sizeof(*s));
739135446Strhodes	if (s == NULL)
740135446Strhodes		return (ISC_R_NOMEMORY);
741135446Strhodes	s->common.mctx = mctx;
742135446Strhodes	s->common.methods = &compound_rrstream_methods;
743135446Strhodes	s->components[0] = *soa_stream;
744135446Strhodes	s->components[1] = *data_stream;
745135446Strhodes	s->components[2] = *soa_stream;
746135446Strhodes	s->state = -1;
747135446Strhodes	s->result = ISC_R_FAILURE;
748135446Strhodes
749135446Strhodes	*soa_stream = NULL;
750135446Strhodes	*data_stream = NULL;
751135446Strhodes	*sp = (rrstream_t *) s;
752135446Strhodes	return (ISC_R_SUCCESS);
753135446Strhodes}
754135446Strhodes
755135446Strhodesstatic isc_result_t
756135446Strhodescompound_rrstream_first(rrstream_t *rs) {
757135446Strhodes	compound_rrstream_t *s = (compound_rrstream_t *) rs;
758135446Strhodes	s->state = 0;
759135446Strhodes	do {
760135446Strhodes		rrstream_t *curstream = s->components[s->state];
761135446Strhodes		s->result = curstream->methods->first(curstream);
762135446Strhodes	} while (s->result == ISC_R_NOMORE && s->state < 2);
763135446Strhodes	return (s->result);
764135446Strhodes}
765135446Strhodes
766135446Strhodesstatic isc_result_t
767135446Strhodescompound_rrstream_next(rrstream_t *rs) {
768135446Strhodes	compound_rrstream_t *s = (compound_rrstream_t *) rs;
769135446Strhodes	rrstream_t *curstream = s->components[s->state];
770135446Strhodes	s->result = curstream->methods->next(curstream);
771135446Strhodes	while (s->result == ISC_R_NOMORE) {
772135446Strhodes		/*
773135446Strhodes		 * Make sure locks held by the current stream
774135446Strhodes		 * are released before we switch streams.
775135446Strhodes		 */
776135446Strhodes		curstream->methods->pause(curstream);
777135446Strhodes		if (s->state == 2)
778135446Strhodes			return (ISC_R_NOMORE);
779135446Strhodes		s->state++;
780135446Strhodes		curstream = s->components[s->state];
781135446Strhodes		s->result = curstream->methods->first(curstream);
782135446Strhodes	}
783135446Strhodes	return (s->result);
784135446Strhodes}
785135446Strhodes
786135446Strhodesstatic void
787135446Strhodescompound_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
788135446Strhodes			  dns_rdata_t **rdata)
789135446Strhodes{
790135446Strhodes	compound_rrstream_t *s = (compound_rrstream_t *) rs;
791135446Strhodes	rrstream_t *curstream;
792135446Strhodes	INSIST(0 <= s->state && s->state < 3);
793135446Strhodes	INSIST(s->result == ISC_R_SUCCESS);
794135446Strhodes	curstream = s->components[s->state];
795135446Strhodes	curstream->methods->current(curstream, name, ttl, rdata);
796135446Strhodes}
797135446Strhodes
798135446Strhodesstatic void
799135446Strhodescompound_rrstream_pause(rrstream_t *rs)
800135446Strhodes{
801135446Strhodes	compound_rrstream_t *s = (compound_rrstream_t *) rs;
802135446Strhodes	rrstream_t *curstream;
803135446Strhodes	INSIST(0 <= s->state && s->state < 3);
804135446Strhodes	curstream = s->components[s->state];
805135446Strhodes	curstream->methods->pause(curstream);
806135446Strhodes}
807135446Strhodes
808135446Strhodesstatic void
809135446Strhodescompound_rrstream_destroy(rrstream_t **rsp) {
810135446Strhodes	compound_rrstream_t *s = (compound_rrstream_t *) *rsp;
811135446Strhodes	s->components[0]->methods->destroy(&s->components[0]);
812135446Strhodes	s->components[1]->methods->destroy(&s->components[1]);
813135446Strhodes	s->components[2] = NULL; /* Copy of components[0]. */
814135446Strhodes	isc_mem_put(s->common.mctx, s, sizeof(*s));
815135446Strhodes}
816135446Strhodes
817135446Strhodesstatic rrstream_methods_t compound_rrstream_methods = {
818135446Strhodes	compound_rrstream_first,
819135446Strhodes	compound_rrstream_next,
820135446Strhodes	compound_rrstream_current,
821135446Strhodes	compound_rrstream_pause,
822135446Strhodes	compound_rrstream_destroy
823135446Strhodes};
824135446Strhodes
825135446Strhodes/**************************************************************************/
826135446Strhodes/*
827135446Strhodes * An 'xfrout_ctx_t' contains the state of an outgoing AXFR or IXFR
828135446Strhodes * in progress.
829135446Strhodes */
830135446Strhodes
831135446Strhodestypedef struct {
832135446Strhodes	isc_mem_t 		*mctx;
833135446Strhodes	ns_client_t		*client;
834135446Strhodes	unsigned int 		id;		/* ID of request */
835135446Strhodes	dns_name_t		*qname;		/* Question name of request */
836135446Strhodes	dns_rdatatype_t		qtype;		/* dns_rdatatype_{a,i}xfr */
837135446Strhodes	dns_rdataclass_t	qclass;
838193149Sdougb	dns_zone_t 		*zone;		/* (necessary for stats) */
839135446Strhodes	dns_db_t 		*db;
840135446Strhodes	dns_dbversion_t 	*ver;
841135446Strhodes	isc_quota_t		*quota;
842135446Strhodes	rrstream_t 		*stream;	/* The XFR RR stream */
843135446Strhodes	isc_boolean_t		end_of_stream;	/* EOS has been reached */
844135446Strhodes	isc_buffer_t 		buf;		/* Buffer for message owner
845135446Strhodes						   names and rdatas */
846135446Strhodes	isc_buffer_t 		txlenbuf;	/* Transmit length buffer */
847135446Strhodes	isc_buffer_t		txbuf;		/* Transmit message buffer */
848135446Strhodes	void 			*txmem;
849135446Strhodes	unsigned int 		txmemlen;
850135446Strhodes	unsigned int		nmsg;		/* Number of messages sent */
851135446Strhodes	dns_tsigkey_t		*tsigkey;	/* Key used to create TSIG */
852135446Strhodes	isc_buffer_t		*lasttsig;	/* the last TSIG */
853135446Strhodes	isc_boolean_t		many_answers;
854135446Strhodes	int			sends;		/* Send in progress */
855135446Strhodes	isc_boolean_t		shuttingdown;
856135446Strhodes	const char		*mnemonic;	/* Style of transfer */
857135446Strhodes} xfrout_ctx_t;
858135446Strhodes
859135446Strhodesstatic isc_result_t
860135446Strhodesxfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client,
861135446Strhodes		  unsigned int id, dns_name_t *qname, dns_rdatatype_t qtype,
862193149Sdougb		  dns_rdataclass_t qclass, dns_zone_t *zone,
863135446Strhodes		  dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
864135446Strhodes		  rrstream_t *stream, dns_tsigkey_t *tsigkey,
865135446Strhodes		  isc_buffer_t *lasttsig,
866135446Strhodes		  unsigned int maxtime,
867135446Strhodes		  unsigned int idletime,
868135446Strhodes		  isc_boolean_t many_answers,
869135446Strhodes		  xfrout_ctx_t **xfrp);
870135446Strhodes
871135446Strhodesstatic void
872135446Strhodessendstream(xfrout_ctx_t *xfr);
873135446Strhodes
874135446Strhodesstatic void
875135446Strhodesxfrout_senddone(isc_task_t *task, isc_event_t *event);
876135446Strhodes
877135446Strhodesstatic void
878135446Strhodesxfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg);
879135446Strhodes
880135446Strhodesstatic void
881135446Strhodesxfrout_maybe_destroy(xfrout_ctx_t *xfr);
882135446Strhodes
883135446Strhodesstatic void
884135446Strhodesxfrout_ctx_destroy(xfrout_ctx_t **xfrp);
885135446Strhodes
886135446Strhodesstatic void
887135446Strhodesxfrout_client_shutdown(void *arg, isc_result_t result);
888135446Strhodes
889135446Strhodesstatic void
890135446Strhodesxfrout_log1(ns_client_t *client, dns_name_t *zonename,
891135446Strhodes	    dns_rdataclass_t rdclass, int level,
892135446Strhodes	    const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6);
893135446Strhodes
894135446Strhodesstatic void
895153816Sdougbxfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...)
896135446Strhodes	   ISC_FORMAT_PRINTF(3, 4);
897135446Strhodes
898135446Strhodes/**************************************************************************/
899135446Strhodes
900135446Strhodesvoid
901135446Strhodesns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
902135446Strhodes	isc_result_t result;
903135446Strhodes	dns_name_t *question_name;
904135446Strhodes	dns_rdataset_t *question_rdataset;
905135446Strhodes	dns_zone_t *zone = NULL;
906135446Strhodes	dns_db_t *db = NULL;
907135446Strhodes	dns_dbversion_t *ver = NULL;
908135446Strhodes	dns_rdataclass_t question_class;
909135446Strhodes	rrstream_t *soa_stream = NULL;
910135446Strhodes	rrstream_t *data_stream = NULL;
911135446Strhodes	rrstream_t *stream = NULL;
912135446Strhodes	dns_difftuple_t *current_soa_tuple = NULL;
913135446Strhodes	dns_name_t *soa_name;
914135446Strhodes	dns_rdataset_t *soa_rdataset;
915135446Strhodes	dns_rdata_t soa_rdata = DNS_RDATA_INIT;
916135446Strhodes	isc_boolean_t have_soa = ISC_FALSE;
917135446Strhodes	const char *mnemonic = NULL;
918135446Strhodes	isc_mem_t *mctx = client->mctx;
919135446Strhodes	dns_message_t *request = client->message;
920135446Strhodes	xfrout_ctx_t *xfr = NULL;
921135446Strhodes	isc_quota_t *quota = NULL;
922135446Strhodes	dns_transfer_format_t format = client->view->transfer_format;
923135446Strhodes	isc_netaddr_t na;
924135446Strhodes	dns_peer_t *peer = NULL;
925135446Strhodes	isc_buffer_t *tsigbuf = NULL;
926135446Strhodes	char *journalfile;
927135446Strhodes	char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")];
928135446Strhodes	char keyname[DNS_NAME_FORMATSIZE];
929135446Strhodes	isc_boolean_t is_poll = ISC_FALSE;
930170222Sdougb#ifdef DLZ
931170222Sdougb	isc_boolean_t is_dlz = ISC_FALSE;
932170222Sdougb#endif
933135446Strhodes
934135446Strhodes	switch (reqtype) {
935135446Strhodes	case dns_rdatatype_axfr:
936135446Strhodes		mnemonic = "AXFR";
937135446Strhodes		break;
938135446Strhodes	case dns_rdatatype_ixfr:
939135446Strhodes		mnemonic = "IXFR";
940135446Strhodes		break;
941135446Strhodes	default:
942135446Strhodes		INSIST(0);
943135446Strhodes		break;
944135446Strhodes	}
945135446Strhodes
946135446Strhodes	ns_client_log(client,
947135446Strhodes		      DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT,
948135446Strhodes		      ISC_LOG_DEBUG(6), "%s request", mnemonic);
949135446Strhodes	/*
950135446Strhodes	 * Apply quota.
951135446Strhodes	 */
952135446Strhodes	result = isc_quota_attach(&ns_g_server->xfroutquota, &quota);
953135446Strhodes	if (result != ISC_R_SUCCESS) {
954135446Strhodes		isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING,
955135446Strhodes			      "%s request denied: %s", mnemonic,
956135446Strhodes			      isc_result_totext(result));
957135446Strhodes		goto failure;
958135446Strhodes	}
959135446Strhodes
960135446Strhodes	/*
961135446Strhodes	 * Interpret the question section.
962135446Strhodes	 */
963135446Strhodes	result = dns_message_firstname(request, DNS_SECTION_QUESTION);
964135446Strhodes	INSIST(result == ISC_R_SUCCESS);
965135446Strhodes
966135446Strhodes	/*
967135446Strhodes	 * The question section must contain exactly one question, and
968135446Strhodes	 * it must be for AXFR/IXFR as appropriate.
969135446Strhodes	 */
970135446Strhodes	question_name = NULL;
971135446Strhodes	dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name);
972135446Strhodes	question_rdataset = ISC_LIST_HEAD(question_name->list);
973135446Strhodes	question_class = question_rdataset->rdclass;
974135446Strhodes	INSIST(question_rdataset->type == reqtype);
975135446Strhodes	if (ISC_LIST_NEXT(question_rdataset, link) != NULL)
976135446Strhodes		FAILC(DNS_R_FORMERR, "multiple questions");
977135446Strhodes	result = dns_message_nextname(request, DNS_SECTION_QUESTION);
978135446Strhodes	if (result != ISC_R_NOMORE)
979135446Strhodes		FAILC(DNS_R_FORMERR, "multiple questions");
980135446Strhodes
981135446Strhodes	result = dns_zt_find(client->view->zonetable, question_name, 0, NULL,
982135446Strhodes			     &zone);
983170222Sdougb
984135446Strhodes	if (result != ISC_R_SUCCESS)
985170222Sdougb#ifdef DLZ
986170222Sdougb	{
987170222Sdougb		/*
988218384Sdougb		 * Normal zone table does not have a match.
989218384Sdougb		 * Try the DLZ database
990170222Sdougb		 */
991193149Sdougb		if (client->view->dlzdatabase != NULL) {
992170222Sdougb			result = dns_dlzallowzonexfr(client->view,
993218384Sdougb						     question_name,
994218384Sdougb						     &client->peeraddr,
995170222Sdougb						     &db);
996170222Sdougb
997170222Sdougb			if (result == ISC_R_NOPERM) {
998170222Sdougb				char _buf1[DNS_NAME_FORMATSIZE];
999170222Sdougb				char _buf2[DNS_RDATACLASS_FORMATSIZE];
1000170222Sdougb
1001170222Sdougb				result = DNS_R_REFUSED;
1002170222Sdougb				dns_name_format(question_name, _buf1,
1003170222Sdougb						sizeof(_buf1));
1004170222Sdougb				dns_rdataclass_format(question_class,
1005170222Sdougb						      _buf2, sizeof(_buf2));
1006170222Sdougb				ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1007170222Sdougb					      NS_LOGMODULE_XFER_OUT,
1008170222Sdougb					      ISC_LOG_ERROR,
1009170222Sdougb					      "zone transfer '%s/%s' denied",
1010170222Sdougb					      _buf1, _buf2);
1011170222Sdougb				goto failure;
1012170222Sdougb			}
1013170222Sdougb			if (result != ISC_R_SUCCESS)
1014170222Sdougb#endif
1015170222Sdougb			FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
1016170222Sdougb				  question_name, question_class);
1017170222Sdougb#ifdef DLZ
1018170222Sdougb			is_dlz = ISC_TRUE;
1019170222Sdougb			/*
1020170222Sdougb			 * DLZ only support full zone transfer, not incremental
1021170222Sdougb			 */
1022170222Sdougb			if (reqtype != dns_rdatatype_axfr) {
1023170222Sdougb				mnemonic = "AXFR-style IXFR";
1024170222Sdougb				reqtype = dns_rdatatype_axfr;
1025170222Sdougb			}
1026170222Sdougb
1027170222Sdougb		} else {
1028170222Sdougb			/*
1029193149Sdougb			 * not DLZ and not in normal zone table, we are
1030170222Sdougb			 * not authoritative
1031170222Sdougb			 */
1032170222Sdougb			FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
1033170222Sdougb			      question_name, question_class);
1034170222Sdougb		}
1035170222Sdougb	} else {
1036170222Sdougb		/* zone table has a match */
1037170222Sdougb#endif
1038170222Sdougb		switch(dns_zone_gettype(zone)) {
1039170222Sdougb			case dns_zone_master:
1040170222Sdougb			case dns_zone_slave:
1041170222Sdougb				break;	/* Master and slave zones are OK for transfer. */
1042170222Sdougb			default:
1043170222Sdougb				FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class);
1044170222Sdougb			}
1045170222Sdougb		CHECK(dns_zone_getdb(zone, &db));
1046170222Sdougb		dns_db_currentversion(db, &ver);
1047170222Sdougb#ifdef DLZ
1048135446Strhodes	}
1049170222Sdougb#endif
1050135446Strhodes
1051135446Strhodes	xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
1052135446Strhodes		    "%s question section OK", mnemonic);
1053135446Strhodes
1054135446Strhodes	/*
1055135446Strhodes	 * Check the authority section.  Look for a SOA record with
1056135446Strhodes	 * the same name and class as the question.
1057135446Strhodes	 */
1058135446Strhodes	for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY);
1059135446Strhodes	     result == ISC_R_SUCCESS;
1060135446Strhodes	     result = dns_message_nextname(request, DNS_SECTION_AUTHORITY))
1061135446Strhodes	{
1062135446Strhodes		soa_name = NULL;
1063135446Strhodes		dns_message_currentname(request, DNS_SECTION_AUTHORITY,
1064135446Strhodes					&soa_name);
1065135446Strhodes
1066135446Strhodes		/*
1067135446Strhodes		 * Ignore data whose owner name is not the zone apex.
1068135446Strhodes		 */
1069135446Strhodes		if (! dns_name_equal(soa_name, question_name))
1070135446Strhodes			continue;
1071135446Strhodes
1072135446Strhodes		for (soa_rdataset = ISC_LIST_HEAD(soa_name->list);
1073135446Strhodes		     soa_rdataset != NULL;
1074135446Strhodes		     soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link))
1075135446Strhodes		{
1076135446Strhodes			/*
1077135446Strhodes			 * Ignore non-SOA data.
1078135446Strhodes			 */
1079135446Strhodes			if (soa_rdataset->type != dns_rdatatype_soa)
1080135446Strhodes				continue;
1081135446Strhodes			if (soa_rdataset->rdclass != question_class)
1082135446Strhodes				continue;
1083135446Strhodes
1084135446Strhodes			CHECK(dns_rdataset_first(soa_rdataset));
1085135446Strhodes			dns_rdataset_current(soa_rdataset, &soa_rdata);
1086135446Strhodes			result = dns_rdataset_next(soa_rdataset);
1087135446Strhodes			if (result == ISC_R_SUCCESS)
1088135446Strhodes				FAILC(DNS_R_FORMERR,
1089135446Strhodes				      "IXFR authority section "
1090135446Strhodes				      "has multiple SOAs");
1091135446Strhodes			have_soa = ISC_TRUE;
1092135446Strhodes			goto got_soa;
1093135446Strhodes		}
1094135446Strhodes	}
1095135446Strhodes got_soa:
1096135446Strhodes	if (result != ISC_R_NOMORE)
1097135446Strhodes		CHECK(result);
1098135446Strhodes
1099135446Strhodes	xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
1100135446Strhodes		    "%s authority section OK", mnemonic);
1101135446Strhodes
1102135446Strhodes	/*
1103135446Strhodes	 * Decide whether to allow this transfer.
1104135446Strhodes	 */
1105170222Sdougb#ifdef DLZ
1106170222Sdougb	/*
1107170222Sdougb	 * if not a DLZ zone decide whether to allow this transfer.
1108170222Sdougb	 */
1109170222Sdougb	if (!is_dlz) {
1110170222Sdougb#endif
1111170222Sdougb		ns_client_aclmsg("zone transfer", question_name, reqtype,
1112170222Sdougb				 client->view->rdclass, msg, sizeof(msg));
1113193149Sdougb		CHECK(ns_client_checkacl(client, NULL, msg,
1114193149Sdougb					 dns_zone_getxfracl(zone),
1115193149Sdougb					 ISC_TRUE, ISC_LOG_ERROR));
1116170222Sdougb#ifdef DLZ
1117170222Sdougb	}
1118170222Sdougb#endif
1119135446Strhodes
1120135446Strhodes	/*
1121135446Strhodes	 * AXFR over UDP is not possible.
1122135446Strhodes	 */
1123135446Strhodes	if (reqtype == dns_rdatatype_axfr &&
1124135446Strhodes	    (client->attributes & NS_CLIENTATTR_TCP) == 0)
1125135446Strhodes		FAILC(DNS_R_FORMERR, "attempted AXFR over UDP");
1126135446Strhodes
1127135446Strhodes	/*
1128135446Strhodes	 * Look up the requesting server in the peer table.
1129135446Strhodes	 */
1130135446Strhodes	isc_netaddr_fromsockaddr(&na, &client->peeraddr);
1131135446Strhodes	(void)dns_peerlist_peerbyaddr(client->view->peers, &na, &peer);
1132135446Strhodes
1133135446Strhodes	/*
1134135446Strhodes	 * Decide on the transfer format (one-answer or many-answers).
1135135446Strhodes	 */
1136135446Strhodes	if (peer != NULL)
1137135446Strhodes		(void)dns_peer_gettransferformat(peer, &format);
1138135446Strhodes
1139135446Strhodes	/*
1140135446Strhodes	 * Get a dynamically allocated copy of the current SOA.
1141135446Strhodes	 */
1142170222Sdougb#ifdef DLZ
1143170222Sdougb	if (is_dlz)
1144170222Sdougb		dns_db_currentversion(db, &ver);
1145170222Sdougb#endif
1146135446Strhodes	CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
1147135446Strhodes				    &current_soa_tuple));
1148135446Strhodes
1149135446Strhodes	if (reqtype == dns_rdatatype_ixfr) {
1150135446Strhodes		isc_uint32_t begin_serial, current_serial;
1151135446Strhodes		isc_boolean_t provide_ixfr;
1152135446Strhodes
1153135446Strhodes		/*
1154135446Strhodes		 * Outgoing IXFR may have been disabled for this peer
1155135446Strhodes		 * or globally.
1156135446Strhodes		 */
1157135446Strhodes		provide_ixfr = client->view->provideixfr;
1158135446Strhodes		if (peer != NULL)
1159135446Strhodes			(void) dns_peer_getprovideixfr(peer, &provide_ixfr);
1160135446Strhodes		if (provide_ixfr == ISC_FALSE)
1161135446Strhodes			goto axfr_fallback;
1162135446Strhodes
1163135446Strhodes		if (! have_soa)
1164135446Strhodes			FAILC(DNS_R_FORMERR,
1165135446Strhodes			      "IXFR request missing SOA");
1166135446Strhodes
1167135446Strhodes		begin_serial = dns_soa_getserial(&soa_rdata);
1168135446Strhodes		current_serial = dns_soa_getserial(&current_soa_tuple->rdata);
1169135446Strhodes
1170135446Strhodes		/*
1171135446Strhodes		 * RFC1995 says "If an IXFR query with the same or
1172135446Strhodes		 * newer version number than that of the server
1173135446Strhodes		 * is received, it is replied to with a single SOA
1174135446Strhodes		 * record of the server's current version, just as
1175135446Strhodes		 * in AXFR".  The claim about AXFR is incorrect,
1176135446Strhodes		 * but other than that, we do as the RFC says.
1177135446Strhodes		 *
1178135446Strhodes		 * Sending a single SOA record is also how we refuse
1179135446Strhodes		 * IXFR over UDP (currently, we always do).
1180135446Strhodes		 */
1181135446Strhodes		if (DNS_SERIAL_GE(begin_serial, current_serial) ||
1182135446Strhodes		    (client->attributes & NS_CLIENTATTR_TCP) == 0)
1183135446Strhodes		{
1184135446Strhodes			CHECK(soa_rrstream_create(mctx, db, ver, &stream));
1185135446Strhodes			is_poll = ISC_TRUE;
1186135446Strhodes			goto have_stream;
1187135446Strhodes		}
1188135446Strhodes		journalfile = dns_zone_getjournal(zone);
1189135446Strhodes		if (journalfile != NULL)
1190135446Strhodes			result = ixfr_rrstream_create(mctx,
1191135446Strhodes						      journalfile,
1192135446Strhodes						      begin_serial,
1193135446Strhodes						      current_serial,
1194135446Strhodes						      &data_stream);
1195135446Strhodes		else
1196135446Strhodes			result = ISC_R_NOTFOUND;
1197135446Strhodes		if (result == ISC_R_NOTFOUND ||
1198135446Strhodes		    result == ISC_R_RANGE) {
1199135446Strhodes			xfrout_log1(client, question_name, question_class,
1200135446Strhodes				    ISC_LOG_DEBUG(4),
1201135446Strhodes				    "IXFR version not in journal, "
1202135446Strhodes				    "falling back to AXFR");
1203135446Strhodes			mnemonic = "AXFR-style IXFR";
1204135446Strhodes			goto axfr_fallback;
1205135446Strhodes		}
1206135446Strhodes		CHECK(result);
1207135446Strhodes	} else {
1208135446Strhodes	axfr_fallback:
1209135446Strhodes		CHECK(axfr_rrstream_create(mctx, db, ver,
1210135446Strhodes					   &data_stream));
1211135446Strhodes	}
1212135446Strhodes
1213135446Strhodes	/*
1214193149Sdougb	 * Bracket the data stream with SOAs.
1215135446Strhodes	 */
1216135446Strhodes	CHECK(soa_rrstream_create(mctx, db, ver, &soa_stream));
1217135446Strhodes	CHECK(compound_rrstream_create(mctx, &soa_stream, &data_stream,
1218135446Strhodes				       &stream));
1219135446Strhodes	soa_stream = NULL;
1220135446Strhodes	data_stream = NULL;
1221135446Strhodes
1222135446Strhodes have_stream:
1223135446Strhodes	CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf));
1224135446Strhodes	/*
1225135446Strhodes	 * Create the xfrout context object.  This transfers the ownership
1226135446Strhodes	 * of "stream", "db", "ver", and "quota" to the xfrout context object.
1227135446Strhodes	 */
1228170222Sdougb
1229170222Sdougb
1230170222Sdougb
1231170222Sdougb#ifdef DLZ
1232170222Sdougb	if (is_dlz)
1233218384Sdougb		CHECK(xfrout_ctx_create(mctx, client, request->id,
1234218384Sdougb					question_name, reqtype, question_class,
1235218384Sdougb					zone, db, ver, quota, stream,
1236193149Sdougb					dns_message_gettsigkey(request),
1237193149Sdougb					tsigbuf,
1238193149Sdougb					3600,
1239193149Sdougb					3600,
1240193149Sdougb					(format == dns_many_answers) ?
1241193149Sdougb					ISC_TRUE : ISC_FALSE,
1242193149Sdougb					&xfr));
1243193149Sdougb	else
1244170222Sdougb#endif
1245218384Sdougb		CHECK(xfrout_ctx_create(mctx, client, request->id,
1246218384Sdougb					question_name, reqtype, question_class,
1247218384Sdougb					zone, db, ver, quota, stream,
1248193149Sdougb					dns_message_gettsigkey(request),
1249193149Sdougb					tsigbuf,
1250193149Sdougb					dns_zone_getmaxxfrout(zone),
1251193149Sdougb					dns_zone_getidleout(zone),
1252193149Sdougb					(format == dns_many_answers) ?
1253193149Sdougb					ISC_TRUE : ISC_FALSE,
1254193149Sdougb					&xfr));
1255170222Sdougb
1256135446Strhodes	xfr->mnemonic = mnemonic;
1257135446Strhodes	stream = NULL;
1258135446Strhodes	quota = NULL;
1259135446Strhodes
1260135446Strhodes	CHECK(xfr->stream->methods->first(xfr->stream));
1261135446Strhodes
1262135446Strhodes	if (xfr->tsigkey != NULL) {
1263135446Strhodes		dns_name_format(&xfr->tsigkey->name, keyname, sizeof(keyname));
1264135446Strhodes	} else
1265135446Strhodes		keyname[0] = '\0';
1266135446Strhodes	if (is_poll)
1267135446Strhodes		xfrout_log1(client, question_name, question_class,
1268135446Strhodes			    ISC_LOG_DEBUG(1), "IXFR poll up to date%s%s",
1269135446Strhodes			    (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname);
1270135446Strhodes	else
1271135446Strhodes		xfrout_log1(client, question_name, question_class,
1272135446Strhodes			    ISC_LOG_INFO, "%s started%s%s", mnemonic,
1273135446Strhodes			    (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname);
1274135446Strhodes
1275135446Strhodes	/*
1276135446Strhodes	 * Hand the context over to sendstream().  Set xfr to NULL;
1277135446Strhodes	 * sendstream() is responsible for either passing the
1278135446Strhodes	 * context on to a later event handler or destroying it.
1279135446Strhodes	 */
1280135446Strhodes	sendstream(xfr);
1281135446Strhodes	xfr = NULL;
1282135446Strhodes
1283135446Strhodes	result = ISC_R_SUCCESS;
1284135446Strhodes
1285135446Strhodes failure:
1286193149Sdougb	if (result == DNS_R_REFUSED)
1287193149Sdougb		inc_stats(zone, dns_nsstatscounter_xfrrej);
1288135446Strhodes	if (quota != NULL)
1289135446Strhodes		isc_quota_detach(&quota);
1290135446Strhodes	if (current_soa_tuple != NULL)
1291135446Strhodes		dns_difftuple_free(&current_soa_tuple);
1292135446Strhodes	if (stream != NULL)
1293135446Strhodes		stream->methods->destroy(&stream);
1294135446Strhodes	if (soa_stream != NULL)
1295135446Strhodes		soa_stream->methods->destroy(&soa_stream);
1296135446Strhodes	if (data_stream != NULL)
1297135446Strhodes		data_stream->methods->destroy(&data_stream);
1298135446Strhodes	if (ver != NULL)
1299135446Strhodes		dns_db_closeversion(db, &ver, ISC_FALSE);
1300135446Strhodes	if (db != NULL)
1301135446Strhodes		dns_db_detach(&db);
1302135446Strhodes	if (zone != NULL)
1303135446Strhodes		dns_zone_detach(&zone);
1304135446Strhodes	/* XXX kludge */
1305135446Strhodes	if (xfr != NULL) {
1306135446Strhodes		xfrout_fail(xfr, result, "setting up zone transfer");
1307135446Strhodes	} else if (result != ISC_R_SUCCESS) {
1308135446Strhodes		ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT,
1309135446Strhodes			      NS_LOGMODULE_XFER_OUT,
1310135446Strhodes			      ISC_LOG_DEBUG(3), "zone transfer setup failed");
1311135446Strhodes		ns_client_error(client, result);
1312135446Strhodes	}
1313135446Strhodes}
1314135446Strhodes
1315135446Strhodesstatic isc_result_t
1316135446Strhodesxfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
1317135446Strhodes		  dns_name_t *qname, dns_rdatatype_t qtype,
1318193149Sdougb		  dns_rdataclass_t qclass, dns_zone_t *zone,
1319135446Strhodes		  dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
1320135446Strhodes		  rrstream_t *stream, dns_tsigkey_t *tsigkey,
1321135446Strhodes		  isc_buffer_t *lasttsig, unsigned int maxtime,
1322135446Strhodes		  unsigned int idletime, isc_boolean_t many_answers,
1323135446Strhodes		  xfrout_ctx_t **xfrp)
1324135446Strhodes{
1325135446Strhodes	xfrout_ctx_t *xfr;
1326135446Strhodes	isc_result_t result;
1327135446Strhodes	unsigned int len;
1328135446Strhodes	void *mem;
1329135446Strhodes
1330135446Strhodes	INSIST(xfrp != NULL && *xfrp == NULL);
1331135446Strhodes	xfr = isc_mem_get(mctx, sizeof(*xfr));
1332135446Strhodes	if (xfr == NULL)
1333135446Strhodes		return (ISC_R_NOMEMORY);
1334135446Strhodes	xfr->mctx = mctx;
1335135446Strhodes	xfr->client = NULL;
1336135446Strhodes	ns_client_attach(client, &xfr->client);
1337135446Strhodes	xfr->id = id;
1338135446Strhodes	xfr->qname = qname;
1339135446Strhodes	xfr->qtype = qtype;
1340135446Strhodes	xfr->qclass = qclass;
1341193149Sdougb	xfr->zone = NULL;
1342135446Strhodes	xfr->db = NULL;
1343135446Strhodes	xfr->ver = NULL;
1344193149Sdougb	if (zone != NULL)	/* zone will be NULL if it's DLZ */
1345193149Sdougb		dns_zone_attach(zone, &xfr->zone);
1346135446Strhodes	dns_db_attach(db, &xfr->db);
1347135446Strhodes	dns_db_attachversion(db, ver, &xfr->ver);
1348135446Strhodes	xfr->end_of_stream = ISC_FALSE;
1349135446Strhodes	xfr->tsigkey = tsigkey;
1350135446Strhodes	xfr->lasttsig = lasttsig;
1351135446Strhodes	xfr->txmem = NULL;
1352135446Strhodes	xfr->txmemlen = 0;
1353135446Strhodes	xfr->nmsg = 0;
1354135446Strhodes	xfr->many_answers = many_answers,
1355135446Strhodes	xfr->sends = 0;
1356135446Strhodes	xfr->shuttingdown = ISC_FALSE;
1357135446Strhodes	xfr->mnemonic = NULL;
1358135446Strhodes	xfr->buf.base = NULL;
1359135446Strhodes	xfr->buf.length = 0;
1360135446Strhodes	xfr->txmem = NULL;
1361135446Strhodes	xfr->txmemlen = 0;
1362135446Strhodes	xfr->stream = NULL;
1363135446Strhodes	xfr->quota = NULL;
1364135446Strhodes
1365135446Strhodes	/*
1366135446Strhodes	 * Allocate a temporary buffer for the uncompressed response
1367135446Strhodes	 * message data.  The size should be no more than 65535 bytes
1368135446Strhodes	 * so that the compressed data will fit in a TCP message,
1369135446Strhodes	 * and no less than 65535 bytes so that an almost maximum-sized
1370135446Strhodes	 * RR will fit.  Note that although 65535-byte RRs are allowed
1371135446Strhodes	 * in principle, they cannot be zone-transferred (at least not
1372135446Strhodes	 * if uncompressible), because the message and RR headers would
1373135446Strhodes	 * push the size of the TCP message over the 65536 byte limit.
1374135446Strhodes	 */
1375135446Strhodes	len = 65535;
1376135446Strhodes	mem = isc_mem_get(mctx, len);
1377135446Strhodes	if (mem == NULL) {
1378135446Strhodes		result = ISC_R_NOMEMORY;
1379135446Strhodes		goto failure;
1380135446Strhodes	}
1381135446Strhodes	isc_buffer_init(&xfr->buf, mem, len);
1382135446Strhodes
1383135446Strhodes	/*
1384135446Strhodes	 * Allocate another temporary buffer for the compressed
1385135446Strhodes	 * response message and its TCP length prefix.
1386135446Strhodes	 */
1387135446Strhodes	len = 2 + 65535;
1388135446Strhodes	mem = isc_mem_get(mctx, len);
1389135446Strhodes	if (mem == NULL) {
1390135446Strhodes		result = ISC_R_NOMEMORY;
1391135446Strhodes		goto failure;
1392135446Strhodes	}
1393135446Strhodes	isc_buffer_init(&xfr->txlenbuf, mem, 2);
1394135446Strhodes	isc_buffer_init(&xfr->txbuf, (char *) mem + 2, len - 2);
1395135446Strhodes	xfr->txmem = mem;
1396135446Strhodes	xfr->txmemlen = len;
1397135446Strhodes
1398135446Strhodes	CHECK(dns_timer_setidle(xfr->client->timer,
1399135446Strhodes				maxtime, idletime, ISC_FALSE));
1400135446Strhodes
1401135446Strhodes	/*
1402135446Strhodes	 * Register a shutdown callback with the client, so that we
1403135446Strhodes	 * can stop the transfer immediately when the client task
1404135446Strhodes	 * gets a shutdown event.
1405135446Strhodes	 */
1406135446Strhodes	xfr->client->shutdown = xfrout_client_shutdown;
1407135446Strhodes	xfr->client->shutdown_arg = xfr;
1408135446Strhodes	/*
1409135446Strhodes	 * These MUST be after the last "goto failure;" / CHECK to
1410135446Strhodes	 * prevent a double free by the caller.
1411135446Strhodes	 */
1412135446Strhodes	xfr->quota = quota;
1413135446Strhodes	xfr->stream = stream;
1414135446Strhodes
1415135446Strhodes	*xfrp = xfr;
1416135446Strhodes	return (ISC_R_SUCCESS);
1417135446Strhodes
1418135446Strhodesfailure:
1419135446Strhodes	xfrout_ctx_destroy(&xfr);
1420135446Strhodes	return (result);
1421135446Strhodes}
1422135446Strhodes
1423135446Strhodes
1424135446Strhodes/*
1425135446Strhodes * Arrange to send as much as we can of "stream" without blocking.
1426135446Strhodes *
1427135446Strhodes * Requires:
1428135446Strhodes *	The stream iterator is initialized and points at an RR,
1429193149Sdougb *      or possibly at the end of the stream (that is, the
1430135446Strhodes *      _first method of the iterator has been called).
1431135446Strhodes */
1432135446Strhodesstatic void
1433135446Strhodessendstream(xfrout_ctx_t *xfr) {
1434135446Strhodes	dns_message_t *tcpmsg = NULL;
1435135446Strhodes	dns_message_t *msg = NULL; /* Client message if UDP, tcpmsg if TCP */
1436135446Strhodes	isc_result_t result;
1437135446Strhodes	isc_region_t used;
1438135446Strhodes	isc_region_t region;
1439135446Strhodes	dns_rdataset_t *qrdataset;
1440135446Strhodes	dns_name_t *msgname = NULL;
1441135446Strhodes	dns_rdata_t *msgrdata = NULL;
1442135446Strhodes	dns_rdatalist_t *msgrdl = NULL;
1443135446Strhodes	dns_rdataset_t *msgrds = NULL;
1444135446Strhodes	dns_compress_t cctx;
1445135446Strhodes	isc_boolean_t cleanup_cctx = ISC_FALSE;
1446135446Strhodes
1447135446Strhodes	int n_rrs;
1448135446Strhodes
1449135446Strhodes	isc_buffer_clear(&xfr->buf);
1450135446Strhodes	isc_buffer_clear(&xfr->txlenbuf);
1451135446Strhodes	isc_buffer_clear(&xfr->txbuf);
1452135446Strhodes
1453135446Strhodes	if ((xfr->client->attributes & NS_CLIENTATTR_TCP) == 0) {
1454135446Strhodes		/*
1455135446Strhodes		 * In the UDP case, we put the response data directly into
1456135446Strhodes		 * the client message.
1457135446Strhodes		 */
1458135446Strhodes		msg = xfr->client->message;
1459135446Strhodes		CHECK(dns_message_reply(msg, ISC_TRUE));
1460135446Strhodes	} else {
1461135446Strhodes		/*
1462135446Strhodes		 * TCP. Build a response dns_message_t, temporarily storing
1463135446Strhodes		 * the raw, uncompressed owner names and RR data contiguously
1464135446Strhodes		 * in xfr->buf.  We know that if the uncompressed data fits
1465135446Strhodes		 * in xfr->buf, the compressed data will surely fit in a TCP
1466135446Strhodes		 * message.
1467135446Strhodes		 */
1468135446Strhodes
1469135446Strhodes		CHECK(dns_message_create(xfr->mctx,
1470135446Strhodes					 DNS_MESSAGE_INTENTRENDER, &tcpmsg));
1471135446Strhodes		msg = tcpmsg;
1472135446Strhodes
1473135446Strhodes		msg->id = xfr->id;
1474135446Strhodes		msg->rcode = dns_rcode_noerror;
1475135446Strhodes		msg->flags = DNS_MESSAGEFLAG_QR | DNS_MESSAGEFLAG_AA;
1476135446Strhodes		if ((xfr->client->attributes & NS_CLIENTATTR_RA) != 0)
1477135446Strhodes			msg->flags |= DNS_MESSAGEFLAG_RA;
1478135446Strhodes		CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
1479135446Strhodes		CHECK(dns_message_setquerytsig(msg, xfr->lasttsig));
1480135446Strhodes		if (xfr->lasttsig != NULL)
1481135446Strhodes			isc_buffer_free(&xfr->lasttsig);
1482135446Strhodes
1483135446Strhodes		/*
1484135446Strhodes		 * Include a question section in the first message only.
1485135446Strhodes		 * BIND 8.2.1 will not recognize an IXFR if it does not
1486135446Strhodes		 * have a question section.
1487135446Strhodes		 */
1488135446Strhodes		if (xfr->nmsg == 0) {
1489135446Strhodes			dns_name_t *qname = NULL;
1490135446Strhodes			isc_region_t r;
1491135446Strhodes
1492135446Strhodes			/*
1493135446Strhodes			 * Reserve space for the 12-byte message header
1494135446Strhodes			 * and 4 bytes of question.
1495135446Strhodes			 */
1496135446Strhodes			isc_buffer_add(&xfr->buf, 12 + 4);
1497135446Strhodes
1498135446Strhodes			qrdataset = NULL;
1499135446Strhodes			result = dns_message_gettemprdataset(msg, &qrdataset);
1500135446Strhodes			if (result != ISC_R_SUCCESS)
1501135446Strhodes				goto failure;
1502135446Strhodes			dns_rdataset_init(qrdataset);
1503135446Strhodes			dns_rdataset_makequestion(qrdataset,
1504135446Strhodes					xfr->client->message->rdclass,
1505135446Strhodes					xfr->qtype);
1506135446Strhodes
1507135446Strhodes			result = dns_message_gettempname(msg, &qname);
1508135446Strhodes			if (result != ISC_R_SUCCESS)
1509135446Strhodes				goto failure;
1510135446Strhodes			dns_name_init(qname, NULL);
1511135446Strhodes			isc_buffer_availableregion(&xfr->buf, &r);
1512135446Strhodes			INSIST(r.length >= xfr->qname->length);
1513135446Strhodes			r.length = xfr->qname->length;
1514135446Strhodes			isc_buffer_putmem(&xfr->buf, xfr->qname->ndata,
1515135446Strhodes					  xfr->qname->length);
1516135446Strhodes			dns_name_fromregion(qname, &r);
1517135446Strhodes			ISC_LIST_INIT(qname->list);
1518135446Strhodes			ISC_LIST_APPEND(qname->list, qrdataset, link);
1519135446Strhodes
1520135446Strhodes			dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
1521135446Strhodes		}
1522135446Strhodes		else
1523135446Strhodes			msg->tcp_continuation = 1;
1524135446Strhodes	}
1525135446Strhodes
1526135446Strhodes	/*
1527135446Strhodes	 * Try to fit in as many RRs as possible, unless "one-answer"
1528135446Strhodes	 * format has been requested.
1529135446Strhodes	 */
1530135446Strhodes	for (n_rrs = 0; ; n_rrs++) {
1531135446Strhodes		dns_name_t *name = NULL;
1532135446Strhodes		isc_uint32_t ttl;
1533135446Strhodes		dns_rdata_t *rdata = NULL;
1534135446Strhodes
1535135446Strhodes		unsigned int size;
1536135446Strhodes		isc_region_t r;
1537135446Strhodes
1538135446Strhodes		msgname = NULL;
1539135446Strhodes		msgrdata = NULL;
1540135446Strhodes		msgrdl = NULL;
1541135446Strhodes		msgrds = NULL;
1542135446Strhodes
1543135446Strhodes		xfr->stream->methods->current(xfr->stream,
1544135446Strhodes					      &name, &ttl, &rdata);
1545135446Strhodes		size = name->length + 10 + rdata->length;
1546135446Strhodes		isc_buffer_availableregion(&xfr->buf, &r);
1547135446Strhodes		if (size >= r.length) {
1548135446Strhodes			/*
1549135446Strhodes			 * RR would not fit.  If there are other RRs in the
1550135446Strhodes			 * buffer, send them now and leave this RR to the
1551135446Strhodes			 * next message.  If this RR overflows the buffer
1552135446Strhodes			 * all by itself, fail.
1553135446Strhodes			 *
1554135446Strhodes			 * In theory some RRs might fit in a TCP message
1555135446Strhodes			 * when compressed even if they do not fit when
1556135446Strhodes			 * uncompressed, but surely we don't want
1557135446Strhodes			 * to send such monstrosities to an unsuspecting
1558135446Strhodes			 * slave.
1559135446Strhodes			 */
1560135446Strhodes			if (n_rrs == 0) {
1561135446Strhodes				xfrout_log(xfr, ISC_LOG_WARNING,
1562135446Strhodes					   "RR too large for zone transfer "
1563135446Strhodes					   "(%d bytes)", size);
1564135446Strhodes				/* XXX DNS_R_RRTOOLARGE? */
1565135446Strhodes				result = ISC_R_NOSPACE;
1566135446Strhodes				goto failure;
1567135446Strhodes			}
1568135446Strhodes			break;
1569135446Strhodes		}
1570135446Strhodes
1571135446Strhodes		if (isc_log_wouldlog(ns_g_lctx, XFROUT_RR_LOGLEVEL))
1572135446Strhodes			log_rr(name, rdata, ttl); /* XXX */
1573135446Strhodes
1574135446Strhodes		result = dns_message_gettempname(msg, &msgname);
1575135446Strhodes		if (result != ISC_R_SUCCESS)
1576135446Strhodes			goto failure;
1577135446Strhodes		dns_name_init(msgname, NULL);
1578135446Strhodes		isc_buffer_availableregion(&xfr->buf, &r);
1579135446Strhodes		INSIST(r.length >= name->length);
1580135446Strhodes		r.length = name->length;
1581135446Strhodes		isc_buffer_putmem(&xfr->buf, name->ndata, name->length);
1582135446Strhodes		dns_name_fromregion(msgname, &r);
1583135446Strhodes
1584135446Strhodes		/* Reserve space for RR header. */
1585135446Strhodes		isc_buffer_add(&xfr->buf, 10);
1586135446Strhodes
1587135446Strhodes		result = dns_message_gettemprdata(msg, &msgrdata);
1588135446Strhodes		if (result != ISC_R_SUCCESS)
1589135446Strhodes			goto failure;
1590135446Strhodes		isc_buffer_availableregion(&xfr->buf, &r);
1591135446Strhodes		r.length = rdata->length;
1592135446Strhodes		isc_buffer_putmem(&xfr->buf, rdata->data, rdata->length);
1593135446Strhodes		dns_rdata_init(msgrdata);
1594135446Strhodes		dns_rdata_fromregion(msgrdata,
1595135446Strhodes				     rdata->rdclass, rdata->type, &r);
1596135446Strhodes
1597135446Strhodes		result = dns_message_gettemprdatalist(msg, &msgrdl);
1598135446Strhodes		if (result != ISC_R_SUCCESS)
1599135446Strhodes			goto failure;
1600135446Strhodes		msgrdl->type = rdata->type;
1601135446Strhodes		msgrdl->rdclass = rdata->rdclass;
1602135446Strhodes		msgrdl->ttl = ttl;
1603193149Sdougb		if (rdata->type == dns_rdatatype_sig ||
1604193149Sdougb		    rdata->type == dns_rdatatype_rrsig)
1605193149Sdougb			msgrdl->covers = dns_rdata_covers(rdata);
1606193149Sdougb		else
1607193149Sdougb			msgrdl->covers = dns_rdatatype_none;
1608135446Strhodes		ISC_LINK_INIT(msgrdl, link);
1609135446Strhodes		ISC_LIST_INIT(msgrdl->rdata);
1610135446Strhodes		ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link);
1611135446Strhodes
1612135446Strhodes		result = dns_message_gettemprdataset(msg, &msgrds);
1613135446Strhodes		if (result != ISC_R_SUCCESS)
1614135446Strhodes			goto failure;
1615135446Strhodes		dns_rdataset_init(msgrds);
1616135446Strhodes		result = dns_rdatalist_tordataset(msgrdl, msgrds);
1617135446Strhodes		INSIST(result == ISC_R_SUCCESS);
1618135446Strhodes
1619135446Strhodes		ISC_LIST_APPEND(msgname->list, msgrds, link);
1620135446Strhodes
1621135446Strhodes		dns_message_addname(msg, msgname, DNS_SECTION_ANSWER);
1622135446Strhodes		msgname = NULL;
1623135446Strhodes
1624135446Strhodes		result = xfr->stream->methods->next(xfr->stream);
1625135446Strhodes		if (result == ISC_R_NOMORE) {
1626135446Strhodes			xfr->end_of_stream = ISC_TRUE;
1627135446Strhodes			break;
1628135446Strhodes		}
1629135446Strhodes		CHECK(result);
1630135446Strhodes
1631135446Strhodes		if (! xfr->many_answers)
1632135446Strhodes			break;
1633135446Strhodes	}
1634135446Strhodes
1635135446Strhodes	if ((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0) {
1636135446Strhodes		CHECK(dns_compress_init(&cctx, -1, xfr->mctx));
1637170222Sdougb		dns_compress_setsensitive(&cctx, ISC_TRUE);
1638135446Strhodes		cleanup_cctx = ISC_TRUE;
1639135446Strhodes		CHECK(dns_message_renderbegin(msg, &cctx, &xfr->txbuf));
1640135446Strhodes		CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0));
1641135446Strhodes		CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0));
1642135446Strhodes		CHECK(dns_message_renderend(msg));
1643135446Strhodes		dns_compress_invalidate(&cctx);
1644135446Strhodes		cleanup_cctx = ISC_FALSE;
1645135446Strhodes
1646135446Strhodes		isc_buffer_usedregion(&xfr->txbuf, &used);
1647135446Strhodes		isc_buffer_putuint16(&xfr->txlenbuf,
1648135446Strhodes				     (isc_uint16_t)used.length);
1649135446Strhodes		region.base = xfr->txlenbuf.base;
1650135446Strhodes		region.length = 2 + used.length;
1651135446Strhodes		xfrout_log(xfr, ISC_LOG_DEBUG(8),
1652135446Strhodes			   "sending TCP message of %d bytes",
1653135446Strhodes			   used.length);
1654135446Strhodes		CHECK(isc_socket_send(xfr->client->tcpsocket, /* XXX */
1655135446Strhodes				      &region, xfr->client->task,
1656135446Strhodes				      xfrout_senddone,
1657135446Strhodes				      xfr));
1658135446Strhodes		xfr->sends++;
1659135446Strhodes	} else {
1660135446Strhodes		xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending IXFR UDP response");
1661135446Strhodes		ns_client_send(xfr->client);
1662135446Strhodes		xfr->stream->methods->pause(xfr->stream);
1663135446Strhodes		xfrout_ctx_destroy(&xfr);
1664135446Strhodes		return;
1665135446Strhodes	}
1666135446Strhodes
1667135446Strhodes	/* Advance lasttsig to be the last TSIG generated */
1668135446Strhodes	CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig));
1669135446Strhodes
1670135446Strhodes	xfr->nmsg++;
1671135446Strhodes
1672135446Strhodes failure:
1673135446Strhodes	if (msgname != NULL) {
1674135446Strhodes		if (msgrds != NULL) {
1675135446Strhodes			if (dns_rdataset_isassociated(msgrds))
1676135446Strhodes				dns_rdataset_disassociate(msgrds);
1677135446Strhodes			dns_message_puttemprdataset(msg, &msgrds);
1678135446Strhodes		}
1679135446Strhodes		if (msgrdl != NULL) {
1680135446Strhodes			ISC_LIST_UNLINK(msgrdl->rdata, msgrdata, link);
1681135446Strhodes			dns_message_puttemprdatalist(msg, &msgrdl);
1682135446Strhodes		}
1683135446Strhodes		if (msgrdata != NULL)
1684135446Strhodes			dns_message_puttemprdata(msg, &msgrdata);
1685135446Strhodes		dns_message_puttempname(msg, &msgname);
1686135446Strhodes	}
1687135446Strhodes
1688135446Strhodes	if (tcpmsg != NULL)
1689135446Strhodes		dns_message_destroy(&tcpmsg);
1690135446Strhodes
1691135446Strhodes	if (cleanup_cctx)
1692135446Strhodes		dns_compress_invalidate(&cctx);
1693135446Strhodes	/*
1694135446Strhodes	 * Make sure to release any locks held by database
1695135446Strhodes	 * iterators before returning from the event handler.
1696135446Strhodes	 */
1697135446Strhodes	xfr->stream->methods->pause(xfr->stream);
1698193149Sdougb
1699135446Strhodes	if (result == ISC_R_SUCCESS)
1700135446Strhodes		return;
1701135446Strhodes
1702135446Strhodes	xfrout_fail(xfr, result, "sending zone data");
1703135446Strhodes}
1704135446Strhodes
1705135446Strhodesstatic void
1706135446Strhodesxfrout_ctx_destroy(xfrout_ctx_t **xfrp) {
1707135446Strhodes	xfrout_ctx_t *xfr = *xfrp;
1708135446Strhodes
1709135446Strhodes	INSIST(xfr->sends == 0);
1710135446Strhodes
1711135446Strhodes	xfr->client->shutdown = NULL;
1712135446Strhodes	xfr->client->shutdown_arg = NULL;
1713135446Strhodes
1714135446Strhodes	if (xfr->stream != NULL)
1715135446Strhodes		xfr->stream->methods->destroy(&xfr->stream);
1716135446Strhodes	if (xfr->buf.base != NULL)
1717135446Strhodes		isc_mem_put(xfr->mctx, xfr->buf.base, xfr->buf.length);
1718135446Strhodes	if (xfr->txmem != NULL)
1719135446Strhodes		isc_mem_put(xfr->mctx, xfr->txmem, xfr->txmemlen);
1720135446Strhodes	if (xfr->lasttsig != NULL)
1721135446Strhodes		isc_buffer_free(&xfr->lasttsig);
1722135446Strhodes	if (xfr->quota != NULL)
1723135446Strhodes		isc_quota_detach(&xfr->quota);
1724135446Strhodes	if (xfr->ver != NULL)
1725135446Strhodes		dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
1726193149Sdougb	if (xfr->zone != NULL)
1727193149Sdougb		dns_zone_detach(&xfr->zone);
1728135446Strhodes	if (xfr->db != NULL)
1729135446Strhodes		dns_db_detach(&xfr->db);
1730135446Strhodes
1731135446Strhodes	ns_client_detach(&xfr->client);
1732135446Strhodes
1733135446Strhodes	isc_mem_put(xfr->mctx, xfr, sizeof(*xfr));
1734135446Strhodes
1735135446Strhodes	*xfrp = NULL;
1736135446Strhodes}
1737135446Strhodes
1738135446Strhodesstatic void
1739135446Strhodesxfrout_senddone(isc_task_t *task, isc_event_t *event) {
1740135446Strhodes	isc_socketevent_t *sev = (isc_socketevent_t *)event;
1741135446Strhodes	xfrout_ctx_t *xfr = (xfrout_ctx_t *)event->ev_arg;
1742135446Strhodes	isc_result_t evresult = sev->result;
1743135446Strhodes
1744135446Strhodes	UNUSED(task);
1745135446Strhodes
1746135446Strhodes	INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
1747135446Strhodes
1748135446Strhodes	isc_event_free(&event);
1749135446Strhodes	xfr->sends--;
1750135446Strhodes	INSIST(xfr->sends == 0);
1751135446Strhodes
1752135446Strhodes	(void)isc_timer_touch(xfr->client->timer);
1753135446Strhodes	if (xfr->shuttingdown == ISC_TRUE) {
1754135446Strhodes		xfrout_maybe_destroy(xfr);
1755135446Strhodes	} else if (evresult != ISC_R_SUCCESS) {
1756135446Strhodes		xfrout_fail(xfr, evresult, "send");
1757135446Strhodes	} else if (xfr->end_of_stream == ISC_FALSE) {
1758135446Strhodes		sendstream(xfr);
1759135446Strhodes	} else {
1760135446Strhodes		/* End of zone transfer stream. */
1761193149Sdougb		inc_stats(xfr->zone, dns_nsstatscounter_xfrdone);
1762135446Strhodes		xfrout_log(xfr, ISC_LOG_INFO, "%s ended", xfr->mnemonic);
1763135446Strhodes		ns_client_next(xfr->client, ISC_R_SUCCESS);
1764135446Strhodes		xfrout_ctx_destroy(&xfr);
1765135446Strhodes	}
1766135446Strhodes}
1767135446Strhodes
1768135446Strhodesstatic void
1769135446Strhodesxfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg) {
1770135446Strhodes	xfr->shuttingdown = ISC_TRUE;
1771135446Strhodes	xfrout_log(xfr, ISC_LOG_ERROR, "%s: %s",
1772135446Strhodes		   msg, isc_result_totext(result));
1773135446Strhodes	xfrout_maybe_destroy(xfr);
1774135446Strhodes}
1775135446Strhodes
1776135446Strhodesstatic void
1777135446Strhodesxfrout_maybe_destroy(xfrout_ctx_t *xfr) {
1778135446Strhodes	INSIST(xfr->shuttingdown == ISC_TRUE);
1779135446Strhodes	if (xfr->sends > 0) {
1780135446Strhodes		/*
1781135446Strhodes		 * If we are currently sending, cancel it and wait for
1782135446Strhodes		 * cancel event before destroying the context.
1783135446Strhodes		 */
1784135446Strhodes		isc_socket_cancel(xfr->client->tcpsocket, xfr->client->task,
1785135446Strhodes				  ISC_SOCKCANCEL_SEND);
1786135446Strhodes	} else {
1787135446Strhodes		ns_client_next(xfr->client, ISC_R_CANCELED);
1788135446Strhodes		xfrout_ctx_destroy(&xfr);
1789135446Strhodes	}
1790135446Strhodes}
1791135446Strhodes
1792135446Strhodesstatic void
1793135446Strhodesxfrout_client_shutdown(void *arg, isc_result_t result) {
1794135446Strhodes	xfrout_ctx_t *xfr = (xfrout_ctx_t *) arg;
1795135446Strhodes	xfrout_fail(xfr, result, "aborted");
1796135446Strhodes}
1797135446Strhodes
1798135446Strhodes/*
1799135446Strhodes * Log outgoing zone transfer messages in a format like
1800135446Strhodes * <client>: transfer of <zone>: <message>
1801135446Strhodes */
1802135446Strhodes
1803135446Strhodesstatic void
1804135446Strhodesxfrout_logv(ns_client_t *client, dns_name_t *zonename,
1805135446Strhodes	    dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap)
1806135446Strhodes     ISC_FORMAT_PRINTF(5, 0);
1807135446Strhodes
1808135446Strhodesstatic void
1809135446Strhodesxfrout_logv(ns_client_t *client, dns_name_t *zonename,
1810135446Strhodes	    dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap)
1811135446Strhodes{
1812135446Strhodes	char msgbuf[2048];
1813135446Strhodes	char namebuf[DNS_NAME_FORMATSIZE];
1814135446Strhodes	char classbuf[DNS_RDATACLASS_FORMATSIZE];
1815135446Strhodes
1816135446Strhodes	dns_name_format(zonename, namebuf, sizeof(namebuf));
1817135446Strhodes	dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
1818135446Strhodes	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
1819135446Strhodes	ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT,
1820135446Strhodes		      NS_LOGMODULE_XFER_OUT, level,
1821135446Strhodes		      "transfer of '%s/%s': %s", namebuf, classbuf, msgbuf);
1822135446Strhodes}
1823135446Strhodes
1824135446Strhodes/*
1825135446Strhodes * Logging function for use when a xfrout_ctx_t has not yet been created.
1826135446Strhodes */
1827135446Strhodesstatic void
1828135446Strhodesxfrout_log1(ns_client_t *client, dns_name_t *zonename,
1829135446Strhodes	    dns_rdataclass_t rdclass, int level, const char *fmt, ...) {
1830135446Strhodes	va_list ap;
1831135446Strhodes	va_start(ap, fmt);
1832135446Strhodes	xfrout_logv(client, zonename, rdclass, level, fmt, ap);
1833135446Strhodes	va_end(ap);
1834135446Strhodes}
1835135446Strhodes
1836135446Strhodes/*
1837135446Strhodes * Logging function for use when there is a xfrout_ctx_t.
1838135446Strhodes */
1839135446Strhodesstatic void
1840153816Sdougbxfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...) {
1841135446Strhodes	va_list ap;
1842135446Strhodes	va_start(ap, fmt);
1843135446Strhodes	xfrout_logv(xfr->client, xfr->qname, xfr->qclass, level, fmt, ap);
1844135446Strhodes	va_end(ap);
1845135446Strhodes}
1846