[PATCH] make getaddrinfo hint AI_ADDRCONFIG work [2nd submission]

Ricard Wanderlof ricard.wanderlof at axis.com
Wed Jun 25 09:42:14 UTC 2008


Hi all,

This is an old issue which I brought up a couple of months ago; I was 
going to submit a completed patch then but other things got in the way and 
it never got done.

I've redone the patch against an svn from yesterday (22487) which I'm 
submitting here again. If no one has any objections I'll try and commit 
this before the week is out.

(copied from old post:)

We experienced a problem with a product when IPv6 is compiled into the
product (and uClibc) but selectively disabled by a user. In this case, 
IPv6
traffic was still be generated, which in this particular case was not
acceptable.

The problem turned out to be in getaddrinfo(). When hints.ai_flags has the
AI_ADDRCONFIG bit set in a call to getaddrinfo, IPv4 and IPv6 addresses
should only be returned if the system has at least one address of the
appropriate type configured. However, this turned out not to be the case,
and both types of addresses were returned in any case, causing IPv6 DNS
traffic even if no interface had an IPv6 address configured.

The following patch fixes the problem, by copying the __check_pf()
function from glibc, and ultimately using this function in getaddrinfo()
to check for available protocol families on the interface in question.

In order to do this, the previously not used (#if 0'd) code in
lic/inet/ifaddrs.c has been enabled.

Since the size of the resulting uClibc library file grows slightly (4.6
kbytes on the architecture we use), there's also a config variable which
has to be enabled to use this feature.

The patch also fixes a bug which caused an infinite loop when
hints->ai_flags had the AI_ADDRCONFIG bit set (the lines around ++g in the
patch). (Perhaps this was a failed attempt att fixing the problem at some
time, as the coresponding glibc version does not seem to have the
corresponding if-followed-by-continue clause. It has been in the svn repo
since the first entry in 2002 (svn 4414) ).

I've also included the patch as an attachement. It was made against svn
22487, head of the development tree att the time of writing.

/Ricard
--
Ricard Wolf Wanderlöf                           ricardw(at)axis.com
Axis Communications AB, Lund, Sweden            www.axis.com
Phone +46 46 272 2016                           Fax +46 46 13 61 30
-------------- next part --------------
diff -urN uClibc-22487/extra/Configs/Config.in uClibc-22487-applied+fixed/extra/Configs/Config.in
--- uClibc-22487/extra/Configs/Config.in	2008-06-24 14:33:33.000000000 +0200
+++ uClibc-22487-applied+fixed/extra/Configs/Config.in	2008-06-24 15:25:01.000000000 +0200
@@ -1018,6 +1018,18 @@
 
 	  Most people can safely answer N.
 
+config UCLIBC_SUPPORT_AI_ADDRCONFIG
+	bool "Support the AI_ADDRCONFIG flag"
+	depends on UCLIBC_USE_NETLINK
+	default n
+	help
+	  The implementation of AI_ADDRCONFIG is aligned with the glibc
+	  implementation using netlink to query interfaces to find both
+	  ipv4 and ipv6 support. This is only needed if an application uses
+	  the AI_ADDRCONFIG flag.
+
+	  Most people can safely answer N.
+
 config UCLIBC_HAS_BSD_RES_CLOSE
 	bool "Support res_close() (bsd-compat)"
 	default n
diff -urN uClibc-22487/include/ifaddrs.h uClibc-22487-applied+fixed/include/ifaddrs.h
--- uClibc-22487/include/ifaddrs.h	1970-01-01 01:00:00.000000000 +0100
+++ uClibc-22487-applied+fixed/include/ifaddrs.h	2008-06-24 15:25:01.000000000 +0200
@@ -0,0 +1,19 @@
+#ifndef _IFADDRS_H
+#include <libc/inet/ifaddrs.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+struct in6addrinfo
+{
+  enum {
+    in6ai_deprecated = 1,
+    in6ai_temporary = 2,
+    in6ai_homeaddress = 4
+  } flags;
+  uint32_t addr[4];
+};
+
+extern void __check_pf (bool *seen_ipv4, bool *seen_ipv6)
+  attribute_hidden;
+
+#endif	/* ifaddrs.h */
diff -urN uClibc-22487/libc/inet/check_pf.c uClibc-22487-applied+fixed/libc/inet/check_pf.c
--- uClibc-22487/libc/inet/check_pf.c	1970-01-01 01:00:00.000000000 +0100
+++ uClibc-22487-applied+fixed/libc/inet/check_pf.c	2008-06-24 15:25:01.000000000 +0200
@@ -0,0 +1,65 @@
+/* Determine protocol families for which interfaces exist.  Generic version.
+   Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <features.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+
+
+void
+attribute_hidden
+__check_pf (bool *seen_ipv4, bool *seen_ipv6)
+{
+  *seen_ipv4 = false;
+  *seen_ipv6 = false;
+#if __UCLIBC_SUPPORT_AI_ADDRCONFIG__
+  {
+    /* Get the interface list via getifaddrs.  */
+    struct ifaddrs *ifa = NULL;
+    struct ifaddrs *runp;
+    if (getifaddrs (&ifa) != 0)
+    {
+      /* We cannot determine what interfaces are available.  Be
+      optimistic.  */
+      *seen_ipv4 = true;
+#if __UCLIBC_HAS_IPV6__
+      *seen_ipv6 = true;
+#endif /* __UCLIBC_HAS_IPV6__ */
+      return;
+    }
+
+    for (runp = ifa; runp != NULL; runp = runp->ifa_next)
+      if (runp->ifa_addr->sa_family == PF_INET)
+        *seen_ipv4 = true;
+#if __UCLIBC_HAS_IPV6__
+      else if (runp->ifa_addr->sa_family == PF_INET6)
+        *seen_ipv6 = true;
+#endif /* __UCLIBC_HAS_IPV6__ */
+
+    (void) freeifaddrs (ifa);
+  }
+#else
+  /* AI_ADDRCONFIG is disabled, assume both ipv4 and ipv6 available. */
+  *seen_ipv4 = true;
+#if __UCLIBC_HAS_IPV6__
+  *seen_ipv6 = true;
+#endif /* __UCLIBC_HAS_IPV6__ */
+
+#endif /* __UCLIBC_SUPPORT_AI_ADDRCONFIG__ */
+}
diff -urN uClibc-22487/libc/inet/getaddrinfo.c uClibc-22487-applied+fixed/libc/inet/getaddrinfo.c
--- uClibc-22487/libc/inet/getaddrinfo.c	2008-06-24 14:33:37.000000000 +0200
+++ uClibc-22487-applied+fixed/libc/inet/getaddrinfo.c	2008-06-24 17:20:23.000000000 +0200
@@ -66,6 +66,7 @@
 #include <sys/un.h>
 #include <sys/utsname.h>
 #include <net/if.h>
