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>
This commit is contained in:
parent
29051bce39
commit
9fb68a90af
@ -689,6 +689,7 @@ void qt_qhostinfo_clear_cache()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef QT_BUILD_INTERNAL
|
||||||
void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
|
void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
|
||||||
{
|
{
|
||||||
QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
|
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 for 60 seconds
|
||||||
// cache 128 items
|
// cache 128 items
|
||||||
QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(128)
|
QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(128)
|
||||||
|
@ -117,6 +117,7 @@ public:
|
|||||||
QHostInfo Q_NETWORK_EXPORT qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id);
|
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_clear_cache();
|
||||||
void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e);
|
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
|
class QHostInfoCache
|
||||||
{
|
{
|
||||||
|
@ -976,7 +976,7 @@ void QAbstractSocketPrivate::startConnectingByName(const QString &host)
|
|||||||
|
|
||||||
connectTimeElapsed = 0;
|
connectTimeElapsed = 0;
|
||||||
|
|
||||||
if (initSocketLayer(QAbstractSocket::UnknownNetworkLayerProtocol)) {
|
if (cachedSocketDescriptor != -1 || initSocketLayer(QAbstractSocket::UnknownNetworkLayerProtocol)) {
|
||||||
if (socketEngine->connectToHostByName(host, port) ||
|
if (socketEngine->connectToHostByName(host, port) ||
|
||||||
socketEngine->state() == QAbstractSocket::ConnectingState) {
|
socketEngine->state() == QAbstractSocket::ConnectingState) {
|
||||||
cachedSocketDescriptor = socketEngine->socketDescriptor();
|
cachedSocketDescriptor = socketEngine->socketDescriptor();
|
||||||
@ -1117,7 +1117,7 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
|
|||||||
host.toString().toLatin1().constData(), port, addresses.count());
|
host.toString().toLatin1().constData(), port, addresses.count());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!initSocketLayer(host.protocol())) {
|
if (cachedSocketDescriptor == -1 && !initSocketLayer(host.protocol())) {
|
||||||
// hope that the next address is better
|
// hope that the next address is better
|
||||||
#if defined(QABSTRACTSOCKET_DEBUG)
|
#if defined(QABSTRACTSOCKET_DEBUG)
|
||||||
qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), failed to initialize sock layer");
|
qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), failed to initialize sock layer");
|
||||||
@ -1134,9 +1134,6 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
|
|||||||
return;
|
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
|
// Check that we're in delayed connection state. If not, try
|
||||||
// the next address
|
// the next address
|
||||||
if (socketEngine->state() != QAbstractSocket::ConnectingState) {
|
if (socketEngine->state() != QAbstractSocket::ConnectingState) {
|
||||||
@ -1481,54 +1478,60 @@ void QAbstractSocket::setPauseMode(PauseModes pauseMode)
|
|||||||
bool QAbstractSocket::bind(const QHostAddress &address, quint16 port, BindMode mode)
|
bool QAbstractSocket::bind(const QHostAddress &address, quint16 port, BindMode mode)
|
||||||
{
|
{
|
||||||
Q_D(QAbstractSocket);
|
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
|
// 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;
|
QHostAddress nullAddress;
|
||||||
d->resolveProxy(nullAddress.toString(), port);
|
resolveProxy(nullAddress.toString(), port);
|
||||||
|
|
||||||
QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
|
QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
|
||||||
if (protocol == QAbstractSocket::UnknownNetworkLayerProtocol)
|
if (protocol == QAbstractSocket::UnknownNetworkLayerProtocol)
|
||||||
protocol = nullAddress.protocol();
|
protocol = nullAddress.protocol();
|
||||||
|
|
||||||
if (!d->initSocketLayer(protocol))
|
if (!initSocketLayer(protocol))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode != DefaultForPlatform) {
|
if (mode != QAbstractSocket::DefaultForPlatform) {
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
if ((mode & ShareAddress) || (mode & ReuseAddressHint))
|
if ((mode & QAbstractSocket::ShareAddress) || (mode & QAbstractSocket::ReuseAddressHint))
|
||||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
|
socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
|
||||||
else
|
else
|
||||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
|
socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
|
||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
if (mode & ReuseAddressHint)
|
if (mode & QAbstractSocket::ReuseAddressHint)
|
||||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
|
socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
|
||||||
else
|
else
|
||||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
|
socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
|
||||||
if (mode & DontShareAddress)
|
if (mode & QAbstractSocket::DontShareAddress)
|
||||||
d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 1);
|
socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 1);
|
||||||
else
|
else
|
||||||
d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 0);
|
socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
bool result = d->socketEngine->bind(address, port);
|
bool result = socketEngine->bind(address, port);
|
||||||
d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
|
cachedSocketDescriptor = socketEngine->socketDescriptor();
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
d->socketError = d->socketEngine->error();
|
socketError = socketEngine->error();
|
||||||
setErrorString(d->socketEngine->errorString());
|
q->setErrorString(socketEngine->errorString());
|
||||||
emit error(d->socketError);
|
emit q->error(socketError);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
d->state = BoundState;
|
state = QAbstractSocket::BoundState;
|
||||||
d->localAddress = d->socketEngine->localAddress();
|
localAddress = socketEngine->localAddress();
|
||||||
d->localPort = d->socketEngine->localPort();
|
localPort = socketEngine->localPort();
|
||||||
|
|
||||||
emit stateChanged(d->state);
|
emit q->stateChanged(state);
|
||||||
d->socketEngine->setReadNotificationEnabled(true);
|
socketEngine->setReadNotificationEnabled(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1605,14 +1608,16 @@ void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
|
|||||||
d->preferredNetworkLayerProtocol = protocol;
|
d->preferredNetworkLayerProtocol = protocol;
|
||||||
d->hostName = hostName;
|
d->hostName = hostName;
|
||||||
d->port = port;
|
d->port = port;
|
||||||
d->state = UnconnectedState;
|
|
||||||
d->buffer.clear();
|
d->buffer.clear();
|
||||||
d->writeBuffer.clear();
|
d->writeBuffer.clear();
|
||||||
d->abortCalled = false;
|
d->abortCalled = false;
|
||||||
d->pendingClose = false;
|
d->pendingClose = false;
|
||||||
d->localPort = 0;
|
if (d->state != BoundState) {
|
||||||
|
d->state = UnconnectedState;
|
||||||
|
d->localPort = 0;
|
||||||
|
d->localAddress.clear();
|
||||||
|
}
|
||||||
d->peerPort = 0;
|
d->peerPort = 0;
|
||||||
d->localAddress.clear();
|
|
||||||
d->peerAddress.clear();
|
d->peerAddress.clear();
|
||||||
d->peerName = hostName;
|
d->peerName = hostName;
|
||||||
if (d->hostLookupId != -1) {
|
if (d->hostLookupId != -1) {
|
||||||
|
@ -135,9 +135,11 @@ public:
|
|||||||
PauseModes pauseMode() const;
|
PauseModes pauseMode() const;
|
||||||
void setPauseMode(PauseModes pauseMode);
|
void setPauseMode(PauseModes pauseMode);
|
||||||
|
|
||||||
|
// ### Qt6: make the first one virtual
|
||||||
bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);
|
bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);
|
||||||
bool bind(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 QString &hostName, quint16 port, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
|
||||||
virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
|
virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
|
||||||
virtual void disconnectFromHost();
|
virtual void disconnectFromHost();
|
||||||
|
@ -78,6 +78,8 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
virtual bool bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode);
|
||||||
|
|
||||||
bool canReadNotification();
|
bool canReadNotification();
|
||||||
bool canWriteNotification();
|
bool canWriteNotification();
|
||||||
void canCloseNotification();
|
void canCloseNotification();
|
||||||
|
@ -136,6 +136,12 @@ QT_BEGIN_NAMESPACE
|
|||||||
" not in "#state1" or "#state2); \
|
" not in "#state1" or "#state2); \
|
||||||
return (returnValue); \
|
return (returnValue); \
|
||||||
} } while (0)
|
} } 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 { \
|
#define Q_CHECK_TYPE(function, type, returnValue) do { \
|
||||||
if (d->socketType != (type)) { \
|
if (d->socketType != (type)) { \
|
||||||
qWarning(#function" was called by a" \
|
qWarning(#function" was called by a" \
|
||||||
@ -495,7 +501,7 @@ bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 por
|
|||||||
if (!d->checkProxy(address))
|
if (!d->checkProxy(address))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Q_CHECK_STATES(QNativeSocketEngine::connectToHost(),
|
Q_CHECK_STATES3(QNativeSocketEngine::connectToHost(), QAbstractSocket::BoundState,
|
||||||
QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);
|
QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);
|
||||||
|
|
||||||
d->peerAddress = address;
|
d->peerAddress = address;
|
||||||
@ -961,7 +967,7 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
|
|||||||
QNativeSocketEnginePrivate::TimeOutErrorString);
|
QNativeSocketEnginePrivate::TimeOutErrorString);
|
||||||
d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions
|
d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions
|
||||||
return false;
|
return false;
|
||||||
} else if (state() == QAbstractSocket::ConnectingState) {
|
} else if (state() == QAbstractSocket::ConnectingState || (state() == QAbstractSocket::BoundState && d->socketDescriptor != -1)) {
|
||||||
connectToHost(d->peerAddress, d->peerPort);
|
connectToHost(d->peerAddress, d->peerPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2404,6 +2404,29 @@ bool QSslSocketPrivate::isPaused() const
|
|||||||
return paused;
|
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
|
\internal
|
||||||
*/
|
*/
|
||||||
|
@ -172,6 +172,7 @@ public:
|
|||||||
static void checkSettingSslContext(QSslSocket*, QSharedPointer<QSslContext>);
|
static void checkSettingSslContext(QSslSocket*, QSharedPointer<QSslContext>);
|
||||||
static QSharedPointer<QSslContext> sslContext(QSslSocket *socket);
|
static QSharedPointer<QSslContext> sslContext(QSslSocket *socket);
|
||||||
bool isPaused() const;
|
bool isPaused() const;
|
||||||
|
bool bind(const QHostAddress &address, quint16, QAbstractSocket::BindMode) Q_DECL_OVERRIDE;
|
||||||
void _q_connectedSlot();
|
void _q_connectedSlot();
|
||||||
void _q_hostFoundSlot();
|
void _q_hostFoundSlot();
|
||||||
void _q_disconnectedSlot();
|
void _q_disconnectedSlot();
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
// RVCT compiles also unused inline methods
|
// RVCT compiles also unused inline methods
|
||||||
# include <QNetworkProxy>
|
# include <QNetworkProxy>
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -123,6 +124,8 @@ private slots:
|
|||||||
void constructing();
|
void constructing();
|
||||||
void bind_data();
|
void bind_data();
|
||||||
void bind();
|
void bind();
|
||||||
|
void bindThenResolveHost_data();
|
||||||
|
void bindThenResolveHost();
|
||||||
void setInvalidSocketDescriptor();
|
void setInvalidSocketDescriptor();
|
||||||
#ifndef Q_OS_WINRT
|
#ifndef Q_OS_WINRT
|
||||||
void setSocketDescriptor();
|
void setSocketDescriptor();
|
||||||
@ -245,6 +248,9 @@ private:
|
|||||||
int earlyBytesWrittenCount;
|
int earlyBytesWrittenCount;
|
||||||
int earlyReadyReadCount;
|
int earlyReadyReadCount;
|
||||||
QString stressTestDir;
|
QString stressTestDir;
|
||||||
|
|
||||||
|
QString firstFailName;
|
||||||
|
QHostInfo firstFailInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ProxyTests {
|
enum ProxyTests {
|
||||||
@ -297,7 +303,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
tst_QTcpSocket::tst_QTcpSocket()
|
tst_QTcpSocket::tst_QTcpSocket()
|
||||||
|
: firstFailName("qt-test-server-first-fail")
|
||||||
{
|
{
|
||||||
|
qsrand(time(NULL));
|
||||||
|
|
||||||
tmpSocket = 0;
|
tmpSocket = 0;
|
||||||
|
|
||||||
//This code relates to the socketsConstructedBeforeEventLoop test case
|
//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[0], SIGNAL(readyRead()), this, SLOT(earlySocketReadyRead()));
|
||||||
connect(earlyConstructedSockets->endPoints[1], SIGNAL(bytesWritten(qint64)), this, SLOT(earlySocketBytesSent(qint64)));
|
connect(earlyConstructedSockets->endPoints[1], SIGNAL(bytesWritten(qint64)), this, SLOT(earlySocketBytesSent(qint64)));
|
||||||
earlyConstructedSockets->endPoints[1]->write("hello work");
|
earlyConstructedSockets->endPoints[1]->write("hello work");
|
||||||
|
|
||||||
|
firstFailInfo.setAddresses(QList<QHostAddress>() << QHostAddress("224.0.0.0") << QtNetworkSettings::serverIP());
|
||||||
}
|
}
|
||||||
|
|
||||||
tst_QTcpSocket::~tst_QTcpSocket()
|
tst_QTcpSocket::~tst_QTcpSocket()
|
||||||
@ -389,6 +400,7 @@ void tst_QTcpSocket::init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
qt_qhostinfo_clear_cache();
|
qt_qhostinfo_clear_cache();
|
||||||
|
qt_qhostinfo_cache_inject(firstFailName, firstFailInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
QTcpSocket *tst_QTcpSocket::newSocket() const
|
QTcpSocket *tst_QTcpSocket::newSocket() const
|
||||||
@ -486,9 +498,12 @@ void tst_QTcpSocket::constructing()
|
|||||||
void tst_QTcpSocket::bind_data()
|
void tst_QTcpSocket::bind_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("stringAddr");
|
QTest::addColumn<QString>("stringAddr");
|
||||||
|
QTest::addColumn<int>("port");
|
||||||
QTest::addColumn<bool>("successExpected");
|
QTest::addColumn<bool>("successExpected");
|
||||||
QTest::addColumn<QString>("stringExpectedLocalAddress");
|
QTest::addColumn<QString>("stringExpectedLocalAddress");
|
||||||
|
|
||||||
|
bool testIpv6 = false;
|
||||||
|
|
||||||
// iterate all interfaces, add all addresses on them as test data
|
// iterate all interfaces, add all addresses on them as test data
|
||||||
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
|
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
|
||||||
foreach (const QNetworkInterface &netinterface, interfaces) {
|
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.
|
continue; // link-local bind will fail, at least on Linux, so skip it.
|
||||||
|
|
||||||
QString ip(entry.ip().toString());
|
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
|
// additionally, try bind to known-bad addresses, and make sure this doesn't work
|
||||||
// these ranges are guaranteed to be reserved for 'documentation purposes',
|
// 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
|
// 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 << "198.51.100.1";
|
||||||
knownBad << "2001:0DB8::1";
|
knownBad << "2001:0DB8::1";
|
||||||
foreach (const QString &badAddress, knownBad) {
|
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()
|
void tst_QTcpSocket::bind()
|
||||||
@ -523,23 +562,124 @@ void tst_QTcpSocket::bind()
|
|||||||
if (setProxy)
|
if (setProxy)
|
||||||
return; // QTBUG-22964 for proxies, QTBUG-29972 for QSKIP
|
return; // QTBUG-22964 for proxies, QTBUG-29972 for QSKIP
|
||||||
QFETCH(QString, stringAddr);
|
QFETCH(QString, stringAddr);
|
||||||
|
QFETCH(int, port);
|
||||||
QFETCH(bool, successExpected);
|
QFETCH(bool, successExpected);
|
||||||
QFETCH(QString, stringExpectedLocalAddress);
|
QFETCH(QString, stringExpectedLocalAddress);
|
||||||
|
|
||||||
QHostAddress addr(stringAddr);
|
QHostAddress addr(stringAddr);
|
||||||
QHostAddress expectedLocalAddress(stringExpectedLocalAddress);
|
QHostAddress expectedLocalAddress(stringExpectedLocalAddress);
|
||||||
|
|
||||||
|
QTcpSocket dummySocket; // used only to "use up" a file descriptor
|
||||||
|
dummySocket.bind();
|
||||||
|
|
||||||
QTcpSocket *socket = newSocket();
|
QTcpSocket *socket = newSocket();
|
||||||
qDebug() << "Binding " << addr;
|
quint16 boundPort;
|
||||||
|
qintptr fd;
|
||||||
|
|
||||||
if (successExpected) {
|
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 {
|
} else {
|
||||||
QVERIFY(!socket->bind(addr));
|
QVERIFY(!socket->bind(addr, port));
|
||||||
|
QCOMPARE(socket->localPort(), quint16(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
QCOMPARE(socket->localAddress(), expectedLocalAddress);
|
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;
|
delete socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,13 +732,10 @@ void tst_QTcpSocket::setSocketDescriptor()
|
|||||||
QCOMPARE(socket->socketDescriptor(), (qintptr)sock);
|
QCOMPARE(socket->socketDescriptor(), (qintptr)sock);
|
||||||
|
|
||||||
qt_qhostinfo_clear_cache(); //avoid the HostLookupState being skipped due to address being in cache from previous test.
|
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->state(), QTcpSocket::HostLookupState);
|
||||||
QCOMPARE(socket->socketDescriptor(), (qintptr)sock);
|
QCOMPARE(socket->socketDescriptor(), (qintptr)sock);
|
||||||
QVERIFY(socket->waitForConnected(10000));
|
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);
|
QCOMPARE(socket->socketDescriptor(), (qintptr)sock);
|
||||||
delete socket;
|
delete socket;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
@ -615,8 +752,8 @@ void tst_QTcpSocket::socketDescriptor()
|
|||||||
|
|
||||||
QCOMPARE(socket->socketDescriptor(), (qintptr)-1);
|
QCOMPARE(socket->socketDescriptor(), (qintptr)-1);
|
||||||
socket->connectToHost(QtNetworkSettings::serverName(), 143);
|
socket->connectToHost(QtNetworkSettings::serverName(), 143);
|
||||||
QVERIFY((socket->state() == QAbstractSocket::HostLookupState && socket->socketDescriptor() == -1) ||
|
QVERIFY(socket->state() == QAbstractSocket::HostLookupState ||
|
||||||
(socket->state() == QAbstractSocket::ConnectingState && socket->socketDescriptor() != -1));
|
socket->state() == QAbstractSocket::ConnectingState);
|
||||||
QVERIFY(socket->waitForConnected(10000));
|
QVERIFY(socket->waitForConnected(10000));
|
||||||
QVERIFY(socket->state() == QAbstractSocket::ConnectedState);
|
QVERIFY(socket->state() == QAbstractSocket::ConnectedState);
|
||||||
QVERIFY(socket->socketDescriptor() != -1);
|
QVERIFY(socket->socketDescriptor() != -1);
|
||||||
|
@ -85,7 +85,8 @@ private slots:
|
|||||||
void dualStack();
|
void dualStack();
|
||||||
void dualStackAutoBinding();
|
void dualStackAutoBinding();
|
||||||
void dualStackNoIPv4onV6only();
|
void dualStackNoIPv4onV6only();
|
||||||
void readLine();
|
void connectToHost();
|
||||||
|
void bindAndConnectToHost();
|
||||||
void pendingDatagramSize();
|
void pendingDatagramSize();
|
||||||
void writeDatagram();
|
void writeDatagram();
|
||||||
void performance();
|
void performance();
|
||||||
@ -621,7 +622,7 @@ void tst_QUdpSocket::empty_connectedSlot()
|
|||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
void tst_QUdpSocket::readLine()
|
void tst_QUdpSocket::connectToHost()
|
||||||
{
|
{
|
||||||
QUdpSocket socket1;
|
QUdpSocket socket1;
|
||||||
QUdpSocket socket2;
|
QUdpSocket socket2;
|
||||||
@ -629,6 +630,7 @@ void tst_QUdpSocket::readLine()
|
|||||||
socket1.setProperty("_q_networksession", QVariant::fromValue(networkSession));
|
socket1.setProperty("_q_networksession", QVariant::fromValue(networkSession));
|
||||||
socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession));
|
socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QVERIFY2(socket1.bind(), socket1.errorString().toLatin1().constData());
|
QVERIFY2(socket1.bind(), socket1.errorString().toLatin1().constData());
|
||||||
|
|
||||||
socket2.connectToHost(makeNonAny(socket1.localAddress()), socket1.localPort());
|
socket2.connectToHost(makeNonAny(socket1.localAddress()), socket1.localPort());
|
||||||
@ -637,6 +639,36 @@ void tst_QUdpSocket::readLine()
|
|||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
void tst_QUdpSocket::pendingDatagramSize()
|
void tst_QUdpSocket::pendingDatagramSize()
|
||||||
{
|
{
|
||||||
QUdpSocket server;
|
QUdpSocket server;
|
||||||
|
Loading…
Reference in New Issue
Block a user