| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| |
| #include <QtTest/QTest> |
| #include <QtTest/QTestEventLoop> |
| |
| #include <QtCore/QQueue> |
| #include <QtCore/QString> |
| #include <QtCore/QCoreApplication> |
| #include <QtCore/QMetaType> |
| #include <QtCore/QTimer> |
| |
| #include <private/qsocks5socketengine_p.h> |
| #include <qhostinfo.h> |
| #include <qhostaddress.h> |
| #include <qtcpsocket.h> |
| #include <qauthenticator.h> |
| #include <qdebug.h> |
| #include <qtcpserver.h> |
| #include <qmetatype.h> |
| #include <qdebug.h> |
| |
| #include "../../../network-settings.h" |
| |
| class tst_QSocks5SocketEngine : public QObject, public QAbstractSocketEngineReceiver |
| { |
| Q_OBJECT |
| |
| private slots: |
| void initTestCase(); |
| void construction(); |
| void errorTest_data(); |
| void errorTest(); |
| void simpleConnectToIMAP(); |
| void simpleErrorsAndStates(); |
| void udpTest(); |
| void serverTest(); |
| void tcpSocketBlockingTest(); |
| void tcpSocketNonBlockingTest(); |
| void downloadBigFile(); |
| // void tcpLoopbackPerformance(); |
| void passwordAuth(); |
| void passwordAuth2(); |
| void fragmentation_data(); |
| void fragmentation(); |
| void incomplete_data(); |
| void incomplete(); |
| |
| protected slots: |
| void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); |
| |
| private: |
| void readNotification() { } |
| void writeNotification() { } |
| void closeNotification() { } |
| void exceptionNotification() { } |
| void connectionNotification() { } |
| }; |
| |
| class MiniSocks5ResponseHandler : public QObject |
| { |
| Q_OBJECT |
| public: |
| QQueue<QByteArray> responses; |
| QTcpSocket *client; |
| |
| MiniSocks5ResponseHandler(QQueue<QByteArray> r, QTcpSocket *c, int autoResponseTime) |
| : responses(r), client(c) |
| { |
| client->setParent(this); |
| connect(client, SIGNAL(disconnected()), SLOT(deleteLater())); |
| connect(client, SIGNAL(readyRead()), SLOT(sendNextResponse())); |
| if (autoResponseTime) |
| QTimer::singleShot(autoResponseTime, this, SLOT(sendNextResponse())); |
| } |
| |
| private slots: |
| void sendNextResponse() |
| { |
| // WARNING |
| // this assumes that the client command is received in its entirety |
| // should be ok, since SOCKSv5 commands are rather small |
| if (responses.isEmpty()) |
| client->disconnectFromHost(); |
| else |
| client->write(responses.dequeue()); |
| } |
| }; |
| |
| class MiniSocks5Server: public QTcpServer |
| { |
| Q_OBJECT |
| public: |
| QQueue<QByteArray> responses; |
| int autoResponseTime; |
| |
| MiniSocks5Server(const QQueue<QByteArray> r, int t = 0) |
| : responses(r), autoResponseTime(t) |
| { |
| listen(); |
| connect(this, SIGNAL(newConnection()), SLOT(handleNewConnection())); |
| } |
| |
| private slots: |
| void handleNewConnection() |
| { |
| QTcpSocket *client = nextPendingConnection(); |
| new MiniSocks5ResponseHandler(responses, client, autoResponseTime); |
| } |
| }; |
| |
| void tst_QSocks5SocketEngine::initTestCase() |
| { |
| #ifdef QT_TEST_SERVER |
| QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1080)); |
| QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 80)); |
| QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::imapServerName(), 143)); |
| #else |
| if (!QtNetworkSettings::verifyTestNetworkSettings()) |
| QSKIP("No network test server available"); |
| #endif |
| } |
| |
| //--------------------------------------------------------------------------- |
| void tst_QSocks5SocketEngine::construction() |
| { |
| QSocks5SocketEngine socketDevice; |
| |
| QVERIFY(!socketDevice.isValid()); |
| |
| // Initialize device |
| QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); |
| QVERIFY(socketDevice.isValid()); |
| QCOMPARE(socketDevice.protocol(), QAbstractSocket::IPv4Protocol); |
| QCOMPARE(socketDevice.socketType(), QAbstractSocket::TcpSocket); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
| // QVERIFY(socketDevice.socketDescriptor() != -1); |
| QCOMPARE(socketDevice.localAddress(), QHostAddress()); |
| QCOMPARE(socketDevice.localPort(), quint16(0)); |
| QCOMPARE(socketDevice.peerAddress(), QHostAddress()); |
| QCOMPARE(socketDevice.peerPort(), quint16(0)); |
| QCOMPARE(socketDevice.error(), QAbstractSocket::UnknownSocketError); |
| |
| //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::bytesAvailable() was called in QAbstractSocket::UnconnectedState"); |
| QCOMPARE(socketDevice.bytesAvailable(), 0); |
| |
| //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::hasPendingDatagrams() was called in QAbstractSocket::UnconnectedState"); |
| QVERIFY(!socketDevice.hasPendingDatagrams()); |
| } |
| |
| //--------------------------------------------------------------------------- |
| void tst_QSocks5SocketEngine::errorTest_data() |
| { |
| QTest::addColumn<QString>("hostname"); |
| QTest::addColumn<int>("port"); |
| QTest::addColumn<QString>("username"); |
| QTest::addColumn<QQueue<QByteArray> >("responses"); |
| QTest::addColumn<int>("expectedError"); |
| |
| QQueue<QByteArray> responses; |
| QTest::newRow("proxy-host-not-found") << "this-host-does-not-exist." << 1080 << QString() |
| << responses |
| << int(QAbstractSocket::ProxyNotFoundError); |
| QTest::newRow("proxy-connection-refused") << "127.0.0.1" << 2 << QString() |
| << responses |
| << int(QAbstractSocket::ProxyConnectionRefusedError); |
| |
| #define REPLY(name, contents) \ |
| static const char raw_ ## name [] = contents; \ |
| const QByteArray name = QByteArray::fromRawData(raw_ ## name, sizeof raw_ ## name - 1) |
| |
| REPLY(garbage, "\4\4\4\4"); |
| // authentication method replies |
| REPLY(noAuthentication, "\5\0"); |
| REPLY(passwordAuthentication, "\5\2"); |
| REPLY(garbageAuthentication, "\5\177"); |
| REPLY(noAcceptableAuthentication, "\5\377"); |
| // authentication replies |
| REPLY(authenticationAccepted, "\5\0"); |
| REPLY(authenticationNotAccepted, "\5\1"); |
| // connection replies |
| REPLY(connectionAccepted, "\5\0\0\4\177\0\0\1\0\100"); |
| REPLY(connectionNotAllowed, "\5\2\0"); |
| REPLY(networkUnreachable, "\5\3\0"); |
| REPLY(hostUnreachable, "\5\4\0"); |
| REPLY(connectionRefused, "\5\5\0"); |
| |
| #undef REPLY |
| |
| responses << garbage; |
| QTest::newRow("garbage1") << QString() << 0 << QString() << responses |
| << int(QAbstractSocket::ProxyProtocolError); |
| |
| responses.clear(); |
| responses << noAuthentication << garbage; |
| QTest::newRow("garbage2") << QString() << 0 << QString() << responses |
| << int(QAbstractSocket::ProxyProtocolError); |
| |
| responses.clear(); |
| responses << garbageAuthentication; |
| QTest::newRow("unknown-auth-method") << QString() << 0 << QString() |
| << responses |
| << int(QAbstractSocket::SocketAccessError); |
| |
| responses.clear(); |
| responses << noAcceptableAuthentication; |
| QTest::newRow("no-acceptable-authentication") << QString() << 0 << QString() |
| << responses |
| << int(QAbstractSocket::ProxyAuthenticationRequiredError); |
| |
| responses.clear(); |
| responses << passwordAuthentication << authenticationNotAccepted; |
| QTest::newRow("authentication-required") << QString() << 0 << "foo" |
| << responses |
| << int(QAbstractSocket::ProxyAuthenticationRequiredError); |
| |
| responses.clear(); |
| responses << noAuthentication << connectionNotAllowed; |
| QTest::newRow("connection-not-allowed") << QString() << 0 << QString() |
| << responses |
| << int(QAbstractSocket::SocketAccessError); |
| |
| responses.clear(); |
| responses << noAuthentication << networkUnreachable; |
| QTest::newRow("network-unreachable") << QString() << 0 << QString() |
| << responses |
| << int(QAbstractSocket::NetworkError); |
| |
| responses.clear(); |
| responses << noAuthentication << hostUnreachable; |
| QTest::newRow("host-unreachable") << QString() << 0 << QString() |
| << responses |
| << int(QAbstractSocket::HostNotFoundError); |
| |
| responses.clear(); |
| responses << noAuthentication << connectionRefused; |
| QTest::newRow("connection-refused") << QString() << 0 << QString() |
| << responses |
| << int(QAbstractSocket::ConnectionRefusedError); |
| } |
| |
| void tst_QSocks5SocketEngine::errorTest() |
| { |
| QFETCH(QString, hostname); |
| QFETCH(int, port); |
| QFETCH(QString, username); |
| QFETCH(QQueue<QByteArray>, responses); |
| QFETCH(int, expectedError); |
| |
| MiniSocks5Server server(responses); |
| |
| if (hostname.isEmpty()) { |
| hostname = "127.0.0.1"; |
| port = server.serverPort(); |
| } |
| QTcpSocket socket; |
| socket.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, hostname, port, username, username)); |
| socket.connectToHost("0.1.2.3", 12345); |
| |
| connect(&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), |
| &QTestEventLoop::instance(), SLOT(exitLoop())); |
| QTestEventLoop::instance().enterLoop(10); |
| QVERIFY(!QTestEventLoop::instance().timeout()); |
| |
| QCOMPARE(int(socket.error()), expectedError); |
| } |
| |
| //--------------------------------------------------------------------------- |
| void tst_QSocks5SocketEngine::simpleConnectToIMAP() |
| { |
| QSocks5SocketEngine socketDevice; |
| |
| // Initialize device |
| QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
| |
| socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080)); |
| |
| QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143)); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState); |
| QVERIFY(socketDevice.waitForWrite()); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectedState); |
| QCOMPARE(socketDevice.peerAddress(), QtNetworkSettings::imapServerIp()); |
| |
| // Wait for the greeting |
| QVERIFY2(socketDevice.waitForRead(), qPrintable("Socket error:" + socketDevice.errorString())); |
| |
| // Read the greeting |
| qint64 available = socketDevice.bytesAvailable(); |
| QVERIFY(available > 0); |
| QByteArray array; |
| array.resize(available); |
| QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
| |
| // Check that the greeting is what we expect it to be |
| QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); |
| |
| // Write a logout message |
| QByteArray array2 = "XXXX LOGOUT\r\n"; |
| QVERIFY(socketDevice.write(array2.data(), |
| array2.size()) == array2.size()); |
| |
| // Wait for the response |
| QVERIFY(socketDevice.waitForRead()); |
| |
| available = socketDevice.bytesAvailable(); |
| QVERIFY(available > 0); |
| array.resize(available); |
| QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
| |
| // Check that the greeting is what we expect it to be |
| QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); |
| |
| // Wait for the response |
| QVERIFY(socketDevice.waitForRead()); |
| char c; |
| QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); |
| QCOMPARE(socketDevice.error(), QAbstractSocket::RemoteHostClosedError); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
| } |
| |
| //--------------------------------------------------------------------------- |
| void tst_QSocks5SocketEngine::simpleErrorsAndStates() |
| { |
| { |
| QSocks5SocketEngine socketDevice; |
| |
| // Initialize device |
| QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); |
| |
| socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080)); |
| |
| QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
| QVERIFY(!socketDevice.connectToHost(QHostInfo::fromName(QtNetworkSettings::socksProxyServerName()).addresses().first(), 8088)); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState); |
| if (socketDevice.waitForWrite(15000)) { |
| QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState || |
| socketDevice.state() == QAbstractSocket::ConnectedState); |
| } else { |
| QCOMPARE(socketDevice.error(), QAbstractSocket::SocketTimeoutError); |
| } |
| } |
| |
| } |
| |
| /* |
| //--------------------------------------------------------------------------- |
| void tst_QSocks5SocketEngine::tcpLoopbackPerformance() |
| { |
| QTcpServer server; |
| |
| // Bind to any port on all interfaces |
| QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); |
| QCOMPARE(server.state(), QAbstractSocket::BoundState); |
| quint16 port = server.localPort(); |
| |
| // Listen for incoming connections |
| QVERIFY(server.listen()); |
| QCOMPARE(server.state(), QAbstractSocket::ListeningState); |
| |
| // Initialize a Tcp socket |
| QSocks5SocketEngine client; |
| QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); |
| |
| client.setProxy(QHostAddress("80.232.37.158"), 1081); |
| |
| // Connect to our server |
| if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { |
| QVERIFY(client.waitForWrite()); |
| QVERIFY(client.connectToHost(QHostAddress("127.0.0.1"), port)); |
| } |
| |
| // The server accepts the connectio |
| int socketDescriptor = server.accept(); |
| QVERIFY(socketDescriptor > 0); |
| |
| // A socket device is initialized on the server side, passing the |
| // socket descriptor from accept(). It's pre-connected. |
| QSocketLayer serverSocket; |
| QVERIFY(serverSocket.initialize(socketDescriptor)); |
| QCOMPARE(serverSocket.state(), QAbstractSocket::ConnectedState); |
| |
| const int messageSize = 1024 * 256; |
| QByteArray message1(messageSize, '@'); |
| QByteArray answer(messageSize, '@'); |
| |
| QTime timer; |
| timer.start(); |
| qlonglong readBytes = 0; |
| while (timer.elapsed() < 5000) { |
| qlonglong written = serverSocket.write(message1.data(), message1.size()); |
| while (written > 0) { |
| client.waitForRead(); |
| if (client.bytesAvailable() > 0) { |
| qlonglong readNow = client.read(answer.data(), answer.size()); |
| written -= readNow; |
| readBytes += readNow; |
| } |
| } |
| } |
| |
| qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", |
| readBytes / (1024.0 * 1024.0), |
| timer.elapsed() / 1024.0, |
| (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); |
| } |
| */ |
| |
| //--------------------------------------------------------------------------- |
| void tst_QSocks5SocketEngine::serverTest() |
| { |
| QSocks5SocketEngine server; |
| |
| // Initialize a Tcp socket |
| QVERIFY(server.initialize(QAbstractSocket::TcpSocket)); |
| |
| QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080); |
| |
| server.setProxy(proxy); |
| |
| // Bind to any port on all interfaces |
| QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); |
| QCOMPARE(server.state(), QAbstractSocket::BoundState); |
| |
| // Listen for incoming connections |
| QVERIFY(server.listen()); |
| QCOMPARE(server.state(), QAbstractSocket::ListeningState); |
| |
| // Initialize a Tcp socket |
| QSocks5SocketEngine client; |
| QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); |
| |
| client.setProxy(proxy); |
| |
| // QTest::wait(100000); // ### timing problem on win32 |
| |
| |
| // Connect to our server |
| if (!client.connectToHost(server.localAddress(), server.localPort())) { |
| QVERIFY(client.waitForWrite()); |
| // QTest::wait(100); // ### timing problem on win32 |
| QCOMPARE(client.state(), QAbstractSocket::ConnectedState); |
| //QTest::wait(100); |
| } |
| |
| QVERIFY(server.waitForRead()); |
| |
| // The server accepts the connection |
| int socketDescriptor = server.accept(); |
| QVERIFY(socketDescriptor > 0); |
| |
| // A socket device is initialized on the server side, passing the |
| // socket descriptor from accept(). It's pre-connected. |
| |
| QSocks5SocketEngine serverSocket; |
| QVERIFY(serverSocket.initialize(socketDescriptor)); |
| QCOMPARE(serverSocket.state(), QAbstractSocket::ConnectedState); |
| |
| QCOMPARE(serverSocket.localAddress(), client.peerAddress()); |
| QCOMPARE(serverSocket.localPort(), client.peerPort()); |
| // this seems depends on the socks server implementation, especially |
| // when connecting /to/ the socks server /through/ the same socks server |
| //QCOMPARE(serverSocket.peerAddress(), client.localAddress()); |
| //QCOMPARE(serverSocket.peerPort(), client.localPort()); |
| |
| // The server socket sends a greeting to the client |
| QByteArray greeting = "Greetings!"; |
| QVERIFY(serverSocket.write(greeting.data(), |
| greeting.size()) == greeting.size()); |
| |
| // The client waits for the greeting to arrive |
| QVERIFY(client.waitForRead()); |
| qint64 available = client.bytesAvailable(); |
| QVERIFY(available > 0); |
| |
| // The client reads the greeting and checks that it's correct |
| QByteArray response; |
| response.resize(available); |
| QVERIFY(client.read(response.data(), |
| response.size()) == response.size()); |
| QCOMPARE(response, greeting); |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| void tst_QSocks5SocketEngine::udpTest() |
| { |
| QSocks5SocketEngine udpSocket; |
| |
| // Initialize device #1 |
| QVERIFY(udpSocket.initialize(QAbstractSocket::UdpSocket)); |
| QVERIFY(udpSocket.isValid()); |
| |
| QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080); |
| |
| udpSocket.setProxy(proxy); |
| |
| QCOMPARE(udpSocket.protocol(), QAbstractSocket::IPv4Protocol); |
| QCOMPARE(udpSocket.socketType(), QAbstractSocket::UdpSocket); |
| QCOMPARE(udpSocket.state(), QAbstractSocket::UnconnectedState); |
| |
| // Bind #1 |
| bool bindSuccessful = udpSocket.bind(QHostAddress("0.0.0.0"), 0); |
| if (!bindSuccessful) |
| QEXPECT_FAIL("", "QTBUG-23380 / QTBUG-35490: Fails on some Ubuntu 11.10 x64 configurations and on new network test server", Abort); |
| QVERIFY(bindSuccessful); |
| QCOMPARE(udpSocket.state(), QAbstractSocket::BoundState); |
| QVERIFY(udpSocket.localPort() != 0); |
| |
| // Initialize device #2 |
| QSocks5SocketEngine udpSocket2; |
| QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket)); |
| |
| udpSocket2.setProxy(proxy); |
| |
| // Connect device #2 to #1 |
| QVERIFY(udpSocket2.connectToHost(udpSocket.localAddress(), udpSocket.localPort())); |
| QCOMPARE(udpSocket2.state(), QAbstractSocket::ConnectedState); |
| |
| // Write a message to #1 |
| QByteArray message1 = "hei der"; |
| QVERIFY(udpSocket2.write(message1.data(), |
| message1.size()) == message1.size()); |
| |
| // Read the message from #2 |
| QVERIFY(udpSocket.waitForRead()); |
| QVERIFY(udpSocket.hasPendingDatagrams()); |
| qint64 available = udpSocket.pendingDatagramSize(); |
| QVERIFY(available > 0); |
| QByteArray answer; |
| answer.resize(available); |
| QIpPacketHeader header; |
| QCOMPARE(udpSocket.readDatagram(answer.data(), answer.size(), |
| &header, QAbstractSocketEngine::WantDatagramSender), |
| qint64(message1.size())); |
| QVERIFY(header.senderAddress == udpSocket2.localAddress()); |
| QCOMPARE(header.senderAddress, udpSocket2.localAddress()); |
| QCOMPARE(header.senderPort, udpSocket2.localPort()); |
| } |
| |
| void tst_QSocks5SocketEngine::tcpSocketBlockingTest() |
| { |
| QSocks5SocketEngineHandler socks5; |
| |
| QTcpSocket socket; |
| |
| // Connect |
| socket.connectToHost(QtNetworkSettings::imapServerName(), 143); |
| QVERIFY(socket.waitForConnected()); |
| QCOMPARE(socket.state(), QTcpSocket::ConnectedState); |
| |
| // Read greeting |
| QVERIFY(socket.waitForReadyRead(5000)); |
| QByteArray s = socket.readLine(); |
| QVERIFY2(QtNetworkSettings::compareReplyIMAP(s), s.constData()); |
| |
| // Write NOOP |
| QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); |
| |
| if (!socket.canReadLine()) |
| QVERIFY(socket.waitForReadyRead(5000)); |
| |
| // Read response |
| s = socket.readLine(); |
| QCOMPARE(s, QByteArrayLiteral("1 OK Completed\r\n")); |
| |
| // Write LOGOUT |
| QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); |
| |
| if (!socket.canReadLine()) |
| QVERIFY(socket.waitForReadyRead(5000)); |
| |
| // Read two lines of respose |
| s = socket.readLine(); |
| QCOMPARE(s, QByteArrayLiteral("* BYE LOGOUT received\r\n")); |
| |
| if (!socket.canReadLine()) |
| QVERIFY(socket.waitForReadyRead(5000)); |
| |
| s = socket.readLine(); |
| QCOMPARE(s, QByteArrayLiteral("2 OK Completed\r\n")); |
| |
| // Close the socket |
| socket.close(); |
| |
| // Check that it's closed |
| QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); |
| } |
| |
| //---------------------------------------------------------------------------------- |
| |
| void tst_QSocks5SocketEngine::tcpSocketNonBlockingTest() |
| { |
| QSocks5SocketEngineHandler socks5; |
| |
| qint64 tcpSocketNonBlocking_totalWritten = 0; |
| QStringList tcpSocketNonBlocking_data; |
| QTcpSocket socket; |
| connect(&socket, &QAbstractSocket::hostFound, |
| &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); |
| connect(&socket, &QAbstractSocket::connected, |
| &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); |
| connect(&socket, &QIODevice::bytesWritten, |
| [&tcpSocketNonBlocking_totalWritten] (qint64 written) |
| { |
| tcpSocketNonBlocking_totalWritten += written; |
| QTestEventLoop::instance().exitLoop(); |
| }); |
| |
| connect(&socket, &QIODevice::readyRead, |
| [&tcpSocketNonBlocking_data, &socket] () |
| { |
| while (socket.canReadLine()) |
| tcpSocketNonBlocking_data.append(socket.readLine()); |
| QTestEventLoop::instance().exitLoop(); |
| }); |
| |
| // Connect |
| socket.connectToHost(QtNetworkSettings::imapServerName(), 143); |
| QVERIFY(socket.state() == QTcpSocket::HostLookupState || |
| socket.state() == QTcpSocket::ConnectingState); |
| |
| QTestEventLoop::instance().enterLoop(30); |
| if (QTestEventLoop::instance().timeout()) { |
| QFAIL("Timed out"); |
| } |
| |
| if (socket.state() == QTcpSocket::ConnectingState) { |
| QTestEventLoop::instance().enterLoop(30); |
| if (QTestEventLoop::instance().timeout()) { |
| QFAIL("Timed out"); |
| } |
| } |
| |
| QCOMPARE(socket.state(), QTcpSocket::ConnectedState); |
| |
| QTestEventLoop::instance().enterLoop(30); |
| if (QTestEventLoop::instance().timeout()) { |
| QFAIL("Timed out"); |
| } |
| |
| // Read greeting |
| QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); |
| QByteArray data = tcpSocketNonBlocking_data.at(0).toLatin1(); |
| QVERIFY2(QtNetworkSettings::compareReplyIMAP(data), data.constData()); |
| |
| tcpSocketNonBlocking_data.clear(); |
| |
| tcpSocketNonBlocking_totalWritten = 0; |
| |
| // Write NOOP |
| QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); |
| |
| |
| QTestEventLoop::instance().enterLoop(30); |
| if (QTestEventLoop::instance().timeout()) { |
| QFAIL("Timed out"); |
| } |
| |
| QCOMPARE(tcpSocketNonBlocking_totalWritten, 8); |
| |
| |
| QTestEventLoop::instance().enterLoop(30); |
| if (QTestEventLoop::instance().timeout()) { |
| QFAIL("Timed out"); |
| } |
| |
| // Read response |
| QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); |
| QCOMPARE(tcpSocketNonBlocking_data.at(0), QLatin1String("1 OK Completed\r\n")); |
| tcpSocketNonBlocking_data.clear(); |
| |
| |
| tcpSocketNonBlocking_totalWritten = 0; |
| |
| // Write LOGOUT |
| QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); |
| |
| QTestEventLoop::instance().enterLoop(30); |
| if (QTestEventLoop::instance().timeout()) { |
| QFAIL("Timed out"); |
| } |
| |
| QCOMPARE(tcpSocketNonBlocking_totalWritten, 10); |
| |
| // Wait for greeting |
| QTestEventLoop::instance().enterLoop(30); |
| if (QTestEventLoop::instance().timeout()) { |
| QFAIL("Timed out"); |
| } |
| |
| // Read two lines of respose |
| QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n"); |
| QCOMPARE(tcpSocketNonBlocking_data.at(1).toLatin1().constData(), "2 OK Completed\r\n"); |
| tcpSocketNonBlocking_data.clear(); |
| |
| // Close the socket |
| socket.close(); |
| |
| // Check that it's closed |
| QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); |
| } |
| |
| //---------------------------------------------------------------------------------- |
| |
| void tst_QSocks5SocketEngine::downloadBigFile() |
| { |
| QSocks5SocketEngineHandler socks5; |
| |
| QTcpSocket socket; |
| qint64 bytesAvailable = 0; |
| |
| QElapsedTimer stopWatch; |
| stopWatch.start(); |
| |
| connect(&socket, &QAbstractSocket::connected, |
| &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); |
| connect(&socket, &QIODevice::readyRead, |
| [&socket, &bytesAvailable] () |
| { |
| const QByteArray tmp = socket.readAll(); |
| int correction = tmp.indexOf(char(0), 0); //skip header |
| if (correction == -1) |
| correction = 0; |
| bytesAvailable += (tmp.size() - correction); |
| if (bytesAvailable >= 10000000) |
| QTestEventLoop::instance().exitLoop(); |
| }); |
| |
| connect(&socket, &QAbstractSocket::errorOccurred, |
| [&socket, &stopWatch] (QAbstractSocket::SocketError errorCode) |
| { |
| qWarning().noquote().nospace() << QTest::currentTestFunction() |
| << ": error " << errorCode << ": " << socket.errorString() |
| << " (" << stopWatch.elapsed() << "ms)"; |
| }); |
| |
| socket.connectToHost(QtNetworkSettings::httpServerName(), 80); |
| |
| QTestEventLoop::instance().enterLoop(30); |
| if (QTestEventLoop::instance().timeout()) |
| QFAIL("Network operation timed out"); |
| |
| QByteArray hostName = QtNetworkSettings::httpServerName().toLatin1(); |
| QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
| QVERIFY(socket.write("GET /qtest/mediumfile HTTP/1.0\r\n") > 0); |
| QVERIFY(socket.write("HOST: ") > 0); |
| QVERIFY(socket.write(hostName.data()) > 0); |
| QVERIFY(socket.write("\r\n") > 0); |
| QVERIFY(socket.write("\r\n") > 0); |
| |
| stopWatch.restart(); |
| QTestEventLoop::instance().enterLoop(60); |
| if (QTestEventLoop::instance().timeout()) |
| QFAIL("Network operation timed out"); |
| |
| QCOMPARE(bytesAvailable, qint64(10000000)); |
| |
| QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
| |
| /*qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", |
| bytesAvailable / (1024.0 * 1024.0), |
| stopWatch.elapsed() / 1024.0, |
| (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024));*/ |
| } |
| |
| void tst_QSocks5SocketEngine::passwordAuth() |
| { |
| QSocks5SocketEngine socketDevice; |
| |
| // Initialize device |
| QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
| |
| socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080, "qsockstest", "password")); |
| |
| // Connect to imap.trolltech.com's IP |
| QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143)); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState); |
| QVERIFY(socketDevice.waitForWrite()); |
| if (!socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143)) { |
| qDebug("%d, %s", socketDevice.error(), socketDevice.errorString().toLatin1().constData()); |
| } |
| QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectedState); |
| QCOMPARE(socketDevice.peerAddress(), QtNetworkSettings::serverIP()); |
| |
| // Wait for the greeting |
| QVERIFY(socketDevice.waitForRead()); |
| |
| // Read the greeting |
| qint64 available = socketDevice.bytesAvailable(); |
| QVERIFY(available > 0); |
| QByteArray array; |
| array.resize(available); |
| QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
| |
| // Check that the greeting is what we expect it to be |
| QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); |
| |
| // Write a logout message |
| QByteArray array2 = "XXXX LOGOUT\r\n"; |
| QVERIFY(socketDevice.write(array2.data(), |
| array2.size()) == array2.size()); |
| |
| // Wait for the response |
| QVERIFY(socketDevice.waitForRead()); |
| |
| available = socketDevice.bytesAvailable(); |
| QVERIFY(available > 0); |
| array.resize(available); |
| QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
| |
| // Check that the greeting is what we expect it to be |
| QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); |
| |
| // Wait for the response |
| QVERIFY(socketDevice.waitForRead()); |
| char c; |
| QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); |
| QCOMPARE(socketDevice.error(), QAbstractSocket::RemoteHostClosedError); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
| } |
| |
| //---------------------------------------------------------------------------------- |
| |
| void tst_QSocks5SocketEngine::proxyAuthenticationRequired(const QNetworkProxy &, |
| QAuthenticator *auth) |
| { |
| auth->setUser("qsockstest"); |
| auth->setPassword("password"); |
| } |
| |
| void tst_QSocks5SocketEngine::passwordAuth2() |
| { |
| QSocks5SocketEngine socketDevice; |
| |
| // Initialize device |
| QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
| |
| socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081)); |
| socketDevice.setReceiver(this); |
| |
| QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143)); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState); |
| while (socketDevice.state() == QAbstractSocket::ConnectingState) { |
| QVERIFY(socketDevice.waitForWrite()); |
| socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143); |
| } |
| if (socketDevice.state() != QAbstractSocket::ConnectedState) |
| qDebug("%d, %s", socketDevice.error(), socketDevice.errorString().toLatin1().constData()); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectedState); |
| QCOMPARE(socketDevice.peerAddress(), QtNetworkSettings::imapServerIp()); |
| |
| // Wait for the greeting |
| QVERIFY(socketDevice.waitForRead()); |
| |
| // Read the greeting |
| qint64 available = socketDevice.bytesAvailable(); |
| QVERIFY(available > 0); |
| QByteArray array; |
| array.resize(available); |
| QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
| |
| // Check that the greeting is what we expect it to be |
| QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); |
| |
| // Write a logout message |
| QByteArray array2 = "XXXX LOGOUT\r\n"; |
| QVERIFY(socketDevice.write(array2.data(), |
| array2.size()) == array2.size()); |
| |
| // Wait for the response |
| QVERIFY(socketDevice.waitForRead()); |
| |
| available = socketDevice.bytesAvailable(); |
| QVERIFY(available > 0); |
| array.resize(available); |
| QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
| |
| // Check that the greeting is what we expect it to be |
| QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); |
| |
| // Wait for the response |
| QVERIFY(socketDevice.waitForRead()); |
| char c; |
| QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); |
| QCOMPARE(socketDevice.error(), QAbstractSocket::RemoteHostClosedError); |
| QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
| } |
| |
| void tst_QSocks5SocketEngine::fragmentation_data() |
| { |
| QTest::addColumn<QQueue<QByteArray> >("responses"); |
| |
| QByteArray authMethodNone = QByteArray::fromRawData("\5\0", 2); |
| QByteArray authMethodBasic = QByteArray::fromRawData("\5\2", 2); |
| QByteArray authSuccess = QByteArray::fromRawData("\1\0", 2); |
| QByteArray connectResponseIPv4 = QByteArray::fromRawData("\5\0\0\1\1\2\3\4\5\6", 10); |
| QByteArray connectResponseIPv6 = QByteArray::fromRawData("\5\0\0\4\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\5\6", 22); |
| |
| QQueue<QByteArray> responses; |
| responses << authMethodNone.left(1) << authMethodNone.mid(1) << connectResponseIPv4; |
| QTest::newRow("auth-method") << responses; |
| |
| responses.clear(); |
| responses << authMethodBasic << authSuccess.left(1) << authSuccess.mid(1) << connectResponseIPv4; |
| QTest::newRow("auth-response") << responses; |
| |
| for (int i = 1; i < connectResponseIPv4.length() - 1; i++) { |
| responses.clear(); |
| responses << authMethodNone << connectResponseIPv4.left(i) << connectResponseIPv4.mid(i); |
| QTest::newRow(qPrintable(QString("connect-response-ipv4-") + QString::number(i))) << responses; |
| } |
| |
| for (int i = 1; i < connectResponseIPv6.length() - 1; i++) { |
| responses.clear(); |
| responses << authMethodNone << connectResponseIPv6.left(i) << connectResponseIPv6.mid(i); |
| QTest::newRow(qPrintable(QString("connect-response-ipv6-") + QString::number(i))) << responses; |
| } |
| } |
| |
| void tst_QSocks5SocketEngine::fragmentation() |
| { |
| QFETCH(QQueue<QByteArray>, responses); |
| MiniSocks5Server server(responses, 500); |
| |
| QTcpSocket socket; |
| socket.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, "localhost", server.serverPort(), "user", "password")); |
| socket.connectToHost("0.1.2.3", 12345); |
| |
| connect(&socket, SIGNAL(connected()), |
| &QTestEventLoop::instance(), SLOT(exitLoop())); |
| QTestEventLoop::instance().enterLoop(10); |
| QVERIFY(!QTestEventLoop::instance().timeout()); |
| |
| QVERIFY(socket.localAddress() == QHostAddress("1.2.3.4") || socket.localAddress() == QHostAddress("0123:4567:89ab:cdef:0123:4567:89ab:cdef")); |
| QCOMPARE(socket.localPort(), quint16(0x0506)); |
| } |
| |
| void tst_QSocks5SocketEngine::incomplete_data() |
| { |
| QTest::addColumn<QQueue<QByteArray> >("responses"); |
| |
| QByteArray authMethodNone = QByteArray::fromRawData("\5\0", 2); |
| QByteArray authMethodBasic = QByteArray::fromRawData("\5\2", 2); |
| QByteArray authSuccess = QByteArray::fromRawData("\1\0", 2); |
| QByteArray connectResponseIPv4 = QByteArray::fromRawData("\5\0\0\1\1\2\3\4\5\6", 10); |
| QByteArray connectResponseIPv6 = QByteArray::fromRawData("\5\0\0\4\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\5\6", 22); |
| |
| QQueue<QByteArray> responses; |
| responses << authMethodNone.left(1); |
| QTest::newRow("auth-method") << responses; |
| |
| responses.clear(); |
| responses << authMethodBasic << authSuccess.left(1); |
| QTest::newRow("auth-response") << responses; |
| |
| for (int i = 1; i < connectResponseIPv4.length() - 1; i++) { |
| responses.clear(); |
| responses << authMethodNone << connectResponseIPv4.left(i); |
| QTest::newRow(qPrintable(QString("connect-response-ipv4-") + QString::number(i))) << responses; |
| } |
| |
| for (int i = 1; i < connectResponseIPv6.length() - 1; i++) { |
| responses.clear(); |
| responses << authMethodNone << connectResponseIPv6.left(i); |
| QTest::newRow(qPrintable(QString("connect-response-ipv6-") + QString::number(i))) << responses; |
| } |
| } |
| |
| void tst_QSocks5SocketEngine::incomplete() |
| { |
| QFETCH(QQueue<QByteArray>, responses); |
| MiniSocks5Server server(responses, 500); |
| |
| QTcpSocket socket; |
| socket.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, "127.0.0.1", server.serverPort(), "user", "password")); |
| socket.connectToHost("0.1.2.3", 12345); |
| |
| connect(&socket, SIGNAL(connected()), |
| &QTestEventLoop::instance(), SLOT(exitLoop())); |
| connect(&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), |
| &QTestEventLoop::instance(), SLOT(exitLoop())); |
| QTestEventLoop::instance().enterLoop(70); |
| QVERIFY(!QTestEventLoop::instance().timeout()); |
| |
| QCOMPARE(socket.error(), QAbstractSocket::ProxyConnectionClosedError); |
| } |
| |
| //---------------------------------------------------------------------------------- |
| |
| QTEST_MAIN(tst_QSocks5SocketEngine) |
| #include "tst_qsocks5socketengine.moc" |