1135446Strhodes/*
2254402Serwin * Copyright (C) 2004-2008, 2011-2013  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * 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
18234010Sdougb/* $Id$ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24135446Strhodes#include <isc/mem.h>
25135446Strhodes#include <isc/print.h>
26135446Strhodes#include <isc/random.h>
27135446Strhodes#include <isc/string.h>		/* Required for HP/UX (and others?) */
28135446Strhodes#include <isc/task.h>
29135446Strhodes#include <isc/timer.h>
30135446Strhodes#include <isc/util.h>
31135446Strhodes
32135446Strhodes#include <dns/db.h>
33135446Strhodes#include <dns/diff.h>
34135446Strhodes#include <dns/events.h>
35135446Strhodes#include <dns/journal.h>
36135446Strhodes#include <dns/log.h>
37135446Strhodes#include <dns/message.h>
38135446Strhodes#include <dns/rdataclass.h>
39135446Strhodes#include <dns/rdatalist.h>
40135446Strhodes#include <dns/rdataset.h>
41135446Strhodes#include <dns/result.h>
42135446Strhodes#include <dns/soa.h>
43135446Strhodes#include <dns/tcpmsg.h>
44135446Strhodes#include <dns/timer.h>
45135446Strhodes#include <dns/tsig.h>
46135446Strhodes#include <dns/view.h>
47135446Strhodes#include <dns/xfrin.h>
48135446Strhodes#include <dns/zone.h>
49135446Strhodes
50135446Strhodes#include <dst/dst.h>
51135446Strhodes
52135446Strhodes/*
53135446Strhodes * Incoming AXFR and IXFR.
54135446Strhodes */
55135446Strhodes
56170222Sdougb/*%
57135446Strhodes * It would be non-sensical (or at least obtuse) to use FAIL() with an
58135446Strhodes * ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler
59135446Strhodes * from complaining about "end-of-loop code not reached".
60135446Strhodes */
61135446Strhodes#define FAIL(code) \
62135446Strhodes	do { result = (code);					\
63135446Strhodes		if (result != ISC_R_SUCCESS) goto failure;	\
64135446Strhodes	} while (0)
65135446Strhodes
66135446Strhodes#define CHECK(op) \
67135446Strhodes	do { result = (op);					\
68135446Strhodes		if (result != ISC_R_SUCCESS) goto failure;	\
69135446Strhodes	} while (0)
70135446Strhodes
71170222Sdougb/*%
72135446Strhodes * The states of the *XFR state machine.  We handle both IXFR and AXFR
73135446Strhodes * with a single integrated state machine because they cannot be distinguished
74135446Strhodes * immediately - an AXFR response to an IXFR request can only be detected
75135446Strhodes * when the first two (2) response RRs have already been received.
76135446Strhodes */
77135446Strhodestypedef enum {
78165071Sdougb	XFRST_SOAQUERY,
79165071Sdougb	XFRST_GOTSOA,
80135446Strhodes	XFRST_INITIALSOA,
81135446Strhodes	XFRST_FIRSTDATA,
82135446Strhodes	XFRST_IXFR_DELSOA,
83135446Strhodes	XFRST_IXFR_DEL,
84135446Strhodes	XFRST_IXFR_ADDSOA,
85135446Strhodes	XFRST_IXFR_ADD,
86224092Sdougb	XFRST_IXFR_END,
87135446Strhodes	XFRST_AXFR,
88224092Sdougb	XFRST_AXFR_END
89135446Strhodes} xfrin_state_t;
90135446Strhodes
91170222Sdougb/*%
92135446Strhodes * Incoming zone transfer context.
93135446Strhodes */
94135446Strhodes
95135446Strhodesstruct dns_xfrin_ctx {
96135446Strhodes	unsigned int		magic;
97135446Strhodes	isc_mem_t		*mctx;
98135446Strhodes	dns_zone_t		*zone;
99135446Strhodes
100135446Strhodes	int			refcount;
101135446Strhodes
102135446Strhodes	isc_task_t 		*task;
103135446Strhodes	isc_timer_t		*timer;
104135446Strhodes	isc_socketmgr_t 	*socketmgr;
105135446Strhodes
106170222Sdougb	int			connects; 	/*%< Connect in progress */
107170222Sdougb	int			sends;		/*%< Send in progress */
108170222Sdougb	int			recvs;	  	/*%< Receive in progress */
109135446Strhodes	isc_boolean_t		shuttingdown;
110135446Strhodes
111170222Sdougb	dns_name_t 		name; 		/*%< Name of zone to transfer */
112135446Strhodes	dns_rdataclass_t 	rdclass;
113135446Strhodes
114135446Strhodes	isc_boolean_t		checkid;
115135446Strhodes	dns_messageid_t		id;
116135446Strhodes
117170222Sdougb	/*%
118135446Strhodes	 * Requested transfer type (dns_rdatatype_axfr or
119135446Strhodes	 * dns_rdatatype_ixfr).  The actual transfer type
120135446Strhodes	 * may differ due to IXFR->AXFR fallback.
121135446Strhodes	 */
122135446Strhodes	dns_rdatatype_t 	reqtype;
123135446Strhodes
124135446Strhodes	isc_sockaddr_t 		masteraddr;
125135446Strhodes	isc_sockaddr_t		sourceaddr;
126135446Strhodes	isc_socket_t 		*socket;
127135446Strhodes
128170222Sdougb	/*% Buffer for IXFR/AXFR request message */
129135446Strhodes	isc_buffer_t 		qbuffer;
130135446Strhodes	unsigned char 		qbuffer_data[512];
131135446Strhodes
132170222Sdougb	/*% Incoming reply TCP message */
133135446Strhodes	dns_tcpmsg_t		tcpmsg;
134135446Strhodes	isc_boolean_t		tcpmsg_valid;
135135446Strhodes
136135446Strhodes	dns_db_t 		*db;
137135446Strhodes	dns_dbversion_t 	*ver;
138170222Sdougb	dns_diff_t 		diff;		/*%< Pending database changes */
139170222Sdougb	int 			difflen;	/*%< Number of pending tuples */
140135446Strhodes
141135446Strhodes	xfrin_state_t 		state;
142135446Strhodes	isc_uint32_t 		end_serial;
143135446Strhodes	isc_boolean_t 		is_ixfr;
144135446Strhodes
145170222Sdougb	unsigned int		nmsg;		/*%< Number of messages recvd */
146193149Sdougb	unsigned int		nrecs;		/*%< Number of records recvd */
147193149Sdougb	isc_uint64_t		nbytes;		/*%< Number of bytes received */
148135446Strhodes
149193149Sdougb	isc_time_t		start;		/*%< Start time of the transfer */
150193149Sdougb	isc_time_t		end;		/*%< End time of the transfer */
151193149Sdougb
152170222Sdougb	dns_tsigkey_t		*tsigkey;	/*%< Key used to create TSIG */
153170222Sdougb	isc_buffer_t		*lasttsig;	/*%< The last TSIG */
154170222Sdougb	dst_context_t		*tsigctx;	/*%< TSIG verification context */
155170222Sdougb	unsigned int		sincetsig;	/*%< recvd since the last TSIG */
156135446Strhodes	dns_xfrindone_t		done;
157135446Strhodes
158170222Sdougb	/*%
159135446Strhodes	 * AXFR- and IXFR-specific data.  Only one is used at a time
160135446Strhodes	 * according to the is_ixfr flag, so this could be a union,
161135446Strhodes	 * but keeping them separate makes it a bit simpler to clean
162135446Strhodes	 * things up when destroying the context.
163135446Strhodes	 */
164135446Strhodes	struct {
165135446Strhodes		dns_addrdatasetfunc_t add_func;
166135446Strhodes		dns_dbload_t	      *add_private;
167135446Strhodes	} axfr;
168135446Strhodes
169135446Strhodes	struct {
170135446Strhodes		isc_uint32_t 	request_serial;
171135446Strhodes		isc_uint32_t 	current_serial;
172135446Strhodes		dns_journal_t	*journal;
173135446Strhodes
174135446Strhodes	} ixfr;
175135446Strhodes};
176135446Strhodes
177135446Strhodes#define XFRIN_MAGIC		  ISC_MAGIC('X', 'f', 'r', 'I')
178135446Strhodes#define VALID_XFRIN(x)		  ISC_MAGIC_VALID(x, XFRIN_MAGIC)
179135446Strhodes
180135446Strhodes/**************************************************************************/
181135446Strhodes/*
182135446Strhodes * Forward declarations.
183135446Strhodes */
184135446Strhodes
185135446Strhodesstatic isc_result_t
186135446Strhodesxfrin_create(isc_mem_t *mctx,
187135446Strhodes	     dns_zone_t *zone,
188135446Strhodes	     dns_db_t *db,
189135446Strhodes	     isc_task_t *task,
190135446Strhodes	     isc_timermgr_t *timermgr,
191135446Strhodes	     isc_socketmgr_t *socketmgr,
192135446Strhodes	     dns_name_t *zonename,
193135446Strhodes	     dns_rdataclass_t rdclass,
194135446Strhodes	     dns_rdatatype_t reqtype,
195135446Strhodes	     isc_sockaddr_t *masteraddr,
196135446Strhodes	     isc_sockaddr_t *sourceaddr,
197135446Strhodes	     dns_tsigkey_t *tsigkey,
198135446Strhodes	     dns_xfrin_ctx_t **xfrp);
199135446Strhodes
200135446Strhodesstatic isc_result_t axfr_init(dns_xfrin_ctx_t *xfr);
201135446Strhodesstatic isc_result_t axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp);
202135446Strhodesstatic isc_result_t axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
203135446Strhodes				   dns_name_t *name, dns_ttl_t ttl,
204135446Strhodes				   dns_rdata_t *rdata);
205135446Strhodesstatic isc_result_t axfr_apply(dns_xfrin_ctx_t *xfr);
206135446Strhodesstatic isc_result_t axfr_commit(dns_xfrin_ctx_t *xfr);
207224092Sdougbstatic isc_result_t axfr_finalize(dns_xfrin_ctx_t *xfr);
208135446Strhodes
209135446Strhodesstatic isc_result_t ixfr_init(dns_xfrin_ctx_t *xfr);
210135446Strhodesstatic isc_result_t ixfr_apply(dns_xfrin_ctx_t *xfr);
211135446Strhodesstatic isc_result_t ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
212135446Strhodes				 dns_name_t *name, dns_ttl_t ttl,
213135446Strhodes				 dns_rdata_t *rdata);
214135446Strhodesstatic isc_result_t ixfr_commit(dns_xfrin_ctx_t *xfr);
215135446Strhodes
216135446Strhodesstatic isc_result_t xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name,
217135446Strhodes			   isc_uint32_t ttl, dns_rdata_t *rdata);
218135446Strhodes
219135446Strhodesstatic isc_result_t xfrin_start(dns_xfrin_ctx_t *xfr);
220135446Strhodes
221135446Strhodesstatic void xfrin_connect_done(isc_task_t *task, isc_event_t *event);
222135446Strhodesstatic isc_result_t xfrin_send_request(dns_xfrin_ctx_t *xfr);
223135446Strhodesstatic void xfrin_send_done(isc_task_t *task, isc_event_t *event);
224135446Strhodesstatic void xfrin_recv_done(isc_task_t *task, isc_event_t *event);
225135446Strhodesstatic void xfrin_timeout(isc_task_t *task, isc_event_t *event);
226135446Strhodes
227135446Strhodesstatic void maybe_free(dns_xfrin_ctx_t *xfr);
228135446Strhodes
229135446Strhodesstatic void
230135446Strhodesxfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg);
231135446Strhodesstatic isc_result_t
232135446Strhodesrender(dns_message_t *msg, isc_mem_t *mctx, isc_buffer_t *buf);
233135446Strhodes
234135446Strhodesstatic void
235170222Sdougbxfrin_logv(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
236170222Sdougb	   const char *fmt, va_list ap)
237170222Sdougb     ISC_FORMAT_PRINTF(4, 0);
238135446Strhodes
239135446Strhodesstatic void
240170222Sdougbxfrin_log1(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
241170222Sdougb	   const char *fmt, ...)
242170222Sdougb     ISC_FORMAT_PRINTF(4, 5);
243135446Strhodes
244135446Strhodesstatic void
245153816Sdougbxfrin_log(dns_xfrin_ctx_t *xfr, int level, const char *fmt, ...)
246135446Strhodes     ISC_FORMAT_PRINTF(3, 4);
247135446Strhodes
248135446Strhodes/**************************************************************************/
249135446Strhodes/*
250135446Strhodes * AXFR handling
251135446Strhodes */
252135446Strhodes
253135446Strhodesstatic isc_result_t
254135446Strhodesaxfr_init(dns_xfrin_ctx_t *xfr) {
255135446Strhodes	isc_result_t result;
256135446Strhodes
257186462Sdougb	xfr->is_ixfr = ISC_FALSE;
258135446Strhodes
259135446Strhodes	if (xfr->db != NULL)
260135446Strhodes		dns_db_detach(&xfr->db);
261135446Strhodes
262135446Strhodes	CHECK(axfr_makedb(xfr, &xfr->db));
263135446Strhodes	CHECK(dns_db_beginload(xfr->db, &xfr->axfr.add_func,
264135446Strhodes			       &xfr->axfr.add_private));
265135446Strhodes	result = ISC_R_SUCCESS;
266135446Strhodes failure:
267135446Strhodes	return (result);
268135446Strhodes}
269135446Strhodes
270135446Strhodesstatic isc_result_t
271135446Strhodesaxfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp) {
272262706Serwin	isc_result_t result;
273262706Serwin
274262706Serwin	result = dns_db_create(xfr->mctx, /* XXX */
275262706Serwin			       "rbt",	/* XXX guess */
276262706Serwin			       &xfr->name,
277262706Serwin			       dns_dbtype_zone,
278262706Serwin			       xfr->rdclass,
279262706Serwin			       0, NULL, /* XXX guess */
280262706Serwin			       dbp);
281262706Serwin	if (result == ISC_R_SUCCESS)
282262706Serwin		result = dns_zone_rpz_enable_db(xfr->zone, *dbp);
283262706Serwin	return (result);
284135446Strhodes}
285135446Strhodes
286135446Strhodesstatic isc_result_t
287135446Strhodesaxfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
288135446Strhodes	     dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata)
289135446Strhodes{
290135446Strhodes	isc_result_t result;
291135446Strhodes
292135446Strhodes	dns_difftuple_t *tuple = NULL;
293135446Strhodes
294135446Strhodes	CHECK(dns_zone_checknames(xfr->zone, name, rdata));
295135446Strhodes	CHECK(dns_difftuple_create(xfr->diff.mctx, op,
296135446Strhodes				   name, ttl, rdata, &tuple));
297135446Strhodes	dns_diff_append(&xfr->diff, &tuple);
298135446Strhodes	if (++xfr->difflen > 100)
299135446Strhodes		CHECK(axfr_apply(xfr));
300135446Strhodes	result = ISC_R_SUCCESS;
301135446Strhodes failure:
302135446Strhodes	return (result);
303135446Strhodes}
304135446Strhodes
305135446Strhodes/*
306135446Strhodes * Store a set of AXFR RRs in the database.
307135446Strhodes */
308135446Strhodesstatic isc_result_t
309135446Strhodesaxfr_apply(dns_xfrin_ctx_t *xfr) {
310135446Strhodes	isc_result_t result;
311135446Strhodes
312135446Strhodes	CHECK(dns_diff_load(&xfr->diff,
313135446Strhodes			    xfr->axfr.add_func, xfr->axfr.add_private));
314135446Strhodes	xfr->difflen = 0;
315135446Strhodes	dns_diff_clear(&xfr->diff);
316135446Strhodes	result = ISC_R_SUCCESS;
317135446Strhodes failure:
318135446Strhodes	return (result);
319135446Strhodes}
320135446Strhodes
321135446Strhodesstatic isc_result_t
322135446Strhodesaxfr_commit(dns_xfrin_ctx_t *xfr) {
323135446Strhodes	isc_result_t result;
324135446Strhodes
325135446Strhodes	CHECK(axfr_apply(xfr));
326135446Strhodes	CHECK(dns_db_endload(xfr->db, &xfr->axfr.add_private));
327224092Sdougb
328224092Sdougb	result = ISC_R_SUCCESS;
329224092Sdougb failure:
330224092Sdougb	return (result);
331224092Sdougb}
332224092Sdougb
333224092Sdougbstatic isc_result_t
334224092Sdougbaxfr_finalize(dns_xfrin_ctx_t *xfr) {
335224092Sdougb	isc_result_t result;
336224092Sdougb
337135446Strhodes	CHECK(dns_zone_replacedb(xfr->zone, xfr->db, ISC_TRUE));
338135446Strhodes
339135446Strhodes	result = ISC_R_SUCCESS;
340135446Strhodes failure:
341135446Strhodes	return (result);
342135446Strhodes}
343135446Strhodes
344135446Strhodes/**************************************************************************/
345135446Strhodes/*
346135446Strhodes * IXFR handling
347135446Strhodes */
348135446Strhodes
349135446Strhodesstatic isc_result_t
350135446Strhodesixfr_init(dns_xfrin_ctx_t *xfr) {
351135446Strhodes	isc_result_t result;
352135446Strhodes	char *journalfile;
353135446Strhodes
354135446Strhodes	if (xfr->reqtype != dns_rdatatype_ixfr) {
355135446Strhodes		xfrin_log(xfr, ISC_LOG_ERROR,
356135446Strhodes			  "got incremental response to AXFR request");
357135446Strhodes		return (DNS_R_FORMERR);
358135446Strhodes	}
359135446Strhodes
360135446Strhodes	xfr->is_ixfr = ISC_TRUE;
361135446Strhodes	INSIST(xfr->db != NULL);
362135446Strhodes	xfr->difflen = 0;
363135446Strhodes
364135446Strhodes	journalfile = dns_zone_getjournal(xfr->zone);
365135446Strhodes	if (journalfile != NULL)
366135446Strhodes		CHECK(dns_journal_open(xfr->mctx, journalfile,
367254897Serwin				       DNS_JOURNAL_CREATE, &xfr->ixfr.journal));
368135446Strhodes
369135446Strhodes	result = ISC_R_SUCCESS;
370135446Strhodes failure:
371135446Strhodes	return (result);
372135446Strhodes}
373135446Strhodes
374135446Strhodesstatic isc_result_t
375135446Strhodesixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
376135446Strhodes	     dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata)
377135446Strhodes{
378135446Strhodes	isc_result_t result;
379135446Strhodes
380135446Strhodes	dns_difftuple_t *tuple = NULL;
381135446Strhodes	if (op == DNS_DIFFOP_ADD)
382135446Strhodes		CHECK(dns_zone_checknames(xfr->zone, name, rdata));
383135446Strhodes	CHECK(dns_difftuple_create(xfr->diff.mctx, op,
384135446Strhodes				   name, ttl, rdata, &tuple));
385135446Strhodes	dns_diff_append(&xfr->diff, &tuple);
386135446Strhodes	if (++xfr->difflen > 100)
387135446Strhodes		CHECK(ixfr_apply(xfr));
388135446Strhodes	result = ISC_R_SUCCESS;
389135446Strhodes failure:
390135446Strhodes	return (result);
391135446Strhodes}
392135446Strhodes
393135446Strhodes/*
394135446Strhodes * Apply a set of IXFR changes to the database.
395135446Strhodes */
396135446Strhodesstatic isc_result_t
397135446Strhodesixfr_apply(dns_xfrin_ctx_t *xfr) {
398135446Strhodes	isc_result_t result;
399135446Strhodes
400135446Strhodes	if (xfr->ver == NULL) {
401135446Strhodes		CHECK(dns_db_newversion(xfr->db, &xfr->ver));
402135446Strhodes		if (xfr->ixfr.journal != NULL)
403135446Strhodes			CHECK(dns_journal_begin_transaction(xfr->ixfr.journal));
404135446Strhodes	}
405135446Strhodes	CHECK(dns_diff_apply(&xfr->diff, xfr->db, xfr->ver));
406135446Strhodes	if (xfr->ixfr.journal != NULL) {
407135446Strhodes		result = dns_journal_writediff(xfr->ixfr.journal, &xfr->diff);
408135446Strhodes		if (result != ISC_R_SUCCESS)
409135446Strhodes			goto failure;
410135446Strhodes	}
411135446Strhodes	dns_diff_clear(&xfr->diff);
412135446Strhodes	xfr->difflen = 0;
413135446Strhodes	result = ISC_R_SUCCESS;
414135446Strhodes failure:
415135446Strhodes	return (result);
416135446Strhodes}
417135446Strhodes
418135446Strhodesstatic isc_result_t
419135446Strhodesixfr_commit(dns_xfrin_ctx_t *xfr) {
420135446Strhodes	isc_result_t result;
421135446Strhodes
422135446Strhodes	CHECK(ixfr_apply(xfr));
423135446Strhodes	if (xfr->ver != NULL) {
424135446Strhodes		/* XXX enter ready-to-commit state here */
425135446Strhodes		if (xfr->ixfr.journal != NULL)
426135446Strhodes			CHECK(dns_journal_commit(xfr->ixfr.journal));
427135446Strhodes		dns_db_closeversion(xfr->db, &xfr->ver, ISC_TRUE);
428135446Strhodes		dns_zone_markdirty(xfr->zone);
429135446Strhodes	}
430135446Strhodes	result = ISC_R_SUCCESS;
431135446Strhodes failure:
432135446Strhodes	return (result);
433135446Strhodes}
434135446Strhodes
435135446Strhodes/**************************************************************************/
436135446Strhodes/*
437135446Strhodes * Common AXFR/IXFR protocol code
438135446Strhodes */
439135446Strhodes
440135446Strhodes/*
441135446Strhodes * Handle a single incoming resource record according to the current
442135446Strhodes * state.
443135446Strhodes */
444135446Strhodesstatic isc_result_t
445135446Strhodesxfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name, isc_uint32_t ttl,
446135446Strhodes       dns_rdata_t *rdata)
447135446Strhodes{
448135446Strhodes	isc_result_t result;
449135446Strhodes
450193149Sdougb	xfr->nrecs++;
451193149Sdougb
452186462Sdougb	if (rdata->type == dns_rdatatype_none ||
453186462Sdougb	    dns_rdatatype_ismeta(rdata->type))
454186462Sdougb		FAIL(DNS_R_FORMERR);
455186462Sdougb
456135446Strhodes redo:
457135446Strhodes	switch (xfr->state) {
458165071Sdougb	case XFRST_SOAQUERY:
459165071Sdougb		if (rdata->type != dns_rdatatype_soa) {
460165071Sdougb			xfrin_log(xfr, ISC_LOG_ERROR,
461165071Sdougb				  "non-SOA response to SOA query");
462165071Sdougb			FAIL(DNS_R_FORMERR);
463165071Sdougb		}
464165071Sdougb		xfr->end_serial = dns_soa_getserial(rdata);
465165071Sdougb		if (!DNS_SERIAL_GT(xfr->end_serial, xfr->ixfr.request_serial) &&
466165071Sdougb		    !dns_zone_isforced(xfr->zone)) {
467165071Sdougb			xfrin_log(xfr, ISC_LOG_DEBUG(3),
468165071Sdougb				  "requested serial %u, "
469165071Sdougb				  "master has %u, not updating",
470165071Sdougb				  xfr->ixfr.request_serial, xfr->end_serial);
471165071Sdougb			FAIL(DNS_R_UPTODATE);
472165071Sdougb		}
473165071Sdougb		xfr->state = XFRST_GOTSOA;
474165071Sdougb		break;
475165071Sdougb
476165071Sdougb	case XFRST_GOTSOA:
477165071Sdougb		/*
478165071Sdougb		 * Skip other records in the answer section.
479165071Sdougb		 */
480165071Sdougb		break;
481165071Sdougb
482135446Strhodes	case XFRST_INITIALSOA:
483135446Strhodes		if (rdata->type != dns_rdatatype_soa) {
484135446Strhodes			xfrin_log(xfr, ISC_LOG_ERROR,
485135446Strhodes				  "first RR in zone transfer must be SOA");
486135446Strhodes			FAIL(DNS_R_FORMERR);
487135446Strhodes		}
488135446Strhodes		/*
489170222Sdougb		 * Remember the serial number in the initial SOA.
490135446Strhodes		 * We need it to recognize the end of an IXFR.
491135446Strhodes		 */
492135446Strhodes		xfr->end_serial = dns_soa_getserial(rdata);
493135446Strhodes		if (xfr->reqtype == dns_rdatatype_ixfr &&
494135446Strhodes		    ! DNS_SERIAL_GT(xfr->end_serial, xfr->ixfr.request_serial)
495135446Strhodes		    && !dns_zone_isforced(xfr->zone))
496135446Strhodes		{
497135446Strhodes			/*
498135446Strhodes			 * This must be the single SOA record that is
499135446Strhodes			 * sent when the current version on the master
500135446Strhodes			 * is not newer than the version in the request.
501135446Strhodes			 */
502135446Strhodes			xfrin_log(xfr, ISC_LOG_DEBUG(3),
503135446Strhodes				  "requested serial %u, "
504135446Strhodes				  "master has %u, not updating",
505135446Strhodes				  xfr->ixfr.request_serial, xfr->end_serial);
506135446Strhodes			FAIL(DNS_R_UPTODATE);
507135446Strhodes		}
508135446Strhodes		if (xfr->reqtype == dns_rdatatype_axfr)
509135446Strhodes			xfr->checkid = ISC_FALSE;
510135446Strhodes		xfr->state = XFRST_FIRSTDATA;
511135446Strhodes		break;
512135446Strhodes
513135446Strhodes	case XFRST_FIRSTDATA:
514135446Strhodes		/*
515135446Strhodes		 * If the transfer begins with one SOA record, it is an AXFR,
516135446Strhodes		 * if it begins with two SOAs, it is an IXFR.
517135446Strhodes		 */
518135446Strhodes		if (xfr->reqtype == dns_rdatatype_ixfr &&
519135446Strhodes		    rdata->type == dns_rdatatype_soa &&
520135446Strhodes		    xfr->ixfr.request_serial == dns_soa_getserial(rdata)) {
521135446Strhodes			xfrin_log(xfr, ISC_LOG_DEBUG(3),
522135446Strhodes				  "got incremental response");
523135446Strhodes			CHECK(ixfr_init(xfr));
524135446Strhodes			xfr->state = XFRST_IXFR_DELSOA;
525135446Strhodes		} else {
526135446Strhodes			xfrin_log(xfr, ISC_LOG_DEBUG(3),
527135446Strhodes				  "got nonincremental response");
528135446Strhodes			CHECK(axfr_init(xfr));
529135446Strhodes			xfr->state = XFRST_AXFR;
530135446Strhodes		}
531135446Strhodes		goto redo;
532135446Strhodes
533135446Strhodes	case XFRST_IXFR_DELSOA:
534135446Strhodes		INSIST(rdata->type == dns_rdatatype_soa);
535135446Strhodes		CHECK(ixfr_putdata(xfr, DNS_DIFFOP_DEL, name, ttl, rdata));
536135446Strhodes		xfr->state = XFRST_IXFR_DEL;
537135446Strhodes		break;
538135446Strhodes
539135446Strhodes	case XFRST_IXFR_DEL:
540135446Strhodes		if (rdata->type == dns_rdatatype_soa) {
541135446Strhodes			isc_uint32_t soa_serial = dns_soa_getserial(rdata);
542135446Strhodes			xfr->state = XFRST_IXFR_ADDSOA;
543135446Strhodes			xfr->ixfr.current_serial = soa_serial;
544135446Strhodes			goto redo;
545135446Strhodes		}
546135446Strhodes		CHECK(ixfr_putdata(xfr, DNS_DIFFOP_DEL, name, ttl, rdata));
547135446Strhodes		break;
548135446Strhodes
549135446Strhodes	case XFRST_IXFR_ADDSOA:
550135446Strhodes		INSIST(rdata->type == dns_rdatatype_soa);
551135446Strhodes		CHECK(ixfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
552135446Strhodes		xfr->state = XFRST_IXFR_ADD;
553135446Strhodes		break;
554135446Strhodes
555135446Strhodes	case XFRST_IXFR_ADD:
556135446Strhodes		if (rdata->type == dns_rdatatype_soa) {
557135446Strhodes			isc_uint32_t soa_serial = dns_soa_getserial(rdata);
558135446Strhodes			if (soa_serial == xfr->end_serial) {
559143731Sdougb				CHECK(ixfr_commit(xfr));
560224092Sdougb				xfr->state = XFRST_IXFR_END;
561135446Strhodes				break;
562135446Strhodes			} else if (soa_serial != xfr->ixfr.current_serial) {
563135446Strhodes				xfrin_log(xfr, ISC_LOG_ERROR,
564135446Strhodes					  "IXFR out of sync: "
565135446Strhodes					  "expected serial %u, got %u",
566135446Strhodes					  xfr->ixfr.current_serial, soa_serial);
567135446Strhodes				FAIL(DNS_R_FORMERR);
568135446Strhodes			} else {
569143731Sdougb				CHECK(ixfr_commit(xfr));
570135446Strhodes				xfr->state = XFRST_IXFR_DELSOA;
571135446Strhodes				goto redo;
572135446Strhodes			}
573135446Strhodes		}
574135446Strhodes		if (rdata->type == dns_rdatatype_ns &&
575135446Strhodes		    dns_name_iswildcard(name))
576135446Strhodes			FAIL(DNS_R_INVALIDNS);
577135446Strhodes		CHECK(ixfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
578135446Strhodes		break;
579135446Strhodes
580135446Strhodes	case XFRST_AXFR:
581135446Strhodes		/*
582135446Strhodes		 * Old BINDs sent cross class A records for non IN classes.
583135446Strhodes		 */
584135446Strhodes		if (rdata->type == dns_rdatatype_a &&
585135446Strhodes		    rdata->rdclass != xfr->rdclass &&
586135446Strhodes		    xfr->rdclass != dns_rdataclass_in)
587135446Strhodes			break;
588135446Strhodes		CHECK(axfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
589135446Strhodes		if (rdata->type == dns_rdatatype_soa) {
590135446Strhodes			CHECK(axfr_commit(xfr));
591224092Sdougb			xfr->state = XFRST_AXFR_END;
592135446Strhodes			break;
593135446Strhodes		}
594135446Strhodes		break;
595224092Sdougb	case XFRST_AXFR_END:
596224092Sdougb	case XFRST_IXFR_END:
597135446Strhodes		FAIL(DNS_R_EXTRADATA);
598254402Serwin		/* NOTREACHED */
599135446Strhodes	default:
600135446Strhodes		INSIST(0);
601135446Strhodes		break;
602135446Strhodes	}
603135446Strhodes	result = ISC_R_SUCCESS;
604135446Strhodes failure:
605135446Strhodes	return (result);
606135446Strhodes}
607135446Strhodes
608135446Strhodesisc_result_t
609135446Strhodesdns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
610135446Strhodes		 isc_sockaddr_t *masteraddr, dns_tsigkey_t *tsigkey,
611135446Strhodes		 isc_mem_t *mctx, isc_timermgr_t *timermgr,
612135446Strhodes		 isc_socketmgr_t *socketmgr, isc_task_t *task,
613135446Strhodes		 dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp)
614135446Strhodes{
615135446Strhodes	isc_sockaddr_t sourceaddr;
616135446Strhodes
617135446Strhodes	switch (isc_sockaddr_pf(masteraddr)) {
618135446Strhodes	case PF_INET:
619135446Strhodes		sourceaddr = *dns_zone_getxfrsource4(zone);
620135446Strhodes		break;
621135446Strhodes	case PF_INET6:
622135446Strhodes		sourceaddr = *dns_zone_getxfrsource6(zone);
623135446Strhodes		break;
624135446Strhodes	default:
625135446Strhodes		INSIST(0);
626135446Strhodes	}
627135446Strhodes
628135446Strhodes	return(dns_xfrin_create2(zone, xfrtype, masteraddr, &sourceaddr,
629135446Strhodes				 tsigkey, mctx, timermgr, socketmgr,
630135446Strhodes				 task, done, xfrp));
631135446Strhodes}
632135446Strhodes
633135446Strhodesisc_result_t
634135446Strhodesdns_xfrin_create2(dns_zone_t *zone, dns_rdatatype_t xfrtype,
635135446Strhodes		  isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr,
636135446Strhodes		  dns_tsigkey_t *tsigkey, isc_mem_t *mctx,
637135446Strhodes		  isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr,
638254897Serwin		  isc_task_t *task, dns_xfrindone_t done,
639254897Serwin		  dns_xfrin_ctx_t **xfrp)
640135446Strhodes{
641135446Strhodes	dns_name_t *zonename = dns_zone_getorigin(zone);
642153816Sdougb	dns_xfrin_ctx_t *xfr = NULL;
643135446Strhodes	isc_result_t result;
644135446Strhodes	dns_db_t *db = NULL;
645135446Strhodes
646135446Strhodes	REQUIRE(xfrp != NULL && *xfrp == NULL);
647135446Strhodes
648135446Strhodes	(void)dns_zone_getdb(zone, &db);
649135446Strhodes
650165071Sdougb	if (xfrtype == dns_rdatatype_soa || xfrtype == dns_rdatatype_ixfr)
651165071Sdougb		REQUIRE(db != NULL);
652165071Sdougb
653135446Strhodes	CHECK(xfrin_create(mctx, zone, db, task, timermgr, socketmgr, zonename,
654135446Strhodes			   dns_zone_getclass(zone), xfrtype, masteraddr,
655135446Strhodes			   sourceaddr, tsigkey, &xfr));
656135446Strhodes
657135446Strhodes	CHECK(xfrin_start(xfr));
658135446Strhodes
659135446Strhodes	xfr->done = done;
660135446Strhodes	xfr->refcount++;
661135446Strhodes	*xfrp = xfr;
662135446Strhodes
663135446Strhodes failure:
664135446Strhodes	if (db != NULL)
665135446Strhodes		dns_db_detach(&db);
666170222Sdougb	if (result != ISC_R_SUCCESS) {
667170222Sdougb		char zonetext[DNS_NAME_MAXTEXT+32];
668170222Sdougb		dns_zone_name(zone, zonetext, sizeof(zonetext));
669170222Sdougb		xfrin_log1(ISC_LOG_ERROR, zonetext, masteraddr,
670170222Sdougb			   "zone transfer setup failed");
671170222Sdougb	}
672135446Strhodes	return (result);
673135446Strhodes}
674135446Strhodes
675135446Strhodesvoid
676135446Strhodesdns_xfrin_shutdown(dns_xfrin_ctx_t *xfr) {
677135446Strhodes	if (! xfr->shuttingdown)
678135446Strhodes		xfrin_fail(xfr, ISC_R_CANCELED, "shut down");
679135446Strhodes}
680135446Strhodes
681135446Strhodesvoid
682135446Strhodesdns_xfrin_attach(dns_xfrin_ctx_t *source, dns_xfrin_ctx_t **target) {
683135446Strhodes	REQUIRE(target != NULL && *target == NULL);
684135446Strhodes	source->refcount++;
685135446Strhodes	*target = source;
686135446Strhodes}
687135446Strhodes
688135446Strhodesvoid
689135446Strhodesdns_xfrin_detach(dns_xfrin_ctx_t **xfrp) {
690135446Strhodes	dns_xfrin_ctx_t *xfr = *xfrp;
691135446Strhodes	INSIST(xfr->refcount > 0);
692135446Strhodes	xfr->refcount--;
693135446Strhodes	maybe_free(xfr);
694135446Strhodes	*xfrp = NULL;
695135446Strhodes}
696135446Strhodes
697135446Strhodesstatic void
698135446Strhodesxfrin_cancelio(dns_xfrin_ctx_t *xfr) {
699135446Strhodes	if (xfr->connects > 0) {
700135446Strhodes		isc_socket_cancel(xfr->socket, xfr->task,
701135446Strhodes				  ISC_SOCKCANCEL_CONNECT);
702135446Strhodes	} else if (xfr->recvs > 0) {
703135446Strhodes		dns_tcpmsg_cancelread(&xfr->tcpmsg);
704135446Strhodes	} else if (xfr->sends > 0) {
705135446Strhodes		isc_socket_cancel(xfr->socket, xfr->task,
706135446Strhodes				  ISC_SOCKCANCEL_SEND);
707135446Strhodes	}
708135446Strhodes}
709135446Strhodes
710135446Strhodesstatic void
711135446Strhodesxfrin_reset(dns_xfrin_ctx_t *xfr) {
712135446Strhodes	REQUIRE(VALID_XFRIN(xfr));
713135446Strhodes
714135446Strhodes	xfrin_log(xfr, ISC_LOG_INFO, "resetting");
715135446Strhodes
716135446Strhodes	xfrin_cancelio(xfr);
717135446Strhodes
718135446Strhodes	if (xfr->socket != NULL)
719135446Strhodes		isc_socket_detach(&xfr->socket);
720135446Strhodes
721135446Strhodes	if (xfr->lasttsig != NULL)
722135446Strhodes		isc_buffer_free(&xfr->lasttsig);
723135446Strhodes
724135446Strhodes	dns_diff_clear(&xfr->diff);
725135446Strhodes	xfr->difflen = 0;
726135446Strhodes
727135446Strhodes	if (xfr->ixfr.journal != NULL)
728135446Strhodes		dns_journal_destroy(&xfr->ixfr.journal);
729135446Strhodes
730135446Strhodes	if (xfr->axfr.add_private != NULL) {
731135446Strhodes		(void)dns_db_endload(xfr->db, &xfr->axfr.add_private);
732135446Strhodes		xfr->axfr.add_func = NULL;
733135446Strhodes	}
734135446Strhodes
735135446Strhodes	if (xfr->tcpmsg_valid) {
736135446Strhodes		dns_tcpmsg_invalidate(&xfr->tcpmsg);
737135446Strhodes		xfr->tcpmsg_valid = ISC_FALSE;
738135446Strhodes	}
739135446Strhodes
740135446Strhodes	if (xfr->ver != NULL)
741135446Strhodes		dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
742135446Strhodes}
743135446Strhodes
744135446Strhodes
745135446Strhodesstatic void
746135446Strhodesxfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg) {
747135446Strhodes	if (result != DNS_R_UPTODATE) {
748135446Strhodes		xfrin_log(xfr, ISC_LOG_ERROR, "%s: %s",
749135446Strhodes			  msg, isc_result_totext(result));
750135446Strhodes		if (xfr->is_ixfr)
751135446Strhodes			/* Pass special result code to force AXFR retry */
752135446Strhodes			result = DNS_R_BADIXFR;
753135446Strhodes	}
754135446Strhodes	xfrin_cancelio(xfr);
755174187Sdougb	/*
756174187Sdougb	 * Close the journal.
757174187Sdougb	 */
758174187Sdougb	if (xfr->ixfr.journal != NULL)
759174187Sdougb		dns_journal_destroy(&xfr->ixfr.journal);
760135446Strhodes	if (xfr->done != NULL) {
761135446Strhodes		(xfr->done)(xfr->zone, result);
762135446Strhodes		xfr->done = NULL;
763135446Strhodes	}
764135446Strhodes	xfr->shuttingdown = ISC_TRUE;
765135446Strhodes	maybe_free(xfr);
766135446Strhodes}
767135446Strhodes
768135446Strhodesstatic isc_result_t
769135446Strhodesxfrin_create(isc_mem_t *mctx,
770135446Strhodes	     dns_zone_t *zone,
771135446Strhodes	     dns_db_t *db,
772135446Strhodes	     isc_task_t *task,
773135446Strhodes	     isc_timermgr_t *timermgr,
774135446Strhodes	     isc_socketmgr_t *socketmgr,
775135446Strhodes	     dns_name_t *zonename,
776135446Strhodes	     dns_rdataclass_t rdclass,
777135446Strhodes	     dns_rdatatype_t reqtype,
778135446Strhodes	     isc_sockaddr_t *masteraddr,
779135446Strhodes	     isc_sockaddr_t *sourceaddr,
780135446Strhodes	     dns_tsigkey_t *tsigkey,
781135446Strhodes	     dns_xfrin_ctx_t **xfrp)
782135446Strhodes{
783135446Strhodes	dns_xfrin_ctx_t *xfr = NULL;
784135446Strhodes	isc_result_t result;
785135446Strhodes	isc_uint32_t tmp;
786135446Strhodes
787135446Strhodes	xfr = isc_mem_get(mctx, sizeof(*xfr));
788135446Strhodes	if (xfr == NULL)
789135446Strhodes		return (ISC_R_NOMEMORY);
790254402Serwin	xfr->mctx = NULL;
791254402Serwin	isc_mem_attach(mctx, &xfr->mctx);
792135446Strhodes	xfr->refcount = 0;
793135446Strhodes	xfr->zone = NULL;
794135446Strhodes	dns_zone_iattach(zone, &xfr->zone);
795135446Strhodes	xfr->task = NULL;
796135446Strhodes	isc_task_attach(task, &xfr->task);
797135446Strhodes	xfr->timer = NULL;
798135446Strhodes	xfr->socketmgr = socketmgr;
799135446Strhodes	xfr->done = NULL;
800135446Strhodes
801135446Strhodes	xfr->connects = 0;
802135446Strhodes	xfr->sends = 0;
803135446Strhodes	xfr->recvs = 0;
804135446Strhodes	xfr->shuttingdown = ISC_FALSE;
805135446Strhodes
806135446Strhodes	dns_name_init(&xfr->name, NULL);
807135446Strhodes	xfr->rdclass = rdclass;
808135446Strhodes	isc_random_get(&tmp);
809135446Strhodes	xfr->checkid = ISC_TRUE;
810135446Strhodes	xfr->id	= (isc_uint16_t)(tmp & 0xffff);
811135446Strhodes	xfr->reqtype = reqtype;
812135446Strhodes
813135446Strhodes	/* sockaddr */
814135446Strhodes	xfr->socket = NULL;
815135446Strhodes	/* qbuffer */
816135446Strhodes	/* qbuffer_data */
817135446Strhodes	/* tcpmsg */
818135446Strhodes	xfr->tcpmsg_valid = ISC_FALSE;
819135446Strhodes
820135446Strhodes	xfr->db = NULL;
821135446Strhodes	if (db != NULL)
822135446Strhodes		dns_db_attach(db, &xfr->db);
823135446Strhodes	xfr->ver = NULL;
824135446Strhodes	dns_diff_init(xfr->mctx, &xfr->diff);
825135446Strhodes	xfr->difflen = 0;
826135446Strhodes
827165071Sdougb	if (reqtype == dns_rdatatype_soa)
828165071Sdougb		xfr->state = XFRST_SOAQUERY;
829165071Sdougb	else
830165071Sdougb		xfr->state = XFRST_INITIALSOA;
831135446Strhodes	/* end_serial */
832135446Strhodes
833135446Strhodes	xfr->nmsg = 0;
834193149Sdougb	xfr->nrecs = 0;
835193149Sdougb	xfr->nbytes = 0;
836193149Sdougb	isc_time_now(&xfr->start);
837135446Strhodes
838135446Strhodes	xfr->tsigkey = NULL;
839135446Strhodes	if (tsigkey != NULL)
840135446Strhodes		dns_tsigkey_attach(tsigkey, &xfr->tsigkey);
841135446Strhodes	xfr->lasttsig = NULL;
842135446Strhodes	xfr->tsigctx = NULL;
843135446Strhodes	xfr->sincetsig = 0;
844135446Strhodes	xfr->is_ixfr = ISC_FALSE;
845135446Strhodes
846135446Strhodes	/* ixfr.request_serial */
847135446Strhodes	/* ixfr.current_serial */
848135446Strhodes	xfr->ixfr.journal = NULL;
849135446Strhodes
850135446Strhodes	xfr->axfr.add_func = NULL;
851135446Strhodes	xfr->axfr.add_private = NULL;
852135446Strhodes
853135446Strhodes	CHECK(dns_name_dup(zonename, mctx, &xfr->name));
854135446Strhodes
855135446Strhodes	CHECK(isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
856135446Strhodes			       task, xfrin_timeout, xfr, &xfr->timer));
857135446Strhodes	CHECK(dns_timer_setidle(xfr->timer,
858135446Strhodes				dns_zone_getmaxxfrin(xfr->zone),
859135446Strhodes				dns_zone_getidlein(xfr->zone),
860135446Strhodes				ISC_FALSE));
861135446Strhodes
862135446Strhodes	xfr->masteraddr = *masteraddr;
863135446Strhodes
864135446Strhodes	INSIST(isc_sockaddr_pf(masteraddr) == isc_sockaddr_pf(sourceaddr));
865135446Strhodes	xfr->sourceaddr = *sourceaddr;
866135446Strhodes	isc_sockaddr_setport(&xfr->sourceaddr, 0);
867135446Strhodes
868262706Serwin	/*
869262706Serwin	 * Reserve 2 bytes for TCP length at the begining of the buffer.
870262706Serwin	 */
871262706Serwin	isc_buffer_init(&xfr->qbuffer, &xfr->qbuffer_data[2],
872262706Serwin			sizeof(xfr->qbuffer_data) - 2);
873135446Strhodes
874135446Strhodes	xfr->magic = XFRIN_MAGIC;
875135446Strhodes	*xfrp = xfr;
876135446Strhodes	return (ISC_R_SUCCESS);
877135446Strhodes
878135446Strhodes failure:
879165071Sdougb	if (xfr->timer != NULL)
880165071Sdougb		isc_timer_detach(&xfr->timer);
881165071Sdougb	if (dns_name_dynamic(&xfr->name))
882165071Sdougb		dns_name_free(&xfr->name, xfr->mctx);
883165071Sdougb	if (xfr->tsigkey != NULL)
884165071Sdougb		dns_tsigkey_detach(&xfr->tsigkey);
885165071Sdougb	if (xfr->db != NULL)
886165071Sdougb		dns_db_detach(&xfr->db);
887165071Sdougb	isc_task_detach(&xfr->task);
888165071Sdougb	dns_zone_idetach(&xfr->zone);
889254402Serwin	isc_mem_putanddetach(&xfr->mctx, xfr, sizeof(*xfr));
890165071Sdougb
891135446Strhodes	return (result);
892135446Strhodes}
893135446Strhodes
894135446Strhodesstatic isc_result_t
895135446Strhodesxfrin_start(dns_xfrin_ctx_t *xfr) {
896135446Strhodes	isc_result_t result;
897135446Strhodes	CHECK(isc_socket_create(xfr->socketmgr,
898135446Strhodes				isc_sockaddr_pf(&xfr->sourceaddr),
899135446Strhodes				isc_sockettype_tcp,
900135446Strhodes				&xfr->socket));
901193149Sdougb	isc_socket_setname(xfr->socket, "xfrin", NULL);
902165071Sdougb#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
903182645Sdougb	CHECK(isc_socket_bind(xfr->socket, &xfr->sourceaddr,
904182645Sdougb			      ISC_SOCKET_REUSEADDRESS));
905165071Sdougb#endif
906135446Strhodes	CHECK(isc_socket_connect(xfr->socket, &xfr->masteraddr, xfr->task,
907135446Strhodes				 xfrin_connect_done, xfr));
908135446Strhodes	xfr->connects++;
909135446Strhodes	return (ISC_R_SUCCESS);
910135446Strhodes failure:
911135446Strhodes	xfrin_fail(xfr, result, "failed setting up socket");
912135446Strhodes	return (result);
913135446Strhodes}
914135446Strhodes
915135446Strhodes/* XXX the resolver could use this, too */
916135446Strhodes
917135446Strhodesstatic isc_result_t
918135446Strhodesrender(dns_message_t *msg, isc_mem_t *mctx, isc_buffer_t *buf) {
919135446Strhodes	dns_compress_t cctx;
920135446Strhodes	isc_boolean_t cleanup_cctx = ISC_FALSE;
921135446Strhodes	isc_result_t result;
922135446Strhodes
923135446Strhodes	CHECK(dns_compress_init(&cctx, -1, mctx));
924135446Strhodes	cleanup_cctx = ISC_TRUE;
925135446Strhodes	CHECK(dns_message_renderbegin(msg, &cctx, buf));
926135446Strhodes	CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0));
927135446Strhodes	CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0));
928135446Strhodes	CHECK(dns_message_rendersection(msg, DNS_SECTION_AUTHORITY, 0));
929135446Strhodes	CHECK(dns_message_rendersection(msg, DNS_SECTION_ADDITIONAL, 0));
930135446Strhodes	CHECK(dns_message_renderend(msg));
931135446Strhodes	result = ISC_R_SUCCESS;
932135446Strhodes failure:
933186462Sdougb	if (cleanup_cctx)
934186462Sdougb		dns_compress_invalidate(&cctx);
935135446Strhodes	return (result);
936135446Strhodes}
937135446Strhodes
938135446Strhodes/*
939135446Strhodes * A connection has been established.
940135446Strhodes */
941135446Strhodesstatic void
942135446Strhodesxfrin_connect_done(isc_task_t *task, isc_event_t *event) {
943135446Strhodes	isc_socket_connev_t *cev = (isc_socket_connev_t *) event;
944135446Strhodes	dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
945193149Sdougb	isc_result_t result = cev->result;
946135446Strhodes	char sourcetext[ISC_SOCKADDR_FORMATSIZE];
947135446Strhodes	isc_sockaddr_t sockaddr;
948262706Serwin	dns_zonemgr_t * zmgr;
949262706Serwin	isc_time_t now;
950135446Strhodes
951135446Strhodes	REQUIRE(VALID_XFRIN(xfr));
952135446Strhodes
953135446Strhodes	UNUSED(task);
954135446Strhodes
955135446Strhodes	INSIST(event->ev_type == ISC_SOCKEVENT_CONNECT);
956135446Strhodes	isc_event_free(&event);
957135446Strhodes
958135446Strhodes	xfr->connects--;
959135446Strhodes	if (xfr->shuttingdown) {
960135446Strhodes		maybe_free(xfr);
961135446Strhodes		return;
962135446Strhodes	}
963135446Strhodes
964262706Serwin	zmgr = dns_zone_getmgr(xfr->zone);
965262706Serwin	if (zmgr != NULL) {
966262706Serwin		if (result != ISC_R_SUCCESS) {
967193149Sdougb			TIME_NOW(&now);
968193149Sdougb			dns_zonemgr_unreachableadd(zmgr, &xfr->masteraddr,
969193149Sdougb						   &xfr->sourceaddr, &now);
970262706Serwin			goto failure;
971262706Serwin		} else
972262706Serwin			dns_zonemgr_unreachabledel(zmgr, &xfr->masteraddr,
973262706Serwin						   &xfr->sourceaddr);
974193149Sdougb	}
975193149Sdougb
976135446Strhodes	result = isc_socket_getsockname(xfr->socket, &sockaddr);
977135446Strhodes	if (result == ISC_R_SUCCESS) {
978135446Strhodes		isc_sockaddr_format(&sockaddr, sourcetext, sizeof(sourcetext));
979135446Strhodes	} else
980135446Strhodes		strcpy(sourcetext, "<UNKNOWN>");
981135446Strhodes	xfrin_log(xfr, ISC_LOG_INFO, "connected using %s", sourcetext);
982135446Strhodes
983135446Strhodes	dns_tcpmsg_init(xfr->mctx, xfr->socket, &xfr->tcpmsg);
984135446Strhodes	xfr->tcpmsg_valid = ISC_TRUE;
985135446Strhodes
986135446Strhodes	CHECK(xfrin_send_request(xfr));
987135446Strhodes failure:
988135446Strhodes	if (result != ISC_R_SUCCESS)
989135446Strhodes		xfrin_fail(xfr, result, "failed to connect");
990135446Strhodes}
991135446Strhodes
992135446Strhodes/*
993135446Strhodes * Convert a tuple into a dns_name_t suitable for inserting
994135446Strhodes * into the given dns_message_t.
995135446Strhodes */
996135446Strhodesstatic isc_result_t
997135446Strhodestuple2msgname(dns_difftuple_t *tuple, dns_message_t *msg, dns_name_t **target)
998135446Strhodes{
999135446Strhodes	isc_result_t result;
1000135446Strhodes	dns_rdata_t *rdata = NULL;
1001135446Strhodes	dns_rdatalist_t *rdl = NULL;
1002135446Strhodes	dns_rdataset_t *rds = NULL;
1003135446Strhodes	dns_name_t *name = NULL;
1004135446Strhodes
1005135446Strhodes	REQUIRE(target != NULL && *target == NULL);
1006135446Strhodes
1007135446Strhodes	CHECK(dns_message_gettemprdata(msg, &rdata));
1008135446Strhodes	dns_rdata_init(rdata);
1009135446Strhodes	dns_rdata_clone(&tuple->rdata, rdata);
1010135446Strhodes
1011135446Strhodes	CHECK(dns_message_gettemprdatalist(msg, &rdl));
1012135446Strhodes	dns_rdatalist_init(rdl);
1013135446Strhodes	rdl->type = tuple->rdata.type;
1014135446Strhodes	rdl->rdclass = tuple->rdata.rdclass;
1015135446Strhodes	rdl->ttl = tuple->ttl;
1016135446Strhodes	ISC_LIST_APPEND(rdl->rdata, rdata, link);
1017135446Strhodes
1018135446Strhodes	CHECK(dns_message_gettemprdataset(msg, &rds));
1019135446Strhodes	dns_rdataset_init(rds);
1020135446Strhodes	CHECK(dns_rdatalist_tordataset(rdl, rds));
1021135446Strhodes
1022135446Strhodes	CHECK(dns_message_gettempname(msg, &name));
1023135446Strhodes	dns_name_init(name, NULL);
1024135446Strhodes	dns_name_clone(&tuple->name, name);
1025135446Strhodes	ISC_LIST_APPEND(name->list, rds, link);
1026135446Strhodes
1027135446Strhodes	*target = name;
1028135446Strhodes	return (ISC_R_SUCCESS);
1029135446Strhodes
1030135446Strhodes failure:
1031135446Strhodes
1032143731Sdougb	if (rds != NULL) {
1033135446Strhodes		dns_rdataset_disassociate(rds);
1034135446Strhodes		dns_message_puttemprdataset(msg, &rds);
1035143731Sdougb	}
1036135446Strhodes	if (rdl != NULL) {
1037135446Strhodes		ISC_LIST_UNLINK(rdl->rdata, rdata, link);
1038135446Strhodes		dns_message_puttemprdatalist(msg, &rdl);
1039135446Strhodes	}
1040135446Strhodes	if (rdata != NULL)
1041135446Strhodes		dns_message_puttemprdata(msg, &rdata);
1042135446Strhodes
1043135446Strhodes	return (result);
1044135446Strhodes}
1045135446Strhodes
1046135446Strhodes
1047135446Strhodes/*
1048135446Strhodes * Build an *XFR request and send its length prefix.
1049135446Strhodes */
1050135446Strhodesstatic isc_result_t
1051135446Strhodesxfrin_send_request(dns_xfrin_ctx_t *xfr) {
1052135446Strhodes	isc_result_t result;
1053135446Strhodes	isc_region_t region;
1054135446Strhodes	dns_rdataset_t *qrdataset = NULL;
1055135446Strhodes	dns_message_t *msg = NULL;
1056135446Strhodes	dns_difftuple_t *soatuple = NULL;
1057135446Strhodes	dns_name_t *qname = NULL;
1058135446Strhodes	dns_dbversion_t *ver = NULL;
1059135446Strhodes	dns_name_t *msgsoaname = NULL;
1060135446Strhodes
1061135446Strhodes	/* Create the request message */
1062135446Strhodes	CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTRENDER, &msg));
1063135446Strhodes	CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
1064135446Strhodes
1065135446Strhodes	/* Create a name for the question section. */
1066135446Strhodes	CHECK(dns_message_gettempname(msg, &qname));
1067135446Strhodes	dns_name_init(qname, NULL);
1068135446Strhodes	dns_name_clone(&xfr->name, qname);
1069135446Strhodes
1070135446Strhodes	/* Formulate the question and attach it to the question name. */
1071135446Strhodes	CHECK(dns_message_gettemprdataset(msg, &qrdataset));
1072135446Strhodes	dns_rdataset_init(qrdataset);
1073135446Strhodes	dns_rdataset_makequestion(qrdataset, xfr->rdclass, xfr->reqtype);
1074135446Strhodes	ISC_LIST_APPEND(qname->list, qrdataset, link);
1075135446Strhodes	qrdataset = NULL;
1076135446Strhodes
1077135446Strhodes	dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
1078135446Strhodes	qname = NULL;
1079135446Strhodes
1080135446Strhodes	if (xfr->reqtype == dns_rdatatype_ixfr) {
1081135446Strhodes		/* Get the SOA and add it to the authority section. */
1082135446Strhodes		/* XXX is using the current version the right thing? */
1083135446Strhodes		dns_db_currentversion(xfr->db, &ver);
1084135446Strhodes		CHECK(dns_db_createsoatuple(xfr->db, ver, xfr->mctx,
1085135446Strhodes					    DNS_DIFFOP_EXISTS, &soatuple));
1086135446Strhodes		xfr->ixfr.request_serial = dns_soa_getserial(&soatuple->rdata);
1087135446Strhodes		xfr->ixfr.current_serial = xfr->ixfr.request_serial;
1088135446Strhodes		xfrin_log(xfr, ISC_LOG_DEBUG(3),
1089135446Strhodes			  "requesting IXFR for serial %u",
1090135446Strhodes			  xfr->ixfr.request_serial);
1091135446Strhodes
1092135446Strhodes		CHECK(tuple2msgname(soatuple, msg, &msgsoaname));
1093135446Strhodes		dns_message_addname(msg, msgsoaname, DNS_SECTION_AUTHORITY);
1094165071Sdougb	} else if (xfr->reqtype == dns_rdatatype_soa)
1095165071Sdougb		CHECK(dns_db_getsoaserial(xfr->db, NULL,
1096165071Sdougb					  &xfr->ixfr.request_serial));
1097135446Strhodes
1098135446Strhodes	xfr->checkid = ISC_TRUE;
1099135446Strhodes	xfr->id++;
1100174187Sdougb	xfr->nmsg = 0;
1101193149Sdougb	xfr->nrecs = 0;
1102193149Sdougb	xfr->nbytes = 0;
1103193149Sdougb	isc_time_now(&xfr->start);
1104135446Strhodes	msg->id = xfr->id;
1105186462Sdougb	if (xfr->tsigctx != NULL)
1106186462Sdougb		dst_context_destroy(&xfr->tsigctx);
1107135446Strhodes
1108135446Strhodes	CHECK(render(msg, xfr->mctx, &xfr->qbuffer));
1109135446Strhodes
1110135446Strhodes	/*
1111135446Strhodes	 * Free the last tsig, if there is one.
1112135446Strhodes	 */
1113135446Strhodes	if (xfr->lasttsig != NULL)
1114135446Strhodes		isc_buffer_free(&xfr->lasttsig);
1115135446Strhodes
1116135446Strhodes	/*
1117135446Strhodes	 * Save the query TSIG and don't let message_destroy free it.
1118135446Strhodes	 */
1119135446Strhodes	CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig));
1120135446Strhodes
1121135446Strhodes	isc_buffer_usedregion(&xfr->qbuffer, &region);
1122135446Strhodes	INSIST(region.length <= 65535);
1123135446Strhodes
1124262706Serwin	/*
1125262706Serwin	 * Record message length and adjust region to include TCP
1126262706Serwin	 * length field.
1127262706Serwin	 */
1128262706Serwin	xfr->qbuffer_data[0] = (region.length >> 8) & 0xff;
1129262706Serwin	xfr->qbuffer_data[1] = region.length & 0xff;
1130262706Serwin	region.base -= 2;
1131262706Serwin	region.length += 2;
1132262706Serwin	CHECK(isc_socket_send(xfr->socket, &region, xfr->task,
1133262706Serwin			      xfrin_send_done, xfr));
1134135446Strhodes	xfr->sends++;
1135135446Strhodes
1136135446Strhodes failure:
1137135446Strhodes	if (qname != NULL)
1138135446Strhodes		dns_message_puttempname(msg, &qname);
1139135446Strhodes	if (qrdataset != NULL)
1140135446Strhodes		dns_message_puttemprdataset(msg, &qrdataset);
1141135446Strhodes	if (msg != NULL)
1142135446Strhodes		dns_message_destroy(&msg);
1143135446Strhodes	if (soatuple != NULL)
1144135446Strhodes		dns_difftuple_free(&soatuple);
1145135446Strhodes	if (ver != NULL)
1146135446Strhodes		dns_db_closeversion(xfr->db, &ver, ISC_FALSE);
1147135446Strhodes	return (result);
1148135446Strhodes}
1149135446Strhodes
1150135446Strhodesstatic void
1151135446Strhodesxfrin_send_done(isc_task_t *task, isc_event_t *event) {
1152135446Strhodes	isc_socketevent_t *sev = (isc_socketevent_t *) event;
1153135446Strhodes	dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
1154135446Strhodes	isc_result_t result;
1155135446Strhodes
1156135446Strhodes	REQUIRE(VALID_XFRIN(xfr));
1157135446Strhodes
1158135446Strhodes	UNUSED(task);
1159135446Strhodes
1160135446Strhodes	INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
1161135446Strhodes
1162135446Strhodes	xfr->sends--;
1163135446Strhodes	xfrin_log(xfr, ISC_LOG_DEBUG(3), "sent request data");
1164135446Strhodes	CHECK(sev->result);
1165135446Strhodes
1166135446Strhodes	CHECK(dns_tcpmsg_readmessage(&xfr->tcpmsg, xfr->task,
1167135446Strhodes				     xfrin_recv_done, xfr));
1168135446Strhodes	xfr->recvs++;
1169135446Strhodes failure:
1170135446Strhodes	isc_event_free(&event);
1171135446Strhodes	if (result != ISC_R_SUCCESS)
1172135446Strhodes		xfrin_fail(xfr, result, "failed sending request data");
1173135446Strhodes}
1174135446Strhodes
1175135446Strhodes
1176135446Strhodesstatic void
1177135446Strhodesxfrin_recv_done(isc_task_t *task, isc_event_t *ev) {
1178135446Strhodes	dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) ev->ev_arg;
1179135446Strhodes	isc_result_t result;
1180135446Strhodes	dns_message_t *msg = NULL;
1181135446Strhodes	dns_name_t *name;
1182135446Strhodes	dns_tcpmsg_t *tcpmsg;
1183135446Strhodes	dns_name_t *tsigowner = NULL;
1184135446Strhodes
1185135446Strhodes	REQUIRE(VALID_XFRIN(xfr));
1186135446Strhodes
1187135446Strhodes	UNUSED(task);
1188135446Strhodes
1189135446Strhodes	INSIST(ev->ev_type == DNS_EVENT_TCPMSG);
1190135446Strhodes	tcpmsg = ev->ev_sender;
1191135446Strhodes	isc_event_free(&ev);
1192135446Strhodes
1193135446Strhodes	xfr->recvs--;
1194135446Strhodes	if (xfr->shuttingdown) {
1195135446Strhodes		maybe_free(xfr);
1196135446Strhodes		return;
1197135446Strhodes	}
1198135446Strhodes
1199135446Strhodes	CHECK(tcpmsg->result);
1200135446Strhodes
1201135446Strhodes	xfrin_log(xfr, ISC_LOG_DEBUG(7), "received %u bytes",
1202135446Strhodes		  tcpmsg->buffer.used);
1203135446Strhodes
1204135446Strhodes	CHECK(isc_timer_touch(xfr->timer));
1205135446Strhodes
1206135446Strhodes	CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTPARSE, &msg));
1207135446Strhodes
1208135446Strhodes	CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
1209135446Strhodes	CHECK(dns_message_setquerytsig(msg, xfr->lasttsig));
1210186462Sdougb
1211135446Strhodes	msg->tsigctx = xfr->tsigctx;
1212186462Sdougb	xfr->tsigctx = NULL;
1213186462Sdougb
1214292321Sdelphij	dns_message_setclass(msg, xfr->rdclass);
1215292321Sdelphij
1216135446Strhodes	if (xfr->nmsg > 0)
1217135446Strhodes		msg->tcp_continuation = 1;
1218135446Strhodes
1219135446Strhodes	result = dns_message_parse(msg, &tcpmsg->buffer,
1220135446Strhodes				   DNS_MESSAGEPARSE_PRESERVEORDER);
1221135446Strhodes
1222135446Strhodes	if (result != ISC_R_SUCCESS || msg->rcode != dns_rcode_noerror ||
1223135446Strhodes	    (xfr->checkid && msg->id != xfr->id)) {
1224135446Strhodes		if (result == ISC_R_SUCCESS)
1225135446Strhodes			result = ISC_RESULTCLASS_DNSRCODE + msg->rcode; /*XXX*/
1226135446Strhodes		if (result == ISC_R_SUCCESS || result == DNS_R_NOERROR)
1227135446Strhodes			result = DNS_R_UNEXPECTEDID;
1228135446Strhodes		if (xfr->reqtype == dns_rdatatype_axfr ||
1229135446Strhodes		    xfr->reqtype == dns_rdatatype_soa)
1230225361Sdougb			goto failure;
1231135446Strhodes		xfrin_log(xfr, ISC_LOG_DEBUG(3), "got %s, retrying with AXFR",
1232135446Strhodes		       isc_result_totext(result));
1233135446Strhodes try_axfr:
1234135446Strhodes		dns_message_destroy(&msg);
1235135446Strhodes		xfrin_reset(xfr);
1236165071Sdougb		xfr->reqtype = dns_rdatatype_soa;
1237165071Sdougb		xfr->state = XFRST_SOAQUERY;
1238135446Strhodes		(void)xfrin_start(xfr);
1239135446Strhodes		return;
1240135446Strhodes	}
1241135446Strhodes
1242135446Strhodes	/*
1243135446Strhodes	 * Does the server know about IXFR?  If it doesn't we will get
1244135446Strhodes	 * a message with a empty answer section or a potentially a CNAME /
1245135446Strhodes	 * DNAME, the later is handled by xfr_rr() which will return FORMERR
1246135446Strhodes	 * if the first RR in the answer section is not a SOA record.
1247135446Strhodes	 */
1248135446Strhodes	if (xfr->reqtype == dns_rdatatype_ixfr &&
1249135446Strhodes	    xfr->state == XFRST_INITIALSOA &&
1250135446Strhodes	    msg->counts[DNS_SECTION_ANSWER] == 0) {
1251135446Strhodes		xfrin_log(xfr, ISC_LOG_DEBUG(3),
1252135446Strhodes			  "empty answer section, retrying with AXFR");
1253135446Strhodes		goto try_axfr;
1254135446Strhodes	}
1255135446Strhodes
1256135446Strhodes	if (xfr->reqtype == dns_rdatatype_soa &&
1257135446Strhodes	    (msg->flags & DNS_MESSAGEFLAG_AA) == 0) {
1258135446Strhodes		FAIL(DNS_R_NOTAUTHORITATIVE);
1259135446Strhodes	}
1260135446Strhodes
1261135446Strhodes
1262135446Strhodes	result = dns_message_checksig(msg, dns_zone_getview(xfr->zone));
1263135446Strhodes	if (result != ISC_R_SUCCESS) {
1264135446Strhodes		xfrin_log(xfr, ISC_LOG_DEBUG(3), "TSIG check failed: %s",
1265135446Strhodes		       isc_result_totext(result));
1266225361Sdougb		goto failure;
1267135446Strhodes	}
1268135446Strhodes
1269135446Strhodes	for (result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
1270135446Strhodes	     result == ISC_R_SUCCESS;
1271135446Strhodes	     result = dns_message_nextname(msg, DNS_SECTION_ANSWER))
1272135446Strhodes	{
1273135446Strhodes		dns_rdataset_t *rds;
1274135446Strhodes
1275135446Strhodes		name = NULL;
1276135446Strhodes		dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
1277135446Strhodes		for (rds = ISC_LIST_HEAD(name->list);
1278135446Strhodes		     rds != NULL;
1279135446Strhodes		     rds = ISC_LIST_NEXT(rds, link))
1280135446Strhodes		{
1281135446Strhodes			for (result = dns_rdataset_first(rds);
1282135446Strhodes			     result == ISC_R_SUCCESS;
1283135446Strhodes			     result = dns_rdataset_next(rds))
1284135446Strhodes			{
1285135446Strhodes				dns_rdata_t rdata = DNS_RDATA_INIT;
1286135446Strhodes				dns_rdataset_current(rds, &rdata);
1287135446Strhodes				CHECK(xfr_rr(xfr, name, rds->ttl, &rdata));
1288135446Strhodes			}
1289135446Strhodes		}
1290135446Strhodes	}
1291135446Strhodes	if (result != ISC_R_NOMORE)
1292135446Strhodes		goto failure;
1293135446Strhodes
1294135446Strhodes	if (dns_message_gettsig(msg, &tsigowner) != NULL) {
1295135446Strhodes		/*
1296135446Strhodes		 * Reset the counter.
1297135446Strhodes		 */
1298135446Strhodes		xfr->sincetsig = 0;
1299135446Strhodes
1300135446Strhodes		/*
1301135446Strhodes		 * Free the last tsig, if there is one.
1302135446Strhodes		 */
1303135446Strhodes		if (xfr->lasttsig != NULL)
1304135446Strhodes			isc_buffer_free(&xfr->lasttsig);
1305135446Strhodes
1306135446Strhodes		/*
1307135446Strhodes		 * Update the last tsig pointer.
1308135446Strhodes		 */
1309135446Strhodes		CHECK(dns_message_getquerytsig(msg, xfr->mctx,
1310135446Strhodes					       &xfr->lasttsig));
1311135446Strhodes
1312135446Strhodes	} else if (dns_message_gettsigkey(msg) != NULL) {
1313135446Strhodes		xfr->sincetsig++;
1314224092Sdougb		if (xfr->sincetsig > 100 || xfr->nmsg == 0 ||
1315224092Sdougb		    xfr->state == XFRST_AXFR_END ||
1316224092Sdougb		    xfr->state == XFRST_IXFR_END)
1317135446Strhodes		{
1318135446Strhodes			result = DNS_R_EXPECTEDTSIG;
1319135446Strhodes			goto failure;
1320135446Strhodes		}
1321135446Strhodes	}
1322135446Strhodes
1323135446Strhodes	/*
1324135446Strhodes	 * Update the number of messages received.
1325135446Strhodes	 */
1326135446Strhodes	xfr->nmsg++;
1327135446Strhodes
1328135446Strhodes	/*
1329193149Sdougb	 * Update the number of bytes received.
1330193149Sdougb	 */
1331193149Sdougb	xfr->nbytes += tcpmsg->buffer.used;
1332193149Sdougb
1333193149Sdougb	/*
1334186462Sdougb	 * Take the context back.
1335135446Strhodes	 */
1336186462Sdougb	INSIST(xfr->tsigctx == NULL);
1337135446Strhodes	xfr->tsigctx = msg->tsigctx;
1338186462Sdougb	msg->tsigctx = NULL;
1339135446Strhodes
1340135446Strhodes	dns_message_destroy(&msg);
1341135446Strhodes
1342224092Sdougb	switch (xfr->state) {
1343224092Sdougb	case XFRST_GOTSOA:
1344165071Sdougb		xfr->reqtype = dns_rdatatype_axfr;
1345165071Sdougb		xfr->state = XFRST_INITIALSOA;
1346165071Sdougb		CHECK(xfrin_send_request(xfr));
1347224092Sdougb		break;
1348224092Sdougb	case XFRST_AXFR_END:
1349224092Sdougb		CHECK(axfr_finalize(xfr));
1350224092Sdougb		/* FALLTHROUGH */
1351224092Sdougb	case XFRST_IXFR_END:
1352135446Strhodes		/*
1353174187Sdougb		 * Close the journal.
1354174187Sdougb		 */
1355174187Sdougb		if (xfr->ixfr.journal != NULL)
1356174187Sdougb			dns_journal_destroy(&xfr->ixfr.journal);
1357224092Sdougb
1358174187Sdougb		/*
1359135446Strhodes		 * Inform the caller we succeeded.
1360135446Strhodes		 */
1361135446Strhodes		if (xfr->done != NULL) {
1362135446Strhodes			(xfr->done)(xfr->zone, ISC_R_SUCCESS);
1363135446Strhodes			xfr->done = NULL;
1364135446Strhodes		}
1365135446Strhodes		/*
1366135446Strhodes		 * We should have no outstanding events at this
1367135446Strhodes		 * point, thus maybe_free() should succeed.
1368135446Strhodes		 */
1369135446Strhodes		xfr->shuttingdown = ISC_TRUE;
1370135446Strhodes		maybe_free(xfr);
1371224092Sdougb		break;
1372224092Sdougb	default:
1373135446Strhodes		/*
1374135446Strhodes		 * Read the next message.
1375135446Strhodes		 */
1376135446Strhodes		CHECK(dns_tcpmsg_readmessage(&xfr->tcpmsg, xfr->task,
1377135446Strhodes					     xfrin_recv_done, xfr));
1378135446Strhodes		xfr->recvs++;
1379135446Strhodes	}
1380135446Strhodes	return;
1381135446Strhodes
1382135446Strhodes failure:
1383135446Strhodes	if (msg != NULL)
1384135446Strhodes		dns_message_destroy(&msg);
1385135446Strhodes	if (result != ISC_R_SUCCESS)
1386135446Strhodes		xfrin_fail(xfr, result, "failed while receiving responses");
1387135446Strhodes}
1388135446Strhodes
1389135446Strhodesstatic void
1390135446Strhodesxfrin_timeout(isc_task_t *task, isc_event_t *event) {
1391135446Strhodes	dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
1392135446Strhodes
1393135446Strhodes	REQUIRE(VALID_XFRIN(xfr));
1394135446Strhodes
1395135446Strhodes	UNUSED(task);
1396135446Strhodes
1397135446Strhodes	isc_event_free(&event);
1398135446Strhodes	/*
1399135446Strhodes	 * This will log "giving up: timeout".
1400135446Strhodes	 */
1401135446Strhodes	xfrin_fail(xfr, ISC_R_TIMEDOUT, "giving up");
1402135446Strhodes}
1403135446Strhodes
1404135446Strhodesstatic void
1405135446Strhodesmaybe_free(dns_xfrin_ctx_t *xfr) {
1406193149Sdougb	isc_uint64_t msecs;
1407193149Sdougb	isc_uint64_t persec;
1408193149Sdougb
1409135446Strhodes	REQUIRE(VALID_XFRIN(xfr));
1410135446Strhodes
1411135446Strhodes	if (! xfr->shuttingdown || xfr->refcount != 0 ||
1412135446Strhodes	    xfr->connects != 0 || xfr->sends != 0 ||
1413135446Strhodes	    xfr->recvs != 0)
1414135446Strhodes		return;
1415135446Strhodes
1416193149Sdougb	/*
1417193149Sdougb	 * Calculate the length of time the transfer took,
1418193149Sdougb	 * and print a log message with the bytes and rate.
1419193149Sdougb	 */
1420193149Sdougb	isc_time_now(&xfr->end);
1421193149Sdougb	msecs = isc_time_microdiff(&xfr->end, &xfr->start) / 1000;
1422193149Sdougb	if (msecs == 0)
1423193149Sdougb		msecs = 1;
1424193149Sdougb	persec = (xfr->nbytes * 1000) / msecs;
1425193149Sdougb	xfrin_log(xfr, ISC_LOG_INFO,
1426193149Sdougb		  "Transfer completed: %d messages, %d records, "
1427193149Sdougb		  "%" ISC_PRINT_QUADFORMAT "u bytes, "
1428193149Sdougb		  "%u.%03u secs (%u bytes/sec)",
1429193149Sdougb		  xfr->nmsg, xfr->nrecs, xfr->nbytes,
1430193149Sdougb		  (unsigned int) (msecs / 1000), (unsigned int) (msecs % 1000),
1431193149Sdougb		  (unsigned int) persec);
1432135446Strhodes
1433135446Strhodes	if (xfr->socket != NULL)
1434135446Strhodes		isc_socket_detach(&xfr->socket);
1435135446Strhodes
1436135446Strhodes	if (xfr->timer != NULL)
1437135446Strhodes		isc_timer_detach(&xfr->timer);
1438135446Strhodes
1439135446Strhodes	if (xfr->task != NULL)
1440135446Strhodes		isc_task_detach(&xfr->task);
1441135446Strhodes
1442135446Strhodes	if (xfr->tsigkey != NULL)
1443135446Strhodes		dns_tsigkey_detach(&xfr->tsigkey);
1444135446Strhodes
1445135446Strhodes	if (xfr->lasttsig != NULL)
1446135446Strhodes		isc_buffer_free(&xfr->lasttsig);
1447135446Strhodes
1448135446Strhodes	dns_diff_clear(&xfr->diff);
1449135446Strhodes
1450135446Strhodes	if (xfr->ixfr.journal != NULL)
1451135446Strhodes		dns_journal_destroy(&xfr->ixfr.journal);
1452135446Strhodes
1453135446Strhodes	if (xfr->axfr.add_private != NULL)
1454135446Strhodes		(void)dns_db_endload(xfr->db, &xfr->axfr.add_private);
1455135446Strhodes
1456135446Strhodes	if (xfr->tcpmsg_valid)
1457135446Strhodes		dns_tcpmsg_invalidate(&xfr->tcpmsg);
1458135446Strhodes
1459186462Sdougb	if (xfr->tsigctx != NULL)
1460186462Sdougb		dst_context_destroy(&xfr->tsigctx);
1461186462Sdougb
1462135446Strhodes	if ((xfr->name.attributes & DNS_NAMEATTR_DYNAMIC) != 0)
1463135446Strhodes		dns_name_free(&xfr->name, xfr->mctx);
1464135446Strhodes
1465135446Strhodes	if (xfr->ver != NULL)
1466135446Strhodes		dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
1467135446Strhodes
1468135446Strhodes	if (xfr->db != NULL)
1469135446Strhodes		dns_db_detach(&xfr->db);
1470135446Strhodes
1471135446Strhodes	if (xfr->zone != NULL)
1472135446Strhodes		dns_zone_idetach(&xfr->zone);
1473135446Strhodes
1474254402Serwin	isc_mem_putanddetach(&xfr->mctx, xfr, sizeof(*xfr));
1475135446Strhodes}
1476135446Strhodes
1477135446Strhodes/*
1478135446Strhodes * Log incoming zone transfer messages in a format like
1479135446Strhodes * transfer of <zone> from <address>: <message>
1480135446Strhodes */
1481135446Strhodesstatic void
1482170222Sdougbxfrin_logv(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
1483170222Sdougb	   const char *fmt, va_list ap)
1484135446Strhodes{
1485135446Strhodes	char mastertext[ISC_SOCKADDR_FORMATSIZE];
1486135446Strhodes	char msgtext[2048];
1487135446Strhodes
1488135446Strhodes	isc_sockaddr_format(masteraddr, mastertext, sizeof(mastertext));
1489135446Strhodes	vsnprintf(msgtext, sizeof(msgtext), fmt, ap);
1490135446Strhodes
1491135446Strhodes	isc_log_write(dns_lctx, DNS_LOGCATEGORY_XFER_IN,
1492135446Strhodes		      DNS_LOGMODULE_XFER_IN, level,
1493170222Sdougb		      "transfer of '%s' from %s: %s",
1494170222Sdougb		      zonetext, mastertext, msgtext);
1495135446Strhodes}
1496135446Strhodes
1497135446Strhodes/*
1498135446Strhodes * Logging function for use when a xfrin_ctx_t has not yet been created.
1499135446Strhodes */
1500135446Strhodes
1501135446Strhodesstatic void
1502170222Sdougbxfrin_log1(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
1503186462Sdougb	   const char *fmt, ...)
1504135446Strhodes{
1505135446Strhodes	va_list ap;
1506135446Strhodes
1507135446Strhodes	if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
1508135446Strhodes		return;
1509135446Strhodes
1510135446Strhodes	va_start(ap, fmt);
1511170222Sdougb	xfrin_logv(level, zonetext, masteraddr, fmt, ap);
1512135446Strhodes	va_end(ap);
1513135446Strhodes}
1514135446Strhodes
1515135446Strhodes/*
1516135446Strhodes * Logging function for use when there is a xfrin_ctx_t.
1517135446Strhodes */
1518135446Strhodes
1519135446Strhodesstatic void
1520153816Sdougbxfrin_log(dns_xfrin_ctx_t *xfr, int level, const char *fmt, ...)
1521135446Strhodes{
1522135446Strhodes	va_list ap;
1523170222Sdougb	char zonetext[DNS_NAME_MAXTEXT+32];
1524135446Strhodes
1525135446Strhodes	if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
1526135446Strhodes		return;
1527135446Strhodes
1528170222Sdougb	dns_zone_name(xfr->zone, zonetext, sizeof(zonetext));
1529170222Sdougb
1530135446Strhodes	va_start(ap, fmt);
1531170222Sdougb	xfrin_logv(level, zonetext, &xfr->masteraddr, fmt, ap);
1532135446Strhodes	va_end(ap);
1533135446Strhodes}
1534