1999-08-20 05:35:16 +00:00
|
|
|
/*
|
1999-09-15 23:49:29 +00:00
|
|
|
* Copyright (C) 1999 Internet Software Consortium.
|
1999-08-20 05:35:16 +00:00
|
|
|
*
|
|
|
|
* 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
|
|
|
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
|
|
|
* CONSORTIUM 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.
|
|
|
|
*/
|
|
|
|
|
2000-01-12 18:01:12 +00:00
|
|
|
/* $Id: xfrout.c,v 1.36 2000/01/12 18:01:11 gson Exp $ */
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
1999-09-28 13:50:04 +00:00
|
|
|
#include <string.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include <isc/assertions.h>
|
|
|
|
#include <isc/error.h>
|
|
|
|
#include <isc/mem.h>
|
|
|
|
#include <isc/result.h>
|
1999-12-02 22:31:13 +00:00
|
|
|
#include <isc/timer.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
|
1999-12-16 23:11:07 +00:00
|
|
|
#include <dns/acl.h>
|
1999-10-25 13:25:43 +00:00
|
|
|
#include <dns/db.h>
|
|
|
|
#include <dns/dbiterator.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
#include <dns/fixedname.h>
|
1999-10-25 13:25:43 +00:00
|
|
|
#include <dns/journal.h>
|
|
|
|
#include <dns/message.h>
|
|
|
|
#include <dns/name.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
#include <dns/rdata.h>
|
|
|
|
#include <dns/rdatalist.h>
|
|
|
|
#include <dns/rdataset.h>
|
|
|
|
#include <dns/rdatasetiter.h>
|
1999-10-25 13:25:43 +00:00
|
|
|
#include <dns/rdatastruct.h>
|
|
|
|
#include <dns/result.h>
|
|
|
|
#include <dns/types.h>
|
|
|
|
#include <dns/view.h>
|
1999-10-14 00:06:11 +00:00
|
|
|
#include <dns/zone.h>
|
|
|
|
#include <dns/zt.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
#include <named/client.h>
|
1999-10-26 19:40:18 +00:00
|
|
|
#include <named/globals.h>
|
|
|
|
#include <named/log.h>
|
1999-12-16 23:11:07 +00:00
|
|
|
#include <named/server.h>
|
1999-08-20 05:35:16 +00:00
|
|
|
#include <named/xfrout.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Outgoing AXFR and IXFR.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO:
|
1999-10-26 19:40:18 +00:00
|
|
|
* - IXFR over UDP
|
1999-08-20 05:35:16 +00:00
|
|
|
*/
|
|
|
|
|
1999-10-26 19:40:18 +00:00
|
|
|
#define XFROUT_COMMON_LOGARGS \
|
|
|
|
ns_g_lctx, NS_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)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fail unconditionally and log as a client error.
|
|
|
|
*/
|
|
|
|
#define FAILC(code, msg) \
|
|
|
|
do { \
|
1999-11-02 19:17:39 +00:00
|
|
|
result = (code); \
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_PROTOCOL_LOGARGS, \
|
|
|
|
"bad zone transfer request: %s (%s)", \
|
|
|
|
msg, isc_result_totext(code)); \
|
|
|
|
goto failure; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define CHECK(op) \
|
|
|
|
do { result = (op); \
|
|
|
|
if (result != DNS_R_SUCCESS) goto failure; \
|
|
|
|
} while (0)
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* 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 {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t db_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db,
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_dbversion_t *ver, isc_stdtime_t now);
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t db_rr_iterator_first(db_rr_iterator_t *it);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t db_rr_iterator_next(db_rr_iterator_t *it);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
void db_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name,
|
|
|
|
isc_uint32_t *ttl, dns_rdata_t **rdata);
|
|
|
|
|
|
|
|
void db_rr_iterator_destroy(db_rr_iterator_t *it);
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
db_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
|
|
|
|
isc_stdtime_t now)
|
|
|
|
{
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
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 != DNS_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
it->rdatasetit = NULL;
|
|
|
|
dns_rdataset_init(&it->rdataset);
|
|
|
|
dns_fixedname_init(&it->fixedname);
|
1999-09-09 08:39:00 +00:00
|
|
|
INSIST(! dns_rdataset_isassociated(&it->rdataset));
|
1999-08-20 05:35:16 +00:00
|
|
|
it->result = DNS_R_SUCCESS;
|
|
|
|
return (it->result);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
db_rr_iterator_first(db_rr_iterator_t *it) {
|
|
|
|
it->result = dns_dbiterator_first(it->dbit);
|
|
|
|
if (it->result != DNS_R_SUCCESS)
|
|
|
|
return (it->result);
|
|
|
|
it->result = dns_dbiterator_current(it->dbit, &it->node,
|
1999-08-24 06:40:54 +00:00
|
|
|
dns_fixedname_name(&it->fixedname));
|
1999-08-20 05:35:16 +00:00
|
|
|
if (it->result != DNS_R_SUCCESS)
|
|
|
|
return (it->result);
|
|
|
|
|
|
|
|
it->result = dns_db_allrdatasets(it->db, it->node,
|
|
|
|
it->ver, it->now,
|
|
|
|
&it->rdatasetit);
|
|
|
|
if (it->result != DNS_R_SUCCESS)
|
|
|
|
return (it->result);
|
|
|
|
|
|
|
|
it->result = dns_rdatasetiter_first(it->rdatasetit);
|
|
|
|
if (it->result != DNS_R_SUCCESS)
|
|
|
|
return (it->result);
|
|
|
|
|
|
|
|
dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
|
|
|
|
|
|
|
|
it->result = dns_rdataset_first(&it->rdataset);
|
|
|
|
return (it->result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
db_rr_iterator_next(db_rr_iterator_t *it) {
|
|
|
|
if (it->result != DNS_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 == DNS_R_NOMORE) {
|
|
|
|
dns_rdataset_disassociate(&it->rdataset);
|
|
|
|
it->result = dns_rdatasetiter_next(it->rdatasetit);
|
1999-08-27 19:51:41 +00:00
|
|
|
while (it->result == DNS_R_NOMORE) {
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_rdatasetiter_destroy(&it->rdatasetit);
|
|
|
|
dns_db_detachnode(it->db, &it->node);
|
|
|
|
it->result = dns_dbiterator_next(it->dbit);
|
|
|
|
if (it->result == DNS_R_NOMORE) {
|
|
|
|
/* We are at the end of the entire database. */
|
|
|
|
return (it->result);
|
|
|
|
}
|
|
|
|
if (it->result != DNS_R_SUCCESS)
|
|
|
|
return (it->result);
|
1999-08-24 06:40:54 +00:00
|
|
|
it->result = dns_dbiterator_current(it->dbit,
|
|
|
|
&it->node,
|
|
|
|
dns_fixedname_name(&it->fixedname));
|
1999-08-20 05:35:16 +00:00
|
|
|
if (it->result != DNS_R_SUCCESS)
|
|
|
|
return (it->result);
|
|
|
|
it->result = dns_db_allrdatasets(it->db, it->node,
|
|
|
|
it->ver, it->now,
|
|
|
|
&it->rdatasetit);
|
|
|
|
if (it->result != DNS_R_SUCCESS)
|
|
|
|
return (it->result);
|
|
|
|
it->result = dns_rdatasetiter_first(it->rdatasetit);
|
|
|
|
}
|
|
|
|
if (it->result != DNS_R_SUCCESS)
|
|
|
|
return (it->result);
|
|
|
|
dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
|
|
|
|
}
|
|
|
|
return (it->result);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
db_rr_iterator_destroy(db_rr_iterator_t *it) {
|
1999-09-09 08:39:00 +00:00
|
|
|
if (dns_rdataset_isassociated(&it->rdataset))
|
1999-08-20 05:35:16 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 == DNS_R_SUCCESS);
|
|
|
|
*name = dns_fixedname_name(&it->fixedname);
|
|
|
|
*ttl = it->rdataset.ttl;
|
|
|
|
dns_rdataset_current(&it->rdataset, &it->rdata);
|
|
|
|
*rdata = &it->rdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
1999-10-26 19:40:18 +00:00
|
|
|
/* Log an RR (for debugging) */
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
static void
|
1999-10-26 19:40:18 +00:00
|
|
|
log_rr(dns_name_t *name, dns_rdata_t *rdata, isc_uint32_t ttl) {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
isc_buffer_t buf;
|
|
|
|
char mem[2000];
|
|
|
|
isc_region_t r;
|
|
|
|
dns_rdatalist_t rdl;
|
|
|
|
dns_rdataset_t rds;
|
|
|
|
|
|
|
|
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);
|
|
|
|
ISC_LIST_APPEND(rdl.rdata, rdata, link);
|
|
|
|
RUNTIME_CHECK(dns_rdatalist_tordataset(&rdl, &rds) == DNS_R_SUCCESS);
|
|
|
|
|
|
|
|
isc_buffer_init(&buf, mem, sizeof(mem), ISC_BUFFERTYPE_TEXT);
|
|
|
|
result = dns_rdataset_totext(&rds, name,
|
|
|
|
ISC_FALSE, ISC_FALSE, &buf);
|
|
|
|
|
1999-10-29 00:24:36 +00:00
|
|
|
/* Get rid of final newline. */
|
|
|
|
INSIST(buf.used >= 1 && ((char *) buf.base)[buf.used-1] == '\n');
|
|
|
|
buf.used--;
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
if (result == DNS_R_SUCCESS) {
|
|
|
|
isc_buffer_used(&buf, &r);
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(8),
|
|
|
|
"%.*s", (int) r.length, (char *) r.base);
|
1999-08-20 05:35:16 +00:00
|
|
|
} else {
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(8),
|
|
|
|
"<RR too large to print>");
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* 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 {
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t (*first)(rrstream_t *);
|
|
|
|
isc_result_t (*next)(rrstream_t *);
|
1999-08-20 05:35:16 +00:00
|
|
|
void (*current)(rrstream_t *,
|
|
|
|
dns_name_t **,
|
|
|
|
isc_uint32_t *,
|
|
|
|
dns_rdata_t **);
|
|
|
|
void (*destroy)(rrstream_t **);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
ixfr_rrstream_create(isc_mem_t *mctx,
|
1999-10-14 00:06:11 +00:00
|
|
|
const char *journal_filename,
|
|
|
|
isc_uint32_t begin_serial,
|
|
|
|
isc_uint32_t end_serial,
|
|
|
|
rrstream_t **sp)
|
1999-08-20 05:35:16 +00:00
|
|
|
{
|
|
|
|
ixfr_rrstream_t *s;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
INSIST(sp != NULL && *sp == NULL);
|
|
|
|
|
|
|
|
s = isc_mem_get(mctx, sizeof(*s));
|
|
|
|
if (s == NULL)
|
|
|
|
return (DNS_R_NOMEMORY);
|
|
|
|
s->common.mctx = mctx;
|
|
|
|
s->common.methods = &ixfr_rrstream_methods;
|
|
|
|
s->journal = NULL;
|
|
|
|
|
1999-10-14 00:06:11 +00:00
|
|
|
CHECK(dns_journal_open(mctx, journal_filename,
|
1999-08-24 06:40:54 +00:00
|
|
|
ISC_FALSE, &s->journal));
|
1999-08-20 05:35:16 +00:00
|
|
|
CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial));
|
|
|
|
|
|
|
|
*sp = (rrstream_t *) s;
|
|
|
|
return (DNS_R_SUCCESS);
|
|
|
|
|
|
|
|
failure:
|
|
|
|
ixfr_rrstream_destroy((rrstream_t **) &s);
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
ixfr_rrstream_first(rrstream_t *rs)
|
|
|
|
{
|
|
|
|
ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
|
|
|
|
return (dns_journal_first_rr(s->journal));
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
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,
|
|
|
|
ixfr_rrstream_destroy
|
|
|
|
};
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
|
|
* An 'ixfr_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;
|
|
|
|
int state;
|
|
|
|
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;
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
axfr_rrstream_create(isc_mem_t *mctx,
|
|
|
|
dns_db_t *db,
|
|
|
|
dns_dbversion_t *ver,
|
|
|
|
rrstream_t **sp)
|
|
|
|
{
|
|
|
|
axfr_rrstream_t *s;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
INSIST(sp != NULL && *sp == NULL);
|
|
|
|
|
|
|
|
s = isc_mem_get(mctx, sizeof(*s));
|
|
|
|
if (s == NULL)
|
|
|
|
return (DNS_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 (DNS_R_SUCCESS);
|
|
|
|
|
|
|
|
failure:
|
|
|
|
axfr_rrstream_destroy((rrstream_t **) &s);
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
axfr_rrstream_first(rrstream_t *rs)
|
|
|
|
{
|
|
|
|
axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
result = db_rr_iterator_first(&s->it);
|
|
|
|
/* 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 != DNS_R_SUCCESS)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
axfr_rrstream_next(rrstream_t *rs)
|
|
|
|
{
|
|
|
|
axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/* 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 != DNS_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_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_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;
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
soa_rrstream_create(isc_mem_t *mctx,
|
|
|
|
dns_db_t *db,
|
|
|
|
dns_dbversion_t *ver,
|
|
|
|
rrstream_t **sp)
|
|
|
|
{
|
|
|
|
soa_rrstream_t *s;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
INSIST(sp != NULL && *sp == NULL);
|
|
|
|
|
|
|
|
s = isc_mem_get(mctx, sizeof(*s));
|
|
|
|
if (s == NULL)
|
|
|
|
return (DNS_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 (DNS_R_SUCCESS);
|
|
|
|
|
|
|
|
failure:
|
|
|
|
soa_rrstream_destroy((rrstream_t **) &s);
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
soa_rrstream_first(rrstream_t *rs) {
|
|
|
|
rs = rs; /* Unused */
|
|
|
|
return (DNS_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
soa_rrstream_next(rrstream_t *rs) {
|
|
|
|
rs = rs; /* Unused */
|
|
|
|
return (DNS_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,
|
|
|
|
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);
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t compound_rrstream_next(rrstream_t *rs);
|
1999-08-20 05:35:16 +00:00
|
|
|
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.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
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 (DNS_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 (DNS_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
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 == DNS_R_NOMORE && s->state < 2) ;
|
|
|
|
return (s->result);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
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 == DNS_R_NOMORE) {
|
|
|
|
if (s->state == 2)
|
|
|
|
return (DNS_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 == DNS_R_SUCCESS);
|
|
|
|
curstream = s->components[s->state];
|
|
|
|
curstream->methods->current(curstream, name, ttl, rdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
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_destroy
|
|
|
|
};
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
/*
|
1999-08-23 11:07:53 +00:00
|
|
|
* An 'xfrout_ctx_t' contains the state of an outgoing AXFR or IXFR
|
1999-08-20 05:35:16 +00:00
|
|
|
* 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_db_t *db;
|
|
|
|
dns_dbversion_t *ver;
|
|
|
|
rrstream_t *stream; /* The XFR RR stream */
|
1999-12-02 22:31:13 +00:00
|
|
|
isc_boolean_t end_of_stream; /* EOS has been reached */
|
1999-08-20 05:35:16 +00:00
|
|
|
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 */
|
1999-10-08 18:37:24 +00:00
|
|
|
dns_tsigkey_t *tsigkey; /* Key used to create TSIG */
|
1999-09-10 15:01:04 +00:00
|
|
|
dns_rdata_any_tsig_t *lasttsig; /* the last TSIG */
|
1999-12-02 22:31:13 +00:00
|
|
|
isc_timer_t *timer;
|
|
|
|
int sends; /* Send in progress */
|
|
|
|
isc_boolean_t shuttingdown;
|
1999-08-20 05:35:16 +00:00
|
|
|
} xfrout_ctx_t;
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client,
|
|
|
|
unsigned int id, dns_name_t *qname, dns_rdatatype_t qtype,
|
|
|
|
dns_db_t *db, dns_dbversion_t *ver,
|
1999-10-08 18:37:24 +00:00
|
|
|
rrstream_t *stream, dns_tsigkey_t *tsigkey,
|
1999-12-16 02:59:32 +00:00
|
|
|
dns_rdata_any_tsig_t *lasttsig,
|
|
|
|
unsigned int maxtime,
|
|
|
|
unsigned int idletime,
|
1999-12-02 22:31:13 +00:00
|
|
|
xfrout_ctx_t **xfrp);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
static void sendstream(xfrout_ctx_t *xfr);
|
|
|
|
|
1999-12-02 22:31:13 +00:00
|
|
|
static void xfrout_senddone(isc_task_t *task, isc_event_t *event);
|
|
|
|
static void xfrout_timeout(isc_task_t *task, isc_event_t *event);
|
1999-12-23 00:09:04 +00:00
|
|
|
static void xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, char *msg);
|
1999-12-02 22:31:13 +00:00
|
|
|
static void xfrout_maybe_destroy(xfrout_ctx_t *xfr);
|
1999-08-20 05:35:16 +00:00
|
|
|
static void xfrout_ctx_destroy(xfrout_ctx_t **xfrp);
|
2000-01-12 18:01:12 +00:00
|
|
|
static void xfrout_client_shutdown(void *arg);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
|
|
|
void
|
|
|
|
ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype)
|
|
|
|
{
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_name_t *question_name;
|
|
|
|
dns_rdataset_t *question_rdataset;
|
1999-10-14 00:06:11 +00:00
|
|
|
dns_zone_t *zone = NULL;
|
1999-08-20 05:35:16 +00:00
|
|
|
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;
|
|
|
|
isc_boolean_t have_soa = ISC_FALSE;
|
|
|
|
char *mnemonic = NULL;
|
|
|
|
isc_mem_t *mctx = client->mctx;
|
|
|
|
dns_message_t *request = client->message;
|
|
|
|
xfrout_ctx_t *xfr = NULL;
|
|
|
|
|
|
|
|
switch (reqtype) {
|
|
|
|
case dns_rdatatype_axfr:
|
|
|
|
mnemonic = "AXFR";
|
|
|
|
break;
|
|
|
|
case dns_rdatatype_ixfr:
|
|
|
|
mnemonic = "IXFR";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
INSIST(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(6), "got %s request", mnemonic);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
2000-01-11 21:18:22 +00:00
|
|
|
/*
|
|
|
|
* Apply quotas.
|
|
|
|
*/
|
|
|
|
result = ns_client_getquota(client, &ns_g_server->xfroutquota);
|
|
|
|
if (result != DNS_R_SUCCESS) {
|
|
|
|
isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING,
|
|
|
|
"zone transfer request denied: %s",
|
|
|
|
isc_result_totext(result));
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/*
|
|
|
|
* Interpret the question section.
|
|
|
|
*/
|
|
|
|
result = dns_message_firstname(request, DNS_SECTION_QUESTION);
|
|
|
|
INSIST(result == DNS_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)
|
1999-10-26 19:40:18 +00:00
|
|
|
FAILC(DNS_R_FORMERR,
|
|
|
|
"multiple questions in AXFR/IXFR request");
|
1999-08-20 05:35:16 +00:00
|
|
|
result = dns_message_nextname(request, DNS_SECTION_QUESTION);
|
|
|
|
if (result != DNS_R_NOMORE)
|
1999-10-26 19:40:18 +00:00
|
|
|
FAILC(DNS_R_FORMERR,
|
|
|
|
"multiple questions in AXFR/IXFR request");
|
1999-08-20 05:35:16 +00:00
|
|
|
|
1999-10-14 00:06:11 +00:00
|
|
|
result = dns_zt_find(client->view->zonetable, question_name, NULL, &zone);
|
1999-08-20 05:35:16 +00:00
|
|
|
if (result != DNS_R_SUCCESS)
|
1999-10-26 19:40:18 +00:00
|
|
|
FAILC(DNS_R_NOTAUTH,
|
|
|
|
"AXFR/IXFR requested for non-authoritative zone");
|
1999-10-14 00:06:11 +00:00
|
|
|
switch(dns_zone_gettype(zone)) {
|
|
|
|
case dns_zone_master:
|
|
|
|
case dns_zone_slave:
|
|
|
|
break; /* Master and slave zones are OK for transfer. */
|
|
|
|
default:
|
1999-10-26 19:40:18 +00:00
|
|
|
FAILC(DNS_R_NOTAUTH,
|
|
|
|
"AXFR/IXFR requested for non-authoritative zone");
|
1999-10-14 00:06:11 +00:00
|
|
|
}
|
|
|
|
CHECK(dns_zone_getdb(zone, &db));
|
1999-08-24 06:40:54 +00:00
|
|
|
dns_db_currentversion(db, &ver);
|
|
|
|
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(6), "%s question section OK",
|
|
|
|
mnemonic);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 == DNS_R_SUCCESS;
|
|
|
|
result = dns_message_nextname(request, DNS_SECTION_AUTHORITY))
|
|
|
|
{
|
|
|
|
soa_name = NULL;
|
1999-08-24 06:40:54 +00:00
|
|
|
dns_message_currentname(request, DNS_SECTION_AUTHORITY,
|
|
|
|
&soa_name);
|
1999-12-01 00:27:13 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/* 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 == DNS_R_SUCCESS)
|
1999-10-26 19:40:18 +00:00
|
|
|
FAILC(DNS_R_FORMERR,
|
|
|
|
"IXFR authority section "
|
|
|
|
"has multiple SOAs");
|
1999-08-20 05:35:16 +00:00
|
|
|
have_soa = ISC_TRUE;
|
|
|
|
goto got_soa;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
got_soa:
|
|
|
|
if (result != DNS_R_NOMORE)
|
|
|
|
CHECK(result);
|
|
|
|
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(6), "%s authority section OK",
|
|
|
|
mnemonic);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
1999-12-01 00:27:13 +00:00
|
|
|
/* Decide whether to allow this transfer. */
|
1999-12-16 23:11:07 +00:00
|
|
|
CHECK(dns_acl_checkrequest(client->signer,
|
1999-12-02 22:31:13 +00:00
|
|
|
ns_client_getsockaddr(client),
|
|
|
|
"zone transfer",
|
|
|
|
dns_zone_getxfracl(zone),
|
1999-12-16 23:11:07 +00:00
|
|
|
ns_g_server->transferacl,
|
1999-12-02 22:31:13 +00:00
|
|
|
ISC_TRUE));
|
1999-12-01 00:27:13 +00:00
|
|
|
|
|
|
|
/* AXFR over UDP is not possible. */
|
1999-08-24 06:40:54 +00:00
|
|
|
if (reqtype == dns_rdatatype_axfr &&
|
|
|
|
(client->attributes & NS_CLIENTATTR_TCP) == 0) {
|
1999-10-26 19:40:18 +00:00
|
|
|
FAILC(DNS_R_FORMERR, "attempted AXFR over UDP");
|
1999-08-24 06:40:54 +00:00
|
|
|
}
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/* 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;
|
|
|
|
|
|
|
|
if (! have_soa)
|
1999-10-26 19:40:18 +00:00
|
|
|
FAILC(DNS_R_FORMERR,
|
|
|
|
"IXFR request missing SOA");
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
begin_serial = dns_soa_getserial(&soa_rdata);
|
|
|
|
current_serial = dns_soa_getserial(¤t_soa_tuple->rdata);
|
1999-08-24 06:40:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) ||
|
1999-10-22 19:42:38 +00:00
|
|
|
(client->attributes & NS_CLIENTATTR_TCP) == 0)
|
1999-08-24 06:40:54 +00:00
|
|
|
{
|
1999-08-20 05:35:16 +00:00
|
|
|
CHECK(soa_rrstream_create(mctx, db, ver, &stream));
|
|
|
|
goto have_stream;
|
|
|
|
}
|
|
|
|
result = ixfr_rrstream_create(mctx,
|
1999-10-29 06:36:05 +00:00
|
|
|
dns_zone_getjournal(zone),
|
1999-08-20 05:35:16 +00:00
|
|
|
begin_serial,
|
|
|
|
current_serial,
|
|
|
|
&data_stream);
|
|
|
|
if (result == ISC_R_NOTFOUND ||
|
|
|
|
result == DNS_R_RANGE) {
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(4),
|
|
|
|
"IXFR version not in journal, "
|
|
|
|
"falling back to AXFR");
|
1999-08-20 05:35:16 +00:00
|
|
|
goto axfr_fallback;
|
|
|
|
}
|
|
|
|
CHECK(result);
|
|
|
|
} else {
|
|
|
|
axfr_fallback:
|
|
|
|
CHECK(axfr_rrstream_create(mctx, db, ver,
|
1999-10-21 00:36:43 +00:00
|
|
|
&data_stream));
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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));
|
1999-10-29 00:24:36 +00:00
|
|
|
soa_stream = NULL;
|
|
|
|
data_stream = NULL;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
have_stream:
|
1999-10-29 00:24:36 +00:00
|
|
|
/*
|
|
|
|
* Create the xfrout context object. This transfers the ownership
|
|
|
|
* of "stream", "db", and "ver" to the xfrout context object.
|
|
|
|
*/
|
1999-08-20 05:35:16 +00:00
|
|
|
CHECK(xfrout_ctx_create(mctx, client, request->id, question_name,
|
1999-09-10 15:01:04 +00:00
|
|
|
reqtype, db, ver, stream, request->tsigkey,
|
1999-12-02 22:31:13 +00:00
|
|
|
request->tsig,
|
1999-12-16 02:59:32 +00:00
|
|
|
dns_zone_getmaxxfrout(zone),
|
|
|
|
dns_zone_getidleout(zone),
|
1999-12-02 22:31:13 +00:00
|
|
|
&xfr));
|
1999-08-20 05:35:16 +00:00
|
|
|
stream = NULL;
|
1999-08-24 06:40:54 +00:00
|
|
|
db = NULL;
|
|
|
|
ver = NULL;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
CHECK(xfr->stream->methods->first(xfr->stream));
|
|
|
|
|
1999-10-29 00:24:36 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
result = DNS_R_SUCCESS;
|
1999-10-29 00:24:36 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
failure:
|
|
|
|
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);
|
1999-08-24 06:40:54 +00:00
|
|
|
if (ver != NULL)
|
|
|
|
dns_db_closeversion(db, &ver, ISC_FALSE);
|
|
|
|
if (db != NULL)
|
|
|
|
dns_db_detach(&db);
|
1999-10-14 01:37:00 +00:00
|
|
|
if (zone != NULL)
|
|
|
|
dns_zone_detach(&zone);
|
1999-08-20 05:35:16 +00:00
|
|
|
/* XXX kludge */
|
|
|
|
if (xfr != NULL) {
|
1999-10-26 19:40:18 +00:00
|
|
|
xfrout_fail(xfr, result, "setting up zone transfer");
|
1999-10-29 00:24:36 +00:00
|
|
|
} else if (result != DNS_R_SUCCESS) {
|
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(3),
|
1999-10-26 19:40:18 +00:00
|
|
|
"zone transfer setup failed");
|
1999-08-24 06:40:54 +00:00
|
|
|
ns_client_error(client, result);
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-08-20 05:35:16 +00:00
|
|
|
xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
|
|
|
|
dns_name_t *qname, dns_rdatatype_t qtype,
|
|
|
|
dns_db_t *db, dns_dbversion_t *ver,
|
1999-10-08 18:37:24 +00:00
|
|
|
rrstream_t *stream, dns_tsigkey_t *tsigkey,
|
1999-12-16 02:59:32 +00:00
|
|
|
dns_rdata_any_tsig_t *lasttsig, unsigned int maxtime,
|
|
|
|
unsigned int idletime, xfrout_ctx_t **xfrp)
|
1999-08-20 05:35:16 +00:00
|
|
|
{
|
|
|
|
xfrout_ctx_t *xfr;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
unsigned int len;
|
|
|
|
void *mem;
|
1999-12-16 02:59:32 +00:00
|
|
|
isc_interval_t maxinterval, idleinterval;
|
|
|
|
isc_time_t expires;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
INSIST(xfrp != NULL && *xfrp == NULL);
|
|
|
|
xfr = isc_mem_get(mctx, sizeof(*xfr));
|
|
|
|
if (xfr == NULL)
|
|
|
|
return (DNS_R_NOMEMORY);
|
|
|
|
xfr->mctx = mctx;
|
|
|
|
xfr->client = client;
|
2000-01-12 01:17:34 +00:00
|
|
|
ns_client_wait(client);
|
1999-08-20 05:35:16 +00:00
|
|
|
xfr->id = id;
|
|
|
|
xfr->qname = qname;
|
|
|
|
xfr->qtype = qtype;
|
|
|
|
xfr->db = db;
|
|
|
|
xfr->ver = ver;
|
|
|
|
xfr->stream = stream;
|
1999-12-02 22:31:13 +00:00
|
|
|
xfr->end_of_stream = ISC_FALSE;
|
1999-09-10 15:01:04 +00:00
|
|
|
xfr->tsigkey = tsigkey;
|
|
|
|
xfr->lasttsig = lasttsig;
|
1999-08-20 05:35:16 +00:00
|
|
|
xfr->txmem = NULL;
|
|
|
|
xfr->txmemlen = 0;
|
|
|
|
xfr->nmsg = 0;
|
1999-12-02 22:31:13 +00:00
|
|
|
xfr->timer = NULL;
|
|
|
|
xfr->sends = 0;
|
|
|
|
xfr->shuttingdown = ISC_FALSE;
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/*
|
|
|
|
* 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 = DNS_R_NOMEMORY;
|
1999-12-16 02:59:32 +00:00
|
|
|
goto failure;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
isc_buffer_init(&xfr->buf, mem, len, ISC_BUFFERTYPE_BINARY);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 = DNS_R_NOMEMORY;
|
1999-12-16 02:59:32 +00:00
|
|
|
goto failure;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
isc_buffer_init(&xfr->txlenbuf, mem, 2, ISC_BUFFERTYPE_BINARY);
|
|
|
|
isc_buffer_init(&xfr->txbuf, (char *) mem + 2, len - 2,
|
|
|
|
ISC_BUFFERTYPE_BINARY);
|
|
|
|
xfr->txmem = mem;
|
|
|
|
xfr->txmemlen = len;
|
1999-12-02 22:31:13 +00:00
|
|
|
|
1999-12-16 02:59:32 +00:00
|
|
|
isc_interval_set(&maxinterval, maxtime, 0);
|
|
|
|
CHECK(isc_time_nowplusinterval(&expires, &maxinterval));
|
|
|
|
isc_interval_set(&idleinterval, idletime, 0);
|
|
|
|
|
|
|
|
CHECK(isc_timer_create(ns_g_timermgr, isc_timertype_once,
|
|
|
|
&expires, &idleinterval,
|
|
|
|
xfr->client->task,
|
|
|
|
xfrout_timeout, xfr, &xfr->timer));
|
2000-01-12 18:01:12 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
*xfrp = xfr;
|
|
|
|
return (DNS_R_SUCCESS);
|
|
|
|
|
1999-12-16 02:59:32 +00:00
|
|
|
failure:
|
1999-08-20 05:35:16 +00:00
|
|
|
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 *msg = NULL;
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t result;
|
1999-08-20 05:35:16 +00:00
|
|
|
isc_region_t used;
|
|
|
|
isc_region_t region;
|
1999-10-07 19:33:12 +00:00
|
|
|
dns_rdataset_t *qrdataset;
|
1999-08-20 05:35:16 +00:00
|
|
|
int n_rrs;
|
|
|
|
|
|
|
|
isc_buffer_clear(&xfr->buf);
|
|
|
|
isc_buffer_clear(&xfr->txlenbuf);
|
|
|
|
isc_buffer_clear(&xfr->txbuf);
|
1999-10-29 00:24:36 +00:00
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
msg = NULL;
|
|
|
|
CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTRENDER, &msg));
|
|
|
|
|
|
|
|
msg->id = xfr->id;
|
|
|
|
msg->rcode = dns_rcode_noerror;
|
|
|
|
msg->flags = DNS_MESSAGEFLAG_QR | DNS_MESSAGEFLAG_AA;
|
1999-12-10 23:58:04 +00:00
|
|
|
if ((xfr->client->attributes & NS_CLIENTATTR_RA) != 0)
|
|
|
|
msg->flags |= DNS_MESSAGEFLAG_RA;
|
1999-09-10 15:01:04 +00:00
|
|
|
msg->tsigkey = xfr->tsigkey;
|
|
|
|
msg->querytsig = xfr->lasttsig;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
1999-10-07 19:33:12 +00:00
|
|
|
qrdataset = NULL;
|
|
|
|
result = dns_message_gettemprdataset(msg, &qrdataset);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto failure;
|
|
|
|
dns_rdataset_init(qrdataset);
|
|
|
|
dns_rdataset_makequestion(qrdataset,
|
1999-08-24 06:40:54 +00:00
|
|
|
xfr->client->message->rdclass,
|
1999-08-20 05:35:16 +00:00
|
|
|
xfr->qtype);
|
|
|
|
|
1999-10-07 19:33:12 +00:00
|
|
|
result = dns_message_gettempname(msg, &qname);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto failure;
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_name_init(qname, NULL);
|
|
|
|
isc_buffer_available(&xfr->buf, &r);
|
|
|
|
INSIST(r.length >= xfr->qname->length);
|
|
|
|
r.length = xfr->qname->length;
|
1999-08-24 06:40:54 +00:00
|
|
|
isc_buffer_putmem(&xfr->buf, xfr->qname->ndata,
|
|
|
|
xfr->qname->length);
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_name_fromregion(qname, &r);
|
|
|
|
ISC_LIST_INIT(qname->list);
|
1999-10-07 19:33:12 +00:00
|
|
|
ISC_LIST_APPEND(qname->list, qrdataset, link);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
|
|
|
|
}
|
1999-09-10 15:01:04 +00:00
|
|
|
else
|
|
|
|
msg->tcp_continuation = 1;
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
dns_name_t *msgname = NULL;
|
|
|
|
dns_rdata_t *msgrdata = NULL;
|
|
|
|
dns_rdatalist_t *msgrdl = NULL;
|
|
|
|
dns_rdataset_t *msgrds = NULL;
|
|
|
|
|
|
|
|
unsigned int size;
|
|
|
|
isc_region_t r;
|
|
|
|
|
1999-08-24 06:40:54 +00:00
|
|
|
xfr->stream->methods->current(xfr->stream,
|
|
|
|
&name, &ttl, &rdata);
|
1999-08-20 05:35:16 +00:00
|
|
|
size = name->length + 10 + rdata->length;
|
|
|
|
isc_buffer_available(&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) {
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_COMMON_LOGARGS,
|
|
|
|
ISC_LOG_WARNING,
|
|
|
|
"RR too large for zone transfer "
|
|
|
|
"(%d bytes)", size);
|
|
|
|
/* XXX DNS_R_RRTOOLARGE? */
|
|
|
|
result = ISC_R_NOSPACE;
|
|
|
|
goto failure;
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1999-10-26 19:40:18 +00:00
|
|
|
log_rr(name, rdata, ttl); /* XXX */
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
dns_message_gettempname(msg, &msgname);
|
|
|
|
dns_name_init(msgname, NULL);
|
|
|
|
isc_buffer_available(&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);
|
|
|
|
|
|
|
|
dns_message_gettemprdata(msg, &msgrdata);
|
|
|
|
isc_buffer_available(&xfr->buf, &r);
|
|
|
|
r.length = rdata->length;
|
|
|
|
isc_buffer_putmem(&xfr->buf, rdata->data, rdata->length);
|
|
|
|
dns_rdata_init(msgrdata);
|
1999-08-24 06:40:54 +00:00
|
|
|
dns_rdata_fromregion(msgrdata,
|
|
|
|
rdata->rdclass, rdata->type, &r);
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
dns_message_gettemprdatalist(msg, &msgrdl);
|
|
|
|
msgrdl->type = rdata->type;
|
|
|
|
msgrdl->rdclass = rdata->rdclass;
|
|
|
|
msgrdl->ttl = ttl;
|
|
|
|
ISC_LIST_INIT(msgrdl->rdata);
|
|
|
|
ISC_LINK_INIT(msgrdl, link);
|
|
|
|
ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link);
|
|
|
|
|
|
|
|
dns_message_gettemprdataset(msg, &msgrds);
|
|
|
|
dns_rdataset_init(msgrds);
|
|
|
|
result = dns_rdatalist_tordataset(msgrdl, msgrds);
|
|
|
|
INSIST(result == DNS_R_SUCCESS);
|
|
|
|
|
|
|
|
ISC_LIST_APPEND(msgname->list, msgrds, link);
|
|
|
|
|
|
|
|
dns_message_addname(msg, msgname, DNS_SECTION_ANSWER);
|
|
|
|
|
|
|
|
result = xfr->stream->methods->next(xfr->stream);
|
|
|
|
if (result == DNS_R_NOMORE) {
|
1999-12-02 22:31:13 +00:00
|
|
|
xfr->end_of_stream = ISC_TRUE;
|
1999-08-20 05:35:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
CHECK(result);
|
|
|
|
|
1999-10-25 20:22:39 +00:00
|
|
|
/* XXX per-server, too */
|
1999-12-16 23:11:07 +00:00
|
|
|
if (ns_g_server->transfer_format == dns_one_answer)
|
1999-08-20 05:35:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1999-08-24 06:40:54 +00:00
|
|
|
if ((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0) {
|
|
|
|
CHECK(dns_message_renderbegin(msg, &xfr->txbuf));
|
1999-12-22 03:22:59 +00:00
|
|
|
CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0));
|
|
|
|
CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0));
|
1999-08-24 06:40:54 +00:00
|
|
|
CHECK(dns_message_renderend(msg));
|
|
|
|
|
|
|
|
isc_buffer_used(&xfr->txbuf, &used);
|
|
|
|
isc_buffer_putuint16(&xfr->txlenbuf, used.length);
|
|
|
|
region.base = xfr->txlenbuf.base;
|
|
|
|
region.length = 2 + used.length;
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(8),
|
|
|
|
"sending zone transfer TCP message of %d bytes",
|
|
|
|
used.length);
|
1999-08-24 06:40:54 +00:00
|
|
|
CHECK(isc_socket_send(xfr->client->tcpsocket, /* XXX */
|
|
|
|
®ion, xfr->client->task,
|
1999-12-02 22:31:13 +00:00
|
|
|
xfrout_senddone,
|
1999-08-24 06:40:54 +00:00
|
|
|
xfr));
|
1999-12-02 22:31:13 +00:00
|
|
|
xfr->sends++;
|
1999-08-24 06:40:54 +00:00
|
|
|
} else {
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(8),
|
1999-12-21 00:33:17 +00:00
|
|
|
"sending IXFR UDP response");
|
1999-08-24 06:40:54 +00:00
|
|
|
/* XXX kludge */
|
|
|
|
dns_message_destroy(&xfr->client->message);
|
|
|
|
xfr->client->message = msg;
|
|
|
|
msg = NULL;
|
|
|
|
ns_client_send(xfr->client);
|
|
|
|
xfrout_ctx_destroy(&xfr);
|
|
|
|
return;
|
|
|
|
}
|
1999-09-10 15:01:04 +00:00
|
|
|
|
|
|
|
/* Advance lasttsig to be the last TSIG generated */
|
|
|
|
xfr->lasttsig = msg->tsig;
|
|
|
|
|
|
|
|
/* Clear this field so the TSIG is not destroyed */
|
|
|
|
msg->tsig = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is the first message, clear this too, since this is
|
|
|
|
* also pointed to by the request.
|
|
|
|
*/
|
|
|
|
if (xfr->nmsg == 0)
|
|
|
|
msg->querytsig = NULL;
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
xfr->nmsg++;
|
|
|
|
|
|
|
|
failure:
|
1999-10-07 19:33:12 +00:00
|
|
|
/*
|
|
|
|
* XXXRTH need to cleanup qname and qrdataset...
|
|
|
|
*/
|
1999-09-10 15:01:04 +00:00
|
|
|
if (msg != NULL) {
|
1999-08-20 05:35:16 +00:00
|
|
|
dns_message_destroy(&msg);
|
1999-09-10 15:01:04 +00:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
if (result == DNS_R_SUCCESS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
xfrout_fail(xfr, result, "sending zone data");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xfrout_ctx_destroy(xfrout_ctx_t **xfrp) {
|
|
|
|
xfrout_ctx_t *xfr = *xfrp;
|
|
|
|
|
1999-12-02 22:31:13 +00:00
|
|
|
INSIST(xfr->sends == 0);
|
2000-01-12 18:01:12 +00:00
|
|
|
|
|
|
|
xfr->client->shutdown = NULL;
|
|
|
|
xfr->client->shutdown_arg = NULL;
|
|
|
|
|
1999-12-02 22:31:13 +00:00
|
|
|
if (xfr->timer != NULL)
|
|
|
|
isc_timer_detach(&xfr->timer);
|
1999-08-20 05:35:16 +00:00
|
|
|
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);
|
1999-09-10 15:01:04 +00:00
|
|
|
if (xfr->lasttsig != NULL) {
|
|
|
|
dns_rdata_freestruct(xfr->lasttsig);
|
|
|
|
isc_mem_put(xfr->mctx, xfr->lasttsig, sizeof(*xfr->lasttsig));
|
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
|
|
|
|
/* XXX */
|
|
|
|
if (xfr->ver != NULL)
|
|
|
|
dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
|
|
|
|
if (xfr->db != NULL)
|
|
|
|
dns_db_detach(&xfr->db);
|
|
|
|
|
2000-01-12 01:17:34 +00:00
|
|
|
ns_client_unwait(xfr->client);
|
|
|
|
|
1999-08-20 05:35:16 +00:00
|
|
|
isc_mem_put(xfr->mctx, xfr, sizeof(*xfr));
|
|
|
|
|
|
|
|
*xfrp = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1999-12-02 22:31:13 +00:00
|
|
|
xfrout_senddone(isc_task_t *task, isc_event_t *event) {
|
|
|
|
isc_socketevent_t *sev = (isc_socketevent_t *) event;
|
1999-08-20 05:35:16 +00:00
|
|
|
xfrout_ctx_t *xfr = (xfrout_ctx_t *) event->arg;
|
1999-12-02 22:31:13 +00:00
|
|
|
isc_result_t evresult = sev->result;
|
1999-08-20 05:35:16 +00:00
|
|
|
task = task; /* Unused */
|
|
|
|
INSIST(event->type == ISC_SOCKEVENT_SENDDONE);
|
1999-12-02 22:31:13 +00:00
|
|
|
isc_event_free(&event);
|
|
|
|
xfr->sends--;
|
|
|
|
INSIST(xfr->sends == 0);
|
|
|
|
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. */
|
|
|
|
isc_log_write(XFROUT_DEBUG_LOGARGS(6),
|
|
|
|
"end of outgoing zone transfer");
|
|
|
|
ns_client_next(xfr->client, DNS_R_SUCCESS);
|
|
|
|
xfrout_ctx_destroy(&xfr);
|
1999-08-24 09:39:48 +00:00
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1999-12-02 22:31:13 +00:00
|
|
|
xfrout_timeout(isc_task_t *task, isc_event_t *event) {
|
1999-08-20 05:35:16 +00:00
|
|
|
xfrout_ctx_t *xfr = (xfrout_ctx_t *) event->arg;
|
|
|
|
task = task; /* Unused */
|
1999-12-02 22:31:13 +00:00
|
|
|
/* This will log "giving up: timeout". */
|
|
|
|
xfrout_fail(xfr, ISC_R_TIMEDOUT, "giving up");
|
1999-08-24 09:39:48 +00:00
|
|
|
isc_event_free(&event);
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1999-12-23 00:09:04 +00:00
|
|
|
xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, char *msg)
|
1999-08-20 05:35:16 +00:00
|
|
|
{
|
1999-12-02 22:31:13 +00:00
|
|
|
xfr->shuttingdown = ISC_TRUE;
|
1999-10-26 19:40:18 +00:00
|
|
|
isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_ERROR,
|
1999-12-02 22:31:13 +00:00
|
|
|
"outgoing zone transfer: %s: %s",
|
1999-10-26 19:40:18 +00:00
|
|
|
msg, isc_result_totext(result));
|
1999-12-02 22:31:13 +00:00
|
|
|
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);
|
|
|
|
}
|
1999-08-20 05:35:16 +00:00
|
|
|
}
|
2000-01-12 18:01:12 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
xfrout_client_shutdown(void *arg)
|
|
|
|
{
|
|
|
|
xfrout_ctx_t *xfr = (xfrout_ctx_t *) arg;
|
|
|
|
xfrout_fail(xfr, ISC_R_SHUTTINGDOWN, "aborted");
|
|
|
|
}
|