Allow autobound UDP sockets to send to IPv4 and IPv6
When writeDatagram is called without first binding the UDP socket, then bind it as QHostAddress::Any. This allows the same socket to be used to sent to both IPv4 and IPv6 destination addresses. Allowing the OS to autobind the socket inside sendTo() may result in a single protocol socket. Task-number: QTBUG-5275 Change-Id: I2b76507e8a8a38369c6eafb61ce4191d1d6cc930 Reviewed-by: Richard J. Moore <rich@kde.org> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Martin Petersson <Martin.Petersson@nokia.com>bb10
parent
fa8e18c386
commit
cae5299f48
|
|
@ -854,7 +854,8 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
|
|||
|
||||
struct sockaddr_in6 sockAddrIPv6;
|
||||
if (host.protocol() == QAbstractSocket::IPv6Protocol
|
||||
|| socketProtocol == QAbstractSocket::IPv6Protocol) {
|
||||
|| socketProtocol == QAbstractSocket::IPv6Protocol
|
||||
|| socketProtocol == QAbstractSocket::AnyIPProtocol) {
|
||||
memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
|
||||
sockAddrIPv6.sin6_family = AF_INET6;
|
||||
sockAddrIPv6.sin6_port = htons(port);
|
||||
|
|
|
|||
|
|
@ -202,7 +202,8 @@ void QNativeSocketEnginePrivate::setPortAndAddress(sockaddr_in * sockAddrIPv4, q
|
|||
|
||||
if (address.protocol() == QAbstractSocket::IPv6Protocol
|
||||
|| address.protocol() == QAbstractSocket::AnyIPProtocol
|
||||
|| socketProtocol == QAbstractSocket::IPv6Protocol) {
|
||||
|| socketProtocol == QAbstractSocket::IPv6Protocol
|
||||
|| socketProtocol == QAbstractSocket::AnyIPProtocol) {
|
||||
memset(sockAddrIPv6, 0, sizeof(qt_sockaddr_in6));
|
||||
sockAddrIPv6->sin6_family = AF_INET6;
|
||||
sockAddrIPv6->sin6_scope_id = address.scopeId().toInt();
|
||||
|
|
|
|||
|
|
@ -332,8 +332,10 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre
|
|||
qDebug("QUdpSocket::writeDatagram(%p, %llu, \"%s\", %i)", data, size,
|
||||
address.toString().toLatin1().constData(), port);
|
||||
#endif
|
||||
if (!d->ensureInitialized(address))
|
||||
if (!d->doEnsureInitialized(QHostAddress::Any, 0, address))
|
||||
return -1;
|
||||
if (state() == UnconnectedState)
|
||||
bind();
|
||||
|
||||
qint64 sent = d->socketEngine->writeDatagram(data, size, address, port);
|
||||
d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ private slots:
|
|||
void ipv6Loop_data();
|
||||
void ipv6Loop();
|
||||
void dualStack();
|
||||
void dualStackAutoBinding();
|
||||
void dualStackNoIPv4onV6only();
|
||||
void readLine();
|
||||
void pendingDatagramSize();
|
||||
void writeDatagram();
|
||||
|
|
@ -490,6 +492,83 @@ void tst_QUdpSocket::dualStack()
|
|||
|
||||
}
|
||||
|
||||
void tst_QUdpSocket::dualStackAutoBinding()
|
||||
{
|
||||
QFETCH_GLOBAL(bool, setProxy);
|
||||
if (setProxy)
|
||||
QSKIP("test server SOCKS proxy doesn't support IPv6");
|
||||
QUdpSocket v4Sock;
|
||||
QVERIFY(v4Sock.bind(QHostAddress(QHostAddress::AnyIPv4), 0));
|
||||
|
||||
QUdpSocket v6Sock;
|
||||
QVERIFY(v6Sock.bind(QHostAddress(QHostAddress::AnyIPv6), 0));
|
||||
|
||||
QByteArray dualData("dual");
|
||||
QHostAddress from;
|
||||
quint16 port;
|
||||
QByteArray buffer;
|
||||
int size;
|
||||
|
||||
{
|
||||
//test an autobound socket can send to both v4 and v6 addresses (v4 first)
|
||||
QUdpSocket dualSock;
|
||||
|
||||
QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.length());
|
||||
QVERIFY(v4Sock.waitForReadyRead(5000));
|
||||
buffer.reserve(100);
|
||||
size = v4Sock.readDatagram(buffer.data(), 100, &from, &port);
|
||||
QCOMPARE((int)size, dualData.length());
|
||||
buffer.resize(size);
|
||||
QCOMPARE(buffer, dualData);
|
||||
|
||||
QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.length());
|
||||
QVERIFY(v6Sock.waitForReadyRead(5000));
|
||||
buffer.reserve(100);
|
||||
size = v6Sock.readDatagram(buffer.data(), 100, &from, &port);
|
||||
QCOMPARE((int)size, dualData.length());
|
||||
buffer.resize(size);
|
||||
QCOMPARE(buffer, dualData);
|
||||
}
|
||||
|
||||
{
|
||||
//test an autobound socket can send to both v4 and v6 addresses (v6 first)
|
||||
QUdpSocket dualSock;
|
||||
|
||||
QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.length());
|
||||
QVERIFY(v6Sock.waitForReadyRead(5000));
|
||||
buffer.reserve(100);
|
||||
size = v6Sock.readDatagram(buffer.data(), 100, &from, &port);
|
||||
QCOMPARE((int)size, dualData.length());
|
||||
buffer.resize(size);
|
||||
QCOMPARE(buffer, dualData);
|
||||
|
||||
QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.length());
|
||||
QVERIFY(v4Sock.waitForReadyRead(5000));
|
||||
buffer.reserve(100);
|
||||
size = v4Sock.readDatagram(buffer.data(), 100, &from, &port);
|
||||
QCOMPARE((int)size, dualData.length());
|
||||
buffer.resize(size);
|
||||
QCOMPARE(buffer, dualData);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QUdpSocket::dualStackNoIPv4onV6only()
|
||||
{
|
||||
QFETCH_GLOBAL(bool, setProxy);
|
||||
if (setProxy)
|
||||
QSKIP("test server SOCKS proxy doesn't support IPv6");
|
||||
QUdpSocket v4Sock;
|
||||
QVERIFY(v4Sock.bind(QHostAddress(QHostAddress::AnyIPv4), 0));
|
||||
QByteArray v4Data("v4");
|
||||
|
||||
QUdpSocket v6Sock;
|
||||
QVERIFY(v6Sock.bind(QHostAddress(QHostAddress::AnyIPv6), 0));
|
||||
|
||||
//test v4 -> v6 (should not be received as this is a v6 only socket)
|
||||
QCOMPARE((int)v4Sock.writeDatagram(v4Data.constData(), v4Data.length(), QHostAddress(QHostAddress::LocalHost), v6Sock.localPort()), v4Data.length());
|
||||
QVERIFY(!v6Sock.waitForReadyRead(1000));
|
||||
}
|
||||
|
||||
void tst_QUdpSocket::empty_readyReadSlot()
|
||||
{
|
||||
QTestEventLoop::instance().exitLoop();
|
||||
|
|
|
|||
Loading…
Reference in New Issue