]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
1622. [func] probe the system to see if IPV6_(RECV)PKTINFO is
authorMark Andrews <marka@isc.org>
Thu, 29 Apr 2004 01:37:14 +0000 (01:37 +0000)
committerMark Andrews <marka@isc.org>
Thu, 29 Apr 2004 01:37:14 +0000 (01:37 +0000)
                        available, and suppress wildcard binding if not.

1621.   [bug]           match-destinations did not work for IPv6 TCP queries.
                        [RT# 11156]

CHANGES
bin/named/client.c
bin/named/include/named/interfacemgr.h
bin/named/interfacemgr.c
lib/isc/unix/include/isc/net.h
lib/isc/unix/net.c
lib/isc/win32/include/isc/net.h
lib/isc/win32/libisc.def
lib/isc/win32/net.c

diff --git a/CHANGES b/CHANGES
index b74d00be3c6fd83035e159c7b108537f3519d8e9..279838502a2e45e5451293e1bc09ba78a47bc8dc 100644 (file)
--- 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]
 
index 59728288785045cb5f68542eb4eeb3c142dcf5b6..a8c4698321df0d75b6c9bbffe93e486d8a6d375b 100644 (file)
@@ -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 <config.h>
 
@@ -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;
+               }
        }
 
        /*
index f72fe7b59785e5903dd8b9ee119e3aa326878738..9c4d422ef7ced4be6d7848ee581a40d97ec243c0 100644 (file)
@@ -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. */
index f440e521a073e43579bdd22fa151ae1dce84b02a..7a208500442e6b0e879dcddd1b823e525f78d7b6 100644 (file)
@@ -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 <config.h>
 
@@ -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,
                                                            "<any>", &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,
index 7ff2da1921a00f542994f72c5ec1f62e1bca0f55..2044a3cc3d88d9a707a4340950e4033e73e9fea6 100644 (file)
@@ -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);
 
index 16395a15c68a219004c0c6fbf852152b501fe07a..b8576f35bef8eae4da0b596724733c2ed9a054ff 100644 (file)
@@ -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 <config.h>
 
@@ -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();
index f42105ead94ecc3245485d4287e6a35175f28be5..86497492cf7082a9ed9f2f50efe8790a32aff3f0 100644 (file)
@@ -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);
 
index 8421191354a520573b25677c2d19433303fd533c..85074d4bc1c51c648039bd9981fabda9e73129a4 100644 (file)
@@ -432,6 +432,7 @@ isc_net_disableipv6
 isc_task_getcurrenttime
 isc_net_probe_ipv6only
 isc_timermgr_poke
+isc_net_probe_ipv6pktinfo
 
 ; Exported Data
 
index 080e0923e26edb80ecb05b2b99c28d824331c9f5..b86eabd06257d397ede68e80f050d8be3f475ebf 100644 (file)
@@ -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 <config.h>
 
@@ -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();