xfrin.c revision d497aeb8cf1d9316dbab6921792055558510965f
/*
* Copyright (C) 1999, 2000 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 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.
*/
/* $Id: xfrin.c,v 1.55 2000/03/28 03:18:02 bwelling Exp $ */
#include <config.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <isc/assertions.h>
#include <dns/dbiterator.h>
#include <dns/fixedname.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
/*
* Incoming AXFR and IXFR.
*/
} while (0)
/*
* The states of the *XFR state machine. We handle both IXFR and AXFR
* with a single integrated state machine because they cannot be distinguished
* immediately - an AXFR response to an IXFR request can only be detected
* when the first two (2) response RRs have already been received.
*/
typedef enum {
/*
* Incoming zone transfer context.
*/
struct dns_xfrin_ctx {
int refcount;
int connects; /* Connect in progress */
int sends; /* Send in progress */
int recvs; /* Receive in progress */
/*
* Requested transfer type (dns_rdatatype_axfr or
* dns_rdatatype_ixfr). The actual transfer type
* may differ due to IXFR->AXFR fallback.
*/
unsigned char qbuffer_data[512];
/* Incoming reply TCP message */
int difflen; /* Number of pending tuples */
unsigned int nmsg; /* Number of messages recvd */
void *tsigctx; /* TSIG verification context */
unsigned int sincetsig; /* recvd since the last TSIG */
/*
* AXFR- and IXFR-specific data. Only one is used at a time
* according to the is_ixfr flag, so this could be a union,
* but keeping them separate makes it a bit simpler to clean
* things up when destroying the context.
*/
struct {
} axfr;
struct {
} ixfr;
};
/**************************************************************************/
/*
* Forward declarations.
*/
static isc_result_t
dns_xfrin_ctx_t **xfrp);
dns_rdata_t *rdata);
dns_rdata_t *rdata);
static void
static void
const char *fmt, ...);
static void
/**************************************************************************/
/*
* AXFR handling
*/
static isc_result_t
return (result);
}
static isc_result_t
"rbt", /* XXX guess */
0, NULL, /* XXX guess */
dbp));
}
static isc_result_t
{
return (result);
}
/* Store a set of AXFR RRs in the database. */
static isc_result_t
return (result);
}
static isc_result_t
return (result);
}
/**************************************************************************/
/*
* IXFR handling
*/
static isc_result_t
return (result);
}
static isc_result_t
{
return (result);
}
/* Apply a set of IXFR changes to the database. */
static isc_result_t
}
return (result);
}
static isc_result_t
/* XXX enter ready-to-commit state here */
}
return (result);
}
/**************************************************************************/
/*
*/
/*
* Handle a single incoming resource record according to the current
* state.
*/
static isc_result_t
{
redo:
case XFRST_SOAQUERY:
"requested serial %u, "
"master has %u, not updating",
}
break;
case XFRST_GOTSOA:
/*
* skip other records in the answer section
*/
break;
case XFRST_INITIALSOA:
/*
* Remember the serial number in the intial SOA.
* We need it to recognize the end of an IXFR.
*/
{
/*
* This must be the single SOA record that is
* sent when the current version on the master
* is not newer than the version in the request.
*/
"requested serial %u, "
"master has %u, not updating",
}
break;
case XFRST_FIRSTDATA:
/*
* If the transfer begins with one SOA record, it is an AXFR,
* if it begins with two SOAs, it is an IXFR.
*/
"got incremental response");
} else {
"got nonincremental response");
}
goto redo;
case XFRST_IXFR_DELSOA:
break;
case XFRST_IXFR_DEL:
goto redo;
}
break;
case XFRST_IXFR_ADDSOA:
break;
case XFRST_IXFR_ADD:
break;
} else {
goto redo;
}
}
break;
case XFRST_AXFR:
break;
}
break;
case XFRST_END:
default:
INSIST(0);
break;
}
return (result);
}
{
dns_xfrin_ctx_t *xfr, *x;
int nxfrsin, nxfrsperns;
/*
* Find any configured information about the server we are about
* to transfer from.
*/
if (result == DNS_R_NOTLOADED)
else
/*
* Decide whether we should request IXFR or AXFR.
*/
"no database exists yet, "
"requesting AXFR of initial version");
} else {
; /* Using peer setting */
} else {
}
"IXFR disabled, requesting AXFR");
} else {
"requesting IXFR");
}
}
/*
* Determine the maximum number of simultaneous transfers
* allowed for this server, then count the number of
* transfers already in progress and fail if the quota
* is already full.
*
* Count the number of transfers that are in progress from
* this master. We linearly scan a list of all transfers;
* if this turns out to be too slow, we could hash on the
* master address.
*
* Note that we must keep the transfer list locked for an
* awkwardly long time because the scanning of the list
* and the creation of a new entry must be done atomically,
* and we don't want to create the transfer object until we
* know there is quota available.
*/
}
nxfrsin = nxfrsperns = 0;
x != NULL;
x = ISC_LIST_NEXT(x, link))
{
nxfrsin++;
nxfrsperns++;
}
goto unlock;
}
zone,
db,
task,
if (result != ISC_R_SUCCESS)
goto unlock;
dns_db_detach(&db);
if (result != DNS_R_SUCCESS)
"zone transfer setup failed");
return (result);
}
if (! xfr->shuttingdown)
}
}
static void
if (result != DNS_R_UPTODATE) {
}
}
}
}
static isc_result_t
{
return (DNS_R_NOMEMORY);
/* sockaddr */
/* qbuffer */
/* qbuffer_data */
/* tcpmsg */
/* end_serial */
/* is_ixfr */
/* ixfr.request_serial */
/* ixfr.end_serial */
switch (isc_sockaddr_pf(masteraddr)) {
case PF_INET:
break;
case PF_INET6:
break;
default:
INSIST(0);
}
sizeof(xfr->qbuffer_data),
return (DNS_R_SUCCESS);
return (result);
}
static isc_result_t
return (ISC_R_SUCCESS);
return (result);
}
/* XXX the resolver could use this, too */
static isc_result_t
return (result);
}
/*
* A connection has been established.
*/
static void
if (xfr->shuttingdown) {
return;
}
if (result != DNS_R_SUCCESS)
}
/*
* Convert a tuple into a dns_name_t suitable for inserting
* into the given dns_message_t.
*/
static isc_result_t
{
return (result);
}
/*
* Build an *XFR request and send its length prefix.
*/
static isc_result_t
unsigned char length[2];
/* Create the request message */
/* Create a name for the question section. */
/* Formulate the question and attach it to the question name. */
/* Get the SOA and add it to the authority section. */
/* XXX is using the current version the right thing? */
"requesting IXFR for serial %u",
}
/* Save the query TSIG and don't let message_destroy free it */
return (result);
}
/* XXX there should be library support for sending DNS TCP messages */
static void
{
if (xfr->shuttingdown) {
return;
}
xfrin_send_done, xfr));
if (result != DNS_R_SUCCESS)
}
static void
{
xfrin_recv_done, xfr));
if (result != DNS_R_SUCCESS)
}
static void
isc_event_free(&ev);
if (xfr->shuttingdown) {
return;
}
if (result == DNS_R_SUCCESS)
return;
}
result == DNS_R_SUCCESS;
{
{
result == DNS_R_SUCCESS;
{
}
}
}
if (result != DNS_R_NOMORE)
goto failure;
/* Reset the counter */
/* Free the last tsig, if there is one */
}
/* Update the last tsig pointer */
/* Reset msg->tsig so it doesn't get freed */
{
goto failure;
}
}
/* Update the number of messages received */
/* Reset msg->querytsig so it doesn't get freed */
/* Copy the context back */
/*
* Inform the caller we succeeded.
*/
}
/*
* We should have no outstanding events at this
* point, thus maybe_free() should succeed.
*/
} else {
/* Read the next message. */
xfrin_recv_done, xfr));
}
return;
}
if (result != DNS_R_SUCCESS)
}
static void
/* This will log "giving up: timeout". */
}
static void
return;
}
}
if (xfr->tcpmsg_valid)
}
/*
* Log incoming zone transfer messages in a format like
* transfer of <zone> from <address>: <message>
*/
static void
{
char znmem[1024];
char mastermem[256];
char msgmem[2048];
if (result != DNS_R_SUCCESS) {
strlen("<UNKNOWN>"));
}
if (result != ISC_R_SUCCESS)
}
/* Logging function for use when a xfrin_ctx_t has not yet been created. */
static void
const char *fmt, ...)
{
}
/* Logging function for use when there is a xfrin_ctx_t. */
static void
{
}
}
}