diff options
Diffstat (limited to 'usr.sbin/bind/lib/dns/dispatch.c')
-rw-r--r-- | usr.sbin/bind/lib/dns/dispatch.c | 87 |
1 files changed, 77 insertions, 10 deletions
diff --git a/usr.sbin/bind/lib/dns/dispatch.c b/usr.sbin/bind/lib/dns/dispatch.c index 81ea88f5c84..d9c4986fe88 100644 --- a/usr.sbin/bind/lib/dns/dispatch.c +++ b/usr.sbin/bind/lib/dns/dispatch.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2006 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $ISC: dispatch.c,v 1.101.2.6.2.10 2004/09/01 04:27:41 marka Exp $ */ +/* $ISC: dispatch.c,v 1.101.2.6.2.13 2006/07/19 00:44:04 marka Exp $ */ #include <config.h> @@ -618,6 +618,50 @@ udp_recv(isc_task_t *task, isc_event_t *ev_in) { free_buffer(disp, ev->region.base, ev->region.length); goto unlock; } + + /* + * Now that we have the original dispatch the query was sent + * from check that the address and port the response was + * sent to make sense. + */ + if (disp != resp->disp) { + isc_sockaddr_t a1; + isc_sockaddr_t a2; + + /* + * Check that the socket types and ports match. + */ + if (disp->socktype != resp->disp->socktype || + isc_sockaddr_getport(&disp->local) != + isc_sockaddr_getport(&resp->disp->local)) { + free_buffer(disp, ev->region.base, ev->region.length); + goto unlock; + } + + /* + * If both dispatches are bound to an address then fail as + * the addresses can't be equal (enforced by the IP stack). + * + * Note under Linux a packet can be sent out via IPv4 socket + * and the response be received via a IPv6 socket. + * + * Requests sent out via IPv6 should always come back in + * via IPv6. + */ + if (isc_sockaddr_pf(&resp->disp->local) == PF_INET6 && + isc_sockaddr_pf(&disp->local) != PF_INET6) { + free_buffer(disp, ev->region.base, ev->region.length); + goto unlock; + } + isc_sockaddr_anyofpf(&a1, isc_sockaddr_pf(&resp->disp->local)); + isc_sockaddr_anyofpf(&a2, isc_sockaddr_pf(&disp->local)); + if (!isc_sockaddr_eqaddr(&a1, &resp->disp->local) && + !isc_sockaddr_eqaddr(&a2, &disp->local)) { + free_buffer(disp, ev->region.base, ev->region.length); + goto unlock; + } + } + queue_response = resp->item_out; rev = allocate_event(resp->disp); if (rev == NULL) { @@ -1651,6 +1695,11 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, /* * mgr should be locked. */ + +#ifndef DNS_DISPATCH_HELD +#define DNS_DISPATCH_HELD 20U +#endif + static isc_result_t dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_taskmgr_t *taskmgr, @@ -1661,7 +1710,9 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, { isc_result_t result; dns_dispatch_t *disp; - isc_socket_t *sock; + isc_socket_t *sock = NULL; + isc_socket_t *held[DNS_DISPATCH_HELD]; + unsigned int i = 0, j = 0; /* * dispatch_allocate() checks mgr for us. @@ -1672,17 +1723,30 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, return (result); /* - * This assumes that the IP stack will *not* quickly reallocate - * the same port. If it does continually reallocate the same port - * then we need a mechanism to hold all the blacklisted sockets - * until we find a usable socket. + * Try to allocate a socket that is not on the blacklist. + * Hold up to DNS_DISPATCH_HELD sockets to prevent the OS + * from returning the same port to us too quickly. */ + memset(held, 0, sizeof(held)); getsocket: result = create_socket(sockmgr, localaddr, &sock); if (result != ISC_R_SUCCESS) goto deallocate_dispatch; if (isc_sockaddr_getport(localaddr) == 0 && blacklisted(mgr, sock)) { - isc_socket_detach(&sock); + if (held[i] != NULL) + isc_socket_detach(&held[i]); + held[i++] = sock; + sock = NULL; + if (i == DNS_DISPATCH_HELD) + i = 0; + if (j++ == 0xffffU) { + mgr_log(mgr, ISC_LOG_ERROR, "avoid-v%s-udp-ports: " + "unable to allocate a non-blacklisted port", + isc_sockaddr_pf(localaddr) == AF_INET ? + "4" : "6"); + result = ISC_R_FAILURE; + goto deallocate_dispatch; + } goto getsocket; } @@ -1719,7 +1783,7 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, *dispp = disp; - return (ISC_R_SUCCESS); + goto cleanheld; /* * Error returns. @@ -1730,7 +1794,10 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_socket_detach(&disp->socket); deallocate_dispatch: dispatch_free(&disp); - + cleanheld: + for (i = 0; i < DNS_DISPATCH_HELD; i++) + if (held[i] != NULL) + isc_socket_detach(&held[i]); return (result); } |