summaryrefslogtreecommitdiffstats
path: root/tests/auto
diff options
context:
space:
mode:
authorShane Kearns <ext-shane.2.kearns@nokia.com>2012-06-12 16:46:37 +0100
committerQt by Nokia <qt-info@nokia.com>2012-06-21 00:58:19 +0200
commit98fd2eeb6292d3391b7364493b960f818896fa23 (patch)
tree21352980ab5abc123d3b09066f916c0c36975f18 /tests/auto
parentd527f2b9339e20500be0b1c46d81625ba0583895 (diff)
Handle fragmented responses on SOCKS5 control channel
Server responses may arrive in more than one packet, though this is rare due to nagle algorithm. Also fixed IPv6 addresses being discarded from server responses, which was caught by the new autotest. Task-number: QTBUG-18564 Change-Id: I32d9e2978037fb3e1fff27b7e618b5da6d222f28 Reviewed-by: Martin Petersson <Martin.Petersson@nokia.com>
Diffstat (limited to 'tests/auto')
-rw-r--r--tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp157
1 files changed, 138 insertions, 19 deletions
diff --git a/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp b/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp
index 3cc1f32622..461ec55dea 100644
--- a/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp
+++ b/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp
@@ -47,6 +47,7 @@
#include <QtCore/QString>
#include <QtCore/QCoreApplication>
#include <QtCore/QMetaType>
+#include <QtCore/QTimer>
#include <private/qsocks5socketengine_p.h>
#include <qhostinfo.h>
@@ -89,6 +90,10 @@ private slots:
// void tcpLoopbackPerformance();
void passwordAuth();
void passwordAuth2();
+ void fragmentation_data();
+ void fragmentation();
+ void incomplete_data();
+ void incomplete();
protected slots:
void tcpSocketNonBlocking_hostFound();
@@ -112,40 +117,55 @@ private:
qint64 bytesAvailable;
};
-class MiniSocks5Server: public QTcpServer
+class MiniSocks5ResponseHandler : public QObject
{
Q_OBJECT
public:
QQueue<QByteArray> responses;
+ QTcpSocket *client;
- MiniSocks5Server(const QQueue<QByteArray> r)
- : responses(r)
+ MiniSocks5ResponseHandler(QQueue<QByteArray> r, QTcpSocket *c, int autoResponseTime)
+ : responses(r), client(c)
{
- listen();
- connect(this, SIGNAL(newConnection()), SLOT(handleNewConnection()));
+ 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 handleNewConnection()
- {
- QTcpSocket *client = nextPendingConnection();
- connect(client, SIGNAL(readyRead()), SLOT(handleClientCommand()));
- client->setProperty("pendingResponses", QVariant::fromValue(responses));
- }
-
- void handleClientCommand()
+ void sendNextResponse()
{
// WARNING
// this assumes that the client command is received in its entirety
// should be ok, since SOCKSv5 commands are rather small
- QTcpSocket *client = static_cast<QTcpSocket *>(sender());
- QQueue<QByteArray> pendingResponses =
- qvariant_cast<QQueue<QByteArray> >(client->property("pendingResponses"));
- if (pendingResponses.isEmpty())
+ if (responses.isEmpty())
client->disconnectFromHost();
else
- client->write(pendingResponses.dequeue());
- client->setProperty("pendingResponses", QVariant::fromValue(pendingResponses));
+ 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);
}
};
@@ -966,6 +986,105 @@ void tst_QSocks5SocketEngine::passwordAuth2()
QVERIFY(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"));
+ QVERIFY(socket.localPort() == 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(error(QAbstractSocket::SocketError)),
+ &QTestEventLoop::instance(), SLOT(exitLoop()));
+ QTestEventLoop::instance().enterLoop(70);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QCOMPARE(socket.error(), QAbstractSocket::ProxyConnectionClosedError);
+}
+
//----------------------------------------------------------------------------------
QTEST_MAIN(tst_QSocks5SocketEngine)