From e3027d397f237ff018174fd17bf377a4a1890c6b Mon Sep 17 00:00:00 2001 From: Bob Halley Date: Thu, 7 Oct 1999 19:41:16 +0000 Subject: [PATCH] resolver checkpoint --- lib/dns/include/dns/events.h | 2 +- lib/dns/include/dns/resolver.h | 41 +- lib/dns/include/dns/view.h | 8 +- lib/dns/resolver.c | 1205 +++++++++++++++++++------------- lib/dns/view.c | 21 +- 5 files changed, 774 insertions(+), 503 deletions(-) diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h index f456ade289..38bbff8d1e 100644 --- a/lib/dns/include/dns/events.h +++ b/lib/dns/include/dns/events.h @@ -27,7 +27,7 @@ ISC_LANG_BEGINDECLS -#define DNS_EVENT_FETCH (ISC_EVENTCLASS_DNS + 0) +#define DNS_EVENT_FETCHCONTROL (ISC_EVENTCLASS_DNS + 0) #define DNS_EVENT_FETCHDONE (ISC_EVENTCLASS_DNS + 1) #define DNS_EVENT_FIND (ISC_EVENTCLASS_DNS + 2) #define DNS_EVENT_FINDDONE (ISC_EVENTCLASS_DNS + 3) diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index 71e839c982..bb4af604e9 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -45,39 +45,39 @@ * Drafts: */ +#include #include #include +#include #include +#include +#include #include ISC_LANG_BEGINDECLS -struct dns_fetch { - unsigned int magic; - dns_resolver_t * res; - void * private; - ISC_LINK(struct dns_fetch) link; -}; - -#define DNS_FETCH_MAGIC 0x46746368U /* Ftch */ -#define DNS_FETCH_VALID(fetch) ((fetch) != NULL && \ - (fetch)->magic == DNS_FETCH_MAGIC) - -typedef struct dns_fetchdoneevent { - ISC_EVENT_COMMON(struct dns_fetchdoneevent); +typedef struct dns_fetchevent { + ISC_EVENT_COMMON(struct dns_fetchevent); dns_result_t result; -} dns_fetchdoneevent_t; + dns_rdatatype_t qtype; + dns_db_t * db; + dns_dbnode_t * node; + dns_rdataset_t * rdataset; + dns_rdataset_t * sigrdataset; + dns_fixedname_t foundname; +} dns_fetchevent_t; #define DNS_FETCHOPT_TCP 0x01 #define DNS_FETCHOPT_UNSHARED 0x02 #define DNS_FETCHOPT_RECURSIVE 0x04 dns_result_t -dns_resolver_create(isc_mem_t *mctx, dns_view_t *view, +dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr, unsigned int ntasks, - isc_timermgr_t *timermgr, dns_dispatch_t *dispatch, - dns_resolver_t **resp); + isc_socketmgr_t *socketmgr, + isc_timermgr_t *timermgr, + dns_dispatch_t *dispatch, dns_resolver_t **resp); void dns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp); @@ -92,13 +92,12 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, dns_forwarders_t *forwarders, unsigned int options, isc_task_t *task, isc_taskaction_t action, void *arg, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset, dns_fetch_t **fetchp); void -dns_resolver_destroyfetch(dns_fetch_t **fetchp, isc_task_t *task); - -void -dns_resolver_getanswer(isc_event_t *event, dns_message_t **msgp); +dns_resolver_destroyfetch(dns_resolver_t *res, dns_fetch_t **fetchp); ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 818c5a4986..e51562ff10 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -156,8 +156,10 @@ dns_view_detach(dns_view_t **viewp); */ isc_result_t -dns_view_createresolver(dns_view_t *view, isc_taskmgr_t *taskmgr, - unsigned int ntasks, isc_timermgr_t *timermgr, +dns_view_createresolver(dns_view_t *view, + isc_taskmgr_t *taskmgr, unsigned int ntasks, + isc_socketmgr_t *socketmgr, + isc_timermgr_t *timermgr, dns_dispatch_t *dispatch); /* * Create a resolver for the view. @@ -169,7 +171,7 @@ dns_view_createresolver(dns_view_t *view, isc_taskmgr_t *taskmgr, * 'view' does not have a resolver already. * * The requirements of dns_resolver_create() apply to 'taskmgr', - * 'ntasks', 'timermgr', and 'dispatch'. + * 'ntasks', 'socketmgr', 'timermgr', and 'dispatch'. * * Returns: * diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 79ea16c552..476f3677ee 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -46,8 +47,8 @@ #define RTRACE(m) printf("res %p: %s\n", res, (m)) #define RRTRACE(r, m) printf("res %p: %s\n", (r), (m)) #define FCTXTRACE(m) printf("fctx %p: %s\n", fctx, (m)) -#define FTRACE(m) printf("fetch %p (res %p fctx %p): %s\n", \ - fetch, fetch->res, fetch->private, (m)) +#define FTRACE(m) printf("fetch %p (fctx %p): %s\n", \ + fetch, fetch->private, (m)) #define QTRACE(m) printf("query %p (res %p fctx %p): %s\n", \ query, query->fctx->res, query->fctx, (m)) #else @@ -82,57 +83,75 @@ typedef struct query { typedef enum { fetchstate_init = 0, fetchstate_active, - fetchstate_exiting, fetchstate_done } fetchstate; struct fetchctx { /* Not locked. */ unsigned int magic; - fetchstate state; dns_resolver_t * res; - unsigned int references; - unsigned int locknum; dns_name_t name; - dns_rdatatype_t type; /* multiple types??? */ + dns_rdatatype_t type; unsigned int options; isc_task_t * task; + unsigned int bucketnum; /* Locked by lock. */ + fetchstate state; + isc_boolean_t exiting; + unsigned int references; + isc_event_t control_event; + ISC_LINK(struct fetchctx) link; + ISC_LIST(dns_fetchevent_t) events; + /* Only changable by event actions running in the context's task */ dns_name_t domain; dns_rdataset_t nameservers; isc_timer_t * timer; isc_time_t expires; isc_interval_t interval; - ISC_LIST(dns_fetchdoneevent_t) events; - isc_event_t start_event; dns_message_t * qmessage; dns_message_t * rmessage; ISC_LIST(resquery_t) queries; ISC_LIST(isc_sockaddr_t) addresses; isc_sockaddr_t * address; - ISC_LINK(struct fetchctx) link; }; #define FCTX_MAGIC 0x46212121U /* F!!! */ #define VALID_FCTX(fctx) ((fctx) != NULL && \ (fctx)->magic == FCTX_MAGIC) +struct dns_fetch { + unsigned int magic; + void * private; +}; + +#define DNS_FETCH_MAGIC 0x46746368U /* Ftch */ +#define DNS_FETCH_VALID(fetch) ((fetch) != NULL && \ + (fetch)->magic == DNS_FETCH_MAGIC) + +typedef struct fctxbucket { + isc_task_t * task; + isc_mutex_t lock; + ISC_LIST(fetchctx_t) fctxs; +} fctxbucket_t; + struct dns_resolver { /* Unlocked */ unsigned int magic; isc_mem_t * mctx; isc_mutex_t lock; dns_rdataclass_t rdclass; + isc_socketmgr_t * socketmgr; isc_timermgr_t * timermgr; dns_view_t * view; /* Locked by lock. */ unsigned int references; isc_boolean_t exiting; - dns_dispatch_t * dispatch; - unsigned int ntasks; - unsigned int next_task; - isc_task_t ** tasks; - ISC_LIST(fetchctx_t) fctxs; + isc_socket_t * udpsocket4; + isc_socket_t * udpsocket6; + dns_dispatch_t * dispatch4; + dns_dispatch_t * dispatch6; + unsigned int nbuckets; + fctxbucket_t * buckets; }; #define RES_MAGIC 0x52657321U /* Res! */ @@ -147,26 +166,16 @@ static void query_response(isc_task_t *task, isc_event_t *event); * Internal fetch routines. Caller must be holding the proper lock. */ -static inline dns_result_t +static inline isc_result_t fctx_starttimer(fetchctx_t *fctx) { - isc_result_t iresult; - - iresult = isc_timer_reset(fctx->timer, isc_timertype_once, - &fctx->expires, &fctx->interval, - ISC_FALSE); - if (iresult != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_timer_reset(): %s", - isc_result_totext(iresult)); - return (DNS_R_UNEXPECTED); - } - - return (DNS_R_SUCCESS); + return (isc_timer_reset(fctx->timer, isc_timertype_once, + &fctx->expires, &fctx->interval, + ISC_FALSE)); } static inline void fctx_stoptimer(fetchctx_t *fctx) { - isc_result_t iresult; + isc_result_t result; /* * We don't return a result if resetting the timer to inactive fails @@ -174,217 +183,15 @@ fctx_stoptimer(fetchctx_t *fctx) { * should never fail anyway, since the code as currently written * cannot fail in that case. */ - iresult = isc_timer_reset(fctx->timer, isc_timertype_inactive, + result = isc_timer_reset(fctx->timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE); - if (iresult != ISC_R_SUCCESS) { + if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_timer_reset(): %s", - isc_result_totext(iresult)); + isc_result_totext(result)); } } -static void -fctx_done(fetchctx_t *fctx, dns_result_t result) { - dns_fetchdoneevent_t *event, *next_event; - isc_task_t *task; - - /* - * The caller must be holding the proper lock. - */ - - FCTXTRACE("done"); - - fctx_stoptimer(fctx); - - fctx->state = fetchstate_done; - - for (event = ISC_LIST_HEAD(fctx->events); - event != NULL; - event = next_event) { - next_event = ISC_LIST_NEXT(event, link); - task = event->sender; - event->sender = fctx; - event->result = result; - isc_task_sendanddetach(&task, (isc_event_t **)&event); - } - ISC_LIST_INIT(fctx->events); -} - -static void -query_senddone(isc_task_t *task, isc_event_t *event) { - isc_socketevent_t *sevent = (isc_socketevent_t *)event; - resquery_t *query = event->arg; - - REQUIRE(event->type == ISC_SOCKEVENT_SENDDONE); - - (void)task; - - QTRACE("senddone"); - printf("query %p: sendto returned %s\n", query, - isc_result_totext(sevent->result)); - - isc_event_free(&event); -} - -static dns_result_t -fctx_sendquery(fetchctx_t *fctx) { - resquery_t *query; - dns_result_t result; - dns_rdataset_t qrdataset; - isc_sockaddr_t *address = NULL; - isc_sockaddr_t foo; - isc_region_t r; - struct in_addr ina; - - FCTXTRACE("sendquery"); - - RUNTIME_CHECK(inet_aton("127.0.0.1", &ina) != 0); - isc_sockaddr_fromin(&foo, &ina, 53); - address = &foo; - - result = fctx_starttimer(fctx); - if (result != DNS_R_SUCCESS) - return (result); - - dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE); - - query = isc_mem_get(fctx->res->mctx, sizeof *query); - if (query == NULL) - return (DNS_R_NOMEMORY); - isc_buffer_init(&query->buffer, query->data, sizeof query->data, - ISC_BUFFERTYPE_BINARY); - - /* - * If this is a TCP query, then we need to make a socket and - * a dispatch for it here. Otherwise we use the resolver's - * shared dispatch. We do not attach to the resolver's shared - * dispatch if we use it, so the resolver MUST ensure that no - * fetches are running before chaning the shared dispatch. - */ - if ((fctx->options & DNS_FETCHOPT_TCP) != 0) { - /* XXXRTH */ - result = DNS_R_NOTIMPLEMENTED; - goto cleanup_query; - } else - query->dispatch = fctx->res->dispatch; - - /* - * Get a query id from the dispatch. - */ - query->dispentry = NULL; - result = dns_dispatch_addresponse(query->dispatch, - address, - fctx->task, - query_response, - query, - &query->id, - &query->dispentry); - if (result != DNS_R_SUCCESS) - goto cleanup_query; - query->fctx = fctx; - query->tsig = NULL; - query->tsigkey = NULL; - query->magic = QUERY_MAGIC; - - fctx->qmessage->opcode = dns_opcode_query; - - /* - * Set up question. - */ - dns_rdataset_init(&qrdataset); - dns_rdataset_makequestion(&qrdataset, fctx->res->rdclass, fctx->type); - ISC_LIST_INIT(fctx->name.list); - ISC_LIST_APPEND(fctx->name.list, &qrdataset, link); - dns_message_addname(fctx->qmessage, &fctx->name, DNS_SECTION_QUESTION); - if ((fctx->options & DNS_FETCHOPT_RECURSIVE) != 0) - fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD; - /* - * We don't have to set opcode because it defaults to query. - */ - fctx->qmessage->id = query->id; - /* - * XXXRTH Add TSIG and/or ENDS0 OPT record tailored to the current - * recipient. - */ - - /* - * Convert the question to wire format. - */ - result = dns_message_renderbegin(fctx->qmessage, &query->buffer); - if (result != DNS_R_SUCCESS) - goto cleanup_message; - result = dns_message_rendersection(fctx->qmessage, - DNS_SECTION_QUESTION, 0, 0); - if (result != DNS_R_SUCCESS) - goto cleanup_message; - result = dns_message_rendersection(fctx->qmessage, - DNS_SECTION_ADDITIONAL, 0, 0); - if (result != DNS_R_SUCCESS) - goto cleanup_message; - result = dns_message_renderend(fctx->qmessage); - if (result != DNS_R_SUCCESS) - goto cleanup_message; - - if (fctx->qmessage->tsigkey != NULL) { - query->tsigkey = fctx->qmessage->tsigkey; - query->tsig = fctx->qmessage->tsig; - fctx->qmessage->tsig = NULL; - } - - /* - * We're now done with the query message. - * - * It's imperative that we reset the message before we return, - * because the rdataset used for the question is on our stack, - * and won't be valid after we return. - */ - dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); - - /* - * Send the query! - */ - isc_buffer_used(&query->buffer, &r); - result = isc_socket_sendto(dns_dispatch_getsocket(query->dispatch), - &r, fctx->task, query_senddone, - query, address); - if (result != ISC_R_SUCCESS) - goto cleanup_message; - - /* - * Finally, we've got everything going! - */ - ISC_LIST_APPEND(fctx->queries, query, link); - - QTRACE("sent"); - - return (DNS_R_SUCCESS); - - cleanup_message: - /* - * It's imperative that we reset the message here, because - * the rdataset used for the question is on our stack, and won't - * be valid after we return. - */ - dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); - - /* - * Stop the dispatch from listening. - */ - dns_dispatch_removeresponse(query->dispatch, - &query->dispentry, - NULL); - - /* - * XXXRTH will need to cleanup a nonshared dispatch and TCP socket - * here. - */ - - cleanup_query: - query->magic = 0; - isc_mem_put(fctx->res->mctx, query, sizeof *query); - - return (result); -} static inline void fctx_cancelquery(resquery_t *query, dns_dispatchevent_t **deventp) { @@ -392,6 +199,13 @@ fctx_cancelquery(resquery_t *query, dns_dispatchevent_t **deventp) { FCTXTRACE("cancelquery"); + /* + * XXXRTH I don't think that dns_dispatch_removeresponse() will + * reclaim events posted to this task. What do we do + * about this? Doing what we're doing now is bad, because + * we're destroying the query while there may be outstanding + * references to it. + */ dns_dispatch_removeresponse(query->dispatch, &query->dispentry, deventp); ISC_LIST_UNLINK(fctx->queries, query, link); @@ -416,17 +230,336 @@ fctx_cancelqueries(fetchctx_t *fctx) { } static void +fctx_done(fetchctx_t *fctx, isc_result_t result) { + dns_fetchevent_t *event, *next_event; + isc_task_t *task; + dns_resolver_t *res; + + FCTXTRACE("done"); + + res = fctx->res; + + /* XXXRTH Free our scaffolding addresses. */ + { + isc_sockaddr_t *address, *next_address; + + for (address = ISC_LIST_HEAD(fctx->addresses); + address != NULL; + address = next_address) { + next_address = ISC_LIST_NEXT(address, link); + isc_mem_put(fctx->res->mctx, address, sizeof *address); + } + } + + fctx_cancelqueries(fctx); + fctx_stoptimer(fctx); + + LOCK(&res->buckets[fctx->bucketnum].lock); + + fctx->state = fetchstate_done; + + for (event = ISC_LIST_HEAD(fctx->events); + event != NULL; + event = next_event) { + next_event = ISC_LIST_NEXT(event, link); + task = event->sender; + event->sender = fctx; + event->result = result; + isc_task_sendanddetach(&task, (isc_event_t **)&event); + } + ISC_LIST_INIT(fctx->events); + + /* + * XXXRTH check for finished state. + */ + + UNLOCK(&res->buckets[fctx->bucketnum].lock); +} + +static void +query_senddone(isc_task_t *task, isc_event_t *event) { + isc_socketevent_t *sevent = (isc_socketevent_t *)event; + resquery_t *query = event->arg; + + REQUIRE(event->type == ISC_SOCKEVENT_SENDDONE); + + /* + * XXXRTH + * + * Currently we don't wait for the senddone event before retrying + * a query. This means that if we get really behind, we may end + * up doing extra work! + */ + + (void)task; + + QTRACE("senddone"); + printf("query %p: sendto returned %s\n", query, + isc_result_totext(sevent->result)); + + if (sevent->result != ISC_R_SUCCESS) + fctx_cancelquery(query, NULL); + + isc_event_free(&event); +} + +static isc_result_t +fctx_sendquery(fetchctx_t *fctx, isc_sockaddr_t *address) { + resquery_t *query; + isc_result_t result; + dns_rdataset_t *qrdataset; + dns_name_t *qname; + isc_region_t r; + dns_resolver_t *res; + isc_task_t *task; + + FCTXTRACE("sendquery"); + + res = fctx->res; + task = res->buckets[fctx->bucketnum].task; + + result = fctx_starttimer(fctx); + if (result != ISC_R_SUCCESS) + return (result); + + dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE); + + qname = NULL; + result = dns_message_gettempname(fctx->qmessage, &qname); + if (result != ISC_R_SUCCESS) + goto cleanup_temps; + qrdataset = NULL; + result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset); + if (result != ISC_R_SUCCESS) + goto cleanup_temps; + + query = isc_mem_get(res->mctx, sizeof *query); + if (query == NULL) + return (ISC_R_NOMEMORY); + isc_buffer_init(&query->buffer, query->data, sizeof query->data, + ISC_BUFFERTYPE_BINARY); + + /* + * If this is a TCP query, then we need to make a socket and + * a dispatch for it here. Otherwise we use the resolver's + * shared dispatch. We do not attach to the resolver's shared + * dispatch if we use it, so the resolver MUST ensure that no + * fetches are running before changing the shared dispatch. + */ + if ((fctx->options & DNS_FETCHOPT_TCP) != 0) { + /* XXXRTH */ + result = DNS_R_NOTIMPLEMENTED; + goto cleanup_query; + } else { + switch (isc_sockaddr_pf(address)) { + case AF_INET: + query->dispatch = res->dispatch4; + break; + case AF_INET6: + query->dispatch = res->dispatch6; + break; + default: + result = DNS_R_NOTIMPLEMENTED; + goto cleanup_query; + } + /* + * We should always have a valid dispatcher here. If we + * don't support a protocol family, then its dispatcher + * will be NULL, but we shouldn't be finding addresses for + * protocol types we don't support, so the dispatcher + * we found should never be NULL. + */ + INSIST(query->dispatch != NULL); + } + + /* + * Get a query id from the dispatch. + */ + query->dispentry = NULL; + result = dns_dispatch_addresponse(query->dispatch, + address, + task, + query_response, + query, + &query->id, + &query->dispentry); + if (result != ISC_R_SUCCESS) + goto cleanup_query; + query->fctx = fctx; + query->tsig = NULL; + query->tsigkey = NULL; + query->magic = QUERY_MAGIC; + + fctx->qmessage->opcode = dns_opcode_query; + + /* + * Set up question. + */ + dns_name_init(qname, NULL); + dns_name_clone(&fctx->name, qname); + dns_rdataset_init(qrdataset); + dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type); + ISC_LIST_APPEND(qname->list, qrdataset, link); + dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION); + if ((fctx->options & DNS_FETCHOPT_RECURSIVE) != 0) + fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD; + /* + * We don't have to set opcode because it defaults to query. + */ + fctx->qmessage->id = query->id; + /* + * XXXRTH Add TSIG and/or ENDS0 OPT record tailored to the current + * recipient. + */ + + /* + * Convert the question to wire format. + */ + result = dns_message_renderbegin(fctx->qmessage, &query->buffer); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + result = dns_message_rendersection(fctx->qmessage, + DNS_SECTION_QUESTION, 0, 0); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + result = dns_message_rendersection(fctx->qmessage, + DNS_SECTION_ADDITIONAL, 0, 0); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + result = dns_message_renderend(fctx->qmessage); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + + if (fctx->qmessage->tsigkey != NULL) { + query->tsigkey = fctx->qmessage->tsigkey; + query->tsig = fctx->qmessage->tsig; + fctx->qmessage->tsig = NULL; + } + + /* + * We're now done with the query message. + */ + dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); + + /* + * Send the query! + */ + isc_buffer_used(&query->buffer, &r); + result = isc_socket_sendto(dns_dispatch_getsocket(query->dispatch), + &r, task, query_senddone, + query, address); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + + /* + * Finally, we've got everything going! + */ + ISC_LIST_APPEND(fctx->queries, query, link); + + QTRACE("sent"); + + return (ISC_R_SUCCESS); + + cleanup_message: + dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); + + /* + * Stop the dispatcher from listening. + */ + dns_dispatch_removeresponse(query->dispatch, + &query->dispentry, + NULL); + + /* + * XXXRTH will need to cleanup a nonshared dispatch and TCP socket + * here. + */ + + cleanup_query: + query->magic = 0; + isc_mem_put(res->mctx, query, sizeof *query); + + cleanup_temps: + if (qname != NULL) + dns_message_puttempname(fctx->qmessage, &qname); + if (qrdataset != NULL) + dns_message_puttemprdataset(fctx->qmessage, &qrdataset); + + fctx_stoptimer(fctx); + + return (result); +} + +static isc_result_t fctx_getaddresses(fetchctx_t *fctx) { + isc_boolean_t use_hints; + dns_rdata_t rdata; + isc_region_t r; + dns_name_t name; + dns_rdataset_t rdataset; + isc_sockaddr_t *address; + struct in_addr ina; + isc_result_t result; FCTXTRACE("getaddresses"); + /* + * XXXRTH We don't try to handle forwarding yet. + */ + + /* + * XXXRTH This code is a temporary hack until we have working + * address code. + */ + ISC_LIST_INIT(fctx->addresses); fctx->address = NULL; + + use_hints = dns_name_equal(&fctx->domain, dns_rootname); + + INSIST(fctx->nameservers.type == dns_rdatatype_ns); + result = dns_rdataset_first(&fctx->nameservers); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(&fctx->nameservers, &rdata); + dns_rdata_toregion(&rdata, &r); + dns_name_init(&name, NULL); + dns_name_fromregion(&name, &r); + dns_rdataset_init(&rdataset); + result = dns_view_find(fctx->res->view, &name, + dns_rdatatype_a, 0, DNS_DBFIND_GLUEOK, + use_hints, &rdataset, NULL); + if (result == ISC_R_SUCCESS || + result == DNS_R_GLUE || + result == DNS_R_HINT) { + result = dns_rdataset_first(&rdataset); + while (result == ISC_R_SUCCESS) { + address = isc_mem_get(fctx->res->mctx, + sizeof *address); + if (address == NULL) { + result = ISC_R_NOMEMORY; + break; + } + dns_rdataset_current(&rdataset, &rdata); + INSIST(rdata.length == 4); + memcpy(&ina.s_addr, rdata.data, 4); + isc_sockaddr_fromin(address, &ina, 53); + ISC_LIST_APPEND(fctx->addresses, address, + link); + result = dns_rdataset_next(&rdataset); + } + } + dns_rdataset_disassociate(&rdataset); + result = dns_rdataset_next(&fctx->nameservers); + } + if (result == DNS_R_NOMORE) + result = ISC_R_SUCCESS; + + return (result); } static void fctx_try(fetchctx_t *fctx) { - dns_result_t result; + isc_result_t result; /* * Caller must be holding the fetch's lock. @@ -440,12 +573,18 @@ fctx_try(fetchctx_t *fctx) { fctx->address = ISC_LIST_NEXT(fctx->address, link); if (fctx->address == NULL) { - fctx_getaddresses(fctx); + result = fctx_getaddresses(fctx); + if (result != ISC_R_SUCCESS) { + fctx_done(fctx, result); + return; + } fctx->address = ISC_LIST_HEAD(fctx->addresses); } if (fctx->address == NULL) { - /* XXXRTH No addresses are available... */ + /* + * XXXRTH No addresses are available... + */ INSIST(0); } @@ -455,13 +594,18 @@ fctx_try(fetchctx_t *fctx) { * just send a single query. */ - result = fctx_sendquery(fctx); - if (result != DNS_R_SUCCESS) + result = fctx_sendquery(fctx, fctx->address); + if (result != ISC_R_SUCCESS) fctx_done(fctx, result); } static void fctx_destroy(fetchctx_t *fctx) { + + /* + * Caller must be holding the bucket lock. + */ + REQUIRE(VALID_FCTX(fctx)); REQUIRE(fctx->state == fetchstate_done); REQUIRE(ISC_LIST_EMPTY(fctx->events)); @@ -469,6 +613,9 @@ fctx_destroy(fetchctx_t *fctx) { FCTXTRACE("destroy"); + ISC_LIST_UNLINK(fctx->res->buckets[fctx->bucketnum].fctxs, fctx, + link); + isc_timer_detach(&fctx->timer); dns_message_destroy(&fctx->rmessage); dns_message_destroy(&fctx->qmessage); @@ -514,57 +661,101 @@ fctx_timeout(isc_task_t *task, isc_event_t *event) { } static void -fctx_start(isc_task_t *task, isc_event_t *event) { +fctx_shutdown(isc_task_t *task, isc_event_t *event) { fetchctx_t *fctx = event->arg; - isc_boolean_t need_fctx_destroy = ISC_FALSE; - isc_boolean_t need_resolver_destroy = ISC_FALSE; + isc_boolean_t need_done = ISC_FALSE; dns_resolver_t *res; + unsigned int bucketnum; REQUIRE(VALID_FCTX(fctx)); res = fctx->res; + bucketnum = fctx->bucketnum; + (void)task; /* Keep compiler quiet. */ + + FCTXTRACE("shutdown"); + + LOCK(&res->buckets[bucketnum].lock); + + INSIST(fctx->state == fetchstate_active || + fctx->state == fetchstate_done); + INSIST(fctx->exiting); + + if (fctx->state == fetchstate_done) { + if (fctx->references == 0) + fctx_destroy(fctx); + } else + need_done = ISC_TRUE; + + UNLOCK(&res->buckets[bucketnum].lock); + + if (need_done) + fctx_done(fctx, ISC_R_CANCELED); +} + +static void +fctx_start(isc_task_t *task, isc_event_t *event) { + fetchctx_t *fctx = event->arg; + isc_boolean_t done = ISC_FALSE; + dns_resolver_t *res; + unsigned int bucketnum; + + REQUIRE(VALID_FCTX(fctx)); + + res = fctx->res; + bucketnum = fctx->bucketnum; (void)task; /* Keep compiler quiet. */ FCTXTRACE("start"); - LOCK(&res->lock); + LOCK(&res->buckets[bucketnum].lock); - INSIST(fctx->state == fetchstate_init || - fctx->state == fetchstate_exiting); - if (fctx->state == fetchstate_init) { - fctx->state = fetchstate_active; - fctx_try(fctx); - } else { + INSIST(fctx->state == fetchstate_init); + if (fctx->exiting) { /* + * We haven't started this fctx yet, and we've been requested + * to shut it down. + * * The events list should be empty, so we INSIST on it. - * Since the event list is empty, the result code we pass - * to fctx_done doesn't matter. */ INSIST(ISC_LIST_EMPTY(fctx->events)); - fctx_done(fctx, ISC_R_SUCCESS); - need_fctx_destroy = ISC_TRUE; - if (res->exiting && ISC_LIST_EMPTY(res->fctxs)) - need_resolver_destroy = ISC_TRUE; + fctx_destroy(fctx); + done = ISC_TRUE; + } else { + /* + * Normal fctx startup. + */ + fctx->state = fetchstate_active; + /* + * Reset the control event for later use in shutting down + * the fctx. + */ + ISC_EVENT_INIT(event, sizeof *event, 0, NULL, + DNS_EVENT_FETCHCONTROL, fctx_shutdown, fctx, + (void *)fctx_shutdown, NULL, NULL); } - UNLOCK(&res->lock); + UNLOCK(&res->buckets[bucketnum].lock); - if (need_fctx_destroy) - fctx_destroy(fctx); - if (need_resolver_destroy) - destroy(res); + if (!done) { + /* + * All is well. Start working on the fetch. + */ + fctx_try(fctx); + } } /* * Fetch Creation, Joining, and Cancelation. */ -static inline dns_result_t +static inline isc_result_t fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_taskaction_t action, - void *arg, dns_fetch_t *fetch) + void *arg, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, + dns_fetch_t *fetch) { isc_task_t *clone; - dns_fetchdoneevent_t *event; + dns_fetchevent_t *event; FCTXTRACE("join"); @@ -575,55 +766,58 @@ fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_taskaction_t action, */ clone = NULL; isc_task_attach(task, &clone); - event = (dns_fetchdoneevent_t *) + event = (dns_fetchevent_t *) isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE, action, arg, sizeof *event); if (event == NULL) - return (DNS_R_NOMEMORY); - event->result = DNS_R_SUCCESS; + return (ISC_R_NOMEMORY); + event->result = DNS_R_SERVFAIL; + event->qtype = fctx->type; + event->db = NULL; + event->node = NULL; + event->rdataset = rdataset; + event->sigrdataset = sigrdataset; event->tag = fetch; - /* - * XXX other event initialization here. - */ + dns_fixedname_init(&event->foundname); ISC_LIST_APPEND(fctx->events, event, link); fctx->references++; fetch->magic = DNS_FETCH_MAGIC; - fetch->res = fctx->res; fetch->private = fctx; - ISC_LINK_INIT(fetch, link); - return (DNS_R_SUCCESS); + return (ISC_R_SUCCESS); } -static dns_result_t +static isc_result_t fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_name_t *domain, dns_rdataset_t *nameservers, - unsigned int options, - isc_task_t *worker, isc_task_t *task, isc_taskaction_t action, - void *arg, dns_fetch_t *fetch) + unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp) { fetchctx_t *fctx; - dns_result_t result = DNS_R_SUCCESS; + isc_result_t result = ISC_R_SUCCESS; isc_result_t iresult; - isc_event_t *event; isc_interval_t interval; + /* + * Caller must be holding the lock for bucket number 'bucketnum'. + */ + REQUIRE(fctxp != NULL && *fctxp == NULL); + fctx = isc_mem_get(res->mctx, sizeof *fctx); if (fctx == NULL) - return (DNS_R_NOMEMORY); + return (ISC_R_NOMEMORY); FCTXTRACE("create"); dns_name_init(&fctx->name, NULL); result = dns_name_dup(name, res->mctx, &fctx->name); - if (result != DNS_R_SUCCESS) + if (result != ISC_R_SUCCESS) goto cleanup_fetch; dns_name_init(&fctx->domain, NULL); dns_rdataset_init(&fctx->nameservers); if (domain != NULL) { result = dns_name_dup(domain, res->mctx, &fctx->domain); - if (result != DNS_R_SUCCESS) + if (result != ISC_R_SUCCESS) goto cleanup_name; dns_rdataset_clone(nameservers, &fctx->nameservers); } @@ -634,11 +828,11 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, * resolver to ensure that this task doesn't go away while we are * using it. */ - fctx->task = task; fctx->res = res; fctx->references = 0; - fctx->locknum = 0; + fctx->bucketnum = bucketnum; fctx->state = fetchstate_init; + fctx->exiting = ISC_FALSE; ISC_LIST_INIT(fctx->queries); ISC_LIST_INIT(fctx->addresses); fctx->address = NULL; @@ -647,14 +841,14 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, result = dns_message_create(res->mctx, DNS_MESSAGE_INTENTRENDER, &fctx->qmessage); - if (result != DNS_R_SUCCESS) + if (result != ISC_R_SUCCESS) goto cleanup_domain; fctx->rmessage = NULL; result = dns_message_create(res->mctx, DNS_MESSAGE_INTENTPARSE, &fctx->rmessage); - if (result != DNS_R_SUCCESS) + if (result != ISC_R_SUCCESS) goto cleanup_qmessage; /* @@ -664,7 +858,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, iresult = isc_time_nowplusinterval(&fctx->expires, &interval); if (iresult != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_stdtime_get: %s", + "isc_time_nowplusinterval: %s", isc_result_totext(iresult)); result = DNS_R_UNEXPECTED; goto cleanup_rmessage; @@ -683,7 +877,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, fctx->timer = NULL; iresult = isc_timer_create(res->timermgr, isc_timertype_inactive, NULL, NULL, - worker, fctx_timeout, + res->buckets[bucketnum].task, fctx_timeout, fctx, &fctx->timer); if (iresult != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, @@ -694,30 +888,14 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, } ISC_LIST_INIT(fctx->events); - result = fctx_join(fctx, task, action, arg, fetch); - if (result != DNS_R_SUCCESS) - goto cleanup_timer; - ISC_LINK_INIT(fctx, link); fctx->magic = FCTX_MAGIC; - /* - * The fetch is now ready to go. We send the start event to its - * task to get the ball rolling. - * - * XXX we should really send this event from dns_resolver_fetch(), - * after we've unlocked the fetch's lock, otherwise the other task - * could well block on the lock we're about to release. - */ - event = &fctx->start_event; - ISC_EVENT_INIT(event, sizeof *event, 0, NULL, DNS_EVENT_FETCH, - fctx_start, fctx, (void *)fctx_create, NULL, NULL); - isc_task_send(worker, &event); + ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link); - return (DNS_R_SUCCESS); + *fctxp = fctx; - cleanup_timer: - isc_timer_detach(&fctx->timer); + return (ISC_R_SUCCESS); cleanup_rmessage: dns_message_destroy(&fctx->rmessage); @@ -740,6 +918,10 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, return (result); } +/* + * XXXRTH Cleanup + */ +#ifdef obsolete static void fctx_cancel(fetchctx_t *fctx) { isc_result_t iresult; @@ -758,15 +940,15 @@ fctx_cancel(fetchctx_t *fctx) { fctx_done(fctx, DNS_R_CANCELED); } } - +#endif /* * Handle Responses */ -static inline dns_result_t +static inline isc_result_t same_question(fetchctx_t *fctx) { - dns_result_t result; + isc_result_t result; dns_message_t *message = fctx->rmessage; dns_name_t *name; dns_rdataset_t *rdataset; @@ -782,7 +964,7 @@ same_question(fetchctx_t *fctx) { return (DNS_R_FORMERR); result = dns_message_firstname(message, DNS_SECTION_QUESTION); - if (result != DNS_R_SUCCESS) + if (result != ISC_R_SUCCESS) return (result); name = NULL; dns_message_currentname(message, DNS_SECTION_QUESTION, &name); @@ -794,12 +976,12 @@ same_question(fetchctx_t *fctx) { !dns_name_equal(&fctx->name, name)) return (DNS_R_FORMERR); - return (DNS_R_SUCCESS); + return (ISC_R_SUCCESS); } static void query_response(isc_task_t *task, isc_event_t *event) { - dns_result_t result; + isc_result_t result; resquery_t *query = event->arg; dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event; isc_boolean_t bad_sender = ISC_FALSE; @@ -821,7 +1003,7 @@ query_response(isc_task_t *task, isc_event_t *event) { message->querytsig = query->tsig; message->tsigkey = query->tsigkey; result = dns_message_parse(message, &devent->buffer, ISC_FALSE); - if (result != DNS_R_SUCCESS) { + if (result != ISC_R_SUCCESS) { switch (result) { case DNS_R_FORMERR: case DNS_R_UNEXPECTEDEND: @@ -869,18 +1051,43 @@ query_response(isc_task_t *task, isc_event_t *event) { * Is the question the same as the one we asked? */ result = same_question(fctx); - if (result != DNS_R_SUCCESS) { + if (result != ISC_R_SUCCESS) { /* XXXRTH Log */ if (result == DNS_R_FORMERR) bad_sender = ISC_TRUE; goto done; } + /* + * Did we get any answers? + */ + if (message->counts[DNS_SECTION_ANSWER] > 0) { + /* + * We've got answers. + */ + result = DNS_R_NOTIMPLEMENTED; + goto foo; + } else if (message->counts[DNS_SECTION_AUTHORITY] > 0) { + /* + * NXDOMAIN, NXRDATASET, or referral. + */ + result = DNS_R_NOTIMPLEMENTED; + goto foo; + } else { + /* + * The sender is insane. + */ + /* XXXRTH Log */ + bad_sender = ISC_TRUE; + goto done; + } + + foo: query->tsig = NULL; fctx_stoptimer(fctx); fctx_cancelquery(query, &devent); - result = DNS_R_SUCCESS; + result = DNS_R_SERVFAIL; done: /* @@ -920,95 +1127,180 @@ destroy(dns_resolver_t *res) { REQUIRE(res->exiting); REQUIRE(res->references == 0); - REQUIRE(ISC_LIST_EMPTY(res->fctxs)); RTRACE("destroy"); isc_mutex_destroy(&res->lock); - for (i = 0; i < res->ntasks; i++) { - isc_task_shutdown(res->tasks[i]); - isc_task_detach(&res->tasks[i]); + for (i = 0; i < res->nbuckets; i++) { + INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs)); + if (res->udpsocket4 != NULL) + isc_socket_cancel(res->udpsocket4, + res->buckets[i].task, + ISC_SOCKCANCEL_ALL); + if (res->udpsocket6 != NULL) + isc_socket_cancel(res->udpsocket6, + res->buckets[i].task, + ISC_SOCKCANCEL_ALL); + isc_task_shutdown(res->buckets[i].task); + isc_task_detach(&res->buckets[i].task); + isc_mutex_destroy(&res->buckets[i].lock); } - isc_mem_put(res->mctx, res->tasks, - res->ntasks * sizeof (isc_task_t *)); - dns_dispatch_detach(&res->dispatch); + isc_mem_put(res->mctx, res->buckets, + res->nbuckets * sizeof (fctxbucket_t)); + if (res->dispatch4 != NULL) + dns_dispatch_detach(&res->dispatch4); + if (res->udpsocket4 != NULL) + isc_socket_detach(&res->udpsocket4); + if (res->dispatch6 != NULL) + dns_dispatch_detach(&res->dispatch6); + if (res->udpsocket6 != NULL) + isc_socket_detach(&res->udpsocket6); res->magic = 0; isc_mem_put(res->mctx, res, sizeof *res); } -dns_result_t -dns_resolver_create(isc_mem_t *mctx, dns_view_t *view, +isc_result_t +dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr, unsigned int ntasks, - isc_timermgr_t *timermgr, dns_dispatch_t *dispatch, - dns_resolver_t **resp) + isc_socketmgr_t *socketmgr, + isc_timermgr_t *timermgr, + dns_dispatch_t *dispatch, dns_resolver_t **resp) { dns_resolver_t *res; - dns_result_t result = DNS_R_SUCCESS; - isc_result_t iresult; - unsigned int i, tasks_created = 0; + isc_result_t result = ISC_R_SUCCESS; + unsigned int i, buckets_created = 0; REQUIRE(resp != NULL && *resp == NULL); REQUIRE(ntasks > 0); - res = isc_mem_get(mctx, sizeof *res); + res = isc_mem_get(view->mctx, sizeof *res); if (res == NULL) - return (DNS_R_NOMEMORY); + return (ISC_R_NOMEMORY); RTRACE("create"); - res->mctx = mctx; + res->mctx = view->mctx; res->rdclass = view->rdclass; + res->socketmgr = socketmgr; res->timermgr = timermgr; res->view = view; - res->ntasks = ntasks; - res->next_task = 0; - res->dispatch = NULL; - dns_dispatch_attach(dispatch, &res->dispatch); - res->tasks = isc_mem_get(mctx, ntasks * sizeof (isc_task_t *)); - if (res->tasks == NULL) { - result = DNS_R_NOMEMORY; + + res->nbuckets = ntasks; + res->buckets = isc_mem_get(view->mctx, + ntasks * sizeof (fctxbucket_t)); + if (res->buckets == NULL) { + result = ISC_R_NOMEMORY; goto cleanup_res; } for (i = 0; i < ntasks; i++) { - res->tasks[i] = NULL; - iresult = isc_task_create(taskmgr, mctx, 0, &res->tasks[i]); - if (iresult != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_task_create() failed: %s", - isc_result_totext(iresult)); - result = DNS_R_UNEXPECTED; - goto cleanup_tasks; + result = isc_mutex_init(&res->buckets[i].lock); + if (result != ISC_R_SUCCESS) + goto cleanup_buckets; + res->buckets[i].task = NULL; + result = isc_task_create(taskmgr, view->mctx, 0, + &res->buckets[i].task); + if (result != ISC_R_SUCCESS) { + isc_mutex_destroy(&res->buckets[i].lock); + goto cleanup_buckets; } - tasks_created++; + ISC_LIST_INIT(res->buckets[i].fctxs); + buckets_created++; + } + + /* + * IPv4 Dispatcher. + */ + res->dispatch4 = NULL; + res->udpsocket4 = NULL; + if (dispatch != NULL) { + dns_dispatch_attach(dispatch, &res->dispatch4); + } else if (isc_net_probeipv4() == ISC_R_SUCCESS) { + struct in_addr ina; + isc_sockaddr_t sa; + + /* + * Create an IPv4 UDP socket and a dispatcher for it. + */ + result = isc_socket_create(socketmgr, AF_INET, + isc_sockettype_udp, + &res->udpsocket4); + if (result != ISC_R_SUCCESS) + goto cleanup_buckets; + /* + * XXXRTH Temporarily bind() to 5353 to make things + * easier for Bob's firewalls. + */ + ina.s_addr = htonl(INADDR_ANY); + isc_sockaddr_fromin(&sa, &ina, 5353); + result = isc_socket_bind(res->udpsocket4, &sa); + if (result != ISC_R_SUCCESS) + goto cleanup_buckets; + result = dns_dispatch_create(res->mctx, res->udpsocket4, + res->buckets[0].task, 4096, + 50, 50, 14, &res->dispatch4); + if (result != ISC_R_SUCCESS) + goto cleanup_udpsocket4; + } + + /* + * IPv6 Dispatcher. + */ + res->dispatch6 = NULL; + res->udpsocket6 = NULL; + if (isc_net_probeipv6() == ISC_R_SUCCESS) { + /* + * Create an IPv6 UDP socket and a dispatcher for it. + */ + result = isc_socket_create(socketmgr, AF_INET, + isc_sockettype_udp, + &res->udpsocket6); + if (result != ISC_R_SUCCESS) + goto cleanup_dispatch4; + result = dns_dispatch_create(res->mctx, res->udpsocket6, + res->buckets[0].task, 4096, + 50, 50, 14, &res->dispatch6); + if (result != ISC_R_SUCCESS) + goto cleanup_udpsocket6; } res->references = 1; res->exiting = ISC_FALSE; - iresult = isc_mutex_init(&res->lock); - if (iresult != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_mutex_init() failed: %s", - isc_result_totext(iresult)); - result = DNS_R_UNEXPECTED; - goto cleanup_tasks; - } - - ISC_LIST_INIT(res->fctxs); + result = isc_mutex_init(&res->lock); + if (result != ISC_R_SUCCESS) + goto cleanup_dispatch6; res->magic = RES_MAGIC; *resp = res; - return (DNS_R_SUCCESS); + return (ISC_R_SUCCESS); - cleanup_tasks: - for (i = 0; i < tasks_created; i++) { - isc_task_shutdown(res->tasks[i]); - isc_task_detach(&res->tasks[i]); + cleanup_dispatch6: + if (res->dispatch6 != NULL) + dns_dispatch_detach(&res->dispatch6); + + cleanup_udpsocket6: + if (res->udpsocket6 != NULL) + isc_socket_detach(&res->udpsocket6); + + cleanup_dispatch4: + if (res->dispatch4 != NULL) + dns_dispatch_detach(&res->dispatch4); + + cleanup_udpsocket4: + if (res->udpsocket4 != NULL) + isc_socket_detach(&res->udpsocket4); + + cleanup_buckets: + for (i = 0; i < buckets_created; i++) { + (void)isc_mutex_destroy(&res->buckets[i].lock); + isc_task_shutdown(res->buckets[i].task); + isc_task_detach(&res->buckets[i].task); } - isc_mem_put(mctx, res->tasks, res->ntasks * sizeof (isc_task_t *)); + isc_mem_put(view->mctx, res->buckets, + res->nbuckets * sizeof (fctxbucket_t)); cleanup_res: - isc_mem_put(mctx, res, sizeof *res); + isc_mem_put(view->mctx, res, sizeof *res); return (result); } @@ -1033,7 +1325,6 @@ void dns_resolver_detach(dns_resolver_t **resp) { dns_resolver_t *res; isc_boolean_t need_destroy = ISC_FALSE; - fetchctx_t *fctx; REQUIRE(resp != NULL); res = *resp; @@ -1046,12 +1337,10 @@ dns_resolver_detach(dns_resolver_t **resp) { if (res->references == 0) { RTRACE("exiting"); res->exiting = ISC_TRUE; - for (fctx = ISC_LIST_HEAD(res->fctxs); - fctx != NULL; - fctx = ISC_LIST_NEXT(fctx, link)) - fctx_cancel(fctx); - if (ISC_LIST_EMPTY(res->fctxs)) - need_destroy = ISC_TRUE; + /* + * XXXRTH FIXME + */ + INSIST(0); } UNLOCK(&res->lock); @@ -1070,29 +1359,35 @@ fctx_match(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, return (dns_name_equal(&fctx->name, name)); } -dns_result_t +/* + * XXXRTH This routine takes an unconscionable number of arguments! + * + * Maybe caller should allocate an event and pass that in? Something must + * be done! + */ + +isc_result_t dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_name_t *domain, dns_rdataset_t *nameservers, dns_forwarders_t *forwarders, unsigned int options, isc_task_t *task, isc_taskaction_t action, void *arg, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset, dns_fetch_t **fetchp) { dns_fetch_t *fetch; fetchctx_t *fctx = NULL; - dns_result_t result; - isc_task_t *worker; + isc_result_t result; + unsigned int bucketnum; + isc_boolean_t new_fctx = ISC_FALSE; + isc_event_t *event; (void)forwarders; REQUIRE(VALID_RESOLVER(res)); REQUIRE(fetchp != NULL && *fetchp == NULL); - /* - * We require !res->exiting, since if it were exiting, that would - * mean that the reference count was wrong! - */ - REQUIRE(!res->exiting); RTRACE("createfetch"); @@ -1100,24 +1395,19 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, if ((options & DNS_FETCHOPT_TCP) != 0) return (DNS_R_NOTIMPLEMENTED); + /* + * XXXRTH use a mempool? + */ fetch = isc_mem_get(res->mctx, sizeof *fetch); if (fetch == NULL) - return (DNS_R_NOMEMORY); + return (ISC_R_NOMEMORY); - LOCK(&res->lock); + bucketnum = dns_name_hash(name, ISC_FALSE) % res->nbuckets; - /* - * XXXRTH This is for correctness, and doesn't represent the final - * way of assigning tasks, or the final form of the fetch table. - */ - - worker = res->tasks[res->next_task]; - res->next_task++; - if (res->next_task == res->ntasks) - res->next_task = 0; + LOCK(&res->buckets[bucketnum].lock); if ((options & DNS_FETCHOPT_UNSHARED) == 0) { - for (fctx = ISC_LIST_HEAD(res->fctxs); + for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs); fctx != NULL; fctx = ISC_LIST_NEXT(fctx, link)) { if (fctx_match(fctx, name, type, options)) @@ -1126,22 +1416,34 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, } if (fctx == NULL || fctx->state == fetchstate_done) { - result = fctx_create(res, - name, type, domain, nameservers, - options, - worker, - task, action, arg, - fetch); - if (result == DNS_R_SUCCESS) { - fctx = fetch->private; - ISC_LIST_APPEND(res->fctxs, fctx, link); - } - } else - result = fctx_join(fctx, task, action, arg, fetch); + fctx = NULL; + result = fctx_create(res, name, type, domain, nameservers, + options, bucketnum, &fctx); + if (result != ISC_R_SUCCESS) + goto unlock; + new_fctx = ISC_TRUE; + } + result = fctx_join(fctx, task, action, arg, + rdataset, sigrdataset, fetch); + if (new_fctx) { + if (result == ISC_R_SUCCESS) { + /* + * Launch this fctx. + */ + event = &fctx->control_event; + ISC_EVENT_INIT(event, sizeof *event, 0, NULL, + DNS_EVENT_FETCHCONTROL, + fctx_start, fctx, (void *)fctx_create, + NULL, NULL); + isc_task_send(res->buckets[bucketnum].task, &event); + } else + fctx_destroy(fctx); + } - UNLOCK(&res->lock); + unlock: + UNLOCK(&res->buckets[bucketnum].lock); - if (result == DNS_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { FTRACE("created"); *fetchp = fetch; } else @@ -1151,31 +1453,21 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, } void -dns_resolver_destroyfetch(dns_fetch_t **fetchp, isc_task_t *task) { +dns_resolver_destroyfetch(dns_resolver_t *res, dns_fetch_t **fetchp) { dns_fetch_t *fetch; - dns_fetchdoneevent_t *event, *next_event; + dns_fetchevent_t *event, *next_event; + isc_event_t *cevent; fetchctx_t *fctx; - dns_resolver_t *res; - isc_boolean_t need_fctx_destroy = ISC_FALSE; - isc_boolean_t need_resolver_destroy = ISC_FALSE; isc_task_t *etask; - /* - * XXXRTH We could make it so that even if all the clients detach - * from the fetch, the fctx keeps going. Perhaps this should be - * a resolver option? Right now if they all go away the fctx will - * be destroyed too. - */ - REQUIRE(fetchp != NULL); fetch = *fetchp; REQUIRE(DNS_FETCH_VALID(fetch)); - res = fetch->res; fctx = fetch->private; FTRACE("destroyfetch"); - LOCK(&res->lock); + LOCK(&res->buckets[fctx->bucketnum].lock); event = NULL; if (fctx->state != fetchstate_done) { @@ -1184,64 +1476,43 @@ dns_resolver_destroyfetch(dns_fetch_t **fetchp, isc_task_t *task) { event = next_event) { next_event = ISC_LIST_NEXT(event, link); if (event->tag == fetch) { - ISC_LIST_UNLINK(fctx->events, event, link); + ISC_LIST_UNLINK(fctx->events, event, + link); FTRACE("found"); break; } } - } else if (task != NULL) - (void)isc_task_purge(task, fctx, DNS_EVENT_FETCHDONE, fetch); + } + if (event != NULL) { + etask = event->sender; + event->result = ISC_R_CANCELED; + isc_task_sendanddetach(&etask, (isc_event_t **)&event); + } INSIST(fctx->references > 0); fctx->references--; if (fctx->references == 0) { - INSIST(ISC_LIST_EMPTY(fctx->events)); - ISC_LIST_UNLINK(res->fctxs, fctx, link); - if (fctx->state == fetchstate_init) { - /* - * The fctx is still initializing, which means that - * the start event either hasn't been delivered, or - * is being processed right now, but is blocked waiting - * for the lock. - * - * Rather than try to purge the event, we simply - * wait for it to happen, deferring further destruction - * until it has been processed. - */ - fctx->state = fetchstate_exiting; - } else { - if (fctx->state != fetchstate_done) - fctx_cancel(fctx); - need_fctx_destroy = ISC_TRUE; - if (res->exiting && ISC_LIST_EMPTY(res->fctxs)) - need_resolver_destroy = ISC_TRUE; + /* + * No one cares about the result of this fetch anymore. + * Shut it down. + */ + fctx->exiting = ISC_TRUE; + + /* + * Unless we're still initializing (in which case the + * control event is still outstanding), we need to post + * the control event to tell the fetch we want it to + * exit. + */ + if (fctx->state != fetchstate_init) { + cevent = &fctx->control_event; + isc_task_send(res->buckets[fctx->bucketnum].task, + &cevent); } } - UNLOCK(&res->lock); + UNLOCK(&res->buckets[fctx->bucketnum].lock); isc_mem_put(res->mctx, fetch, sizeof *fetch); *fetchp = NULL; - - if (event != NULL) { - etask = event->sender; - isc_task_detach(&etask); - isc_event_free((isc_event_t **)&event); - } - if (need_fctx_destroy) - fctx_destroy(fctx); - if (need_resolver_destroy) - destroy(res); -} - -void -dns_resolver_getanswer(isc_event_t *event, dns_message_t **msgp) { - fetchctx_t *fctx; - - REQUIRE(msgp != NULL && *msgp == NULL); - REQUIRE(event != NULL); - fctx = event->sender; - REQUIRE(VALID_FCTX(fctx)); - - *msgp = fctx->rmessage; } diff --git a/lib/dns/view.c b/lib/dns/view.c index 3847e2f85f..22fcdedd72 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -166,8 +166,10 @@ dns_view_detach(dns_view_t **viewp) { } isc_result_t -dns_view_createresolver(dns_view_t *view, isc_taskmgr_t *taskmgr, - unsigned int ntasks, isc_timermgr_t *timermgr, +dns_view_createresolver(dns_view_t *view, + isc_taskmgr_t *taskmgr, unsigned int ntasks, + isc_socketmgr_t *socketmgr, + isc_timermgr_t *timermgr, dns_dispatch_t *dispatch) { /* @@ -178,12 +180,8 @@ dns_view_createresolver(dns_view_t *view, isc_taskmgr_t *taskmgr, REQUIRE(!view->frozen); REQUIRE(view->resolver == NULL); -#ifdef notyet - return (dns_resolver_create(view, taskmgr, ntasks, timermgr, dispatch, - &view->resolver)); -#else - return (DNS_R_NOTIMPLEMENTED); -#endif + return (dns_resolver_create(view, taskmgr, ntasks, socketmgr, + timermgr, dispatch, &view->resolver)); } void @@ -309,7 +307,7 @@ dns_view_find(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, result == DNS_R_NXGLUE) { if (rdataset->methods != NULL) dns_rdataset_disassociate(rdataset); - if (sigrdataset->methods != NULL) + if (sigrdataset != NULL && sigrdataset->methods != NULL) dns_rdataset_disassociate(sigrdataset); if (is_zone) { if (view->cachedb != NULL) { @@ -351,7 +349,8 @@ dns_view_find(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, version = NULL; dns_rdataset_clone(rdataset, &zrdataset); dns_rdataset_disassociate(rdataset); - if (sigrdataset->methods != NULL) { + if (sigrdataset != NULL && + sigrdataset->methods != NULL) { dns_rdataset_clone(sigrdataset, &zsigrdataset); dns_rdataset_disassociate(sigrdataset); } @@ -368,7 +367,7 @@ dns_view_find(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, if (result == DNS_R_NOTFOUND && use_hints && view->hints != NULL) { if (rdataset->methods != NULL) dns_rdataset_disassociate(rdataset); - if (sigrdataset->methods != NULL) + if (sigrdataset != NULL && sigrdataset->methods != NULL) dns_rdataset_disassociate(sigrdataset); dns_fixedname_init(&foundname); result = dns_db_find(view->hints, name, NULL, type, options,