xfrin.c revision 7cd594b8427fe742d44295790ba367e1de22a47d
/*
* Copyright (C) 1999-2008, 2011-2017 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* $Id$ */
/*! \file */
#include <config.h>
#include <dns/callbacks.h>
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
/*
* Incoming AXFR and IXFR.
*/
/*%
* It would be non-sensical (or at least obtuse) to use FAIL() with an
* ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler
* from complaining about "end-of-loop code not reached".
*/
} while (0)
} 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 {
unsigned int magic;
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 */
unsigned int nrecs; /*%< Number of records recvd */
unsigned int maxrecords; /*%< The maximum number of
records set for the zone */
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 {
} ixfr;
};
/**************************************************************************/
/*
* Forward declarations.
*/
static isc_result_t
dns_xfrin_ctx_t **xfrp);
dns_rdata_t *rdata);
dns_rdata_t *rdata);
static void
static isc_result_t
static void
ISC_FORMAT_PRINTF(4, 0);
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);
if (result == ISC_R_SUCCESS) {
}
return (result);
}
static isc_result_t
{
return(DNS_R_BADCLASS);
return (result);
}
/*
* Store a set of AXFR RRs in the database.
*/
static isc_result_t
if (xfr->maxrecords != 0U) {
goto failure;
}
}
return (result);
}
static isc_result_t
return (result);
}
static isc_result_t
return (result);
}
/**************************************************************************/
/*
* IXFR handling
*/
static isc_result_t
char *journalfile;
"got incremental response to AXFR request");
return (DNS_R_FORMERR);
}
if (journalfile != NULL)
return (result);
}
static isc_result_t
{
return(DNS_R_BADCLASS);
if (op == DNS_DIFFOP_ADD)
return (result);
}
/*
* Apply a set of IXFR changes to the database.
*/
static isc_result_t
}
if (xfr->maxrecords != 0U) {
goto failure;
}
}
if (result != ISC_R_SUCCESS)
goto failure;
}
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:
"non-SOA response to SOA query");
}
"requested serial %u, "
"master has %u, not updating",
}
break;
case XFRST_GOTSOA:
/*
* Skip other records in the answer section.
*/
break;
case XFRST_INITIALSOA:
"first RR in zone transfer must be SOA");
}
/*
* Remember the serial number in the initial 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;
"IXFR out of sync: "
"expected serial %u, got %u",
} else {
goto redo;
}
}
break;
case XFRST_AXFR:
/*
* Old BINDs sent cross class A records for non IN classes.
*/
break;
break;
}
break;
case XFRST_AXFR_END:
case XFRST_IXFR_END:
/* NOTREACHED */
/* FALLTHROUGH */
default:
INSIST(0);
break;
}
return (result);
}
{
switch (isc_sockaddr_pf(masteraddr)) {
case PF_INET:
break;
case PF_INET6:
break;
default:
INSIST(0);
}
}
{
}
{
dns_db_detach(&db);
if (result != ISC_R_SUCCESS) {
"zone transfer setup failed");
}
return (result);
}
void
if (! xfr->shuttingdown)
}
void
}
void
}
static void
}
}
static void
if (xfr->tcpmsg_valid) {
}
}
static void
/* Pass special result code to force AXFR retry */
}
/*
* Close the journal.
*/
}
}
static isc_result_t
{
return (ISC_R_NOMEMORY);
/* sockaddr */
/* qbuffer */
/* qbuffer_data */
/* tcpmsg */
if (reqtype == dns_rdatatype_soa)
else
/* end_serial */
/* ixfr.request_serial */
/* ixfr.current_serial */
ISC_FALSE));
/*
* Reserve 2 bytes for TCP length at the begining of the buffer.
*/
return (ISC_R_SUCCESS);
return (result);
}
static isc_result_t
#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
#endif
return (ISC_R_SUCCESS);
return (result);
}
/* XXX the resolver could use this, too */
static isc_result_t
if (cleanup_cctx)
return (result);
}
/*
* A connection has been established.
*/
static void
char signerbuf[DNS_NAME_FORMATSIZE];
if (xfr->shuttingdown) {
return;
}
if (result != ISC_R_SUCCESS) {
goto failure;
} else
&xfr->sourceaddr);
}
if (result == ISC_R_SUCCESS) {
} else {
}
sep = " TSIG ";
}
if (result != ISC_R_SUCCESS)
}
/*
* Convert a tuple into a dns_name_t suitable for inserting
* into the given dns_message_t.
*/
static isc_result_t
{
return (ISC_R_SUCCESS);
}
}
return (result);
}
/*
* Build an *XFR request and send its length prefix.
*/
static isc_result_t
/* 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",
/*
* Free the last tsig, if there is one.
*/
/*
* Save the query TSIG and don't let message_destroy free it.
*/
/*
* Record message length and adjust region to include TCP
* length field.
*/
xfrin_send_done, xfr));
return (result);
}
static void
xfrin_recv_done, xfr));
if (result != ISC_R_SUCCESS)
}
static void
isc_event_free(&ev);
if (xfr->shuttingdown) {
return;
}
if (result == ISC_R_SUCCESS)
else
else if (result == ISC_R_SUCCESS &&
else if (result == ISC_R_SUCCESS &&
goto failure;
(void)xfrin_start(xfr);
return;
}
/*
* Does the server know about IXFR? If it doesn't we will get
* a message with a empty answer section or a potentially a CNAME /
* DNAME, the later is handled by xfr_rr() which will return FORMERR
* if the first RR in the answer section is not a SOA record.
*/
"empty answer section, retrying with AXFR");
goto try_axfr;
}
}
if (result != ISC_R_SUCCESS) {
goto failure;
}
result == ISC_R_SUCCESS;
{
{
result == ISC_R_SUCCESS;
{
}
}
}
if (result != ISC_R_NOMORE)
goto failure;
/*
* Reset the counter.
*/
/*
* Free the last tsig, if there is one.
*/
/*
* Update the last tsig pointer.
*/
{
goto failure;
}
}
/*
* Update the number of messages received.
*/
/*
* Update the number of bytes received.
*/
/*
* Take the context back.
*/
case XFRST_GOTSOA:
break;
case XFRST_AXFR_END:
/* FALLTHROUGH */
case XFRST_IXFR_END:
/*
* Close the journal.
*/
/*
* Inform the caller we succeeded.
*/
}
/*
* We should have no outstanding events at this
* point, thus maybe_free() should succeed.
*/
break;
default:
/*
* Read the next message.
*/
xfrin_recv_done, xfr));
}
return;
if (result != ISC_R_SUCCESS)
}
static void
/*
* This will log "giving up: timeout".
*/
}
static void
const char *result_str;
return;
/* If we're called through dns_xfrin_detach() and are not
* shutting down, we can't know what the transfer status is as
* we are only called when the last reference is lost.
*/
/*
* Calculate the length of time the transfer took,
* and print a log message with the bytes and rate.
*/
if (msecs == 0)
msecs = 1;
"Transfer completed: %d messages, %d records, "
(unsigned int) persec);
if (xfr->tcpmsg_valid)
}
/*
* Log incoming zone transfer messages in a format like
* transfer of <zone> from <address>: <message>
*/
static void
{
char msgtext[2048];
"transfer of '%s' from %s: %s",
}
/*
* Logging function for use when a xfrin_ctx_t has not yet been created.
*/
static void
const char *fmt, ...)
{
return;
}
/*
* Logging function for use when there is a xfrin_ctx_t.
*/
static void
{
return;
}