xfrin.c revision 0f78de4d61441acbb0af7088c2dfda60a7ed5500
/*
* Copyright (C) 1999 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.6 1999/09/10 15:01:04 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/fixedname.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/dbiterator.h>
/*
* Incoming AXFR and IXFR.
*/
/*
* TODO:
*
* maintenance based on SOA timers (with support for the "dialup" option)
* transmitting SOA queries
* more error checking
*/
} while (0)
typedef struct xfrin_ctx xfrin_ctx_t;
/*
* 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 xfrin_ctx {
int recvs; /* Number of receives in progress */
int tasks; /* Number of active tasks (0 or 1) */
/*
* 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 dns_result_t
char *addrstr, /* XXX */
unsigned int port,
xfrin_ctx_t **xfrp);
dns_rdata_t *rdata);
dns_rdata_t *rdata);
static dns_result_t
char *journal_filename);
/**************************************************************************/
/*
* AXFR handling
*/
static dns_result_t
}
return (result);
}
static dns_result_t
"rbt", /* XXX guess */
0, NULL, /* XXX guess */
dbp));
}
static dns_result_t
{
return (result);
}
/* Store a set of AXFR RRs in the database. */
static dns_result_t
return (result);
}
/*
* Install the newly AXFR'ed database, and generate diffs if
* configured to do so.
*
* XXX Temporary code for testing only. This should all be
* part of the zone object functionality, and shared between
* AXFR and reloading of manually maintained master files.
*/
static dns_result_t
{
/*
* XXX should be configurable per-zone with
* somehting like "journal-from-axfr yes"?
*
* The initial version of a slave zone is always dumped;
* subsequent versions may be journalled instead.
*/
printf("generating diffs\n");
"journal"));
} else {
printf("dumping new version\n");
/* XXX should use temporary file and rename */
printf("unlinking journal\n");
}
/* Replace the database (XXX atomically). */
printf("replacing database...");
printf("done\n");
return (result);
}
static dns_result_t
return (result);
}
/**************************************************************************/
/*
* IXFR handling
*/
static dns_result_t
return (result);
}
static dns_result_t
{
return (result);
}
/* Apply a set of IXFR changes to the database. */
static dns_result_t
}
return (result);
}
static dns_result_t
/* XXX enter ready-to-commit state here */
}
return (result);
}
/**************************************************************************/
/*
*/
/*
* Handle a single incoming resource record according to the current
* state.
*/
static dns_result_t
{
redo:
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.
*/
printf("requested %u, master has %u, not updating\n",
}
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.
*/
} else {
}
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:
break;
default:
INSIST(0);
break;
}
return (result);
}
static void
unsigned int len;
== DNS_R_SUCCESS);
if (result == DNS_R_NOTFOUND) {
printf("no database exists, trying to create with axfr\n");
} else {
printf("database exists, trying ixfr\n");
}
dbi,
db,
task,
name,
}
void
xfrin_test(void) {
}
}
}
/* The rest will be done when the task runs its shutdown event. */
}
static void
if (result != DNS_R_UPTODATE) {
printf("error in incoming zone transfer: %s: %s\n",
}
}
char *addrstr, /* XXX */
unsigned int port,
xfrin_ctx_t **xfrp)
{
return (DNS_R_NOMEMORY);
/* sockaddr */
/* qbuffer */
/* qbuffer_data */
/* tcpmsg */
/* end_serial */
/* is_ixfr */
/* ixfr.request_serial */
/* ixfr.end_serial */
sizeof(xfr->qbuffer_data),
return (DNS_R_SUCCESS);
return (result);
}
void
return;
}
/* XXX the resolver could use this, too */
static dns_result_t
return (result);
}
/*
* A connection has been established.
*/
static void
printf("connected\n");
return;
}
/*
* Build an *XFR request and send its length prefix.
*/
static dns_result_t
unsigned char length[2];
/* Get the SOA. */
/* XXX is using the current version the right thing? */
printf("requesting IXFR for serial %u\n",
/* Create a dns_rdatalist_t */
}
/* Save the query TSIG and don't let message_destroy free it */
return (DNS_R_SUCCESS);
return (result);
}
/* XXX there should be library support for sending DNS TCP messages */
static void
{
printf("sendlen done\n");
xfrin_send_done, xfr));
return;
}
static void
{
printf("send done\n");
xfrin_recv_done, xfr));
return;
}
static void
isc_event_free(&ev);
printf("got tcp message\n");
if (maybe_free(xfr))
return;
printf("got %s, retrying with AXFR\n",
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 */
}
else {
goto failure;
}
}
/* Update the number of messages received */
/* Reset msg->querytsig so it doesn't get freed */
/* Copy the context back */
} else {
/* Read the next message. */
xfrin_recv_done, xfr));
}
return;
}
}
static void
}
static void
}
static isc_boolean_t
return (ISC_FALSE);
printf("freeing xfrin context\n");
if (xfr->tcpmsg_valid)
printf("xfrin_shutdown done\n");
return (ISC_TRUE);
}
/**************************************************************************/
/*
* Generating diffs from databases
*/
/*
* Construct a diff containing all the RRs at the current name of the
* database iterator 'dbit' in database 'db', version 'ver'.
* Set '*name' to the current name, and append the diff to 'diff'.
* All new tuples will have the operation 'op'.
*
* Requires: 'name' must have buffer large enough to hold the name.
* Typically, a dns_fixedname_t would be used.
*/
static dns_result_t
{
if (result != DNS_R_SUCCESS)
return (result);
if (result != DNS_R_SUCCESS)
goto cleanup_node;
result == DNS_R_SUCCESS;
{
result == DNS_R_SUCCESS;
{
&tuple);
if (result != DNS_R_SUCCESS) {
goto cleanup_iterator;
}
}
if (result != DNS_R_NOMORE)
goto cleanup_iterator;
}
if (result != DNS_R_NOMORE)
goto cleanup_iterator;
return (result);
}
/*
* Comparison function for use by dns_diff_subtract when sorting
* the diffs to be subtracted. The sort keys are the rdata type
* and the rdata itself. The owner name is ignored, because
* it is known to be the same for all tuples.
*/
static int
{
dns_difftuple_t *a = *ap;
dns_difftuple_t *b = *bp;
int r;
if (r != 0)
return (r);
return (r);
}
static dns_result_t
{
dns_difftuple_t *p[2];
int i, t;
for (;;) {
break;
for (i = 0; i < 2; i++)
if (p[!i] == NULL) {
goto next;
}
t = rdata_order(&p[0], &p[1]);
if (t < 0) {
goto next;
}
if (t > 0) {
goto next;
}
INSIST(t == 0);
/* Identical RRs in both databases; skip them both. */
for (i = 0; i < 2; i++) {
dns_difftuple_free(&p[i]);
}
next: ;
}
return (result);
}
/*
* Compare the databases 'dba' and 'dbb' and generate a journal
* entry containing the changes to make 'dba' from 'dbb' (note
* the order). This journal entry will consist of a single,
* possibly very large transaction.
*/
static dns_result_t
char *journal_filename)
{
int i, t;
dns_fixedname_init(&fixname[0]);
for (;;) {
for (i = 0; i < 2; i++) {
dns_fixedname_name(&fixname[i]),
i == 0 ?
&diff[i]));
}
}
break;
}
for (i = 0; i < 2; i++) {
if (! have[!i]) {
goto next;
}
}
if (t < 0) {
continue;
}
if (t > 0) {
continue;
}
INSIST(t == 0);
next: ;
}
if (itresult[0] != DNS_R_NOMORE)
printf("no changes\n");
} else {
}
dns_dbiterator_destroy(&dbit[0]);
return (result);
}