summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2014-12-23 13:37:01 -0200
committerThiago Macieira <thiago.macieira@intel.com>2015-03-04 23:58:03 +0000
commit9fb68a90af79df3b8dc3225a3a97e2c6387afeec (patch)
treeb51f4ca6effbcf2da792595837a92198a2181f01
parent29051bce39d24a6e33155feeec2833b79b55ff16 (diff)
Fix bind+connect in both TCP and UDP
This has been known to be broken for a while. Now it works: you can bind and you'll retain the port (and the file descriptor) for the connect call. Incidentally, in fixing the binding for more than one IP for the hostname (with event loop), this commit fixes the setSocketDescriptor XFAIL. [ChangeLog][QtNetwork] Fixed a bug that caused both QTcpSocket and QUdpSocket to close the socket and lose any bound ports before connecting. Now bind()/setSocketDescriptor() followed by connect() will retain the original file descriptor. Task-number: QTBUG-26538 Change-Id: I691caed7e8fd16a9cf687b5995afbf3006bf453a Reviewed-by: Richard J. Moore <rich@kde.org>
-rw-r--r--src/network/kernel/qhostinfo.cpp11
-rw-r--r--src/network/kernel/qhostinfo_p.h1
-rw-r--r--src/network/socket/qabstractsocket.cpp67
-rw-r--r--src/network/socket/qabstractsocket.h2
-rw-r--r--src/network/socket/qabstractsocket_p.h2
-rw-r--r--src/network/socket/qnativesocketengine.cpp10
-rw-r--r--src/network/ssl/qsslsocket.cpp23
-rw-r--r--src/network/ssl/qsslsocket_p.h1
-rw-r--r--tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp159
-rw-r--r--tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp36
10 files changed, 266 insertions, 46 deletions
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index d2dc7c4850..a2ac9065fd 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -689,6 +689,7 @@ void qt_qhostinfo_clear_cache()
}
}
+#ifdef QT_BUILD_INTERNAL
void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
{
QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
@@ -697,6 +698,16 @@ void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
}
}
+void qt_qhostinfo_cache_inject(const QString &hostname, const QHostInfo &resolution)
+{
+ QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ if (!manager || !manager->cache.isEnabled())
+ return;
+
+ manager->cache.put(hostname, resolution);
+}
+#endif
+
// cache for 60 seconds
// cache 128 items
QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(128)
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index 74cfe2a9aa..a99c3dc8ca 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -117,6 +117,7 @@ public:
QHostInfo Q_NETWORK_EXPORT qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id);
void Q_AUTOTEST_EXPORT qt_qhostinfo_clear_cache();
void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e);
+void Q_AUTOTEST_EXPORT qt_qhostinfo_cache_inject(const QString &hostname, const QHostInfo &resolution);
class QHostInfoCache
{
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index f9fe40955a..3b10387b37 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -976,7 +976,7 @@ void QAbstractSocketPrivate::startConnectingByName(const QString &host)
connectTimeElapsed = 0;
- if (initSocketLayer(QAbstractSocket::UnknownNetworkLayerProtocol)) {
+ if (cachedSocketDescriptor != -1 || initSocketLayer(QAbstractSocket::UnknownNetworkLayerProtocol)) {
if (socketEngine->connectToHostByName(host, port) ||
socketEngine->state() == QAbstractSocket::ConnectingState) {
cachedSocketDescriptor = socketEngine->socketDescriptor();
@@ -1117,7 +1117,7 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
host.toString().toLatin1().constData(), port, addresses.count());
#endif
- if (!initSocketLayer(host.protocol())) {
+ if (cachedSocketDescriptor == -1 && !initSocketLayer(host.protocol())) {
// hope that the next address is better
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), failed to initialize sock layer");
@@ -1134,9 +1134,6 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
return;
}
- // cache the socket descriptor even if we're not fully connected yet
- cachedSocketDescriptor = socketEngine->socketDescriptor();
-
// Check that we're in delayed connection state. If not, try
// the next address
if (socketEngine->state() != QAbstractSocket::ConnectingState) {
@@ -1481,54 +1478,60 @@ void QAbstractSocket::setPauseMode(PauseModes pauseMode)
bool QAbstractSocket::bind(const QHostAddress &address, quint16 port, BindMode mode)
{
Q_D(QAbstractSocket);
+ return d->bind(address, port, mode);
+}
+
+bool QAbstractSocketPrivate::bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode)
+{
+ Q_Q(QAbstractSocket);
// now check if the socket engine is initialized and to the right type
- if (!d->socketEngine || !d->socketEngine->isValid()) {
+ if (!socketEngine || !socketEngine->isValid()) {
QHostAddress nullAddress;
- d->resolveProxy(nullAddress.toString(), port);
+ resolveProxy(nullAddress.toString(), port);
QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
if (protocol == QAbstractSocket::UnknownNetworkLayerProtocol)
protocol = nullAddress.protocol();
- if (!d->initSocketLayer(protocol))
+ if (!initSocketLayer(protocol))
return false;
}
- if (mode != DefaultForPlatform) {
+ if (mode != QAbstractSocket::DefaultForPlatform) {
#ifdef Q_OS_UNIX
- if ((mode & ShareAddress) || (mode & ReuseAddressHint))
- d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
+ if ((mode & QAbstractSocket::ShareAddress) || (mode & QAbstractSocket::ReuseAddressHint))
+ socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
else
- d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
+ socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
#endif
#ifdef Q_OS_WIN
- if (mode & ReuseAddressHint)
- d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
+ if (mode & QAbstractSocket::ReuseAddressHint)
+ socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
else
- d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
- if (mode & DontShareAddress)
- d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 1);
+ socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
+ if (mode & QAbstractSocket::DontShareAddress)
+ socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 1);
else
- d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 0);
+ socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 0);
#endif
}
- bool result = d->socketEngine->bind(address, port);
- d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
+ bool result = socketEngine->bind(address, port);
+ cachedSocketDescriptor = socketEngine->socketDescriptor();
if (!result) {
- d->socketError = d->socketEngine->error();
- setErrorString(d->socketEngine->errorString());
- emit error(d->socketError);
+ socketError = socketEngine->error();
+ q->setErrorString(socketEngine->errorString());
+ emit q->error(socketError);
return false;
}
- d->state = BoundState;
- d->localAddress = d->socketEngine->localAddress();
- d->localPort = d->socketEngine->localPort();
+ state = QAbstractSocket::BoundState;
+ localAddress = socketEngine->localAddress();
+ localPort = socketEngine->localPort();
- emit stateChanged(d->state);
- d->socketEngine->setReadNotificationEnabled(true);
+ emit q->stateChanged(state);
+ socketEngine->setReadNotificationEnabled(true);
return true;
}
@@ -1605,14 +1608,16 @@ void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
d->preferredNetworkLayerProtocol = protocol;
d->hostName = hostName;
d->port = port;
- d->state = UnconnectedState;
d->buffer.clear();
d->writeBuffer.clear();
d->abortCalled = false;
d->pendingClose = false;
- d->localPort = 0;
+ if (d->state != BoundState) {
+ d->state = UnconnectedState;
+ d->localPort = 0;
+ d->localAddress.clear();
+ }
d->peerPort = 0;
- d->localAddress.clear();
d->peerAddress.clear();
d->peerName = hostName;
if (d->hostLookupId != -1) {
diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h
index 76b1e5f538..f3d7f13f48 100644
--- a/src/network/socket/qabstractsocket.h
+++ b/src/network/socket/qabstractsocket.h
@@ -135,9 +135,11 @@ public:
PauseModes pauseMode() const;
void setPauseMode(PauseModes pauseMode);
+ // ### Qt6: make the first one virtual
bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);
bool bind(quint16 port = 0, BindMode mode = DefaultForPlatform);
+ // ### Qt6: de-virtualize connectToHost(QHostAddress) overload
virtual void connectToHost(const QString &hostName, quint16 port, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
virtual void disconnectFromHost();
diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h
index 379657f4fa..63440b6416 100644
--- a/src/network/socket/qabstractsocket_p.h
+++ b/src/network/socket/qabstractsocket_p.h
@@ -78,6 +78,8 @@ public:
}
#endif
+ virtual bool bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode);
+
bool canReadNotification();
bool canWriteNotification();
void canCloseNotification();
diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp
index db6c7c487a..fcfef87e3c 100644
--- a/src/network/socket/qnativesocketengine.cpp
+++ b/src/network/socket/qnativesocketengine.cpp
@@ -136,6 +136,12 @@ QT_BEGIN_NAMESPACE
" not in "#state1" or "#state2); \
return (returnValue); \
} } while (0)
+#define Q_CHECK_STATES3(function, state1, state2, state3, returnValue) do { \
+ if (d->socketState != (state1) && d->socketState != (state2) && d->socketState != (state3)) { \
+ qWarning(""#function" was called" \
+ " not in "#state1" or "#state2); \
+ return (returnValue); \
+ } } while (0)
#define Q_CHECK_TYPE(function, type, returnValue) do { \
if (d->socketType != (type)) { \
qWarning(#function" was called by a" \
@@ -495,7 +501,7 @@ bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 por
if (!d->checkProxy(address))
return false;
- Q_CHECK_STATES(QNativeSocketEngine::connectToHost(),
+ Q_CHECK_STATES3(QNativeSocketEngine::connectToHost(), QAbstractSocket::BoundState,
QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);
d->peerAddress = address;
@@ -961,7 +967,7 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
QNativeSocketEnginePrivate::TimeOutErrorString);
d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions
return false;
- } else if (state() == QAbstractSocket::ConnectingState) {
+ } else if (state() == QAbstractSocket::ConnectingState || (state() == QAbstractSocket::BoundState && d->socketDescriptor != -1)) {
connectToHost(d->peerAddress, d->peerPort);
}
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 508d300d42..b1076ebd68 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -2404,6 +2404,29 @@ bool QSslSocketPrivate::isPaused() const
return paused;
}
+bool QSslSocketPrivate::bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode)
+{
+ // this function is called from QAbstractSocket::bind
+ if (!initialized)
+ init();
+ initialized = false;
+
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "QSslSocket::bind(" << address << ',' << port << ',' << mode << ')';
+#endif
+ if (!plainSocket) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "\tcreating internal plain socket";
+#endif
+ createPlainSocket(QIODevice::ReadWrite);
+ }
+ bool ret = plainSocket->bind(address, port, mode);
+ localPort = plainSocket->localPort();
+ localAddress = plainSocket->localAddress();
+ cachedSocketDescriptor = plainSocket->socketDescriptor();
+ return ret;
+}
+
/*!
\internal
*/
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index b110d65f9a..5f726f2371 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -172,6 +172,7 @@ public:
static void checkSettingSslContext(QSslSocket*, QSharedPointer<QSslContext>);
static QSharedPointer<QSslContext> sslContext(QSslSocket *socket);
bool isPaused() const;
+ bool bind(const QHostAddress &address, quint16, QAbstractSocket::BindMode) Q_DECL_OVERRIDE;
void _q_connectedSlot();
void _q_hostFoundSlot();
void _q_disconnectedSlot();
diff --git a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp
index 878d5875b5..0ba9b6a58c 100644
--- a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp
+++ b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp
@@ -73,6 +73,7 @@
// RVCT compiles also unused inline methods
# include <QNetworkProxy>
+#include <time.h>
#ifdef Q_OS_LINUX
#include <stdio.h>
#include <stdlib.h>
@@ -123,6 +124,8 @@ private slots:
void constructing();
void bind_data();
void bind();
+ void bindThenResolveHost_data();
+ void bindThenResolveHost();
void setInvalidSocketDescriptor();
#ifndef Q_OS_WINRT
void setSocketDescriptor();
@@ -245,6 +248,9 @@ private:
int earlyBytesWrittenCount;
int earlyReadyReadCount;
QString stressTestDir;
+
+ QString firstFailName;
+ QHostInfo firstFailInfo;
};
enum ProxyTests {
@@ -297,7 +303,10 @@ public:
};
tst_QTcpSocket::tst_QTcpSocket()
+ : firstFailName("qt-test-server-first-fail")
{
+ qsrand(time(NULL));
+
tmpSocket = 0;
//This code relates to the socketsConstructedBeforeEventLoop test case
@@ -308,6 +317,8 @@ tst_QTcpSocket::tst_QTcpSocket()
connect(earlyConstructedSockets->endPoints[0], SIGNAL(readyRead()), this, SLOT(earlySocketReadyRead()));
connect(earlyConstructedSockets->endPoints[1], SIGNAL(bytesWritten(qint64)), this, SLOT(earlySocketBytesSent(qint64)));
earlyConstructedSockets->endPoints[1]->write("hello work");
+
+ firstFailInfo.setAddresses(QList<QHostAddress>() << QHostAddress("224.0.0.0") << QtNetworkSettings::serverIP());
}
tst_QTcpSocket::~tst_QTcpSocket()
@@ -389,6 +400,7 @@ void tst_QTcpSocket::init()
}
qt_qhostinfo_clear_cache();
+ qt_qhostinfo_cache_inject(firstFailName, firstFailInfo);
}
QTcpSocket *tst_QTcpSocket::newSocket() const
@@ -486,9 +498,12 @@ void tst_QTcpSocket::constructing()
void tst_QTcpSocket::bind_data()
{
QTest::addColumn<QString>("stringAddr");
+ QTest::addColumn<int>("port");
QTest::addColumn<bool>("successExpected");
QTest::addColumn<QString>("stringExpectedLocalAddress");
+ bool testIpv6 = false;
+
// iterate all interfaces, add all addresses on them as test data
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
foreach (const QNetworkInterface &netinterface, interfaces) {
@@ -501,10 +516,26 @@ void tst_QTcpSocket::bind_data()
continue; // link-local bind will fail, at least on Linux, so skip it.
QString ip(entry.ip().toString());
- QTest::newRow(ip.toLatin1().constData()) << ip << true << ip;
+ QTest::newRow(ip.toLatin1().constData()) << ip << 0 << true << ip;
+
+ if (!testIpv6 && entry.ip().protocol() == QAbstractSocket::IPv6Protocol)
+ testIpv6 = true;
}
}
+ // test binding to localhost
+ QTest::newRow("0.0.0.0") << "0.0.0.0" << 0 << true << "0.0.0.0";
+ if (testIpv6)
+ QTest::newRow("[::]") << "::" << 0 << true << "::";
+
+ // and binding with a port number...
+ // Since we want to test that we got the port number we asked for, we need a random port number.
+ // We use random in case a previous run of the test left the port lingering open.
+ // -1 indicates "random port"
+ QTest::newRow("0.0.0.0:randomport") << "0.0.0.0" << -1 << true << "0.0.0.0";
+ if (testIpv6)
+ QTest::newRow("[::]:randomport") << "::" << -1 << true << "::";
+
// additionally, try bind to known-bad addresses, and make sure this doesn't work
// these ranges are guaranteed to be reserved for 'documentation purposes',
// and thus, should be unused in the real world. Not that I'm assuming the
@@ -513,8 +544,16 @@ void tst_QTcpSocket::bind_data()
knownBad << "198.51.100.1";
knownBad << "2001:0DB8::1";
foreach (const QString &badAddress, knownBad) {
- QTest::newRow(badAddress.toLatin1().constData()) << badAddress << false << QString();
+ QTest::newRow(badAddress.toLatin1().constData()) << badAddress << 0 << false << QString();
}
+
+#ifdef Q_OS_UNIX
+ // try to bind to a privileged ports
+ // we should fail if we're not root (unless the ports are in use!)
+ QTest::newRow("127.0.0.1:1") << "127.0.0.1" << 1 << !geteuid() << (geteuid() ? QString() : "127.0.0.1");
+ if (testIpv6)
+ QTest::newRow("[::]:1") << "::" << 1 << !geteuid() << (geteuid() ? QString() : "::");
+#endif
}
void tst_QTcpSocket::bind()
@@ -523,23 +562,124 @@ void tst_QTcpSocket::bind()
if (setProxy)
return; // QTBUG-22964 for proxies, QTBUG-29972 for QSKIP
QFETCH(QString, stringAddr);
+ QFETCH(int, port);
QFETCH(bool, successExpected);
QFETCH(QString, stringExpectedLocalAddress);
QHostAddress addr(stringAddr);
QHostAddress expectedLocalAddress(stringExpectedLocalAddress);
+ QTcpSocket dummySocket; // used only to "use up" a file descriptor
+ dummySocket.bind();
+
QTcpSocket *socket = newSocket();
- qDebug() << "Binding " << addr;
+ quint16 boundPort;
+ qintptr fd;
if (successExpected) {
- QVERIFY2(socket->bind(addr), qPrintable(socket->errorString()));
+ bool randomPort = port == -1;
+ int attemptsLeft = 5; // only used with randomPort
+ do {
+ if (randomPort) {
+ // try to get a random port number
+ // we do this to ensure we're not trying to bind to the same port as we've just used in
+ // a previous run - race condition with the OS actually freeing the port
+ Q_STATIC_ASSERT(RAND_MAX > 1024);
+ port = qrand() & USHRT_MAX;
+ if (port < 1024)
+ continue;
+ }
+
+ bool bindSuccess = socket->bind(addr, port);
+ if (!bindSuccess && randomPort && socket->error() == QTcpSocket::AddressInUseError) {
+ // we may have been unlucky and hit an already open port, so try another
+ --attemptsLeft;
+ continue;
+ }
+
+ QVERIFY2(bindSuccess, qPrintable(socket->errorString() + ", tried port " + QString::number(port)));
+ break;
+ } while (randomPort && attemptsLeft);
+
+ QCOMPARE(socket->state(), QAbstractSocket::BoundState);
+ boundPort = socket->localPort();
+ if (port)
+ QCOMPARE(int(boundPort), port);
+ fd = socket->socketDescriptor();
+ QVERIFY(fd != INVALID_SOCKET);
} else {
- QVERIFY(!socket->bind(addr));
+ QVERIFY(!socket->bind(addr, port));
+ QCOMPARE(socket->localPort(), quint16(0));
}
QCOMPARE(socket->localAddress(), expectedLocalAddress);
+ if (successExpected) {
+ // try to use the socket and expect it to remain working
+ QTcpServer server;
+ QVERIFY(server.listen(addr));
+
+ // free up the file descriptor
+ dummySocket.close();
+
+ QHostAddress remoteAddr = addr;
+ if (addr == QHostAddress::AnyIPv4)
+ remoteAddr = QHostAddress::LocalHost;
+ else if (addr == QHostAddress::AnyIPv6)
+ remoteAddr = QHostAddress::LocalHostIPv6;
+
+ socket->connectToHost(remoteAddr, server.serverPort());
+ QVERIFY2(socket->waitForConnected(2000), socket->errorString().toLocal8Bit());
+ QVERIFY(server.waitForNewConnection(2000));
+
+ QTcpSocket *acceptedSocket = server.nextPendingConnection();
+ QCOMPARE(socket->localPort(), boundPort);
+ QCOMPARE(acceptedSocket->peerPort(), boundPort);
+ QCOMPARE(socket->localAddress(), remoteAddr);
+ QCOMPARE(socket->socketDescriptor(), fd);
+ }
+
+ delete socket;
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QTcpSocket::bindThenResolveHost_data()
+{
+ QTest::addColumn<QString>("hostName");
+ QTest::newRow("ip-literal") << QtNetworkSettings::serverIP().toString();
+ QTest::newRow("name") << QtNetworkSettings::serverName();
+ QTest::newRow("first-fail") << firstFailName;
+}
+
+// similar to the previous test, but we'll connect to a host name that needs resolving
+void tst_QTcpSocket::bindThenResolveHost()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return; // doesn't make sense to test binding locally with proxies
+
+ QFETCH(QString, hostName);
+
+ QTcpSocket dummySocket; // used only to "use up" a file descriptor
+ dummySocket.bind();
+
+ QTcpSocket *socket = newSocket();
+
+ QVERIFY2(socket->bind(QHostAddress(QHostAddress::AnyIPv4), 0), socket->errorString().toLocal8Bit());
+ QCOMPARE(socket->state(), QAbstractSocket::BoundState);
+ quint16 boundPort = socket->localPort();
+ qintptr fd = socket->socketDescriptor();
+ QVERIFY(fd != INVALID_SOCKET);
+
+ dummySocket.close();
+
+ socket->connectToHost(hostName, 80);
+ QVERIFY2(socket->waitForConnected(), "Network timeout");
+
+ QCOMPARE(socket->localPort(), boundPort);
+ QCOMPARE(socket->socketDescriptor(), fd);
+
delete socket;
}
@@ -592,13 +732,10 @@ void tst_QTcpSocket::setSocketDescriptor()
QCOMPARE(socket->socketDescriptor(), (qintptr)sock);
qt_qhostinfo_clear_cache(); //avoid the HostLookupState being skipped due to address being in cache from previous test.
- socket->connectToHost(QtNetworkSettings::serverName(), 143);
+ socket->connectToHost(QtNetworkSettings::serverName(), 80);
QCOMPARE(socket->state(), QTcpSocket::HostLookupState);
QCOMPARE(socket->socketDescriptor(), (qintptr)sock);
QVERIFY(socket->waitForConnected(10000));
- // skip this, it has been broken for years, see task 260735
- // if somebody complains, consider fixing it, but it might break existing applications.
- QEXPECT_FAIL("", "bug has been around for years, will not fix without need", Continue);
QCOMPARE(socket->socketDescriptor(), (qintptr)sock);
delete socket;
#ifdef Q_OS_WIN
@@ -615,8 +752,8 @@ void tst_QTcpSocket::socketDescriptor()
QCOMPARE(socket->socketDescriptor(), (qintptr)-1);
socket->connectToHost(QtNetworkSettings::serverName(), 143);
- QVERIFY((socket->state() == QAbstractSocket::HostLookupState && socket->socketDescriptor() == -1) ||
- (socket->state() == QAbstractSocket::ConnectingState && socket->socketDescriptor() != -1));
+ QVERIFY(socket->state() == QAbstractSocket::HostLookupState ||
+ socket->state() == QAbstractSocket::ConnectingState);
QVERIFY(socket->waitForConnected(10000));
QVERIFY(socket->state() == QAbstractSocket::ConnectedState);
QVERIFY(socket->socketDescriptor() != -1);
diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
index 76d4543da9..4bd330b04f 100644
--- a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
+++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
@@ -85,7 +85,8 @@ private slots:
void dualStack();
void dualStackAutoBinding();
void dualStackNoIPv4onV6only();
- void readLine();
+ void connectToHost();
+ void bindAndConnectToHost();
void pendingDatagramSize();
void writeDatagram();
void performance();
@@ -621,7 +622,7 @@ void tst_QUdpSocket::empty_connectedSlot()
//----------------------------------------------------------------------------------
-void tst_QUdpSocket::readLine()
+void tst_QUdpSocket::connectToHost()
{
QUdpSocket socket1;
QUdpSocket socket2;
@@ -629,10 +630,41 @@ void tst_QUdpSocket::readLine()
socket1.setProperty("_q_networksession", QVariant::fromValue(networkSession));
socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
+
+ QVERIFY2(socket1.bind(), socket1.errorString().toLatin1().constData());
+
+ socket2.connectToHost(makeNonAny(socket1.localAddress()), socket1.localPort());
+ QVERIFY(socket2.waitForConnected(5000));
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::bindAndConnectToHost()
+{
+ QUdpSocket socket1;
+ QUdpSocket socket2;
+ QUdpSocket dummysocket;
+#ifdef FORCE_SESSION
+ socket1.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+ socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+ dummysocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+
+ // we use the dummy socket to use up a file descriptor
+ dummysocket.bind();
+
+ QVERIFY2(socket2.bind(), socket2.errorString().toLatin1());
+ quint16 boundPort = socket2.localPort();
+ qintptr fd = socket2.socketDescriptor();
+
QVERIFY2(socket1.bind(), socket1.errorString().toLatin1().constData());
+ dummysocket.close();
socket2.connectToHost(makeNonAny(socket1.localAddress()), socket1.localPort());
QVERIFY(socket2.waitForConnected(5000));
+
+ QCOMPARE(socket2.localPort(), boundPort);
+ QCOMPARE(socket2.socketDescriptor(), fd);
}
//----------------------------------------------------------------------------------