// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) override; private: void readNotification() override { } void writeNotification() override { } void closeNotification() override { } void exceptionNotification() override { } void connectionNotification() override { } }; class MiniSocks5ResponseHandler : public QObject { Q_OBJECT public: QQueue responses; QTcpSocket *client; MiniSocks5ResponseHandler(QQueue 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 responses; int autoResponseTime; MiniSocks5Server(const QQueue 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() { if (qEnvironmentVariable("QT_QPA_PLATFORM").contains("offscreen") && !qEnvironmentVariableIsEmpty("QEMU_LD_PREFIX")) QSKIP("Not support yet for B2Qt"); #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("hostname"); QTest::addColumn("port"); QTest::addColumn("username"); QTest::addColumn >("responses"); QTest::addColumn("expectedError"); QQueue 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, 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, if it hasn't arrived yet qint64 available = socketDevice.bytesAvailable(); if (available == 0) { QVERIFY2(socketDevice.waitForRead(), qPrintable("Socket error:" + socketDevice.errorString())); available = socketDevice.bytesAvailable(); } QVERIFY(available > 0); // Read the greeting 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(50)); 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 >("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 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.size() - 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.size() - 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, 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 >("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 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.size() - 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.size() - 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, 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"