diff --git a/src/murmur/Meta.cpp b/src/murmur/Meta.cpp index 2369ee25e..d22ac7c6c 100644 --- a/src/murmur/Meta.cpp +++ b/src/murmur/Meta.cpp @@ -125,25 +125,6 @@ T MetaParams::typeCheckedFromSettings(const QString &name, const T &defaultValue return cfgVariable.value(); } -bool MetaParams::hasDualStackSupport() { -#ifdef Q_OS_UNIX - int s = ::socket(AF_INET6, SOCK_STREAM, 0); - if (s != -1) { - int ipv6only = 0; - socklen_t optlen = sizeof(ipv6only); - if (getsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, &optlen) == 0) { - if (ipv6only == 0) - return true; - } - close(s); - } - return false; -#else - // Assume dual-stacked for non-unix systems - return true; -#endif -} - void MetaParams::read(QString fname) { if (fname.isEmpty()) { QStringList datapaths; @@ -262,7 +243,7 @@ void MetaParams::read(QString fname) { #if QT_VERSION >= 0x050000 if (hasipv6) { - if (hasDualStackSupport() && hasipv4) { + if (SslServer::hasDualStackSupport() && hasipv4) { qlBind << QHostAddress(QHostAddress::Any); hasipv4 = false; // No need to add a separate ipv4 socket } else { @@ -281,7 +262,7 @@ void MetaParams::read(QString fname) { if (hasipv6) { qlBind << QHostAddress(QHostAddress::AnyIPv6); - if (hasDualStackSupport() && hasipv4) { + if (SslServer::hasDualStackSupport() && hasipv4) { hasipv4 = false; // No need to add a separate ipv4 socket } } diff --git a/src/murmur/Meta.h b/src/murmur/Meta.h index a3f67bb7f..884e86038 100644 --- a/src/murmur/Meta.h +++ b/src/murmur/Meta.h @@ -138,9 +138,6 @@ public: private: template T typeCheckedFromSettings(const QString &name, const T &variable); - - /// Checks whether the AF_INET6 socket has dual-stack support - static bool hasDualStackSupport(); }; class Meta : public QObject { diff --git a/src/murmur/Server.cpp b/src/murmur/Server.cpp index 7485c88a4..10e7035b6 100644 --- a/src/murmur/Server.cpp +++ b/src/murmur/Server.cpp @@ -73,6 +73,41 @@ void ExecEvent::execute() { SslServer::SslServer(QObject *p) : QTcpServer(p) { } +bool SslServer::hasDualStackSupport() { + // Create a AF_INET6 socket and try to switch off IPV6_V6ONLY. This + // should only fail if the system does not support dual-stack mode + // for this socket type. + + bool result = false; +#ifdef Q_OS_UNIX + int s = ::socket(AF_INET6, SOCK_STREAM, 0); +#else + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + if (WSAStartup(wVersionRequested, &wsaData) != 0) { + // Seems like we won't be doing any network stuff anyways + return false; + } + + SOCKET s = ::WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); +#endif + + if (s != -1) { // Equals INVALID_SOCKET + const int ipv6only = 0; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&ipv6only, sizeof(ipv6only)) == 0) { + result = true; + } +#ifdef Q_OS_UNIX + ::close(s); + } +#else + closesocket(s); + } + WSACleanup(); +#endif + return result; +} + #if QT_VERSION >= 0x050000 void SslServer::incomingConnection(qintptr v) { #else @@ -166,6 +201,20 @@ Server::Server(int snum, QObject *p) : QThread(p) { bValid = false; return; } else { + if (addr.ss_family == AF_INET6) { + // Copy IPV6_V6ONLY attribute from tcp socket, it defaults to nonzero on Windows + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms738574%28v=vs.85%29.aspx + // This will fail for WindowsXP which is ok. Our TCP code will have split that up + // into two sockets. + int ipv6only = 0; + socklen_t optlen = sizeof(ipv6only); + if (::getsockopt(tcpsock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, &optlen) == 0) { + if (::setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&ipv6only, optlen) == SOCKET_ERROR) { + log(QString("Failed to copy IPV6_V6ONLY socket attribute from tcp to udp socket")); + } + } + } + if (::bind(sock, reinterpret_cast(&addr), len) == SOCKET_ERROR) { log(QString("Failed to bind UDP Socket to %1").arg(addressToString(ss->serverAddress(), usPort))); } else { diff --git a/src/murmur/Server.h b/src/murmur/Server.h index 1fb9806b6..19e26706e 100644 --- a/src/murmur/Server.h +++ b/src/murmur/Server.h @@ -99,6 +99,9 @@ class SslServer : public QTcpServer { public: QSslSocket *nextPendingSSLConnection(); SslServer(QObject *parent = NULL); + + /// Checks whether the AF_INET6 socket on this system has dual-stack support. + static bool hasDualStackSupport(); }; #define EXEC_QEVENT (QEvent::User + 959)