diff --git a/CHANGES b/CHANGES index b74d00be3c..279838502a 100644 --- a/CHANGES +++ b/CHANGES @@ -2,9 +2,11 @@ "sending notifies" log message when also-notify was used. [RT #11177] -1622. [placeholder] rt11156 +1622. [func] probe the system to see if IPV6_(RECV)PKTINFO is + available, and suppress wildcard binding if not. -1621. [placeholder] rt11156 +1621. [bug] match-destinations did not work for IPv6 TCP queries. + [RT# 11156] 1620. [func] When loading a zone report if it is signed. [RT #11149] diff --git a/bin/named/client.c b/bin/named/client.c index 5972828878..a8c4698321 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: client.c,v 1.219 2004/03/05 04:57:46 marka Exp $ */ +/* $Id: client.c,v 1.220 2004/04/29 01:37:12 marka Exp $ */ #include @@ -1344,12 +1344,33 @@ client_request(isc_task_t *task, isc_event_t *event) { } /* - * Determine the destination address. For IPv6, we get this from the - * pktinfo structure (if supported). For IPv4, we have to make do with - * the address of the interface where the request was received. + * Determine the destination address. If the receiving interface is + * bound to a specific address, we simply use it regardless of the + * address family. All IPv4 queries should fall into this case. + * Otherwise, if this is a TCP query, get the address from the + * receiving socket (this needs a system call and can be heavy). + * For IPv6 UDP queries, we get this from the pktinfo structure (if + * supported). + * If all the attempts fail (this can happen due to memory shortage, + * etc), we regard this as an error for safety. */ - if (client->interface->addr.type.sa.sa_family == AF_INET6) { - if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0) { + if ((client->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0) + isc_netaddr_fromsockaddr(&destaddr, &client->interface->addr); + else { + result = ISC_R_FAILURE; + + if (TCP_CLIENT(client)) { + isc_sockaddr_t destsockaddr; + + result = isc_socket_getsockname(client->tcpsocket, + &destsockaddr); + if (result == ISC_R_SUCCESS) + isc_netaddr_fromsockaddr(&destaddr, + &destsockaddr); + } + if (result != ISC_R_SUCCESS && + client->interface->addr.type.sa.sa_family == AF_INET6 && + (client->attributes & NS_CLIENTATTR_PKTINFO) != 0) { isc_uint32_t zone = 0; /* @@ -1366,11 +1387,15 @@ client_request(isc_task_t *task, isc_event_t *event) { isc_netaddr_fromin6(&destaddr, &client->pktinfo.ipi6_addr); isc_netaddr_setzone(&destaddr, zone); - - } else - isc_netaddr_any6(&destaddr); - } else { - isc_netaddr_fromsockaddr(&destaddr, &client->interface->addr); + result = ISC_R_SUCCESS; + } + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "failed to get request's " + "destination: %s", + isc_result_totext(result)); + goto cleanup; + } } /* diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h index f72fe7b597..9c4d422ef7 100644 --- a/bin/named/include/named/interfacemgr.h +++ b/bin/named/include/named/interfacemgr.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: interfacemgr.h,v 1.26 2004/03/05 04:57:55 marka Exp $ */ +/* $Id: interfacemgr.h,v 1.27 2004/04/29 01:37:13 marka Exp $ */ #ifndef NAMED_INTERFACEMGR_H #define NAMED_INTERFACEMGR_H 1 @@ -65,6 +65,8 @@ #define IFACE_MAGIC ISC_MAGIC('I',':','-',')') #define NS_INTERFACE_VALID(t) ISC_MAGIC_VALID(t, IFACE_MAGIC) +#define NS_INTERFACEFLAG_ANYADDR 0x01U /* bound to "any" address */ + struct ns_interface { unsigned int magic; /* Magic number. */ ns_interfacemgr_t * mgr; /* Interface manager. */ @@ -72,6 +74,7 @@ struct ns_interface { int references; /* Locked */ unsigned int generation; /* Generation number. */ isc_sockaddr_t addr; /* Address and port. */ + unsigned int flags; /* Interface characteristics */ char name[32]; /* Null terminated. */ dns_dispatch_t * udpdispatch; /* UDP dispatcher. */ isc_socket_t * tcpsocket; /* TCP socket. */ diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c index f440e521a0..7a20850044 100644 --- a/bin/named/interfacemgr.c +++ b/bin/named/interfacemgr.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: interfacemgr.c,v 1.76 2004/03/05 04:57:46 marka Exp $ */ +/* $Id: interfacemgr.c,v 1.77 2004/04/29 01:37:12 marka Exp $ */ #include @@ -545,6 +545,7 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, isc_boolean_t scan_ipv6 = ISC_FALSE; isc_boolean_t adjusting = ISC_FALSE; isc_boolean_t ipv6only = ISC_TRUE; + isc_boolean_t ipv6pktinfo = ISC_TRUE; isc_result_t result; isc_netaddr_t zero_address, zero_address6; ns_listenelt_t *le; @@ -586,7 +587,12 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, log_explicit = ISC_TRUE; } #endif - if (scan_ipv6 == ISC_TRUE && ipv6only) { + if (scan_ipv6 == ISC_TRUE && + isc_net_probe_ipv6pktinfo() != ISC_R_SUCCESS) { + ipv6pktinfo = ISC_FALSE; + log_explicit = ISC_TRUE; + } + if (scan_ipv6 == ISC_TRUE && ipv6only && ipv6pktinfo) { for (le = ISC_LIST_HEAD(mgr->listenon6->elts); le != NULL; le = ISC_LIST_NEXT(le, link)) { @@ -610,7 +616,9 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, result = ns_interface_setup(mgr, &listen_addr, "", &ifp, ISC_TRUE); - if (result != ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) + ifp->flags |= NS_INTERFACEFLAG_ANYADDR; + else isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR, "listening on all IPv6 " @@ -719,7 +727,7 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, * The case of "any" IPv6 address will require * special considerations later, so remember it. */ - if (family == AF_INET6 && ipv6only && + if (family == AF_INET6 && ipv6only && ipv6pktinfo && listenon_is_ip6_any(le)) ipv6_wildcard = ISC_TRUE; @@ -760,14 +768,14 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, continue; if (log_explicit && family == AF_INET6 && - !adjusting) { + !adjusting && listenon_is_ip6_any(le)) { isc_log_write(IFMGR_COMMON_LOGARGS, verbose ? ISC_LOG_INFO : ISC_LOG_DEBUG(1), - "IPv6-only option is not" - " available; explicitly" - " binding to all IPv6" - " addresses."); + "IPv6 socket API is " + "incomplete; explicitly " + "binding to each IPv6 " + "address separately"); log_explicit = ISC_FALSE; } isc_sockaddr_format(&listen_sockaddr, diff --git a/lib/isc/unix/include/isc/net.h b/lib/isc/unix/include/isc/net.h index 7ff2da1921..2044a3cc3d 100644 --- a/lib/isc/unix/include/isc/net.h +++ b/lib/isc/unix/include/isc/net.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: net.h,v 1.39 2004/03/05 05:11:52 marka Exp $ */ +/* $Id: net.h,v 1.40 2004/04/29 01:37:13 marka Exp $ */ #ifndef ISC_NET_H #define ISC_NET_H 1 @@ -278,6 +278,19 @@ isc_net_probe_ipv6only(void); * ISC_R_UNEXPECTED */ +isc_result_t +isc_net_probe_ipv6pktinfo(void); +/* + * Check if the system's kernel supports the IPV6_(RECV)PKTINFO socket option + * for UDP sockets. + * + * Returns: + * + * ISC_R_SUCCESS the option is supported. + * ISC_R_NOTFOUND IPv6 itself or the option is not supported. + * ISC_R_UNEXPECTED + */ + void isc_net_disableipv4(void); diff --git a/lib/isc/unix/net.c b/lib/isc/unix/net.c index 16395a15c6..b8576f35be 100644 --- a/lib/isc/unix/net.c +++ b/lib/isc/unix/net.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: net.c,v 1.29 2004/03/05 05:11:46 marka Exp $ */ +/* $Id: net.c,v 1.30 2004/04/29 01:37:13 marka Exp $ */ #include @@ -40,9 +40,11 @@ const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT; static isc_once_t once = ISC_ONCE_INIT; static isc_once_t once_ipv6only = ISC_ONCE_INIT; +static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; static isc_result_t ipv4_result = ISC_R_NOTFOUND; static isc_result_t ipv6_result = ISC_R_NOTFOUND; static isc_result_t ipv6only_result = ISC_R_NOTFOUND; +static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; static isc_result_t try_proto(int domain) { @@ -225,7 +227,7 @@ try_ipv6only(void) { close: close(s); return; -#endif +#endif /* IPV6_V6ONLY */ } static void @@ -233,8 +235,61 @@ initialize_ipv6only(void) { RUNTIME_CHECK(isc_once_do(&once_ipv6only, try_ipv6only) == ISC_R_SUCCESS); } +#endif /* IPV6_V6ONLY */ + +static void +try_ipv6pktinfo(void) { + int s, on; + char strbuf[ISC_STRERRORSIZE]; + isc_result_t result; + int optname; + + result = isc_net_probeipv6(); + if (result != ISC_R_SUCCESS) { + ipv6pktinfo_result = result; + return; + } + + /* we only use this for UDP sockets */ + s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + ipv6pktinfo_result = ISC_R_UNEXPECTED; + return; + } + +#ifdef IPV6_RECVPKTINFO + optname = IPV6_RECVPKTINFO; +#else + optname = IPV6_PKTINFO; #endif -#endif + on = 1; + if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) { + ipv6pktinfo_result = ISC_R_NOTFOUND; + goto close; + } + + close(s); + ipv6pktinfo_result = ISC_R_SUCCESS; + +close: + close(s); + return; +} + +static void +initialize_ipv6pktinfo(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, + try_ipv6pktinfo) == ISC_R_SUCCESS); +} +#endif /* WANT_IPV6 */ isc_result_t isc_net_probe_ipv6only(void) { @@ -248,6 +303,18 @@ isc_net_probe_ipv6only(void) { return (ipv6only_result); } +isc_result_t +isc_net_probe_ipv6pktinfo(void) { +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 + initialize_ipv6pktinfo(); +#else + ipv6pktinfo_result = ISC_R_NOTFOUND; +#endif +#endif + return (ipv6pktinfo_result); +} + void isc_net_disableipv4(void) { initialize(); diff --git a/lib/isc/win32/include/isc/net.h b/lib/isc/win32/include/isc/net.h index f42105ead9..86497492cf 100644 --- a/lib/isc/win32/include/isc/net.h +++ b/lib/isc/win32/include/isc/net.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: net.h,v 1.22 2004/04/19 04:16:55 marka Exp $ */ +/* $Id: net.h,v 1.23 2004/04/29 01:37:14 marka Exp $ */ #ifndef ISC_NET_H #define ISC_NET_H 1 @@ -270,6 +270,19 @@ isc_net_probe_ipv6only(void); * ISC_R_UNEXPECTED */ +isc_result_t +isc_net_probe_ipv6pktinfo(void); +/* + * Check if the system's kernel supports the IPV6_(RECV)PKTINFO socket option + * for UDP sockets. + * + * Returns: + * + * ISC_R_SUCCESS the option is supported. + * ISC_R_NOTFOUND IPv6 itself or the option is not supported. + * ISC_R_UNEXPECTED + */ + void isc_net_disableipv4(void); diff --git a/lib/isc/win32/libisc.def b/lib/isc/win32/libisc.def index 8421191354..85074d4bc1 100644 --- a/lib/isc/win32/libisc.def +++ b/lib/isc/win32/libisc.def @@ -432,6 +432,7 @@ isc_net_disableipv6 isc_task_getcurrenttime isc_net_probe_ipv6only isc_timermgr_poke +isc_net_probe_ipv6pktinfo ; Exported Data diff --git a/lib/isc/win32/net.c b/lib/isc/win32/net.c index 080e0923e2..b86eabd062 100644 --- a/lib/isc/win32/net.c +++ b/lib/isc/win32/net.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: net.c,v 1.9 2004/03/16 05:52:22 marka Exp $ */ +/* $Id: net.c,v 1.10 2004/04/29 01:37:14 marka Exp $ */ #include @@ -36,9 +36,11 @@ const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT; static isc_once_t once = ISC_ONCE_INIT; static isc_once_t once_ipv6only = ISC_ONCE_INIT; +static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; static isc_result_t ipv4_result = ISC_R_NOTFOUND; static isc_result_t ipv6_result = ISC_R_NOTFOUND; static isc_result_t ipv6only_result = ISC_R_NOTFOUND; +static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; static isc_result_t try_proto(int domain) { @@ -218,7 +220,7 @@ try_ipv6only(void) { close: close(s); return; -#endif +#endif /* IPV6_V6ONLY */ } static void @@ -226,8 +228,61 @@ initialize_ipv6only(void) { RUNTIME_CHECK(isc_once_do(&once_ipv6only, try_ipv6only) == ISC_R_SUCCESS); } + +static void +try_ipv6pktinfo(void) { + int s, on; + char strbuf[ISC_STRERRORSIZE]; + isc_result_t result; + int optname; + + result = isc_net_probeipv6(); + if (result != ISC_R_SUCCESS) { + ipv6pktinfo_result = result; + return; + } + + /* we only use this for UDP sockets */ + s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + ipv6pktinfo_result = ISC_R_UNEXPECTED; + return; + } + +#ifdef IPV6_RECVPKTINFO + optname = IPV6_RECVPKTINFO; +#else + optname = IPV6_PKTINFO; #endif -#endif + on = 1; + if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) { + ipv6pktinfo_result = ISC_R_NOTFOUND; + goto close; + } + + close(s); + ipv6pktinfo_result = ISC_R_SUCCESS; + +close: + close(s); + return; +} + +static void +initialize_ipv6pktinfo(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, + try_ipv6pktinfo) == ISC_R_SUCCESS); +} +#endif /* WANT_IPV6 */ +#endif /* ISC_PLATFORM_HAVEIPV6 */ isc_result_t isc_net_probe_ipv6only(void) { @@ -241,6 +296,18 @@ isc_net_probe_ipv6only(void) { return (ipv6only_result); } +isc_result_t +isc_net_probe_ipv6pktinfo(void) { +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 + initialize_ipv6pktinfo(); +#else + ipv6pktinfo_result = ISC_R_NOTFOUND; +#endif +#endif + return (ipv6pktinfo_result); +} + void isc_net_disableipv4(void) { initialize();