mirror of https://github.com/qt/qtbase.git
Fix broadcasts with dual-socket IPv6 in setSocketDescriptor
Amends and complements 28619f1ddd4a4b21d8489476d18927564ee0839d, which had identified this could be a problem, but since there was no test, we couldn't confirm it. Now that there is, we can see that it fails with VxWorks, where I guess the two stacks are wholly separate. Note fetchConnectionParameters() only sets AnyIPProtocol if the socket is bound to :: or ::ffff:0.0.0.0. Binding to other addresses is technically possible, but broadcasting would only work if bound to another v4-mapped address. Changing fetchConnectionParameters() to, for example, use toIPv4Address() and check for success is left as an exercise for the reader. Task-number: QTBUG-139586 Task-number: QTBUG-130070 Pick-to: 6.10 6.9 6.8 6.5 Change-Id: I44daa5ec27877abc2e09fffd875d4be6dfc86446 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
1261e71b90
commit
1472386c95
|
@ -511,6 +511,7 @@ bool QNativeSocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::
|
||||||
|
|
||||||
// Set the broadcasting flag if it's a UDP socket.
|
// Set the broadcasting flag if it's a UDP socket.
|
||||||
if (d->socketType == QAbstractSocket::UdpSocket
|
if (d->socketType == QAbstractSocket::UdpSocket
|
||||||
|
&& d->socketProtocol != QAbstractSocket::IPv6Protocol
|
||||||
&& !setOption(BroadcastSocketOption, 1)) {
|
&& !setOption(BroadcastSocketOption, 1)) {
|
||||||
d->setError(QAbstractSocket::UnsupportedSocketOperationError,
|
d->setError(QAbstractSocket::UnsupportedSocketOperationError,
|
||||||
QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString);
|
QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString);
|
||||||
|
|
|
@ -34,18 +34,24 @@
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX)
|
#if defined(Q_OS_LINUX)
|
||||||
#define SHOULD_CHECK_SYSCALL_SUPPORT
|
#define SHOULD_CHECK_SYSCALL_SUPPORT
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#else
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
|
||||||
# include <sys/socket.h>
|
|
||||||
#endif
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN) || defined(SO_NREAD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN) || defined(SO_NREAD)
|
||||||
# define RELIABLE_BYTES_AVAILABLE
|
# define RELIABLE_BYTES_AVAILABLE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef INVALID_SOCKET
|
||||||
|
# define INVALID_SOCKET -1
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QHostAddress)
|
Q_DECLARE_METATYPE(QHostAddress)
|
||||||
|
@ -62,6 +68,8 @@ private slots:
|
||||||
void init();
|
void init();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
void constructing();
|
void constructing();
|
||||||
|
void setSocketDescriptor_data();
|
||||||
|
void setSocketDescriptor();
|
||||||
void unconnectedServerAndClientTest();
|
void unconnectedServerAndClientTest();
|
||||||
void broadcasting();
|
void broadcasting();
|
||||||
void broadcastingDualSocket_data();
|
void broadcastingDualSocket_data();
|
||||||
|
@ -321,6 +329,105 @@ void tst_QUdpSocket::constructing()
|
||||||
// Check the state of the socket api
|
// Check the state of the socket api
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void tst_QUdpSocket::setSocketDescriptor_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QAbstractSocket::NetworkLayerProtocol>("protocol");
|
||||||
|
QTest::newRow("ipv4") << QAbstractSocket::IPv4Protocol;
|
||||||
|
|
||||||
|
bool hasIPv6 = true;
|
||||||
|
#if defined(PF_INET6) && defined(Q_OS_UNIX)
|
||||||
|
if (int fd = socket(PF_INET6, SOCK_DGRAM, 0); fd >= 0)
|
||||||
|
close(fd);
|
||||||
|
else
|
||||||
|
hasIPv6 = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (hasIPv6) {
|
||||||
|
QTest::newRow("ipv6") << QAbstractSocket::IPv6Protocol;
|
||||||
|
# if defined(IPV6_V6ONLY) && !defined(Q_OS_VXWORKS)
|
||||||
|
QTest::newRow("dualstack") << QAbstractSocket::AnyIPProtocol;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QUdpSocket::setSocketDescriptor()
|
||||||
|
{
|
||||||
|
QFETCH(QAbstractSocket::NetworkLayerProtocol, protocol);
|
||||||
|
qintptr descriptor = -1;
|
||||||
|
int port = -1;
|
||||||
|
|
||||||
|
int domain = (protocol == QAbstractSocket::IPv4Protocol) ? AF_INET : AF_INET6;
|
||||||
|
descriptor = socket(domain, SOCK_DGRAM, 0);
|
||||||
|
|
||||||
|
if (descriptor == INVALID_SOCKET)
|
||||||
|
QSKIP("Could not create native socket");
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
auto closeSocket = qScopeGuard([&descriptor] { closesocket(descriptor); });
|
||||||
|
using socklen_t = int;
|
||||||
|
#else
|
||||||
|
auto closeSocket = qScopeGuard([&descriptor] { close(descriptor); });
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (domain == AF_INET) {
|
||||||
|
sockaddr_in addr = {};
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
if (bind(descriptor, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) < 0)
|
||||||
|
QSKIP("Could not bind IPv4 socket");
|
||||||
|
|
||||||
|
socklen_t len = sizeof(addr);
|
||||||
|
if (getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &len) < 0)
|
||||||
|
QSKIP("Could not get local IPv4 port number");
|
||||||
|
port = qFromBigEndian(addr.sin_port);
|
||||||
|
#ifdef PF_INET6
|
||||||
|
} else {
|
||||||
|
# ifdef IPV6_V6ONLY
|
||||||
|
int v6only = protocol == QAbstractSocket::IPv6Protocol;
|
||||||
|
if (setsockopt(descriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6only, sizeof(v6only)) < 0)
|
||||||
|
QSKIP("Could not set IPV6_V6ONLY");
|
||||||
|
# endif
|
||||||
|
|
||||||
|
sockaddr_in6 addr = {};
|
||||||
|
addr.sin6_family = AF_INET6;
|
||||||
|
if (bind(descriptor, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) < 0)
|
||||||
|
QSKIP("Could not bind IPv6 socket");
|
||||||
|
|
||||||
|
socklen_t len = sizeof(addr);
|
||||||
|
if (getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &len) < 0)
|
||||||
|
QSKIP("Could not get local IPv6 port number");
|
||||||
|
port = qFromBigEndian(addr.sin6_port);
|
||||||
|
# endif // IPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
QUdpSocket socket;
|
||||||
|
QSignalSpy spy(&socket, &QUdpSocket::stateChanged);
|
||||||
|
QVERIFY2(socket.setSocketDescriptor(descriptor, QAbstractSocket::BoundState),
|
||||||
|
qPrintable(socket.errorString()));
|
||||||
|
QCOMPARE(socket.socketDescriptor(), descriptor);
|
||||||
|
closeSocket.dismiss();
|
||||||
|
|
||||||
|
QVERIFY(socket.isValid());
|
||||||
|
QCOMPARE(socket.socketType(), QAbstractSocket::UdpSocket);
|
||||||
|
|
||||||
|
QCOMPARE(socket.state(), QAbstractSocket::BoundState);
|
||||||
|
QCOMPARE(spy.size(), 1);
|
||||||
|
QCOMPARE(spy.at(0), QVariantList{QVariant::fromValue(QAbstractSocket::BoundState)});
|
||||||
|
|
||||||
|
QCOMPARE(socket.localPort(), port);
|
||||||
|
if (protocol == QAbstractSocket::IPv4Protocol)
|
||||||
|
QCOMPARE(socket.localAddress(), QHostAddress::AnyIPv4);
|
||||||
|
else if (protocol == QAbstractSocket::IPv6Protocol)
|
||||||
|
QCOMPARE(socket.localAddress(), QHostAddress::AnyIPv6);
|
||||||
|
else
|
||||||
|
QCOMPARE(socket.localAddress(), QHostAddress::Any);
|
||||||
|
QCOMPARE(socket.bytesToWrite(), 0);
|
||||||
|
QCOMPARE(socket.bytesAvailable(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
void tst_QUdpSocket::unconnectedServerAndClientTest()
|
void tst_QUdpSocket::unconnectedServerAndClientTest()
|
||||||
{
|
{
|
||||||
QUdpSocket serverSocket;
|
QUdpSocket serverSocket;
|
||||||
|
|
Loading…
Reference in New Issue