From: Tim Waugh Date: Fri, 21 Nov 2014 16:07:34 +0000 (+0000) Subject: IPv6: create new socket if IPv6 bind fails (#77). X-Git-Tag: v1.4.90~68 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e4d9726c19d132cdb8794c51562e634fa3581d06;p=tigervnc.git IPv6: create new socket if IPv6 bind fails (#77). --- diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx index 0db6ff6b..f2d76bfa 100644 --- a/common/network/TcpSocket.cxx +++ b/common/network/TcpSocket.cxx @@ -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) {