diff options
Diffstat (limited to 'lib/dns/resolver.c')
-rw-r--r-- | lib/dns/resolver.c | 253 |
1 files changed, 159 insertions, 94 deletions
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 602dcf5f9632..3a02ba798e73 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.218.2.18.4.77.2.3 2008/07/24 05:00:46 jinmei Exp $ */ +/* $Id: resolver.c,v 1.218.2.18.4.85 2008/10/17 22:03:37 jinmei Exp $ */ #include <config.h> @@ -119,6 +119,7 @@ typedef struct query { isc_mem_t * mctx; dns_dispatchmgr_t * dispatchmgr; dns_dispatch_t * dispatch; + isc_boolean_t exclusivesocket; dns_adbaddrinfo_t * addrinfo; isc_socket_t * tcpsocket; isc_time_t start; @@ -301,7 +302,9 @@ struct dns_resolver { unsigned int options; dns_dispatchmgr_t * dispatchmgr; dns_dispatch_t * dispatchv4; + isc_boolean_t exclusivev4; dns_dispatch_t * dispatchv6; + isc_boolean_t exclusivev6; unsigned int nbuckets; fctxbucket_t * buckets; isc_uint32_t lame_ttl; @@ -360,6 +363,8 @@ static isc_result_t ncache_adderesult(dns_message_t *message, isc_result_t *eresultp); static void validated(isc_task_t *task, isc_event_t *event); static void maybe_destroy(fetchctx_t *fctx); +static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, + isc_result_t reason); static isc_result_t valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name, @@ -549,6 +554,7 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, unsigned int factor; dns_adbfind_t *find; dns_adbaddrinfo_t *addrinfo; + isc_socket_t *socket; query = *queryp; fctx = query->fctx; @@ -631,35 +637,48 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, 0, factor); } - if (query->dispentry != NULL) - dns_dispatch_removeresponse(&query->dispentry, deventp); - - ISC_LIST_UNLINK(fctx->queries, query, link); - - if (query->tsig != NULL) - isc_buffer_free(&query->tsig); - - if (query->tsigkey != NULL) - dns_tsigkey_detach(&query->tsigkey); - /* * Check for any outstanding socket events. If they exist, cancel * them and let the event handlers finish the cleanup. The resolver * only needs to worry about managing the connect and send events; * the dispatcher manages the recv events. */ - if (RESQUERY_CONNECTING(query)) + if (RESQUERY_CONNECTING(query)) { /* * Cancel the connect. */ - isc_socket_cancel(query->tcpsocket, NULL, - ISC_SOCKCANCEL_CONNECT); - else if (RESQUERY_SENDING(query)) + if (query->tcpsocket != NULL) { + isc_socket_cancel(query->tcpsocket, NULL, + ISC_SOCKCANCEL_CONNECT); + } else if (query->dispentry != NULL) { + INSIST(query->exclusivesocket); + socket = dns_dispatch_getentrysocket(query->dispentry); + if (socket != NULL) + isc_socket_cancel(socket, NULL, + ISC_SOCKCANCEL_CONNECT); + } + } else if (RESQUERY_SENDING(query)) { /* * Cancel the pending send. */ - isc_socket_cancel(dns_dispatch_getsocket(query->dispatch), - NULL, ISC_SOCKCANCEL_SEND); + if (query->exclusivesocket && query->dispentry != NULL) + socket = dns_dispatch_getentrysocket(query->dispentry); + else + socket = dns_dispatch_getsocket(query->dispatch); + if (socket != NULL) + isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_SEND); + } + + if (query->dispentry != NULL) + dns_dispatch_removeresponse(&query->dispentry, deventp); + + ISC_LIST_UNLINK(fctx->queries, query, link); + + if (query->tsig != NULL) + isc_buffer_free(&query->tsig); + + if (query->tsigkey != NULL) + dns_tsigkey_detach(&query->tsigkey); if (query->dispatch != NULL) dns_dispatch_detach(&query->dispatch); @@ -824,43 +843,25 @@ fctx_done(fetchctx_t *fctx, isc_result_t result) { } static void -resquery_senddone(isc_task_t *task, isc_event_t *event) { +process_sendevent(resquery_t *query, isc_event_t *event) { isc_socketevent_t *sevent = (isc_socketevent_t *)event; - resquery_t *query = event->ev_arg; isc_boolean_t retry = ISC_FALSE; isc_result_t result; fetchctx_t *fctx; - REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE); - - QTRACE("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! - */ - - UNUSED(task); - - INSIST(RESQUERY_SENDING(query)); - - query->sends--; fctx = query->fctx; if (RESQUERY_CANCELED(query)) { - if (query->sends == 0) { + if (query->sends == 0 && query->connects == 0) { /* * This query was canceled while the - * isc_socket_sendto() was in progress. + * isc_socket_sendto/connect() was in progress. */ if (query->tcpsocket != NULL) isc_socket_detach(&query->tcpsocket); resquery_destroy(&query); } - } else + } else { switch (sevent->result) { case ISC_R_SUCCESS: break; @@ -874,6 +875,7 @@ resquery_senddone(isc_task_t *task, isc_event_t *event) { /* * No route to remote. */ + add_bad(fctx, query->addrinfo, sevent->result); fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); retry = ISC_TRUE; break; @@ -882,6 +884,7 @@ resquery_senddone(isc_task_t *task, isc_event_t *event) { fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); break; } + } isc_event_free(&event); @@ -899,6 +902,48 @@ resquery_senddone(isc_task_t *task, isc_event_t *event) { } } +static void +resquery_udpconnected(isc_task_t *task, isc_event_t *event) { + resquery_t *query = event->ev_arg; + + REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); + + QTRACE("udpconnected"); + + UNUSED(task); + + INSIST(RESQUERY_CONNECTING(query)); + + query->connects--; + + process_sendevent(query, event); +} + +static void +resquery_senddone(isc_task_t *task, isc_event_t *event) { + resquery_t *query = event->ev_arg; + + REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE); + + QTRACE("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! + */ + + UNUSED(task); + + INSIST(RESQUERY_SENDING(query)); + + query->sends--; + + process_sendevent(query, event); +} + static inline isc_result_t fctx_addopt(dns_message_t *message, dns_resolver_t *res) { dns_rdataset_t *rdataset; @@ -1029,6 +1074,7 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, */ query->dispatchmgr = res->dispatchmgr; query->dispatch = NULL; + query->exclusivesocket = ISC_FALSE; query->tcpsocket = NULL; if ((query->options & DNS_FETCHOPT_TCP) != 0) { isc_sockaddr_t addr; @@ -1070,50 +1116,21 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, * A dispatch will be created once the connect succeeds. */ } else { - isc_sockaddr_t localaddr; - unsigned int attrs, attrmask; - dns_dispatch_t *disp_base; - - attrs = 0; - attrs |= DNS_DISPATCHATTR_UDP; - attrs |= DNS_DISPATCHATTR_RANDOMPORT; - - attrmask = 0; - attrmask |= DNS_DISPATCHATTR_UDP; - attrmask |= DNS_DISPATCHATTR_TCP; - attrmask |= DNS_DISPATCHATTR_IPV4; - attrmask |= DNS_DISPATCHATTR_IPV6; - switch (isc_sockaddr_pf(&addrinfo->sockaddr)) { - case AF_INET: - disp_base = res->dispatchv4; - attrs |= DNS_DISPATCHATTR_IPV4; + case PF_INET: + dns_dispatch_attach(res->dispatchv4, + &query->dispatch); + query->exclusivesocket = res->exclusivev4; break; - case AF_INET6: - disp_base = res->dispatchv6; - attrs |= DNS_DISPATCHATTR_IPV6; + case PF_INET6: + dns_dispatch_attach(res->dispatchv6, + &query->dispatch); + query->exclusivesocket = res->exclusivev6; break; default: result = ISC_R_NOTIMPLEMENTED; goto cleanup_query; } - - result = dns_dispatch_getlocaladdress(disp_base, &localaddr); - if (result != ISC_R_SUCCESS) - goto cleanup_query; - if (isc_sockaddr_getport(&localaddr) == 0) { - result = dns_dispatch_getudp(res->dispatchmgr, - res->socketmgr, - res->taskmgr, - &localaddr, - 4096, 1000, 32768, - 16411, 16433, - attrs, attrmask, - &query->dispatch); - if (result != ISC_R_SUCCESS) - goto cleanup_query; - } else - dns_dispatch_attach(disp_base, &query->dispatch); /* * We should always have a valid dispatcher here. If we * don't support a protocol family, then its dispatcher @@ -1224,13 +1241,14 @@ resquery_send(resquery_t *query) { /* * Get a query id from the dispatch. */ - result = dns_dispatch_addresponse(query->dispatch, - &query->addrinfo->sockaddr, - task, - resquery_response, - query, - &query->id, - &query->dispentry); + result = dns_dispatch_addresponse2(query->dispatch, + &query->addrinfo->sockaddr, + task, + resquery_response, + query, + &query->id, + &query->dispentry, + res->socketmgr); if (result != ISC_R_SUCCESS) goto cleanup_temps; @@ -1412,12 +1430,24 @@ resquery_send(resquery_t *query) { */ dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); - socket = dns_dispatch_getsocket(query->dispatch); + if (query->exclusivesocket) + socket = dns_dispatch_getentrysocket(query->dispentry); + else + socket = dns_dispatch_getsocket(query->dispatch); /* * Send the query! */ - if ((query->options & DNS_FETCHOPT_TCP) == 0) + if ((query->options & DNS_FETCHOPT_TCP) == 0) { address = &query->addrinfo->sockaddr; + if (query->exclusivesocket) { + result = isc_socket_connect(socket, address, task, + resquery_udpconnected, + query); + if (result != ISC_R_SUCCESS) + goto cleanup_message; + query->connects++; + } + } isc_buffer_usedregion(buffer, &r); /* @@ -1740,7 +1770,7 @@ add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason) { return; if (reason == DNS_R_UNEXPECTEDRCODE && - fctx->rmessage->opcode == dns_rcode_servfail && + fctx->rmessage->rcode == dns_rcode_servfail && ISFORWARDER(addrinfo)) return; @@ -2026,6 +2056,13 @@ fctx_getaddresses(fetchctx_t *fctx) { } while (sa != NULL) { + if ((isc_sockaddr_pf(sa) == AF_INET && + fctx->res->dispatchv4 == NULL) || + (isc_sockaddr_pf(sa) == AF_INET6 && + fctx->res->dispatchv6 == NULL)) { + sa = ISC_LIST_NEXT(sa, link); + continue; + } ai = NULL; result = dns_adb_findaddrinfo(fctx->adb, sa, &ai, 0); /* XXXMLG */ @@ -5315,6 +5352,19 @@ resquery_response(isc_task_t *task, isc_event_t *event) { * There's no hope for this query. */ keep_trying = ISC_TRUE; + + /* + * If this is a network error on an exclusive query + * socket, mark the server as bad so that we won't try + * it for this fetch again. + */ + if (query->exclusivesocket && + (devent->result == ISC_R_HOSTUNREACH || + devent->result == ISC_R_NETUNREACH || + devent->result == ISC_R_CONNREFUSED || + devent->result == ISC_R_CANCELED)) { + broken_server = devent->result; + } } goto done; } @@ -5465,12 +5515,17 @@ resquery_response(isc_task_t *task, isc_event_t *event) { */ if (message->rcode != dns_rcode_noerror && message->rcode != dns_rcode_nxdomain) { - if ((message->rcode == dns_rcode_formerr || - message->rcode == dns_rcode_notimp || - message->rcode == dns_rcode_servfail) && + if (((message->rcode == dns_rcode_formerr || + message->rcode == dns_rcode_notimp) || + (message->rcode == dns_rcode_servfail && + dns_message_getopt(message) == NULL)) && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { /* * It's very likely they don't like EDNS0. + * If the response code is SERVFAIL, also check if the + * response contains an OPT RR and don't cache the + * failure since it can be returned for various other + * reasons. * * XXXRTH We should check if the question * we're asking requires EDNS0, and @@ -5936,6 +5991,7 @@ dns_resolver_create(dns_view_t *view, isc_result_t result = ISC_R_SUCCESS; unsigned int i, buckets_created = 0; char name[16]; + unsigned dispattr; /* * Create a resolver. @@ -5991,11 +6047,20 @@ dns_resolver_create(dns_view_t *view, } res->dispatchv4 = NULL; - if (dispatchv4 != NULL) + if (dispatchv4 != NULL) { dns_dispatch_attach(dispatchv4, &res->dispatchv4); + dispattr = dns_dispatch_getattributes(dispatchv4); + res->exclusivev4 = + ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); + } + res->dispatchv6 = NULL; - if (dispatchv6 != NULL) + if (dispatchv6 != NULL) { dns_dispatch_attach(dispatchv6, &res->dispatchv6); + dispattr = dns_dispatch_getattributes(dispatchv6); + res->exclusivev6 = + ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); + } res->references = 1; res->exiting = ISC_FALSE; @@ -6253,12 +6318,12 @@ dns_resolver_shutdown(dns_resolver_t *res) { fctx != NULL; fctx = ISC_LIST_NEXT(fctx, link)) fctx_shutdown(fctx); - if (res->dispatchv4 != NULL) { + if (res->dispatchv4 != NULL && !res->exclusivev4) { sock = dns_dispatch_getsocket(res->dispatchv4); isc_socket_cancel(sock, res->buckets[i].task, ISC_SOCKCANCEL_ALL); } - if (res->dispatchv6 != NULL) { + if (res->dispatchv6 != NULL && !res->exclusivev6) { sock = dns_dispatch_getsocket(res->dispatchv6); isc_socket_cancel(sock, res->buckets[i].task, ISC_SOCKCANCEL_ALL); |