+#include <ifaddrs.h>
 
 /* Experimentally off - libc_hidden_proto(memcpy) */
 /* Experimentally off - libc_hidden_proto(memset) */
@@ -156,19 +157,29 @@
 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
 #endif
 
-
 static int addrconfig (sa_family_t af)
 {
     int s;
     int ret;
     int saved_errno = errno;
-    s = socket(af, SOCK_DGRAM, 0);
-    if (s < 0)
-	ret = (errno == EMFILE) ? 1 : 0;
+    bool seen_ipv4;
+    bool seen_ipv6;
+
+    __check_pf(&seen_ipv4, &seen_ipv6);
+    if (af == AF_INET) 
+	ret = (int)seen_ipv4;
+    else if (af == AF_INET6)
+	ret = (int)seen_ipv6;
     else
     {
-	close(s);
-	ret = 1;
+	s = socket(af, SOCK_DGRAM, 0);
+	if (s < 0)
+	    ret = (errno == EMFILE) ? 1 : 0;
+	else
+	{
+	    close(s);
+	    ret = 1;
+	}
     }
     __set_errno (saved_errno);
     return ret;
@@ -373,6 +384,9 @@
     int rc;
     int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) &&
 	(req->ai_flags & AI_V4MAPPED);
+    bool seen_ipv4;
+    bool seen_ipv6;
+    __check_pf(&seen_ipv4, &seen_ipv6);
 
     if (req->ai_protocol || req->ai_socktype)
     {
@@ -560,14 +574,16 @@
 
 #if defined __UCLIBC_HAS_IPV6__
 	    if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-		gethosts (AF_INET6, struct in6_addr);
+		if (!(req->ai_flags & AI_ADDRCONFIG) || seen_ipv6)
+		    gethosts (AF_INET6, struct in6_addr);
 #endif
 	    no_inet6_data = no_data;
 
 	    if (req->ai_family == AF_INET ||
 		(!v4mapped && req->ai_family == AF_UNSPEC) ||
 		(v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL))))
