mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
Fix dual-stack UDP on Windows and improve dual-stack detection
This patch fixes dual-stack UDP on Windows as well as for unsual Linux configurations by ensuring the UDP sockets we create inherit their IPV6_V6ONLY sockopt from their corresponding TCP socket. Previously on systems where IPV6_V6ONLY was enabled by default (e.g. Windows) we would incorrectly create an IPv6 only socket even though our TCP socket and the system are dual-stacked. The dual-stack detection moved from Meta.h to Server.h into the SslServer class where it is a better fit. Also modified the function to instead of querying the value of IPV6_V6ONLY on a test socket it now tries to actively disable it. While previously dual stack support was only detected if it was the default configuration for that system, it should now be detected in all cases. The function also now performs the same check on Windows socket where the default actually is to have IPV6_V6ONLY enabled.
This commit is contained in:
parent
9a426b1df7
commit
e934c1e648
@ -125,25 +125,6 @@ T MetaParams::typeCheckedFromSettings(const QString &name, const T &defaultValue
|
||||
return cfgVariable.value<T>();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,9 +138,6 @@ public:
|
||||
private:
|
||||
template <class T>
|
||||
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 {
|
||||
|
||||
@ -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<sockaddr *>(&addr), len) == SOCKET_ERROR) {
|
||||
log(QString("Failed to bind UDP Socket to %1").arg(addressToString(ss->serverAddress(), usPort)));
|
||||
} else {
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user