diff options
author | wtc%netscape.com <devnull@localhost> | 2002-06-13 20:50:23 +0000 |
---|---|---|
committer | wtc%netscape.com <devnull@localhost> | 2002-06-13 20:50:23 +0000 |
commit | 7b7cd5f35969827b5fdbfdc95e0a691ec2dafc55 (patch) | |
tree | 02a34e7da4c97c01202735096eab5c66cfd2f838 | |
parent | e6ba7f438e041bdfd1c91f7b834363147ed5f484 (diff) | |
download | nspr-hg-7b7cd5f35969827b5fdbfdc95e0a691ec2dafc55.tar.gz |
Bug 144886: On platforms with gethostbyname2, add the infrastructure for
PR_GetIPNodeByName to implement PR_AI_ADDRCONFIG correctly. Right now
only AIX implements the function to determine if the system has any IPv4
or IPv6 source address configured. On other platforms PR_GetIPNodeByName
still behaves as if the system had both IPv4 and IPv6 source addresses
configured.
Tag: NSPRPUB_PRE_4_2_CLIENT_BRANCH
-rw-r--r-- | pr/src/misc/prnetdb.c | 198 |
1 files changed, 192 insertions, 6 deletions
diff --git a/pr/src/misc/prnetdb.c b/pr/src/misc/prnetdb.c index 64006266..58342292 100644 --- a/pr/src/misc/prnetdb.c +++ b/pr/src/misc/prnetdb.c @@ -163,6 +163,175 @@ const PRIPv6Addr _pr_in6addr_loopback = {{{ 0, 0, 0, 0, #define _PR_IN6_V4MAPPED_TO_IPADDR(a) ((a)->pr_s6_addr32[3]) +#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2) + +/* + * The _pr_QueryNetIfs() function finds out if the system has + * IPv4 or IPv6 source addresses configured and sets _pr_have_inet_if + * and _pr_have_inet6_if accordingly. + * + * We have an implementation using SIOCGIFCONF ioctl and a + * default implementation that simply sets _pr_have_inet_if + * and _pr_have_inet6_if to true. A better implementation + * would be to use the routing sockets (see Chapter 17 of + * W. Richard Stevens' Unix Network Programming, Vol. 1, 2nd. Ed.) + */ + +static PRBool _pr_have_inet_if = PR_FALSE; +static PRBool _pr_have_inet6_if = PR_FALSE; + +#undef DEBUG_QUERY_IFS + +#if defined(AIX) + +/* + * Use SIOCGIFCONF ioctl on platforms that don't have routing + * sockets. Warning: whether SIOCGIFCONF ioctl returns AF_INET6 + * network interfaces is not portable. + * + * The _pr_QueryNetIfs() function is derived from the code in + * src/lib/libc/net/getifaddrs.c in BSD Unix and the code in + * Section 16.6 of W. Richard Stevens' Unix Network Programming, + * Vol. 1, 2nd. Ed. + */ + +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if.h> + +#ifdef DEBUG_QUERY_IFS +static void +_pr_PrintIfreq(struct ifreq *ifr) +{ + PRNetAddr addr; + struct sockaddr *sa; + const char* family; + char addrstr[64]; + + sa = &ifr->ifr_addr; + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + family = "inet"; + memcpy(&addr.inet.ip, &sin->sin_addr, sizeof(sin->sin_addr)); + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + family = "inet6"; + memcpy(&addr.ipv6.ip, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); + } else { + return; /* skip if not AF_INET or AF_INET6 */ + } + addr.raw.family = sa->sa_family; + PR_NetAddrToString(&addr, addrstr, sizeof(addrstr)); + printf("%s: %s %s\n", ifr->ifr_name, family, addrstr); +} +#endif + +static void +_pr_QueryNetIfs(void) +{ + int sock; + int rv; + struct ifconf ifc; + struct ifreq *ifr; + struct ifreq *lifr; + PRUint32 len, lastlen; + char *buf; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + return; + } + + /* Issue SIOCGIFCONF request in a loop. */ + lastlen = 0; + len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ + for (;;) { + buf = PR_Malloc(len); + if (NULL == buf) { + close(sock); + return; + } + ifc.ifc_buf = buf; + ifc.ifc_len = len; + rv = ioctl(sock, SIOCGIFCONF, &ifc); + if (rv < 0) { + if (errno != EINVAL || lastlen != 0) { + close(sock); + PR_Free(buf); + return; + } + } else { + if (ifc.ifc_len == lastlen) + break; /* success, len has not changed */ + lastlen = ifc.ifc_len; + } + len += 10 * sizeof(struct ifreq); /* increment */ + PR_Free(buf); + } + close(sock); + + ifr = ifc.ifc_req; + lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; + + while (ifr < lifr) { + struct sockaddr *sa; + int sa_len; + +#ifdef DEBUG_QUERY_IFS + _pr_PrintIfreq(ifr); +#endif + sa = &ifr->ifr_addr; + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { + _pr_have_inet_if = PR_TRUE; + } + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; + if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) { + _pr_have_inet6_if = PR_TRUE; + } + } + +#ifdef _PR_HAVE_SOCKADDR_LEN + sa_len = PR_MAX(sa->sa_len, sizeof(struct sockaddr)); +#else + switch (sa->sa_family) { +#ifdef AF_LINK + case AF_LINK: + sa_len = sizeof(struct sockaddr_dl); + break; +#endif + case AF_INET6: + sa_len = sizeof(struct sockaddr_in6); + break; + default: + sa_len = sizeof(struct sockaddr); + break; + } +#endif + ifr = (struct ifreq *)(((char *)sa) + sa_len); + } + PR_Free(buf); +} + +#else /* default */ + +/* + * Emulate the code in NSPR 4.2 or older. PR_GetIPNodeByName behaves + * as if the system had both IPv4 and IPv6 source addresses configured. + */ +static void +_pr_QueryNetIfs(void) +{ + _pr_have_inet_if = PR_TRUE; + _pr_have_inet6_if = PR_TRUE; +} + +#endif + +#endif /* _PR_INET6 && _PR_HAVE_GETHOSTBYNAME2 */ + void _PR_InitNet(void) { #if defined(XP_UNIX) @@ -180,6 +349,15 @@ void _PR_InitNet(void) #if !defined(_PR_HAVE_GETPROTO_R) _getproto_lock = PR_NewLock(); #endif +#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2) + _pr_QueryNetIfs(); +#ifdef DEBUG_QUERY_IFS + if (_pr_have_inet_if) + printf("Have IPv4 source address\n"); + if (_pr_have_inet6_if) + printf("Have IPv6 source address\n"); +#endif +#endif } void _PR_CleanupNet(void) @@ -573,11 +751,15 @@ PR_IMPLEMENT(PRStatus) PR_GetIPNodeByName( LOCK_DNS(); if (af == PR_AF_INET6) { + if ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet6_if) + { #ifdef _PR_INET6_PROBE - if (_pr_ipv6_is_present == PR_TRUE) + if (_pr_ipv6_is_present == PR_TRUE) #endif - h = GETHOSTBYNAME2(name, AF_INET6); - if ((NULL == h) && (flags & PR_AI_V4MAPPED)) + h = GETHOSTBYNAME2(name, AF_INET6); + } + if ((NULL == h) && (flags & PR_AI_V4MAPPED) + && ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet_if)) { did_af_inet = PR_TRUE; h = GETHOSTBYNAME2(name, AF_INET); @@ -585,8 +767,11 @@ PR_IMPLEMENT(PRStatus) PR_GetIPNodeByName( } else { - did_af_inet = PR_TRUE; - h = GETHOSTBYNAME2(name, af); + if ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet_if) + { + did_af_inet = PR_TRUE; + h = GETHOSTBYNAME2(name, af); + } } #elif defined(_PR_HAVE_GETIPNODEBYNAME) h = getipnodebyname(name, md_af, tmp_flags, &error_num); @@ -639,7 +824,8 @@ PR_IMPLEMENT(PRStatus) PR_GetIPNodeByName( #endif #if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2) if ((PR_SUCCESS == rv) && (flags & PR_AI_V4MAPPED) - && (flags & (PR_AI_ALL|PR_AI_ADDRCONFIG)) + && ((flags & PR_AI_ALL) + || ((flags & PR_AI_ADDRCONFIG) && _pr_have_inet_if)) && !did_af_inet && (h = GETHOSTBYNAME2(name, AF_INET)) != 0) { rv = AppendV4AddrsToHostent(h, &buf, &bufsize, hp); if (PR_SUCCESS != rv) |