[PATCH] make getaddrinfo hint AI_ADDRCONFIG work
Ricard Wanderlof
ricard.wanderlof at axis.com
Thu Feb 21 14:30:19 UTC 2008
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 as 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, while still touching the existing
code as little as possible. It has been tested and works and I would like
to commit it, but comments would be valued.
I've also included the patch as an attachement. It was made against svn
21085, head of the development tree att the time of writing.
Some comments on the changes:
First of all, two new functions ipv4_available() and ipv6_available()
check if ipv4 or ipb6 is enabled. For ipv6, /proc/net/if_inet6 is checked.
For ipv4 there is no corresponding file, instead /proc/net/route is
checked to determine there is a route defined (i.e. the file has one
more line with data besides the header line), as this indicates ipv4 is
available.
The addrconfig() and gaih_inet() functions have then been modified to use
these two functions when AF_INET or AF_INET6 is specified, respectively.
For addrconfig() it has been noted that just being able to create a
SOCK_DGRAM socket of the corresponding address family type is not enough
to determine the existence of correspondingly configured interfaces.
The final block in the patch fixes a bug which caused an infinite loop
when hints->ai_flags had the AI_ADDRCONFIG bit set. (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) ).
/Ricard
Index: libc/inet/getaddrinfo.c
===================================================================
--- libc/inet/getaddrinfo.c (revision 21085)
+++ libc/inet/getaddrinfo.c (working copy)
@@ -165,20 +165,72 @@
{ 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
#endif
+static int ipv4_available(void) {
+ int fd;
+ size_t bytes = 1;
+ void *pos;
+ char buf[10];
+ int firstline = 1;
+ int routefound = 0;
+ fd = open("/proc/net/route", O_RDONLY);
+ // If proc is not mounted assume IPv4 is available
+ if (fd < 0)
+ return 1;
+
+ while (routefound == 0 && bytes > 0)
+ {
+ bytes = read(fd, buf, sizeof(buf));
+ pos = memchr(buf, '\n', bytes);
+ if (pos != NULL)
+ if (!firstline)
+ routefound = 1;
+ else
+ firstline = 0;
+ }
+ close(fd);
+ return routefound;
+}
+
+#if __UCLIBC_HAS_IPV6__
+static int ipv6_available(void) {
+ int fd;
+ size_t bytes = 0;
+ char buf[1];
+
+ fd = open("/proc/net/if_inet6", O_RDONLY);
+ // If proc is not mounted assume IPv4 is available
+ if (fd < 0)
+ return 1;
+
+ bytes = read(fd, buf, sizeof(buf));
+ close(fd);
+ return (bytes > 0) ? 1 : 0;
+}
+#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;
- else
- {
- close(s);
- ret = 1;
+ if (af == AF_INET)
+ ret = ipv4_available();
+#if __UCLIBC_HAS_IPV6__
+ else if (af == AF_INET6)
+ ret = ipv6_available();
+#endif
+ else {
+ 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;
}
@@ -383,6 +435,11 @@
int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) &&
(req->ai_flags & AI_V4MAPPED);
+#if __UCLIBC_HAS_IPV6__
+ int ipv6 = ipv6_available();
+#endif
+ int ipv4 = ipv4_available();
+
if (req->ai_protocol || req->ai_socktype)
{
++tp;
@@ -569,14 +626,16 @@
#if __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) || 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) || ipv4)
+ gethosts (AF_INET, struct in_addr);
if (no_data != 0 && no_inet6_data != 0)
{
@@ -704,6 +763,13 @@
for (st2 = st; st2 != NULL; st2 = st2->next)
{
+ if (req->ai_flags & AI_ADDRCONFIG)
+ if (family == AF_INET && !ipv4)
+ break;
+#if __UCLIBC_HAS_IPV6__
+ else if (family == AF_INET6 && !ipv6)
+ break;
+#endif
*pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
if (*pai == NULL)
return -EAI_MEMORY;
@@ -862,7 +928,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)
{
--
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 --------------
Index: libc/inet/getaddrinfo.c
===================================================================
--- libc/inet/getaddrinfo.c (revision 21085)
+++ libc/inet/getaddrinfo.c (working copy)
@@ -165,20 +165,72 @@
{ 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
#endif
+static int ipv4_available(void) {
+ int fd;
+ size_t bytes = 1;
+ void *pos;
+ char buf[10];
+ int firstline = 1;
+ int routefound = 0;
+ fd = open("/proc/net/route", O_RDONLY);
+ // If proc is not mounted assume IPv4 is available
+ if (fd < 0)
+ return 1;
+
+ while (routefound == 0 && bytes > 0)
+ {
+ bytes = read(fd, buf, sizeof(buf));
+ pos = memchr(buf, '\n', bytes);
+ if (pos != NULL)
+ if (!firstline)
+ routefound = 1;
+ else
+ firstline = 0;
+ }
+ close(fd);
+ return routefound;
+}
+
+#if __UCLIBC_HAS_IPV6__
+static int ipv6_available(void) {
+ int fd;
+ size_t bytes = 0;
+ char buf[1];
+
+ fd = open("/proc/net/if_inet6", O_RDONLY);
+ // If proc is not mounted assume IPv4 is available
+ if (fd < 0)
+ return 1;
+
+ bytes = read(fd, buf, sizeof(buf));
+ close(fd);
+ return (bytes > 0) ? 1 : 0;
+}
+#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;
- else
- {
- close(s);
- ret = 1;
+ if (af == AF_INET)
+ ret = ipv4_available();
+#if __UCLIBC_HAS_IPV6__
+ else if (af == AF_INET6)
+ ret = ipv6_available();
+#endif
+ else {
+ 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;
}
@@ -383,6 +435,11 @@
int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) &&
(req->ai_flags & AI_V4MAPPED);
+#if __UCLIBC_HAS_IPV6__
+ int ipv6 = ipv6_available();
+#endif
+ int ipv4 = ipv4_available();
+
if (req->ai_protocol || req->ai_socktype)
{
++tp;
@@ -569,14 +626,16 @@
#if __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) || 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) || ipv4)
+ gethosts (AF_INET, struct in_addr);
if (no_data != 0 && no_inet6_data != 0)
{
@@ -704,6 +763,13 @@
for (st2 = st; st2 != NULL; st2 = st2->next)
{
+ if (req->ai_flags & AI_ADDRCONFIG)
+ if (family == AF_INET && !ipv4)
+ break;
+#if __UCLIBC_HAS_IPV6__
+ else if (family == AF_INET6 && !ipv6)
+ break;
+#endif
*pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
if (*pai == NULL)
return -EAI_MEMORY;
@@ -862,7 +928,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)
{
More information about the uClibc
mailing list