zone.c revision 9fae7ff63821e5586d2931deca299ba0c578204b
/*
* 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: zone.c,v 1.81 2000/02/16 16:37:52 bwelling Exp $ */
#include <config.h>
#include <string.h>
#include <isc/assertions.h>
#include <isc/mktemplate.h>
#include <isc/taskpool.h>
#include <dns/dbiterator.h>
#include <dns/dispatch.h>
#include <dns/masterdump.h>
#include <dns/rdatalist.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatastruct.h>
#include <dns/resolver.h>
/* XXX remove once config changes are in place */
#include <stdarg.h>
#define ZONE_MAGIC 0x5a4f4e45U
#define CHECKSERVERS_MAGIC 0x43484346U
#define DNS_ZONE_VALID(zone) \
#define DNS_CHECKSERVERS_VALID(server) \
#ifndef DNS_GLOBAL_OPTION /* XXX MPA */
#define DNS_GLOBAL_OPTION(o) 0
#endif
#define RANGE(a, b, c) (((a) < (b)) ? (b) : ((a) < (c) ? (a) : (c)))
#define DNS_MIN_REFRESH 2
#define DNS_MIN_RETRY 1
typedef enum {
typedef struct dns_zone_checkservers {
struct dns_zone {
/* Unlocked */
unsigned int magic;
/* Locked */
unsigned int erefs;
unsigned int irefs;
char *database;
char *journal;
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) : \
struct dns_zonemgr {
isc_task_t * task;
/* Locked by rwlock. */
/* Maximum locked by conflock. */
};
static void cancel_refresh(dns_zone_t *);
...);
char **s);
#if 0
/* ondestroy example */
#endif
#ifdef notyet
static void record_serial(void);
#endif
#define PRINT_ZONE_REF(zone) \
do { \
char *s = NULL; \
isc_result_t r; \
if (r == DNS_R_SUCCESS) { \
} \
} while (0)
/***
*** Public functions.
***/
struct in_addr in4addr_any;
return (DNS_R_NOMEMORY);
if (result != 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;
return (DNS_R_SUCCESS);
}
static void
/* managed objects */
/* order is important */
/* unmanaged objects */
zone->masterport = 0;
/* last stuff */
}
/*
* Single shot.
*/
void
/* test and set */
}
}
/*
* Single shot.
*/
void
/* test and set */
}
return (result);
}
}
return (result);
}
else
return (result);
}
static isc_result_t
int len;
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
return (result);
}
char *
}
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;
}
}
const char me[] = "dns_zone_load";
int soacount = 0;
int nscount = 0;
case dns_zone_forward:
case dns_zone_none:
goto cleanup;
case dns_zone_master:
case dns_zone_slave:
case dns_zone_stub:
case dns_zone_hint:
break;
case dns_zone_cache:
break;
default:
}
if (result != DNS_R_SUCCESS)
goto cleanup;
/*
* Initiate zone transfer? We may need a error code that
* indicates that the "permanent" form does not exist.
* XXX better error feedback to log.
*/
if (result != DNS_R_SUCCESS) {
"no database file");
} else {
"database %s: dns_db_load failed: %s",
}
goto cleanup;
}
/*
* Apply update log, if any.
*/
goto cleanup;
if (result == DNS_R_NOTFOUND) {
"journal out of sync with zone");
goto cleanup;
}
"dns_journal_rollforward: %s",
if (result == DNS_R_SUCCESS)
}
/*
* Obtain ns and soa counts for top of zone.
*/
nscount = 0;
soacount = 0;
if (result == DNS_R_SUCCESS) {
dns_rdatatype_none, 0, &rdataset,
NULL);
if (result == DNS_R_SUCCESS) {
while (result == DNS_R_SUCCESS) {
nscount++;
}
}
dns_rdatatype_none, 0, &rdataset,
NULL);
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 != 1)
"has %d SOA record%s", soacount,
if (nscount == 0)
"no NS records");
goto cleanup;
}
"zone serial has gone backwards");
}
}
/* XXX need database modification time */
}
break;
case dns_zone_hint:
if (nscount == 0) {
goto cleanup;
}
break;
case dns_zone_cache:
break;
default:
goto cleanup;
}
#if 0
/* destroy notification example. */
{
zone,
sizeof(isc_event_t));
}
#endif
if (result != ISC_R_SUCCESS)
goto cleanup;
} else {
}
if (soacount != 0)
dns_db_detach(&db);
return (result);
}
#ifdef notyet
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,
NULL);
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;
}
}
#endif
#ifdef notyet
static void
const char me[] = "checkservers_callback";
/* 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;
}
#endif
#if 0
static 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);
}
}
#endif
#ifdef notyet
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);
}
}
}
#endif
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.
*/
}
static void
{
}
void
}
void
/*
* This zone is being managed. Post
* its control event and let it clean
* up synchronously in the context of
* its task.
*/
} else {
/*
* This zone is not being managed; it has
* no task and can have no outstanding
* events. Free it immediately.
*/
}
}
if (free_now)
}
void
}
void
}
void
}
}
static isc_result_t
char outbuf[1024];
if (result == DNS_R_SUCCESS)
else {
sizeof outbuf - 1);
}
} else {
}
}
void
if (value)
else
}
void
{
if (value)
else
}
void
}
void
unsigned int *optionsmask)
{
}
/*
* Allocate new 'db_argv' and set last to be copy of 'arg'.
*/
goto cleanup;
goto cleanup;
/*
* Copy old 'db_argv' if required the free it.
*/
}
return (DNS_R_SUCCESS);
return (DNS_R_NOMEMORY);
}
void
unsigned int i;
}
}
return (DNS_R_SUCCESS);
}
return (&zone->xfrsource4);
}
return (DNS_R_SUCCESS);
}
return (&zone->xfrsource6);
}
goto cleanup;
}
return (DNS_R_SUCCESS);
return (DNS_R_NOMEMORY);
}
void
}
}
return (DNS_R_NOMEMORY);
}
if (zone->masterscnt > 0) {
}
zone->masterscnt++;
return (DNS_R_SUCCESS);
}
void
}
zone->masterscnt = 0;
}
}
else
return (result);
}
/*
* Co-ordinates the starting of routine jobs.
*/
void
const char me[] = "dns_zone_maintenance";
/*
* 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:
#ifdef notyet
#endif
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
}
static void
}
void
/*
* Set DNS_ZONE_F_REFRESH so that there is only one refresh operation
* in progress at the one time.
*/
if ((oldflags & DNS_ZONE_F_REFRESH) != 0)
return;
/*
* Set the next refresh time as if refresh check has failed.
* If we are successful it will be reset using zone->refresh.
*/
#ifdef notyet
/* initiate soa query */
#else
/* initiate zone transfer */
#endif
}
char *buf;
int buflen;
FILE *f;
int n;
if (result != ISC_R_SUCCESS)
return (result);
if (f == NULL) {
goto cleanup;
}
&dns_master_style_default, f);
dns_db_detach(&top);
n = fflush(f);
if (n != 0)
n = ferror(f);
if (n != 0)
n = fclose(f);
if (n != 0)
if (result == ISC_R_SUCCESS) {
if (n == -1) {
}
} else
return (result);
}
dns_db_detach(&top);
return (result);
}
void
}
static void
/* caller to lock */
}
void
/*XXX MPA*/
}
#ifdef notyet
/*
* For reference only. Use dns_zonemanager_managezone() instead.
*/
static isc_result_t
#if 1
(void)tmgr;
return (DNS_R_SUCCESS);
#else
/*
* XXXRTH Zones do not have resolvers!!!!
*/
if (result != ISC_R_SUCCESS) {
/* XXX */
return (DNS_R_UNEXPECTED);
}
if (result != ISC_R_SUCCESS) {
/* XXX */
return (DNS_R_UNEXPECTED);
}
isc_socket_t *s;
== ISC_R_SUCCESS);
s = NULL;
isc_sockettype_udp, &s) == ISC_R_SUCCESS);
4096, 1000, 1000, 17, 19,
&dispatch) == DNS_R_SUCCESS);
if (result != DNS_R_SUCCESS)
return (result);
isc_socket_detach(&s);
}
return (DNS_R_SUCCESS);
#endif
}
#endif
void
{
}
void
unsigned int i;
return;
/*
* Enqueue notify request.
*/
/* XXX IPv6 */
}
if (result != DNS_R_SUCCESS)
goto cleanup1;
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;
}
/* XXX IPv6 */
}
}
}
}
/***
*** Private
***/
#ifdef notyet
static void
char *master;
char mastermem[256];
/*
* if timeout log and next master;
*/
&masterbuf);
if (result == ISC_R_SUCCESS)
else
master = "<UNKNOWN>";
goto next_master;
}
/*
* Unexpected rcode.
*/
char rcode[128];
"unexpected rcode (%.*s) from %s\n",
goto next_master;
}
/*
* if non-auth log and next master;
*/
"non-authorative answer from %s", master);
goto next_master;
}
/*
* There should not be a CNAME record at top of zone.
*/
if (cnamecnt != 0) {
"CNAME discovered: master %s", master);
goto next_master;
}
if (soacnt != 1) {
goto next_master;
}
/*
* if referral log and next master;
*/
"referral from: master %s", master);
goto next_master;
}
/*
* if nodata log and next master;
*/
"NODATA from master %s", master);
goto next_master;
}
/*
* Extract serial
*/
if (result != DNS_R_SUCCESS) {
"unable to get soa record from %s", master);
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;
}
#endif
#ifdef notyet
static void
if (result != DNS_R_SUCCESS)
}
#endif
/*
* Handle the control event. Note that although this event causes the zone
* to shut down, it is not a shutdown event in the sense of the task library.
*/
static void
}
static void
const char me[] = "zone_timer";
/* XXX if we use a view, we need to lock its configuration, too. */
}
static isc_result_t
const char me[] = "zone_settimer";
isc_stdtime_t next = 0;
case dns_zone_master:
}
break;
case dns_zone_slave:
case dns_zone_stub:
}
break;
default:
break;
}
if (next == 0) {
"settimer inactive");
} else {
"settimer %d %d = %d seconds",
}
if (result != ISC_R_SUCCESS) {
/* XXX */
return (DNS_R_UNEXPECTED);
}
return (DNS_R_SUCCESS);
}
static void
/*
* caller to lock.
*/
}
static isc_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);
}
{
const char me[] = "dns_zone_notifyreceive";
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.
*/
"FORMERR no question");
return (DNS_R_FORMERR);
}
"REFUSED zone does not match");
return (DNS_R_NOTIMP);
}
/*
* If we are a master zone just succeed.
*/
return (DNS_R_SUCCESS);
for (i = 0; i < zone->masterscnt; i++)
break;
if (i >= zone->masterscnt) {
"REFUSED notify from non master");
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) {
"zone up to date");
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
*/
"refresh in progress, refresh check queued");
return (DNS_R_SUCCESS);
}
return (DNS_R_SUCCESS);
}
void
}
void
}
void
}
}
return (zone->update_acl);
}
}
void
}
void
}
void
}
void
}
return (zone->check_names);
}
void
}
return (zone->journalsize);
}
void
}
return (zone->masterport);
}
static void
const char *fmt, ...) {
char message[4096];
int len;
if (result != DNS_R_SUCCESS)
}
#ifdef notyet
static int
int res = 0;
while (result == DNS_R_SUCCESS) {
res++;
}
}
return (res);
}
#endif
void
}
void
}
}
void
}
}
void
}
}
}
void
}
void
}
const char *
}
void
if (idlein == 0)
}
}
void
if (idleout == 0)
}
}
#ifdef notyet
static void
record_serial() {
}
#endif
unsigned int i;
goto false;
goto false;
goto false;
goto false;
goto false;
goto false;
goto false;
goto false;
goto false;
for (i = 0; i < oldzone->masterscnt; i++)
goto false;
goto false
checks are done. */
false:
return (ISC_FALSE);
}
return (result);
}
static isc_result_t
/*
* The initial version of a slave zone is always dumped;
* subsequent versions may be journalled instead if this
* is enabled in the configuration.
*/
zone->diff_on_reload) {
"generating diffs");
if (result != DNS_R_SUCCESS)
goto fail;
} else {
if (dump) {
"dumping new zone version");
/* XXX should use temporary file and rename */
if (result != DNS_R_SUCCESS)
goto fail;
}
"removing journal file");
}
}
"replacing zone database");
return (DNS_R_SUCCESS);
fail:
return (result);
}
static void
const char me[] = "xfrdone";
switch (result) {
case DNS_R_UPTODATE:
case DNS_R_SUCCESS:
} else
break;
default:
else {
}
break;
}
/*
* If creating the transfer object failed, zone->xfr is NULL.
* Otherwise, we are called as the done callback of a zone
* transfer object that just entered its shutting-down
* state. Since we are no longer responsible for shutting
* it down, we can detach our reference.
*/
/*
* Retry with a different server if necessary.
*/
if (again)
}
void
}
void
}
/***
*** Zone manager.
***/
static void
return;
if (port == 0)
if (result != DNS_R_SUCCESS)
}
{
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS) {
"isc_rwlock_init() failed: %s",
goto free_mem;
}
if (result != ISC_R_SUCCESS) {
"isc_rwlock_init() failed: %s",
goto free_rwlock;
}
if (result != ISC_R_SUCCESS) {
"isc_quota_init() failed: %s",
goto free_conflock;
}
/* Create the zone task pool. */
if (result != ISC_R_SUCCESS)
goto free_transfersin;
/* Create a single task for queueing of SOA queries. */
if (result != ISC_R_SUCCESS)
goto free_taskpool;
return (ISC_R_SUCCESS);
return (result);
}
if (result != ISC_R_SUCCESS)
goto cleanup_task;
goto unlock;
return (result);
}
static void
/*
* Caller to lock zone and zmgr
*/
}
void
}
dns_zone_t *p;
p != NULL;
p = ISC_LIST_NEXT(p, link))
{
}
return (ISC_R_SUCCESS);
}
void
}
void
}
/* Probably done already, but does not hurt to repeat. */
}
void
}
void
}
void
}
int
}
#if 0
/* hook for ondestroy notifcation from a database. */
static void
"database (%p) destroyed", (void*) db);
}
#endif