-		gethosts (AF_INET, struct in_addr);
+		if (!(req->ai_flags & AI_ADDRCONFIG) || seen_ipv4)
+		    gethosts (AF_INET, struct in_addr);
 
 	    if (no_data != 0 && no_inet6_data != 0)
 	    {
@@ -698,6 +714,14 @@
 #endif
 	    for (st2 = st; st2 != NULL; st2 = st2->next)
 	    {
+		if (req->ai_flags & AI_ADDRCONFIG) {
+		    if (family == AF_INET && !seen_ipv4)
+			break;
+#if defined __UCLIBC_HAS_IPV6__
+		    else if (family == AF_INET6 && !seen_ipv6)
+			break;
+#endif
+		}
 		*pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
 		if (*pai == NULL)
 		    return -EAI_MEMORY;
@@ -859,7 +883,10 @@
 	if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
 	{
 	    if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family))
+	    {
+		++g;
 		continue;
+	    }
 	    j++;
 	    if (pg == NULL || pg->gaih != g->gaih)
 	    {
diff -urN uClibc-22487/libc/inet/ifaddrs.c uClibc-22487-applied+fixed/libc/inet/ifaddrs.c
--- uClibc-22487/libc/inet/ifaddrs.c	2008-06-24 14:33:37.000000000 +0200
+++ uClibc-22487-applied+fixed/libc/inet/ifaddrs.c	2008-06-24 15:43:14.000000000 +0200
@@ -22,7 +22,7 @@
 #include <alloca.h>
 #include <assert.h>
 #include <errno.h>
-/*#include <ifaddrs.h>*/
+#include <ifaddrs.h>
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netpacket/packet.h>
@@ -57,7 +57,7 @@
 
 
 #if __ASSUME_NETLINK_SUPPORT
-#if 0 /* unused code */
+#ifdef __UCLIBC_SUPPORT_AI_ADDRCONFIG__
 /* struct to hold the data for one ifaddrs entry, so we can allocate
    everything at once.  */
 struct ifaddrs_storage
@@ -74,7 +74,7 @@
   } addr, netmask, broadaddr;
   char name[IF_NAMESIZE + 1];
 };
-#endif /* unused code */
+#endif /* __UCLIBC_SUPPORT_AI_ADDRCONFIG__ */
 
 
 void
@@ -324,7 +324,7 @@
 }
 
 
-#if 0 /* unused code */
+#ifdef __UCLIBC_SUPPORT_AI_ADDRCONFIG__
 /* We know the number of RTM_NEWLINK entries, so we reserve the first
    # of entries for this type. All RTM_NEWADDR entries have an index
    pointer to the RTM_NEWLINK entry.  To find the entry, create
@@ -562,7 +562,7 @@
 		      if ((rta_payload + 1) <= sizeof (ifas[ifa_index].name))
 			{
 			  ifas[ifa_index].ifa.ifa_name = ifas[ifa_index].name;
-			  *(char *) __mempcpy (ifas[ifa_index].name, rta_data,
+			  *(char *) mempcpy (ifas[ifa_index].name, rta_data,
 					       rta_payload) = '\0';
 			}
 		      break;
@@ -761,7 +761,7 @@
 		      if (rta_payload + 1 <= sizeof (ifas[ifa_index].name))
 			{
 			  ifas[ifa_index].ifa.ifa_name = ifas[ifa_index].name;
-			  *(char *) __mempcpy (ifas[ifa_index].name, rta_data,
+			  *(char *) mempcpy (ifas[ifa_index].name, rta_data,
 					       rta_payload) = '\0';
 			}
 		      else
@@ -872,6 +872,6 @@
 }
 #endif
 
-#endif /* unused code */
+#endif /* __UCLIBC_SUPPORT_AI_ADDRCONFIG__ */
 
 #endif /* __ASSUME_NETLINK_SUPPORT */
