From 6b2413cb386c718ad8974e55d5ab460b07c41a56 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Sun, 23 Oct 2011 20:04:52 +0200 Subject: [BB10-internal] Move support for socket binding from QUdpSocket to QAbstractSocketPrivate (backport of 03f852cb47d508d98aa90f501e9b7f4214e8ad8b) This is a backport of a Qt5 commit, moving the bind method to QAbstractSocketPrivate to retain binary compatibility, yet still being able to use it from the HTTP layer (i.e. QHttpNetworkConnectionChannel). Task-number: QTBUG-121 Change-Id: I733efe90ea70c5774971f11d6a2054c84b467988 Signed-off-by: Peter Hartmann --- src/network/socket/qabstractsocket.cpp | 53 +++++++++++++++++++++++ src/network/socket/qabstractsocket_p.h | 15 +++++++ tests/auto/qtcpsocket/tst_qtcpsocket.cpp | 72 ++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 3db544b849..a11bf89636 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -1234,6 +1234,59 @@ void QAbstractSocketPrivate::resumeSocketNotifiers(QAbstractSocket *socket) socketEngine->setExceptionNotificationEnabled(socket->d_func()->prePauseExceptionSocketNotifierState); } +bool QAbstractSocketPrivate::bind(QAbstractSocket *socket, const QHostAddress &address, + quint16 port, BindMode mode) +{ + QAbstractSocketPrivate *d = socket->d_func(); + + // now check if the socket engine is initialized and to the right type + if (!d->socketEngine || !d->socketEngine->isValid()) { + QHostAddress nullAddress; + d->resolveProxy(nullAddress.toString(), port); + + QAbstractSocket::NetworkLayerProtocol protocol = address.protocol(); + if (protocol == QAbstractSocket::UnknownNetworkLayerProtocol) + protocol = nullAddress.protocol(); + + if (!d->initSocketLayer(protocol)) + return false; + } + +#ifdef Q_OS_UNIX + if ((mode & ShareAddress) || (mode & ReuseAddressHint)) + d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1); + else + d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0); +#endif +#ifdef Q_OS_WIN + if (mode & ReuseAddressHint) + d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1); + else + d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0); + if (mode & DontShareAddress) + d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 1); + else + d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 0); +#endif + bool result = d->socketEngine->bind(address, port); + d->cachedSocketDescriptor = d->socketEngine->socketDescriptor(); + + if (!result) { + d->socketError = d->socketEngine->error(); + socket->setErrorString(d->socketEngine->errorString()); + emit socket->error(d->socketError); + return false; + } + + d->state = QAbstractSocket::BoundState; + d->localAddress = d->socketEngine->localAddress(); + d->localPort = d->socketEngine->localPort(); + + emit socket->stateChanged(d->state); + d->socketEngine->setReadNotificationEnabled(true); + return true; +} + QAbstractSocketEngine* QAbstractSocketPrivate::getSocketEngine(QAbstractSocket *socket) { return socket->d_func()->socketEngine; diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h index 904ee28563..9c63c1276b 100644 --- a/src/network/socket/qabstractsocket_p.h +++ b/src/network/socket/qabstractsocket_p.h @@ -161,9 +161,24 @@ public: bool prePauseExceptionSocketNotifierState; static void pauseSocketNotifiers(QAbstractSocket*); static void resumeSocketNotifiers(QAbstractSocket*); + + enum BindFlag { + DefaultForPlatform = 0x0, + ShareAddress = 0x1, + DontShareAddress = 0x2, + ReuseAddressHint = 0x4 + }; + Q_DECLARE_FLAGS(BindMode, BindFlag) + + Q_AUTOTEST_EXPORT static bool bind(QAbstractSocket *socket, const QHostAddress &address, + quint16 port = 0, BindMode mode = DefaultForPlatform); + // we don't need the other overload for now + static QAbstractSocketEngine* getSocketEngine(QAbstractSocket*); }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractSocketPrivate::BindMode) + QT_END_NAMESPACE #endif // QABSTRACTSOCKET_P_H diff --git a/tests/auto/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/qtcpsocket/tst_qtcpsocket.cpp index f0efc9f832..6de324ab83 100644 --- a/tests/auto/qtcpsocket/tst_qtcpsocket.cpp +++ b/tests/auto/qtcpsocket/tst_qtcpsocket.cpp @@ -92,6 +92,10 @@ #include #endif +#ifdef QT_BUILD_INTERNAL +#include "private/qabstractsocket_p.h" +#endif + #include "private/qhostinfo_p.h" #include "../network-settings.h" @@ -141,6 +145,10 @@ public slots: private slots: void socketsConstructedBeforeEventLoop(); void constructing(); +#ifdef QT_BUILD_INTERNAL + void bind_data(); + void bind(); +#endif void setInvalidSocketDescriptor(); void setSocketDescriptor(); void socketDescriptor(); @@ -476,6 +484,70 @@ void tst_QTcpSocket::constructing() //---------------------------------------------------------------------------------- +#ifdef QT_BUILD_INTERNAL +void tst_QTcpSocket::bind_data() +{ + QTest::addColumn("stringAddr"); + QTest::addColumn("successExpected"); + QTest::addColumn("stringExpectedLocalAddress"); + + // iterate all interfaces, add all addresses on them as test data + QList interfaces = QNetworkInterface::allInterfaces(); + foreach (const QNetworkInterface &interface, interfaces) { + if (!interface.isValid()) + continue; + + foreach (const QNetworkAddressEntry &entry, interface.addressEntries()) { + if (entry.ip().isInSubnet(QHostAddress::parseSubnet("fe80::/10")) + || entry.ip().isInSubnet(QHostAddress::parseSubnet("169.254/16"))) + 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; + } + } + + // 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 + // world is full of competent administrators, or anything. + QStringList knownBad; + knownBad << "198.51.100.1"; + knownBad << "2001:0DB8::1"; + foreach (const QString &badAddress, knownBad) { + QTest::newRow(badAddress.toLatin1().constData()) << badAddress << false << QString(); + } +} + +void tst_QTcpSocket::bind() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + QSKIP("QTBUG-22964", SkipSingle); + QFETCH(QString, stringAddr); + QFETCH(bool, successExpected); + QFETCH(QString, stringExpectedLocalAddress); + + QHostAddress addr(stringAddr); + QHostAddress expectedLocalAddress(stringExpectedLocalAddress); + + QTcpSocket *socket = newSocket(); + qDebug() << "Binding " << addr; + + if (successExpected) { + QVERIFY2(QAbstractSocketPrivate::bind(socket, addr), qPrintable(socket->errorString())); + } else { + QVERIFY(!QAbstractSocketPrivate::bind(socket, addr)); + } + + QCOMPARE(socket->localAddress(), expectedLocalAddress); + + delete socket; +} +#endif + +//---------------------------------------------------------------------------------- + void tst_QTcpSocket::setInvalidSocketDescriptor() { QTcpSocket *socket = newSocket(); -- cgit v1.2.3