summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/socket/qsocks5socketengine.cpp41
-rw-r--r--src/network/socket/qsocks5socketengine_p.h1
-rw-r--r--tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp157
3 files changed, 165 insertions, 34 deletions
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index 76b00bc4a9..48a866cc3a 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -213,11 +213,12 @@ static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 po
/*
retrives the host address in buf at pos and updates pos.
+ return 1 if OK, 0 if need more data, -1 if error
if the func fails the value of the address and the pos is undefined
*/
-static bool qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddress *pAddress, quint16 *pPort, int *pPos)
+static int qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddress *pAddress, quint16 *pPort, int *pPos)
{
- bool ret = false;
+ int ret = -1;
int pos = *pPos;
const unsigned char *pBuf = reinterpret_cast<const unsigned char*>(buf.constData());
QHostAddress address;
@@ -225,27 +226,28 @@ static bool qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddr
if (buf.size() - pos < 1) {
QSOCKS5_DEBUG << "need more data address/port";
- return false;
+ return 0;
}
if (pBuf[pos] == S5_IP_V4) {
pos++;
if (buf.size() - pos < 4) {
QSOCKS5_DEBUG << "need more data for ip4 address";
- return false;
+ return 0;
}
address.setAddress(qFromBigEndian<quint32>(&pBuf[pos]));
pos += 4;
- ret = true;
+ ret = 1;
} else if (pBuf[pos] == S5_IP_V6) {
pos++;
if (buf.size() - pos < 16) {
QSOCKS5_DEBUG << "need more data for ip6 address";
- return false;
+ return 0;
}
QIPv6Address add;
for (int i = 0; i < 16; ++i)
add[i] = buf[pos++];
- ret = true;
+ address.setAddress(add);
+ ret = 1;
} else if (pBuf[pos] == S5_DOMAINNAME){
// just skip it
pos++;
@@ -253,19 +255,19 @@ static bool qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddr
pos += uchar(pBuf[pos]);
} else {
QSOCKS5_DEBUG << "invalid address type" << (int)pBuf[pos];
- ret = false;
+ ret = -1;
}
- if (ret) {
+ if (ret == 1) {
if (buf.size() - pos < 2) {
QSOCKS5_DEBUG << "need more data for port";
- return false;
+ return 0;
}
port = qFromBigEndian<quint16>(&pBuf[pos]);
pos += 2;
}
- if (ret) {
+ if (ret == 1) {
QSOCKS5_DEBUG << "got [" << address << ':' << port << ']';
*pAddress = address;
*pPort = port;
@@ -848,16 +850,20 @@ void QSocks5SocketEnginePrivate::parseRequestMethodReply()
QSOCKS5_DEBUG << "unSeal failed, needs more data";
return;
}
+
+ inBuf.prepend(receivedHeaderFragment);
+ receivedHeaderFragment.clear();
QSOCKS5_DEBUG << dump(inBuf);
- if (inBuf.size() < 2) {
+ if (inBuf.size() < 3) {
QSOCKS5_DEBUG << "need more data for request reply header .. put this data somewhere";
+ receivedHeaderFragment = inBuf;
return;
}
QHostAddress address;
quint16 port = 0;
- if (inBuf.at(0) != S5_VERSION_5 || inBuf.length() < 3 || inBuf.at(2) != 0x00) {
+ if (inBuf.at(0) != S5_VERSION_5 || inBuf.at(2) != 0x00) {
QSOCKS5_DEBUG << "socks protocol error";
setErrorState(SocksError);
} else if (inBuf.at(1) != S5_SUCCESS) {
@@ -873,9 +879,14 @@ void QSocks5SocketEnginePrivate::parseRequestMethodReply()
} else {
// connection success, retrieve the remote addresses
int pos = 3;
- if (!qt_socks5_get_host_address_and_port(inBuf, &address, &port, &pos)) {
+ int err = qt_socks5_get_host_address_and_port(inBuf, &address, &port, &pos);
+ if (err == -1) {
QSOCKS5_DEBUG << "error getting address";
setErrorState(SocksError);
+ } else if (err == 0) {
+ //need more data
+ receivedHeaderFragment = inBuf;
+ return;
} else {
inBuf.remove(0, pos);
for (int i = inBuf.size() - 1; i >= 0 ; --i)
@@ -1310,7 +1321,7 @@ void QSocks5SocketEnginePrivate::_q_udpSocketReadNotification()
QSOCKS5_D_DEBUG << "don't support fragmentation yet disgarding";
return;
}
- if (!qt_socks5_get_host_address_and_port(inBuf, &datagram.address, &datagram.port, &pos)) {
+ if (qt_socks5_get_host_address_and_port(inBuf, &datagram.address, &datagram.port, &pos) != 1) {
QSOCKS5_D_DEBUG << "failed to get address from datagram disgarding";
return;
}
diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h
index 662ce0d347..01b9d1ea7d 100644
--- a/src/network/socket/qsocks5socketengine_p.h
+++ b/src/network/socket/qsocks5socketengine_p.h
@@ -270,6 +270,7 @@ public:
#endif
QSocks5BindData *bindData;
QString peerName;
+ QByteArray receivedHeaderFragment;
mutable bool readNotificationActivated;
mutable bool writeNotificationActivated;
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)