diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2017-11-28 16:49:00 -0800 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2017-12-29 00:10:54 +0000 |
commit | 198c59dbce63e97ef0fa8f75236ee8b2887997c2 (patch) | |
tree | d39cf80c4c4a74ed9d9e3ea229ae4ad293fc4e3c /src/network/socket/qnativesocketengine_win.cpp | |
parent | 656804b9645a83430dcd0929c49b4065d43d7990 (diff) |
tst_QUdpSocket: always use an interface when binding to IPv6
Binding without an interface and expecting the OS to select something is
not supported in all OSes. On FreeBSD, I keep getting EADDRNOTAVAIL. So
modify our test to only join, leave and send to multicast groups with an
interface selection.
With this, all tests either pass or are skipped for me on Linux,
FreeBSD, and macOS. On Windows, this revealed an inconsistency in
behavior, which this commit adds a workaround for.
Change-Id: Ifb5969bf206e4cd7b14efffd14fb6815456494d2
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/network/socket/qnativesocketengine_win.cpp')
-rw-r--r-- | src/network/socket/qnativesocketengine_win.cpp | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 747fdeb967..6a091209be 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -1319,6 +1319,9 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l setPortAndAddress(header.destinationPort, header.destinationAddress, &aa, &msg.namelen); + uint oldIfIndex = 0; + bool mustSetIpv6MulticastIf = false; + if (msg.namelen == sizeof(aa.a6)) { // sending IPv6 if (header.hopLimit != -1) { @@ -1330,7 +1333,7 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr) + WSA_CMSG_SPACE(sizeof(int))); } - if (header.ifindex != 0 || !header.senderAddress.isNull()) { + if (!header.senderAddress.isNull()) { struct in6_pktinfo *data = reinterpret_cast<in6_pktinfo *>(WSA_CMSG_DATA(cmsgptr)); memset(data, 0, sizeof(*data)); msg.Control.len += WSA_CMSG_SPACE(sizeof(*data)); @@ -1343,6 +1346,21 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l memcpy(&data->ipi6_addr, &tmp, sizeof(tmp)); cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr) + WSA_CMSG_SPACE(sizeof(*data))); + } else if (header.ifindex != 0) { + // Unlike other operating systems, setting the interface index in the in6_pktinfo + // structure above and leaving the ipi6_addr set to :: will cause the packets to be + // sent with source address ::. So we have to use IPV6_MULTICAST_IF, which MSDN is + // quite clear that "This option does not change the default interface for receiving + // IPv6 multicast traffic." + QT_SOCKOPTLEN_T len = sizeof(oldIfIndex); + if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, + reinterpret_cast<char *>(&oldIfIndex), &len) == -1 + || ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, + reinterpret_cast<const char *>(&header.ifindex), sizeof(header.ifindex)) == -1) { + setError(QAbstractSocket::NetworkError, SendDatagramErrorString); + return -1; + } + mustSetIpv6MulticastIf = true; } } else { // sending IPv4 @@ -1396,6 +1414,12 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l ret = qint64(bytesSent); } + if (mustSetIpv6MulticastIf) { + // undo what we did above + ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, + reinterpret_cast<char *>(&oldIfIndex), sizeof(oldIfIndex)); + } + #if defined (QNATIVESOCKETENGINE_DEBUG) qDebug("QNativeSocketEnginePrivate::nativeSendDatagram(%p \"%s\", %lli, \"%s\", %i) == %lli", data, qt_prettyDebug(data, qMin<qint64>(len, 16), len).data(), len, |