zone.c revision 29ed825602cca1b202ca314d619ddf33dc0630df
/*
* 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: zone.c,v 1.2 1999/09/03 13:21:30 marka Exp $ */
#include <config.h>
#include <isc/assertions.h>
#include <dns/dbiterator.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatastruct.h>
#include <dns/rdatalist.h>
/* XXX remove once config changes are in place */
#include <dns/confparser.h>
#include <dns/resolver.h>
#include <dns/dispatch.h>
#include <stdarg.h>
#define ZONE_MAGIC_USED 0x5a4f4e45U
#define ZONE_MAGIC_FREE 0x7a6f6e65U
#define CHECKSERVERS_MAGIC 0x43484346U
#define VALID_ZONE(zone) \
#define VALID_ZONE_FREE(zone) \
#define VALID_CHECKSERVERS(server) \
#ifndef DNS_GLOBAL_OPTION /* XXX MPA */
#define DNS_GLOBAL_OPTION(o) 0
#endif
typedef enum {
typedef struct dns_zone_checkservers {
struct dns_zone {
/* Unlocked */
unsigned int magic;
/* Locked */
unsigned int references;
char *database;
char *ixfrlog; /*
* XXX merge w/ updatelog to
* locate transaction log
*/
char *updatelog;
unsigned int flags;
unsigned int options;
unsigned int setoptions;
char * db_type;
unsigned int db_argc;
char ** db_argv;
unsigned int masterscnt;
unsigned int curmaster;
unsigned int notifycnt;
isc_task_t * task;
/* Access Control Lists */
};
#define DNS_ZONE_FLAG(z,f) (((z)->flags & (f)) != 0)
/* XXX MPA these may need to go back into zone.h */
* uptodate */
#define DNS_ZONE_OPTION(z,o) ((((z)->setoptions & (o)) != 0) ? \
(((z)->options & (o)) != 0) : \
static void cancel_refresh(dns_zone_t *);
unsigned int port);
static void record_serial(void);
/***
*** Public functions.
***/
return (DNS_R_NOMEMORY);
if (iresult != ISC_R_SUCCESS) {
"isc_mutex_init() failed: %s",
return (DNS_R_UNEXPECTED);
}
/* XXX MPA check that all elements are initialised */
zone->setoptions = 0;
zone->expiretime = 0;
zone->refreshtime = 0;
zone->servertime = 0;
zone->parenttime = 0;
zone->masterscnt = 0;
zone->masterport = 0;
#ifdef notyet
/* XXX */
#endif
return (DNS_R_SUCCESS);
}
static void
/* managed objects */
/* order is important */
/* unmanaged objects */
zone->masterport = 0;
/* last stuff */
}
/*
* Single shot.
*/
void
}
/*
* Single shot.
*/
void
}
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
}
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
void
case dns_zone_master:
case dns_zone_slave:
case dns_zone_stub:
case dns_zone_hint:
/*FALLTHROUGH*/
case dns_zone_forward:
break;
case dns_zone_cache:
break;
}
}
int soacount = 0;
int nscount = 0;
/*XXX*/
return (DNS_R_UNEXPECTED);
}
case dns_zone_forward:
case dns_zone_none:
return (DNS_R_SUCCESS);
case dns_zone_master:
case dns_zone_slave:
case dns_zone_stub:
case dns_zone_hint:
break;
case dns_zone_cache:
break;
}
if (result != DNS_R_SUCCESS)
return (result);
/*
* XXX Initiate zone transfer? We may need a error code that
* indicates that the "permanent" form does not exist.
*/
if (result != DNS_R_SUCCESS)
return (result);
/*
* XXX apply update log to zone.
*/
/*
* Obtain ns and soa counts for top of zone.
*/
nscount = 0;
soacount = 0;
if (result == DNS_R_SUCCESS) {
dns_rdatatype_none, 0, &rdataset);
if (result == DNS_R_SUCCESS) {
while (result == DNS_R_SUCCESS) {
nscount++;
}
}
dns_rdatatype_none, 0, &rdataset);
if (result == DNS_R_SUCCESS) {
while (result == DNS_R_SUCCESS) {
if (soacount == 0)
soacount++;
}
}
}
/*
* Master / Slave / Stub zones require both NS and SOA records at
* the top of the zone.
* Hint zones only require NS records.
* Cache zones have no reqirements.
*/
case dns_zone_master:
case dns_zone_slave:
case dns_zone_stub:
if (soacount != 0)
return (DNS_R_BADZONE);
}
}
break;
case dns_zone_hint:
if (nscount == 0) {
if (soacount != 0)
return (DNS_R_BADZONE);
}
break;
case dns_zone_cache:
break;
default:
if (soacount != 0)
return (DNS_R_UNEXPECTED);
}
if (soacount != 0)
return (DNS_R_SUCCESS);
}
void
unsigned int i;
/* XXX MPA */
/*
* get NS list from database, add in notify also list
*/
if (result == DNS_R_SUCCESS) {
dns_rdatatype_none, 0, &rdataset);
if (result == DNS_R_SUCCESS) {
while (result == DNS_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
continue;
sizeof *checkservers);
if (checkservers == NULL)
break;
&checkservers->server);
}
}
}
/*
* Foreach NS in NS list perform a non-recursive query to obtain
* NS list for zone (remove self from list).
*
* callback to check:
* If NXDOMAIN -> log error.
* If NODATA -> log error.
* If referral -> log error.
* If non-auth -> log error.
* Compare NS list returned with server list if not identical
* log error if current list is at least 3 x refresh old.
*/
/*
* Foreach NS in NS list perform a non-recursive query to obtain
* SOA record for zone (remove self from list).
*
* callback to check:
* If NXDOMAIN -> log error.
* If NODATA -> log error.
* If referral -> log error.
* If no-auth -> log error.
* Compare SOA serial with ixfr list and if older that 3x refresh
* log error.
*/
if (checkservers == NULL)
break;
}
}
static void
char *master;
/* timeout */
switch (state) {
case get_a6:
case get_aaaa:
case get_a:
"unable to obtain address for (%s)");
break;
case get_ns:
case get_soa:
"unable to obtain %s RRset from %s"
);
}
goto cleanup;
}
switch (state) {
case get_a6:
break;
case get_aaaa:
break;
case get_a:
/* make NS query to address */
break;
case get_ns:
case get_soa:
char rcode[128];
"server %s (%s) unexpected rcode = %.*s",
break;
}
"server %s (%s) referral response");
else
"server %s (%s) type = %s NODATA response");
}
"server %s (%s) not authorative");
}
/* compare NS RR sets */
/* make soa query to address */
&checkservers->fetch);
break;
} else {
/* compare SOA RR sets */
goto cleanup;
}
break;
default:
break;
}
return;
checkservers->magic = 0;
}
void
/*
* extract SOA from message
*/
if (result != DNS_R_SUCCESS) {
"Unable to extract SOA from answer: %s",
server);
return;
}
if (DNS_R_SUCCESS != result)
return;
if (DNS_R_SUCCESS != result)
return;
if (DNS_R_NOMORE != result) {
"More that one SOA record returned: %s",
server);
goto cleanup_msgsoa;
}
/*
* Get SOA record for zone.
*/
if (result != DNS_R_SUCCESS) {
/* XXXMPA */
goto cleanup_msgsoa;
}
if (DNS_R_SUCCESS != result)
return;
if (DNS_R_SUCCESS != result)
return;
if (DNS_R_NOMORE != result) {
goto cleanup_msgsoa;
}
/*
* Check SOA contents. If serials do not match check to see
* if the slave is ahead of us (i.e. we have reset the serial
* number).
*
* If the serials do match then check the other values for
* consistancy.
*/
"slave serial not less than or equal to zone serial: %s",
server);
goto cleanup_zonesoa;
}
goto cleanup_zonesoa;
}
server);
}
}
static void
{
return;
return;
while (DNS_R_SUCCESS == result) {
switch (type) {
case dns_rdatatype_a:
checkservers->mctx);
dns_rdata_freestruct(&a);
break;
case dns_rdatatype_a6:
checkservers->mctx);
break;
default:
INSIST(0);
}
}
}
void
/* XXX MPA */
/*
* Obtain a parent NS list.
* Remove LSL from zone name. Check to see if we are serving
* zone otherwise make non-recursive query for NS set of
* of given name. Follow referral until NXDOMAIN, NODATA or
* answer is found. If NXDOMAIN or NODATA remove next LSL
* and repeat.
*/
/*
* If self in NS list check masked NS list in parent against zone
* ns list.
*
* Foreach NS on parent NS list make non recursive query for NS set
* of current zone (removed self from list if required).
*
* Check NS list return for agreement with zone's NS list.
*/
}
void
/* XXX MPA */
/*
* For each child zone obtain NS list from parent zone.
* For each NS in list send non-recursive query for child zone's
* NS list for zone.
*
* If NXDOMAIN is returned log error.
* If NODATA is return log error.
* If referral is return log error.
* If non-auth is return log error.
* If NS list disagree's with parents NS list log error.
*/
}
void
/* XXX MPA */
/*
* For each glue record in this zone, check with an authorative
* server for the zone to ensure that there have not been any
* changes.
*/
}
void
source->references++;
}
void
zone->references--;
if (zone->references == 0)
}
void
if (value)
else
}
void
{
if (value)
else
}
void
}
void
unsigned int *optionsmask)
{
}
char **new;
/*
* Allocate new 'db_argv' and set last to be copy of 'arg'.
*/
return (DNS_R_NOMEMORY);
return (DNS_R_NOMEMORY);
}
/*
* Copy old 'db_argv' if required the free it.
*/
}
return (DNS_R_SUCCESS);
}
void
unsigned int i;
}
}
return (DNS_R_SUCCESS);
}
}
return (DNS_R_NOMEMORY);
}
return (DNS_R_SUCCESS);
}
void
return;
}
return (DNS_R_NOMEMORY);
}
if (zone->masterscnt > 0) {
}
zone->masterscnt++;
return (DNS_R_SUCCESS);
}
void
}
return;
}
zone->masterscnt = 0;
}
dns_db_t *
}
/*
* Co-ordinates the starting of routine jobs.
*/
void
return;
/*
* Expire check.
*/
case dns_zone_slave:
case dns_zone_stub:
}
break;
default:
break;
}
/*
* Up to date check.
*/
case dns_zone_slave:
case dns_zone_stub:
break;
default:
break;
}
/*
* Do we need to consolidate the backing store?
*/
case dns_zone_master:
}
break;
default:
break;
}
/*
* Check servers for zone.
*/
case dns_zone_master:
case dns_zone_slave:
case dns_zone_stub:
break;
default:
break;
}
/*
* Check parent servers for zone.
*/
case dns_zone_master:
case dns_zone_slave:
case dns_zone_stub:
break;
default:
break;
}
/*
* Check child servers for zone.
*/
case dns_zone_master:
case dns_zone_slave:
case dns_zone_stub:
break;
default:
break;
}
}
void
}
void
return;
/*
* Set DNS_ZONE_F_REFRESH so that there is only one refresh operation
* in progress at the one time.
*/
/*
* Set the next refresh time as if refresh check has failed.
* If we are successful it will be reset using zone->refresh.
*/
/* initiate soa query */
}
unsigned int buflen = 1024;
if (result == DNS_R_SUCCESS)
while (result == DNS_R_SUCCESS) {
break;
&rdsiter);
if (result != DNS_R_SUCCESS) {
break;
}
while (result == DNS_R_SUCCESS) {
if (result == DNS_R_SUCCESS)
if (result == DNS_R_NOSPACE) {
buflen += 1024;
goto retry;
}
if (result == DNS_R_SUCCESS)
if (result == DNS_R_SUCCESS)
}
if (result == DNS_R_NOMORE)
}
return (result);
}
void
}
void
/*XXX MPA*/
}
if (iresult != ISC_R_SUCCESS) {
/* XXX */
return (DNS_R_UNEXPECTED);
}
if (iresult != ISC_R_SUCCESS) {
/* XXX */
return (DNS_R_UNEXPECTED);
}
if (iresult != ISC_R_SUCCESS) {
/* XXX */
return (DNS_R_UNEXPECTED);
}
isc_socket_t *s;
== ISC_R_SUCCESS);
s = NULL;
isc_sockettype_udp, &s) == ISC_R_SUCCESS);
if (result != DNS_R_SUCCESS)
return (result);
isc_socket_detach(&s);
}
return (DNS_R_SUCCESS);
}
void
{
}
void
unsigned int i;
return;
/*
* Enqueue notify request.
*/
}
if (result != DNS_R_SUCCESS)
goto cleanup1;
dns_rdatatype_none, 0, &nsrdset);
if (result != DNS_R_SUCCESS)
goto cleanup2;
while (result == DNS_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
continue;
/*
* Look up address records.
*/
/* XXX MPA */
if (result == DNS_R_NOTFOUND) {
/*
* Query for address.
* Arrange for notify to be sent when
* we have it.
*/
/* XXX MPA*/
continue;
} else if (result != DNS_R_SUCCESS) {
continue;
}
while (result == DNS_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
continue;
/*
* Remove duplicates w/ notify list.
*/
break;
}
}
}
}
}
/***
*** Private
***/
static void
char *master;
char *unknown = "<UNKNOWN>";
/*
* if timeout log and next master;
*/
goto next_master;
}
/*
* Unexpected rcode.
*/
char rcode[128];
"refresh: unexpected rcode (%.*s) from %s\n",
goto next_master;
}
/*
* if non-auth log and next master;
*/
"refresh: non-authorative answer from %s", master);
goto next_master;
}
/*
* There should not be a CNAME record at top of zone.
*/
if (cnamecnt != 0) {
"refresh: CNAME discovered: master %s", master);
goto next_master;
}
if (soacnt != 1) {
"refresh: SOA count (%d) != 1: master %s",
goto next_master;
}
/*
* if referral log and next master;
*/
"refresh: referral: master %s", master);
goto next_master;
}
/*
* if nodata log and next master;
*/
goto next_master;
}
/*
* Extract serial
*/
if (result != DNS_R_SUCCESS) {
goto next_master;
}
if (result != DNS_R_SUCCESS) {
goto next_master;
}
if (result != DNS_R_SUCCESS) {
goto next_master;
}
goto next_master;
} else {
goto next_master;
}
return;
return;
return;
}
return;
}
static void
if (result != DNS_R_SUCCESS)
}
static void
}
static void
}
static dns_result_t
isc_stdtime_t next = 0;
case dns_zone_master:
}
break;
case dns_zone_slave:
case dns_zone_stub:
}
break;
default:
break;
}
if (next == 0) {
} else {
else
ISC_TRUE);
} else {
}
if (iresult != ISC_R_SUCCESS) {
/* XXX */
return (DNS_R_UNEXPECTED);
}
}
return (DNS_R_SUCCESS);
}
void
/* XXX MPA*/
return;
}
static dns_result_t
{
/* dns_rdatalist_t *rdatalist = NULL; */
char buf[512];
if (result != DNS_R_SUCCESS)
return (result);
/* result = dns_message_gettemprdatalist(msg, &rdatalist); */
if (result != DNS_R_SUCCESS)
goto cleanup;
if (result != DNS_R_SUCCESS)
goto cleanup;
if (result != DNS_R_SUCCESS)
goto cleanup;
if (result != DNS_R_SUCCESS)
goto cleanup;
if (result != DNS_R_SUCCESS)
goto cleanup;
if (result != DNS_R_SUCCESS)
goto cleanup;
/* XXX TSIG here */
if (result != DNS_R_SUCCESS)
goto cleanup;
/* XXX Queue for sending */
return (result);
}
{
unsigned int i;
/*
* If type != T_SOA return DNS_R_REFUSED. We don't yet support
* ROLLOVER.
*
* SOA: RFC 1996
* Check that 'from' is a valid notify source, (zone->masters).
* Return DNS_R_REFUSED if not.
*
* If the notify message contains a serial number check it
* against the zones serial and return if <= current serial
*
* If a refresh check is progress, if so just record the
* fact we received a NOTIFY and from where and return.
* We will perform a new refresh check when the current one
* completes. Return DNS_R_SUCCESS.
*
* Otherwise initiate a refresh check using 'from' as the
* first address to check. Return DNS_R_SUCCESS.
*/
/*
* We only handle NOTIFY (SOA) at the present.
*/
return (DNS_R_REFUSED);
}
for (i = 0; i < zone->masterscnt; i++)
break;
if (i >= zone->masterscnt) {
return (DNS_R_REFUSED);
}
/*
* If the zone is loaded and there are answers check the serial
* to see if we need to do a refresh. Do not worry about this
* check if we are a dialup zone as we use the notify request
* to trigger a refresh check.
*/
&rdataset);
if (result == DNS_R_SUCCESS)
if (result == DNS_R_SUCCESS) {
isc_uint32_t serial = 0;
if (result == DNS_R_SUCCESS) {
return (DNS_R_SUCCESS);
}
}
}
/*
* If we got this far and there was a refresh in progress just
* let it complete. Record where we got the notify from so we
* can perform a refresh check when the current one completes
*/
return (DNS_R_SUCCESS);
}
return (DNS_R_SUCCESS);
}
/*
* Find zone in mount table.
*/
return (ISC_R_NOTIMPLEMENTED);
}
/*
* Copy / translate zone configuration data to dns_zone_t.
*/
isc_uint32_t i;
case dns_c_zone_master:
if (iresult != ISC_R_SUCCESS)
return (iresult);
if (result != DNS_R_SUCCESS)
return (iresult);
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
return (result);
}
} else
if (iresult == ISC_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
return (result);
}
if (iresult == ISC_R_SUCCESS)
if (iresult == ISC_R_SUCCESS)
else
break;
case dns_c_zone_slave:
if (iresult != ISC_R_SUCCESS)
return (iresult);
if (result != DNS_R_SUCCESS)
return (iresult);
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
/* notify is off by default for slave zones */
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
return (result);
}
} else
if (iresult == ISC_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
return (result);
}
/* czone->u.szone.ixfr_tmp; XXX*/
if (iresult == ISC_R_SUCCESS)
if (iresult == ISC_R_SUCCESS)
else
/*
* should master port be seperate or just applied to
* isc_sockaddr_t's
*/
if (iresult != ISC_R_SUCCESS)
port = 53;
if (iresult == ISC_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
return (result);
}
} else
if (iresult == ISC_R_SUCCESS) {
sockaddr_fromaddr(&s, &addr, 0);
if (result != DNS_R_SUCCESS)
return (result);
}
if (iresult == ISC_R_SUCCESS)
else
break;
case dns_c_zone_forward:
#ifdef notyet
/*
* forward zones are still in a state of flux
*/
#endif
break;
case dns_c_zone_stub:
if (iresult != ISC_R_SUCCESS)
return (iresult);
if (result != DNS_R_SUCCESS)
return (iresult);
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
if (iresult != ISC_R_SUCCESS)
port = 53;
if (iresult == ISC_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
return (result);
}
} else
break;
case dns_c_zone_hint:
if (iresult != ISC_R_SUCCESS)
return (iresult);
if (result != DNS_R_SUCCESS)
return (result);
if (iresult == ISC_R_SUCCESS)
else
if (iresult == ISC_R_SUCCESS)
else
break;
}
return (DNS_R_SUCCESS);
}
void
}
void
}
void
}
}
return (zone->update_acl);
}
}
void
}
void
}
void
}
void
}
return (zone->check_names);
}
void
}
}
void
}
return (zone->ixfrlogsize);
}
void
}
return (zone->masterport);
}
static void
char message[4096];
char namebuf[1024];
int len;
&buffer);
if (result == DNS_R_SUCCESS)
else
len = 0;
}
static int
int res = 0;
while (result == DNS_R_SUCCESS) {
res++;
}
}
return (res);
}
void
}
void
}
}
void
}
/*
* XXX should become isc_sockaddr_fromaddr() once dns_c_addr_t -> isc
*/
static void
unsigned int port) {
switch (a->a_family) {
case AF_INET:
break;
case AF_INET6:
break;
default:
INSIST(0);
}
}
static void
record_serial() {
}