io: use bind() to check for IPv4/6 availability

Currently the test-io-channel-socket.c test uses getifaddrs
to see if an IPv4/6 address is present on any host NIC, as
a way to determine if IPv4/6 sockets can be used. This is
problematic because getifaddrs is not available on Win32.

Rather than testing indirectly via getifaddrs, just create
a socket and try to bind() to the loopback address instead.

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrange 2016-03-08 11:39:36 +00:00
parent c619644067
commit 0a27af918b

View file

@ -22,66 +22,49 @@
#include "io/channel-socket.h"
#include "io/channel-util.h"
#include "io-channel-helpers.h"
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
static int check_bind(struct sockaddr *sa, socklen_t salen, bool *has_proto)
{
#ifdef HAVE_IFADDRS_H
struct ifaddrs *ifaddr = NULL, *ifa;
struct addrinfo hints = { 0 };
struct addrinfo *ai = NULL;
int gaierr;
int fd;
*has_ipv4 = *has_ipv6 = false;
if (getifaddrs(&ifaddr) < 0) {
g_printerr("Failed to lookup interface addresses: %s\n",
strerror(errno));
fd = socket(sa->sa_family, SOCK_STREAM, 0);
if (fd < 0) {
return -1;
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) {
*has_ipv4 = true;
}
if (ifa->ifa_addr->sa_family == AF_INET6) {
*has_ipv6 = true;
if (bind(fd, sa, salen) < 0) {
close(fd);
if (errno == EADDRNOTAVAIL) {
*has_proto = false;
return 0;
}
return -1;
}
freeifaddrs(ifaddr);
close(fd);
*has_proto = true;
return 0;
}
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
{
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) },
};
struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_LOOPBACK_INIT,
};
gaierr = getaddrinfo("::1", NULL, &hints, &ai);
if (gaierr != 0) {
if (gaierr == EAI_ADDRFAMILY ||
gaierr == EAI_FAMILY ||
gaierr == EAI_NONAME) {
*has_ipv6 = false;
} else {
g_printerr("Failed to resolve ::1 address: %s\n",
gai_strerror(gaierr));
return -1;
}
if (check_bind((struct sockaddr *)&sin, sizeof(sin), has_ipv4) < 0) {
return -1;
}
if (check_bind((struct sockaddr *)&sin6, sizeof(sin6), has_ipv6) < 0) {
return -1;
}
freeaddrinfo(ai);
return 0;
#else
*has_ipv4 = *has_ipv6 = false;
return -1;
#endif
}