1*00b67f09SDavid van Moolenbroek /* $NetBSD: xfrout.c,v 1.9 2015/07/08 17:28:55 christos Exp $ */
2*00b67f09SDavid van Moolenbroek
3*00b67f09SDavid van Moolenbroek /*
4*00b67f09SDavid van Moolenbroek * Copyright (C) 2004-2015 Internet Systems Consortium, Inc. ("ISC")
5*00b67f09SDavid van Moolenbroek * Copyright (C) 1999-2003 Internet Software Consortium.
6*00b67f09SDavid van Moolenbroek *
7*00b67f09SDavid van Moolenbroek * Permission to use, copy, modify, and/or distribute this software for any
8*00b67f09SDavid van Moolenbroek * purpose with or without fee is hereby granted, provided that the above
9*00b67f09SDavid van Moolenbroek * copyright notice and this permission notice appear in all copies.
10*00b67f09SDavid van Moolenbroek *
11*00b67f09SDavid van Moolenbroek * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12*00b67f09SDavid van Moolenbroek * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13*00b67f09SDavid van Moolenbroek * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14*00b67f09SDavid van Moolenbroek * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15*00b67f09SDavid van Moolenbroek * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16*00b67f09SDavid van Moolenbroek * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*00b67f09SDavid van Moolenbroek * PERFORMANCE OF THIS SOFTWARE.
18*00b67f09SDavid van Moolenbroek */
19*00b67f09SDavid van Moolenbroek
20*00b67f09SDavid van Moolenbroek /* Id */
21*00b67f09SDavid van Moolenbroek
22*00b67f09SDavid van Moolenbroek #include <config.h>
23*00b67f09SDavid van Moolenbroek
24*00b67f09SDavid van Moolenbroek #include <isc/formatcheck.h>
25*00b67f09SDavid van Moolenbroek #include <isc/mem.h>
26*00b67f09SDavid van Moolenbroek #include <isc/timer.h>
27*00b67f09SDavid van Moolenbroek #include <isc/print.h>
28*00b67f09SDavid van Moolenbroek #include <isc/stats.h>
29*00b67f09SDavid van Moolenbroek #include <isc/util.h>
30*00b67f09SDavid van Moolenbroek
31*00b67f09SDavid van Moolenbroek #include <dns/db.h>
32*00b67f09SDavid van Moolenbroek #include <dns/dbiterator.h>
33*00b67f09SDavid van Moolenbroek #include <dns/dlz.h>
34*00b67f09SDavid van Moolenbroek #include <dns/fixedname.h>
35*00b67f09SDavid van Moolenbroek #include <dns/journal.h>
36*00b67f09SDavid van Moolenbroek #include <dns/message.h>
37*00b67f09SDavid van Moolenbroek #include <dns/peer.h>
38*00b67f09SDavid van Moolenbroek #include <dns/rdataclass.h>
39*00b67f09SDavid van Moolenbroek #include <dns/rdatalist.h>
40*00b67f09SDavid van Moolenbroek #include <dns/rdataset.h>
41*00b67f09SDavid van Moolenbroek #include <dns/rdatasetiter.h>
42*00b67f09SDavid van Moolenbroek #include <dns/result.h>
43*00b67f09SDavid van Moolenbroek #include <dns/rriterator.h>
44*00b67f09SDavid van Moolenbroek #include <dns/soa.h>
45*00b67f09SDavid van Moolenbroek #include <dns/stats.h>
46*00b67f09SDavid van Moolenbroek #include <dns/timer.h>
47*00b67f09SDavid van Moolenbroek #include <dns/tsig.h>
48*00b67f09SDavid van Moolenbroek #include <dns/view.h>
49*00b67f09SDavid van Moolenbroek #include <dns/zone.h>
50*00b67f09SDavid van Moolenbroek #include <dns/zt.h>
51*00b67f09SDavid van Moolenbroek
52*00b67f09SDavid van Moolenbroek #include <named/client.h>
53*00b67f09SDavid van Moolenbroek #include <named/log.h>
54*00b67f09SDavid van Moolenbroek #include <named/server.h>
55*00b67f09SDavid van Moolenbroek #include <named/xfrout.h>
56*00b67f09SDavid van Moolenbroek
57*00b67f09SDavid van Moolenbroek #include "pfilter.h"
58*00b67f09SDavid van Moolenbroek
59*00b67f09SDavid van Moolenbroek /*! \file
60*00b67f09SDavid van Moolenbroek * \brief
61*00b67f09SDavid van Moolenbroek * Outgoing AXFR and IXFR.
62*00b67f09SDavid van Moolenbroek */
63*00b67f09SDavid van Moolenbroek
64*00b67f09SDavid van Moolenbroek /*
65*00b67f09SDavid van Moolenbroek * TODO:
66*00b67f09SDavid van Moolenbroek * - IXFR over UDP
67*00b67f09SDavid van Moolenbroek */
68*00b67f09SDavid van Moolenbroek
69*00b67f09SDavid van Moolenbroek #define XFROUT_COMMON_LOGARGS \
70*00b67f09SDavid van Moolenbroek ns_g_lctx, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT
71*00b67f09SDavid van Moolenbroek
72*00b67f09SDavid van Moolenbroek #define XFROUT_PROTOCOL_LOGARGS \
73*00b67f09SDavid van Moolenbroek XFROUT_COMMON_LOGARGS, ISC_LOG_INFO
74*00b67f09SDavid van Moolenbroek
75*00b67f09SDavid van Moolenbroek #define XFROUT_DEBUG_LOGARGS(n) \
76*00b67f09SDavid van Moolenbroek XFROUT_COMMON_LOGARGS, ISC_LOG_DEBUG(n)
77*00b67f09SDavid van Moolenbroek
78*00b67f09SDavid van Moolenbroek #define XFROUT_RR_LOGARGS \
79*00b67f09SDavid van Moolenbroek XFROUT_COMMON_LOGARGS, XFROUT_RR_LOGLEVEL
80*00b67f09SDavid van Moolenbroek
81*00b67f09SDavid van Moolenbroek #define XFROUT_RR_LOGLEVEL ISC_LOG_DEBUG(8)
82*00b67f09SDavid van Moolenbroek
83*00b67f09SDavid van Moolenbroek /*%
84*00b67f09SDavid van Moolenbroek * Fail unconditionally and log as a client error.
85*00b67f09SDavid van Moolenbroek * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
86*00b67f09SDavid van Moolenbroek * from complaining about "end-of-loop code not reached".
87*00b67f09SDavid van Moolenbroek */
88*00b67f09SDavid van Moolenbroek #define FAILC(code, msg) \
89*00b67f09SDavid van Moolenbroek do { \
90*00b67f09SDavid van Moolenbroek result = (code); \
91*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
92*00b67f09SDavid van Moolenbroek NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
93*00b67f09SDavid van Moolenbroek "bad zone transfer request: %s (%s)", \
94*00b67f09SDavid van Moolenbroek msg, isc_result_totext(code)); \
95*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) goto failure; \
96*00b67f09SDavid van Moolenbroek } while (/*CONSTCOND*/0)
97*00b67f09SDavid van Moolenbroek
98*00b67f09SDavid van Moolenbroek #define FAILQ(code, msg, question, rdclass) \
99*00b67f09SDavid van Moolenbroek do { \
100*00b67f09SDavid van Moolenbroek char _buf1[DNS_NAME_FORMATSIZE]; \
101*00b67f09SDavid van Moolenbroek char _buf2[DNS_RDATACLASS_FORMATSIZE]; \
102*00b67f09SDavid van Moolenbroek result = (code); \
103*00b67f09SDavid van Moolenbroek dns_name_format(question, _buf1, sizeof(_buf1)); \
104*00b67f09SDavid van Moolenbroek dns_rdataclass_format(rdclass, _buf2, sizeof(_buf2)); \
105*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
106*00b67f09SDavid van Moolenbroek NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
107*00b67f09SDavid van Moolenbroek "bad zone transfer request: '%s/%s': %s (%s)", \
108*00b67f09SDavid van Moolenbroek _buf1, _buf2, msg, isc_result_totext(code)); \
109*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) goto failure; \
110*00b67f09SDavid van Moolenbroek } while (/*CONSTCOND*/0)
111*00b67f09SDavid van Moolenbroek
112*00b67f09SDavid van Moolenbroek #define CHECK(op) \
113*00b67f09SDavid van Moolenbroek do { result = (op); \
114*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) goto failure; \
115*00b67f09SDavid van Moolenbroek } while (/*CONSTCOND*/0)
116*00b67f09SDavid van Moolenbroek
117*00b67f09SDavid van Moolenbroek /**************************************************************************/
118*00b67f09SDavid van Moolenbroek
119*00b67f09SDavid van Moolenbroek static inline void
inc_stats(dns_zone_t * zone,isc_statscounter_t counter)120*00b67f09SDavid van Moolenbroek inc_stats(dns_zone_t *zone, isc_statscounter_t counter) {
121*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats, counter);
122*00b67f09SDavid van Moolenbroek if (zone != NULL) {
123*00b67f09SDavid van Moolenbroek isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
124*00b67f09SDavid van Moolenbroek if (zonestats != NULL)
125*00b67f09SDavid van Moolenbroek isc_stats_increment(zonestats, counter);
126*00b67f09SDavid van Moolenbroek }
127*00b67f09SDavid van Moolenbroek }
128*00b67f09SDavid van Moolenbroek
129*00b67f09SDavid van Moolenbroek /**************************************************************************/
130*00b67f09SDavid van Moolenbroek
131*00b67f09SDavid van Moolenbroek /*% Log an RR (for debugging) */
132*00b67f09SDavid van Moolenbroek
133*00b67f09SDavid van Moolenbroek static void
log_rr(dns_name_t * name,dns_rdata_t * rdata,isc_uint32_t ttl)134*00b67f09SDavid van Moolenbroek log_rr(dns_name_t *name, dns_rdata_t *rdata, isc_uint32_t ttl) {
135*00b67f09SDavid van Moolenbroek isc_result_t result;
136*00b67f09SDavid van Moolenbroek isc_buffer_t buf;
137*00b67f09SDavid van Moolenbroek char mem[2000];
138*00b67f09SDavid van Moolenbroek dns_rdatalist_t rdl;
139*00b67f09SDavid van Moolenbroek dns_rdataset_t rds;
140*00b67f09SDavid van Moolenbroek dns_rdata_t rd = DNS_RDATA_INIT;
141*00b67f09SDavid van Moolenbroek
142*00b67f09SDavid van Moolenbroek rdl.type = rdata->type;
143*00b67f09SDavid van Moolenbroek rdl.rdclass = rdata->rdclass;
144*00b67f09SDavid van Moolenbroek rdl.ttl = ttl;
145*00b67f09SDavid van Moolenbroek if (rdata->type == dns_rdatatype_sig ||
146*00b67f09SDavid van Moolenbroek rdata->type == dns_rdatatype_rrsig)
147*00b67f09SDavid van Moolenbroek rdl.covers = dns_rdata_covers(rdata);
148*00b67f09SDavid van Moolenbroek else
149*00b67f09SDavid van Moolenbroek rdl.covers = dns_rdatatype_none;
150*00b67f09SDavid van Moolenbroek ISC_LIST_INIT(rdl.rdata);
151*00b67f09SDavid van Moolenbroek ISC_LINK_INIT(&rdl, link);
152*00b67f09SDavid van Moolenbroek dns_rdataset_init(&rds);
153*00b67f09SDavid van Moolenbroek dns_rdata_init(&rd);
154*00b67f09SDavid van Moolenbroek dns_rdata_clone(rdata, &rd);
155*00b67f09SDavid van Moolenbroek ISC_LIST_APPEND(rdl.rdata, &rd, link);
156*00b67f09SDavid van Moolenbroek RUNTIME_CHECK(dns_rdatalist_tordataset(&rdl, &rds) == ISC_R_SUCCESS);
157*00b67f09SDavid van Moolenbroek
158*00b67f09SDavid van Moolenbroek isc_buffer_init(&buf, mem, sizeof(mem));
159*00b67f09SDavid van Moolenbroek result = dns_rdataset_totext(&rds, name,
160*00b67f09SDavid van Moolenbroek ISC_FALSE, ISC_FALSE, &buf);
161*00b67f09SDavid van Moolenbroek
162*00b67f09SDavid van Moolenbroek /*
163*00b67f09SDavid van Moolenbroek * We could use xfrout_log(), but that would produce
164*00b67f09SDavid van Moolenbroek * very long lines with a repetitive prefix.
165*00b67f09SDavid van Moolenbroek */
166*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS) {
167*00b67f09SDavid van Moolenbroek /*
168*00b67f09SDavid van Moolenbroek * Get rid of final newline.
169*00b67f09SDavid van Moolenbroek */
170*00b67f09SDavid van Moolenbroek INSIST(buf.used >= 1 &&
171*00b67f09SDavid van Moolenbroek ((char *) buf.base)[buf.used - 1] == '\n');
172*00b67f09SDavid van Moolenbroek buf.used--;
173*00b67f09SDavid van Moolenbroek
174*00b67f09SDavid van Moolenbroek isc_log_write(XFROUT_RR_LOGARGS, "%.*s",
175*00b67f09SDavid van Moolenbroek (int)isc_buffer_usedlength(&buf),
176*00b67f09SDavid van Moolenbroek (char *)isc_buffer_base(&buf));
177*00b67f09SDavid van Moolenbroek } else {
178*00b67f09SDavid van Moolenbroek isc_log_write(XFROUT_RR_LOGARGS, "<RR too large to print>");
179*00b67f09SDavid van Moolenbroek }
180*00b67f09SDavid van Moolenbroek }
181*00b67f09SDavid van Moolenbroek
182*00b67f09SDavid van Moolenbroek /**************************************************************************/
183*00b67f09SDavid van Moolenbroek /*
184*00b67f09SDavid van Moolenbroek * An 'rrstream_t' is a polymorphic iterator that returns
185*00b67f09SDavid van Moolenbroek * a stream of resource records. There are multiple implementations,
186*00b67f09SDavid van Moolenbroek * e.g. for generating AXFR and IXFR records streams.
187*00b67f09SDavid van Moolenbroek */
188*00b67f09SDavid van Moolenbroek
189*00b67f09SDavid van Moolenbroek typedef struct rrstream_methods rrstream_methods_t;
190*00b67f09SDavid van Moolenbroek
191*00b67f09SDavid van Moolenbroek typedef struct rrstream {
192*00b67f09SDavid van Moolenbroek isc_mem_t *mctx;
193*00b67f09SDavid van Moolenbroek rrstream_methods_t *methods;
194*00b67f09SDavid van Moolenbroek } rrstream_t;
195*00b67f09SDavid van Moolenbroek
196*00b67f09SDavid van Moolenbroek struct rrstream_methods {
197*00b67f09SDavid van Moolenbroek isc_result_t (*first)(rrstream_t *);
198*00b67f09SDavid van Moolenbroek isc_result_t (*next)(rrstream_t *);
199*00b67f09SDavid van Moolenbroek void (*current)(rrstream_t *,
200*00b67f09SDavid van Moolenbroek dns_name_t **,
201*00b67f09SDavid van Moolenbroek isc_uint32_t *,
202*00b67f09SDavid van Moolenbroek dns_rdata_t **);
203*00b67f09SDavid van Moolenbroek void (*pause)(rrstream_t *);
204*00b67f09SDavid van Moolenbroek void (*destroy)(rrstream_t **);
205*00b67f09SDavid van Moolenbroek };
206*00b67f09SDavid van Moolenbroek
207*00b67f09SDavid van Moolenbroek static void
rrstream_noop_pause(rrstream_t * rs)208*00b67f09SDavid van Moolenbroek rrstream_noop_pause(rrstream_t *rs) {
209*00b67f09SDavid van Moolenbroek UNUSED(rs);
210*00b67f09SDavid van Moolenbroek }
211*00b67f09SDavid van Moolenbroek
212*00b67f09SDavid van Moolenbroek /**************************************************************************/
213*00b67f09SDavid van Moolenbroek /*
214*00b67f09SDavid van Moolenbroek * An 'ixfr_rrstream_t' is an 'rrstream_t' that returns
215*00b67f09SDavid van Moolenbroek * an IXFR-like RR stream from a journal file.
216*00b67f09SDavid van Moolenbroek *
217*00b67f09SDavid van Moolenbroek * The SOA at the beginning of each sequence of additions
218*00b67f09SDavid van Moolenbroek * or deletions are included in the stream, but the extra
219*00b67f09SDavid van Moolenbroek * SOAs at the beginning and end of the entire transfer are
220*00b67f09SDavid van Moolenbroek * not included.
221*00b67f09SDavid van Moolenbroek */
222*00b67f09SDavid van Moolenbroek
223*00b67f09SDavid van Moolenbroek typedef struct ixfr_rrstream {
224*00b67f09SDavid van Moolenbroek rrstream_t common;
225*00b67f09SDavid van Moolenbroek dns_journal_t *journal;
226*00b67f09SDavid van Moolenbroek } ixfr_rrstream_t;
227*00b67f09SDavid van Moolenbroek
228*00b67f09SDavid van Moolenbroek /* Forward declarations. */
229*00b67f09SDavid van Moolenbroek static void
230*00b67f09SDavid van Moolenbroek ixfr_rrstream_destroy(rrstream_t **sp);
231*00b67f09SDavid van Moolenbroek
232*00b67f09SDavid van Moolenbroek static rrstream_methods_t ixfr_rrstream_methods;
233*00b67f09SDavid van Moolenbroek
234*00b67f09SDavid van Moolenbroek /*
235*00b67f09SDavid van Moolenbroek * Returns: anything dns_journal_open() or dns_journal_iter_init()
236*00b67f09SDavid van Moolenbroek * may return.
237*00b67f09SDavid van Moolenbroek */
238*00b67f09SDavid van Moolenbroek
239*00b67f09SDavid van Moolenbroek static isc_result_t
ixfr_rrstream_create(isc_mem_t * mctx,const char * journal_filename,isc_uint32_t begin_serial,isc_uint32_t end_serial,rrstream_t ** sp)240*00b67f09SDavid van Moolenbroek ixfr_rrstream_create(isc_mem_t *mctx,
241*00b67f09SDavid van Moolenbroek const char *journal_filename,
242*00b67f09SDavid van Moolenbroek isc_uint32_t begin_serial,
243*00b67f09SDavid van Moolenbroek isc_uint32_t end_serial,
244*00b67f09SDavid van Moolenbroek rrstream_t **sp)
245*00b67f09SDavid van Moolenbroek {
246*00b67f09SDavid van Moolenbroek ixfr_rrstream_t *s;
247*00b67f09SDavid van Moolenbroek isc_result_t result;
248*00b67f09SDavid van Moolenbroek
249*00b67f09SDavid van Moolenbroek INSIST(sp != NULL && *sp == NULL);
250*00b67f09SDavid van Moolenbroek
251*00b67f09SDavid van Moolenbroek s = isc_mem_get(mctx, sizeof(*s));
252*00b67f09SDavid van Moolenbroek if (s == NULL)
253*00b67f09SDavid van Moolenbroek return (ISC_R_NOMEMORY);
254*00b67f09SDavid van Moolenbroek s->common.mctx = NULL;
255*00b67f09SDavid van Moolenbroek isc_mem_attach(mctx, &s->common.mctx);
256*00b67f09SDavid van Moolenbroek s->common.methods = &ixfr_rrstream_methods;
257*00b67f09SDavid van Moolenbroek s->journal = NULL;
258*00b67f09SDavid van Moolenbroek
259*00b67f09SDavid van Moolenbroek CHECK(dns_journal_open(mctx, journal_filename,
260*00b67f09SDavid van Moolenbroek DNS_JOURNAL_READ, &s->journal));
261*00b67f09SDavid van Moolenbroek CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial));
262*00b67f09SDavid van Moolenbroek
263*00b67f09SDavid van Moolenbroek *sp = (rrstream_t *) s;
264*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
265*00b67f09SDavid van Moolenbroek
266*00b67f09SDavid van Moolenbroek failure:
267*00b67f09SDavid van Moolenbroek ixfr_rrstream_destroy((rrstream_t **) (void *)&s);
268*00b67f09SDavid van Moolenbroek return (result);
269*00b67f09SDavid van Moolenbroek }
270*00b67f09SDavid van Moolenbroek
271*00b67f09SDavid van Moolenbroek static isc_result_t
ixfr_rrstream_first(rrstream_t * rs)272*00b67f09SDavid van Moolenbroek ixfr_rrstream_first(rrstream_t *rs) {
273*00b67f09SDavid van Moolenbroek ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
274*00b67f09SDavid van Moolenbroek return (dns_journal_first_rr(s->journal));
275*00b67f09SDavid van Moolenbroek }
276*00b67f09SDavid van Moolenbroek
277*00b67f09SDavid van Moolenbroek static isc_result_t
ixfr_rrstream_next(rrstream_t * rs)278*00b67f09SDavid van Moolenbroek ixfr_rrstream_next(rrstream_t *rs) {
279*00b67f09SDavid van Moolenbroek ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
280*00b67f09SDavid van Moolenbroek return (dns_journal_next_rr(s->journal));
281*00b67f09SDavid van Moolenbroek }
282*00b67f09SDavid van Moolenbroek
283*00b67f09SDavid van Moolenbroek static void
ixfr_rrstream_current(rrstream_t * rs,dns_name_t ** name,isc_uint32_t * ttl,dns_rdata_t ** rdata)284*00b67f09SDavid van Moolenbroek ixfr_rrstream_current(rrstream_t *rs,
285*00b67f09SDavid van Moolenbroek dns_name_t **name, isc_uint32_t *ttl,
286*00b67f09SDavid van Moolenbroek dns_rdata_t **rdata)
287*00b67f09SDavid van Moolenbroek {
288*00b67f09SDavid van Moolenbroek ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
289*00b67f09SDavid van Moolenbroek dns_journal_current_rr(s->journal, name, ttl, rdata);
290*00b67f09SDavid van Moolenbroek }
291*00b67f09SDavid van Moolenbroek
292*00b67f09SDavid van Moolenbroek static void
ixfr_rrstream_destroy(rrstream_t ** rsp)293*00b67f09SDavid van Moolenbroek ixfr_rrstream_destroy(rrstream_t **rsp) {
294*00b67f09SDavid van Moolenbroek ixfr_rrstream_t *s = (ixfr_rrstream_t *) *rsp;
295*00b67f09SDavid van Moolenbroek if (s->journal != 0)
296*00b67f09SDavid van Moolenbroek dns_journal_destroy(&s->journal);
297*00b67f09SDavid van Moolenbroek isc_mem_putanddetach(&s->common.mctx, s, sizeof(*s));
298*00b67f09SDavid van Moolenbroek }
299*00b67f09SDavid van Moolenbroek
300*00b67f09SDavid van Moolenbroek static rrstream_methods_t ixfr_rrstream_methods = {
301*00b67f09SDavid van Moolenbroek ixfr_rrstream_first,
302*00b67f09SDavid van Moolenbroek ixfr_rrstream_next,
303*00b67f09SDavid van Moolenbroek ixfr_rrstream_current,
304*00b67f09SDavid van Moolenbroek rrstream_noop_pause,
305*00b67f09SDavid van Moolenbroek ixfr_rrstream_destroy
306*00b67f09SDavid van Moolenbroek };
307*00b67f09SDavid van Moolenbroek
308*00b67f09SDavid van Moolenbroek /**************************************************************************/
309*00b67f09SDavid van Moolenbroek /*
310*00b67f09SDavid van Moolenbroek * An 'axfr_rrstream_t' is an 'rrstream_t' that returns
311*00b67f09SDavid van Moolenbroek * an AXFR-like RR stream from a database.
312*00b67f09SDavid van Moolenbroek *
313*00b67f09SDavid van Moolenbroek * The SOAs at the beginning and end of the transfer are
314*00b67f09SDavid van Moolenbroek * not included in the stream.
315*00b67f09SDavid van Moolenbroek */
316*00b67f09SDavid van Moolenbroek
317*00b67f09SDavid van Moolenbroek typedef struct axfr_rrstream {
318*00b67f09SDavid van Moolenbroek rrstream_t common;
319*00b67f09SDavid van Moolenbroek dns_rriterator_t it;
320*00b67f09SDavid van Moolenbroek isc_boolean_t it_valid;
321*00b67f09SDavid van Moolenbroek } axfr_rrstream_t;
322*00b67f09SDavid van Moolenbroek
323*00b67f09SDavid van Moolenbroek /*
324*00b67f09SDavid van Moolenbroek * Forward declarations.
325*00b67f09SDavid van Moolenbroek */
326*00b67f09SDavid van Moolenbroek static void
327*00b67f09SDavid van Moolenbroek axfr_rrstream_destroy(rrstream_t **rsp);
328*00b67f09SDavid van Moolenbroek
329*00b67f09SDavid van Moolenbroek static rrstream_methods_t axfr_rrstream_methods;
330*00b67f09SDavid van Moolenbroek
331*00b67f09SDavid van Moolenbroek static isc_result_t
axfr_rrstream_create(isc_mem_t * mctx,dns_db_t * db,dns_dbversion_t * ver,rrstream_t ** sp)332*00b67f09SDavid van Moolenbroek axfr_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
333*00b67f09SDavid van Moolenbroek rrstream_t **sp)
334*00b67f09SDavid van Moolenbroek {
335*00b67f09SDavid van Moolenbroek axfr_rrstream_t *s;
336*00b67f09SDavid van Moolenbroek isc_result_t result;
337*00b67f09SDavid van Moolenbroek
338*00b67f09SDavid van Moolenbroek INSIST(sp != NULL && *sp == NULL);
339*00b67f09SDavid van Moolenbroek
340*00b67f09SDavid van Moolenbroek s = isc_mem_get(mctx, sizeof(*s));
341*00b67f09SDavid van Moolenbroek if (s == NULL)
342*00b67f09SDavid van Moolenbroek return (ISC_R_NOMEMORY);
343*00b67f09SDavid van Moolenbroek s->common.mctx = NULL;
344*00b67f09SDavid van Moolenbroek isc_mem_attach(mctx, &s->common.mctx);
345*00b67f09SDavid van Moolenbroek s->common.methods = &axfr_rrstream_methods;
346*00b67f09SDavid van Moolenbroek s->it_valid = ISC_FALSE;
347*00b67f09SDavid van Moolenbroek
348*00b67f09SDavid van Moolenbroek CHECK(dns_rriterator_init(&s->it, db, ver, 0));
349*00b67f09SDavid van Moolenbroek s->it_valid = ISC_TRUE;
350*00b67f09SDavid van Moolenbroek
351*00b67f09SDavid van Moolenbroek *sp = (rrstream_t *) s;
352*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
353*00b67f09SDavid van Moolenbroek
354*00b67f09SDavid van Moolenbroek failure:
355*00b67f09SDavid van Moolenbroek axfr_rrstream_destroy((rrstream_t **) (void *)&s);
356*00b67f09SDavid van Moolenbroek return (result);
357*00b67f09SDavid van Moolenbroek }
358*00b67f09SDavid van Moolenbroek
359*00b67f09SDavid van Moolenbroek static isc_result_t
axfr_rrstream_first(rrstream_t * rs)360*00b67f09SDavid van Moolenbroek axfr_rrstream_first(rrstream_t *rs) {
361*00b67f09SDavid van Moolenbroek axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
362*00b67f09SDavid van Moolenbroek isc_result_t result;
363*00b67f09SDavid van Moolenbroek result = dns_rriterator_first(&s->it);
364*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
365*00b67f09SDavid van Moolenbroek return (result);
366*00b67f09SDavid van Moolenbroek /* Skip SOA records. */
367*00b67f09SDavid van Moolenbroek for (;;) {
368*00b67f09SDavid van Moolenbroek dns_name_t *name_dummy = NULL;
369*00b67f09SDavid van Moolenbroek isc_uint32_t ttl_dummy;
370*00b67f09SDavid van Moolenbroek dns_rdata_t *rdata = NULL;
371*00b67f09SDavid van Moolenbroek dns_rriterator_current(&s->it, &name_dummy,
372*00b67f09SDavid van Moolenbroek &ttl_dummy, NULL, &rdata);
373*00b67f09SDavid van Moolenbroek if (rdata->type != dns_rdatatype_soa)
374*00b67f09SDavid van Moolenbroek break;
375*00b67f09SDavid van Moolenbroek result = dns_rriterator_next(&s->it);
376*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
377*00b67f09SDavid van Moolenbroek break;
378*00b67f09SDavid van Moolenbroek }
379*00b67f09SDavid van Moolenbroek return (result);
380*00b67f09SDavid van Moolenbroek }
381*00b67f09SDavid van Moolenbroek
382*00b67f09SDavid van Moolenbroek static isc_result_t
axfr_rrstream_next(rrstream_t * rs)383*00b67f09SDavid van Moolenbroek axfr_rrstream_next(rrstream_t *rs) {
384*00b67f09SDavid van Moolenbroek axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
385*00b67f09SDavid van Moolenbroek isc_result_t result;
386*00b67f09SDavid van Moolenbroek
387*00b67f09SDavid van Moolenbroek /* Skip SOA records. */
388*00b67f09SDavid van Moolenbroek for (;;) {
389*00b67f09SDavid van Moolenbroek dns_name_t *name_dummy = NULL;
390*00b67f09SDavid van Moolenbroek isc_uint32_t ttl_dummy;
391*00b67f09SDavid van Moolenbroek dns_rdata_t *rdata = NULL;
392*00b67f09SDavid van Moolenbroek result = dns_rriterator_next(&s->it);
393*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
394*00b67f09SDavid van Moolenbroek break;
395*00b67f09SDavid van Moolenbroek dns_rriterator_current(&s->it, &name_dummy,
396*00b67f09SDavid van Moolenbroek &ttl_dummy, NULL, &rdata);
397*00b67f09SDavid van Moolenbroek if (rdata->type != dns_rdatatype_soa)
398*00b67f09SDavid van Moolenbroek break;
399*00b67f09SDavid van Moolenbroek }
400*00b67f09SDavid van Moolenbroek return (result);
401*00b67f09SDavid van Moolenbroek }
402*00b67f09SDavid van Moolenbroek
403*00b67f09SDavid van Moolenbroek static void
axfr_rrstream_current(rrstream_t * rs,dns_name_t ** name,isc_uint32_t * ttl,dns_rdata_t ** rdata)404*00b67f09SDavid van Moolenbroek axfr_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
405*00b67f09SDavid van Moolenbroek dns_rdata_t **rdata)
406*00b67f09SDavid van Moolenbroek {
407*00b67f09SDavid van Moolenbroek axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
408*00b67f09SDavid van Moolenbroek dns_rriterator_current(&s->it, name, ttl, NULL, rdata);
409*00b67f09SDavid van Moolenbroek }
410*00b67f09SDavid van Moolenbroek
411*00b67f09SDavid van Moolenbroek static void
axfr_rrstream_pause(rrstream_t * rs)412*00b67f09SDavid van Moolenbroek axfr_rrstream_pause(rrstream_t *rs) {
413*00b67f09SDavid van Moolenbroek axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
414*00b67f09SDavid van Moolenbroek dns_rriterator_pause(&s->it);
415*00b67f09SDavid van Moolenbroek }
416*00b67f09SDavid van Moolenbroek
417*00b67f09SDavid van Moolenbroek static void
axfr_rrstream_destroy(rrstream_t ** rsp)418*00b67f09SDavid van Moolenbroek axfr_rrstream_destroy(rrstream_t **rsp) {
419*00b67f09SDavid van Moolenbroek axfr_rrstream_t *s = (axfr_rrstream_t *) *rsp;
420*00b67f09SDavid van Moolenbroek if (s->it_valid)
421*00b67f09SDavid van Moolenbroek dns_rriterator_destroy(&s->it);
422*00b67f09SDavid van Moolenbroek isc_mem_putanddetach(&s->common.mctx, s, sizeof(*s));
423*00b67f09SDavid van Moolenbroek }
424*00b67f09SDavid van Moolenbroek
425*00b67f09SDavid van Moolenbroek static rrstream_methods_t axfr_rrstream_methods = {
426*00b67f09SDavid van Moolenbroek axfr_rrstream_first,
427*00b67f09SDavid van Moolenbroek axfr_rrstream_next,
428*00b67f09SDavid van Moolenbroek axfr_rrstream_current,
429*00b67f09SDavid van Moolenbroek axfr_rrstream_pause,
430*00b67f09SDavid van Moolenbroek axfr_rrstream_destroy
431*00b67f09SDavid van Moolenbroek };
432*00b67f09SDavid van Moolenbroek
433*00b67f09SDavid van Moolenbroek /**************************************************************************/
434*00b67f09SDavid van Moolenbroek /*
435*00b67f09SDavid van Moolenbroek * An 'soa_rrstream_t' is a degenerate 'rrstream_t' that returns
436*00b67f09SDavid van Moolenbroek * a single SOA record.
437*00b67f09SDavid van Moolenbroek */
438*00b67f09SDavid van Moolenbroek
439*00b67f09SDavid van Moolenbroek typedef struct soa_rrstream {
440*00b67f09SDavid van Moolenbroek rrstream_t common;
441*00b67f09SDavid van Moolenbroek dns_difftuple_t *soa_tuple;
442*00b67f09SDavid van Moolenbroek } soa_rrstream_t;
443*00b67f09SDavid van Moolenbroek
444*00b67f09SDavid van Moolenbroek /*
445*00b67f09SDavid van Moolenbroek * Forward declarations.
446*00b67f09SDavid van Moolenbroek */
447*00b67f09SDavid van Moolenbroek static void
448*00b67f09SDavid van Moolenbroek soa_rrstream_destroy(rrstream_t **rsp);
449*00b67f09SDavid van Moolenbroek
450*00b67f09SDavid van Moolenbroek static rrstream_methods_t soa_rrstream_methods;
451*00b67f09SDavid van Moolenbroek
452*00b67f09SDavid van Moolenbroek static isc_result_t
soa_rrstream_create(isc_mem_t * mctx,dns_db_t * db,dns_dbversion_t * ver,rrstream_t ** sp)453*00b67f09SDavid van Moolenbroek soa_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
454*00b67f09SDavid van Moolenbroek rrstream_t **sp)
455*00b67f09SDavid van Moolenbroek {
456*00b67f09SDavid van Moolenbroek soa_rrstream_t *s;
457*00b67f09SDavid van Moolenbroek isc_result_t result;
458*00b67f09SDavid van Moolenbroek
459*00b67f09SDavid van Moolenbroek INSIST(sp != NULL && *sp == NULL);
460*00b67f09SDavid van Moolenbroek
461*00b67f09SDavid van Moolenbroek s = isc_mem_get(mctx, sizeof(*s));
462*00b67f09SDavid van Moolenbroek if (s == NULL)
463*00b67f09SDavid van Moolenbroek return (ISC_R_NOMEMORY);
464*00b67f09SDavid van Moolenbroek s->common.mctx = NULL;
465*00b67f09SDavid van Moolenbroek isc_mem_attach(mctx, &s->common.mctx);
466*00b67f09SDavid van Moolenbroek s->common.methods = &soa_rrstream_methods;
467*00b67f09SDavid van Moolenbroek s->soa_tuple = NULL;
468*00b67f09SDavid van Moolenbroek
469*00b67f09SDavid van Moolenbroek CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
470*00b67f09SDavid van Moolenbroek &s->soa_tuple));
471*00b67f09SDavid van Moolenbroek
472*00b67f09SDavid van Moolenbroek *sp = (rrstream_t *) s;
473*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
474*00b67f09SDavid van Moolenbroek
475*00b67f09SDavid van Moolenbroek failure:
476*00b67f09SDavid van Moolenbroek soa_rrstream_destroy((rrstream_t **) (void *)&s);
477*00b67f09SDavid van Moolenbroek return (result);
478*00b67f09SDavid van Moolenbroek }
479*00b67f09SDavid van Moolenbroek
480*00b67f09SDavid van Moolenbroek static isc_result_t
soa_rrstream_first(rrstream_t * rs)481*00b67f09SDavid van Moolenbroek soa_rrstream_first(rrstream_t *rs) {
482*00b67f09SDavid van Moolenbroek UNUSED(rs);
483*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
484*00b67f09SDavid van Moolenbroek }
485*00b67f09SDavid van Moolenbroek
486*00b67f09SDavid van Moolenbroek static isc_result_t
soa_rrstream_next(rrstream_t * rs)487*00b67f09SDavid van Moolenbroek soa_rrstream_next(rrstream_t *rs) {
488*00b67f09SDavid van Moolenbroek UNUSED(rs);
489*00b67f09SDavid van Moolenbroek return (ISC_R_NOMORE);
490*00b67f09SDavid van Moolenbroek }
491*00b67f09SDavid van Moolenbroek
492*00b67f09SDavid van Moolenbroek static void
soa_rrstream_current(rrstream_t * rs,dns_name_t ** name,isc_uint32_t * ttl,dns_rdata_t ** rdata)493*00b67f09SDavid van Moolenbroek soa_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
494*00b67f09SDavid van Moolenbroek dns_rdata_t **rdata)
495*00b67f09SDavid van Moolenbroek {
496*00b67f09SDavid van Moolenbroek soa_rrstream_t *s = (soa_rrstream_t *) rs;
497*00b67f09SDavid van Moolenbroek *name = &s->soa_tuple->name;
498*00b67f09SDavid van Moolenbroek *ttl = s->soa_tuple->ttl;
499*00b67f09SDavid van Moolenbroek *rdata = &s->soa_tuple->rdata;
500*00b67f09SDavid van Moolenbroek }
501*00b67f09SDavid van Moolenbroek
502*00b67f09SDavid van Moolenbroek static void
soa_rrstream_destroy(rrstream_t ** rsp)503*00b67f09SDavid van Moolenbroek soa_rrstream_destroy(rrstream_t **rsp) {
504*00b67f09SDavid van Moolenbroek soa_rrstream_t *s = (soa_rrstream_t *) *rsp;
505*00b67f09SDavid van Moolenbroek if (s->soa_tuple != NULL)
506*00b67f09SDavid van Moolenbroek dns_difftuple_free(&s->soa_tuple);
507*00b67f09SDavid van Moolenbroek isc_mem_putanddetach(&s->common.mctx, s, sizeof(*s));
508*00b67f09SDavid van Moolenbroek }
509*00b67f09SDavid van Moolenbroek
510*00b67f09SDavid van Moolenbroek static rrstream_methods_t soa_rrstream_methods = {
511*00b67f09SDavid van Moolenbroek soa_rrstream_first,
512*00b67f09SDavid van Moolenbroek soa_rrstream_next,
513*00b67f09SDavid van Moolenbroek soa_rrstream_current,
514*00b67f09SDavid van Moolenbroek rrstream_noop_pause,
515*00b67f09SDavid van Moolenbroek soa_rrstream_destroy
516*00b67f09SDavid van Moolenbroek };
517*00b67f09SDavid van Moolenbroek
518*00b67f09SDavid van Moolenbroek /**************************************************************************/
519*00b67f09SDavid van Moolenbroek /*
520*00b67f09SDavid van Moolenbroek * A 'compound_rrstream_t' objects owns a soa_rrstream
521*00b67f09SDavid van Moolenbroek * and another rrstream, the "data stream". It returns
522*00b67f09SDavid van Moolenbroek * a concatenated stream consisting of the soa_rrstream, then
523*00b67f09SDavid van Moolenbroek * the data stream, then the soa_rrstream again.
524*00b67f09SDavid van Moolenbroek *
525*00b67f09SDavid van Moolenbroek * The component streams are owned by the compound_rrstream_t
526*00b67f09SDavid van Moolenbroek * and are destroyed with it.
527*00b67f09SDavid van Moolenbroek */
528*00b67f09SDavid van Moolenbroek
529*00b67f09SDavid van Moolenbroek typedef struct compound_rrstream {
530*00b67f09SDavid van Moolenbroek rrstream_t common;
531*00b67f09SDavid van Moolenbroek rrstream_t *components[3];
532*00b67f09SDavid van Moolenbroek int state;
533*00b67f09SDavid van Moolenbroek isc_result_t result;
534*00b67f09SDavid van Moolenbroek } compound_rrstream_t;
535*00b67f09SDavid van Moolenbroek
536*00b67f09SDavid van Moolenbroek /*
537*00b67f09SDavid van Moolenbroek * Forward declarations.
538*00b67f09SDavid van Moolenbroek */
539*00b67f09SDavid van Moolenbroek static void
540*00b67f09SDavid van Moolenbroek compound_rrstream_destroy(rrstream_t **rsp);
541*00b67f09SDavid van Moolenbroek
542*00b67f09SDavid van Moolenbroek static isc_result_t
543*00b67f09SDavid van Moolenbroek compound_rrstream_next(rrstream_t *rs);
544*00b67f09SDavid van Moolenbroek
545*00b67f09SDavid van Moolenbroek static rrstream_methods_t compound_rrstream_methods;
546*00b67f09SDavid van Moolenbroek
547*00b67f09SDavid van Moolenbroek /*
548*00b67f09SDavid van Moolenbroek * Requires:
549*00b67f09SDavid van Moolenbroek * soa_stream != NULL && *soa_stream != NULL
550*00b67f09SDavid van Moolenbroek * data_stream != NULL && *data_stream != NULL
551*00b67f09SDavid van Moolenbroek * sp != NULL && *sp == NULL
552*00b67f09SDavid van Moolenbroek *
553*00b67f09SDavid van Moolenbroek * Ensures:
554*00b67f09SDavid van Moolenbroek * *soa_stream == NULL
555*00b67f09SDavid van Moolenbroek * *data_stream == NULL
556*00b67f09SDavid van Moolenbroek * *sp points to a valid compound_rrstream_t
557*00b67f09SDavid van Moolenbroek * The soa and data streams will be destroyed
558*00b67f09SDavid van Moolenbroek * when the compound_rrstream_t is destroyed.
559*00b67f09SDavid van Moolenbroek */
560*00b67f09SDavid van Moolenbroek static isc_result_t
compound_rrstream_create(isc_mem_t * mctx,rrstream_t ** soa_stream,rrstream_t ** data_stream,rrstream_t ** sp)561*00b67f09SDavid van Moolenbroek compound_rrstream_create(isc_mem_t *mctx, rrstream_t **soa_stream,
562*00b67f09SDavid van Moolenbroek rrstream_t **data_stream, rrstream_t **sp)
563*00b67f09SDavid van Moolenbroek {
564*00b67f09SDavid van Moolenbroek compound_rrstream_t *s;
565*00b67f09SDavid van Moolenbroek
566*00b67f09SDavid van Moolenbroek INSIST(sp != NULL && *sp == NULL);
567*00b67f09SDavid van Moolenbroek
568*00b67f09SDavid van Moolenbroek s = isc_mem_get(mctx, sizeof(*s));
569*00b67f09SDavid van Moolenbroek if (s == NULL)
570*00b67f09SDavid van Moolenbroek return (ISC_R_NOMEMORY);
571*00b67f09SDavid van Moolenbroek s->common.mctx = NULL;
572*00b67f09SDavid van Moolenbroek isc_mem_attach(mctx, &s->common.mctx);
573*00b67f09SDavid van Moolenbroek s->common.methods = &compound_rrstream_methods;
574*00b67f09SDavid van Moolenbroek s->components[0] = *soa_stream;
575*00b67f09SDavid van Moolenbroek s->components[1] = *data_stream;
576*00b67f09SDavid van Moolenbroek s->components[2] = *soa_stream;
577*00b67f09SDavid van Moolenbroek s->state = -1;
578*00b67f09SDavid van Moolenbroek s->result = ISC_R_FAILURE;
579*00b67f09SDavid van Moolenbroek
580*00b67f09SDavid van Moolenbroek *soa_stream = NULL;
581*00b67f09SDavid van Moolenbroek *data_stream = NULL;
582*00b67f09SDavid van Moolenbroek *sp = (rrstream_t *) s;
583*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
584*00b67f09SDavid van Moolenbroek }
585*00b67f09SDavid van Moolenbroek
586*00b67f09SDavid van Moolenbroek static isc_result_t
compound_rrstream_first(rrstream_t * rs)587*00b67f09SDavid van Moolenbroek compound_rrstream_first(rrstream_t *rs) {
588*00b67f09SDavid van Moolenbroek compound_rrstream_t *s = (compound_rrstream_t *) rs;
589*00b67f09SDavid van Moolenbroek s->state = 0;
590*00b67f09SDavid van Moolenbroek do {
591*00b67f09SDavid van Moolenbroek rrstream_t *curstream = s->components[s->state];
592*00b67f09SDavid van Moolenbroek s->result = curstream->methods->first(curstream);
593*00b67f09SDavid van Moolenbroek } while (s->result == ISC_R_NOMORE && s->state < 2);
594*00b67f09SDavid van Moolenbroek return (s->result);
595*00b67f09SDavid van Moolenbroek }
596*00b67f09SDavid van Moolenbroek
597*00b67f09SDavid van Moolenbroek static isc_result_t
compound_rrstream_next(rrstream_t * rs)598*00b67f09SDavid van Moolenbroek compound_rrstream_next(rrstream_t *rs) {
599*00b67f09SDavid van Moolenbroek compound_rrstream_t *s = (compound_rrstream_t *) rs;
600*00b67f09SDavid van Moolenbroek rrstream_t *curstream = s->components[s->state];
601*00b67f09SDavid van Moolenbroek s->result = curstream->methods->next(curstream);
602*00b67f09SDavid van Moolenbroek while (s->result == ISC_R_NOMORE) {
603*00b67f09SDavid van Moolenbroek /*
604*00b67f09SDavid van Moolenbroek * Make sure locks held by the current stream
605*00b67f09SDavid van Moolenbroek * are released before we switch streams.
606*00b67f09SDavid van Moolenbroek */
607*00b67f09SDavid van Moolenbroek curstream->methods->pause(curstream);
608*00b67f09SDavid van Moolenbroek if (s->state == 2)
609*00b67f09SDavid van Moolenbroek return (ISC_R_NOMORE);
610*00b67f09SDavid van Moolenbroek s->state++;
611*00b67f09SDavid van Moolenbroek curstream = s->components[s->state];
612*00b67f09SDavid van Moolenbroek s->result = curstream->methods->first(curstream);
613*00b67f09SDavid van Moolenbroek }
614*00b67f09SDavid van Moolenbroek return (s->result);
615*00b67f09SDavid van Moolenbroek }
616*00b67f09SDavid van Moolenbroek
617*00b67f09SDavid van Moolenbroek static void
compound_rrstream_current(rrstream_t * rs,dns_name_t ** name,isc_uint32_t * ttl,dns_rdata_t ** rdata)618*00b67f09SDavid van Moolenbroek compound_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
619*00b67f09SDavid van Moolenbroek dns_rdata_t **rdata)
620*00b67f09SDavid van Moolenbroek {
621*00b67f09SDavid van Moolenbroek compound_rrstream_t *s = (compound_rrstream_t *) rs;
622*00b67f09SDavid van Moolenbroek rrstream_t *curstream;
623*00b67f09SDavid van Moolenbroek INSIST(0 <= s->state && s->state < 3);
624*00b67f09SDavid van Moolenbroek INSIST(s->result == ISC_R_SUCCESS);
625*00b67f09SDavid van Moolenbroek curstream = s->components[s->state];
626*00b67f09SDavid van Moolenbroek curstream->methods->current(curstream, name, ttl, rdata);
627*00b67f09SDavid van Moolenbroek }
628*00b67f09SDavid van Moolenbroek
629*00b67f09SDavid van Moolenbroek static void
compound_rrstream_pause(rrstream_t * rs)630*00b67f09SDavid van Moolenbroek compound_rrstream_pause(rrstream_t *rs)
631*00b67f09SDavid van Moolenbroek {
632*00b67f09SDavid van Moolenbroek compound_rrstream_t *s = (compound_rrstream_t *) rs;
633*00b67f09SDavid van Moolenbroek rrstream_t *curstream;
634*00b67f09SDavid van Moolenbroek INSIST(0 <= s->state && s->state < 3);
635*00b67f09SDavid van Moolenbroek curstream = s->components[s->state];
636*00b67f09SDavid van Moolenbroek curstream->methods->pause(curstream);
637*00b67f09SDavid van Moolenbroek }
638*00b67f09SDavid van Moolenbroek
639*00b67f09SDavid van Moolenbroek static void
compound_rrstream_destroy(rrstream_t ** rsp)640*00b67f09SDavid van Moolenbroek compound_rrstream_destroy(rrstream_t **rsp) {
641*00b67f09SDavid van Moolenbroek compound_rrstream_t *s = (compound_rrstream_t *) *rsp;
642*00b67f09SDavid van Moolenbroek s->components[0]->methods->destroy(&s->components[0]);
643*00b67f09SDavid van Moolenbroek s->components[1]->methods->destroy(&s->components[1]);
644*00b67f09SDavid van Moolenbroek s->components[2] = NULL; /* Copy of components[0]. */
645*00b67f09SDavid van Moolenbroek isc_mem_putanddetach(&s->common.mctx, s, sizeof(*s));
646*00b67f09SDavid van Moolenbroek }
647*00b67f09SDavid van Moolenbroek
648*00b67f09SDavid van Moolenbroek static rrstream_methods_t compound_rrstream_methods = {
649*00b67f09SDavid van Moolenbroek compound_rrstream_first,
650*00b67f09SDavid van Moolenbroek compound_rrstream_next,
651*00b67f09SDavid van Moolenbroek compound_rrstream_current,
652*00b67f09SDavid van Moolenbroek compound_rrstream_pause,
653*00b67f09SDavid van Moolenbroek compound_rrstream_destroy
654*00b67f09SDavid van Moolenbroek };
655*00b67f09SDavid van Moolenbroek
656*00b67f09SDavid van Moolenbroek /**************************************************************************/
657*00b67f09SDavid van Moolenbroek /*
658*00b67f09SDavid van Moolenbroek * An 'xfrout_ctx_t' contains the state of an outgoing AXFR or IXFR
659*00b67f09SDavid van Moolenbroek * in progress.
660*00b67f09SDavid van Moolenbroek */
661*00b67f09SDavid van Moolenbroek
662*00b67f09SDavid van Moolenbroek typedef struct {
663*00b67f09SDavid van Moolenbroek isc_mem_t *mctx;
664*00b67f09SDavid van Moolenbroek ns_client_t *client;
665*00b67f09SDavid van Moolenbroek unsigned int id; /* ID of request */
666*00b67f09SDavid van Moolenbroek dns_name_t *qname; /* Question name of request */
667*00b67f09SDavid van Moolenbroek dns_rdatatype_t qtype; /* dns_rdatatype_{a,i}xfr */
668*00b67f09SDavid van Moolenbroek dns_rdataclass_t qclass;
669*00b67f09SDavid van Moolenbroek dns_zone_t *zone; /* (necessary for stats) */
670*00b67f09SDavid van Moolenbroek dns_db_t *db;
671*00b67f09SDavid van Moolenbroek dns_dbversion_t *ver;
672*00b67f09SDavid van Moolenbroek isc_quota_t *quota;
673*00b67f09SDavid van Moolenbroek rrstream_t *stream; /* The XFR RR stream */
674*00b67f09SDavid van Moolenbroek isc_boolean_t end_of_stream; /* EOS has been reached */
675*00b67f09SDavid van Moolenbroek isc_buffer_t buf; /* Buffer for message owner
676*00b67f09SDavid van Moolenbroek names and rdatas */
677*00b67f09SDavid van Moolenbroek isc_buffer_t txlenbuf; /* Transmit length buffer */
678*00b67f09SDavid van Moolenbroek isc_buffer_t txbuf; /* Transmit message buffer */
679*00b67f09SDavid van Moolenbroek void *txmem;
680*00b67f09SDavid van Moolenbroek unsigned int txmemlen;
681*00b67f09SDavid van Moolenbroek unsigned int nmsg; /* Number of messages sent */
682*00b67f09SDavid van Moolenbroek dns_tsigkey_t *tsigkey; /* Key used to create TSIG */
683*00b67f09SDavid van Moolenbroek isc_buffer_t *lasttsig; /* the last TSIG */
684*00b67f09SDavid van Moolenbroek isc_boolean_t many_answers;
685*00b67f09SDavid van Moolenbroek int sends; /* Send in progress */
686*00b67f09SDavid van Moolenbroek isc_boolean_t shuttingdown;
687*00b67f09SDavid van Moolenbroek const char *mnemonic; /* Style of transfer */
688*00b67f09SDavid van Moolenbroek } xfrout_ctx_t;
689*00b67f09SDavid van Moolenbroek
690*00b67f09SDavid van Moolenbroek static isc_result_t
691*00b67f09SDavid van Moolenbroek xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client,
692*00b67f09SDavid van Moolenbroek unsigned int id, dns_name_t *qname, dns_rdatatype_t qtype,
693*00b67f09SDavid van Moolenbroek dns_rdataclass_t qclass, dns_zone_t *zone,
694*00b67f09SDavid van Moolenbroek dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
695*00b67f09SDavid van Moolenbroek rrstream_t *stream, dns_tsigkey_t *tsigkey,
696*00b67f09SDavid van Moolenbroek isc_buffer_t *lasttsig,
697*00b67f09SDavid van Moolenbroek unsigned int maxtime,
698*00b67f09SDavid van Moolenbroek unsigned int idletime,
699*00b67f09SDavid van Moolenbroek isc_boolean_t many_answers,
700*00b67f09SDavid van Moolenbroek xfrout_ctx_t **xfrp);
701*00b67f09SDavid van Moolenbroek
702*00b67f09SDavid van Moolenbroek static void
703*00b67f09SDavid van Moolenbroek sendstream(xfrout_ctx_t *xfr);
704*00b67f09SDavid van Moolenbroek
705*00b67f09SDavid van Moolenbroek static void
706*00b67f09SDavid van Moolenbroek xfrout_senddone(isc_task_t *task, isc_event_t *event);
707*00b67f09SDavid van Moolenbroek
708*00b67f09SDavid van Moolenbroek static void
709*00b67f09SDavid van Moolenbroek xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg);
710*00b67f09SDavid van Moolenbroek
711*00b67f09SDavid van Moolenbroek static void
712*00b67f09SDavid van Moolenbroek xfrout_maybe_destroy(xfrout_ctx_t *xfr);
713*00b67f09SDavid van Moolenbroek
714*00b67f09SDavid van Moolenbroek static void
715*00b67f09SDavid van Moolenbroek xfrout_ctx_destroy(xfrout_ctx_t **xfrp);
716*00b67f09SDavid van Moolenbroek
717*00b67f09SDavid van Moolenbroek static void
718*00b67f09SDavid van Moolenbroek xfrout_client_shutdown(void *arg, isc_result_t result);
719*00b67f09SDavid van Moolenbroek
720*00b67f09SDavid van Moolenbroek static void
721*00b67f09SDavid van Moolenbroek xfrout_log1(ns_client_t *client, dns_name_t *zonename,
722*00b67f09SDavid van Moolenbroek dns_rdataclass_t rdclass, int level,
723*00b67f09SDavid van Moolenbroek const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6);
724*00b67f09SDavid van Moolenbroek
725*00b67f09SDavid van Moolenbroek static void
726*00b67f09SDavid van Moolenbroek xfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...)
727*00b67f09SDavid van Moolenbroek ISC_FORMAT_PRINTF(3, 4);
728*00b67f09SDavid van Moolenbroek
729*00b67f09SDavid van Moolenbroek /**************************************************************************/
730*00b67f09SDavid van Moolenbroek
731*00b67f09SDavid van Moolenbroek void
ns_xfr_start(ns_client_t * client,dns_rdatatype_t reqtype)732*00b67f09SDavid van Moolenbroek ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
733*00b67f09SDavid van Moolenbroek isc_result_t result;
734*00b67f09SDavid van Moolenbroek dns_name_t *question_name;
735*00b67f09SDavid van Moolenbroek dns_rdataset_t *question_rdataset;
736*00b67f09SDavid van Moolenbroek dns_zone_t *zone = NULL, *raw = NULL, *mayberaw;
737*00b67f09SDavid van Moolenbroek dns_db_t *db = NULL;
738*00b67f09SDavid van Moolenbroek dns_dbversion_t *ver = NULL;
739*00b67f09SDavid van Moolenbroek dns_rdataclass_t question_class;
740*00b67f09SDavid van Moolenbroek rrstream_t *soa_stream = NULL;
741*00b67f09SDavid van Moolenbroek rrstream_t *data_stream = NULL;
742*00b67f09SDavid van Moolenbroek rrstream_t *stream = NULL;
743*00b67f09SDavid van Moolenbroek dns_difftuple_t *current_soa_tuple = NULL;
744*00b67f09SDavid van Moolenbroek dns_name_t *soa_name;
745*00b67f09SDavid van Moolenbroek dns_rdataset_t *soa_rdataset;
746*00b67f09SDavid van Moolenbroek dns_rdata_t soa_rdata = DNS_RDATA_INIT;
747*00b67f09SDavid van Moolenbroek isc_boolean_t have_soa = ISC_FALSE;
748*00b67f09SDavid van Moolenbroek const char *mnemonic = NULL;
749*00b67f09SDavid van Moolenbroek isc_mem_t *mctx = client->mctx;
750*00b67f09SDavid van Moolenbroek dns_message_t *request = client->message;
751*00b67f09SDavid van Moolenbroek xfrout_ctx_t *xfr = NULL;
752*00b67f09SDavid van Moolenbroek isc_quota_t *quota = NULL;
753*00b67f09SDavid van Moolenbroek dns_transfer_format_t format = client->view->transfer_format;
754*00b67f09SDavid van Moolenbroek isc_netaddr_t na;
755*00b67f09SDavid van Moolenbroek dns_peer_t *peer = NULL;
756*00b67f09SDavid van Moolenbroek isc_buffer_t *tsigbuf = NULL;
757*00b67f09SDavid van Moolenbroek char *journalfile;
758*00b67f09SDavid van Moolenbroek char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")];
759*00b67f09SDavid van Moolenbroek char keyname[DNS_NAME_FORMATSIZE];
760*00b67f09SDavid van Moolenbroek isc_boolean_t is_poll = ISC_FALSE;
761*00b67f09SDavid van Moolenbroek isc_boolean_t is_dlz = ISC_FALSE;
762*00b67f09SDavid van Moolenbroek isc_boolean_t is_ixfr = ISC_FALSE;
763*00b67f09SDavid van Moolenbroek isc_uint32_t begin_serial = 0, current_serial;
764*00b67f09SDavid van Moolenbroek
765*00b67f09SDavid van Moolenbroek switch (reqtype) {
766*00b67f09SDavid van Moolenbroek case dns_rdatatype_axfr:
767*00b67f09SDavid van Moolenbroek mnemonic = "AXFR";
768*00b67f09SDavid van Moolenbroek break;
769*00b67f09SDavid van Moolenbroek case dns_rdatatype_ixfr:
770*00b67f09SDavid van Moolenbroek mnemonic = "IXFR";
771*00b67f09SDavid van Moolenbroek break;
772*00b67f09SDavid van Moolenbroek default:
773*00b67f09SDavid van Moolenbroek INSIST(0);
774*00b67f09SDavid van Moolenbroek break;
775*00b67f09SDavid van Moolenbroek }
776*00b67f09SDavid van Moolenbroek
777*00b67f09SDavid van Moolenbroek ns_client_log(client,
778*00b67f09SDavid van Moolenbroek DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT,
779*00b67f09SDavid van Moolenbroek ISC_LOG_DEBUG(6), "%s request", mnemonic);
780*00b67f09SDavid van Moolenbroek /*
781*00b67f09SDavid van Moolenbroek * Apply quota.
782*00b67f09SDavid van Moolenbroek */
783*00b67f09SDavid van Moolenbroek result = isc_quota_attach(&ns_g_server->xfroutquota, "a);
784*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
785*00b67f09SDavid van Moolenbroek isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING,
786*00b67f09SDavid van Moolenbroek "%s request denied: %s", mnemonic,
787*00b67f09SDavid van Moolenbroek isc_result_totext(result));
788*00b67f09SDavid van Moolenbroek goto failure;
789*00b67f09SDavid van Moolenbroek }
790*00b67f09SDavid van Moolenbroek
791*00b67f09SDavid van Moolenbroek /*
792*00b67f09SDavid van Moolenbroek * Interpret the question section.
793*00b67f09SDavid van Moolenbroek */
794*00b67f09SDavid van Moolenbroek result = dns_message_firstname(request, DNS_SECTION_QUESTION);
795*00b67f09SDavid van Moolenbroek INSIST(result == ISC_R_SUCCESS);
796*00b67f09SDavid van Moolenbroek
797*00b67f09SDavid van Moolenbroek /*
798*00b67f09SDavid van Moolenbroek * The question section must contain exactly one question, and
799*00b67f09SDavid van Moolenbroek * it must be for AXFR/IXFR as appropriate.
800*00b67f09SDavid van Moolenbroek */
801*00b67f09SDavid van Moolenbroek question_name = NULL;
802*00b67f09SDavid van Moolenbroek dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name);
803*00b67f09SDavid van Moolenbroek question_rdataset = ISC_LIST_HEAD(question_name->list);
804*00b67f09SDavid van Moolenbroek question_class = question_rdataset->rdclass;
805*00b67f09SDavid van Moolenbroek INSIST(question_rdataset->type == reqtype);
806*00b67f09SDavid van Moolenbroek if (ISC_LIST_NEXT(question_rdataset, link) != NULL)
807*00b67f09SDavid van Moolenbroek FAILC(DNS_R_FORMERR, "multiple questions");
808*00b67f09SDavid van Moolenbroek result = dns_message_nextname(request, DNS_SECTION_QUESTION);
809*00b67f09SDavid van Moolenbroek if (result != ISC_R_NOMORE)
810*00b67f09SDavid van Moolenbroek FAILC(DNS_R_FORMERR, "multiple questions");
811*00b67f09SDavid van Moolenbroek
812*00b67f09SDavid van Moolenbroek result = dns_zt_find(client->view->zonetable, question_name, 0, NULL,
813*00b67f09SDavid van Moolenbroek &zone);
814*00b67f09SDavid van Moolenbroek
815*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
816*00b67f09SDavid van Moolenbroek /*
817*00b67f09SDavid van Moolenbroek * Normal zone table does not have a match.
818*00b67f09SDavid van Moolenbroek * Try the DLZ database
819*00b67f09SDavid van Moolenbroek */
820*00b67f09SDavid van Moolenbroek // Temporary: only searching the first DLZ database
821*00b67f09SDavid van Moolenbroek if (! ISC_LIST_EMPTY(client->view->dlz_searched)) {
822*00b67f09SDavid van Moolenbroek result = dns_dlzallowzonexfr(client->view,
823*00b67f09SDavid van Moolenbroek question_name,
824*00b67f09SDavid van Moolenbroek &client->peeraddr,
825*00b67f09SDavid van Moolenbroek &db);
826*00b67f09SDavid van Moolenbroek
827*00b67f09SDavid van Moolenbroek pfilter_notify(result, client, "zonexfr");
828*00b67f09SDavid van Moolenbroek if (result == ISC_R_NOPERM) {
829*00b67f09SDavid van Moolenbroek char _buf1[DNS_NAME_FORMATSIZE];
830*00b67f09SDavid van Moolenbroek char _buf2[DNS_RDATACLASS_FORMATSIZE];
831*00b67f09SDavid van Moolenbroek
832*00b67f09SDavid van Moolenbroek result = DNS_R_REFUSED;
833*00b67f09SDavid van Moolenbroek dns_name_format(question_name, _buf1,
834*00b67f09SDavid van Moolenbroek sizeof(_buf1));
835*00b67f09SDavid van Moolenbroek dns_rdataclass_format(question_class,
836*00b67f09SDavid van Moolenbroek _buf2, sizeof(_buf2));
837*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
838*00b67f09SDavid van Moolenbroek NS_LOGMODULE_XFER_OUT,
839*00b67f09SDavid van Moolenbroek ISC_LOG_ERROR,
840*00b67f09SDavid van Moolenbroek "zone transfer '%s/%s' denied",
841*00b67f09SDavid van Moolenbroek _buf1, _buf2);
842*00b67f09SDavid van Moolenbroek goto failure;
843*00b67f09SDavid van Moolenbroek }
844*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
845*00b67f09SDavid van Moolenbroek FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
846*00b67f09SDavid van Moolenbroek question_name, question_class);
847*00b67f09SDavid van Moolenbroek is_dlz = ISC_TRUE;
848*00b67f09SDavid van Moolenbroek } else {
849*00b67f09SDavid van Moolenbroek /*
850*00b67f09SDavid van Moolenbroek * not DLZ and not in normal zone table, we are
851*00b67f09SDavid van Moolenbroek * not authoritative
852*00b67f09SDavid van Moolenbroek */
853*00b67f09SDavid van Moolenbroek FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
854*00b67f09SDavid van Moolenbroek question_name, question_class);
855*00b67f09SDavid van Moolenbroek }
856*00b67f09SDavid van Moolenbroek } else {
857*00b67f09SDavid van Moolenbroek /* zone table has a match */
858*00b67f09SDavid van Moolenbroek switch(dns_zone_gettype(zone)) {
859*00b67f09SDavid van Moolenbroek /* Master and slave zones are OK for transfer. */
860*00b67f09SDavid van Moolenbroek case dns_zone_master:
861*00b67f09SDavid van Moolenbroek case dns_zone_slave:
862*00b67f09SDavid van Moolenbroek case dns_zone_dlz:
863*00b67f09SDavid van Moolenbroek break;
864*00b67f09SDavid van Moolenbroek default:
865*00b67f09SDavid van Moolenbroek FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
866*00b67f09SDavid van Moolenbroek question_name, question_class);
867*00b67f09SDavid van Moolenbroek }
868*00b67f09SDavid van Moolenbroek CHECK(dns_zone_getdb(zone, &db));
869*00b67f09SDavid van Moolenbroek dns_db_currentversion(db, &ver);
870*00b67f09SDavid van Moolenbroek }
871*00b67f09SDavid van Moolenbroek
872*00b67f09SDavid van Moolenbroek xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
873*00b67f09SDavid van Moolenbroek "%s question section OK", mnemonic);
874*00b67f09SDavid van Moolenbroek
875*00b67f09SDavid van Moolenbroek /*
876*00b67f09SDavid van Moolenbroek * Check the authority section. Look for a SOA record with
877*00b67f09SDavid van Moolenbroek * the same name and class as the question.
878*00b67f09SDavid van Moolenbroek */
879*00b67f09SDavid van Moolenbroek for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY);
880*00b67f09SDavid van Moolenbroek result == ISC_R_SUCCESS;
881*00b67f09SDavid van Moolenbroek result = dns_message_nextname(request, DNS_SECTION_AUTHORITY))
882*00b67f09SDavid van Moolenbroek {
883*00b67f09SDavid van Moolenbroek soa_name = NULL;
884*00b67f09SDavid van Moolenbroek dns_message_currentname(request, DNS_SECTION_AUTHORITY,
885*00b67f09SDavid van Moolenbroek &soa_name);
886*00b67f09SDavid van Moolenbroek
887*00b67f09SDavid van Moolenbroek /*
888*00b67f09SDavid van Moolenbroek * Ignore data whose owner name is not the zone apex.
889*00b67f09SDavid van Moolenbroek */
890*00b67f09SDavid van Moolenbroek if (! dns_name_equal(soa_name, question_name))
891*00b67f09SDavid van Moolenbroek continue;
892*00b67f09SDavid van Moolenbroek
893*00b67f09SDavid van Moolenbroek for (soa_rdataset = ISC_LIST_HEAD(soa_name->list);
894*00b67f09SDavid van Moolenbroek soa_rdataset != NULL;
895*00b67f09SDavid van Moolenbroek soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link))
896*00b67f09SDavid van Moolenbroek {
897*00b67f09SDavid van Moolenbroek /*
898*00b67f09SDavid van Moolenbroek * Ignore non-SOA data.
899*00b67f09SDavid van Moolenbroek */
900*00b67f09SDavid van Moolenbroek if (soa_rdataset->type != dns_rdatatype_soa)
901*00b67f09SDavid van Moolenbroek continue;
902*00b67f09SDavid van Moolenbroek if (soa_rdataset->rdclass != question_class)
903*00b67f09SDavid van Moolenbroek continue;
904*00b67f09SDavid van Moolenbroek
905*00b67f09SDavid van Moolenbroek CHECK(dns_rdataset_first(soa_rdataset));
906*00b67f09SDavid van Moolenbroek dns_rdataset_current(soa_rdataset, &soa_rdata);
907*00b67f09SDavid van Moolenbroek result = dns_rdataset_next(soa_rdataset);
908*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
909*00b67f09SDavid van Moolenbroek FAILC(DNS_R_FORMERR,
910*00b67f09SDavid van Moolenbroek "IXFR authority section "
911*00b67f09SDavid van Moolenbroek "has multiple SOAs");
912*00b67f09SDavid van Moolenbroek have_soa = ISC_TRUE;
913*00b67f09SDavid van Moolenbroek goto got_soa;
914*00b67f09SDavid van Moolenbroek }
915*00b67f09SDavid van Moolenbroek }
916*00b67f09SDavid van Moolenbroek got_soa:
917*00b67f09SDavid van Moolenbroek if (result != ISC_R_NOMORE)
918*00b67f09SDavid van Moolenbroek CHECK(result);
919*00b67f09SDavid van Moolenbroek
920*00b67f09SDavid van Moolenbroek xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
921*00b67f09SDavid van Moolenbroek "%s authority section OK", mnemonic);
922*00b67f09SDavid van Moolenbroek
923*00b67f09SDavid van Moolenbroek /*
924*00b67f09SDavid van Moolenbroek * If not a DLZ zone, decide whether to allow this transfer.
925*00b67f09SDavid van Moolenbroek */
926*00b67f09SDavid van Moolenbroek if (!is_dlz) {
927*00b67f09SDavid van Moolenbroek ns_client_aclmsg("zone transfer", question_name, reqtype,
928*00b67f09SDavid van Moolenbroek client->view->rdclass, msg, sizeof(msg));
929*00b67f09SDavid van Moolenbroek CHECK(ns_client_checkacl(client, NULL, msg,
930*00b67f09SDavid van Moolenbroek dns_zone_getxfracl(zone),
931*00b67f09SDavid van Moolenbroek ISC_TRUE, ISC_LOG_ERROR));
932*00b67f09SDavid van Moolenbroek }
933*00b67f09SDavid van Moolenbroek
934*00b67f09SDavid van Moolenbroek /*
935*00b67f09SDavid van Moolenbroek * AXFR over UDP is not possible.
936*00b67f09SDavid van Moolenbroek */
937*00b67f09SDavid van Moolenbroek if (reqtype == dns_rdatatype_axfr &&
938*00b67f09SDavid van Moolenbroek (client->attributes & NS_CLIENTATTR_TCP) == 0)
939*00b67f09SDavid van Moolenbroek FAILC(DNS_R_FORMERR, "attempted AXFR over UDP");
940*00b67f09SDavid van Moolenbroek
941*00b67f09SDavid van Moolenbroek /*
942*00b67f09SDavid van Moolenbroek * Look up the requesting server in the peer table.
943*00b67f09SDavid van Moolenbroek */
944*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&na, &client->peeraddr);
945*00b67f09SDavid van Moolenbroek (void)dns_peerlist_peerbyaddr(client->view->peers, &na, &peer);
946*00b67f09SDavid van Moolenbroek
947*00b67f09SDavid van Moolenbroek /*
948*00b67f09SDavid van Moolenbroek * Decide on the transfer format (one-answer or many-answers).
949*00b67f09SDavid van Moolenbroek */
950*00b67f09SDavid van Moolenbroek if (peer != NULL)
951*00b67f09SDavid van Moolenbroek (void)dns_peer_gettransferformat(peer, &format);
952*00b67f09SDavid van Moolenbroek
953*00b67f09SDavid van Moolenbroek /*
954*00b67f09SDavid van Moolenbroek * Get a dynamically allocated copy of the current SOA.
955*00b67f09SDavid van Moolenbroek */
956*00b67f09SDavid van Moolenbroek if (is_dlz)
957*00b67f09SDavid van Moolenbroek dns_db_currentversion(db, &ver);
958*00b67f09SDavid van Moolenbroek
959*00b67f09SDavid van Moolenbroek CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
960*00b67f09SDavid van Moolenbroek ¤t_soa_tuple));
961*00b67f09SDavid van Moolenbroek
962*00b67f09SDavid van Moolenbroek current_serial = dns_soa_getserial(¤t_soa_tuple->rdata);
963*00b67f09SDavid van Moolenbroek if (reqtype == dns_rdatatype_ixfr) {
964*00b67f09SDavid van Moolenbroek isc_boolean_t provide_ixfr;
965*00b67f09SDavid van Moolenbroek
966*00b67f09SDavid van Moolenbroek /*
967*00b67f09SDavid van Moolenbroek * Outgoing IXFR may have been disabled for this peer
968*00b67f09SDavid van Moolenbroek * or globally.
969*00b67f09SDavid van Moolenbroek */
970*00b67f09SDavid van Moolenbroek provide_ixfr = client->view->provideixfr;
971*00b67f09SDavid van Moolenbroek if (peer != NULL)
972*00b67f09SDavid van Moolenbroek (void) dns_peer_getprovideixfr(peer, &provide_ixfr);
973*00b67f09SDavid van Moolenbroek if (provide_ixfr == ISC_FALSE)
974*00b67f09SDavid van Moolenbroek goto axfr_fallback;
975*00b67f09SDavid van Moolenbroek
976*00b67f09SDavid van Moolenbroek if (! have_soa)
977*00b67f09SDavid van Moolenbroek FAILC(DNS_R_FORMERR,
978*00b67f09SDavid van Moolenbroek "IXFR request missing SOA");
979*00b67f09SDavid van Moolenbroek
980*00b67f09SDavid van Moolenbroek begin_serial = dns_soa_getserial(&soa_rdata);
981*00b67f09SDavid van Moolenbroek
982*00b67f09SDavid van Moolenbroek /*
983*00b67f09SDavid van Moolenbroek * RFC1995 says "If an IXFR query with the same or
984*00b67f09SDavid van Moolenbroek * newer version number than that of the server
985*00b67f09SDavid van Moolenbroek * is received, it is replied to with a single SOA
986*00b67f09SDavid van Moolenbroek * record of the server's current version, just as
987*00b67f09SDavid van Moolenbroek * in AXFR". The claim about AXFR is incorrect,
988*00b67f09SDavid van Moolenbroek * but other than that, we do as the RFC says.
989*00b67f09SDavid van Moolenbroek *
990*00b67f09SDavid van Moolenbroek * Sending a single SOA record is also how we refuse
991*00b67f09SDavid van Moolenbroek * IXFR over UDP (currently, we always do).
992*00b67f09SDavid van Moolenbroek */
993*00b67f09SDavid van Moolenbroek if (DNS_SERIAL_GE(begin_serial, current_serial) ||
994*00b67f09SDavid van Moolenbroek (client->attributes & NS_CLIENTATTR_TCP) == 0)
995*00b67f09SDavid van Moolenbroek {
996*00b67f09SDavid van Moolenbroek CHECK(soa_rrstream_create(mctx, db, ver, &stream));
997*00b67f09SDavid van Moolenbroek is_poll = ISC_TRUE;
998*00b67f09SDavid van Moolenbroek goto have_stream;
999*00b67f09SDavid van Moolenbroek }
1000*00b67f09SDavid van Moolenbroek journalfile = is_dlz ? NULL : dns_zone_getjournal(zone);
1001*00b67f09SDavid van Moolenbroek if (journalfile != NULL)
1002*00b67f09SDavid van Moolenbroek result = ixfr_rrstream_create(mctx,
1003*00b67f09SDavid van Moolenbroek journalfile,
1004*00b67f09SDavid van Moolenbroek begin_serial,
1005*00b67f09SDavid van Moolenbroek current_serial,
1006*00b67f09SDavid van Moolenbroek &data_stream);
1007*00b67f09SDavid van Moolenbroek else
1008*00b67f09SDavid van Moolenbroek result = ISC_R_NOTFOUND;
1009*00b67f09SDavid van Moolenbroek if (result == ISC_R_NOTFOUND ||
1010*00b67f09SDavid van Moolenbroek result == ISC_R_RANGE) {
1011*00b67f09SDavid van Moolenbroek xfrout_log1(client, question_name, question_class,
1012*00b67f09SDavid van Moolenbroek ISC_LOG_DEBUG(4),
1013*00b67f09SDavid van Moolenbroek "IXFR version not in journal, "
1014*00b67f09SDavid van Moolenbroek "falling back to AXFR");
1015*00b67f09SDavid van Moolenbroek mnemonic = "AXFR-style IXFR";
1016*00b67f09SDavid van Moolenbroek goto axfr_fallback;
1017*00b67f09SDavid van Moolenbroek }
1018*00b67f09SDavid van Moolenbroek CHECK(result);
1019*00b67f09SDavid van Moolenbroek is_ixfr = ISC_TRUE;
1020*00b67f09SDavid van Moolenbroek } else {
1021*00b67f09SDavid van Moolenbroek axfr_fallback:
1022*00b67f09SDavid van Moolenbroek CHECK(axfr_rrstream_create(mctx, db, ver, &data_stream));
1023*00b67f09SDavid van Moolenbroek }
1024*00b67f09SDavid van Moolenbroek
1025*00b67f09SDavid van Moolenbroek /*
1026*00b67f09SDavid van Moolenbroek * Bracket the data stream with SOAs.
1027*00b67f09SDavid van Moolenbroek */
1028*00b67f09SDavid van Moolenbroek CHECK(soa_rrstream_create(mctx, db, ver, &soa_stream));
1029*00b67f09SDavid van Moolenbroek CHECK(compound_rrstream_create(mctx, &soa_stream, &data_stream,
1030*00b67f09SDavid van Moolenbroek &stream));
1031*00b67f09SDavid van Moolenbroek soa_stream = NULL;
1032*00b67f09SDavid van Moolenbroek data_stream = NULL;
1033*00b67f09SDavid van Moolenbroek
1034*00b67f09SDavid van Moolenbroek have_stream:
1035*00b67f09SDavid van Moolenbroek CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf));
1036*00b67f09SDavid van Moolenbroek /*
1037*00b67f09SDavid van Moolenbroek * Create the xfrout context object. This transfers the ownership
1038*00b67f09SDavid van Moolenbroek * of "stream", "db", "ver", and "quota" to the xfrout context object.
1039*00b67f09SDavid van Moolenbroek */
1040*00b67f09SDavid van Moolenbroek
1041*00b67f09SDavid van Moolenbroek
1042*00b67f09SDavid van Moolenbroek
1043*00b67f09SDavid van Moolenbroek if (is_dlz)
1044*00b67f09SDavid van Moolenbroek CHECK(xfrout_ctx_create(mctx, client, request->id,
1045*00b67f09SDavid van Moolenbroek question_name, reqtype, question_class,
1046*00b67f09SDavid van Moolenbroek zone, db, ver, quota, stream,
1047*00b67f09SDavid van Moolenbroek dns_message_gettsigkey(request),
1048*00b67f09SDavid van Moolenbroek tsigbuf,
1049*00b67f09SDavid van Moolenbroek 3600,
1050*00b67f09SDavid van Moolenbroek 3600,
1051*00b67f09SDavid van Moolenbroek (format == dns_many_answers) ?
1052*00b67f09SDavid van Moolenbroek ISC_TRUE : ISC_FALSE,
1053*00b67f09SDavid van Moolenbroek &xfr));
1054*00b67f09SDavid van Moolenbroek else
1055*00b67f09SDavid van Moolenbroek CHECK(xfrout_ctx_create(mctx, client, request->id,
1056*00b67f09SDavid van Moolenbroek question_name, reqtype, question_class,
1057*00b67f09SDavid van Moolenbroek zone, db, ver, quota, stream,
1058*00b67f09SDavid van Moolenbroek dns_message_gettsigkey(request),
1059*00b67f09SDavid van Moolenbroek tsigbuf,
1060*00b67f09SDavid van Moolenbroek dns_zone_getmaxxfrout(zone),
1061*00b67f09SDavid van Moolenbroek dns_zone_getidleout(zone),
1062*00b67f09SDavid van Moolenbroek (format == dns_many_answers) ?
1063*00b67f09SDavid van Moolenbroek ISC_TRUE : ISC_FALSE,
1064*00b67f09SDavid van Moolenbroek &xfr));
1065*00b67f09SDavid van Moolenbroek
1066*00b67f09SDavid van Moolenbroek xfr->mnemonic = mnemonic;
1067*00b67f09SDavid van Moolenbroek stream = NULL;
1068*00b67f09SDavid van Moolenbroek quota = NULL;
1069*00b67f09SDavid van Moolenbroek
1070*00b67f09SDavid van Moolenbroek CHECK(xfr->stream->methods->first(xfr->stream));
1071*00b67f09SDavid van Moolenbroek
1072*00b67f09SDavid van Moolenbroek if (xfr->tsigkey != NULL)
1073*00b67f09SDavid van Moolenbroek dns_name_format(&xfr->tsigkey->name, keyname, sizeof(keyname));
1074*00b67f09SDavid van Moolenbroek else
1075*00b67f09SDavid van Moolenbroek keyname[0] = '\0';
1076*00b67f09SDavid van Moolenbroek if (is_poll)
1077*00b67f09SDavid van Moolenbroek xfrout_log1(client, question_name, question_class,
1078*00b67f09SDavid van Moolenbroek ISC_LOG_DEBUG(1), "IXFR poll up to date%s%s",
1079*00b67f09SDavid van Moolenbroek (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname);
1080*00b67f09SDavid van Moolenbroek else if (is_ixfr)
1081*00b67f09SDavid van Moolenbroek xfrout_log1(client, question_name, question_class,
1082*00b67f09SDavid van Moolenbroek ISC_LOG_INFO, "%s started%s%s (serial %u -> %u)",
1083*00b67f09SDavid van Moolenbroek mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "",
1084*00b67f09SDavid van Moolenbroek keyname, begin_serial, current_serial);
1085*00b67f09SDavid van Moolenbroek else
1086*00b67f09SDavid van Moolenbroek xfrout_log1(client, question_name, question_class,
1087*00b67f09SDavid van Moolenbroek ISC_LOG_INFO, "%s started%s%s (serial %u)",
1088*00b67f09SDavid van Moolenbroek mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "",
1089*00b67f09SDavid van Moolenbroek keyname, current_serial);
1090*00b67f09SDavid van Moolenbroek
1091*00b67f09SDavid van Moolenbroek
1092*00b67f09SDavid van Moolenbroek if (zone != NULL) {
1093*00b67f09SDavid van Moolenbroek dns_zone_getraw(zone, &raw);
1094*00b67f09SDavid van Moolenbroek mayberaw = (raw != NULL) ? raw : zone;
1095*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0 &&
1096*00b67f09SDavid van Moolenbroek dns_zone_gettype(mayberaw) == dns_zone_slave) {
1097*00b67f09SDavid van Moolenbroek isc_time_t expiretime;
1098*00b67f09SDavid van Moolenbroek isc_uint32_t secs;
1099*00b67f09SDavid van Moolenbroek dns_zone_getexpiretime(zone, &expiretime);
1100*00b67f09SDavid van Moolenbroek secs = isc_time_seconds(&expiretime);
1101*00b67f09SDavid van Moolenbroek if (secs >= client->now && result == ISC_R_SUCCESS) {
1102*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_HAVEEXPIRE;
1103*00b67f09SDavid van Moolenbroek client->expire = secs - client->now;
1104*00b67f09SDavid van Moolenbroek }
1105*00b67f09SDavid van Moolenbroek }
1106*00b67f09SDavid van Moolenbroek if (raw != NULL)
1107*00b67f09SDavid van Moolenbroek dns_zone_detach(&raw);
1108*00b67f09SDavid van Moolenbroek }
1109*00b67f09SDavid van Moolenbroek
1110*00b67f09SDavid van Moolenbroek /*
1111*00b67f09SDavid van Moolenbroek * Hand the context over to sendstream(). Set xfr to NULL;
1112*00b67f09SDavid van Moolenbroek * sendstream() is responsible for either passing the
1113*00b67f09SDavid van Moolenbroek * context on to a later event handler or destroying it.
1114*00b67f09SDavid van Moolenbroek */
1115*00b67f09SDavid van Moolenbroek sendstream(xfr);
1116*00b67f09SDavid van Moolenbroek xfr = NULL;
1117*00b67f09SDavid van Moolenbroek
1118*00b67f09SDavid van Moolenbroek result = ISC_R_SUCCESS;
1119*00b67f09SDavid van Moolenbroek
1120*00b67f09SDavid van Moolenbroek failure:
1121*00b67f09SDavid van Moolenbroek if (result == DNS_R_REFUSED)
1122*00b67f09SDavid van Moolenbroek inc_stats(zone, dns_nsstatscounter_xfrrej);
1123*00b67f09SDavid van Moolenbroek if (quota != NULL)
1124*00b67f09SDavid van Moolenbroek isc_quota_detach("a);
1125*00b67f09SDavid van Moolenbroek if (current_soa_tuple != NULL)
1126*00b67f09SDavid van Moolenbroek dns_difftuple_free(¤t_soa_tuple);
1127*00b67f09SDavid van Moolenbroek if (stream != NULL)
1128*00b67f09SDavid van Moolenbroek stream->methods->destroy(&stream);
1129*00b67f09SDavid van Moolenbroek if (soa_stream != NULL)
1130*00b67f09SDavid van Moolenbroek soa_stream->methods->destroy(&soa_stream);
1131*00b67f09SDavid van Moolenbroek if (data_stream != NULL)
1132*00b67f09SDavid van Moolenbroek data_stream->methods->destroy(&data_stream);
1133*00b67f09SDavid van Moolenbroek if (ver != NULL)
1134*00b67f09SDavid van Moolenbroek dns_db_closeversion(db, &ver, ISC_FALSE);
1135*00b67f09SDavid van Moolenbroek if (db != NULL)
1136*00b67f09SDavid van Moolenbroek dns_db_detach(&db);
1137*00b67f09SDavid van Moolenbroek if (zone != NULL)
1138*00b67f09SDavid van Moolenbroek dns_zone_detach(&zone);
1139*00b67f09SDavid van Moolenbroek /* XXX kludge */
1140*00b67f09SDavid van Moolenbroek if (xfr != NULL) {
1141*00b67f09SDavid van Moolenbroek xfrout_fail(xfr, result, "setting up zone transfer");
1142*00b67f09SDavid van Moolenbroek } else if (result != ISC_R_SUCCESS) {
1143*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT,
1144*00b67f09SDavid van Moolenbroek NS_LOGMODULE_XFER_OUT,
1145*00b67f09SDavid van Moolenbroek ISC_LOG_DEBUG(3), "zone transfer setup failed");
1146*00b67f09SDavid van Moolenbroek ns_client_error(client, result);
1147*00b67f09SDavid van Moolenbroek }
1148*00b67f09SDavid van Moolenbroek }
1149*00b67f09SDavid van Moolenbroek
1150*00b67f09SDavid van Moolenbroek static isc_result_t
xfrout_ctx_create(isc_mem_t * mctx,ns_client_t * client,unsigned int id,dns_name_t * qname,dns_rdatatype_t qtype,dns_rdataclass_t qclass,dns_zone_t * zone,dns_db_t * db,dns_dbversion_t * ver,isc_quota_t * quota,rrstream_t * stream,dns_tsigkey_t * tsigkey,isc_buffer_t * lasttsig,unsigned int maxtime,unsigned int idletime,isc_boolean_t many_answers,xfrout_ctx_t ** xfrp)1151*00b67f09SDavid van Moolenbroek xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
1152*00b67f09SDavid van Moolenbroek dns_name_t *qname, dns_rdatatype_t qtype,
1153*00b67f09SDavid van Moolenbroek dns_rdataclass_t qclass, dns_zone_t *zone,
1154*00b67f09SDavid van Moolenbroek dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
1155*00b67f09SDavid van Moolenbroek rrstream_t *stream, dns_tsigkey_t *tsigkey,
1156*00b67f09SDavid van Moolenbroek isc_buffer_t *lasttsig, unsigned int maxtime,
1157*00b67f09SDavid van Moolenbroek unsigned int idletime, isc_boolean_t many_answers,
1158*00b67f09SDavid van Moolenbroek xfrout_ctx_t **xfrp)
1159*00b67f09SDavid van Moolenbroek {
1160*00b67f09SDavid van Moolenbroek xfrout_ctx_t *xfr;
1161*00b67f09SDavid van Moolenbroek isc_result_t result;
1162*00b67f09SDavid van Moolenbroek unsigned int len;
1163*00b67f09SDavid van Moolenbroek void *mem;
1164*00b67f09SDavid van Moolenbroek
1165*00b67f09SDavid van Moolenbroek INSIST(xfrp != NULL && *xfrp == NULL);
1166*00b67f09SDavid van Moolenbroek xfr = isc_mem_get(mctx, sizeof(*xfr));
1167*00b67f09SDavid van Moolenbroek if (xfr == NULL)
1168*00b67f09SDavid van Moolenbroek return (ISC_R_NOMEMORY);
1169*00b67f09SDavid van Moolenbroek xfr->mctx = NULL;
1170*00b67f09SDavid van Moolenbroek isc_mem_attach(mctx, &xfr->mctx);
1171*00b67f09SDavid van Moolenbroek xfr->client = NULL;
1172*00b67f09SDavid van Moolenbroek ns_client_attach(client, &xfr->client);
1173*00b67f09SDavid van Moolenbroek xfr->id = id;
1174*00b67f09SDavid van Moolenbroek xfr->qname = qname;
1175*00b67f09SDavid van Moolenbroek xfr->qtype = qtype;
1176*00b67f09SDavid van Moolenbroek xfr->qclass = qclass;
1177*00b67f09SDavid van Moolenbroek xfr->zone = NULL;
1178*00b67f09SDavid van Moolenbroek xfr->db = NULL;
1179*00b67f09SDavid van Moolenbroek xfr->ver = NULL;
1180*00b67f09SDavid van Moolenbroek if (zone != NULL) /* zone will be NULL if it's DLZ */
1181*00b67f09SDavid van Moolenbroek dns_zone_attach(zone, &xfr->zone);
1182*00b67f09SDavid van Moolenbroek dns_db_attach(db, &xfr->db);
1183*00b67f09SDavid van Moolenbroek dns_db_attachversion(db, ver, &xfr->ver);
1184*00b67f09SDavid van Moolenbroek xfr->end_of_stream = ISC_FALSE;
1185*00b67f09SDavid van Moolenbroek xfr->tsigkey = tsigkey;
1186*00b67f09SDavid van Moolenbroek xfr->lasttsig = lasttsig;
1187*00b67f09SDavid van Moolenbroek xfr->txmem = NULL;
1188*00b67f09SDavid van Moolenbroek xfr->txmemlen = 0;
1189*00b67f09SDavid van Moolenbroek xfr->nmsg = 0;
1190*00b67f09SDavid van Moolenbroek xfr->many_answers = many_answers,
1191*00b67f09SDavid van Moolenbroek xfr->sends = 0;
1192*00b67f09SDavid van Moolenbroek xfr->shuttingdown = ISC_FALSE;
1193*00b67f09SDavid van Moolenbroek xfr->mnemonic = NULL;
1194*00b67f09SDavid van Moolenbroek xfr->buf.base = NULL;
1195*00b67f09SDavid van Moolenbroek xfr->buf.length = 0;
1196*00b67f09SDavid van Moolenbroek xfr->txmem = NULL;
1197*00b67f09SDavid van Moolenbroek xfr->txmemlen = 0;
1198*00b67f09SDavid van Moolenbroek xfr->stream = NULL;
1199*00b67f09SDavid van Moolenbroek xfr->quota = NULL;
1200*00b67f09SDavid van Moolenbroek
1201*00b67f09SDavid van Moolenbroek /*
1202*00b67f09SDavid van Moolenbroek * Allocate a temporary buffer for the uncompressed response
1203*00b67f09SDavid van Moolenbroek * message data. The size should be no more than 65535 bytes
1204*00b67f09SDavid van Moolenbroek * so that the compressed data will fit in a TCP message,
1205*00b67f09SDavid van Moolenbroek * and no less than 65535 bytes so that an almost maximum-sized
1206*00b67f09SDavid van Moolenbroek * RR will fit. Note that although 65535-byte RRs are allowed
1207*00b67f09SDavid van Moolenbroek * in principle, they cannot be zone-transferred (at least not
1208*00b67f09SDavid van Moolenbroek * if uncompressible), because the message and RR headers would
1209*00b67f09SDavid van Moolenbroek * push the size of the TCP message over the 65536 byte limit.
1210*00b67f09SDavid van Moolenbroek */
1211*00b67f09SDavid van Moolenbroek len = 65535;
1212*00b67f09SDavid van Moolenbroek mem = isc_mem_get(mctx, len);
1213*00b67f09SDavid van Moolenbroek if (mem == NULL) {
1214*00b67f09SDavid van Moolenbroek result = ISC_R_NOMEMORY;
1215*00b67f09SDavid van Moolenbroek goto failure;
1216*00b67f09SDavid van Moolenbroek }
1217*00b67f09SDavid van Moolenbroek isc_buffer_init(&xfr->buf, mem, len);
1218*00b67f09SDavid van Moolenbroek
1219*00b67f09SDavid van Moolenbroek /*
1220*00b67f09SDavid van Moolenbroek * Allocate another temporary buffer for the compressed
1221*00b67f09SDavid van Moolenbroek * response message and its TCP length prefix.
1222*00b67f09SDavid van Moolenbroek */
1223*00b67f09SDavid van Moolenbroek len = 2 + 65535;
1224*00b67f09SDavid van Moolenbroek mem = isc_mem_get(mctx, len);
1225*00b67f09SDavid van Moolenbroek if (mem == NULL) {
1226*00b67f09SDavid van Moolenbroek result = ISC_R_NOMEMORY;
1227*00b67f09SDavid van Moolenbroek goto failure;
1228*00b67f09SDavid van Moolenbroek }
1229*00b67f09SDavid van Moolenbroek isc_buffer_init(&xfr->txlenbuf, mem, 2);
1230*00b67f09SDavid van Moolenbroek isc_buffer_init(&xfr->txbuf, (char *) mem + 2, len - 2);
1231*00b67f09SDavid van Moolenbroek xfr->txmem = mem;
1232*00b67f09SDavid van Moolenbroek xfr->txmemlen = len;
1233*00b67f09SDavid van Moolenbroek
1234*00b67f09SDavid van Moolenbroek CHECK(dns_timer_setidle(xfr->client->timer,
1235*00b67f09SDavid van Moolenbroek maxtime, idletime, ISC_FALSE));
1236*00b67f09SDavid van Moolenbroek
1237*00b67f09SDavid van Moolenbroek /*
1238*00b67f09SDavid van Moolenbroek * Register a shutdown callback with the client, so that we
1239*00b67f09SDavid van Moolenbroek * can stop the transfer immediately when the client task
1240*00b67f09SDavid van Moolenbroek * gets a shutdown event.
1241*00b67f09SDavid van Moolenbroek */
1242*00b67f09SDavid van Moolenbroek xfr->client->shutdown = xfrout_client_shutdown;
1243*00b67f09SDavid van Moolenbroek xfr->client->shutdown_arg = xfr;
1244*00b67f09SDavid van Moolenbroek /*
1245*00b67f09SDavid van Moolenbroek * These MUST be after the last "goto failure;" / CHECK to
1246*00b67f09SDavid van Moolenbroek * prevent a double free by the caller.
1247*00b67f09SDavid van Moolenbroek */
1248*00b67f09SDavid van Moolenbroek xfr->quota = quota;
1249*00b67f09SDavid van Moolenbroek xfr->stream = stream;
1250*00b67f09SDavid van Moolenbroek
1251*00b67f09SDavid van Moolenbroek *xfrp = xfr;
1252*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
1253*00b67f09SDavid van Moolenbroek
1254*00b67f09SDavid van Moolenbroek failure:
1255*00b67f09SDavid van Moolenbroek xfrout_ctx_destroy(&xfr);
1256*00b67f09SDavid van Moolenbroek return (result);
1257*00b67f09SDavid van Moolenbroek }
1258*00b67f09SDavid van Moolenbroek
1259*00b67f09SDavid van Moolenbroek
1260*00b67f09SDavid van Moolenbroek /*
1261*00b67f09SDavid van Moolenbroek * Arrange to send as much as we can of "stream" without blocking.
1262*00b67f09SDavid van Moolenbroek *
1263*00b67f09SDavid van Moolenbroek * Requires:
1264*00b67f09SDavid van Moolenbroek * The stream iterator is initialized and points at an RR,
1265*00b67f09SDavid van Moolenbroek * or possibly at the end of the stream (that is, the
1266*00b67f09SDavid van Moolenbroek * _first method of the iterator has been called).
1267*00b67f09SDavid van Moolenbroek */
1268*00b67f09SDavid van Moolenbroek static void
sendstream(xfrout_ctx_t * xfr)1269*00b67f09SDavid van Moolenbroek sendstream(xfrout_ctx_t *xfr) {
1270*00b67f09SDavid van Moolenbroek dns_message_t *tcpmsg = NULL;
1271*00b67f09SDavid van Moolenbroek dns_message_t *msg = NULL; /* Client message if UDP, tcpmsg if TCP */
1272*00b67f09SDavid van Moolenbroek isc_result_t result;
1273*00b67f09SDavid van Moolenbroek isc_region_t used;
1274*00b67f09SDavid van Moolenbroek isc_region_t region;
1275*00b67f09SDavid van Moolenbroek dns_rdataset_t *qrdataset;
1276*00b67f09SDavid van Moolenbroek dns_name_t *msgname = NULL;
1277*00b67f09SDavid van Moolenbroek dns_rdata_t *msgrdata = NULL;
1278*00b67f09SDavid van Moolenbroek dns_rdatalist_t *msgrdl = NULL;
1279*00b67f09SDavid van Moolenbroek dns_rdataset_t *msgrds = NULL;
1280*00b67f09SDavid van Moolenbroek dns_compress_t cctx;
1281*00b67f09SDavid van Moolenbroek isc_boolean_t cleanup_cctx = ISC_FALSE;
1282*00b67f09SDavid van Moolenbroek
1283*00b67f09SDavid van Moolenbroek int n_rrs;
1284*00b67f09SDavid van Moolenbroek
1285*00b67f09SDavid van Moolenbroek isc_buffer_clear(&xfr->buf);
1286*00b67f09SDavid van Moolenbroek isc_buffer_clear(&xfr->txlenbuf);
1287*00b67f09SDavid van Moolenbroek isc_buffer_clear(&xfr->txbuf);
1288*00b67f09SDavid van Moolenbroek
1289*00b67f09SDavid van Moolenbroek if ((xfr->client->attributes & NS_CLIENTATTR_TCP) == 0) {
1290*00b67f09SDavid van Moolenbroek /*
1291*00b67f09SDavid van Moolenbroek * In the UDP case, we put the response data directly into
1292*00b67f09SDavid van Moolenbroek * the client message.
1293*00b67f09SDavid van Moolenbroek */
1294*00b67f09SDavid van Moolenbroek msg = xfr->client->message;
1295*00b67f09SDavid van Moolenbroek CHECK(dns_message_reply(msg, ISC_TRUE));
1296*00b67f09SDavid van Moolenbroek } else {
1297*00b67f09SDavid van Moolenbroek /*
1298*00b67f09SDavid van Moolenbroek * TCP. Build a response dns_message_t, temporarily storing
1299*00b67f09SDavid van Moolenbroek * the raw, uncompressed owner names and RR data contiguously
1300*00b67f09SDavid van Moolenbroek * in xfr->buf. We know that if the uncompressed data fits
1301*00b67f09SDavid van Moolenbroek * in xfr->buf, the compressed data will surely fit in a TCP
1302*00b67f09SDavid van Moolenbroek * message.
1303*00b67f09SDavid van Moolenbroek */
1304*00b67f09SDavid van Moolenbroek
1305*00b67f09SDavid van Moolenbroek CHECK(dns_message_create(xfr->mctx,
1306*00b67f09SDavid van Moolenbroek DNS_MESSAGE_INTENTRENDER, &tcpmsg));
1307*00b67f09SDavid van Moolenbroek msg = tcpmsg;
1308*00b67f09SDavid van Moolenbroek
1309*00b67f09SDavid van Moolenbroek msg->id = xfr->id;
1310*00b67f09SDavid van Moolenbroek msg->rcode = dns_rcode_noerror;
1311*00b67f09SDavid van Moolenbroek msg->flags = DNS_MESSAGEFLAG_QR | DNS_MESSAGEFLAG_AA;
1312*00b67f09SDavid van Moolenbroek if ((xfr->client->attributes & NS_CLIENTATTR_RA) != 0)
1313*00b67f09SDavid van Moolenbroek msg->flags |= DNS_MESSAGEFLAG_RA;
1314*00b67f09SDavid van Moolenbroek CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
1315*00b67f09SDavid van Moolenbroek CHECK(dns_message_setquerytsig(msg, xfr->lasttsig));
1316*00b67f09SDavid van Moolenbroek if (xfr->lasttsig != NULL)
1317*00b67f09SDavid van Moolenbroek isc_buffer_free(&xfr->lasttsig);
1318*00b67f09SDavid van Moolenbroek
1319*00b67f09SDavid van Moolenbroek /*
1320*00b67f09SDavid van Moolenbroek * Add a EDNS option to the message?
1321*00b67f09SDavid van Moolenbroek */
1322*00b67f09SDavid van Moolenbroek if ((xfr->client->attributes & NS_CLIENTATTR_WANTOPT) != 0) {
1323*00b67f09SDavid van Moolenbroek dns_rdataset_t *opt = NULL;
1324*00b67f09SDavid van Moolenbroek
1325*00b67f09SDavid van Moolenbroek CHECK(ns_client_addopt(xfr->client, msg, &opt));
1326*00b67f09SDavid van Moolenbroek CHECK(dns_message_setopt(msg, opt));
1327*00b67f09SDavid van Moolenbroek /*
1328*00b67f09SDavid van Moolenbroek * Add to first message only.
1329*00b67f09SDavid van Moolenbroek */
1330*00b67f09SDavid van Moolenbroek xfr->client->attributes &= ~NS_CLIENTATTR_WANTNSID;
1331*00b67f09SDavid van Moolenbroek xfr->client->attributes &= ~NS_CLIENTATTR_HAVEEXPIRE;
1332*00b67f09SDavid van Moolenbroek }
1333*00b67f09SDavid van Moolenbroek
1334*00b67f09SDavid van Moolenbroek /*
1335*00b67f09SDavid van Moolenbroek * Account for reserved space.
1336*00b67f09SDavid van Moolenbroek */
1337*00b67f09SDavid van Moolenbroek if (xfr->tsigkey != NULL)
1338*00b67f09SDavid van Moolenbroek INSIST(msg->reserved != 0U);
1339*00b67f09SDavid van Moolenbroek isc_buffer_add(&xfr->buf, msg->reserved);
1340*00b67f09SDavid van Moolenbroek
1341*00b67f09SDavid van Moolenbroek /*
1342*00b67f09SDavid van Moolenbroek * Include a question section in the first message only.
1343*00b67f09SDavid van Moolenbroek * BIND 8.2.1 will not recognize an IXFR if it does not
1344*00b67f09SDavid van Moolenbroek * have a question section.
1345*00b67f09SDavid van Moolenbroek */
1346*00b67f09SDavid van Moolenbroek if (xfr->nmsg == 0) {
1347*00b67f09SDavid van Moolenbroek dns_name_t *qname = NULL;
1348*00b67f09SDavid van Moolenbroek isc_region_t r;
1349*00b67f09SDavid van Moolenbroek
1350*00b67f09SDavid van Moolenbroek /*
1351*00b67f09SDavid van Moolenbroek * Reserve space for the 12-byte message header
1352*00b67f09SDavid van Moolenbroek * and 4 bytes of question.
1353*00b67f09SDavid van Moolenbroek */
1354*00b67f09SDavid van Moolenbroek isc_buffer_add(&xfr->buf, 12 + 4);
1355*00b67f09SDavid van Moolenbroek
1356*00b67f09SDavid van Moolenbroek qrdataset = NULL;
1357*00b67f09SDavid van Moolenbroek result = dns_message_gettemprdataset(msg, &qrdataset);
1358*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1359*00b67f09SDavid van Moolenbroek goto failure;
1360*00b67f09SDavid van Moolenbroek dns_rdataset_makequestion(qrdataset,
1361*00b67f09SDavid van Moolenbroek xfr->client->message->rdclass,
1362*00b67f09SDavid van Moolenbroek xfr->qtype);
1363*00b67f09SDavid van Moolenbroek
1364*00b67f09SDavid van Moolenbroek result = dns_message_gettempname(msg, &qname);
1365*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1366*00b67f09SDavid van Moolenbroek goto failure;
1367*00b67f09SDavid van Moolenbroek dns_name_init(qname, NULL);
1368*00b67f09SDavid van Moolenbroek isc_buffer_availableregion(&xfr->buf, &r);
1369*00b67f09SDavid van Moolenbroek INSIST(r.length >= xfr->qname->length);
1370*00b67f09SDavid van Moolenbroek r.length = xfr->qname->length;
1371*00b67f09SDavid van Moolenbroek isc_buffer_putmem(&xfr->buf, xfr->qname->ndata,
1372*00b67f09SDavid van Moolenbroek xfr->qname->length);
1373*00b67f09SDavid van Moolenbroek dns_name_fromregion(qname, &r);
1374*00b67f09SDavid van Moolenbroek ISC_LIST_INIT(qname->list);
1375*00b67f09SDavid van Moolenbroek ISC_LIST_APPEND(qname->list, qrdataset, link);
1376*00b67f09SDavid van Moolenbroek
1377*00b67f09SDavid van Moolenbroek dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
1378*00b67f09SDavid van Moolenbroek } else {
1379*00b67f09SDavid van Moolenbroek /*
1380*00b67f09SDavid van Moolenbroek * Reserve space for the 12-byte message header
1381*00b67f09SDavid van Moolenbroek */
1382*00b67f09SDavid van Moolenbroek isc_buffer_add(&xfr->buf, 12);
1383*00b67f09SDavid van Moolenbroek msg->tcp_continuation = 1;
1384*00b67f09SDavid van Moolenbroek }
1385*00b67f09SDavid van Moolenbroek }
1386*00b67f09SDavid van Moolenbroek
1387*00b67f09SDavid van Moolenbroek /*
1388*00b67f09SDavid van Moolenbroek * Try to fit in as many RRs as possible, unless "one-answer"
1389*00b67f09SDavid van Moolenbroek * format has been requested.
1390*00b67f09SDavid van Moolenbroek */
1391*00b67f09SDavid van Moolenbroek for (n_rrs = 0; ; n_rrs++) {
1392*00b67f09SDavid van Moolenbroek dns_name_t *name = NULL;
1393*00b67f09SDavid van Moolenbroek isc_uint32_t ttl;
1394*00b67f09SDavid van Moolenbroek dns_rdata_t *rdata = NULL;
1395*00b67f09SDavid van Moolenbroek
1396*00b67f09SDavid van Moolenbroek unsigned int size;
1397*00b67f09SDavid van Moolenbroek isc_region_t r;
1398*00b67f09SDavid van Moolenbroek
1399*00b67f09SDavid van Moolenbroek msgname = NULL;
1400*00b67f09SDavid van Moolenbroek msgrdata = NULL;
1401*00b67f09SDavid van Moolenbroek msgrdl = NULL;
1402*00b67f09SDavid van Moolenbroek msgrds = NULL;
1403*00b67f09SDavid van Moolenbroek
1404*00b67f09SDavid van Moolenbroek xfr->stream->methods->current(xfr->stream,
1405*00b67f09SDavid van Moolenbroek &name, &ttl, &rdata);
1406*00b67f09SDavid van Moolenbroek size = name->length + 10 + rdata->length;
1407*00b67f09SDavid van Moolenbroek isc_buffer_availableregion(&xfr->buf, &r);
1408*00b67f09SDavid van Moolenbroek if (size >= r.length) {
1409*00b67f09SDavid van Moolenbroek /*
1410*00b67f09SDavid van Moolenbroek * RR would not fit. If there are other RRs in the
1411*00b67f09SDavid van Moolenbroek * buffer, send them now and leave this RR to the
1412*00b67f09SDavid van Moolenbroek * next message. If this RR overflows the buffer
1413*00b67f09SDavid van Moolenbroek * all by itself, fail.
1414*00b67f09SDavid van Moolenbroek *
1415*00b67f09SDavid van Moolenbroek * In theory some RRs might fit in a TCP message
1416*00b67f09SDavid van Moolenbroek * when compressed even if they do not fit when
1417*00b67f09SDavid van Moolenbroek * uncompressed, but surely we don't want
1418*00b67f09SDavid van Moolenbroek * to send such monstrosities to an unsuspecting
1419*00b67f09SDavid van Moolenbroek * slave.
1420*00b67f09SDavid van Moolenbroek */
1421*00b67f09SDavid van Moolenbroek if (n_rrs == 0) {
1422*00b67f09SDavid van Moolenbroek xfrout_log(xfr, ISC_LOG_WARNING,
1423*00b67f09SDavid van Moolenbroek "RR too large for zone transfer "
1424*00b67f09SDavid van Moolenbroek "(%d bytes)", size);
1425*00b67f09SDavid van Moolenbroek /* XXX DNS_R_RRTOOLARGE? */
1426*00b67f09SDavid van Moolenbroek result = ISC_R_NOSPACE;
1427*00b67f09SDavid van Moolenbroek goto failure;
1428*00b67f09SDavid van Moolenbroek }
1429*00b67f09SDavid van Moolenbroek break;
1430*00b67f09SDavid van Moolenbroek }
1431*00b67f09SDavid van Moolenbroek
1432*00b67f09SDavid van Moolenbroek if (isc_log_wouldlog(ns_g_lctx, XFROUT_RR_LOGLEVEL))
1433*00b67f09SDavid van Moolenbroek log_rr(name, rdata, ttl); /* XXX */
1434*00b67f09SDavid van Moolenbroek
1435*00b67f09SDavid van Moolenbroek result = dns_message_gettempname(msg, &msgname);
1436*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1437*00b67f09SDavid van Moolenbroek goto failure;
1438*00b67f09SDavid van Moolenbroek dns_name_init(msgname, NULL);
1439*00b67f09SDavid van Moolenbroek isc_buffer_availableregion(&xfr->buf, &r);
1440*00b67f09SDavid van Moolenbroek INSIST(r.length >= name->length);
1441*00b67f09SDavid van Moolenbroek r.length = name->length;
1442*00b67f09SDavid van Moolenbroek isc_buffer_putmem(&xfr->buf, name->ndata, name->length);
1443*00b67f09SDavid van Moolenbroek dns_name_fromregion(msgname, &r);
1444*00b67f09SDavid van Moolenbroek
1445*00b67f09SDavid van Moolenbroek /* Reserve space for RR header. */
1446*00b67f09SDavid van Moolenbroek isc_buffer_add(&xfr->buf, 10);
1447*00b67f09SDavid van Moolenbroek
1448*00b67f09SDavid van Moolenbroek result = dns_message_gettemprdata(msg, &msgrdata);
1449*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1450*00b67f09SDavid van Moolenbroek goto failure;
1451*00b67f09SDavid van Moolenbroek isc_buffer_availableregion(&xfr->buf, &r);
1452*00b67f09SDavid van Moolenbroek r.length = rdata->length;
1453*00b67f09SDavid van Moolenbroek isc_buffer_putmem(&xfr->buf, rdata->data, rdata->length);
1454*00b67f09SDavid van Moolenbroek dns_rdata_init(msgrdata);
1455*00b67f09SDavid van Moolenbroek dns_rdata_fromregion(msgrdata,
1456*00b67f09SDavid van Moolenbroek rdata->rdclass, rdata->type, &r);
1457*00b67f09SDavid van Moolenbroek
1458*00b67f09SDavid van Moolenbroek result = dns_message_gettemprdatalist(msg, &msgrdl);
1459*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1460*00b67f09SDavid van Moolenbroek goto failure;
1461*00b67f09SDavid van Moolenbroek msgrdl->type = rdata->type;
1462*00b67f09SDavid van Moolenbroek msgrdl->rdclass = rdata->rdclass;
1463*00b67f09SDavid van Moolenbroek msgrdl->ttl = ttl;
1464*00b67f09SDavid van Moolenbroek if (rdata->type == dns_rdatatype_sig ||
1465*00b67f09SDavid van Moolenbroek rdata->type == dns_rdatatype_rrsig)
1466*00b67f09SDavid van Moolenbroek msgrdl->covers = dns_rdata_covers(rdata);
1467*00b67f09SDavid van Moolenbroek else
1468*00b67f09SDavid van Moolenbroek msgrdl->covers = dns_rdatatype_none;
1469*00b67f09SDavid van Moolenbroek ISC_LINK_INIT(msgrdl, link);
1470*00b67f09SDavid van Moolenbroek ISC_LIST_INIT(msgrdl->rdata);
1471*00b67f09SDavid van Moolenbroek ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link);
1472*00b67f09SDavid van Moolenbroek
1473*00b67f09SDavid van Moolenbroek result = dns_message_gettemprdataset(msg, &msgrds);
1474*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1475*00b67f09SDavid van Moolenbroek goto failure;
1476*00b67f09SDavid van Moolenbroek result = dns_rdatalist_tordataset(msgrdl, msgrds);
1477*00b67f09SDavid van Moolenbroek INSIST(result == ISC_R_SUCCESS);
1478*00b67f09SDavid van Moolenbroek
1479*00b67f09SDavid van Moolenbroek ISC_LIST_APPEND(msgname->list, msgrds, link);
1480*00b67f09SDavid van Moolenbroek
1481*00b67f09SDavid van Moolenbroek dns_message_addname(msg, msgname, DNS_SECTION_ANSWER);
1482*00b67f09SDavid van Moolenbroek msgname = NULL;
1483*00b67f09SDavid van Moolenbroek
1484*00b67f09SDavid van Moolenbroek result = xfr->stream->methods->next(xfr->stream);
1485*00b67f09SDavid van Moolenbroek if (result == ISC_R_NOMORE) {
1486*00b67f09SDavid van Moolenbroek xfr->end_of_stream = ISC_TRUE;
1487*00b67f09SDavid van Moolenbroek break;
1488*00b67f09SDavid van Moolenbroek }
1489*00b67f09SDavid van Moolenbroek CHECK(result);
1490*00b67f09SDavid van Moolenbroek
1491*00b67f09SDavid van Moolenbroek if (! xfr->many_answers)
1492*00b67f09SDavid van Moolenbroek break;
1493*00b67f09SDavid van Moolenbroek }
1494*00b67f09SDavid van Moolenbroek
1495*00b67f09SDavid van Moolenbroek if ((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0) {
1496*00b67f09SDavid van Moolenbroek CHECK(dns_compress_init(&cctx, -1, xfr->mctx));
1497*00b67f09SDavid van Moolenbroek dns_compress_setsensitive(&cctx, ISC_TRUE);
1498*00b67f09SDavid van Moolenbroek cleanup_cctx = ISC_TRUE;
1499*00b67f09SDavid van Moolenbroek CHECK(dns_message_renderbegin(msg, &cctx, &xfr->txbuf));
1500*00b67f09SDavid van Moolenbroek CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0));
1501*00b67f09SDavid van Moolenbroek CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0));
1502*00b67f09SDavid van Moolenbroek CHECK(dns_message_renderend(msg));
1503*00b67f09SDavid van Moolenbroek dns_compress_invalidate(&cctx);
1504*00b67f09SDavid van Moolenbroek cleanup_cctx = ISC_FALSE;
1505*00b67f09SDavid van Moolenbroek
1506*00b67f09SDavid van Moolenbroek isc_buffer_usedregion(&xfr->txbuf, &used);
1507*00b67f09SDavid van Moolenbroek isc_buffer_putuint16(&xfr->txlenbuf,
1508*00b67f09SDavid van Moolenbroek (isc_uint16_t)used.length);
1509*00b67f09SDavid van Moolenbroek region.base = xfr->txlenbuf.base;
1510*00b67f09SDavid van Moolenbroek region.length = 2 + used.length;
1511*00b67f09SDavid van Moolenbroek xfrout_log(xfr, ISC_LOG_DEBUG(8),
1512*00b67f09SDavid van Moolenbroek "sending TCP message of %d bytes",
1513*00b67f09SDavid van Moolenbroek used.length);
1514*00b67f09SDavid van Moolenbroek CHECK(isc_socket_send(xfr->client->tcpsocket, /* XXX */
1515*00b67f09SDavid van Moolenbroek ®ion, xfr->client->task,
1516*00b67f09SDavid van Moolenbroek xfrout_senddone,
1517*00b67f09SDavid van Moolenbroek xfr));
1518*00b67f09SDavid van Moolenbroek xfr->sends++;
1519*00b67f09SDavid van Moolenbroek } else {
1520*00b67f09SDavid van Moolenbroek xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending IXFR UDP response");
1521*00b67f09SDavid van Moolenbroek ns_client_send(xfr->client);
1522*00b67f09SDavid van Moolenbroek xfr->stream->methods->pause(xfr->stream);
1523*00b67f09SDavid van Moolenbroek xfrout_ctx_destroy(&xfr);
1524*00b67f09SDavid van Moolenbroek return;
1525*00b67f09SDavid van Moolenbroek }
1526*00b67f09SDavid van Moolenbroek
1527*00b67f09SDavid van Moolenbroek /* Advance lasttsig to be the last TSIG generated */
1528*00b67f09SDavid van Moolenbroek CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig));
1529*00b67f09SDavid van Moolenbroek
1530*00b67f09SDavid van Moolenbroek xfr->nmsg++;
1531*00b67f09SDavid van Moolenbroek
1532*00b67f09SDavid van Moolenbroek failure:
1533*00b67f09SDavid van Moolenbroek if (msgname != NULL) {
1534*00b67f09SDavid van Moolenbroek if (msgrds != NULL) {
1535*00b67f09SDavid van Moolenbroek if (dns_rdataset_isassociated(msgrds))
1536*00b67f09SDavid van Moolenbroek dns_rdataset_disassociate(msgrds);
1537*00b67f09SDavid van Moolenbroek dns_message_puttemprdataset(msg, &msgrds);
1538*00b67f09SDavid van Moolenbroek }
1539*00b67f09SDavid van Moolenbroek if (msgrdl != NULL) {
1540*00b67f09SDavid van Moolenbroek ISC_LIST_UNLINK(msgrdl->rdata, msgrdata, link);
1541*00b67f09SDavid van Moolenbroek dns_message_puttemprdatalist(msg, &msgrdl);
1542*00b67f09SDavid van Moolenbroek }
1543*00b67f09SDavid van Moolenbroek if (msgrdata != NULL)
1544*00b67f09SDavid van Moolenbroek dns_message_puttemprdata(msg, &msgrdata);
1545*00b67f09SDavid van Moolenbroek dns_message_puttempname(msg, &msgname);
1546*00b67f09SDavid van Moolenbroek }
1547*00b67f09SDavid van Moolenbroek
1548*00b67f09SDavid van Moolenbroek if (tcpmsg != NULL)
1549*00b67f09SDavid van Moolenbroek dns_message_destroy(&tcpmsg);
1550*00b67f09SDavid van Moolenbroek
1551*00b67f09SDavid van Moolenbroek if (cleanup_cctx)
1552*00b67f09SDavid van Moolenbroek dns_compress_invalidate(&cctx);
1553*00b67f09SDavid van Moolenbroek /*
1554*00b67f09SDavid van Moolenbroek * Make sure to release any locks held by database
1555*00b67f09SDavid van Moolenbroek * iterators before returning from the event handler.
1556*00b67f09SDavid van Moolenbroek */
1557*00b67f09SDavid van Moolenbroek xfr->stream->methods->pause(xfr->stream);
1558*00b67f09SDavid van Moolenbroek
1559*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
1560*00b67f09SDavid van Moolenbroek return;
1561*00b67f09SDavid van Moolenbroek
1562*00b67f09SDavid van Moolenbroek xfrout_fail(xfr, result, "sending zone data");
1563*00b67f09SDavid van Moolenbroek }
1564*00b67f09SDavid van Moolenbroek
1565*00b67f09SDavid van Moolenbroek static void
xfrout_ctx_destroy(xfrout_ctx_t ** xfrp)1566*00b67f09SDavid van Moolenbroek xfrout_ctx_destroy(xfrout_ctx_t **xfrp) {
1567*00b67f09SDavid van Moolenbroek xfrout_ctx_t *xfr = *xfrp;
1568*00b67f09SDavid van Moolenbroek ns_client_t *client = NULL;
1569*00b67f09SDavid van Moolenbroek
1570*00b67f09SDavid van Moolenbroek INSIST(xfr->sends == 0);
1571*00b67f09SDavid van Moolenbroek
1572*00b67f09SDavid van Moolenbroek xfr->client->shutdown = NULL;
1573*00b67f09SDavid van Moolenbroek xfr->client->shutdown_arg = NULL;
1574*00b67f09SDavid van Moolenbroek
1575*00b67f09SDavid van Moolenbroek if (xfr->stream != NULL)
1576*00b67f09SDavid van Moolenbroek xfr->stream->methods->destroy(&xfr->stream);
1577*00b67f09SDavid van Moolenbroek if (xfr->buf.base != NULL)
1578*00b67f09SDavid van Moolenbroek isc_mem_put(xfr->mctx, xfr->buf.base, xfr->buf.length);
1579*00b67f09SDavid van Moolenbroek if (xfr->txmem != NULL)
1580*00b67f09SDavid van Moolenbroek isc_mem_put(xfr->mctx, xfr->txmem, xfr->txmemlen);
1581*00b67f09SDavid van Moolenbroek if (xfr->lasttsig != NULL)
1582*00b67f09SDavid van Moolenbroek isc_buffer_free(&xfr->lasttsig);
1583*00b67f09SDavid van Moolenbroek if (xfr->quota != NULL)
1584*00b67f09SDavid van Moolenbroek isc_quota_detach(&xfr->quota);
1585*00b67f09SDavid van Moolenbroek if (xfr->ver != NULL)
1586*00b67f09SDavid van Moolenbroek dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
1587*00b67f09SDavid van Moolenbroek if (xfr->zone != NULL)
1588*00b67f09SDavid van Moolenbroek dns_zone_detach(&xfr->zone);
1589*00b67f09SDavid van Moolenbroek if (xfr->db != NULL)
1590*00b67f09SDavid van Moolenbroek dns_db_detach(&xfr->db);
1591*00b67f09SDavid van Moolenbroek
1592*00b67f09SDavid van Moolenbroek /*
1593*00b67f09SDavid van Moolenbroek * We want to detch the client after we have released the memory
1594*00b67f09SDavid van Moolenbroek * context as ns_client_detach checks the memory reference count.
1595*00b67f09SDavid van Moolenbroek */
1596*00b67f09SDavid van Moolenbroek ns_client_attach(xfr->client, &client);
1597*00b67f09SDavid van Moolenbroek ns_client_detach(&xfr->client);
1598*00b67f09SDavid van Moolenbroek isc_mem_putanddetach(&xfr->mctx, xfr, sizeof(*xfr));
1599*00b67f09SDavid van Moolenbroek ns_client_detach(&client);
1600*00b67f09SDavid van Moolenbroek
1601*00b67f09SDavid van Moolenbroek *xfrp = NULL;
1602*00b67f09SDavid van Moolenbroek }
1603*00b67f09SDavid van Moolenbroek
1604*00b67f09SDavid van Moolenbroek static void
xfrout_senddone(isc_task_t * task,isc_event_t * event)1605*00b67f09SDavid van Moolenbroek xfrout_senddone(isc_task_t *task, isc_event_t *event) {
1606*00b67f09SDavid van Moolenbroek isc_socketevent_t *sev = (isc_socketevent_t *)event;
1607*00b67f09SDavid van Moolenbroek xfrout_ctx_t *xfr = (xfrout_ctx_t *)event->ev_arg;
1608*00b67f09SDavid van Moolenbroek isc_result_t evresult = sev->result;
1609*00b67f09SDavid van Moolenbroek
1610*00b67f09SDavid van Moolenbroek UNUSED(task);
1611*00b67f09SDavid van Moolenbroek
1612*00b67f09SDavid van Moolenbroek INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
1613*00b67f09SDavid van Moolenbroek
1614*00b67f09SDavid van Moolenbroek isc_event_free(&event);
1615*00b67f09SDavid van Moolenbroek xfr->sends--;
1616*00b67f09SDavid van Moolenbroek INSIST(xfr->sends == 0);
1617*00b67f09SDavid van Moolenbroek
1618*00b67f09SDavid van Moolenbroek (void)isc_timer_touch(xfr->client->timer);
1619*00b67f09SDavid van Moolenbroek if (xfr->shuttingdown == ISC_TRUE) {
1620*00b67f09SDavid van Moolenbroek xfrout_maybe_destroy(xfr);
1621*00b67f09SDavid van Moolenbroek } else if (evresult != ISC_R_SUCCESS) {
1622*00b67f09SDavid van Moolenbroek xfrout_fail(xfr, evresult, "send");
1623*00b67f09SDavid van Moolenbroek } else if (xfr->end_of_stream == ISC_FALSE) {
1624*00b67f09SDavid van Moolenbroek sendstream(xfr);
1625*00b67f09SDavid van Moolenbroek } else {
1626*00b67f09SDavid van Moolenbroek /* End of zone transfer stream. */
1627*00b67f09SDavid van Moolenbroek inc_stats(xfr->zone, dns_nsstatscounter_xfrdone);
1628*00b67f09SDavid van Moolenbroek xfrout_log(xfr, ISC_LOG_INFO, "%s ended", xfr->mnemonic);
1629*00b67f09SDavid van Moolenbroek ns_client_next(xfr->client, ISC_R_SUCCESS);
1630*00b67f09SDavid van Moolenbroek xfrout_ctx_destroy(&xfr);
1631*00b67f09SDavid van Moolenbroek }
1632*00b67f09SDavid van Moolenbroek }
1633*00b67f09SDavid van Moolenbroek
1634*00b67f09SDavid van Moolenbroek static void
xfrout_fail(xfrout_ctx_t * xfr,isc_result_t result,const char * msg)1635*00b67f09SDavid van Moolenbroek xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg) {
1636*00b67f09SDavid van Moolenbroek xfr->shuttingdown = ISC_TRUE;
1637*00b67f09SDavid van Moolenbroek xfrout_log(xfr, ISC_LOG_ERROR, "%s: %s",
1638*00b67f09SDavid van Moolenbroek msg, isc_result_totext(result));
1639*00b67f09SDavid van Moolenbroek xfrout_maybe_destroy(xfr);
1640*00b67f09SDavid van Moolenbroek }
1641*00b67f09SDavid van Moolenbroek
1642*00b67f09SDavid van Moolenbroek static void
xfrout_maybe_destroy(xfrout_ctx_t * xfr)1643*00b67f09SDavid van Moolenbroek xfrout_maybe_destroy(xfrout_ctx_t *xfr) {
1644*00b67f09SDavid van Moolenbroek INSIST(xfr->shuttingdown == ISC_TRUE);
1645*00b67f09SDavid van Moolenbroek if (xfr->sends > 0) {
1646*00b67f09SDavid van Moolenbroek /*
1647*00b67f09SDavid van Moolenbroek * If we are currently sending, cancel it and wait for
1648*00b67f09SDavid van Moolenbroek * cancel event before destroying the context.
1649*00b67f09SDavid van Moolenbroek */
1650*00b67f09SDavid van Moolenbroek isc_socket_cancel(xfr->client->tcpsocket, xfr->client->task,
1651*00b67f09SDavid van Moolenbroek ISC_SOCKCANCEL_SEND);
1652*00b67f09SDavid van Moolenbroek } else {
1653*00b67f09SDavid van Moolenbroek ns_client_next(xfr->client, ISC_R_CANCELED);
1654*00b67f09SDavid van Moolenbroek xfrout_ctx_destroy(&xfr);
1655*00b67f09SDavid van Moolenbroek }
1656*00b67f09SDavid van Moolenbroek }
1657*00b67f09SDavid van Moolenbroek
1658*00b67f09SDavid van Moolenbroek static void
xfrout_client_shutdown(void * arg,isc_result_t result)1659*00b67f09SDavid van Moolenbroek xfrout_client_shutdown(void *arg, isc_result_t result) {
1660*00b67f09SDavid van Moolenbroek xfrout_ctx_t *xfr = (xfrout_ctx_t *) arg;
1661*00b67f09SDavid van Moolenbroek xfrout_fail(xfr, result, "aborted");
1662*00b67f09SDavid van Moolenbroek }
1663*00b67f09SDavid van Moolenbroek
1664*00b67f09SDavid van Moolenbroek /*
1665*00b67f09SDavid van Moolenbroek * Log outgoing zone transfer messages in a format like
1666*00b67f09SDavid van Moolenbroek * <client>: transfer of <zone>: <message>
1667*00b67f09SDavid van Moolenbroek */
1668*00b67f09SDavid van Moolenbroek
1669*00b67f09SDavid van Moolenbroek static void
1670*00b67f09SDavid van Moolenbroek xfrout_logv(ns_client_t *client, dns_name_t *zonename,
1671*00b67f09SDavid van Moolenbroek dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap)
1672*00b67f09SDavid van Moolenbroek ISC_FORMAT_PRINTF(5, 0);
1673*00b67f09SDavid van Moolenbroek
1674*00b67f09SDavid van Moolenbroek static void
xfrout_logv(ns_client_t * client,dns_name_t * zonename,dns_rdataclass_t rdclass,int level,const char * fmt,va_list ap)1675*00b67f09SDavid van Moolenbroek xfrout_logv(ns_client_t *client, dns_name_t *zonename,
1676*00b67f09SDavid van Moolenbroek dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap)
1677*00b67f09SDavid van Moolenbroek {
1678*00b67f09SDavid van Moolenbroek char msgbuf[2048];
1679*00b67f09SDavid van Moolenbroek char namebuf[DNS_NAME_FORMATSIZE];
1680*00b67f09SDavid van Moolenbroek char classbuf[DNS_RDATACLASS_FORMATSIZE];
1681*00b67f09SDavid van Moolenbroek
1682*00b67f09SDavid van Moolenbroek dns_name_format(zonename, namebuf, sizeof(namebuf));
1683*00b67f09SDavid van Moolenbroek dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
1684*00b67f09SDavid van Moolenbroek vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
1685*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT,
1686*00b67f09SDavid van Moolenbroek NS_LOGMODULE_XFER_OUT, level,
1687*00b67f09SDavid van Moolenbroek "transfer of '%s/%s': %s", namebuf, classbuf, msgbuf);
1688*00b67f09SDavid van Moolenbroek }
1689*00b67f09SDavid van Moolenbroek
1690*00b67f09SDavid van Moolenbroek /*
1691*00b67f09SDavid van Moolenbroek * Logging function for use when a xfrout_ctx_t has not yet been created.
1692*00b67f09SDavid van Moolenbroek */
1693*00b67f09SDavid van Moolenbroek static void
xfrout_log1(ns_client_t * client,dns_name_t * zonename,dns_rdataclass_t rdclass,int level,const char * fmt,...)1694*00b67f09SDavid van Moolenbroek xfrout_log1(ns_client_t *client, dns_name_t *zonename,
1695*00b67f09SDavid van Moolenbroek dns_rdataclass_t rdclass, int level, const char *fmt, ...) {
1696*00b67f09SDavid van Moolenbroek va_list ap;
1697*00b67f09SDavid van Moolenbroek va_start(ap, fmt);
1698*00b67f09SDavid van Moolenbroek xfrout_logv(client, zonename, rdclass, level, fmt, ap);
1699*00b67f09SDavid van Moolenbroek va_end(ap);
1700*00b67f09SDavid van Moolenbroek }
1701*00b67f09SDavid van Moolenbroek
1702*00b67f09SDavid van Moolenbroek /*
1703*00b67f09SDavid van Moolenbroek * Logging function for use when there is a xfrout_ctx_t.
1704*00b67f09SDavid van Moolenbroek */
1705*00b67f09SDavid van Moolenbroek static void
xfrout_log(xfrout_ctx_t * xfr,int level,const char * fmt,...)1706*00b67f09SDavid van Moolenbroek xfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...) {
1707*00b67f09SDavid van Moolenbroek va_list ap;
1708*00b67f09SDavid van Moolenbroek va_start(ap, fmt);
1709*00b67f09SDavid van Moolenbroek xfrout_logv(xfr->client, xfr->qname, xfr->qclass, level, fmt, ap);
1710*00b67f09SDavid van Moolenbroek va_end(ap);
1711*00b67f09SDavid van Moolenbroek }
1712