diff -urN uClibc-22487/libc/inet/ifaddrs.h uClibc-22487-applied+fixed/libc/inet/ifaddrs.h
--- uClibc-22487/libc/inet/ifaddrs.h	1970-01-01 01:00:00.000000000 +0100
+++ uClibc-22487-applied+fixed/libc/inet/ifaddrs.h	2008-06-24 15:25:01.000000000 +0200
@@ -0,0 +1,74 @@
+/* ifaddrs.h -- declarations for getting network interface addresses
+   Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _IFADDRS_H
+#define _IFADDRS_H	1
+
+#include <features.h>
+#include <sys/socket.h>
+
+__BEGIN_DECLS
+
+/* The `getifaddrs' function generates a linked list of these structures.
+   Each element of the list describes one network interface.  */
+struct ifaddrs
+{
+  struct ifaddrs *ifa_next;	/* Pointer to the next structure.  */
+
+  char *ifa_name;		/* Name of this network interface.  */
+  unsigned int ifa_flags;	/* Flags as from SIOCGIFFLAGS ioctl.  */
+
+  struct sockaddr *ifa_addr;	/* Network address of this interface.  */
+  struct sockaddr *ifa_netmask; /* Netmask of this interface.  */
+  union
+  {
+    /* At most one of the following two is valid.  If the IFF_BROADCAST
+       bit is set in `ifa_flags', then `ifa_broadaddr' is valid.  If the
+       IFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid.
+       It is never the case that both these bits are set at once.  */
+    struct sockaddr *ifu_broadaddr; /* Broadcast address of this interface. */
+    struct sockaddr *ifu_dstaddr; /* Point-to-point destination address.  */
+  } ifa_ifu;
+  /* These very same macros are defined by <net/if.h> for `struct ifaddr'.
+     So if they are defined already, the existing definitions will be fine.  */
+# ifndef ifa_broadaddr
+#  define ifa_broadaddr	ifa_ifu.ifu_broadaddr
+# endif
+# ifndef ifa_dstaddr
+#  define ifa_dstaddr	ifa_ifu.ifu_dstaddr
+# endif
+
+  void *ifa_data;		/* Address-specific data (may be unused).  */
+};
+
+
+/* Create a linked list of `struct ifaddrs' structures, one for each
+   network interface on the host machine.  If successful, store the
+   list in *IFAP and return 0.  On errors, return -1 and set `errno'.
+
+   The storage returned in *IFAP is allocated dynamically and can
+   only be properly freed by passing it to `freeifaddrs'.  */
+extern int getifaddrs (struct ifaddrs **__ifap) __THROW;
+
+/* Reclaim the storage allocated by a previous `getifaddrs' call.  */
+extern void freeifaddrs (struct ifaddrs *__ifa)  __THROW;
+
+__END_DECLS
+
+#endif /* ifaddrs.h */
diff -urN uClibc-22487/libc/inet/Makefile.in uClibc-22487-applied+fixed/libc/inet/Makefile.in
--- uClibc-22487/libc/inet/Makefile.in	2008-06-24 14:33:37.000000000 +0200
+++ uClibc-22487-applied+fixed/libc/inet/Makefile.in	2008-06-24 15:32:08.000000000 +0200
@@ -14,7 +14,7 @@
 ifneq ($(UCLIBC_HAS_IPV4)$(UCLIBC_HAS_IPV6),)
 CSRC +=	getservice.c getproto.c hostid.c getnetent.c getnetbynm.c getnetbyad.c \
 	inet_net.c herror.c if_index.c gai_strerror.c getaddrinfo.c \
-	ether_addr.c ntohl.c ifaddrs.c ntop.c
+	ether_addr.c ntohl.c ifaddrs.c ntop.c check_pf.c
 endif
 ifeq ($(UCLIBC_HAS_IPV6),y)
 CSRC += in6_addr.c
diff -urN uClibc-22487/libc/inet/netlinkaccess.h uClibc-22487-applied+fixed/libc/inet/netlinkaccess.h
--- uClibc-22487/libc/inet/netlinkaccess.h	2008-06-24 14:33:37.000000000 +0200
+++ uClibc-22487-applied+fixed/libc/inet/netlinkaccess.h	2008-06-24 15:39:54.000000000 +0200
@@ -62,13 +62,13 @@
 };
 
 
-#if 0 /* unused code */
+#ifdef __UCLIBC_SUPPORT_AI_ADDRCONFIG__
 #if __ASSUME_NETLINK_SUPPORT == 0
 extern smallint __no_netlink_support attribute_hidden;
 #else
 # define __no_netlink_support 0
 #endif
-#endif /* unused code */
+#endif /* __UCLIBC_SUPPORT_AI_ADDRCONFIG__ */
 
 
 extern int __netlink_open (struct netlink_handle *h) attribute_hidden;


More information about the uClibc mailing list