diff --git a/CHANGES b/CHANGES index 6ad3b7571b..3e70a5b11e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,11 @@ +1394. [func] It is now possible to check if a particular element is + in a acl. Remove duplicate entries from the localnets + acl. + +1393. [port] Bind to individual IPv6 interfaces if IPV6_IPV6ONLY + is not available in the kernel to prevent accidently + listening on IPv4 interfaces. + 1392. [bug] named-checkzone: update usage. 1391. [func] Add support for IPv6 scoped addresses in named. diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c index 5069d0162e..67957b416c 100644 --- a/bin/named/interfacemgr.c +++ b/bin/named/interfacemgr.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: interfacemgr.c,v 1.72 2002/10/24 03:52:31 marka Exp $ */ +/* $Id: interfacemgr.c,v 1.73 2002/10/29 04:40:23 marka Exp $ */ #include @@ -524,11 +524,13 @@ setup_locals(ns_interfacemgr_t *mgr, isc_interface_t *interface) { isc_result_totext(result)); } else { elt.u.ip_prefix.prefixlen = prefixlen; - /* XXX suppress duplicates */ - result = dns_acl_appendelement(mgr->aclenv.localnets, - &elt); - if (result != ISC_R_SUCCESS) - return (result); + if (dns_acl_elementmatch(mgr->aclenv.localnets, &elt, + NULL) == ISC_R_NOTFOUND) { + result = dns_acl_appendelement(mgr->aclenv.localnets, + &elt); + if (result != ISC_R_SUCCESS) + return (result); + } } return (ISC_R_SUCCESS); @@ -542,6 +544,7 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, isc_boolean_t scan_ipv4 = ISC_FALSE; isc_boolean_t scan_ipv6 = ISC_FALSE; isc_boolean_t adjusting = ISC_FALSE; + isc_boolean_t ipv6only = ISC_TRUE; isc_result_t result; isc_netaddr_t zero_address, zero_address6; ns_listenelt_t *le; @@ -567,9 +570,25 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, verbose ? ISC_LOG_INFO : ISC_LOG_DEBUG(1), "no IPv4 interfaces found"); - /* A special, but typical case; listen-on-v6 { any; } */ - /* XXXJT fix when we probe for IPV6_V6ONLY */ - if (scan_ipv6 == ISC_TRUE) { + /* + * A special, but typical case; listen-on-v6 { any; }. + * When we can make the socket IPv6-only, open a single wildcard + * socket for IPv6 communication. Otherwise, make separate socket + * for each IPv6 address in order to avoid accepting IPv4 packets + * as the form of mapped addresses unintentionally unless explicitly + * allowed. + */ +#ifndef ISC_ALLOW_MAPPED + if (scan_ipv6 == ISC_TRUE && + isc_net_probe_ipv6only() != ISC_R_SUCCESS) { + ipv6only = ISC_FALSE; + isc_log_write(IFMGR_COMMON_LOGARGS, + verbose ? ISC_LOG_INFO : ISC_LOG_DEBUG(1), + "IPv6-only option is not available." + " use explicit binding"); + } +#endif + if (scan_ipv6 == ISC_TRUE && ipv6only) { for (le = ISC_LIST_HEAD(mgr->listenon6->elts); le != NULL; le = ISC_LIST_NEXT(le, link)) { @@ -702,7 +721,8 @@ 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 && listenon_is_ip6_any(le)) + if (family == AF_INET6 && ipv6only && + listenon_is_ip6_any(le)) ipv6_wildcard = ISC_TRUE; /* diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index c678fccdf1..339a4421d2 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -2,7 +2,7 @@ - + BIND 9 Administrator Reference Manual @@ -3549,11 +3549,10 @@ listen-on port 1234 { !1.2.3.4; 1.2/16; }; server will listen on port 53 on all interfaces. By default, the server does not bind a separate socket to each -IPv6 interface address as it does for IPv4. Instead, it always -listens on the IPv6 wildcard address. -However, some particular IPv6 addresses can also be specified, -in which case the server makes a separate socket for each specified -address. +IPv6 interface address as it does for IPv4. Instead, it listens on the +IPv6 wildcard address. +Alternatively, a list of IPv6 addresses can be specified, in which case +the server listens on a separate socket for each specified address. Multiple listen-on-v6 options can be used. For example, diff --git a/lib/dns/acl.c b/lib/dns/acl.c index a5083f12e8..83aa5d61cd 100644 --- a/lib/dns/acl.c +++ b/lib/dns/acl.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: acl.c,v 1.23 2001/05/31 10:43:37 tale Exp $ */ +/* $Id: acl.c,v 1.24 2002/10/29 04:40:23 marka Exp $ */ #include @@ -149,6 +149,29 @@ dns_acl_match(isc_netaddr_t *reqaddr, return (ISC_R_SUCCESS); } +isc_result_t +dns_acl_elementmatch(dns_acl_t *acl, + dns_aclelement_t *elt, + dns_aclelement_t **matchelt) +{ + unsigned int i; + + REQUIRE(elt != NULL); + REQUIRE(matchelt == NULL || *matchelt == NULL); + + for (i = 0; i < acl->length; i++) { + dns_aclelement_t *e = &acl->elements[i]; + + if (dns_aclelement_equal(e, elt) == ISC_TRUE) { + if (matchelt != NULL) + *matchelt = e; + return (ISC_R_SUCCESS); + } + } + + return (ISC_R_NOTFOUND); +} + isc_boolean_t dns_aclelement_match(isc_netaddr_t *reqaddr, dns_name_t *reqsigner, @@ -297,8 +320,9 @@ dns_aclelement_equal(dns_aclelement_t *ea, dns_aclelement_t *eb) { if (ea->u.ip_prefix.prefixlen != eb->u.ip_prefix.prefixlen) return (ISC_FALSE); - return (isc_netaddr_equal(&ea->u.ip_prefix.address, - &eb->u.ip_prefix.address)); + return (isc_netaddr_eqprefix(&ea->u.ip_prefix.address, + &eb->u.ip_prefix.address, + ea->u.ip_prefix.prefixlen)); case dns_aclelementtype_keyname: return (dns_name_equal(&ea->u.keyname, &eb->u.keyname)); case dns_aclelementtype_nestedacl: diff --git a/lib/dns/include/dns/acl.h b/lib/dns/include/dns/acl.h index e758bdf275..7ca4cefe74 100644 --- a/lib/dns/include/dns/acl.h +++ b/lib/dns/include/dns/acl.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: acl.h,v 1.20 2001/08/28 03:58:11 marka Exp $ */ +/* $Id: acl.h,v 1.21 2002/10/29 04:40:24 marka Exp $ */ #ifndef DNS_ACL_H #define DNS_ACL_H 1 @@ -199,6 +199,23 @@ dns_aclelement_match(isc_netaddr_t *reqaddr, * returned through 'matchelt' is not necessarily 'e' itself. */ +isc_result_t +dns_acl_elementmatch(dns_acl_t *acl, + dns_aclelement_t *elt, + dns_aclelement_t **matchelt); +/* + * Search for an ACL element in 'acl' which is exactly the same as 'elt'. + * If there is one, and 'matchelt' is non NULL, then '*matchelt' will point + * to the entry. + * + * This function is intended to be used for avoiding duplicated ACL entries + * before adding an entry. + * + * Returns: + * ISC_R_SUCCESS Match succeeds. + * ISC_R_NOTFOUND Match fails. + */ + ISC_LANG_ENDDECLS #endif /* DNS_ACL_H */ diff --git a/lib/isc/unix/include/isc/net.h b/lib/isc/unix/include/isc/net.h index f527a977df..2997588eae 100644 --- a/lib/isc/unix/include/isc/net.h +++ b/lib/isc/unix/include/isc/net.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: net.h,v 1.35 2002/10/24 03:52:35 marka Exp $ */ +/* $Id: net.h,v 1.36 2002/10/29 04:40:25 marka Exp $ */ #ifndef ISC_NET_H #define ISC_NET_H 1 @@ -260,6 +260,18 @@ isc_net_probeipv6(void); * ISC_R_UNEXPECTED */ +isc_result_t +isc_net_probe_ipv6only(void); +/* + * Check if the system's kernel supports the IPV6_V6ONLY socket option. + * + * Returns: + * + * ISC_R_SUCCESS the option is supported for both TCP and UDP. + * ISC_R_NOTFOUND IPv6 itself or the option is not supported. + * ISC_R_UNEXPECTED + */ + #ifdef ISC_PLATFORM_NEEDNTOP const char * isc_net_ntop(int af, const void *src, char *dst, size_t size); diff --git a/lib/isc/unix/net.c b/lib/isc/unix/net.c index bb86eae9ec..b4899c17e5 100644 --- a/lib/isc/unix/net.c +++ b/lib/isc/unix/net.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: net.c,v 1.25 2001/11/30 01:59:45 gson Exp $ */ +/* $Id: net.c,v 1.26 2002/10/29 04:40:25 marka Exp $ */ #include @@ -39,8 +39,10 @@ const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT; #endif static isc_once_t once = ISC_ONCE_INIT; +static isc_once_t once_ipv6only = 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 try_proto(int domain) { @@ -146,8 +148,102 @@ isc_net_probeipv4(void) { return (ipv4_result); } +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 isc_result_t isc_net_probeipv6(void) { initialize(); return (ipv6_result); } + +static void +try_ipv6only(void) { +#ifdef IPV6_V6ONLY + int s, on; + char strbuf[ISC_STRERRORSIZE]; +#endif + isc_result_t result; + + result = isc_net_probeipv6(); + if (result != ISC_R_SUCCESS) { + ipv6only_result = result; + return; + } + +#ifndef IPV6_V6ONLY + ipv6only_result = ISC_R_NOTFOUND; + return; +#else + /* check for TCP sockets */ + s = socket(PF_INET6, SOCK_STREAM, 0); + 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); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + close(s); + + /* check for UDP sockets */ + s = socket(PF_INET6, SOCK_DGRAM, 0); + 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); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + close(s); + + ipv6only_result = ISC_R_SUCCESS; + +close: + close(s); + return; +#endif +} + +static void +initialize_ipv6only(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6only, + try_ipv6only) == ISC_R_SUCCESS); +} +#endif +#endif + +isc_result_t +isc_net_probe_ipv6only(void) { +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 + initialize_ipv6only(); +#else + ipv6only_result = ISC_R_NOTFOUND; +#endif +#endif + return (ipv6only_result); +} diff --git a/lib/isc/win32/include/isc/net.h b/lib/isc/win32/include/isc/net.h index 43e54fbe36..1f909362fd 100644 --- a/lib/isc/win32/include/isc/net.h +++ b/lib/isc/win32/include/isc/net.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: net.h,v 1.17 2002/08/01 03:56:08 mayer Exp $ */ +/* $Id: net.h,v 1.18 2002/10/29 04:40:26 marka Exp $ */ #ifndef ISC_NET_H #define ISC_NET_H 1 @@ -247,6 +247,18 @@ isc_net_probeipv6(void); * ISC_R_UNEXPECTED */ +isc_result_t +isc_net_probe_ipv6only(void); +/* + * Check if the system's kernel supports the IPV6_V6ONLY socket option. + * + * Returns: + * + * ISC_R_SUCCESS the option is supported for both TCP and UDP. + * ISC_R_NOTFOUND IPv6 itself or the option is not supported. + * ISC_R_UNEXPECTED + */ + #ifdef ISC_PLATFORM_NEEDNTOP const char * isc_net_ntop(int af, const void *src, char *dst, size_t size); diff --git a/lib/isc/win32/net.c b/lib/isc/win32/net.c index 2491903236..6f69f29fa5 100644 --- a/lib/isc/win32/net.c +++ b/lib/isc/win32/net.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: net.c,v 1.4 2001/11/21 05:07:25 mayer Exp $ */ +/* $Id: net.c,v 1.5 2002/10/29 04:40:25 marka Exp $ */ #include @@ -35,8 +35,10 @@ const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT; #endif static isc_once_t once = ISC_ONCE_INIT; +static isc_once_t once_ipv6only = 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 try_proto(int domain) { @@ -140,8 +142,102 @@ isc_net_probeipv4(void) { return (ipv4_result); } +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 isc_result_t isc_net_probeipv6(void) { initialize(); return (ipv6_result); } + +static void +try_ipv6only(void) { +#ifdef IPV6_V6ONLY + int s, on; + char strbuf[ISC_STRERRORSIZE]; +#endif + isc_result_t result; + + result = isc_net_probeipv6(); + if (result != ISC_R_SUCCESS) { + ipv6only_result = result; + return; + } + +#ifndef IPV6_V6ONLY + ipv6only_result = ISC_R_NOTFOUND; + return; +#else + /* check for TCP sockets */ + s = socket(PF_INET6, SOCK_STREAM, 0); + 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); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + close(s); + + /* check for UDP sockets */ + s = socket(PF_INET6, SOCK_DGRAM, 0); + 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); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + close(s); + + ipv6only_result = ISC_R_SUCCESS; + +close: + close(s); + return; +#endif +} + +static void +initialize_ipv6only(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6only, + try_ipv6only) == ISC_R_SUCCESS); +} +#endif +#endif + +isc_result_t +isc_net_probe_ipv6only(void) { +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 + initialize_ipv6only(); +#else + ipv6only_result = ISC_R_NOTFOUND; +#endif +#endif + return (ipv6only_result); +}