zone.c revision aa79283a0d97d5d77d3c17bcb1756035eabe647a
/*
* 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.148 2000/06/15 17:40:18 gson Exp $ */
#include <config.h>
#include <isc/ratelimiter.h>
#include <isc/taskpool.h>
#include <dns/masterdump.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/resolver.h>
#define DNS_ZONE_VALID(zone) \
#define DNS_NOTIFY_VALID(notify) \
#define DNS_STUB_VALID(stub) \
#define DNS_ZONEMGR_VALID(stub) \
#define RANGE(a, b, c) (((a) < (b)) ? (b) : ((a) < (c) ? (a) : (c)))
/*
* Implementation limits.
*/
/*
* Default values.
*/
typedef struct dns_notify dns_notify_t;
typedef struct dns_stub dns_stub_t;
struct dns_zone {
/* Unlocked */
unsigned int magic;
/* Locked */
unsigned int erefs;
unsigned int irefs;
char *dbname;
char *journal;
unsigned int flags;
unsigned int options;
char *db_type;
unsigned int db_argc;
char **db_argv;
unsigned int masterscnt;
unsigned int curmaster;
unsigned int notifycnt;
/* Access Control Lists */
/*
* Zones in certain states such as "waiting for zone transfer"
* or "zone transfer in progress" are kept on per-state linked lists
* in the zone manager using the 'statelink' field. The 'statelist'
* field points at the list the zone is currently on. It the zone
* is not on any such list, statelist is NULL.
*/
};
#define DNS_ZONE_FLAG(z,f) (((z)->flags & (f)) != 0)
/* XXX MPA these may need to go back into zone.h */
/* #define DNS_ZONEFLG_UNUSED 0x00000008U */ /* unused */
/* #define DNS_ZONEFLG_UNUSED 0x00000010U */ /* unused */
* uptodate */
* messages */
* reload */
* zone with no masters
* occured */
#define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0)
struct dns_zonemgr {
unsigned int magic;
int refs; /* Locked by rwlock */
isc_task_t * task;
/* Locked by rwlock. */
/* Locked by conflock. */
int transfersin;
int transfersperns;
};
/*
* Hold notify state.
*/
struct dns_notify {
};
/*
* dns_stub holds state while performing a 'stub' transfer.
* 'db' is the zone's 'db' or a new one if this is the initial
* transfer.
*/
struct dns_stub {
};
static void cancel_refresh(dns_zone_t *);
...);
char **s);
#if 0
/* ondestroy example */
#endif
dns_stub_t *stub);
dns_zone_t *zone);
static isc_result_t
#define PRINT_ZONE_REF(zone) \
do { \
char *s = NULL; \
isc_result_t r; \
if (r == ISC_R_SUCCESS) { \
} \
} while (0)
/***
*** Public functions.
***/
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS) {
"isc_mutex_init() failed: %s",
return (ISC_R_UNEXPECTED);
}
/* XXX MPA check that all elements are initialised */
zone->expiretime = 0;
zone->refreshtime = 0;
zone->masterscnt = 0;
return (ISC_R_SUCCESS);
}
/*
* Free a zone. Because we require that there be no more
* outstanding events or references, no locking is necessary.
*/
static void
/*
* Managed objects. Order is important.
*/
/* Unmanaged objects */
/* last stuff */
}
/*
* Single shot.
*/
void
/*
* Test and set.
*/
}
}
/*
* Single shot.
*/
void
/*
* Test and set.
*/
}
return (result);
}
void
}
}
}
return (result);
}
else
return (result);
}
static isc_result_t
int len;
return (ISC_R_NOMEMORY);
return (ISC_R_SUCCESS);
}
return (result);
}
char *
}
const char me[] = "dns_zone_load";
unsigned int soacount = 0;
unsigned int nscount = 0;
/*
* The zone has no master file (maybe it is the built-in
* version.bind. CH zone). Do nothing.
*/
goto cleanup;
}
/*
* Don't do the load if the file that stores the zone is older
* than the last time the zone was loaded. If the zone has not
* been loaded yet, zone->loadtime will be the epoch.
*/
"skipping: database file older than last load");
goto cleanup;
}
/*
* Store the current time before the zone is loaded, so that if the
* file changes between the time of the load and the time that
* zone->loadtime is set, then the file will still be reloaded
* the next time dns_zone_load is called.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
&db);
if (result != ISC_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 != ISC_R_SUCCESS) {
"no database file");
/* Mark the zone for immediate refresh. */
} else {
"database %s: dns_db_load failed: %s",
}
goto cleanup;
}
/*
* Apply update log, if any.
*/
goto cleanup;
if (result == ISC_R_NOTFOUND) {
"journal out of sync with zone");
goto cleanup;
}
"dns_journal_rollforward: %s",
if (result == ISC_R_SUCCESS)
}
/*
* Obtain ns and soa counts for top of zone.
*/
nscount = 0;
soacount = 0;
if (result != ISC_R_SUCCESS) {
}
/*
* Master / Slave / Stub zones require both NS and SOA records at
* the top of the zone.
*/
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");
}
}
isc_time_t t;
if (result == ISC_R_SUCCESS)
else
}
break;
default:
goto cleanup;
}
#if 0
/* destroy notification example. */
{
zone,
sizeof(isc_event_t));
}
#endif
if (result != ISC_R_SUCCESS)
goto cleanup;
} else {
}
dns_db_detach(&db);
return (result);
}
static void
{
/*
* DNS_ZONEFLG_EXITING can only be set if erefs == 0.
*/
}
}
static isc_result_t
unsigned int *nscount)
{
unsigned int count;
if (result != ISC_R_SUCCESS)
goto invalidate_rdataset;
count = 0;
while (result == ISC_R_SUCCESS) {
count++;
}
return (result);
}
static isc_result_t
unsigned int *soacount,
{
unsigned int count;
if (result != ISC_R_SUCCESS)
goto invalidate_rdataset;
count = 0;
while (result == ISC_R_SUCCESS) {
count++;
if (count == 1)
}
if (count > 0) {
}
return (result);
}
/*
* zone must be locked.
*/
static isc_result_t
{
if (result != ISC_R_SUCCESS)
goto closeversion;
if (result != ISC_R_SUCCESS)
goto detachnode;
}
minimum);
if (result != ISC_R_SUCCESS)
goto detachnode;
}
return (result);
}
void
}
void
/*
* We just detached the last external reference.
*/
/*
* 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.
*/
/*
* Unmanaged zones should not have non-null views;
* we have no way of detaching from the view here
* without causing deadlock because this code is called
* with the view already locked.
*/
}
}
if (free_now)
}
void
}
void
}
void
}
}
}
static isc_result_t
char outbuf[1024];
if (result == ISC_R_SUCCESS)
else {
sizeof outbuf - 1);
}
} else {
}
}
void
if (value)
else
}
void
{
if (value)
else
}
unsigned int
}
/*
* 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 (ISC_R_SUCCESS);
return (ISC_R_NOMEMORY);
}
void
unsigned int i;
}
}
return (ISC_R_SUCCESS);
}
return (&zone->xfrsource4);
}
return (ISC_R_SUCCESS);
}
return (&zone->xfrsource6);
}
{
}
goto unlock;
return (ISC_R_NOMEMORY);
}
return (ISC_R_SUCCESS);
}
{
zone->masterscnt = 0;
}
goto unlock;
return (ISC_R_NOMEMORY);
}
return (ISC_R_SUCCESS);
}
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:
if (result != ISC_R_SUCCESS)
"failed: %s",
}
break;
default:
break;
}
/*
* Do we need to send out notify messages?
*/
case dns_zone_master:
case dns_zone_slave:
}
default:
break;
}
}
void
}
static void
/*
* 'zone' locked by caller.
*/
if (result != ISC_R_SUCCESS)
}
}
void
return;
/*
* Set DNS_ZONEFLG_REFRESH so that there is only one refresh operation
* in progress at a time.
*/
if (zone->masterscnt == 0) {
if ((oldflags & DNS_ZONEFLG_NOMASTERS) == 0)
"no masters");
return;
}
if ((oldflags & DNS_ZONEFLG_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.
*/
/* initiate soa query */
}
return (result);
}
static isc_result_t
char *buf;
int buflen;
int n;
/*
* 'zone' locked by caller.
*/
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
&dns_master_style_default, f);
dns_db_detach(&db);
n = fflush(f);
if (n != 0 && result == ISC_R_SUCCESS)
n = ferror(f);
if (n != 0 && result == ISC_R_SUCCESS)
n = fclose(f);
if (n != 0 && result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS) {
if (n == -1) {
} else {
}
} else
return (result);
}
dns_db_detach(&db);
return (result);
}
void
}
static void
/* caller to lock */
}
void
{
}
static isc_boolean_t
continue;
return (ISC_TRUE);
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static void
/*
* Caller holds zone lock.
*/
}
}
static isc_result_t
return (ISC_R_NOMEMORY);
return (ISC_R_SUCCESS);
}
/*
* XXXAG should check for DNS_ZONEFLG_EXITING
*/
static void
isc_event_free(&ev);
if (result == DNS_EVENT_ADBNOMOREADDRESSES) {
goto detach;
}
if (result == DNS_EVENT_ADBMOREADDRESSES) {
goto detach;
}
}
static void
unsigned int options;
/* Something failed? */
if (result != ISC_R_SUCCESS) {
return;
}
/* More addresses pending? */
return;
}
/* We have as many addresses as we can get. */
}
static isc_result_t
isc_event_t *e;
notify, sizeof(isc_event_t));
if (e == NULL)
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
isc_event_free(&e);
return (result);
}
static void
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result == ISC_R_SUCCESS &&
{
if (result == ISC_R_NOTFOUND)
}
if (result != ISC_R_SUCCESS)
}
static void
/*
* Zone lock held by caller.
*/
if (result != ISC_R_SUCCESS)
return;
continue;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
}
}
void
unsigned int i;
return;
}
/*
* Enqueue notify request.
*/
continue;
if (result != ISC_R_SUCCESS) {
return;
}
if (result != ISC_R_SUCCESS) {
return;
}
}
/*
* Process NS RRset to generate notifies.
*/
if (result != ISC_R_SUCCESS)
goto cleanup1;
if (result != ISC_R_SUCCESS)
goto cleanup2;
/*
* Find master server's name.
*/
while (result == ISC_R_SUCCESS) {
if (result != ISC_R_SUCCESS)
continue;
if (result != ISC_R_SUCCESS)
continue;
if (result != ISC_R_NOMORE)
break;
}
if (result != ISC_R_NOMORE)
goto cleanup3;
if (result != ISC_R_SUCCESS)
goto cleanup3;
while (result == ISC_R_SUCCESS) {
if (result != ISC_R_SUCCESS)
continue;
/*
* don't notify the master server.
*/
continue;
}
if (isqueued) {
continue;
}
if (result != ISC_R_SUCCESS)
continue;
if (result != ISC_R_SUCCESS) {
continue;
}
}
if (dns_name_dynamic(&master))
}
/***
*** Private
***/
static inline isc_result_t
{
/*
* Extract NS RRset from message.
*/
NULL, &nsrdataset);
if (result != ISC_R_SUCCESS)
goto fail;
/*
* Add NS rdataset.
*/
if (result != ISC_R_SUCCESS)
goto fail;
nsrdataset, 0, NULL);
if (result != ISC_R_SUCCESS)
goto fail;
/*
* Add glue rdatasets.
*/
result == ISC_R_SUCCESS;
continue;
}
&rdataset);
if (result == ISC_R_SUCCESS) {
if (result != ISC_R_SUCCESS)
goto fail;
if (result != ISC_R_SUCCESS)
goto fail;
}
&rdataset);
if (result == ISC_R_SUCCESS) {
if (result != ISC_R_SUCCESS)
goto fail;
if (result != ISC_R_SUCCESS)
goto fail;
}
&rdataset);
if (result == ISC_R_SUCCESS) {
if (result != ISC_R_SUCCESS)
goto fail;
if (result != ISC_R_SUCCESS)
goto fail;
}
}
if (result != ISC_R_NOMORE)
goto fail;
return (ISC_R_SUCCESS);
fail:
return (result);
}
static void
const char me[] = "stub_callback";
char master[ISC_SOCKADDR_FORMATSIZE];
/* XXX add test for exiting */
goto next_master;
}
if (result != ISC_R_SUCCESS)
goto next_master;
if (result != ISC_R_SUCCESS)
goto next_master;
/*
* Unexpected rcode.
*/
char rcode[128];
"unexpected rcode (%.*s) from %s",
goto next_master;
}
/*
* We need complete messages.
*/
"truncated TCP response from %s", master);
goto next_master;
}
goto same_master;
}
/*
* If non-auth log and next master.
*/
"non-authorative answer from %s", master);
goto next_master;
}
/*
* Sanity checks.
*/
if (cnamecnt != 0) {
"found unexpected CNAME %s", master);
goto next_master;
}
if (nscnt == 0) {
"no NS records from %s", master);
goto next_master;
}
/*
* Save answer.
*/
if (result != ISC_R_SUCCESS) {
"unable to save ns records from %s", master);
goto next_master;
}
/*
* Tidy up.
*/
goto free_stub;
return;
}
goto free_stub;
goto detach;
return;
}
static void
const char me[] = "refresh_callback";
char master[ISC_SOCKADDR_FORMATSIZE];
/*
* if timeout log and next master;
*/
goto next_master;
}
if (result != ISC_R_SUCCESS)
goto next_master;
if (result != ISC_R_SUCCESS)
goto next_master;
/*
* Unexpected rcode.
*/
char rcode[128];
"unexpected rcode (%.*s) from %s",
goto next_master;
}
/*
* If truncated punt to zone transfer which will query again.
*/
"truncated UDP answer initiating "
"TCP zone xfer %s",
master);
goto tcp_transfer;
} else {
"truncated TCP response from %s",
master);
goto next_master;
}
goto same_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 at top of zone discovered: master %s", master);
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;
}
/*
* Only one soa at top of zone.
*/
if (soacnt != 1) {
"Answer SOA count (%d) != 1: master %s",
goto next_master;
}
/*
* Extract serial
*/
if (result != ISC_R_SUCCESS) {
"unable to get soa record from %s", master);
goto next_master;
}
if (result != ISC_R_SUCCESS) {
goto next_master;
}
if (result != ISC_R_SUCCESS) {
goto next_master;
}
} else {
}
isc_time_t t;
isc_time_set(&t, now, 0);
if (result != ISC_R_SUCCESS)
"isc_file_settime(%s): %s",
}
goto next_master;
} else {
goto next_master;
}
return;
}
return;
}
return;
return;
}
static void
const char me[] = "queue_soa_query";
isc_event_t *e;
return;
}
if (e == NULL) {
return;
}
/*
* Attach so that we won't clean up
* until the event is delivered.
*/
if (result != ISC_R_SUCCESS) {
isc_event_free(&e);
}
}
static inline isc_result_t
{
&message);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* Make question.
*/
return (ISC_R_SUCCESS);
return (result);
}
static void
const char me[] = "soa_query";
return;
}
/*
* XXX Optimisation: Create message when zone is setup and reuse.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result == ISC_R_SUCCESS &&
{
if (result == ISC_R_NOTFOUND)
}
DNS_REQUESTOPT_TCP : 0;
if (result != ISC_R_SUCCESS) {
"dns_request_create failed: %s",
goto cleanup;
}
return;
return;
}
static void
const char me[] = "ns_query";
goto cleanup;
/*
* Attach so that the zone won't disappear from under us.
*/
/*
* If a db exists we will update it, otherwise we create a
* new one and attach it to the zone once we have the NS
* RRset and glue.
*/
else {
if (result != ISC_R_SUCCESS) {
"dns_db_create failed: %s",
goto cleanup;
}
}
/*
* Update SOA record.
*/
&node);
if (result != ISC_R_SUCCESS) {
"dns_db_findnode failed: %s",
goto cleanup;
}
soardataset, 0, NULL);
if (result != ISC_R_SUCCESS) {
"dns_db_addrdataset failed: %s",
goto cleanup;
}
}
/*
* XXX Optimisation: Create message when zone is setup and reuse.
*/
if (result == ISC_R_SUCCESS &&
{
if (result == ISC_R_NOTFOUND)
}
/*
* Always use TCP so that we shouldn't truncate in additional section.
*/
if (result != ISC_R_SUCCESS) {
"dns_request_create failed: %s",
goto cleanup;
}
return;
}
return;
}
/*
* 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
/*
* If we were waiting for xfrin quota, step out of
* the queue.
*/
}
}
}
}
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:
/*FALLTHROUGH*/
case dns_zone_stub:
}
break;
default:
break;
}
if (next == 0) {
"settimer inactive");
} else {
"settimer %d %d = %d seconds",
}
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}
static void
const char me[] = "cancel_refresh";
/*
* caller to lock.
*/
}
static isc_result_t
{
isc_region_t r;
isc_buffer_t *b = NULL;
&message);
if (result != ISC_R_SUCCESS)
goto fail;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* Make question.
*/
temprdataset = NULL;
/*
* If the zone is dialup we are done as we don't want to send
* the current soa so as to force a refresh query.
*/
goto done;
if (result != ISC_R_SUCCESS)
goto done;
if (result != ISC_R_SUCCESS)
goto done;
if (result != ISC_R_SUCCESS)
goto done;
if (result != ISC_R_SUCCESS)
goto done;
if (result != ISC_R_SUCCESS)
goto done;
dns_rdatatype_none, 0, &rdataset,
NULL);
if (result != ISC_R_SUCCESS)
goto done;
if (result != ISC_R_SUCCESS)
goto done;
dns_rdata_toregion(&rdata, &r);
if (result != ISC_R_SUCCESS)
goto done;
isc_buffer_usedregion(b, &r);
if (result != ISC_R_NOMORE)
goto done;
temprdatalist->covers = 0;
if (result != ISC_R_SUCCESS)
goto done;
temprdataset = NULL;
done:
if (temprdataset != NULL)
if (temprdatalist != NULL)
fail:
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 ISC_R_SUCCESS.
*
* Otherwise initiate a refresh check using 'from' as the
* first address to check. Return ISC_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 (ISC_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 == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS) {
isc_uint32_t serial = 0;
if (result == ISC_R_SUCCESS) {
"zone up to date");
return (ISC_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 (ISC_R_SUCCESS);
}
return (ISC_R_SUCCESS);
}
void
}
void
}
void
}
}
return (zone->update_acl);
}
}
void
}
void
}
void
}
void
}
return (zone->check_names);
}
void
}
return (zone->journalsize);
}
static void
char message[4096];
int len;
if (result != ISC_R_SUCCESS)
}
static int
{
int count = 0;
while (result == ISC_R_SUCCESS) {
count++;
}
}
return (count);
}
void
}
}
void
}
}
}
}
void
}
void
}
const char *
}
void
if (idlein == 0)
}
}
void
if (idleout == 0)
}
}
static void
const char me[] = "notify_done";
}
unsigned int i;
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 != ISC_R_SUCCESS)
goto fail;
} else {
"dumping new zone version");
/* XXX should use temporary file and rename */
if (result != ISC_R_SUCCESS)
goto fail;
/*
* Update the time the zone was updated, so
* dns_zone_load can avoid loading it when
* the server is reloaded. If isc_time_now
* fails for some reason, all that happens is
* the timestamp is not updated.
*/
}
"removing journal file");
}
}
"replacing zone database");
return (ISC_R_SUCCESS);
fail:
return (result);
}
static void
const char me[] = "zone_xfrdone";
unsigned int soacount;
unsigned int nscount;
switch (result) {
case ISC_R_SUCCESS:
/*FALLTHROUGH*/
case DNS_R_UPTODATE:
/*
* This is not neccessary if we just performed a AXFR
* however it is necessary for an IXFR / UPTODATE and
* won't hurt with an AXFR.
*/
isc_time_t t;
isc_time_set(&t, now, 0);
if (result != ISC_R_SUCCESS)
"isc_file_settime(%s): %s",
}
/*
* Update the zone structure's data from the actual
* SOA received.
*/
nscount = 0;
soacount = 0;
if (result == ISC_R_SUCCESS) {
if (soacount != 1)
"has %d SOA record%s", soacount,
if (nscount == 0)
"no NS records");
}
/*
*/
} 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.
*/
/*
* This transfer finishing freed up a transfer quota slot.
* Let any zones waiting for quota have it.
*/
/*
* Retry with a different server if necessary.
*/
}
void
}
void
}
void
}
return (zone->sigvalidityinterval);
}
static void
const char me[] = "queue_xfrin";
if (result == ISC_R_QUOTA) {
"zone transfer deferred due to quota");
} else if (result != ISC_R_SUCCESS) {
"starting zone transfer: %s",
}
}
/*
* This event callback is called when a zone has received
* any necessary zone transfer quota. This is the time
* to go ahead and start the transfer.
*/
static void
const char me[] = "got_transfer_quota";
char mastertext[256];
goto cleanup;
}
/*
* Decide whether we should request IXFR or AXFR.
*/
"no database exists yet, requesting AXFR of "
"initial version from %s", mastertext);
} else {
; /* Using peer setting */
} else {
}
"IXFR disabled, requesting AXFR from %s",
} else {
"requesting IXFR form %s",
}
}
/*
* Determine if we should attempt to sign the request with TSIG.
*/
view->statickeys);
if (result == ISC_R_NOTFOUND)
view->dynamickeys);
"error getting tsig keys "
"for zone transfer: %s",
goto cleanup;
}
}
/*
* Any failure in this function is handled like a failed
* zone transfer. This ensures that we get removed from
* zmgr->xfrin_in_progress.
*/
if (result != ISC_R_SUCCESS)
return;
}
/***
*** Zone manager.
***/
{
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;
}
/* Create the zone task pool. */
if (result != ISC_R_SUCCESS)
goto free_conflock;
/* Create a single task for queueing of SOA queries. */
if (result != ISC_R_SUCCESS)
goto free_taskpool;
if (result != ISC_R_SUCCESS)
goto free_task;
/* 20 refresh queries / notifies per second. */
return (ISC_R_SUCCESS);
return (result);
}
/*
* Set the task name. The tag will arbitrarily point to one
* of the zones sharing the task (in practice, the one
* to be managed last).
*/
if (result != ISC_R_SUCCESS)
goto cleanup_task;
goto unlock;
return (result);
}
void
if (free_now)
}
void
}
void
if (free_now)
}
dns_zone_t *p;
p != NULL;
p = ISC_LIST_NEXT(p, link))
{
}
return (ISC_R_SUCCESS);
}
void
}
static void
}
void
}
void
}
void
}
int
return (zmgr->transfersin);
}
void
}
int
return (zmgr->transfersperns);
}
/*
* Try to start a new incoming zone transfer to fill a quota
* slot that was just vacated.
*
* Requires:
* The zone manager is locked by the caller.
*/
static void
static char me[] = "zmgr_resume_xfrs";
{
if (result == ISC_R_SUCCESS) {
/*
* We successfully filled the slot. We're done.
*/
break;
} else if (result == ISC_R_QUOTA) {
/*
* Not enough quota. This is probably the per-server
* quota, because we only get called when a unit of
* global quota has just been freed. Try the next
* zone, it may succeed if it uses another master.
*/
continue;
} else {
"starting zone transfer: %s",
break;
}
}
}
/*
* Try to start an incoming zone transfer for 'zone', quota permitting.
*
* Requires:
* The zone manager is locked by the caller.
*
* Returns:
* ISC_R_SUCCESS There was enough quota and we attempted to
* start a transfer. zone_xfrdone() has been or will
* be called.
* ISC_R_QUOTA Not enough quota.
* Others Failure.
*/
static isc_result_t
int nxfrsin, nxfrsperns;
dns_zone_t *x;
isc_event_t *e;
/*
* Find any configured information about the server we'd
* like to transfer this zone from.
*/
/*
* Determine the total maximum number of simultaneous
* transfers allowed, and the maximum for this specific
* master.
*/
/*
* Count the total number of transfers that are in progress,
* and the number of transfers 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.
*/
nxfrsin = nxfrsperns = 0;
x != NULL;
x = ISC_LIST_NEXT(x, statelink))
{
nxfrsin++;
nxfrsperns++;
}
/* Enforce quota. */
if (nxfrsin >= maxtransfersin)
return (ISC_R_QUOTA);
if (nxfrsperns >= maxtransfersperns)
return (ISC_R_QUOTA);
/*
* We have sufficient quota. Move the zone to the "xfrin_in_progress"
* list and send it an event to let it start the actual transfer in the
* context of its own task.
*/
sizeof(isc_event_t));
if (e == NULL)
return (ISC_R_NOMEMORY);
/*
* Make sure the zone does not go away before it has processed
* the event; in effect, the event is attached to the zone.
*
* XXXAG This should be done as soon as the zone goes on the
* queue, using irefs.
*/
return (ISC_R_SUCCESS);
}
#if 0
/* Hook for ondestroy notifcation from a database. */
static void
"database (%p) destroyed", (void*) db);
}
#endif