Browse Source

IPv6: create new socket if IPv6 bind fails (#77).

tags/v1.4.90
Tim Waugh 9 years ago
parent
commit
e4d9726c19
1 changed files with 90 additions and 93 deletions
  1. 90
    93
      common/network/TcpSocket.cxx

+ 90
- 93
common/network/TcpSocket.cxx View File

@@ -399,125 +399,122 @@ int TcpSocket::getSockPort(int sock)
}
}

TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
int sock, bool close_) : closeFd(close_)
static int bindIPv6 (const char *listenaddr,
int port,
bool localhostOnly)
{
if (sock != -1) {
fd = sock;
return;
}

bool use_ipv6;
int af;
#ifdef HAVE_GETADDRINFO
use_ipv6 = true;
af = AF_INET6;
struct sockaddr_in6 addr6;
socklen_t sa_len;
int fd;

if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
return -1;

#ifdef IPV6_V6ONLY
// - We made an IPv6-capable socket, and we need it to do IPv4 too
int opt = 0;
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof(opt));
#else
use_ipv6 = false;
af = AF_INET;
vlog.error("IPV6_V6ONLY support is missing. "
"IPv4 clients may not be able to connect.");
#endif

initSockets();
if ((fd = socket(af, SOCK_STREAM, 0)) < 0) {
// - Socket creation failed
if (use_ipv6) {
// - Trying to make an IPv6-capable socket failed - try again, IPv4-only
use_ipv6 = false;
af = AF_INET;
fd = socket(af, SOCK_STREAM, 0);
memset(&addr6, 0, (sa_len = sizeof(addr6)));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);

if (localhostOnly)
addr6.sin6_addr = in6addr_loopback;
else if (listenaddr != NULL) {
#ifdef HAVE_INET_PTON
if (inet_pton(AF_INET6, listenaddr, &addr6.sin6_addr) != 1) {
closesocket(fd);
return -1;
}
if (fd < 0)
throw SocketException("unable to create listening socket", errorNumber);
} else {
// - Socket creation succeeded
if (use_ipv6) {
#ifdef IPV6_V6ONLY
// - We made an IPv6-capable socket, and we need it to do IPv4 too
int opt = 0;
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof(opt));
#else
vlog.error("IPV6_V6ONLY support is missing. "
"IPv4 clients may not be able to connect.");
// Unable to parse without inet_pton
closesocket(fd);
return -1;
#endif
}
}

#ifndef WIN32
// - By default, close the socket on exec()
fcntl(fd, F_SETFD, FD_CLOEXEC);

int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(char *)&one, sizeof(one)) < 0) {
int e = errorNumber;
if (bind(fd, (struct sockaddr *) &addr6, sa_len) == -1) {
closesocket(fd);
throw SocketException("unable to create listening socket", e);
return -1;
}
#endif

// - Bind it to the desired port
return fd;
#else
return -1;
#endif /* HAVE_GETADDRINFO */
}

static int bindIPv4 (const char *listenaddr,
int port,
bool localhostOnly)
{
struct sockaddr_in addr;
#ifdef HAVE_GETADDRINFO
struct sockaddr_in6 addr6;
#endif
struct sockaddr *sa;
int sa_len;
socklen_t sa_len;
int fd;

#ifdef HAVE_GETADDRINFO
if (use_ipv6) {
memset(&addr6, 0, (sa_len = sizeof(addr6)));
addr6.sin6_family = af;
addr6.sin6_port = htons(port);
if (localhostOnly)
addr6.sin6_addr = in6addr_loopback;
else if (listenaddr != NULL) {
#ifdef HAVE_INET_PTON
if (inet_pton(AF_INET6, listenaddr, &addr6.sin6_addr) != 1)
use_ipv6 = false;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1;
memset(&addr, 0, (sa_len = sizeof(addr)));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (localhostOnly)
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
else if (listenaddr != NULL) {
#ifdef HAVE_INET_ATON
if (inet_aton(listenaddr, &addr.sin_addr) == 0)
#else
// Unable to parse without inet_pton
use_ipv6 = false;
/* Some systems (e.g. Windows) do not have inet_aton, sigh */
if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
#endif
}
{
closesocket(fd);
throw Exception("invalid network interface address: %s", listenaddr);
}
} else
/* Bind to 0.0.0.0 by default. */
addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (use_ipv6)
sa = (struct sockaddr *)&addr6;
if (bind(fd, (struct sockaddr *) &addr, sa_len) == -1) {
closesocket(fd);
return -1;
}
#endif

if (!use_ipv6) {
memset(&addr, 0, (sa_len = sizeof(addr)));
addr.sin_family = af;
addr.sin_port = htons(port);
return fd;
}

if (localhostOnly) {
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
} else if (listenaddr != NULL) {
#ifdef HAVE_INET_ATON
if (inet_aton(listenaddr, &addr.sin_addr) == 0)
#else
/* Some systems (e.g. Windows) do not have inet_aton, sigh */
if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
#endif
{
closesocket(fd);
throw Exception("invalid network interface address: %s", listenaddr);
}
} else
/* Bind to 0.0.0.0 by default. */
addr.sin_addr.s_addr = htonl(INADDR_ANY);

sa = (struct sockaddr *)&addr;
TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
int sock, bool close_) : closeFd(close_)
{
if (sock != -1) {
fd = sock;
return;
}

addr.sin_port = htons(port);
if (bind(fd, sa, sa_len) < 0) {
initSockets();
if ((fd = bindIPv6 (listenaddr, port, localhostOnly)) < 0)
if ((fd = bindIPv4 (listenaddr, port, localhostOnly)) < 0)
throw SocketException("unable to create listening socket", errorNumber);

#ifndef WIN32
// - By default, close the socket on exec()
fcntl(fd, F_SETFD, FD_CLOEXEC);

int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(char *)&one, sizeof(one)) < 0) {
int e = errorNumber;
closesocket(fd);
throw SocketException("unable to bind listening socket", e);
throw SocketException("unable to create listening socket", e);
}
#endif

// - Set it to be a listening socket
if (listen(fd, 5) < 0) {

Loading…
Cancel
Save