From e01ecff4b1562a24e6de7e9396c60e9dffdb78ce Mon Sep 17 00:00:00 2001 From: Andreas Gustafsson Date: Fri, 25 Feb 2000 00:52:11 +0000 Subject: [PATCH] Implemented the 'transfers' clause of the 'server' statement > and the 'transfers-per-ns' option. --- bin/named/server.c | 9 +++ doc/misc/options | 4 +- lib/dns/include/dns/types.h | 1 + lib/dns/include/dns/xfrin.h | 13 ++++ lib/dns/include/dns/zone.h | 11 ++++ lib/dns/xfrin.c | 120 ++++++++++++++++++++++++++++++------ lib/dns/zone.c | 39 +++++++++++- 7 files changed, 173 insertions(+), 24 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index c23a69882c..652bb67570 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -802,6 +802,15 @@ load_configuration(const char *filename, ns_server_t *server, configure_server_quota(configctx, dns_c_ctx_getrecursiveclients, &server->recursionquota, 100); + /* + * Configure the zone manager. + */ + { + isc_int32_t transfersperns = 2; + (void) dns_c_ctx_gettransfersperns(configctx, &transfersperns); + dns_zonemgr_settransfersperns(server->zonemgr, transfersperns); + } + /* * Configure the interface manager according to the "listen-on" * statement. diff --git a/doc/misc/options b/doc/misc/options index f7a93978b2..fbf55d0897 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -71,7 +71,7 @@ options { [ transfer-format ( one-answer | many-answers ); ] Yes [ transfers-in number; ] No [ transfers-out number; ] Yes - [ transfers-per-ns number; ] No + [ transfers-per-ns number; ] Yes [ transfer-source ip_addr; ] Yes [ transfer-source-v6 ip_addr; ] Yes* [ maintain-ixfr-base yes_or_no; ] Obsolete+ @@ -104,7 +104,7 @@ controls No server ip_addr { [ bogus yes_or_no; ] No [ support-ixfr yes_or_no; ] Yes - [ transfers number; ] No + [ transfers number; ] Yes [ transfer-format ( one-answer | many-answers ); ] Yes [ keys { key_id [key_id ... ] }; ] No }; diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 10fe1a8b57..f4f8365b55 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -101,6 +101,7 @@ typedef struct dns_keytable dns_keytable_t; typedef struct dns_keynode dns_keynode_t; typedef struct dns_peer dns_peer_t; typedef struct dns_peerlist dns_peerlist_t; +typedef struct dns_xfrinlist dns_xfrinlist_t; typedef enum { dns_labeltype_ordinary = 0, diff --git a/lib/dns/include/dns/xfrin.h b/lib/dns/include/dns/xfrin.h index 99dfea8d89..a2b4e1fb1e 100644 --- a/lib/dns/include/dns/xfrin.h +++ b/lib/dns/include/dns/xfrin.h @@ -31,14 +31,23 @@ ***/ #include +#include + #include /*** *** Types ***/ +/* 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 ***/ @@ -75,6 +84,10 @@ 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 */ diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index a6ef568530..4b5d770969 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -821,6 +821,8 @@ dns_zone_getmctx(dns_zone_t *zone); * Get the memory context of a zone. */ +dns_zonemgr_t *dns_zone_getmgr(dns_zone_t *zone); + isc_result_t dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, @@ -884,6 +886,15 @@ dns_zonemgr_settransfersin(dns_zonemgr_t *zmgr, int value); int dns_zonemgr_getttransfersin(dns_zonemgr_t *zmgr); +void +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 */ diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 51d0456e56..0102aeae6d 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -15,7 +15,7 @@ * SOFTWARE. */ - /* $Id: xfrin.c,v 1.49 2000/02/24 21:43:11 gson Exp $ */ + /* $Id: xfrin.c,v 1.50 2000/02/25 00:50:36 gson Exp $ */ #include @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -160,6 +161,9 @@ struct dns_xfrin_ctx { dns_journal_t *journal; } ixfr; + + ISC_LINK(dns_xfrin_ctx_t) link; + dns_xfrinlist_t *transferlist; }; /**************************************************************************/ @@ -497,17 +501,31 @@ dns_xfrin_create(dns_zone_t *zone, isc_sockaddr_t *masteraddr, dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp) { dns_name_t *zonename; - dns_xfrin_ctx_t *xfr; + dns_xfrin_ctx_t *xfr, *x; isc_result_t result; dns_db_t *db = NULL; dns_rdatatype_t xfrtype; dns_tsigkey_t *key = NULL; + isc_netaddr_t masterip; + dns_peer_t *peer = NULL; + int maxtransfers; + dns_xfrinlist_t *transferlist; + int nxfrs; + 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); @@ -523,15 +541,9 @@ dns_xfrin_create(dns_zone_t *zone, isc_sockaddr_t *masteraddr, "requesting AXFR of initial version"); xfrtype = dns_rdatatype_axfr; } else { - dns_peer_t *peer = NULL; isc_boolean_t use_ixfr = ISC_TRUE; - isc_netaddr_t na; - isc_netaddr_fromsockaddr(&na, masteraddr); - if (dns_peerlist_peerbyaddr(dns_zone_getview(zone)->peers, - &na, &peer) - == ISC_R_SUCCESS && - dns_peer_getsupportixfr(peer, &use_ixfr) - == ISC_R_SUCCESS && + if (peer != NULL && + dns_peer_getsupportixfr(peer, &use_ixfr) == ISC_R_SUCCESS && use_ixfr == ISC_FALSE) { xfrin_log1(ISC_LOG_DEBUG(3), zonename, masteraddr, @@ -544,15 +556,66 @@ dns_xfrin_create(dns_zone_t *zone, isc_sockaddr_t *masteraddr, } } - CHECK(xfrin_create(mctx, - zone, - db, - task, - timermgr, - socketmgr, - zonename, - dns_zone_getclass(zone), xfrtype, - masteraddr, key, &xfr)); + /* + * 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. + */ + maxtransfers = dns_zonemgr_getttransfersperns(dns_zone_getmgr(zone)); + if (peer != NULL) { + (void) dns_peer_gettransfers(peer, &maxtransfers); + } + + transferlist = dns_zonemgr_gettransferlist(dns_zone_getmgr(zone)); + LOCK(&transferlist->lock); + nxfrs = 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); + if (isc_netaddr_equal(&xip, &masterip)) + nxfrs++; + } + + if (nxfrs >= maxtransfers) { + 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, + task, + timermgr, + socketmgr, + zonename, + dns_zone_getclass(zone), xfrtype, + masteraddr, key, &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)); @@ -679,6 +742,9 @@ 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); @@ -1122,6 +1188,13 @@ 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); @@ -1225,3 +1298,12 @@ 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); +} diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 0954de0b44..19be164e4f 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * SOFTWARE. */ - /* $Id: zone.c,v 1.82 2000/02/24 21:40:51 gson Exp $ */ + /* $Id: zone.c,v 1.83 2000/02/25 00:50:38 gson Exp $ */ #include @@ -201,6 +201,8 @@ struct dns_zonemgr { ISC_LIST(dns_zone_t) zones; /* Maximum locked by conflock. */ isc_quota_t transfersin; + int transfersperns; + dns_xfrinlist_t transferlist; }; static isc_result_t zone_settimer(dns_zone_t *, isc_stdtime_t); @@ -1339,7 +1341,12 @@ dns_zone_print(dns_zone_t *zone) { isc_mem_t * dns_zone_getmctx(dns_zone_t *zone) { - return zone->mctx; + return (zone->mctx); +} + +dns_zonemgr_t * +dns_zone_getmgr(dns_zone_t *zone) { + return (zone->zmgr); } static isc_result_t @@ -3148,12 +3155,21 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, result = DNS_R_UNEXPECTED; goto free_conflock; } + zmgr->transfersperns = 2; + result = dns_xfrinlist_init(&zmgr->transferlist); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_transferlist_init() failed: %s", + isc_result_totext(result)); + result = DNS_R_UNEXPECTED; + goto free_transfersin; + } /* Create the zone task pool. */ result = isc_taskpool_create(taskmgr, mctx, 8 /* XXX */, 0, &zmgr->zonetasks); if (result != ISC_R_SUCCESS) - goto free_transfersin; + goto free_transferlist; /* Create a single task for queueing of SOA queries. */ result = isc_task_create(taskmgr, mctx, 1, &zmgr->task); @@ -3164,6 +3180,8 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, *zmgrp = zmgr; return (ISC_R_SUCCESS); + free_transferlist: + dns_xfrinlist_destroy(&zmgr->transferlist); free_taskpool: isc_taskpool_destroy(&zmgr->zonetasks); free_transfersin: @@ -3303,6 +3321,21 @@ dns_zonemgr_getttransfersin(dns_zonemgr_t *zmgr) { return (zmgr->transfersin.max); } +void +dns_zonemgr_settransfersperns(dns_zonemgr_t *zmgr, int value) { + zmgr->transfersperns = value; +} + +int +dns_zonemgr_getttransfersperns(dns_zonemgr_t *zmgr) { + return (zmgr->transfersperns); +} + + +dns_xfrinlist_t * +dns_zonemgr_gettransferlist(dns_zonemgr_t *zmgr) { + return (&zmgr->transferlist); +} #if 0 /* hook for ondestroy notifcation from a database. */