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
Shane Kearns 2012-03-15 17:58:22 +00:00 committed by Qt by Nokia
parent fa8e18c386
commit cae5299f48
4 changed files with 86 additions and 3 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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();

View File

@ -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();