From 2c64e05d497b460f95cd39ea20c4e805b2c8c402 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 11 Mar 2015 20:13:16 -0700 Subject: QNativeSocketEngine Windows: bring bind() in line with Unix The IPv4+IPv6 dual stack code that has been in Qt since 5.0 has been giving test failures for tst_qudpsocket: some binds that shouldn't succeed do succeed. Instead, copy the core code from the Unix version so the two OSes will behave the same way. The one difference in behavior between Windows and Unix is that on Unix you can bind an IPv4 address to a multicast IP and on Windows you can't. So I left the "correction" that was in the original code, but I'm unsure if it is the right thing to do. Are people expecting to join the multicast group this way? Change-Id: Iee8cbc07c4434ce9b560ffff13caa1c3d5a7e8fd Reviewed-by: Richard J. Moore --- src/network/socket/qnativesocketengine_win.cpp | 44 +++++++++++--------------- 1 file changed, 19 insertions(+), 25 deletions(-) (limited to 'src/network') diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 9dba6693c8..aecae40a44 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -775,42 +775,36 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &a, quint16 port) { QHostAddress address = a; - DWORD ipv6only = 0; - switch (address.protocol()) { - case QAbstractSocket::IPv6Protocol: - if (address.toIPv6Address()[0] == 0xff) { - // binding to a multicast address - address = QHostAddress(QHostAddress::AnyIPv6); - } - //This is default in current windows versions, it may change in future so set it explicitly - if (QSysInfo::windowsVersion() >= QSysInfo::WV_6_0) { - ipv6only = 1; - ipv6only = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) ); - } - break; - case QAbstractSocket::IPv4Protocol: + if (address.protocol() == QAbstractSocket::IPv4Protocol) { if ((address.toIPv4Address() & 0xffff0000) == 0xefff0000) { // binding to a multicast address address = QHostAddress(QHostAddress::AnyIPv4); } - break; - case QAbstractSocket::AnyIPProtocol: - if (QSysInfo::windowsVersion() >= QSysInfo::WV_6_0) { - ipv6only = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) ); - } else { - address = QHostAddress(QHostAddress::AnyIPv4); //xp/WS2003 and earlier don't support dual stack, so bind to IPv4 - socketProtocol = QAbstractSocket::IPv4Protocol; - } - break; - default: - break; } qt_sockaddr aa; QT_SOCKLEN_T sockAddrSize = 0; setPortAndAddress(port, address, &aa, &sockAddrSize); + if (aa.a.sa_family == AF_INET6) { + // The default may change in future, so set it explicitly + int ipv6only = 0; + if (address.protocol() == QAbstractSocket::IPv6Protocol) + ipv6only = 1; + ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) ); + } + + int bindResult = ::bind(socketDescriptor, &aa.a, sockAddrSize); + if (bindResult == SOCKET_ERROR && WSAGetLastError() == WSAEAFNOSUPPORT + && address.protocol() == QAbstractSocket::AnyIPProtocol) { + // retry with v4 + aa.a4.sin_family = AF_INET; + aa.a4.sin_port = htons(port); + aa.a4.sin_addr.s_addr = htonl(address.toIPv4Address()); + sockAddrSize = sizeof(aa.a4); + bindResult = ::bind(socketDescriptor, &aa.a, sockAddrSize); + } if (bindResult == SOCKET_ERROR) { int err = WSAGetLastError(); WS_ERROR_DEBUG(err); -- cgit v1.2.3