zone.c revision c6b5faa0018580cc2592311c2ecfaaa1f528afd5
/*
* Copyright (C) 1999-2001 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.341 2001/09/04 14:27:42 marka Exp $ */
#include <config.h>
#include <isc/ratelimiter.h>
#include <isc/refcount.h>
#include <isc/taskpool.h>
#include <dns/callbacks.h>
#include <dns/masterdump.h>
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/resolver.h>
/*
* Ensure 'a' is at least 'min' but not more than 'max'.
*/
/*
* Default values.
*/
#ifndef DNS_MAX_EXPIRE
#endif
#ifndef DNS_DUMP_DELAY
#endif
typedef struct dns_notify dns_notify_t;
typedef struct dns_stub dns_stub_t;
typedef struct dns_load dns_load_t;
typedef struct dns_forward dns_forward_t;
#define DNS_ZONE_CHECKLOCK
#ifdef DNS_ZONE_CHECKLOCK
#define LOCK_ZONE(z) \
} while (0)
#define UNLOCK_ZONE(z) \
#define LOCKED_ZONE(z) ((z)->locked)
#else
#define LOCKED_ZONE(z) ISC_TRUE
#endif
struct dns_zone {
/* Unlocked */
unsigned int magic;
#ifdef DNS_ZONE_CHECKLOCK
#endif
/* Locked */
unsigned int irefs;
char *masterfile;
char *journal;
unsigned int flags;
unsigned int options;
unsigned int db_argc;
char **db_argv;
unsigned int masterscnt;
unsigned int curmaster;
unsigned int refreshcnt;
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.
*/
/*
* Optional per-zone statistics counters (NULL if not present).
*/
};
#define DNS_ZONE_SETFLAG(z,f) do { \
INSIST(LOCKED_ZONE(z)); \
(z)->flags |= (f); \
} while (0)
#define DNS_ZONE_CLRFLAG(z,f) do { \
INSIST(LOCKED_ZONE(z)); \
(z)->flags &= ~(f); \
} while (0)
/* XXX MPA these may need to go back into zone.h */
* uptodate */
* messages */
* reload */
* zone with no masters
* occured */
* from SOA (if not set, we
* are still using
* default timer values) */
#define DNS_ZONEFLG_NOREFRESH 0x00010000U
#define DNS_ZONEFLG_DIALNOTIFY 0x00020000U
#define DNS_ZONEFLG_DIALREFRESH 0x00040000U
#define DNS_ZONEFLG_SHUTDOWN 0x00080000U
#define DNS_ZONEFLG_FLUSH 0x00200000U
#define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0)
/* Flags for zone_load() */
struct dns_zonemgr {
unsigned int magic;
int refs; /* Locked by rwlock */
isc_task_t * task;
/* Locked by rwlock. */
/* Configuration data. */
int transfersin;
int transfersperns;
unsigned int serialqueryrate;
/* Locked by iolock */
};
/*
* Hold notify state.
*/
struct dns_notify {
unsigned int magic;
unsigned int flags;
unsigned int attempt;
};
#define DNS_NOTIFY_NOSOA 0x0001U
/*
* 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 {
unsigned int magic;
};
/*
* Hold load state.
*/
struct dns_load {
unsigned int magic;
};
/*
* Hold forward state.
*/
struct dns_forward {
unsigned int magic;
void *callback_arg;
};
/*
* Hold IO request state.
*/
struct dns_io {
unsigned int magic;
};
static void cancel_refresh(dns_zone_t *);
#if 0
/* ondestroy example */
#endif
dns_stub_t *stub);
unsigned int flags,
dns_zone_t *zone);
static isc_result_t
const char *templat);
const unsigned int dbargc_default = 1;
const char *dbargv_default[] = { "rbt" };
/***
*** 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 */
#ifdef DNS_ZONE_CHECKLOCK
#endif
zone->masterscnt = 0;
zone->refreshcnt = 0;
/* Must be after magic is set. */
if (result != ISC_R_SUCCESS)
goto free_mutex;
return (ISC_R_SUCCESS);
return (ISC_R_NOMEMORY);
}
/*
* 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.
*/
}
}
void
}
/*
* Single shot.
*/
void
/*
* Test and set.
*/
}
static void
unsigned int i;
/* Free the old database argument list. */
}
}
unsigned int i;
/* Set up a new database argument list. */
goto nomem;
for (i = 0; i < dbargc; i++)
for (i = 0; i < dbargc; i++) {
goto nomem;
}
/* Free the old list. */
goto unlock;
for (i = 0; i < dbargc; i++) {
}
}
return (result);
}
void
}
}
}
return (result);
}
static isc_result_t
char *copy;
return (ISC_R_NOMEMORY);
} else {
}
return (ISC_R_SUCCESS);
}
if (result == ISC_R_SUCCESS)
return (result);
}
const char *
return (zone->masterfile);
}
static isc_result_t
char *journal;
/* Calculate string length including '\0'. */
return (ISC_R_NOMEMORY);
} else {
}
return (result);
}
return (result);
}
char *
}
/*
* Return true iff the zone is "dynamic", in the sense that the zone's
* master file (if any) is written by the server, rather than being
* updated manually and read by the server.
*
* This is true for slave zones, stub zones, and zones that allow
* dynamic updates either by having an update policy ("ssutable")
* or an "allow-update" ACL with a value other than exactly "{ none; }".
*/
static isc_boolean_t
&&
}
static isc_result_t
isc_time_now(&now);
goto cleanup;
}
/*
* The zone has no master file configured, but it already
* has a database. It could be the built-in
* version.bind. CH zone, a zone with a persistent
* database being reloaded, or maybe a zone that
* used to have a master file but whose configuration
* was changed so that it no longer has one. Do nothing.
*/
goto cleanup;
}
/*
* This is a slave, stub, or dynamically updated
* zone being reloaded. Do nothing - the database
* we already have is guaranteed to be up-to-date.
*/
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.
*/
/*
* The file is already loaded. If we are just doing a
* "rndc reconfig", we are done.
*/
if ((flags & DNS_ZONELOADFLAG_NOSTAT) != 0) {
goto cleanup;
}
&filetime);
if (result == ISC_R_SUCCESS &&
"skipping load: master 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) {
"loading zone: creating database: %s",
goto cleanup;
}
if (! dns_db_ispersistent(db)) {
} else {
"loading zone: "
"no master file configured");
goto cleanup;
}
"no master file configured: continuing");
}
}
if (result == DNS_R_CONTINUE) {
goto cleanup;
}
dns_db_detach(&db);
return (result);
}
}
}
static void
if (result == ISC_R_CANCELED)
goto fail;
goto fail;
return;
fail:
}
static isc_result_t
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS) {
if (result == ISC_R_SUCCESS)
goto cleanup;
} else
unsigned int options;
if (result != ISC_R_SUCCESS)
return (result);
if (result == ISC_R_SUCCESS)
} else {
}
return (result);
return (result);
}
static isc_result_t
{
unsigned int soacount = 0;
unsigned int nscount = 0;
isc_time_now(&now);
/*
* 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_FILENOTFOUND)
"no master file");
else if (result != DNS_R_NOMASTERFILE)
"loading master file %s: %s",
} else
"loading master file %s: %s",
goto cleanup;
}
"number of nodes in database: %u",
if (result == DNS_R_SEENINCLUDE)
else
/*
* Apply update log, if any.
*/
result != ISC_R_RANGE) {
"journal rollforward failed: %s",
goto cleanup;
}
"journal rollforward failed: "
"journal out of sync with zone");
goto cleanup;
}
"journal rollforward completed "
"successfully: %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 records", soacount);
}
if (nscount == 0) {
"has no NS records");
}
if (result != ISC_R_SUCCESS)
goto cleanup;
"zone serial has gone backwards");
}
}
isc_time_t t;
if (result != ISC_R_SUCCESS)
&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 {
}
if (needdump)
return (result);
/* Mark the zone for immediate refresh. */
}
return (result);
}
static isc_boolean_t
{
/*
* DNS_ZONEFLG_SHUTDOWN can only be set if erefs == 0.
*/
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static isc_result_t
unsigned int *nscount)
{
unsigned int count;
if (result == ISC_R_NOTFOUND) {
*nscount = 0;
goto invalidate_rdataset;
}
else 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)
}
minimum);
if (result != ISC_R_SUCCESS)
}
return (answer);
}
void
}
void
unsigned int refs;
if (refs == 0) {
/*
* 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
}
static void
/*
* 'source' locked by caller.
*/
}
static void
/*
* 'zone' locked by caller.
*/
}
void
if (free_needed)
}
}
}
void
if (value)
else
}
void
{
if (value)
else
}
unsigned int
}
return (ISC_R_SUCCESS);
}
return (&zone->xfrsource4);
}
return (ISC_R_SUCCESS);
}
return (&zone->xfrsource6);
}
return (ISC_R_SUCCESS);
}
return (&zone->notifysrc4);
}
return (ISC_R_SUCCESS);
}
return (&zone->notifysrc6);
}
{
}
goto unlock;
return (ISC_R_NOMEMORY);
}
return (ISC_R_SUCCESS);
}
{
return (result);
}
{
unsigned int i;
}
}
for (i = 0; i < zone->masterscnt; i++) {
zone->masterkeynames[i],
sizeof(dns_name_t));
}
}
}
zone->masterscnt = 0;
/*
* If count == 0, don't allocate any space for masters or keynames
* so internally, those pointers are NULL if count == 0
*/
if (count == 0)
goto unlock;
/*
* masters must countain count elements!
*/
count * sizeof(isc_sockaddr_t));
goto unlock;
}
/*
* if keynames is non-NULL, it must contain count elements!
*/
count * sizeof(dns_name_t *));
goto unlock;
}
for (i = 0; i < count; i++)
for (i = 0; i < count; i++) {
sizeof(dns_name_t));
goto allocfail;
newname[i]);
if (result != ISC_R_SUCCESS) {
for (i = 0; i < count; i++)
newname[i],
goto unlock;
}
}
}
}
return (result);
}
else
return (result);
}
/*
* Co-ordinates the starting of routine jobs.
*/
void
const char me[] = "dns_zone_maintenance";
isc_time_now(&now);
}
static inline isc_boolean_t
if (!dumping) {
}
return (dumping);
}
static void
const char me[] = "zone_maintenance";
/*
* Configuring the view of this zone may have
* failed, for example because the config file
* had a syntax error. In that case, the view
* adb or resolver, and we had better not try
* to do maintenance on it.
*/
return;
isc_time_now(&now);
/*
* 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:
case dns_zone_slave:
} else
if (!dumping) {
if (result != ISC_R_SUCCESS)
"dump failed: %s",
}
break;
default:
break;
}
/*
* Do we need to send out notify messages?
*/
case dns_zone_master:
case dns_zone_slave:
break;
default:
break;
}
}
void
}
void
}
static void
/*
* 'zone' locked by caller.
*/
}
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)
"cannot refresh: no masters");
goto unlock;
}
goto unlock;
/*
* Set the next refresh time as if refresh check has failed.
* Setting this to the retry time will do that. XXXMLG
* If we are successful it will be reset using zone->refresh.
*/
0);
/*
* When lacking user-specified timer values from the SOA,
* do exponential backoff of the retry time up to a
* maximum of six hours.
*/
zone->refreshcnt = 0;
/* initiate soa query */
}
else
if (!dumping)
return (result);
}
if (!dumping)
return (result);
}
static void
/*
* 'zone' locked by caller
*/
/*
* Do we have a place to dump to and are we loaded?
*/
return;
isc_interval_set(&i, delay, 0);
isc_time_now(&now);
/* add some noise */
}
static isc_result_t
char *masterfile = NULL;
/*
* 'compact' MUST only be set if we are task locked.
*/
redo:
goto fail;
}
if (masterfile == NULL) {
goto fail;
}
/*
* is only set if we are task locked.
*/
if (tresult == ISC_R_SUCCESS) {
switch (tresult) {
case ISC_R_SUCCESS:
case ISC_R_NOSPACE:
"dns_journal_compact: %s",
break;
default:
"dns_journal_compact failed: %s",
break;
}
}
}
fail:
dns_db_detach(&db);
if (masterfile != NULL)
masterfile = NULL;
if (result != ISC_R_SUCCESS) {
/*
* Try again in a short while.
*/
}
if (again)
goto redo;
return (result);
}
return (DNS_R_NOTLOADED);
dns_db_detach(&db);
return (result);
}
void
}
static void
/*
* 'zone' locked by caller.
*/
}
}
static void
/*
* 'zone' locked by caller.
*/
}
void
}
void
}
void
}
void
}
static isc_boolean_t
continue;
return (ISC_TRUE);
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static void
/*
* Caller holds zone lock.
*/
if (!locked)
if (!locked)
if (locked)
else
}
}
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_ADBMOREADDRESSES) {
return;
}
if (result == DNS_EVENT_ADBNOMOREADDRESSES) {
}
}
static void
unsigned int options;
goto destroy;
/* Something failed? */
if (result != ISC_R_SUCCESS)
goto destroy;
/* 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
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
int timeout;
goto cleanup;
}
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
addrbuf);
case PF_INET:
break;
case PF_INET6:
break;
default:
goto cleanup_key;
}
timeout = 15;
timeout = 30;
if (result != ISC_R_SUCCESS)
}
static void
/*
* Zone lock held by caller.
*/
continue;
&new);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
}
}
void
isc_time_now(&now);
}
static void
isc_uint32_t serial = 0;
unsigned int i;
unsigned int flags = 0;
return;
if (notifytype == dns_notifytype_no)
return;
/*
* 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.
*/
/*
* Enqueue notify requests for 'also-notify' servers.
*/
continue;
if (result != ISC_R_SUCCESS) {
return;
}
if (result != ISC_R_SUCCESS) {
return;
}
}
if (notifytype == dns_notifytype_explicit)
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.
*/
if (result == ISC_R_SUCCESS) {
if (result == ISC_R_SUCCESS) {
&master);
}
}
if (result != ISC_R_SUCCESS)
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 (!loggednotify) {
"sending notifies (serial %u)",
serial);
}
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];
isc_time_now(&now);
goto next_master;
}
"could not refresh stub from master %s: %s",
goto next_master;
}
if (result != ISC_R_SUCCESS)
goto next_master;
if (result != ISC_R_SUCCESS)
goto next_master;
/*
* Unexpected rcode.
*/
char rcode[128];
"refreshing stub: "
"unexpected rcode (%.*s) from %s",
goto next_master;
}
/*
* We need complete messages.
*/
"refreshing stub: "
"truncated TCP response from master %s",
master);
goto next_master;
}
goto same_master;
}
/*
* If non-auth log and next master.
*/
"non-authoritative answer from master %s",
master);
goto next_master;
}
/*
* Sanity checks.
*/
if (cnamecnt != 0) {
"refreshing stub: unexpected CNAME response "
"from master %s", master);
goto next_master;
}
if (nscnt == 0) {
"refreshing stub: no NS records in response "
"from master %s", master);
goto next_master;
}
/*
* Save answer.
*/
if (result != ISC_R_SUCCESS) {
"refreshing stub: unable to save NS records "
"from master %s", master);
goto next_master;
}
/*
* Tidy up.
*/
}
goto free_stub;
zone->refreshcnt = 0;
goto free_stub;
}
goto free_stub;
goto done;
done:
return;
}
/*
* An SOA query has finished (successfully or not).
*/
static void
const char me[] = "refresh_callback";
char master[ISC_SOCKADDR_FORMATSIZE];
/*
* if timeout log and next master;
*/
isc_time_now(&now);
"refresh: failure trying master %s: %s",
goto same_master;
"refresh: retry limit for "
"master %s exceeded",
master);
}
goto next_master;
}
if (result != ISC_R_SUCCESS)
goto next_master;
if (result != ISC_R_SUCCESS) {
"refresh: failure trying master %s: %s",
goto next_master;
}
/*
* Unexpected rcode.
*/
char rcode[128];
"refresh: unexpected rcode (%.*s) from master %s",
goto next_master;
}
/*
* If truncated punt to zone transfer which will query again.
*/
"refresh: truncated UDP answer, "
"initiating TCP zone xfer "
"for master %s",
master);
goto tcp_transfer;
} else {
"refresh: truncated TCP response "
"from master %s",
master);
goto next_master;
}
goto same_master;
}
}
/*
* if non-auth log and next master;
*/
"refresh: non-authoritative answer from "
"master %s", master);
goto next_master;
}
/*
* There should not be a CNAME record at top of zone.
*/
if (cnamecnt != 0) {
"refresh: CNAME at top of zone "
"in master %s", master);
goto next_master;
}
/*
* if referral log and next master;
*/
"refresh: referral response "
"from master %s", master);
goto next_master;
}
/*
* if nodata log and next master;
*/
"refresh: NODATA response "
"from master %s", master);
goto next_master;
}
/*
* Only one soa at top of zone.
*/
if (soacnt != 1) {
"refresh: answer SOA count (%d) != 1 "
"from master %s",
goto next_master;
}
/*
* Extract serial
*/
if (result != ISC_R_SUCCESS) {
"refresh: unable to get SOA record "
"from master %s", master);
goto next_master;
}
if (result != ISC_R_SUCCESS) {
"refresh: dns_rdataset_first() failed");
goto next_master;
}
if (result != ISC_R_SUCCESS) {
"refresh: dns_rdata_tostruct() failed");
goto next_master;
}
} else {
}
if (result != ISC_R_SUCCESS)
"refresh: could not set file "
"modification time of '%s': %s",
}
goto next_master;
} else {
goto next_master;
}
goto detach;
zone->refreshcnt = 0;
}
goto detach;
}
goto detach;
zone->refreshcnt++;
return;
}
static void
const char me[] = "queue_soa_query";
isc_event_t *e;
/*
* Locked by caller
*/
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";
int timeout;
goto cleanup;
}
/*
* XXX Optimisation: Create message when zone is setup and reuse.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
DNS_REQUESTOPT_TCP : 0;
case PF_INET:
break;
case PF_INET6:
break;
default:
goto cleanup;
}
timeout = 15;
timeout = 30;
if (result != ISC_R_SUCCESS) {
"dns_request_createvia() failed: %s",
goto cleanup;
}
if (cancel)
return;
}
static void
const char me[] = "ns_query";
int timeout;
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) {
"refreshing stub: "
"could not create "
"database: %s",
goto cleanup;
}
}
/*
* Update SOA record.
*/
&node);
if (result != ISC_R_SUCCESS) {
"refreshing stub: "
"dns_db_findnode() failed: %s",
goto cleanup;
}
soardataset, 0, NULL);
if (result != ISC_R_SUCCESS) {
"refreshing stub: "
"dns_db_addrdataset() failed: %s",
goto cleanup;
}
}
/*
* XXX Optimisation: Create message when zone is setup and reuse.
*/
/*
* Always use TCP so that we shouldn't truncate in additional section.
*/
case PF_INET:
break;
case PF_INET6:
break;
default:
goto cleanup;
}
timeout = 15;
timeout = 30;
if (result != ISC_R_SUCCESS) {
"dns_request_createvia() failed: %s",
goto cleanup;
}
goto unlock;
}
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
/*
* Stop things being restarted after we cancel them below.
*/
/*
* If we were waiting for xfrin quota, step out of
* the queue.
* If there's no zone manager, we can't be waiting for the
* xfrin quota
*/
}
}
/*
* In task context, no locking required. See zone_xfrdone().
*/
}
}
/*
* We have now canceled everything set the flag to allow exit_check()
* to succeed. We must not unlock between setting this flag and
* calling exit_check().
*/
if (free_needed)
}
static void
const char me[] = "zone_timer";
}
static void
const char me[] = "zone_settimer";
return;
case dns_zone_master:
if (isc_time_isepoch(&next) ||
}
break;
case dns_zone_slave:
/*FALLTHROUGH*/
case dns_zone_stub:
if (isc_time_isepoch(&next) ||
}
if (isc_time_isepoch(&next) ||
}
break;
default:
break;
}
if (isc_time_isepoch(&next)) {
if (result != ISC_R_SUCCESS)
"could not deactivate zone timer: %s",
} else {
if (result != ISC_R_SUCCESS)
"could not reset zone timer: %s",
}
}
static void
const char me[] = "cancel_refresh";
/*
* 'zone' locked by caller.
*/
isc_time_now(&now);
}
static isc_result_t
{
isc_region_t r;
isc_buffer_t *b = NULL;
&message);
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* Make question.
*/
temprdataset = NULL;
if ((flags & DNS_NOTIFY_NOSOA) != 0)
goto done;
if (result != ISC_R_SUCCESS)
goto soa_cleanup;
if (result != ISC_R_SUCCESS)
goto soa_cleanup;
if (result != ISC_R_SUCCESS)
goto soa_cleanup;
if (result != ISC_R_SUCCESS)
goto soa_cleanup;
if (result != ISC_R_SUCCESS)
goto soa_cleanup;
dns_rdatatype_none, 0, &rdataset,
NULL);
if (result != ISC_R_SUCCESS)
goto soa_cleanup;
if (result != ISC_R_SUCCESS)
goto soa_cleanup;
dns_rdata_toregion(&rdata, &r);
if (result != ISC_R_SUCCESS)
goto soa_cleanup;
isc_buffer_usedregion(b, &r);
if (result != ISC_R_NOMORE)
goto soa_cleanup;
temprdatalist->covers = 0;
if (result != ISC_R_SUCCESS)
goto soa_cleanup;
temprdataset = NULL;
if (temprdataset != NULL)
if (temprdatalist != NULL)
done:
return (ISC_R_SUCCESS);
if (temprdataset != NULL)
return (result);
}
{
unsigned int i;
char fromtext[ISC_SOCKADDR_FORMATSIZE];
int match = 0;
/*
* 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.
*/
"NOTIFY with no "
"question section from: %s", fromtext);
return (DNS_R_FORMERR);
}
"NOTIFY 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;
/*
* Accept notify requests from non masters if they are on
* 'zone->notify_acl'.
*/
match > 0)
{
/* Accept notify. */
} else if (i >= zone->masterscnt) {
"refused notify from non-master: %s", fromtext);
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) {
"notify from %s: "
"zone is up to date",
fromtext);
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
*/
"notify from %s: refresh in progress, "
"refresh check queued",
fromtext);
return (ISC_R_SUCCESS);
}
return (ISC_R_SUCCESS);
}
void
}
void
}
void
}
void
}
void
}
return (zone->notify_acl);
}
}
return (zone->update_acl);
}
return (zone->forward_acl);
}
}
void
}
void
}
void
}
void
}
void
}
void
}
return (zone->check_names);
}
void
}
return (zone->journalsize);
}
static void
/*
* Leave space for terminating '\0'.
*/
if (result != ISC_R_SUCCESS)
}
static void
char message[4096];
return;
}
void
char message[4096];
return;
}
static void
const char *fmt, ...)
{
char message[4096];
return;
}
static int
{
int count = 0;
while (result == ISC_R_SUCCESS) {
count++;
}
}
return (count);
}
void
}
}
void
}
}
}
}
void
}
void
}
void
if (idlein == 0)
}
}
void
}
}
static void
char rcode[128];
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
if (result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS)
"notify response from %s: %.*s",
else
"notify to %s failed: %s", addrbuf,
/*
* Old bind's return formerr if they see a soa record. Retry w/o
* the soa if we see a formerr and had sent a SOA.
*/
if ((result == ISC_R_TIMEDOUT ||
} else {
if (result == ISC_R_TIMEDOUT)
"notify to %s: retries exceeded", addrbuf);
}
}
return (result);
}
static isc_result_t
/*
* 'zone' locked by caller.
*/
/*
* 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");
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.
*/
}
/*
* The in-memory database just changed, and
* because 'dump' is set, it didn't change by
* being loaded from disk. Also, we have not
* journalled diffs for this change.
* Therefore, the on-disk journal is missing
* the deltas for this change. Since it can
* no longer be used to bring the zone
* up-to-date, it is useless and should be
* removed.
*/
"removing journal file");
}
}
"replacing zone database");
return (ISC_R_SUCCESS);
fail:
return (result);
}
static void
unsigned int soacount;
unsigned int nscount;
isc_time_now(&now);
switch (result) {
case ISC_R_SUCCESS:
/*FALLTHROUGH*/
case DNS_R_UPTODATE:
/*
* Has the zone expired underneath us?
*/
goto same_master;
/*
* 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.
*/
if (result != ISC_R_SUCCESS &&
&now);
if (result != ISC_R_SUCCESS)
"transfer: could not set file "
"modification time of '%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)
"transferred zone "
"has %d SOA record%s", soacount,
if (nscount == 0)
"transferred zone "
"has no NS records");
zone->maxrefresh);
}
/*
*/
} else {
}
break;
case DNS_R_BADIXFR:
/* Force retry with AXFR. */
goto same_master;
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 other zones waiting for quota have it.
*/
/*
* Retry with a different server if necessary.
*/
}
static void
static char me[] = "zone_loaddone";
if (tresult != ISC_R_SUCCESS &&
}
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
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 if (dns_zone_isforced(zone)) {
"forced reload, requesting AXFR of "
"initial version from %s", mastertext);
"retrying with AXFR from %s due to "
"previous IXFR failure", mastertext);
} else {
; /* Using peer setting */
} else {
}
"IXFR disabled, "
"requesting AXFR from %s",
} else {
"requesting IXFR from %s",
}
}
/*
* Determine if we should attempt to sign the request with TSIG.
*/
/*
* First, look for a tsig key in the master statement, then
* try for a server key.
*/
}
"could not get TSIG key "
"for zone transfer: %s",
}
/*
* 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;
}
/*
* Update forwarding support.
*/
static void
}
static isc_result_t
return (ISC_R_NOMORE);
}
/*
* Always use TCP regardless of whether the original update
* used TCP.
* XXX The timeout may but a bit small if we are far down a
* transfer graph and the master has to try several masters.
*/
case PF_INET:
break;
case PF_INET6:
break;
default:
goto unlock;
}
return (result);
}
static void
const char me[] = "forward_callback";
char master[ISC_SOCKADDR_FORMATSIZE];
"could not forward dynamic update to %s: %s",
goto next_master;
}
if (result != ISC_R_SUCCESS)
goto next_master;
if (result != ISC_R_SUCCESS)
goto next_master;
/*
* Pass these rcodes back to client.
*/
case dns_rcode_noerror:
case dns_rcode_yxdomain:
case dns_rcode_yxrrset:
case dns_rcode_nxrrset:
case dns_rcode_refused:
case dns_rcode_nxdomain:
break;
case dns_rcode_notzone:
case dns_rcode_notauth: {
char rcode[128];
"forwarding dynamic update: "
"unexpected response: master %s returned: %.*s",
goto next_master;
}
/* Try another server for these rcodes. */
case dns_rcode_formerr:
case dns_rcode_servfail:
case dns_rcode_notimp:
case dns_rcode_badvers:
default:
goto next_master;
}
/* call callback */
return;
if (result != ISC_R_SUCCESS) {
/* call callback */
"exhausted dynamic update forwarder list");
}
}
{
return (ISC_R_NOMEMORY);
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS) {
}
return (result);
}
return (ISC_R_NOMORE);
else
return (ISC_R_SUCCESS);
}
return (ISC_R_NOMORE);
else
return (ISC_R_SUCCESS);
}
/***
*** Zone manager.
***/
{
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS) {
"isc_rwlock_init() failed: %s",
goto free_mem;
}
/* Create the zone task pool. */
if (result != ISC_R_SUCCESS)
goto free_rwlock;
/* 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;
/* default to 20 refresh queries / notifies per second. */
if (result != ISC_R_SUCCESS) {
"isc_mutex_init() failed: %s",
goto free_rl;
}
return (ISC_R_SUCCESS);
#if 0
#endif
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;
/*
* The timer "holds" a iref.
*/
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))
{
}
/*
* Recent configuration changes may have increased the
* amount of available transfers quota. Make sure any
* transfers currently blocked on quota get started if
* possible.
*/
return (ISC_R_SUCCESS);
}
void
}
static 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
{
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);
}
void
}
}
/*
* Get permission to request a file handle from the OS.
* An event will be sent to action when one is available.
* There are two queues available (high and low), the high
* queue will be serviced before the low one.
*
* zonemgr_putio() must be called after the event is delivered to
* 'action'.
*/
static isc_result_t
{
return (ISC_R_NOMEMORY);
return (ISC_R_NOMEMORY);
}
if (queue) {
else
}
if (!queue) {
}
return (ISC_R_SUCCESS);
}
static void
else
}
}
static void
/*
* If we are queued to be run then dequeue.
*/
else
}
if (send_event) {
}
}
static void
char *buf;
int buflen;
return;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
}
#if 0
/* Hook for ondestroy notifcation from a database. */
static void
"database (%p) destroyed", (void*) db);
}
#endif
void
isc_uint32_t s, ns;
if (value == 0)
value = 1;
if (value == 1) {
s = 1;
ns = 0;
pertic = 1;
} else if (value < 10) {
s = 0;
pertic = 1;
} else {
s = 0;
pertic = 10;
}
}
unsigned int
return (zmgr->serialqueryrate);
}
void
}
}
if (on) {
goto done;
} else {
goto done;
}
done:
return (result);
}
}
void
"notify = %d, refresh = %d",
}
void
switch (dialup) {
case dns_dialuptype_no:
break;
case dns_dialuptype_yes:
break;
case dns_dialuptype_notify:
break;
break;
case dns_dialuptype_refresh:
break;
case dns_dialuptype_passive:
break;
default:
INSIST(0);
}
}
unsigned int
unsigned int count = 0;
switch (state) {
count++;
break;
count++;
break;
case DNS_ZONESTATE_SOAQUERY:
count++;
break;
case DNS_ZONESTATE_ANY:
count++;
break;
default:
INSIST(0);
}
return (count);
}