Fix multicast join/leave when binding to QHostAddress::Any.
authorRobin Burchell <robin+qt@viroteck.net>
Fri, 30 Mar 2012 14:23:24 +0000 (16:23 +0200)
committerQt by Nokia <qt-info@nokia.com>
Mon, 2 Apr 2012 10:49:38 +0000 (12:49 +0200)
On OS X and Windows, this was not working, because the socket was being bound
in v6 mode (due to ::Any being for dual mode), but the address passed was a v4
address, meaning it took the wrong codepath. Linux, strangely, apparently works
anyway.

This is fixable in OS X (by using the v6 join path when bound in v6/dual mode),
but the same fix doesn't work on Windows, failing with WSAEADDRNOTAVAIL.

Don't allow this behaviour, and provide a sane error message telling the user
what to do instead.

Done-with: Shane Kearns
Task-number: QTBUG-25047
Change-Id: Iaf5bbee82e13ac92e11b60c558f5af9ce26f474b
Reviewed-by: Shane Kearns <shane.kearns@accenture.com>
examples/network/multicastreceiver/receiver.cpp
src/network/socket/qnativesocketengine.cpp
src/network/socket/qudpsocket.cpp
tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp

index 6448817..041812f 100644 (file)
@@ -52,7 +52,7 @@ Receiver::Receiver(QWidget *parent)
     quitButton = new QPushButton(tr("&Quit"));
 
     udpSocket = new QUdpSocket(this);
-    udpSocket->bind(45454, QUdpSocket::ShareAddress);
+    udpSocket->bind(QHostAddress::AnyIPv4, 45454, QUdpSocket::ShareAddress);
     udpSocket->joinMulticastGroup(groupAddress);
 
     connect(udpSocket, SIGNAL(readyRead()),
index a34b19f..8fac361 100644 (file)
@@ -637,6 +637,19 @@ bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress,
     Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false);
     Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false);
     Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false);
+
+    // if the user binds a socket to an IPv6 address (or QHostAddress::Any) and
+    // then attempts to join an IPv4 multicast group, this won't work on
+    // Windows. In order to make this cross-platform, we warn & fail on all
+    // platforms.
+    if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol &&
+        (d->socketProtocol == QAbstractSocket::IPv6Protocol ||
+         d->socketProtocol == QAbstractSocket::AnyIPProtocol)) {
+        qWarning("QAbstractSocket: cannot bind to QHostAddress::Any (or an IPv6 address) and join an IPv4 multicast group");
+        qWarning("QAbstractSocket: bind to QHostAddress::AnyIPv4 instead if you want to do this");
+        return false;
+    }
+
     return d->nativeJoinMulticastGroup(groupAddress, iface);
 }
 
index ec751c2..23c1956 100644 (file)
@@ -178,6 +178,10 @@ QUdpSocket::~QUdpSocket()
     interface chosen by the operating system. The socket must be in BoundState,
     otherwise an error occurs.
 
+    Note that if you are attempting to join an IPv4 group, your socket must not
+    be bound using IPv6 (or in dual mode, using QHostAddress::Any). You must use
+    QHostAddress::AnyIPv4 instead.
+
     This function returns true if successful; otherwise it returns false
     and sets the socket error accordingly.
 
index c53450e..f63c593 100644 (file)
@@ -1202,6 +1202,7 @@ void tst_QUdpSocket::multicast_data()
     QHostAddress groupAddress = QHostAddress("239.255.118.62");
     QHostAddress any6Address = QHostAddress(QHostAddress::AnyIPv6);
     QHostAddress group6Address = QHostAddress("FF01::114");
+    QHostAddress dualAddress = QHostAddress(QHostAddress::Any);
 
     QTest::addColumn<QHostAddress>("bindAddress");
     QTest::addColumn<bool>("bindResult");
@@ -1213,6 +1214,8 @@ void tst_QUdpSocket::multicast_data()
     QTest::newRow("valid bind, group ipv6 address") << any6Address << true << group6Address << true;
     QTest::newRow("valid bind, invalid group ipv6 address") << any6Address << true << any6Address << false;
     QTest::newRow("same bind, group ipv6 address") << group6Address << true << group6Address << true;
+    QTest::newRow("dual bind, group ipv4 address") << dualAddress << true << groupAddress << false;
+    QTest::newRow("dual bind, group ipv6 address") << dualAddress << true << group6Address << true;
 }
 
 void tst_QUdpSocket::multicast()