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.
This commit is contained in:
wtc%netscape.com 2002-06-13 20:47:39 +00:00
Родитель bd3ae1eb74
Коммит ef6de7f68e
1 изменённых файлов: 192 добавлений и 6 удалений

Просмотреть файл

@ -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)