From 897ba5e12014b2001dba9ef7fceeec7d9182e447 Mon Sep 17 00:00:00 2001 From: Andreas Gustafsson Date: Fri, 28 Jan 2000 23:48:58 +0000 Subject: [PATCH] Abort incoming zone transfers on server shutdown. To support this, zone transfers now use the zone's task, the zone holds a pointer to any zone transfer in progress, and the zone now registers a shutdown callback. --- lib/dns/include/dns/xfrin.h | 48 +++++++++- lib/dns/xfrin.c | 179 ++++++++++++++++++++---------------- lib/dns/zone.c | 59 +++++++++--- 3 files changed, 186 insertions(+), 100 deletions(-) diff --git a/lib/dns/include/dns/xfrin.h b/lib/dns/include/dns/xfrin.h index bae55d7b92..9cbc105449 100644 --- a/lib/dns/include/dns/xfrin.h +++ b/lib/dns/include/dns/xfrin.h @@ -26,15 +26,55 @@ * Incoming zone transfers (AXFR + IXFR). */ +/*** + *** Imports + ***/ + +#include #include +/*** + *** Types + ***/ + +typedef struct dns_xfrin_ctx dns_xfrin_ctx_t; + /*** *** Functions ***/ -void dns_xfrin_start(dns_zone_t *zone, isc_sockaddr_t *master, - isc_mem_t *mctx, isc_taskmgr_t *taskmgr, - isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, - dns_xfrindone_t done); +ISC_LANG_BEGINDECLS + +isc_result_t +dns_xfrin_create(dns_zone_t *zone, isc_sockaddr_t *masteraddr, + 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); +/* + * Attempt to start an incoming zone transfer of 'zone' + * from 'masteraddr', creating a dns_xfrin_ctx_t object to + * manage it. Attach '*xfrp' to the newly created object. + * + * Iff ISC_R_SUCCESS is returned, '*done' is guaranteed to be + * called in the context of 'task', with 'zone' and a result + * code as arguments when the transfer finishes. + */ + +void dns_xfrin_shutdown(dns_xfrin_ctx_t *xfr); +/* + * If the zone transfer 'xfr' has already finished, + * do nothing. Otherwise, abort it and cause it to call + * its done callback with a status of ISC_R_CANCELLED. + */ + +void dns_xfrin_detach(dns_xfrin_ctx_t **xfrp); +/* + * Detach a reference to a zone transfer object. + * + * (Because there is no attach() method, there can currently + * only be one reference). + */ + +ISC_LANG_ENDDECLS #endif /* DNS_XFRIN_H */ diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 7f5829e8c6..ec4ae6db8d 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -15,7 +15,7 @@ * SOFTWARE. */ - /* $Id: xfrin.c,v 1.40 2000/01/28 01:12:01 gson Exp $ */ + /* $Id: xfrin.c,v 1.41 2000/01/28 23:48:57 gson Exp $ */ #include @@ -65,8 +65,6 @@ if (result != DNS_R_SUCCESS) goto failure; \ } while (0) -typedef struct xfrin_ctx xfrin_ctx_t; - /* * The states of the *XFR state machine. We handle both IXFR and AXFR * with a single integrated state machine because they cannot be distinguished @@ -90,10 +88,12 @@ typedef enum { * Incoming zone transfer context. */ -struct xfrin_ctx { +struct dns_xfrin_ctx { isc_mem_t *mctx; dns_zone_t *zone; + int refcount; + isc_task_t *task; isc_timer_t *timer; isc_socketmgr_t *socketmgr; @@ -173,44 +173,43 @@ xfrin_create(isc_mem_t *mctx, isc_task_t *task, isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, - dns_xfrindone_t done, dns_name_t *zonename, dns_rdataclass_t rdclass, dns_rdatatype_t reqtype, isc_sockaddr_t *masteraddr, dns_tsigkey_t *tsigkey, - xfrin_ctx_t **xfrp); + dns_xfrin_ctx_t **xfrp); -static isc_result_t axfr_init(xfrin_ctx_t *xfr); -static isc_result_t axfr_makedb(xfrin_ctx_t *xfr, dns_db_t **dbp); -static isc_result_t axfr_putdata(xfrin_ctx_t *xfr, dns_diffop_t op, +static isc_result_t axfr_init(dns_xfrin_ctx_t *xfr); +static isc_result_t axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp); +static isc_result_t axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata); -static isc_result_t axfr_apply(xfrin_ctx_t *xfr); -static isc_result_t axfr_commit(xfrin_ctx_t *xfr); +static isc_result_t axfr_apply(dns_xfrin_ctx_t *xfr); +static isc_result_t axfr_commit(dns_xfrin_ctx_t *xfr); -static isc_result_t ixfr_init(xfrin_ctx_t *xfr); -static isc_result_t ixfr_apply(xfrin_ctx_t *xfr); -static isc_result_t ixfr_putdata(xfrin_ctx_t *xfr, dns_diffop_t op, +static isc_result_t ixfr_init(dns_xfrin_ctx_t *xfr); +static isc_result_t ixfr_apply(dns_xfrin_ctx_t *xfr); +static isc_result_t ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata); -static isc_result_t ixfr_commit(xfrin_ctx_t *xfr); +static isc_result_t ixfr_commit(dns_xfrin_ctx_t *xfr); -static isc_result_t xfr_rr(xfrin_ctx_t *xfr, dns_name_t *name, +static isc_result_t xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name, isc_uint32_t ttl, dns_rdata_t *rdata); -void xfrin_start(xfrin_ctx_t *xfr); +static isc_result_t xfrin_start(dns_xfrin_ctx_t *xfr); static void xfrin_connect_done(isc_task_t *task, isc_event_t *event); -static isc_result_t xfrin_send_request(xfrin_ctx_t *xfr); +static isc_result_t xfrin_send_request(dns_xfrin_ctx_t *xfr); static void xfrin_send_done(isc_task_t *task, isc_event_t *event); static void xfrin_sendlen_done(isc_task_t *task, isc_event_t *event); static void xfrin_recv_done(isc_task_t *task, isc_event_t *event); static void xfrin_timeout(isc_task_t *task, isc_event_t *event); -static isc_boolean_t maybe_free(xfrin_ctx_t *xfr); +static void maybe_free(dns_xfrin_ctx_t *xfr); -static void xfrin_fail(xfrin_ctx_t *xfr, isc_result_t result, char *msg); +static void xfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, char *msg); static isc_result_t render(dns_message_t *msg, isc_buffer_t *buf); static void @@ -220,7 +219,7 @@ static void xfrin_log1(int level, dns_name_t *zonename, isc_sockaddr_t *masteraddr, const char *fmt, ...); static void -xfrin_log(xfrin_ctx_t *xfr, unsigned int level, const char *fmt, ...); +xfrin_log(dns_xfrin_ctx_t *xfr, unsigned int level, const char *fmt, ...); /**************************************************************************/ @@ -229,7 +228,7 @@ xfrin_log(xfrin_ctx_t *xfr, unsigned int level, const char *fmt, ...); */ static isc_result_t -axfr_init(xfrin_ctx_t *xfr) { +axfr_init(dns_xfrin_ctx_t *xfr) { isc_result_t result; xfr->is_ixfr = ISC_FALSE; @@ -245,7 +244,7 @@ axfr_init(xfrin_ctx_t *xfr) { } static isc_result_t -axfr_makedb(xfrin_ctx_t *xfr, dns_db_t **dbp) { +axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp) { return (dns_db_create(xfr->mctx, /* XXX */ "rbt", /* XXX guess */ &xfr->name, @@ -256,7 +255,7 @@ axfr_makedb(xfrin_ctx_t *xfr, dns_db_t **dbp) { } static isc_result_t -axfr_putdata(xfrin_ctx_t *xfr, dns_diffop_t op, +axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata) { isc_result_t result; @@ -273,7 +272,7 @@ axfr_putdata(xfrin_ctx_t *xfr, dns_diffop_t op, /* Store a set of AXFR RRs in the database. */ static isc_result_t -axfr_apply(xfrin_ctx_t *xfr) { +axfr_apply(dns_xfrin_ctx_t *xfr) { isc_result_t result; CHECK(dns_diff_load(&xfr->diff, xfr->axfr.add_func, xfr->axfr.add_private)); @@ -285,7 +284,7 @@ axfr_apply(xfrin_ctx_t *xfr) { } static isc_result_t -axfr_commit(xfrin_ctx_t *xfr) { +axfr_commit(dns_xfrin_ctx_t *xfr) { isc_result_t result; CHECK(axfr_apply(xfr)); @@ -303,7 +302,7 @@ axfr_commit(xfrin_ctx_t *xfr) { */ static isc_result_t -ixfr_init(xfrin_ctx_t *xfr) { +ixfr_init(dns_xfrin_ctx_t *xfr) { isc_result_t result; xfr->is_ixfr = ISC_TRUE; INSIST(xfr->db != NULL); @@ -316,7 +315,7 @@ ixfr_init(xfrin_ctx_t *xfr) { } static isc_result_t -ixfr_putdata(xfrin_ctx_t *xfr, dns_diffop_t op, +ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata) { isc_result_t result; @@ -333,7 +332,7 @@ ixfr_putdata(xfrin_ctx_t *xfr, dns_diffop_t op, /* Apply a set of IXFR changes to the database. */ static isc_result_t -ixfr_apply(xfrin_ctx_t *xfr) { +ixfr_apply(dns_xfrin_ctx_t *xfr) { isc_result_t result; if (xfr->ver == NULL) { CHECK(dns_db_newversion(xfr->db, &xfr->ver)); @@ -349,7 +348,7 @@ ixfr_apply(xfrin_ctx_t *xfr) { } static isc_result_t -ixfr_commit(xfrin_ctx_t *xfr) { +ixfr_commit(dns_xfrin_ctx_t *xfr) { isc_result_t result; ixfr_apply(xfr); if (xfr->ver != NULL) { @@ -372,7 +371,7 @@ ixfr_commit(xfrin_ctx_t *xfr) { * state. */ static isc_result_t -xfr_rr(xfrin_ctx_t *xfr, +xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name, isc_uint32_t ttl, dns_rdata_t *rdata) { isc_result_t result; @@ -490,19 +489,19 @@ xfr_rr(xfrin_ctx_t *xfr, return (result); } -void -dns_xfrin_start(dns_zone_t *zone, isc_sockaddr_t *masteraddr, - isc_mem_t *mctx, isc_taskmgr_t *taskmgr, - isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, - dns_xfrindone_t done) +isc_result_t +dns_xfrin_create(dns_zone_t *zone, isc_sockaddr_t *masteraddr, + 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; - isc_task_t *task; - xfrin_ctx_t *xfr; + dns_xfrin_ctx_t *xfr; isc_result_t result; dns_db_t *db = NULL; dns_rdatatype_t xfrtype; dns_tsigkey_t *key = NULL; + REQUIRE(xfrp != NULL && *xfrp == NULL); zonename = dns_zone_getorigin(zone); @@ -514,10 +513,6 @@ dns_xfrin_start(dns_zone_t *zone, isc_sockaddr_t *masteraddr, else CHECK(result); - task = NULL; - CHECK(isc_task_create(taskmgr, mctx, 0, &task)); - isc_task_setname(task, "xfrin", zone); - if (db == NULL) { xfrin_log1(ISC_LOG_DEBUG(3), zonename, masteraddr, "no database exists yet, " @@ -535,34 +530,44 @@ dns_xfrin_start(dns_zone_t *zone, isc_sockaddr_t *masteraddr, task, timermgr, socketmgr, - done, zonename, dns_zone_getclass(zone), xfrtype, masteraddr, key, &xfr)); - xfrin_start(xfr); - goto cleanup; + CHECK(xfrin_start(xfr)); + + xfr->done = done; + xfr->refcount++; + *xfrp = xfr; failure: - if (done != NULL) - (done)(zone, result); - - cleanup: if (db != NULL) dns_db_detach(&db); if (result != DNS_R_SUCCESS) xfrin_log1(ISC_LOG_ERROR, zonename, masteraddr, "zone transfer setup failed"); - return; + return (result); +} + +void dns_xfrin_shutdown(dns_xfrin_ctx_t *xfr) { + if (! xfr->shuttingdown) + xfrin_fail(xfr, ISC_R_CANCELED, "shut down"); +} + +void dns_xfrin_detach(dns_xfrin_ctx_t **xfrp) { + dns_xfrin_ctx_t *xfr = *xfrp; + INSIST(xfr->refcount > 0); + xfr->refcount--; + maybe_free(xfr); + *xfrp = NULL; } static void -xfrin_fail(xfrin_ctx_t *xfr, isc_result_t result, char *msg) { +xfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, char *msg) { if (result != DNS_R_UPTODATE) { xfrin_log(xfr, ISC_LOG_ERROR, "%s: %s", msg, isc_result_totext(result)); } - xfr->shuttingdown = ISC_TRUE; if (xfr->connects > 0) { isc_socket_cancel(xfr->socket, xfr->task, ISC_SOCKCANCEL_CONNECT); @@ -572,8 +577,11 @@ xfrin_fail(xfrin_ctx_t *xfr, isc_result_t result, char *msg) { isc_socket_cancel(xfr->socket, xfr->task, ISC_SOCKCANCEL_SEND); } - if (xfr->done != NULL) + xfr->shuttingdown = ISC_TRUE; + if (xfr->done != NULL) { (xfr->done)(xfr->zone, result); + xfr->done = NULL; + } maybe_free(xfr); } @@ -584,15 +592,14 @@ xfrin_create(isc_mem_t *mctx, isc_task_t *task, isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, - dns_xfrindone_t done, dns_name_t *zonename, dns_rdataclass_t rdclass, dns_rdatatype_t reqtype, isc_sockaddr_t *masteraddr, dns_tsigkey_t *tsigkey, - xfrin_ctx_t **xfrp) + dns_xfrin_ctx_t **xfrp) { - xfrin_ctx_t *xfr = NULL; + dns_xfrin_ctx_t *xfr = NULL; isc_result_t result; isc_interval_t interval; @@ -600,12 +607,14 @@ xfrin_create(isc_mem_t *mctx, if (xfr == NULL) return (DNS_R_NOMEMORY); xfr->mctx = mctx; + xfr->refcount = 0; xfr->zone = NULL; dns_zone_attach(zone, &xfr->zone); - xfr->task = task; + xfr->task = NULL; + isc_task_attach(task, &xfr->task); xfr->timer = NULL; xfr->socketmgr = socketmgr; - xfr->done = done; + xfr->done = NULL; xfr->connects = 0; xfr->sends = 0; @@ -672,8 +681,8 @@ xfrin_create(isc_mem_t *mctx, return (result); } -void -xfrin_start(xfrin_ctx_t *xfr) { +isc_result_t +xfrin_start(dns_xfrin_ctx_t *xfr) { isc_result_t result; CHECK(isc_socket_create(xfr->socketmgr, isc_sockaddr_pf(&xfr->masteraddr), @@ -683,9 +692,10 @@ xfrin_start(xfrin_ctx_t *xfr) { CHECK(isc_socket_connect(xfr->socket, &xfr->masteraddr, xfr->task, xfrin_connect_done, xfr)); xfr->connects++; - return; + return (ISC_R_SUCCESS); failure: xfrin_fail(xfr, result, "setting up socket"); + return (result); } /* XXX the resolver could use this, too */ @@ -711,7 +721,7 @@ render(dns_message_t *msg, isc_buffer_t *buf) { static void xfrin_connect_done(isc_task_t *task, isc_event_t *event) { isc_socket_connev_t *cev = (isc_socket_connev_t *) event; - xfrin_ctx_t *xfr = (xfrin_ctx_t *) event->arg; + dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->arg; isc_result_t evresult = cev->result; isc_result_t result; task = task; /* Unused */ @@ -719,8 +729,10 @@ xfrin_connect_done(isc_task_t *task, isc_event_t *event) { isc_event_free(&event); xfr->connects--; - if (maybe_free(xfr)) + if (xfr->shuttingdown) { + maybe_free(xfr); return; + } CHECK(evresult); xfrin_log(xfr, ISC_LOG_DEBUG(3), "connected"); @@ -779,7 +791,7 @@ tuple2msgname(dns_difftuple_t *tuple, dns_message_t *msg, dns_name_t **target) * Build an *XFR request and send its length prefix. */ static isc_result_t -xfrin_send_request(xfrin_ctx_t *xfr) { +xfrin_send_request(dns_xfrin_ctx_t *xfr) { isc_result_t result; isc_region_t region; isc_region_t lregion; @@ -858,7 +870,7 @@ static void xfrin_sendlen_done(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sev = (isc_socketevent_t *) event; - xfrin_ctx_t *xfr = (xfrin_ctx_t *) event->arg; + dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->arg; isc_result_t evresult = sev->result; isc_result_t result; isc_region_t region; @@ -868,8 +880,10 @@ xfrin_sendlen_done(isc_task_t *task, isc_event_t *event) isc_event_free(&event); xfr->sends--; - if (maybe_free(xfr)) + if (xfr->shuttingdown) { + maybe_free(xfr); return; + } xfrin_log(xfr, ISC_LOG_DEBUG(3), "sent request length prefix"); CHECK(evresult); @@ -888,7 +902,7 @@ static void xfrin_send_done(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sev = (isc_socketevent_t *) event; - xfrin_ctx_t *xfr = (xfrin_ctx_t *) event->arg; + dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->arg; isc_result_t result; task = task; /* Unused */ @@ -910,7 +924,7 @@ xfrin_send_done(isc_task_t *task, isc_event_t *event) static void xfrin_recv_done(isc_task_t *task, isc_event_t *ev) { - xfrin_ctx_t *xfr = (xfrin_ctx_t *) ev->arg; + dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) ev->arg; isc_result_t result; dns_message_t *msg = NULL; dns_name_t *name; @@ -923,8 +937,10 @@ xfrin_recv_done(isc_task_t *task, isc_event_t *ev) { isc_event_free(&ev); xfr->recvs--; - if (maybe_free(xfr)) + if (xfr->shuttingdown) { + maybe_free(xfr); return; + } CHECK(tcpmsg->result); @@ -1032,13 +1048,15 @@ xfrin_recv_done(isc_task_t *task, isc_event_t *ev) { /* * Inform the caller we succeeded. */ - if (xfr->done != NULL) + if (xfr->done != NULL) { (xfr->done)(xfr->zone, ISC_R_SUCCESS); + xfr->done = NULL; + } /* * We should have no outstanding events at this * point, thus maybe_free() should succeed. */ - RUNTIME_CHECK(maybe_free(xfr) == ISC_TRUE); + maybe_free(xfr); } else { /* Read the next message. */ CHECK(dns_tcpmsg_readmessage(&xfr->tcpmsg, xfr->task, @@ -1058,18 +1076,19 @@ xfrin_recv_done(isc_task_t *task, isc_event_t *ev) { static void xfrin_timeout(isc_task_t *task, isc_event_t *event) { - xfrin_ctx_t *xfr = (xfrin_ctx_t *) event->arg; + dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->arg; task = task; /* Unused */ isc_event_free(&event); /* This will log "giving up: timeout". */ xfrin_fail(xfr, ISC_R_TIMEDOUT, "giving up"); } -static isc_boolean_t -maybe_free(xfrin_ctx_t *xfr) { - if (! xfr->shuttingdown || xfr->connects != 0 || - xfr->sends != 0 || xfr->recvs != 0) - return (ISC_FALSE); +static void +maybe_free(dns_xfrin_ctx_t *xfr) { + if (! xfr->shuttingdown || xfr->refcount != 0 || + xfr->connects != 0 || xfr->sends != 0 || + xfr->recvs != 0) + return; xfrin_log(xfr, ISC_LOG_INFO, "end of transfer"); @@ -1080,7 +1099,7 @@ maybe_free(xfrin_ctx_t *xfr) { isc_timer_detach(&xfr->timer); if (xfr->task != NULL) - isc_task_destroy(&xfr->task); + isc_task_detach(&xfr->task); if (xfr->lasttsig != NULL) { dns_rdata_freestruct(xfr->lasttsig); @@ -1111,8 +1130,6 @@ maybe_free(xfrin_ctx_t *xfr) { dns_zone_detach(&xfr->zone); isc_mem_put(xfr->mctx, xfr, sizeof(*xfr)); - - return (ISC_TRUE); } /* @@ -1167,7 +1184,7 @@ xfrin_log1(int level, dns_name_t *zonename, isc_sockaddr_t *masteraddr, /* Logging function for use when there is a xfin_ctx_t. */ static void -xfrin_log(xfrin_ctx_t *xfr, unsigned int level, const char *fmt, ...) +xfrin_log(dns_xfrin_ctx_t *xfr, unsigned int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 2d27b31832..223cfeb296 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * SOFTWARE. */ - /* $Id: zone.c,v 1.70 2000/01/28 16:10:47 bwelling Exp $ */ + /* $Id: zone.c,v 1.71 2000/01/28 23:48:58 gson Exp $ */ #include @@ -142,6 +142,7 @@ struct dns_zone { isc_sockaddr_t notifyfrom; isc_task_t * task; isc_sockaddr_t xfrsource; + dns_xfrin_ctx_t * xfr; /* Access Control Lists */ dns_acl_t *update_acl; dns_acl_t *query_acl; @@ -209,11 +210,11 @@ static isc_result_t default_journal(dns_zone_t *zone); static void releasezone(dns_zonemgr_t *zmgr, dns_zone_t *zone); static void xfrin_start_temporary_kludge(dns_zone_t *zone); static void xfrdone(dns_zone_t *zone, isc_result_t result); +static void zone_shutdown(isc_task_t *, isc_event_t *); #ifdef notyet static void refresh_callback(isc_task_t *, isc_event_t *); static void soa_query(dns_zone_t *, isc_taskaction_t); static void checkservers_callback(isc_task_t *task, isc_event_t *event); -static void zone_shutdown(isc_task_t *, isc_event_t *); static int message_count(dns_message_t *msg, dns_section_t section, dns_rdatatype_t type); static void add_address_tocheck(dns_message_t *msg, @@ -315,6 +316,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->idleout = DNS_DEFAULT_IDLEOUT; ISC_LIST_INIT(zone->checkservers); zone->xfrsource = sockaddr_any; + zone->xfr = NULL; zone->maxxfrin = MAX_XFER_TIME; zone->maxxfrout = MAX_XFER_TIME; zone->diff_on_reload = ISC_FALSE; @@ -2130,15 +2132,16 @@ soa_query(dns_zone_t *zone, isc_taskaction_t callback) { } #endif -#ifdef notyet static void zone_shutdown(isc_task_t *task, isc_event_t *event) { - dns_zone_t *zone = (dns_zone_t *)event->arg; + dns_zone_t *zone = (dns_zone_t *) event->arg; isc_event_free(&event); - task = task; /* XXX */ - zone = zone; /* XXX */ + UNUSED(task); + REQUIRE(DNS_ZONE_VALID(zone)); + zone_log(zone, "zone_shutdown", ISC_LOG_DEBUG(3), "shutting down"); + if (zone->xfr != NULL) + dns_xfrin_shutdown(zone->xfr); } -#endif static void zone_timer(isc_task_t *task, isc_event_t *event) { @@ -2911,6 +2914,7 @@ xfrdone(dns_zone_t *zone, isc_result_t result) { LOCK(&zone->lock); INSIST((zone->flags & DNS_ZONE_F_REFRESH) != 0); zone->flags &= ~DNS_ZONE_F_REFRESH; + switch (result) { case DNS_R_UPTODATE: case DNS_R_SUCCESS: @@ -2934,6 +2938,20 @@ xfrdone(dns_zone_t *zone, isc_result_t result) { break; } UNLOCK(&zone->lock); + + /* + * 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. + */ + if (zone->xfr != NULL) + dns_xfrin_detach(&zone->xfr); + + /* + * Retry with a different server if necessary. + */ if (again) xfrin_start_temporary_kludge(zone); } @@ -2944,8 +2962,10 @@ xfrdone(dns_zone_t *zone, isc_result_t result) { static void xfrin_start_temporary_kludge(dns_zone_t *zone) { + isc_result_t result; isc_sockaddr_t sa; in_port_t port; + if (zone->masterscnt < 1) return; port = zone->masterport; @@ -2954,10 +2974,12 @@ xfrin_start_temporary_kludge(dns_zone_t *zone) { isc_sockaddr_fromin(&sa, &zone->masters[zone->curmaster].type.sin.sin_addr, port); - dns_xfrin_start(zone, &sa, zone->mctx, - zone->zmgr->taskmgr, zone->zmgr->timermgr, - zone->zmgr->socketmgr, - xfrdone); + result = dns_xfrin_create(zone, &sa, zone->mctx, + zone->zmgr->timermgr, zone->zmgr->socketmgr, + zone->task, + xfrdone, &zone->xfr); + if (result != DNS_R_SUCCESS) + xfrdone(zone, result); } isc_result_t @@ -3054,18 +3076,25 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { zmgr->task, zone_timer, zone, &zone->timer); if (result != ISC_R_SUCCESS) - goto failure; + goto cleanup_task; + result = isc_task_onshutdown(zone->task, zone_shutdown, zone); + if (result != ISC_R_SUCCESS) + goto cleanup_timer; + zone->zmgr = zmgr; ISC_LIST_APPEND(zmgr->zones, zone, link); - goto cleanup; + goto unlock; - failure: + cleanup_timer: + isc_timer_detach(&zone->timer); + + cleanup_task: if (zone->task != NULL) isc_task_detach(&zone->task); - cleanup: + unlock: UNLOCK(&zone->lock); RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write); return (result);