Commit f7d85bae authored by Andreas Gustafsson's avatar Andreas Gustafsson
Browse files

Keep a queue of zones waiting for zone transfer quota so that a new

transfer can be dispatched immediately whenever quota becomes available
parent de8661e5
173. [func] Keep a queue of zones waiting for zone transfer
quota so that a new transfer can be dispatched
immediately whenever quota becomes available.
172. [bug] $TTL directive was sometimes missing from dumped
master files because totext_ctx_init() failed to
initialize ctx->current_ttl_valid.
......
......@@ -49,6 +49,7 @@
#define DNS_EVENT_VIEWREQSHUTDOWN (ISC_EVENTCLASS_DNS + 22)
#define DNS_EVENT_NOTIFYSENDTOADDR (ISC_EVENTCLASS_DNS + 23)
#define DNS_EVENT_ZONE (ISC_EVENTCLASS_DNS + 24)
#define DNS_EVENT_ZONESTARTXFRIN (ISC_EVENTCLASS_DNS + 25)
#define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0)
#define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535)
......
......@@ -96,8 +96,8 @@ typedef isc_uint32_t dns_ttl_t;
typedef struct dns_validator dns_validator_t;
typedef struct dns_view dns_view_t;
typedef ISC_LIST(dns_view_t) dns_viewlist_t;
typedef struct dns_xfrinlist dns_xfrinlist_t;
typedef struct dns_zone dns_zone_t;
typedef ISC_LIST(dns_zone_t) dns_zonelist_t;
typedef struct dns_zonemgr dns_zonemgr_t;
typedef struct dns_zt dns_zt_t;
......
......@@ -42,12 +42,6 @@
/* A transfer in progress. This is an opaque type. */
typedef struct dns_xfrin_ctx dns_xfrin_ctx_t;
/* A list of transfers in progress. */
struct dns_xfrinlist {
isc_mutex_t lock;
ISC_LIST(dns_xfrin_ctx_t) transfers;
};
/***
*** Functions
***/
......@@ -55,7 +49,8 @@ struct dns_xfrinlist {
ISC_LANG_BEGINDECLS
isc_result_t
dns_xfrin_create(dns_zone_t *zone, isc_sockaddr_t *masteraddr,
dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
isc_sockaddr_t *masteraddr, dns_tsigkey_t *tsigkey,
isc_mem_t *mctx, isc_timermgr_t *timermgr,
isc_socketmgr_t *socketmgr, isc_task_t *task,
dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp);
......@@ -84,10 +79,6 @@ void dns_xfrin_detach(dns_xfrin_ctx_t **xfrp);
* only be one reference).
*/
isc_result_t dns_xfrinlist_init(dns_xfrinlist_t *list);
void dns_xfrinlist_destroy(dns_xfrinlist_t *list);
ISC_LANG_ENDDECLS
#endif /* DNS_XFRIN_H */
......@@ -880,9 +880,6 @@ dns_zonemgr_settransfersperns(dns_zonemgr_t *zmgr, int value);
int
dns_zonemgr_getttransfersperns(dns_zonemgr_t *zmgr);
dns_xfrinlist_t *
dns_zonemgr_gettransferlist(dns_zonemgr_t *zmgr);
ISC_LANG_ENDDECLS
#endif /* DNS_ZONE_H */
......@@ -15,7 +15,7 @@
* SOFTWARE.
*/
/* $Id: xfrin.c,v 1.68 2000/05/10 03:33:54 tale Exp $ */
/* $Id: xfrin.c,v 1.69 2000/05/14 20:01:27 gson Exp $ */
#include <config.h>
......@@ -31,7 +31,6 @@
#include <dns/journal.h>
#include <dns/log.h>
#include <dns/message.h>
#include <dns/peer.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/result.h>
......@@ -155,9 +154,6 @@ struct dns_xfrin_ctx {
dns_journal_t *journal;
} ixfr;
ISC_LINK(dns_xfrin_ctx_t) link;
dns_xfrinlist_t *transferlist;
};
#define XFRIN_MAGIC 0x58667269U /* XfrI. */
......@@ -503,132 +499,21 @@ xfr_rr(dns_xfrin_ctx_t *xfr,
}
isc_result_t
dns_xfrin_create(dns_zone_t *zone, isc_sockaddr_t *masteraddr,
dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
isc_sockaddr_t *masteraddr, dns_tsigkey_t *tsigkey,
isc_mem_t *mctx, isc_timermgr_t *timermgr,
isc_socketmgr_t *socketmgr, isc_task_t *task,
dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp)
{
dns_name_t *zonename;
dns_xfrin_ctx_t *xfr, *x;
dns_name_t *zonename = dns_zone_getorigin(zone);
dns_xfrin_ctx_t *xfr;
isc_result_t result;
dns_db_t *db = NULL;
dns_rdatatype_t xfrtype;
dns_tsigkey_t *key = NULL;
dns_name_t *keyname = NULL;
isc_netaddr_t masterip;
dns_peer_t *peer = NULL;
int maxtransfersin, maxtransfersperns;
int nxfrsin, nxfrsperns;
dns_xfrinlist_t *transferlist;
REQUIRE(xfrp != NULL && *xfrp == NULL);
zonename = dns_zone_getorigin(zone);
xfrin_log1(ISC_LOG_INFO, zonename, masteraddr, "starting");
/*
* Find any configured information about the server we are about
* to transfer from.
*/
isc_netaddr_fromsockaddr(&masterip, masteraddr);
(void) dns_peerlist_peerbyaddr(dns_zone_getview(zone)->peers,
&masterip, &peer);
result = dns_zone_getdb(zone, &db);
if (result == DNS_R_NOTLOADED)
INSIST(db == NULL);
else
CHECK(result);
/*
* Decide whether we should request IXFR or AXFR.
*/
if (db == NULL) {
xfrin_log1(ISC_LOG_DEBUG(3), zonename, masteraddr,
"no database exists yet, "
"requesting AXFR of initial version");
xfrtype = dns_rdatatype_axfr;
} else {
isc_boolean_t use_ixfr = ISC_TRUE;
if (peer != NULL &&
dns_peer_getrequestixfr(peer, &use_ixfr) ==
ISC_R_SUCCESS) {
; /* Using peer setting */
} else {
use_ixfr = dns_zone_getview(zone)->requestixfr;
}
if (use_ixfr == ISC_FALSE) {
xfrin_log1(ISC_LOG_DEBUG(3), zonename, masteraddr,
"IXFR disabled, requesting AXFR");
xfrtype = dns_rdatatype_axfr;
} else {
xfrin_log1(ISC_LOG_DEBUG(3), zonename, masteraddr,
"requesting IXFR");
xfrtype = dns_rdatatype_ixfr;
}
}
/*
* Determine the maximum number of simultaneous transfers
* allowed for this server, then count the number of
* transfers already in progress and fail if the quota
* is already full.
*
* Count the number of transfers that are 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.
*
* Note that we must keep the transfer list locked for an
* awkwardly long time because the scanning of the list
* and the creation of a new entry must be done atomically,
* and we don't want to create the transfer object until we
* know there is quota available.
*/
maxtransfersin =
dns_zonemgr_getttransfersin(dns_zone_getmgr(zone));
maxtransfersperns =
dns_zonemgr_getttransfersperns(dns_zone_getmgr(zone));
if (peer != NULL) {
(void) dns_peer_gettransfers(peer, &maxtransfersperns);
}
REQUIRE(xfrp != NULL && *xfrp == NULL);
/*
* Determine if we should attempt to sign the request with TSIG.
*/
if (peer != NULL && dns_peer_getkey(peer, &keyname) == ISC_R_SUCCESS) {
dns_view_t *view = dns_zone_getview(zone);
result = dns_tsigkey_find(&key, keyname, NULL,
view->statickeys);
if (result == ISC_R_NOTFOUND)
result = dns_tsigkey_find(&key, keyname, NULL,
view->dynamickeys);
if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
goto failure;
}
(void) dns_zone_getdb(zone, &db);
transferlist = dns_zonemgr_gettransferlist(dns_zone_getmgr(zone));
LOCK(&transferlist->lock);
nxfrsin = nxfrsperns = 0;
for (x = ISC_LIST_HEAD(transferlist->transfers);
x != NULL;
x = ISC_LIST_NEXT(x, link))
{
isc_netaddr_t xip;
isc_netaddr_fromsockaddr(&xip, &x->masteraddr);
nxfrsin++;
if (isc_netaddr_equal(&xip, &masterip))
nxfrsperns++;
}
if (nxfrsin >= maxtransfersin || nxfrsperns >= maxtransfersperns) {
result = ISC_R_QUOTA;
xfrin_log1(ISC_LOG_INFO, zonename, masteraddr,
"deferred: %s", isc_result_totext(result));
goto unlock;
}
result = xfrin_create(mctx,
zone,
db,
......@@ -637,15 +522,11 @@ dns_xfrin_create(dns_zone_t *zone, isc_sockaddr_t *masteraddr,
socketmgr,
zonename,
dns_zone_getclass(zone), xfrtype,
masteraddr, key, &xfr);
masteraddr, tsigkey, &xfr);
if (result != ISC_R_SUCCESS)
goto unlock;
xfr->transferlist = transferlist;
ISC_LIST_APPEND(transferlist->transfers, xfr, link);
unlock:
UNLOCK(&transferlist->lock);
CHECK(result);
CHECK(xfrin_start(xfr));
......@@ -773,9 +654,6 @@ xfrin_create(isc_mem_t *mctx,
xfr->axfr.add_func = NULL;
xfr->axfr.add_private = NULL;
ISC_LINK_INIT(xfr, link);
xfr->transferlist = NULL;
CHECK(dns_name_dup(zonename, mctx, &xfr->name));
isc_interval_set(&maxinterval, dns_zone_getmaxxfrin(xfr->zone), 0);
......@@ -1267,13 +1145,6 @@ maybe_free(dns_xfrin_ctx_t *xfr) {
xfrin_log(xfr, ISC_LOG_INFO, "end of transfer");
if (xfr->transferlist != NULL) {
LOCK(&xfr->transferlist->lock);
ISC_LIST_UNLINK(xfr->transferlist->transfers, xfr, link);
UNLOCK(&xfr->transferlist->lock);
xfr->transferlist = NULL;
}
if (xfr->socket != NULL)
isc_socket_detach(&xfr->socket);
......@@ -1369,14 +1240,3 @@ xfrin_log(dns_xfrin_ctx_t *xfr, unsigned int level, const char *fmt, ...)
xfrin_logv(level, &xfr->name, &xfr->masteraddr, fmt, ap);
va_end(ap);
}
isc_result_t
dns_xfrinlist_init(dns_xfrinlist_t *list) {
ISC_LIST_INIT(list->transfers);
return (isc_mutex_init(&list->lock));
}
void
dns_xfrinlist_destroy(dns_xfrinlist_t *list) {
isc_mutex_destroy(&list->lock);
}
......@@ -15,7 +15,7 @@
* SOFTWARE.
*/
/* $Id: zone.c,v 1.112 2000/05/12 10:21:06 marka Exp $ */
/* $Id: zone.c,v 1.113 2000/05/14 20:01:25 gson Exp $ */
#include <config.h>
......@@ -36,6 +36,7 @@
#include <dns/log.h>
#include <dns/masterdump.h>
#include <dns/message.h>
#include <dns/peer.h>
#include <dns/rcode.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
......@@ -43,6 +44,7 @@
#include <dns/resolver.h>
#include <dns/result.h>
#include <dns/ssu.h>
#include <dns/tsig.h>
#include <dns/xfrin.h>
#include <dns/zone.h>
......@@ -139,6 +141,16 @@ struct dns_zone {
isc_event_t ctlevent;
dns_ssutable_t *ssutable;
dns_view_t *view;
/*
* 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.
*/
ISC_LINK(dns_zone_t) statelink;
dns_zonelist_t *statelist;
};
#define DNS_ZONE_FLAG(z,f) (((z)->flags & (f)) != 0)
......@@ -175,12 +187,13 @@ struct dns_zonemgr {
isc_rwlock_t rwlock;
isc_rwlock_t conflock;
/* Locked by rwlock. */
ISC_LIST(dns_zone_t) zones;
dns_zonelist_t zones;
dns_zonelist_t waiting_for_xfrin;
dns_zonelist_t xfrin_in_progress;
/* Locked by conflock. */
int transfersin;
int transfersperns;
/* Contains its own lock. */
dns_xfrinlist_t transferlist;
};
/*
......@@ -202,7 +215,7 @@ static void cancel_refresh(dns_zone_t *);
static void zone_log(dns_zone_t *zone, const char *, int, const char *msg,
...);
static void dns_zone_transfer_in(dns_zone_t *zone);
static void queue_xfrin(dns_zone_t *zone);
static isc_result_t dns_zone_tostr(dns_zone_t *zone, isc_mem_t *mctx,
char **s);
static void zone_unload(dns_zone_t *zone);
......@@ -231,7 +244,10 @@ static isc_result_t notify_createmessage(dns_zone_t *zone,
static void notify_done(isc_task_t *task, isc_event_t *event);
static void notify_send_toaddr(isc_task_t *task, isc_event_t *event);
static isc_result_t zone_dump(dns_zone_t *);
static void got_transfer_quota(isc_task_t *task, isc_event_t *event);
static isc_result_t zmgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr, dns_zone_t *zone);
static void zmgr_resume_xfrs(dns_zonemgr_t *zmgr);
#define PRINT_ZONE_REF(zone) \
do { \
char *s = NULL; \
......@@ -331,6 +347,9 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
zone->diff_on_reload = ISC_FALSE;
zone->ssutable = NULL;
zone->view = NULL;
ISC_LINK_INIT(zone, statelink);
zone->statelist = NULL;
zone->magic = ZONE_MAGIC;
ISC_EVENT_INIT(&zone->ctlevent, sizeof(zone->ctlevent), 0, NULL,
DNS_EVENT_ZONECONTROL, zone_shutdown, zone, zone,
......@@ -349,7 +368,6 @@ zone_free(dns_zone_t *zone) {
zone->flags |= DNS_ZONE_F_EXITING;
UNLOCK(&zone->lock);
/*
* Managed objects. Order is important.
*/
......@@ -365,8 +383,10 @@ zone_free(dns_zone_t *zone) {
isc_task_detach(&zone->task);
if (zone->zmgr)
dns_zonemgr_releasezone(zone->zmgr, zone);
INSIST(zone->statelist == NULL);
/* unmanaged objects */
/* Unmanaged objects */
if (zone->dbname != NULL)
isc_mem_free(zone->mctx, zone->dbname);
zone->dbname = NULL;
......@@ -1635,9 +1655,9 @@ notify_send_queue(notify_t *notify) {
isc_result_t result;
e = isc_event_allocate(notify->mctx, NULL,
DNS_EVENT_NOTIFYSENDTOADDR,
notify_send_toaddr,
notify, sizeof(isc_event_t));
DNS_EVENT_NOTIFYSENDTOADDR,
notify_send_toaddr,
notify, sizeof(isc_event_t));
if (e == NULL)
return (ISC_R_NOMEMORY);
e->ev_arg = notify;
......@@ -1885,7 +1905,7 @@ dns_zone_notify(dns_zone_t *zone) {
static void
refresh_callback(isc_task_t *task, isc_event_t *event) {
char me[] = "refresh_callback";
const char me[] = "refresh_callback";
dns_requestevent_t *revent = (dns_requestevent_t *)event;
dns_zone_t *zone;
dns_message_t *msg = NULL;
......@@ -2045,7 +2065,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
tcp_transfer:
isc_event_free(&event);
dns_request_destroy(&zone->request);
dns_zone_transfer_in(zone);
queue_xfrin(zone);
} else if (isc_serial_eq(soa.serial, zone->serial)) {
dns_zone_uptodate(zone);
goto next_master;
......@@ -2212,6 +2232,19 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) {
LOCK(&zone->lock);
zone->flags |= DNS_ZONE_F_EXITING;
UNLOCK(&zone->lock);
/*
* If we were waiting for xfrin quota, step out of
* the queue.
*/
if (zone->statelist == &zone->zmgr->waiting_for_xfrin) {
RWLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
ISC_LIST_UNLINK(zone->zmgr->waiting_for_xfrin, zone,
statelink);
RWUNLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
zone->statelist = NULL;
}
if (zone->xfr != NULL)
dns_xfrin_shutdown(zone->xfr);
......@@ -2720,8 +2753,7 @@ dns_zone_getjournalsize(dns_zone_t *zone) {
}
static void
zone_log(dns_zone_t *zone, const char *me, int level,
const char *fmt, ...) {
zone_log(dns_zone_t *zone, const char *me, int level, const char *fmt, ...) {
va_list ap;
char message[4096];
char namebuf[1024+32];
......@@ -2881,7 +2913,7 @@ record_serial() {
static void
notify_done(isc_task_t *task, isc_event_t *event) {
char me[] = "notify_done";
const char me[] = "notify_done";
notify_t *notify;
dns_zone_t *zone = NULL;
......@@ -3112,6 +3144,16 @@ xfrdone(dns_zone_t *zone, isc_result_t result) {
if (zone->xfr != NULL)
dns_xfrin_detach(&zone->xfr);
/*
* This transfer finishing freed up a transfer quota slot.
* Let any zones waiting for quota have it.
*/
RWLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
ISC_LIST_UNLINK(zone->zmgr->xfrin_in_progress, zone, statelink);
zone->statelist = NULL;
zmgr_resume_xfrs(zone->zmgr);
RWUNLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
/*
* Retry with a different server if necessary.
*/
......@@ -3134,28 +3176,152 @@ dns_zone_setssutable(dns_zone_t *zone, dns_ssutable_t *table) {
zone->ssutable = table;
}
/***
*** Zone manager.
***/
static void
dns_zone_transfer_in(dns_zone_t *zone) {
const char me[] = "dns_zone_transfer_in";
queue_xfrin(dns_zone_t *zone) {
const char me[] = "queue_xfrin";
isc_result_t result;
dns_zonemgr_t *zmgr = zone->zmgr;
DNS_ENTER;
if (zone->masterscnt < 1)
return;
INSIST(zone->statelist == NULL);
RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
ISC_LIST_APPEND(zmgr->waiting_for_xfrin, zone, statelink);
zone->statelist = &zmgr->waiting_for_xfrin;
result = zmgr_start_xfrin_ifquota(zmgr, zone);
RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
if (result == ISC_R_QUOTA) {
zone_log(zone, me, ISC_LOG_DEBUG(1),
"zone transfer deferred due to quota");
} else if (result != ISC_R_SUCCESS) {
zone_log(zone, me, ISC_LOG_ERROR,
"starting zone transfer: %s",
isc_result_totext(result));
}
}
/*
* Format a human-readable representation of the socket address '*sa'
* into the character array 'array', which is of size 'size'.
* The resulting string is guaranteed to be null-terminated.
*/
static void
sockaddr_format(isc_sockaddr_t *sa, char *array, unsigned int size)
{
isc_result_t result;
isc_buffer_t buf;
isc_buffer_init(&buf, array, size);
result = isc_sockaddr_totext(sa, &buf);
if (result != ISC_R_SUCCESS) {
snprintf(array, size,
"<unknown address, family %u>",
sa->type.sa.sa_family);
array[size - 1] = '\0';
}
}
/*
* 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
got_transfer_quota(isc_task_t *task, isc_event_t *event) {
const char me[] = "got_transfer_quota";
isc_result_t result;
dns_peer_t *peer = NULL;
dns_tsigkey_t *tsigkey = NULL;
dns_name_t *keyname = NULL;
char mastertext[256];
dns_rdatatype_t xfrtype;
dns_zone_t *zone = event->ev_arg;
isc_netaddr_t masterip;
UNUSED(task);
INSIST(task == zone->task);
if (DNS_ZONE_FLAG(zone, DNS_ZONE_F_EXITING)) {
result = ISC_R_CANCELED;
goto cleanup;
}
sockaddr_format(&zone->masteraddr, mastertext, sizeof(mastertext));
isc_netaddr_fromsockaddr(&masterip, &zone->masteraddr);
(void)dns_peerlist_peerbyaddr(zone->view->peers,
&masterip, &peer);
/*
* Decide whether we should request IXFR or AXFR.
*/
if (zone->db == NULL) {
zone_log(zone, me, ISC_LOG_DEBUG(3),
"no database exists yet, requesting AXFR of "
"initial version from %s", mastertext);
xfrtype = dns_rdatatype_axfr;
} else {
isc_boolean_t use_ixfr = ISC_TRUE;
if (peer != NULL &&
dns_peer_getrequestixfr(peer, &use_ixfr) ==
ISC_R_SUCCESS) {
; /* Using peer setting */
} else {
use_ixfr = zone->view->requestixfr;
}
if (use_ixfr == ISC_FALSE) {
zone_log(zone, me, ISC_LOG_DEBUG(3),
"IXFR disabled, requesting AXFR from %s",
mastertext);
xfrtype = dns_rdatatype_axfr;
} else {
zone_log(zone, me, ISC_LOG_DEBUG(3),
"requesting IXFR form %s",
mastertext);
xfrtype = dns_rdatatype_ixfr;
}
}
result = dns_xfrin_create(zone, &zone->masteraddr, zone->mctx,
/*
* Determine if we should attempt to sign the request with TSIG.
*/
if (peer != NULL && dns_peer_getkey(peer, &keyname) == ISC_R_SUCCESS) {
dns_view_t *view = dns_zone_getview(zone);
result = dns_tsigkey_find(&tsigkey, keyname, NULL,
view->statickeys);
if (result == ISC_R_NOTFOUND)
result = dns_tsigkey_find(&tsigkey, keyname, NULL,
view->dynamickeys);
if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
zone_log(zone, me, ISC_LOG_ERROR,
"error getting tsig keys for zone transfer: %s",
isc_result_totext(result));
goto cleanup;
}
}
result = dns_xfrin_create(zone, xfrtype, &zone->masteraddr,
tsigkey, zone->mctx,
zone->zmgr->timermgr, zone->zmgr->socketmgr,
zone->task,
xfrdone, &zone->xfr);
zone->task, xfrdone, &zone->xfr);
if (result != ISC_R_SUCCESS)
xfrdone(zone, result);
cleanup:
isc_event_free(&event);
dns_zone_detach(&zone);