diff options
Diffstat (limited to 'contrib/bind9/bin/named/xfrout.c')
-rw-r--r-- | contrib/bind9/bin/named/xfrout.c | 1718 |
1 files changed, 0 insertions, 1718 deletions
diff --git a/contrib/bind9/bin/named/xfrout.c b/contrib/bind9/bin/named/xfrout.c deleted file mode 100644 index 687c287f4bda..000000000000 --- a/contrib/bind9/bin/named/xfrout.c +++ /dev/null @@ -1,1718 +0,0 @@ -/* - * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") - * Copyright (C) 1999-2003 Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -/* $Id: xfrout.c,v 1.101.2.5.2.12 2005/10/14 02:13:05 marka Exp $ */ - -#include <config.h> - -#include <isc/formatcheck.h> -#include <isc/mem.h> -#include <isc/timer.h> -#include <isc/print.h> -#include <isc/util.h> - -#include <dns/db.h> -#include <dns/dbiterator.h> -#include <dns/fixedname.h> -#include <dns/journal.h> -#include <dns/message.h> -#include <dns/peer.h> -#include <dns/rdataclass.h> -#include <dns/rdatalist.h> -#include <dns/rdataset.h> -#include <dns/rdatasetiter.h> -#include <dns/result.h> -#include <dns/soa.h> -#include <dns/timer.h> -#include <dns/tsig.h> -#include <dns/view.h> -#include <dns/zone.h> -#include <dns/zt.h> - -#include <named/client.h> -#include <named/log.h> -#include <named/server.h> -#include <named/xfrout.h> - -/* - * Outgoing AXFR and IXFR. - */ - -/* - * TODO: - * - IXFR over UDP - */ - -#define XFROUT_COMMON_LOGARGS \ - ns_g_lctx, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT - -#define XFROUT_PROTOCOL_LOGARGS \ - XFROUT_COMMON_LOGARGS, ISC_LOG_INFO - -#define XFROUT_DEBUG_LOGARGS(n) \ - XFROUT_COMMON_LOGARGS, ISC_LOG_DEBUG(n) - -#define XFROUT_RR_LOGARGS \ - XFROUT_COMMON_LOGARGS, XFROUT_RR_LOGLEVEL - -#define XFROUT_RR_LOGLEVEL ISC_LOG_DEBUG(8) - -/* - * Fail unconditionally and log as a client error. - * The test against ISC_R_SUCCESS is there to keep the Solaris compiler - * from complaining about "end-of-loop code not reached". - */ -#define FAILC(code, msg) \ - do { \ - result = (code); \ - ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \ - NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \ - "bad zone transfer request: %s (%s)", \ - msg, isc_result_totext(code)); \ - if (result != ISC_R_SUCCESS) goto failure; \ - } while (0) - -#define FAILQ(code, msg, question, rdclass) \ - do { \ - char _buf1[DNS_NAME_FORMATSIZE]; \ - char _buf2[DNS_RDATACLASS_FORMATSIZE]; \ - result = (code); \ - dns_name_format(question, _buf1, sizeof(_buf1)); \ - dns_rdataclass_format(rdclass, _buf2, sizeof(_buf2)); \ - ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \ - NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \ - "bad zone transfer request: '%s/%s': %s (%s)", \ - _buf1, _buf2, msg, isc_result_totext(code)); \ - if (result != ISC_R_SUCCESS) goto failure; \ - } while (0) - -#define CHECK(op) \ - do { result = (op); \ - if (result != ISC_R_SUCCESS) goto failure; \ - } while (0) - -/**************************************************************************/ -/* - * A db_rr_iterator_t is an iterator that iterates over an entire database, - * returning one RR at a time, in some arbitrary order. - */ - -typedef struct db_rr_iterator db_rr_iterator_t; - -struct db_rr_iterator { - isc_result_t result; - dns_db_t *db; - dns_dbiterator_t *dbit; - dns_dbversion_t *ver; - isc_stdtime_t now; - dns_dbnode_t *node; - dns_fixedname_t fixedname; - dns_rdatasetiter_t *rdatasetit; - dns_rdataset_t rdataset; - dns_rdata_t rdata; -}; - -static isc_result_t -db_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver, - isc_stdtime_t now); - -static isc_result_t -db_rr_iterator_first(db_rr_iterator_t *it); - -static isc_result_t -db_rr_iterator_next(db_rr_iterator_t *it); - -static void -db_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name, - isc_uint32_t *ttl, dns_rdata_t **rdata); - -static void -db_rr_iterator_destroy(db_rr_iterator_t *it); - -static isc_result_t -db_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver, - isc_stdtime_t now) -{ - isc_result_t result; - it->db = db; - it->dbit = NULL; - it->ver = ver; - it->now = now; - it->node = NULL; - result = dns_db_createiterator(it->db, ISC_FALSE, &it->dbit); - if (result != ISC_R_SUCCESS) - return (result); - it->rdatasetit = NULL; - dns_rdata_init(&it->rdata); - dns_rdataset_init(&it->rdataset); - dns_fixedname_init(&it->fixedname); - INSIST(! dns_rdataset_isassociated(&it->rdataset)); - it->result = ISC_R_SUCCESS; - return (it->result); -} - -static isc_result_t -db_rr_iterator_first(db_rr_iterator_t *it) { - it->result = dns_dbiterator_first(it->dbit); - /* - * The top node may be empty when out of zone glue exists. - * Walk the tree to find the first node with data. - */ - while (it->result == ISC_R_SUCCESS) { - it->result = dns_dbiterator_current(it->dbit, &it->node, - dns_fixedname_name(&it->fixedname)); - if (it->result != ISC_R_SUCCESS) - return (it->result); - - it->result = dns_db_allrdatasets(it->db, it->node, - it->ver, it->now, - &it->rdatasetit); - if (it->result != ISC_R_SUCCESS) - return (it->result); - - it->result = dns_rdatasetiter_first(it->rdatasetit); - if (it->result != ISC_R_SUCCESS) { - /* - * This node is empty. Try next node. - */ - dns_rdatasetiter_destroy(&it->rdatasetit); - dns_db_detachnode(it->db, &it->node); - it->result = dns_dbiterator_next(it->dbit); - continue; - } - dns_rdatasetiter_current(it->rdatasetit, &it->rdataset); - - it->result = dns_rdataset_first(&it->rdataset); - return (it->result); - } - return (it->result); -} - - -static isc_result_t -db_rr_iterator_next(db_rr_iterator_t *it) { - if (it->result != ISC_R_SUCCESS) - return (it->result); - - INSIST(it->dbit != NULL); - INSIST(it->node != NULL); - INSIST(it->rdatasetit != NULL); - - it->result = dns_rdataset_next(&it->rdataset); - if (it->result == ISC_R_NOMORE) { - dns_rdataset_disassociate(&it->rdataset); - it->result = dns_rdatasetiter_next(it->rdatasetit); - /* - * The while loop body is executed more than once - * only when an empty dbnode needs to be skipped. - */ - while (it->result == ISC_R_NOMORE) { - dns_rdatasetiter_destroy(&it->rdatasetit); - dns_db_detachnode(it->db, &it->node); - it->result = dns_dbiterator_next(it->dbit); - if (it->result == ISC_R_NOMORE) { - /* We are at the end of the entire database. */ - return (it->result); - } - if (it->result != ISC_R_SUCCESS) - return (it->result); - it->result = dns_dbiterator_current(it->dbit, - &it->node, - dns_fixedname_name(&it->fixedname)); - if (it->result != ISC_R_SUCCESS) - return (it->result); - it->result = dns_db_allrdatasets(it->db, it->node, - it->ver, it->now, - &it->rdatasetit); - if (it->result != ISC_R_SUCCESS) - return (it->result); - it->result = dns_rdatasetiter_first(it->rdatasetit); - } - if (it->result != ISC_R_SUCCESS) - return (it->result); - dns_rdatasetiter_current(it->rdatasetit, &it->rdataset); - it->result = dns_rdataset_first(&it->rdataset); - if (it->result != ISC_R_SUCCESS) - return (it->result); - } - return (it->result); -} - -static void -db_rr_iterator_pause(db_rr_iterator_t *it) { - RUNTIME_CHECK(dns_dbiterator_pause(it->dbit) == ISC_R_SUCCESS); -} - -static void -db_rr_iterator_destroy(db_rr_iterator_t *it) { - if (dns_rdataset_isassociated(&it->rdataset)) - dns_rdataset_disassociate(&it->rdataset); - if (it->rdatasetit != NULL) - dns_rdatasetiter_destroy(&it->rdatasetit); - if (it->node != NULL) - dns_db_detachnode(it->db, &it->node); - dns_dbiterator_destroy(&it->dbit); -} - -static void -db_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name, - isc_uint32_t *ttl, dns_rdata_t **rdata) -{ - REQUIRE(name != NULL && *name == NULL); - REQUIRE(it->result == ISC_R_SUCCESS); - *name = dns_fixedname_name(&it->fixedname); - *ttl = it->rdataset.ttl; - dns_rdata_reset(&it->rdata); - dns_rdataset_current(&it->rdataset, &it->rdata); - *rdata = &it->rdata; -} - -/**************************************************************************/ - -/* Log an RR (for debugging) */ - -static void -log_rr(dns_name_t *name, dns_rdata_t *rdata, isc_uint32_t ttl) { - isc_result_t result; - isc_buffer_t buf; - char mem[2000]; - dns_rdatalist_t rdl; - dns_rdataset_t rds; - dns_rdata_t rd = DNS_RDATA_INIT; - - rdl.type = rdata->type; - rdl.rdclass = rdata->rdclass; - rdl.ttl = ttl; - ISC_LIST_INIT(rdl.rdata); - ISC_LINK_INIT(&rdl, link); - dns_rdataset_init(&rds); - dns_rdata_init(&rd); - dns_rdata_clone(rdata, &rd); - ISC_LIST_APPEND(rdl.rdata, &rd, link); - RUNTIME_CHECK(dns_rdatalist_tordataset(&rdl, &rds) == ISC_R_SUCCESS); - - isc_buffer_init(&buf, mem, sizeof(mem)); - result = dns_rdataset_totext(&rds, name, - ISC_FALSE, ISC_FALSE, &buf); - - /* - * We could use xfrout_log(), but that would produce - * very long lines with a repetitive prefix. - */ - if (result == ISC_R_SUCCESS) { - /* - * Get rid of final newline. - */ - INSIST(buf.used >= 1 && - ((char *) buf.base)[buf.used - 1] == '\n'); - buf.used--; - - isc_log_write(XFROUT_RR_LOGARGS, "%.*s", - (int)isc_buffer_usedlength(&buf), - (char *)isc_buffer_base(&buf)); - } else { - isc_log_write(XFROUT_RR_LOGARGS, "<RR too large to print>"); - } -} - -/**************************************************************************/ -/* - * An 'rrstream_t' is a polymorphic iterator that returns - * a stream of resource records. There are multiple implementations, - * e.g. for generating AXFR and IXFR records streams. - */ - -typedef struct rrstream_methods rrstream_methods_t; - -typedef struct rrstream { - isc_mem_t *mctx; - rrstream_methods_t *methods; -} rrstream_t; - -struct rrstream_methods { - isc_result_t (*first)(rrstream_t *); - isc_result_t (*next)(rrstream_t *); - void (*current)(rrstream_t *, - dns_name_t **, - isc_uint32_t *, - dns_rdata_t **); - void (*pause)(rrstream_t *); - void (*destroy)(rrstream_t **); -}; - -static void -rrstream_noop_pause(rrstream_t *rs) { - UNUSED(rs); -} - -/**************************************************************************/ -/* - * An 'ixfr_rrstream_t' is an 'rrstream_t' that returns - * an IXFR-like RR stream from a journal file. - * - * The SOA at the beginning of each sequence of additions - * or deletions are included in the stream, but the extra - * SOAs at the beginning and end of the entire transfer are - * not included. - */ - -typedef struct ixfr_rrstream { - rrstream_t common; - dns_journal_t *journal; -} ixfr_rrstream_t; - -/* Forward declarations. */ -static void -ixfr_rrstream_destroy(rrstream_t **sp); - -static rrstream_methods_t ixfr_rrstream_methods; - -/* - * Returns: anything dns_journal_open() or dns_journal_iter_init() - * may return. - */ - -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) -{ - ixfr_rrstream_t *s; - isc_result_t result; - - INSIST(sp != NULL && *sp == NULL); - - s = isc_mem_get(mctx, sizeof(*s)); - if (s == NULL) - return (ISC_R_NOMEMORY); - s->common.mctx = mctx; - s->common.methods = &ixfr_rrstream_methods; - s->journal = NULL; - - CHECK(dns_journal_open(mctx, journal_filename, - ISC_FALSE, &s->journal)); - CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial)); - - *sp = (rrstream_t *) s; - return (ISC_R_SUCCESS); - - failure: - ixfr_rrstream_destroy((rrstream_t **) (void *)&s); - return (result); -} - -static isc_result_t -ixfr_rrstream_first(rrstream_t *rs) { - ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs; - return (dns_journal_first_rr(s->journal)); -} - -static isc_result_t -ixfr_rrstream_next(rrstream_t *rs) { - ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs; - return (dns_journal_next_rr(s->journal)); -} - -static void -ixfr_rrstream_current(rrstream_t *rs, - dns_name_t **name, isc_uint32_t *ttl, - dns_rdata_t **rdata) -{ - ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs; - dns_journal_current_rr(s->journal, name, ttl, rdata); -} - -static void -ixfr_rrstream_destroy(rrstream_t **rsp) { - ixfr_rrstream_t *s = (ixfr_rrstream_t *) *rsp; - if (s->journal != 0) - dns_journal_destroy(&s->journal); - isc_mem_put(s->common.mctx, s, sizeof(*s)); -} - -static rrstream_methods_t ixfr_rrstream_methods = { - ixfr_rrstream_first, - ixfr_rrstream_next, - ixfr_rrstream_current, - rrstream_noop_pause, - ixfr_rrstream_destroy -}; - -/**************************************************************************/ -/* - * An 'axfr_rrstream_t' is an 'rrstream_t' that returns - * an AXFR-like RR stream from a database. - * - * The SOAs at the beginning and end of the transfer are - * not included in the stream. - */ - -typedef struct axfr_rrstream { - rrstream_t common; - db_rr_iterator_t it; - isc_boolean_t it_valid; -} axfr_rrstream_t; - -/* - * Forward declarations. - */ -static void -axfr_rrstream_destroy(rrstream_t **rsp); - -static rrstream_methods_t axfr_rrstream_methods; - -static isc_result_t -axfr_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver, - rrstream_t **sp) -{ - axfr_rrstream_t *s; - isc_result_t result; - - INSIST(sp != NULL && *sp == NULL); - - s = isc_mem_get(mctx, sizeof(*s)); - if (s == NULL) - return (ISC_R_NOMEMORY); - s->common.mctx = mctx; - s->common.methods = &axfr_rrstream_methods; - s->it_valid = ISC_FALSE; - - CHECK(db_rr_iterator_init(&s->it, db, ver, 0)); - s->it_valid = ISC_TRUE; - - *sp = (rrstream_t *) s; - return (ISC_R_SUCCESS); - - failure: - axfr_rrstream_destroy((rrstream_t **) (void *)&s); - return (result); -} - -static isc_result_t -axfr_rrstream_first(rrstream_t *rs) { - axfr_rrstream_t *s = (axfr_rrstream_t *) rs; - isc_result_t result; - result = db_rr_iterator_first(&s->it); - if (result != ISC_R_SUCCESS) - return (result); - /* Skip SOA records. */ - for (;;) { - dns_name_t *name_dummy = NULL; - isc_uint32_t ttl_dummy; - dns_rdata_t *rdata = NULL; - db_rr_iterator_current(&s->it, &name_dummy, - &ttl_dummy, &rdata); - if (rdata->type != dns_rdatatype_soa) - break; - result = db_rr_iterator_next(&s->it); - if (result != ISC_R_SUCCESS) - break; - } - return (result); -} - -static isc_result_t -axfr_rrstream_next(rrstream_t *rs) { - axfr_rrstream_t *s = (axfr_rrstream_t *) rs; - isc_result_t result; - - /* Skip SOA records. */ - for (;;) { - dns_name_t *name_dummy = NULL; - isc_uint32_t ttl_dummy; - dns_rdata_t *rdata = NULL; - result = db_rr_iterator_next(&s->it); - if (result != ISC_R_SUCCESS) - break; - db_rr_iterator_current(&s->it, &name_dummy, - &ttl_dummy, &rdata); - if (rdata->type != dns_rdatatype_soa) - break; - } - return (result); -} - -static void -axfr_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl, - dns_rdata_t **rdata) -{ - axfr_rrstream_t *s = (axfr_rrstream_t *) rs; - db_rr_iterator_current(&s->it, name, ttl, rdata); -} - -static void -axfr_rrstream_pause(rrstream_t *rs) { - axfr_rrstream_t *s = (axfr_rrstream_t *) rs; - db_rr_iterator_pause(&s->it); -} - -static void -axfr_rrstream_destroy(rrstream_t **rsp) { - axfr_rrstream_t *s = (axfr_rrstream_t *) *rsp; - if (s->it_valid) - db_rr_iterator_destroy(&s->it); - isc_mem_put(s->common.mctx, s, sizeof(*s)); -} - -static rrstream_methods_t axfr_rrstream_methods = { - axfr_rrstream_first, - axfr_rrstream_next, - axfr_rrstream_current, - axfr_rrstream_pause, - axfr_rrstream_destroy -}; - -/**************************************************************************/ -/* - * An 'soa_rrstream_t' is a degenerate 'rrstream_t' that returns - * a single SOA record. - */ - -typedef struct soa_rrstream { - rrstream_t common; - dns_difftuple_t *soa_tuple; -} soa_rrstream_t; - -/* - * Forward declarations. - */ -static void -soa_rrstream_destroy(rrstream_t **rsp); - -static rrstream_methods_t soa_rrstream_methods; - -static isc_result_t -soa_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver, - rrstream_t **sp) -{ - soa_rrstream_t *s; - isc_result_t result; - - INSIST(sp != NULL && *sp == NULL); - - s = isc_mem_get(mctx, sizeof(*s)); - if (s == NULL) - return (ISC_R_NOMEMORY); - s->common.mctx = mctx; - s->common.methods = &soa_rrstream_methods; - s->soa_tuple = NULL; - - CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS, - &s->soa_tuple)); - - *sp = (rrstream_t *) s; - return (ISC_R_SUCCESS); - - failure: - soa_rrstream_destroy((rrstream_t **) (void *)&s); - return (result); -} - -static isc_result_t -soa_rrstream_first(rrstream_t *rs) { - UNUSED(rs); - return (ISC_R_SUCCESS); -} - -static isc_result_t -soa_rrstream_next(rrstream_t *rs) { - UNUSED(rs); - return (ISC_R_NOMORE); -} - -static void -soa_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl, - dns_rdata_t **rdata) -{ - soa_rrstream_t *s = (soa_rrstream_t *) rs; - *name = &s->soa_tuple->name; - *ttl = s->soa_tuple->ttl; - *rdata = &s->soa_tuple->rdata; -} - -static void -soa_rrstream_destroy(rrstream_t **rsp) { - soa_rrstream_t *s = (soa_rrstream_t *) *rsp; - if (s->soa_tuple != NULL) - dns_difftuple_free(&s->soa_tuple); - isc_mem_put(s->common.mctx, s, sizeof(*s)); -} - -static rrstream_methods_t soa_rrstream_methods = { - soa_rrstream_first, - soa_rrstream_next, - soa_rrstream_current, - rrstream_noop_pause, - soa_rrstream_destroy -}; - -/**************************************************************************/ -/* - * A 'compound_rrstream_t' objects owns a soa_rrstream - * and another rrstream, the "data stream". It returns - * a concatenated stream consisting of the soa_rrstream, then - * the data stream, then the soa_rrstream again. - * - * The component streams are owned by the compound_rrstream_t - * and are destroyed with it. - */ - -typedef struct compound_rrstream { - rrstream_t common; - rrstream_t *components[3]; - int state; - isc_result_t result; -} compound_rrstream_t; - -/* - * Forward declarations. - */ -static void -compound_rrstream_destroy(rrstream_t **rsp); - -static isc_result_t -compound_rrstream_next(rrstream_t *rs); - -static rrstream_methods_t compound_rrstream_methods; - -/* - * Requires: - * soa_stream != NULL && *soa_stream != NULL - * data_stream != NULL && *data_stream != NULL - * sp != NULL && *sp == NULL - * - * Ensures: - * *soa_stream == NULL - * *data_stream == NULL - * *sp points to a valid compound_rrstream_t - * The soa and data streams will be destroyed - * when the compound_rrstream_t is destroyed. - */ -static isc_result_t -compound_rrstream_create(isc_mem_t *mctx, rrstream_t **soa_stream, - rrstream_t **data_stream, rrstream_t **sp) -{ - compound_rrstream_t *s; - - INSIST(sp != NULL && *sp == NULL); - - s = isc_mem_get(mctx, sizeof(*s)); - if (s == NULL) - return (ISC_R_NOMEMORY); - s->common.mctx = mctx; - s->common.methods = &compound_rrstream_methods; - s->components[0] = *soa_stream; - s->components[1] = *data_stream; - s->components[2] = *soa_stream; - s->state = -1; - s->result = ISC_R_FAILURE; - - *soa_stream = NULL; - *data_stream = NULL; - *sp = (rrstream_t *) s; - return (ISC_R_SUCCESS); -} - -static isc_result_t -compound_rrstream_first(rrstream_t *rs) { - compound_rrstream_t *s = (compound_rrstream_t *) rs; - s->state = 0; - do { - rrstream_t *curstream = s->components[s->state]; - s->result = curstream->methods->first(curstream); - } while (s->result == ISC_R_NOMORE && s->state < 2); - return (s->result); -} - -static isc_result_t -compound_rrstream_next(rrstream_t *rs) { - compound_rrstream_t *s = (compound_rrstream_t *) rs; - rrstream_t *curstream = s->components[s->state]; - s->result = curstream->methods->next(curstream); - while (s->result == ISC_R_NOMORE) { - /* - * Make sure locks held by the current stream - * are released before we switch streams. - */ - curstream->methods->pause(curstream); - if (s->state == 2) - return (ISC_R_NOMORE); - s->state++; - curstream = s->components[s->state]; - s->result = curstream->methods->first(curstream); - } - return (s->result); -} - -static void -compound_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl, - dns_rdata_t **rdata) -{ - compound_rrstream_t *s = (compound_rrstream_t *) rs; - rrstream_t *curstream; - INSIST(0 <= s->state && s->state < 3); - INSIST(s->result == ISC_R_SUCCESS); - curstream = s->components[s->state]; - curstream->methods->current(curstream, name, ttl, rdata); -} - -static void -compound_rrstream_pause(rrstream_t *rs) -{ - compound_rrstream_t *s = (compound_rrstream_t *) rs; - rrstream_t *curstream; - INSIST(0 <= s->state && s->state < 3); - curstream = s->components[s->state]; - curstream->methods->pause(curstream); -} - -static void -compound_rrstream_destroy(rrstream_t **rsp) { - compound_rrstream_t *s = (compound_rrstream_t *) *rsp; - s->components[0]->methods->destroy(&s->components[0]); - s->components[1]->methods->destroy(&s->components[1]); - s->components[2] = NULL; /* Copy of components[0]. */ - isc_mem_put(s->common.mctx, s, sizeof(*s)); -} - -static rrstream_methods_t compound_rrstream_methods = { - compound_rrstream_first, - compound_rrstream_next, - compound_rrstream_current, - compound_rrstream_pause, - compound_rrstream_destroy -}; - -/**************************************************************************/ -/* - * An 'xfrout_ctx_t' contains the state of an outgoing AXFR or IXFR - * in progress. - */ - -typedef struct { - isc_mem_t *mctx; - ns_client_t *client; - unsigned int id; /* ID of request */ - dns_name_t *qname; /* Question name of request */ - dns_rdatatype_t qtype; /* dns_rdatatype_{a,i}xfr */ - dns_rdataclass_t qclass; - dns_db_t *db; - dns_dbversion_t *ver; - isc_quota_t *quota; - rrstream_t *stream; /* The XFR RR stream */ - isc_boolean_t end_of_stream; /* EOS has been reached */ - isc_buffer_t buf; /* Buffer for message owner - names and rdatas */ - isc_buffer_t txlenbuf; /* Transmit length buffer */ - isc_buffer_t txbuf; /* Transmit message buffer */ - void *txmem; - unsigned int txmemlen; - unsigned int nmsg; /* Number of messages sent */ - dns_tsigkey_t *tsigkey; /* Key used to create TSIG */ - isc_buffer_t *lasttsig; /* the last TSIG */ - isc_boolean_t many_answers; - int sends; /* Send in progress */ - isc_boolean_t shuttingdown; - const char *mnemonic; /* Style of transfer */ -} xfrout_ctx_t; - -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_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); - -static void -sendstream(xfrout_ctx_t *xfr); - -static void -xfrout_senddone(isc_task_t *task, isc_event_t *event); - -static void -xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg); - -static void -xfrout_maybe_destroy(xfrout_ctx_t *xfr); - -static void -xfrout_ctx_destroy(xfrout_ctx_t **xfrp); - -static void -xfrout_client_shutdown(void *arg, isc_result_t result); - -static void -xfrout_log1(ns_client_t *client, dns_name_t *zonename, - dns_rdataclass_t rdclass, int level, - const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6); - -static void -xfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...) - ISC_FORMAT_PRINTF(3, 4); - -/**************************************************************************/ - -void -ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { - isc_result_t result; - dns_name_t *question_name; - dns_rdataset_t *question_rdataset; - dns_zone_t *zone = NULL; - dns_db_t *db = NULL; - dns_dbversion_t *ver = NULL; - dns_rdataclass_t question_class; - rrstream_t *soa_stream = NULL; - rrstream_t *data_stream = NULL; - rrstream_t *stream = NULL; - dns_difftuple_t *current_soa_tuple = NULL; - dns_name_t *soa_name; - dns_rdataset_t *soa_rdataset; - dns_rdata_t soa_rdata = DNS_RDATA_INIT; - isc_boolean_t have_soa = ISC_FALSE; - const char *mnemonic = NULL; - isc_mem_t *mctx = client->mctx; - dns_message_t *request = client->message; - xfrout_ctx_t *xfr = NULL; - isc_quota_t *quota = NULL; - dns_transfer_format_t format = client->view->transfer_format; - isc_netaddr_t na; - dns_peer_t *peer = NULL; - isc_buffer_t *tsigbuf = NULL; - char *journalfile; - char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")]; - char keyname[DNS_NAME_FORMATSIZE]; - isc_boolean_t is_poll = ISC_FALSE; - - switch (reqtype) { - case dns_rdatatype_axfr: - mnemonic = "AXFR"; - break; - case dns_rdatatype_ixfr: - mnemonic = "IXFR"; - break; - default: - INSIST(0); - break; - } - - ns_client_log(client, - DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT, - ISC_LOG_DEBUG(6), "%s request", mnemonic); - /* - * Apply quota. - */ - result = isc_quota_attach(&ns_g_server->xfroutquota, "a); - if (result != ISC_R_SUCCESS) { - isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING, - "%s request denied: %s", mnemonic, - isc_result_totext(result)); - goto failure; - } - - /* - * Interpret the question section. - */ - result = dns_message_firstname(request, DNS_SECTION_QUESTION); - INSIST(result == ISC_R_SUCCESS); - - /* - * The question section must contain exactly one question, and - * it must be for AXFR/IXFR as appropriate. - */ - question_name = NULL; - dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name); - question_rdataset = ISC_LIST_HEAD(question_name->list); - question_class = question_rdataset->rdclass; - INSIST(question_rdataset->type == reqtype); - if (ISC_LIST_NEXT(question_rdataset, link) != NULL) - FAILC(DNS_R_FORMERR, "multiple questions"); - result = dns_message_nextname(request, DNS_SECTION_QUESTION); - if (result != ISC_R_NOMORE) - FAILC(DNS_R_FORMERR, "multiple questions"); - - result = dns_zt_find(client->view->zonetable, question_name, 0, NULL, - &zone); - if (result != ISC_R_SUCCESS) - FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", - question_name, question_class); - switch(dns_zone_gettype(zone)) { - case dns_zone_master: - case dns_zone_slave: - break; /* Master and slave zones are OK for transfer. */ - default: - FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", - question_name, question_class); - } - CHECK(dns_zone_getdb(zone, &db)); - dns_db_currentversion(db, &ver); - - xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), - "%s question section OK", mnemonic); - - /* - * Check the authority section. Look for a SOA record with - * the same name and class as the question. - */ - for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY); - result == ISC_R_SUCCESS; - result = dns_message_nextname(request, DNS_SECTION_AUTHORITY)) - { - soa_name = NULL; - dns_message_currentname(request, DNS_SECTION_AUTHORITY, - &soa_name); - - /* - * Ignore data whose owner name is not the zone apex. - */ - if (! dns_name_equal(soa_name, question_name)) - continue; - - for (soa_rdataset = ISC_LIST_HEAD(soa_name->list); - soa_rdataset != NULL; - soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link)) - { - /* - * Ignore non-SOA data. - */ - if (soa_rdataset->type != dns_rdatatype_soa) - continue; - if (soa_rdataset->rdclass != question_class) - continue; - - CHECK(dns_rdataset_first(soa_rdataset)); - dns_rdataset_current(soa_rdataset, &soa_rdata); - result = dns_rdataset_next(soa_rdataset); - if (result == ISC_R_SUCCESS) - FAILC(DNS_R_FORMERR, - "IXFR authority section " - "has multiple SOAs"); - have_soa = ISC_TRUE; - goto got_soa; - } - } - got_soa: - if (result != ISC_R_NOMORE) - CHECK(result); - - xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), - "%s authority section OK", mnemonic); - - /* - * Decide whether to allow this transfer. - */ - ns_client_aclmsg("zone transfer", question_name, reqtype, - client->view->rdclass, msg, sizeof(msg)); - CHECK(ns_client_checkacl(client, msg, - dns_zone_getxfracl(zone), ISC_TRUE, - ISC_LOG_ERROR)); - - /* - * AXFR over UDP is not possible. - */ - if (reqtype == dns_rdatatype_axfr && - (client->attributes & NS_CLIENTATTR_TCP) == 0) - FAILC(DNS_R_FORMERR, "attempted AXFR over UDP"); - - /* - * Look up the requesting server in the peer table. - */ - isc_netaddr_fromsockaddr(&na, &client->peeraddr); - (void)dns_peerlist_peerbyaddr(client->view->peers, &na, &peer); - - /* - * Decide on the transfer format (one-answer or many-answers). - */ - if (peer != NULL) - (void)dns_peer_gettransferformat(peer, &format); - - /* - * Get a dynamically allocated copy of the current SOA. - */ - CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS, - ¤t_soa_tuple)); - - if (reqtype == dns_rdatatype_ixfr) { - isc_uint32_t begin_serial, current_serial; - isc_boolean_t provide_ixfr; - - /* - * Outgoing IXFR may have been disabled for this peer - * or globally. - */ - provide_ixfr = client->view->provideixfr; - if (peer != NULL) - (void) dns_peer_getprovideixfr(peer, &provide_ixfr); - if (provide_ixfr == ISC_FALSE) - goto axfr_fallback; - - if (! have_soa) - FAILC(DNS_R_FORMERR, - "IXFR request missing SOA"); - - begin_serial = dns_soa_getserial(&soa_rdata); - current_serial = dns_soa_getserial(¤t_soa_tuple->rdata); - - /* - * RFC1995 says "If an IXFR query with the same or - * newer version number than that of the server - * is received, it is replied to with a single SOA - * record of the server's current version, just as - * in AXFR". The claim about AXFR is incorrect, - * but other than that, we do as the RFC says. - * - * Sending a single SOA record is also how we refuse - * IXFR over UDP (currently, we always do). - */ - if (DNS_SERIAL_GE(begin_serial, current_serial) || - (client->attributes & NS_CLIENTATTR_TCP) == 0) - { - CHECK(soa_rrstream_create(mctx, db, ver, &stream)); - is_poll = ISC_TRUE; - goto have_stream; - } - journalfile = dns_zone_getjournal(zone); - if (journalfile != NULL) - result = ixfr_rrstream_create(mctx, - journalfile, - begin_serial, - current_serial, - &data_stream); - else - result = ISC_R_NOTFOUND; - if (result == ISC_R_NOTFOUND || - result == ISC_R_RANGE) { - xfrout_log1(client, question_name, question_class, - ISC_LOG_DEBUG(4), - "IXFR version not in journal, " - "falling back to AXFR"); - mnemonic = "AXFR-style IXFR"; - goto axfr_fallback; - } - CHECK(result); - } else { - axfr_fallback: - CHECK(axfr_rrstream_create(mctx, db, ver, - &data_stream)); - } - - /* - * Bracket the the data stream with SOAs. - */ - CHECK(soa_rrstream_create(mctx, db, ver, &soa_stream)); - CHECK(compound_rrstream_create(mctx, &soa_stream, &data_stream, - &stream)); - soa_stream = NULL; - data_stream = NULL; - - have_stream: - CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf)); - /* - * Create the xfrout context object. This transfers the ownership - * of "stream", "db", "ver", and "quota" to the xfrout context object. - */ - CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, - reqtype, question_class, db, ver, quota, - stream, dns_message_gettsigkey(request), - tsigbuf, - dns_zone_getmaxxfrout(zone), - dns_zone_getidleout(zone), - (format == dns_many_answers) ? - ISC_TRUE : ISC_FALSE, - &xfr)); - xfr->mnemonic = mnemonic; - stream = NULL; - quota = NULL; - - CHECK(xfr->stream->methods->first(xfr->stream)); - - if (xfr->tsigkey != NULL) { - dns_name_format(&xfr->tsigkey->name, keyname, sizeof(keyname)); - } else - keyname[0] = '\0'; - if (is_poll) - xfrout_log1(client, question_name, question_class, - ISC_LOG_DEBUG(1), "IXFR poll up to date%s%s", - (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname); - else - xfrout_log1(client, question_name, question_class, - ISC_LOG_INFO, "%s started%s%s", mnemonic, - (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname); - - /* - * Hand the context over to sendstream(). Set xfr to NULL; - * sendstream() is responsible for either passing the - * context on to a later event handler or destroying it. - */ - sendstream(xfr); - xfr = NULL; - - result = ISC_R_SUCCESS; - - failure: - if (quota != NULL) - isc_quota_detach("a); - if (current_soa_tuple != NULL) - dns_difftuple_free(¤t_soa_tuple); - if (stream != NULL) - stream->methods->destroy(&stream); - if (soa_stream != NULL) - soa_stream->methods->destroy(&soa_stream); - if (data_stream != NULL) - data_stream->methods->destroy(&data_stream); - if (ver != NULL) - dns_db_closeversion(db, &ver, ISC_FALSE); - if (db != NULL) - dns_db_detach(&db); - if (zone != NULL) - dns_zone_detach(&zone); - /* XXX kludge */ - if (xfr != NULL) { - xfrout_fail(xfr, result, "setting up zone transfer"); - } else if (result != ISC_R_SUCCESS) { - ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, - NS_LOGMODULE_XFER_OUT, - ISC_LOG_DEBUG(3), "zone transfer setup failed"); - ns_client_error(client, result); - } -} - -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_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) -{ - xfrout_ctx_t *xfr; - isc_result_t result; - unsigned int len; - void *mem; - - INSIST(xfrp != NULL && *xfrp == NULL); - xfr = isc_mem_get(mctx, sizeof(*xfr)); - if (xfr == NULL) - return (ISC_R_NOMEMORY); - xfr->mctx = mctx; - xfr->client = NULL; - ns_client_attach(client, &xfr->client); - xfr->id = id; - xfr->qname = qname; - xfr->qtype = qtype; - xfr->qclass = qclass; - xfr->db = NULL; - xfr->ver = NULL; - dns_db_attach(db, &xfr->db); - dns_db_attachversion(db, ver, &xfr->ver); - xfr->end_of_stream = ISC_FALSE; - xfr->tsigkey = tsigkey; - xfr->lasttsig = lasttsig; - xfr->txmem = NULL; - xfr->txmemlen = 0; - xfr->nmsg = 0; - xfr->many_answers = many_answers, - xfr->sends = 0; - xfr->shuttingdown = ISC_FALSE; - xfr->mnemonic = NULL; - xfr->buf.base = NULL; - xfr->buf.length = 0; - xfr->txmem = NULL; - xfr->txmemlen = 0; - xfr->stream = NULL; - xfr->quota = NULL; - - /* - * Allocate a temporary buffer for the uncompressed response - * message data. The size should be no more than 65535 bytes - * so that the compressed data will fit in a TCP message, - * and no less than 65535 bytes so that an almost maximum-sized - * RR will fit. Note that although 65535-byte RRs are allowed - * in principle, they cannot be zone-transferred (at least not - * if uncompressible), because the message and RR headers would - * push the size of the TCP message over the 65536 byte limit. - */ - len = 65535; - mem = isc_mem_get(mctx, len); - if (mem == NULL) { - result = ISC_R_NOMEMORY; - goto failure; - } - isc_buffer_init(&xfr->buf, mem, len); - - /* - * Allocate another temporary buffer for the compressed - * response message and its TCP length prefix. - */ - len = 2 + 65535; - mem = isc_mem_get(mctx, len); - if (mem == NULL) { - result = ISC_R_NOMEMORY; - goto failure; - } - isc_buffer_init(&xfr->txlenbuf, mem, 2); - isc_buffer_init(&xfr->txbuf, (char *) mem + 2, len - 2); - xfr->txmem = mem; - xfr->txmemlen = len; - - CHECK(dns_timer_setidle(xfr->client->timer, - maxtime, idletime, ISC_FALSE)); - - /* - * Register a shutdown callback with the client, so that we - * can stop the transfer immediately when the client task - * gets a shutdown event. - */ - xfr->client->shutdown = xfrout_client_shutdown; - xfr->client->shutdown_arg = xfr; - /* - * These MUST be after the last "goto failure;" / CHECK to - * prevent a double free by the caller. - */ - xfr->quota = quota; - xfr->stream = stream; - - *xfrp = xfr; - return (ISC_R_SUCCESS); - -failure: - xfrout_ctx_destroy(&xfr); - return (result); -} - - -/* - * Arrange to send as much as we can of "stream" without blocking. - * - * Requires: - * The stream iterator is initialized and points at an RR, - * or possiby at the end of the stream (that is, the - * _first method of the iterator has been called). - */ -static void -sendstream(xfrout_ctx_t *xfr) { - dns_message_t *tcpmsg = NULL; - dns_message_t *msg = NULL; /* Client message if UDP, tcpmsg if TCP */ - isc_result_t result; - isc_region_t used; - isc_region_t region; - dns_rdataset_t *qrdataset; - dns_name_t *msgname = NULL; - dns_rdata_t *msgrdata = NULL; - dns_rdatalist_t *msgrdl = NULL; - dns_rdataset_t *msgrds = NULL; - dns_compress_t cctx; - isc_boolean_t cleanup_cctx = ISC_FALSE; - - int n_rrs; - - isc_buffer_clear(&xfr->buf); - isc_buffer_clear(&xfr->txlenbuf); - isc_buffer_clear(&xfr->txbuf); - - if ((xfr->client->attributes & NS_CLIENTATTR_TCP) == 0) { - /* - * In the UDP case, we put the response data directly into - * the client message. - */ - msg = xfr->client->message; - CHECK(dns_message_reply(msg, ISC_TRUE)); - } else { - /* - * TCP. Build a response dns_message_t, temporarily storing - * the raw, uncompressed owner names and RR data contiguously - * in xfr->buf. We know that if the uncompressed data fits - * in xfr->buf, the compressed data will surely fit in a TCP - * message. - */ - - CHECK(dns_message_create(xfr->mctx, - DNS_MESSAGE_INTENTRENDER, &tcpmsg)); - msg = tcpmsg; - - msg->id = xfr->id; - msg->rcode = dns_rcode_noerror; - msg->flags = DNS_MESSAGEFLAG_QR | DNS_MESSAGEFLAG_AA; - if ((xfr->client->attributes & NS_CLIENTATTR_RA) != 0) - msg->flags |= DNS_MESSAGEFLAG_RA; - CHECK(dns_message_settsigkey(msg, xfr->tsigkey)); - CHECK(dns_message_setquerytsig(msg, xfr->lasttsig)); - if (xfr->lasttsig != NULL) - isc_buffer_free(&xfr->lasttsig); - - /* - * Include a question section in the first message only. - * BIND 8.2.1 will not recognize an IXFR if it does not - * have a question section. - */ - if (xfr->nmsg == 0) { - dns_name_t *qname = NULL; - isc_region_t r; - - /* - * Reserve space for the 12-byte message header - * and 4 bytes of question. - */ - isc_buffer_add(&xfr->buf, 12 + 4); - - qrdataset = NULL; - result = dns_message_gettemprdataset(msg, &qrdataset); - if (result != ISC_R_SUCCESS) - goto failure; - dns_rdataset_init(qrdataset); - dns_rdataset_makequestion(qrdataset, - xfr->client->message->rdclass, - xfr->qtype); - - result = dns_message_gettempname(msg, &qname); - if (result != ISC_R_SUCCESS) - goto failure; - dns_name_init(qname, NULL); - isc_buffer_availableregion(&xfr->buf, &r); - INSIST(r.length >= xfr->qname->length); - r.length = xfr->qname->length; - isc_buffer_putmem(&xfr->buf, xfr->qname->ndata, - xfr->qname->length); - dns_name_fromregion(qname, &r); - ISC_LIST_INIT(qname->list); - ISC_LIST_APPEND(qname->list, qrdataset, link); - - dns_message_addname(msg, qname, DNS_SECTION_QUESTION); - } - else - msg->tcp_continuation = 1; - } - - /* - * Try to fit in as many RRs as possible, unless "one-answer" - * format has been requested. - */ - for (n_rrs = 0; ; n_rrs++) { - dns_name_t *name = NULL; - isc_uint32_t ttl; - dns_rdata_t *rdata = NULL; - - unsigned int size; - isc_region_t r; - - msgname = NULL; - msgrdata = NULL; - msgrdl = NULL; - msgrds = NULL; - - xfr->stream->methods->current(xfr->stream, - &name, &ttl, &rdata); - size = name->length + 10 + rdata->length; - isc_buffer_availableregion(&xfr->buf, &r); - if (size >= r.length) { - /* - * RR would not fit. If there are other RRs in the - * buffer, send them now and leave this RR to the - * next message. If this RR overflows the buffer - * all by itself, fail. - * - * In theory some RRs might fit in a TCP message - * when compressed even if they do not fit when - * uncompressed, but surely we don't want - * to send such monstrosities to an unsuspecting - * slave. - */ - if (n_rrs == 0) { - xfrout_log(xfr, ISC_LOG_WARNING, - "RR too large for zone transfer " - "(%d bytes)", size); - /* XXX DNS_R_RRTOOLARGE? */ - result = ISC_R_NOSPACE; - goto failure; - } - break; - } - - if (isc_log_wouldlog(ns_g_lctx, XFROUT_RR_LOGLEVEL)) - log_rr(name, rdata, ttl); /* XXX */ - - result = dns_message_gettempname(msg, &msgname); - if (result != ISC_R_SUCCESS) - goto failure; - dns_name_init(msgname, NULL); - isc_buffer_availableregion(&xfr->buf, &r); - INSIST(r.length >= name->length); - r.length = name->length; - isc_buffer_putmem(&xfr->buf, name->ndata, name->length); - dns_name_fromregion(msgname, &r); - - /* Reserve space for RR header. */ - isc_buffer_add(&xfr->buf, 10); - - result = dns_message_gettemprdata(msg, &msgrdata); - if (result != ISC_R_SUCCESS) - goto failure; - isc_buffer_availableregion(&xfr->buf, &r); - r.length = rdata->length; - isc_buffer_putmem(&xfr->buf, rdata->data, rdata->length); - dns_rdata_init(msgrdata); - dns_rdata_fromregion(msgrdata, - rdata->rdclass, rdata->type, &r); - - result = dns_message_gettemprdatalist(msg, &msgrdl); - if (result != ISC_R_SUCCESS) - goto failure; - msgrdl->type = rdata->type; - msgrdl->rdclass = rdata->rdclass; - msgrdl->ttl = ttl; - ISC_LINK_INIT(msgrdl, link); - ISC_LIST_INIT(msgrdl->rdata); - ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link); - - result = dns_message_gettemprdataset(msg, &msgrds); - if (result != ISC_R_SUCCESS) - goto failure; - dns_rdataset_init(msgrds); - result = dns_rdatalist_tordataset(msgrdl, msgrds); - INSIST(result == ISC_R_SUCCESS); - - ISC_LIST_APPEND(msgname->list, msgrds, link); - - dns_message_addname(msg, msgname, DNS_SECTION_ANSWER); - msgname = NULL; - - result = xfr->stream->methods->next(xfr->stream); - if (result == ISC_R_NOMORE) { - xfr->end_of_stream = ISC_TRUE; - break; - } - CHECK(result); - - if (! xfr->many_answers) - break; - } - - if ((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0) { - CHECK(dns_compress_init(&cctx, -1, xfr->mctx)); - cleanup_cctx = ISC_TRUE; - CHECK(dns_message_renderbegin(msg, &cctx, &xfr->txbuf)); - CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0)); - CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0)); - CHECK(dns_message_renderend(msg)); - dns_compress_invalidate(&cctx); - cleanup_cctx = ISC_FALSE; - - isc_buffer_usedregion(&xfr->txbuf, &used); - isc_buffer_putuint16(&xfr->txlenbuf, - (isc_uint16_t)used.length); - region.base = xfr->txlenbuf.base; - region.length = 2 + used.length; - xfrout_log(xfr, ISC_LOG_DEBUG(8), - "sending TCP message of %d bytes", - used.length); - CHECK(isc_socket_send(xfr->client->tcpsocket, /* XXX */ - ®ion, xfr->client->task, - xfrout_senddone, - xfr)); - xfr->sends++; - } else { - xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending IXFR UDP response"); - ns_client_send(xfr->client); - xfr->stream->methods->pause(xfr->stream); - xfrout_ctx_destroy(&xfr); - return; - } - - /* Advance lasttsig to be the last TSIG generated */ - CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig)); - - xfr->nmsg++; - - failure: - if (msgname != NULL) { - if (msgrds != NULL) { - if (dns_rdataset_isassociated(msgrds)) - dns_rdataset_disassociate(msgrds); - dns_message_puttemprdataset(msg, &msgrds); - } - if (msgrdl != NULL) { - ISC_LIST_UNLINK(msgrdl->rdata, msgrdata, link); - dns_message_puttemprdatalist(msg, &msgrdl); - } - if (msgrdata != NULL) - dns_message_puttemprdata(msg, &msgrdata); - dns_message_puttempname(msg, &msgname); - } - - if (tcpmsg != NULL) - dns_message_destroy(&tcpmsg); - - if (cleanup_cctx) - dns_compress_invalidate(&cctx); - /* - * Make sure to release any locks held by database - * iterators before returning from the event handler. - */ - xfr->stream->methods->pause(xfr->stream); - - if (result == ISC_R_SUCCESS) - return; - - xfrout_fail(xfr, result, "sending zone data"); -} - -static void -xfrout_ctx_destroy(xfrout_ctx_t **xfrp) { - xfrout_ctx_t *xfr = *xfrp; - - INSIST(xfr->sends == 0); - - xfr->client->shutdown = NULL; - xfr->client->shutdown_arg = NULL; - - if (xfr->stream != NULL) - xfr->stream->methods->destroy(&xfr->stream); - if (xfr->buf.base != NULL) - isc_mem_put(xfr->mctx, xfr->buf.base, xfr->buf.length); - if (xfr->txmem != NULL) - isc_mem_put(xfr->mctx, xfr->txmem, xfr->txmemlen); - if (xfr->lasttsig != NULL) - isc_buffer_free(&xfr->lasttsig); - if (xfr->quota != NULL) - isc_quota_detach(&xfr->quota); - if (xfr->ver != NULL) - dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE); - if (xfr->db != NULL) - dns_db_detach(&xfr->db); - - ns_client_detach(&xfr->client); - - isc_mem_put(xfr->mctx, xfr, sizeof(*xfr)); - - *xfrp = NULL; -} - -static void -xfrout_senddone(isc_task_t *task, isc_event_t *event) { - isc_socketevent_t *sev = (isc_socketevent_t *)event; - xfrout_ctx_t *xfr = (xfrout_ctx_t *)event->ev_arg; - isc_result_t evresult = sev->result; - - UNUSED(task); - - INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE); - - isc_event_free(&event); - xfr->sends--; - INSIST(xfr->sends == 0); - - (void)isc_timer_touch(xfr->client->timer); - if (xfr->shuttingdown == ISC_TRUE) { - xfrout_maybe_destroy(xfr); - } else if (evresult != ISC_R_SUCCESS) { - xfrout_fail(xfr, evresult, "send"); - } else if (xfr->end_of_stream == ISC_FALSE) { - sendstream(xfr); - } else { - /* End of zone transfer stream. */ - xfrout_log(xfr, ISC_LOG_INFO, "%s ended", xfr->mnemonic); - ns_client_next(xfr->client, ISC_R_SUCCESS); - xfrout_ctx_destroy(&xfr); - } -} - -static void -xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg) { - xfr->shuttingdown = ISC_TRUE; - xfrout_log(xfr, ISC_LOG_ERROR, "%s: %s", - msg, isc_result_totext(result)); - xfrout_maybe_destroy(xfr); -} - -static void -xfrout_maybe_destroy(xfrout_ctx_t *xfr) { - INSIST(xfr->shuttingdown == ISC_TRUE); - if (xfr->sends > 0) { - /* - * If we are currently sending, cancel it and wait for - * cancel event before destroying the context. - */ - isc_socket_cancel(xfr->client->tcpsocket, xfr->client->task, - ISC_SOCKCANCEL_SEND); - } else { - ns_client_next(xfr->client, ISC_R_CANCELED); - xfrout_ctx_destroy(&xfr); - } -} - -static void -xfrout_client_shutdown(void *arg, isc_result_t result) { - xfrout_ctx_t *xfr = (xfrout_ctx_t *) arg; - xfrout_fail(xfr, result, "aborted"); -} - -/* - * Log outgoing zone transfer messages in a format like - * <client>: transfer of <zone>: <message> - */ - -static void -xfrout_logv(ns_client_t *client, dns_name_t *zonename, - dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap) - ISC_FORMAT_PRINTF(5, 0); - -static void -xfrout_logv(ns_client_t *client, dns_name_t *zonename, - dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap) -{ - char msgbuf[2048]; - char namebuf[DNS_NAME_FORMATSIZE]; - char classbuf[DNS_RDATACLASS_FORMATSIZE]; - - dns_name_format(zonename, namebuf, sizeof(namebuf)); - dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf)); - vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); - ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, - NS_LOGMODULE_XFER_OUT, level, - "transfer of '%s/%s': %s", namebuf, classbuf, msgbuf); -} - -/* - * Logging function for use when a xfrout_ctx_t has not yet been created. - */ -static void -xfrout_log1(ns_client_t *client, dns_name_t *zonename, - dns_rdataclass_t rdclass, int level, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - xfrout_logv(client, zonename, rdclass, level, fmt, ap); - va_end(ap); -} - -/* - * Logging function for use when there is a xfrout_ctx_t. - */ -static void -xfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - xfrout_logv(xfr->client, xfr->qname, xfr->qclass, level, fmt, ap); - va_end(ap); -} |