QNativeSocketEngine/Win: fix getting the datagram destination
Looks like I never even tested this. There were two problems:
1) when we asked for the recvmsg and sendmsg functions, we used the
wrong variable (socketDescriptor was still -1)
2) we extracted the destination addresses, but never set them in the
QIpPacketHeader object
The added tests confirm that this works on Windows, Linux, Darwin,
FreeBSD. There also seems to be a problem, obtaining the destination
address on an IPv4 socket with a dual-stack sender (I can reproduce that
on FreeBSD, macOS and Windows, plus an old version of Linux).
Task-number: QTBUG-63605
Change-Id: I638cf58bfa7b4e5fb386fffd14ea732bddbc0c42
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
bb10
parent
d674d227f7
commit
484a186f50
|
|
@ -408,13 +408,13 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
|
|||
// get the pointer to sendmsg and recvmsg
|
||||
DWORD bytesReturned;
|
||||
GUID recvmsgguid = WSAID_WSARECVMSG;
|
||||
if (WSAIoctl(socketDescriptor, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
if (WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
&recvmsgguid, sizeof(recvmsgguid),
|
||||
&recvmsg, sizeof(recvmsg), &bytesReturned, NULL, NULL) == SOCKET_ERROR)
|
||||
recvmsg = 0;
|
||||
|
||||
GUID sendmsgguid = WSAID_WSASENDMSG;
|
||||
if (WSAIoctl(socketDescriptor, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
if (WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
&sendmsgguid, sizeof(sendmsgguid),
|
||||
&sendmsg, sizeof(sendmsg), &bytesReturned, NULL, NULL) == SOCKET_ERROR)
|
||||
sendmsg = 0;
|
||||
|
|
@ -1257,26 +1257,28 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL
|
|||
qt_socket_getPortAndAddress(socketDescriptor, &aa, &header->senderPort, &header->senderAddress);
|
||||
}
|
||||
|
||||
if (ret != -1 && recvmsg) {
|
||||
if (ret != -1 && recvmsg && options != QAbstractSocketEngine::WantNone) {
|
||||
// get the ancillary data
|
||||
header->destinationPort = localPort;
|
||||
WSACMSGHDR *cmsgptr;
|
||||
for (cmsgptr = WSA_CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
|
||||
cmsgptr = WSA_CMSG_NXTHDR(&msg, cmsgptr)) {
|
||||
if (cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_PKTINFO
|
||||
&& cmsgptr->cmsg_len >= WSA_CMSG_LEN(sizeof(in6_pktinfo))) {
|
||||
in6_pktinfo *info = reinterpret_cast<in6_pktinfo *>(WSA_CMSG_DATA(cmsgptr));
|
||||
QHostAddress target(reinterpret_cast<quint8 *>(&info->ipi6_addr));
|
||||
if (info->ipi6_ifindex)
|
||||
target.setScopeId(QString::number(info->ipi6_ifindex));
|
||||
|
||||
header->destinationAddress.setAddress(reinterpret_cast<quint8 *>(&info->ipi6_addr));
|
||||
header->ifindex = info->ipi6_ifindex;
|
||||
if (header->ifindex)
|
||||
header->destinationAddress.setScopeId(QString::number(info->ipi6_ifindex));
|
||||
}
|
||||
if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_PKTINFO
|
||||
&& cmsgptr->cmsg_len >= WSA_CMSG_LEN(sizeof(in_pktinfo))) {
|
||||
in_pktinfo *info = reinterpret_cast<in_pktinfo *>(WSA_CMSG_DATA(cmsgptr));
|
||||
u_long addr;
|
||||
WSANtohl(socketDescriptor, info->ipi_addr.s_addr, &addr);
|
||||
QHostAddress target(addr);
|
||||
if (info->ipi_ifindex)
|
||||
target.setScopeId(QString::number(info->ipi_ifindex));
|
||||
header->destinationAddress.setAddress(addr);
|
||||
header->ifindex = info->ipi_ifindex;
|
||||
}
|
||||
|
||||
if (cmsgptr->cmsg_len == WSA_CMSG_LEN(sizeof(int))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
CONFIG += testcase
|
||||
testcase.timeout = 800 # this test is slow
|
||||
SOURCES += ../tst_qudpsocket.cpp
|
||||
INCLUDEPATH += ../../../../../shared/
|
||||
QT = core network testlib
|
||||
|
||||
MOC_DIR=tmp
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Intel Corporation.
|
||||
** Copyright (C) 2017 Intel Corporation.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
#include <qstringlist.h>
|
||||
#include "../../../network-settings.h"
|
||||
#include "emulationdetector.h"
|
||||
|
||||
#ifndef QT_NO_BEARERMANAGEMENT
|
||||
#include <QtNetwork/qnetworkconfigmanager.h>
|
||||
|
|
@ -227,6 +228,9 @@ void tst_QUdpSocket::initTestCase()
|
|||
QSKIP("No network test server available");
|
||||
allAddresses = QNetworkInterface::allAddresses();
|
||||
m_skipUnsupportedIPv6Tests = shouldSkipIpv6TestsForBrokenSetsockopt();
|
||||
|
||||
if (EmulationDetector::isRunningArmOnX86())
|
||||
QSKIP("This test is unreliable due to QEMU emulation shortcomings.");
|
||||
}
|
||||
|
||||
void tst_QUdpSocket::init()
|
||||
|
|
@ -445,31 +449,58 @@ void tst_QUdpSocket::loop()
|
|||
paul.setProperty("_q_networksession", QVariant::fromValue(networkSession));
|
||||
#endif
|
||||
|
||||
QVERIFY2(peter.bind(), peter.errorString().toLatin1().constData());
|
||||
QVERIFY2(paul.bind(), paul.errorString().toLatin1().constData());
|
||||
// make sure we bind to IPv4
|
||||
QHostAddress localhost = QHostAddress::LocalHost;
|
||||
QVERIFY2(peter.bind(localhost), peter.errorString().toLatin1().constData());
|
||||
QVERIFY2(paul.bind(localhost), paul.errorString().toLatin1().constData());
|
||||
|
||||
QHostAddress peterAddress = makeNonAny(peter.localAddress());
|
||||
QHostAddress pualAddress = makeNonAny(paul.localAddress());
|
||||
QHostAddress paulAddress = makeNonAny(paul.localAddress());
|
||||
|
||||
QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.length(),
|
||||
pualAddress, paul.localPort()), qint64(peterMessage.length()));
|
||||
paulAddress, paul.localPort()), qint64(peterMessage.length()));
|
||||
QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(),
|
||||
peterAddress, peter.localPort()), qint64(paulMessage.length()));
|
||||
|
||||
QVERIFY2(peter.waitForReadyRead(9000), QtNetworkSettings::msgSocketError(peter).constData());
|
||||
QVERIFY2(paul.waitForReadyRead(9000), QtNetworkSettings::msgSocketError(paul).constData());
|
||||
char peterBuffer[16*1024];
|
||||
char paulBuffer[16*1024];
|
||||
|
||||
QNetworkDatagram peterDatagram = peter.receiveDatagram(paulMessage.length() * 2);
|
||||
QNetworkDatagram paulDatagram = paul.receiveDatagram(peterMessage.length() * 2);
|
||||
if (success) {
|
||||
QCOMPARE(peter.readDatagram(peterBuffer, sizeof(peterBuffer)), qint64(paulMessage.length()));
|
||||
QCOMPARE(paul.readDatagram(paulBuffer, sizeof(peterBuffer)), qint64(peterMessage.length()));
|
||||
QCOMPARE(peterDatagram.data().length(), qint64(paulMessage.length()));
|
||||
QCOMPARE(paulDatagram.data().length(), qint64(peterMessage.length()));
|
||||
} else {
|
||||
QVERIFY(peter.readDatagram(peterBuffer, sizeof(peterBuffer)) != paulMessage.length());
|
||||
QVERIFY(paul.readDatagram(paulBuffer, sizeof(peterBuffer)) != peterMessage.length());
|
||||
// this code path seems to never be executed
|
||||
QVERIFY(peterDatagram.data().length() != paulMessage.length());
|
||||
QVERIFY(paulDatagram.data().length() != peterMessage.length());
|
||||
}
|
||||
|
||||
QCOMPARE(QByteArray(peterBuffer, paulMessage.length()), paulMessage);
|
||||
QCOMPARE(QByteArray(paulBuffer, peterMessage.length()), peterMessage);
|
||||
QCOMPARE(peterDatagram.data().left(paulMessage.length()), paulMessage);
|
||||
QCOMPARE(paulDatagram.data().left(peterMessage.length()), peterMessage);
|
||||
|
||||
QCOMPARE(peterDatagram.senderAddress(), paulAddress);
|
||||
QCOMPARE(paulDatagram.senderAddress(), peterAddress);
|
||||
QCOMPARE(paulDatagram.senderPort(), int(peter.localPort()));
|
||||
QCOMPARE(peterDatagram.senderPort(), int(paul.localPort()));
|
||||
|
||||
// Unlike for IPv6 with IPV6_PKTINFO, IPv4 has no standardized way of
|
||||
// obtaining the packet's destination addresses. The destinationAddress and
|
||||
// destinationPort calls could fail, so whitelist the OSes for which we
|
||||
// know we have an implementation.
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) || defined(Q_OS_WIN)
|
||||
QVERIFY(peterDatagram.destinationPort() != -1);
|
||||
QVERIFY(paulDatagram.destinationPort() != -1);
|
||||
#endif
|
||||
if (peterDatagram.destinationPort() == -1) {
|
||||
QCOMPARE(peterDatagram.destinationAddress().protocol(), QAbstractSocket::UnknownNetworkLayerProtocol);
|
||||
QCOMPARE(paulDatagram.destinationAddress().protocol(), QAbstractSocket::UnknownNetworkLayerProtocol);
|
||||
} else {
|
||||
QCOMPARE(peterDatagram.destinationAddress(), makeNonAny(peter.localAddress()));
|
||||
QCOMPARE(paulDatagram.destinationAddress(), makeNonAny(paul.localAddress()));
|
||||
QVERIFY(peterDatagram.destinationAddress().isEqual(makeNonAny(peter.localAddress())));
|
||||
QVERIFY(paulDatagram.destinationAddress().isEqual(makeNonAny(paul.localAddress())));
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
|
@ -492,8 +523,8 @@ void tst_QUdpSocket::ipv6Loop()
|
|||
paul.setProperty("_q_networksession", QVariant::fromValue(networkSession));
|
||||
#endif
|
||||
|
||||
quint16 peterPort;
|
||||
quint16 paulPort;
|
||||
int peterPort;
|
||||
int paulPort;
|
||||
|
||||
if (!peter.bind(QHostAddress(QHostAddress::LocalHostIPv6), 0)) {
|
||||
QCOMPARE(peter.error(), QUdpSocket::UnsupportedSocketOperationError);
|
||||
|
|
@ -502,6 +533,8 @@ void tst_QUdpSocket::ipv6Loop()
|
|||
|
||||
QVERIFY(paul.bind(QHostAddress(QHostAddress::LocalHostIPv6), 0));
|
||||
|
||||
QHostAddress peterAddress = makeNonAny(peter.localAddress());
|
||||
QHostAddress paulAddress = makeNonAny(paul.localAddress());
|
||||
peterPort = peter.localPort();
|
||||
paulPort = paul.localPort();
|
||||
|
||||
|
|
@ -510,20 +543,33 @@ void tst_QUdpSocket::ipv6Loop()
|
|||
QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(),
|
||||
QHostAddress("::1"), peterPort), qint64(paulMessage.length()));
|
||||
|
||||
char peterBuffer[16*1024];
|
||||
char paulBuffer[16*1024];
|
||||
QVERIFY(peter.waitForReadyRead(5000));
|
||||
QVERIFY(paul.waitForReadyRead(5000));
|
||||
QNetworkDatagram peterDatagram = peter.receiveDatagram(paulMessage.length() * 2);
|
||||
QNetworkDatagram paulDatagram = paul.receiveDatagram(peterMessage.length() * 2);
|
||||
|
||||
if (success) {
|
||||
QCOMPARE(peter.readDatagram(peterBuffer, sizeof(peterBuffer)), qint64(paulMessage.length()));
|
||||
QCOMPARE(paul.readDatagram(paulBuffer, sizeof(peterBuffer)), qint64(peterMessage.length()));
|
||||
QCOMPARE(peterDatagram.data().length(), qint64(paulMessage.length()));
|
||||
QCOMPARE(paulDatagram.data().length(), qint64(peterMessage.length()));
|
||||
} else {
|
||||
QVERIFY(peter.readDatagram(peterBuffer, sizeof(peterBuffer)) != paulMessage.length());
|
||||
QVERIFY(paul.readDatagram(paulBuffer, sizeof(peterBuffer)) != peterMessage.length());
|
||||
// this code path seems to never be executed
|
||||
QVERIFY(peterDatagram.data().length() != paulMessage.length());
|
||||
QVERIFY(paulDatagram.data().length() != peterMessage.length());
|
||||
}
|
||||
|
||||
QCOMPARE(QByteArray(peterBuffer, paulMessage.length()), paulMessage);
|
||||
QCOMPARE(QByteArray(paulBuffer, peterMessage.length()), peterMessage);
|
||||
QCOMPARE(peterDatagram.data().left(paulMessage.length()), paulMessage);
|
||||
QCOMPARE(paulDatagram.data().left(peterMessage.length()), peterMessage);
|
||||
|
||||
QCOMPARE(peterDatagram.senderAddress(), paulAddress);
|
||||
QCOMPARE(paulDatagram.senderAddress(), peterAddress);
|
||||
QCOMPARE(paulDatagram.senderPort(), peterPort);
|
||||
QCOMPARE(peterDatagram.senderPort(), paulPort);
|
||||
|
||||
// For IPv6, IPV6_PKTINFO is a mandatory feature (RFC 3542).
|
||||
QCOMPARE(peterDatagram.destinationAddress(), makeNonAny(peter.localAddress()));
|
||||
QCOMPARE(paulDatagram.destinationAddress(), makeNonAny(paul.localAddress()));
|
||||
QCOMPARE(peterDatagram.destinationPort(), peterPort);
|
||||
QCOMPARE(paulDatagram.destinationPort(), paulPort);
|
||||
}
|
||||
|
||||
void tst_QUdpSocket::dualStack()
|
||||
|
|
@ -539,17 +585,23 @@ void tst_QUdpSocket::dualStack()
|
|||
QByteArray v4Data("v4");
|
||||
QVERIFY(v4Sock.bind(QHostAddress(QHostAddress::AnyIPv4), 0));
|
||||
|
||||
QHostAddress from;
|
||||
quint16 port;
|
||||
QByteArray buffer;
|
||||
//test v4 -> dual
|
||||
QCOMPARE((int)v4Sock.writeDatagram(v4Data.constData(), v4Data.length(), QHostAddress(QHostAddress::LocalHost), dualSock.localPort()), v4Data.length());
|
||||
QVERIFY2(dualSock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(dualSock).constData());
|
||||
buffer.reserve(100);
|
||||
qint64 size = dualSock.readDatagram(buffer.data(), 100, &from, &port);
|
||||
QCOMPARE((int)size, v4Data.length());
|
||||
buffer.resize(size);
|
||||
QCOMPARE(buffer, v4Data);
|
||||
QNetworkDatagram dgram = dualSock.receiveDatagram(100);
|
||||
QVERIFY(dgram.isValid());
|
||||
QCOMPARE(dgram.data(), v4Data);
|
||||
QCOMPARE(dgram.senderPort(), int(v4Sock.localPort()));
|
||||
// receiving v4 on dual stack will receive as IPv6, so use isEqual()
|
||||
QVERIFY(dgram.senderAddress().isEqual(makeNonAny(v4Sock.localAddress(), QHostAddress::Null)));
|
||||
if (dualSock.localAddress().protocol() == QAbstractSocket::IPv4Protocol)
|
||||
QCOMPARE(dgram.senderAddress(), makeNonAny(v4Sock.localAddress(), QHostAddress::Null));
|
||||
if (dgram.destinationPort() != -1) {
|
||||
QCOMPARE(dgram.destinationPort(), int(dualSock.localPort()));
|
||||
QVERIFY(dgram.destinationAddress().isEqual(dualSock.localAddress()));
|
||||
} else {
|
||||
qInfo("Getting IPv4 destination address failed.");
|
||||
}
|
||||
|
||||
if (QtNetworkSettings::hasIPv6()) {
|
||||
QUdpSocket v6Sock;
|
||||
|
|
@ -559,30 +611,41 @@ void tst_QUdpSocket::dualStack()
|
|||
//test v6 -> dual
|
||||
QCOMPARE((int)v6Sock.writeDatagram(v6Data.constData(), v6Data.length(), QHostAddress(QHostAddress::LocalHostIPv6), dualSock.localPort()), v6Data.length());
|
||||
QVERIFY2(dualSock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(dualSock).constData());
|
||||
buffer.reserve(100);
|
||||
size = dualSock.readDatagram(buffer.data(), 100, &from, &port);
|
||||
QCOMPARE((int)size, v6Data.length());
|
||||
buffer.resize(size);
|
||||
QCOMPARE(buffer, v6Data);
|
||||
dgram = dualSock.receiveDatagram(100);
|
||||
QVERIFY(dgram.isValid());
|
||||
QCOMPARE(dgram.data(), v6Data);
|
||||
QCOMPARE(dgram.senderPort(), int(v6Sock.localPort()));
|
||||
QCOMPARE(dgram.senderAddress(), makeNonAny(v6Sock.localAddress(), QHostAddress::LocalHostIPv6));
|
||||
QCOMPARE(dgram.destinationPort(), int(dualSock.localPort()));
|
||||
QCOMPARE(dgram.destinationAddress(), makeNonAny(dualSock.localAddress(), QHostAddress::LocalHostIPv6));
|
||||
|
||||
//test dual -> v6
|
||||
QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.length());
|
||||
QVERIFY2(v6Sock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(v6Sock).constData());
|
||||
buffer.reserve(100);
|
||||
size = v6Sock.readDatagram(buffer.data(), 100, &from, &port);
|
||||
QCOMPARE((int)size, dualData.length());
|
||||
buffer.resize(size);
|
||||
QCOMPARE(buffer, dualData);
|
||||
dgram = v6Sock.receiveDatagram(100);
|
||||
QVERIFY(dgram.isValid());
|
||||
QCOMPARE(dgram.data(), dualData);
|
||||
QCOMPARE(dgram.senderPort(), int(dualSock.localPort()));
|
||||
QCOMPARE(dgram.senderAddress(), makeNonAny(dualSock.localAddress(), QHostAddress::LocalHostIPv6));
|
||||
QCOMPARE(dgram.destinationPort(), int(v6Sock.localPort()));
|
||||
QCOMPARE(dgram.destinationAddress(), makeNonAny(v6Sock.localAddress(), QHostAddress::LocalHostIPv6));
|
||||
}
|
||||
|
||||
//test dual -> v4
|
||||
QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.length());
|
||||
QVERIFY2(v4Sock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(v4Sock).constData());
|
||||
buffer.reserve(100);
|
||||
size = v4Sock.readDatagram(buffer.data(), 100, &from, &port);
|
||||
QCOMPARE((int)size, dualData.length());
|
||||
buffer.resize(size);
|
||||
QCOMPARE(buffer, dualData);
|
||||
dgram = v4Sock.receiveDatagram(100);
|
||||
QVERIFY(dgram.isValid());
|
||||
QCOMPARE(dgram.data(), dualData);
|
||||
QCOMPARE(dgram.senderPort(), int(dualSock.localPort()));
|
||||
QCOMPARE(dgram.senderAddress(), makeNonAny(dualSock.localAddress(), QHostAddress::LocalHost));
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) || defined(Q_OS_WIN)
|
||||
QVERIFY(dgram.destinationPort() != -1);
|
||||
#endif
|
||||
if (dgram.destinationPort() != -1) {
|
||||
QCOMPARE(dgram.destinationPort(), int(v4Sock.localPort()));
|
||||
QCOMPARE(dgram.destinationAddress(), makeNonAny(v4Sock.localAddress(), QHostAddress::LocalHost));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QUdpSocket::dualStackAutoBinding()
|
||||
|
|
@ -1603,6 +1666,8 @@ void tst_QUdpSocket::linkLocalIPv6()
|
|||
QVERIFY(dgram.isValid());
|
||||
QCOMPARE(dgram.senderAddress(), s->localAddress());
|
||||
QCOMPARE(dgram.senderPort(), int(s->localPort()));
|
||||
QCOMPARE(dgram.destinationAddress(), s->localAddress());
|
||||
QCOMPARE(dgram.destinationPort(), int(neutral.localPort()));
|
||||
QCOMPARE(dgram.data().length(), testData.length());
|
||||
QCOMPARE(dgram.data(), testData);
|
||||
|
||||
|
|
@ -1684,6 +1749,20 @@ void tst_QUdpSocket::linkLocalIPv4()
|
|||
QCOMPARE(dgram.data().length(), testData.length());
|
||||
QCOMPARE(dgram.data(), testData);
|
||||
|
||||
// Unlike for IPv6 with IPV6_PKTINFO, IPv4 has no standardized way of
|
||||
// obtaining the packet's destination addresses. The destinationAddress
|
||||
// and destinationPort calls could fail, so whitelist the OSes we know
|
||||
// we have an implementation.
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) || defined(Q_OS_WIN)
|
||||
QVERIFY(dgram.destinationPort() != -1);
|
||||
#endif
|
||||
if (dgram.destinationPort() == -1) {
|
||||
QCOMPARE(dgram.destinationAddress().protocol(), QAbstractSocket::UnknownNetworkLayerProtocol);
|
||||
} else {
|
||||
QCOMPARE(dgram.destinationAddress(), s->localAddress());
|
||||
QCOMPARE(dgram.destinationPort(), int(neutral.localPort()));
|
||||
}
|
||||
|
||||
QVERIFY(neutral.writeDatagram(dgram.makeReply(testData)));
|
||||
|
||||
dgram = s->receiveDatagram(testData.length() * 2);
|
||||
|
|
|
|||
Loading…
Reference in New Issue