1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QTest>
31#include <QtTest/QTestEventLoop>
32
33#include <QtCore/QQueue>
34#include <QtCore/QString>
35#include <QtCore/QCoreApplication>
36#include <QtCore/QMetaType>
37#include <QtCore/QTimer>
38
39#include <private/qsocks5socketengine_p.h>
40#include <qhostinfo.h>
41#include <qhostaddress.h>
42#include <qtcpsocket.h>
43#include <qauthenticator.h>
44#include <qdebug.h>
45#include <qtcpserver.h>
46#include <qmetatype.h>
47#include <qdebug.h>
48
49#include "../../../network-settings.h"
50
51class tst_QSocks5SocketEngine : public QObject, public QAbstractSocketEngineReceiver
52{
53 Q_OBJECT
54
55private slots:
56 void initTestCase();
57 void construction();
58 void errorTest_data();
59 void errorTest();
60 void simpleConnectToIMAP();
61 void simpleErrorsAndStates();
62 void udpTest();
63 void serverTest();
64 void tcpSocketBlockingTest();
65 void tcpSocketNonBlockingTest();
66 void downloadBigFile();
67 // void tcpLoopbackPerformance();
68 void passwordAuth();
69 void passwordAuth2();
70 void fragmentation_data();
71 void fragmentation();
72 void incomplete_data();
73 void incomplete();
74
75protected slots:
76 void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);
77
78private:
79 void readNotification() { }
80 void writeNotification() { }
81 void closeNotification() { }
82 void exceptionNotification() { }
83 void connectionNotification() { }
84};
85
86class MiniSocks5ResponseHandler : public QObject
87{
88 Q_OBJECT
89public:
90 QQueue<QByteArray> responses;
91 QTcpSocket *client;
92
93 MiniSocks5ResponseHandler(QQueue<QByteArray> r, QTcpSocket *c, int autoResponseTime)
94 : responses(r), client(c)
95 {
96 client->setParent(this);
97 connect(asender: client, SIGNAL(disconnected()), SLOT(deleteLater()));
98 connect(asender: client, SIGNAL(readyRead()), SLOT(sendNextResponse()));
99 if (autoResponseTime)
100 QTimer::singleShot(msec: autoResponseTime, receiver: this, SLOT(sendNextResponse()));
101 }
102
103private slots:
104 void sendNextResponse()
105 {
106 // WARNING
107 // this assumes that the client command is received in its entirety
108 // should be ok, since SOCKSv5 commands are rather small
109 if (responses.isEmpty())
110 client->disconnectFromHost();
111 else
112 client->write(data: responses.dequeue());
113 }
114};
115
116class MiniSocks5Server: public QTcpServer
117{
118 Q_OBJECT
119public:
120 QQueue<QByteArray> responses;
121 int autoResponseTime;
122
123 MiniSocks5Server(const QQueue<QByteArray> r, int t = 0)
124 : responses(r), autoResponseTime(t)
125 {
126 listen();
127 connect(asender: this, SIGNAL(newConnection()), SLOT(handleNewConnection()));
128 }
129
130private slots:
131 void handleNewConnection()
132 {
133 QTcpSocket *client = nextPendingConnection();
134 new MiniSocks5ResponseHandler(responses, client, autoResponseTime);
135 }
136};
137
138void tst_QSocks5SocketEngine::initTestCase()
139{
140#ifdef QT_TEST_SERVER
141 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1080));
142 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 80));
143 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::imapServerName(), 143));
144#else
145 if (!QtNetworkSettings::verifyTestNetworkSettings())
146 QSKIP("No network test server available");
147#endif
148}
149
150//---------------------------------------------------------------------------
151void tst_QSocks5SocketEngine::construction()
152{
153 QSocks5SocketEngine socketDevice;
154
155 QVERIFY(!socketDevice.isValid());
156
157 // Initialize device
158 QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol));
159 QVERIFY(socketDevice.isValid());
160 QCOMPARE(socketDevice.protocol(), QAbstractSocket::IPv4Protocol);
161 QCOMPARE(socketDevice.socketType(), QAbstractSocket::TcpSocket);
162 QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
163 // QVERIFY(socketDevice.socketDescriptor() != -1);
164 QCOMPARE(socketDevice.localAddress(), QHostAddress());
165 QCOMPARE(socketDevice.localPort(), quint16(0));
166 QCOMPARE(socketDevice.peerAddress(), QHostAddress());
167 QCOMPARE(socketDevice.peerPort(), quint16(0));
168 QCOMPARE(socketDevice.error(), QAbstractSocket::UnknownSocketError);
169
170 //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::bytesAvailable() was called in QAbstractSocket::UnconnectedState");
171 QCOMPARE(socketDevice.bytesAvailable(), 0);
172
173 //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::hasPendingDatagrams() was called in QAbstractSocket::UnconnectedState");
174 QVERIFY(!socketDevice.hasPendingDatagrams());
175}
176
177//---------------------------------------------------------------------------
178void tst_QSocks5SocketEngine::errorTest_data()
179{
180 QTest::addColumn<QString>(name: "hostname");
181 QTest::addColumn<int>(name: "port");
182 QTest::addColumn<QString>(name: "username");
183 QTest::addColumn<QQueue<QByteArray> >(name: "responses");
184 QTest::addColumn<int>(name: "expectedError");
185
186 QQueue<QByteArray> responses;
187 QTest::newRow(dataTag: "proxy-host-not-found") << "this-host-does-not-exist." << 1080 << QString()
188 << responses
189 << int(QAbstractSocket::ProxyNotFoundError);
190 QTest::newRow(dataTag: "proxy-connection-refused") << "127.0.0.1" << 2 << QString()
191 << responses
192 << int(QAbstractSocket::ProxyConnectionRefusedError);
193
194#define REPLY(name, contents) \
195 static const char raw_ ## name [] = contents; \
196 const QByteArray name = QByteArray::fromRawData(raw_ ## name, sizeof raw_ ## name - 1)
197
198 REPLY(garbage, "\4\4\4\4");
199 // authentication method replies
200 REPLY(noAuthentication, "\5\0");
201 REPLY(passwordAuthentication, "\5\2");
202 REPLY(garbageAuthentication, "\5\177");
203 REPLY(noAcceptableAuthentication, "\5\377");
204 // authentication replies
205 REPLY(authenticationAccepted, "\5\0");
206 REPLY(authenticationNotAccepted, "\5\1");
207 // connection replies
208 REPLY(connectionAccepted, "\5\0\0\4\177\0\0\1\0\100");
209 REPLY(connectionNotAllowed, "\5\2\0");
210 REPLY(networkUnreachable, "\5\3\0");
211 REPLY(hostUnreachable, "\5\4\0");
212 REPLY(connectionRefused, "\5\5\0");
213
214#undef REPLY
215
216 responses << garbage;
217 QTest::newRow(dataTag: "garbage1") << QString() << 0 << QString() << responses
218 << int(QAbstractSocket::ProxyProtocolError);
219
220 responses.clear();
221 responses << noAuthentication << garbage;
222 QTest::newRow(dataTag: "garbage2") << QString() << 0 << QString() << responses
223 << int(QAbstractSocket::ProxyProtocolError);
224
225 responses.clear();
226 responses << garbageAuthentication;
227 QTest::newRow(dataTag: "unknown-auth-method") << QString() << 0 << QString()
228 << responses
229 << int(QAbstractSocket::SocketAccessError);
230
231 responses.clear();
232 responses << noAcceptableAuthentication;
233 QTest::newRow(dataTag: "no-acceptable-authentication") << QString() << 0 << QString()
234 << responses
235 << int(QAbstractSocket::ProxyAuthenticationRequiredError);
236
237 responses.clear();
238 responses << passwordAuthentication << authenticationNotAccepted;
239 QTest::newRow(dataTag: "authentication-required") << QString() << 0 << "foo"
240 << responses
241 << int(QAbstractSocket::ProxyAuthenticationRequiredError);
242
243 responses.clear();
244 responses << noAuthentication << connectionNotAllowed;
245 QTest::newRow(dataTag: "connection-not-allowed") << QString() << 0 << QString()
246 << responses
247 << int(QAbstractSocket::SocketAccessError);
248
249 responses.clear();
250 responses << noAuthentication << networkUnreachable;
251 QTest::newRow(dataTag: "network-unreachable") << QString() << 0 << QString()
252 << responses
253 << int(QAbstractSocket::NetworkError);
254
255 responses.clear();
256 responses << noAuthentication << hostUnreachable;
257 QTest::newRow(dataTag: "host-unreachable") << QString() << 0 << QString()
258 << responses
259 << int(QAbstractSocket::HostNotFoundError);
260
261 responses.clear();
262 responses << noAuthentication << connectionRefused;
263 QTest::newRow(dataTag: "connection-refused") << QString() << 0 << QString()
264 << responses
265 << int(QAbstractSocket::ConnectionRefusedError);
266}
267
268void tst_QSocks5SocketEngine::errorTest()
269{
270 QFETCH(QString, hostname);
271 QFETCH(int, port);
272 QFETCH(QString, username);
273 QFETCH(QQueue<QByteArray>, responses);
274 QFETCH(int, expectedError);
275
276 MiniSocks5Server server(responses);
277
278 if (hostname.isEmpty()) {
279 hostname = "127.0.0.1";
280 port = server.serverPort();
281 }
282 QTcpSocket socket;
283 socket.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, hostname, port, username, username));
284 socket.connectToHost(hostName: "0.1.2.3", port: 12345);
285
286 connect(sender: &socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
287 receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
288 QTestEventLoop::instance().enterLoop(secs: 10);
289 QVERIFY(!QTestEventLoop::instance().timeout());
290
291 QCOMPARE(int(socket.error()), expectedError);
292}
293
294//---------------------------------------------------------------------------
295void tst_QSocks5SocketEngine::simpleConnectToIMAP()
296{
297 QSocks5SocketEngine socketDevice;
298
299 // Initialize device
300 QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol));
301 QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
302
303 socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080));
304
305 QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143));
306 QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState);
307 QVERIFY(socketDevice.waitForWrite());
308 QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectedState);
309 QCOMPARE(socketDevice.peerAddress(), QtNetworkSettings::imapServerIp());
310
311 // Wait for the greeting
312 QVERIFY2(socketDevice.waitForRead(), qPrintable("Socket error:" + socketDevice.errorString()));
313
314 // Read the greeting
315 qint64 available = socketDevice.bytesAvailable();
316 QVERIFY(available > 0);
317 QByteArray array;
318 array.resize(size: available);
319 QVERIFY(socketDevice.read(array.data(), array.size()) == available);
320
321 // Check that the greeting is what we expect it to be
322 QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData());
323
324 // Write a logout message
325 QByteArray array2 = "XXXX LOGOUT\r\n";
326 QVERIFY(socketDevice.write(array2.data(),
327 array2.size()) == array2.size());
328
329 // Wait for the response
330 QVERIFY(socketDevice.waitForRead());
331
332 available = socketDevice.bytesAvailable();
333 QVERIFY(available > 0);
334 array.resize(size: available);
335 QVERIFY(socketDevice.read(array.data(), array.size()) == available);
336
337 // Check that the greeting is what we expect it to be
338 QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n");
339
340 // Wait for the response
341 QVERIFY(socketDevice.waitForRead());
342 char c;
343 QVERIFY(socketDevice.read(&c, sizeof(c)) == -1);
344 QCOMPARE(socketDevice.error(), QAbstractSocket::RemoteHostClosedError);
345 QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
346}
347
348//---------------------------------------------------------------------------
349void tst_QSocks5SocketEngine::simpleErrorsAndStates()
350{
351 {
352 QSocks5SocketEngine socketDevice;
353
354 // Initialize device
355 QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol));
356
357 socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080));
358
359 QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
360 QVERIFY(!socketDevice.connectToHost(QHostInfo::fromName(QtNetworkSettings::socksProxyServerName()).addresses().first(), 8088));
361 QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState);
362 if (socketDevice.waitForWrite(msecs: 15000)) {
363 QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState ||
364 socketDevice.state() == QAbstractSocket::ConnectedState);
365 } else {
366 QCOMPARE(socketDevice.error(), QAbstractSocket::SocketTimeoutError);
367 }
368 }
369
370}
371
372/*
373//---------------------------------------------------------------------------
374void tst_QSocks5SocketEngine::tcpLoopbackPerformance()
375{
376 QTcpServer server;
377
378 // Bind to any port on all interfaces
379 QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0));
380 QCOMPARE(server.state(), QAbstractSocket::BoundState);
381 quint16 port = server.localPort();
382
383 // Listen for incoming connections
384 QVERIFY(server.listen());
385 QCOMPARE(server.state(), QAbstractSocket::ListeningState);
386
387 // Initialize a Tcp socket
388 QSocks5SocketEngine client;
389 QVERIFY(client.initialize(QAbstractSocket::TcpSocket));
390
391 client.setProxy(QHostAddress("80.232.37.158"), 1081);
392
393 // Connect to our server
394 if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) {
395 QVERIFY(client.waitForWrite());
396 QVERIFY(client.connectToHost(QHostAddress("127.0.0.1"), port));
397 }
398
399 // The server accepts the connectio
400 int socketDescriptor = server.accept();
401 QVERIFY(socketDescriptor > 0);
402
403 // A socket device is initialized on the server side, passing the
404 // socket descriptor from accept(). It's pre-connected.
405 QSocketLayer serverSocket;
406 QVERIFY(serverSocket.initialize(socketDescriptor));
407 QCOMPARE(serverSocket.state(), QAbstractSocket::ConnectedState);
408
409 const int messageSize = 1024 * 256;
410 QByteArray message1(messageSize, '@');
411 QByteArray answer(messageSize, '@');
412
413 QTime timer;
414 timer.start();
415 qlonglong readBytes = 0;
416 while (timer.elapsed() < 5000) {
417 qlonglong written = serverSocket.write(message1.data(), message1.size());
418 while (written > 0) {
419 client.waitForRead();
420 if (client.bytesAvailable() > 0) {
421 qlonglong readNow = client.read(answer.data(), answer.size());
422 written -= readNow;
423 readBytes += readNow;
424 }
425 }
426 }
427
428 qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s",
429 readBytes / (1024.0 * 1024.0),
430 timer.elapsed() / 1024.0,
431 (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024));
432}
433*/
434
435//---------------------------------------------------------------------------
436void tst_QSocks5SocketEngine::serverTest()
437{
438 QSocks5SocketEngine server;
439
440 // Initialize a Tcp socket
441 QVERIFY(server.initialize(QAbstractSocket::TcpSocket));
442
443 QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080);
444
445 server.setProxy(proxy);
446
447 // Bind to any port on all interfaces
448 QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0));
449 QCOMPARE(server.state(), QAbstractSocket::BoundState);
450
451 // Listen for incoming connections
452 QVERIFY(server.listen());
453 QCOMPARE(server.state(), QAbstractSocket::ListeningState);
454
455 // Initialize a Tcp socket
456 QSocks5SocketEngine client;
457 QVERIFY(client.initialize(QAbstractSocket::TcpSocket));
458
459 client.setProxy(proxy);
460
461 // QTest::wait(100000); // ### timing problem on win32
462
463
464 // Connect to our server
465 if (!client.connectToHost(address: server.localAddress(), port: server.localPort())) {
466 QVERIFY(client.waitForWrite());
467 // QTest::wait(100); // ### timing problem on win32
468 QCOMPARE(client.state(), QAbstractSocket::ConnectedState);
469 //QTest::wait(100);
470 }
471
472 QVERIFY(server.waitForRead());
473
474 // The server accepts the connection
475 int socketDescriptor = server.accept();
476 QVERIFY(socketDescriptor > 0);
477
478 // A socket device is initialized on the server side, passing the
479 // socket descriptor from accept(). It's pre-connected.
480
481 QSocks5SocketEngine serverSocket;
482 QVERIFY(serverSocket.initialize(socketDescriptor));
483 QCOMPARE(serverSocket.state(), QAbstractSocket::ConnectedState);
484
485 QCOMPARE(serverSocket.localAddress(), client.peerAddress());
486 QCOMPARE(serverSocket.localPort(), client.peerPort());
487 // this seems depends on the socks server implementation, especially
488 // when connecting /to/ the socks server /through/ the same socks server
489 //QCOMPARE(serverSocket.peerAddress(), client.localAddress());
490 //QCOMPARE(serverSocket.peerPort(), client.localPort());
491
492 // The server socket sends a greeting to the client
493 QByteArray greeting = "Greetings!";
494 QVERIFY(serverSocket.write(greeting.data(),
495 greeting.size()) == greeting.size());
496
497 // The client waits for the greeting to arrive
498 QVERIFY(client.waitForRead());
499 qint64 available = client.bytesAvailable();
500 QVERIFY(available > 0);
501
502 // The client reads the greeting and checks that it's correct
503 QByteArray response;
504 response.resize(size: available);
505 QVERIFY(client.read(response.data(),
506 response.size()) == response.size());
507 QCOMPARE(response, greeting);
508}
509
510
511//---------------------------------------------------------------------------
512void tst_QSocks5SocketEngine::udpTest()
513{
514 QSocks5SocketEngine udpSocket;
515
516 // Initialize device #1
517 QVERIFY(udpSocket.initialize(QAbstractSocket::UdpSocket));
518 QVERIFY(udpSocket.isValid());
519
520 QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080);
521
522 udpSocket.setProxy(proxy);
523
524 QCOMPARE(udpSocket.protocol(), QAbstractSocket::IPv4Protocol);
525 QCOMPARE(udpSocket.socketType(), QAbstractSocket::UdpSocket);
526 QCOMPARE(udpSocket.state(), QAbstractSocket::UnconnectedState);
527
528 // Bind #1
529 bool bindSuccessful = udpSocket.bind(address: QHostAddress("0.0.0.0"), port: 0);
530 if (!bindSuccessful)
531 QEXPECT_FAIL("", "QTBUG-23380 / QTBUG-35490: Fails on some Ubuntu 11.10 x64 configurations and on new network test server", Abort);
532 QVERIFY(bindSuccessful);
533 QCOMPARE(udpSocket.state(), QAbstractSocket::BoundState);
534 QVERIFY(udpSocket.localPort() != 0);
535
536 // Initialize device #2
537 QSocks5SocketEngine udpSocket2;
538 QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket));
539
540 udpSocket2.setProxy(proxy);
541
542 // Connect device #2 to #1
543 QVERIFY(udpSocket2.connectToHost(udpSocket.localAddress(), udpSocket.localPort()));
544 QCOMPARE(udpSocket2.state(), QAbstractSocket::ConnectedState);
545
546 // Write a message to #1
547 QByteArray message1 = "hei der";
548 QVERIFY(udpSocket2.write(message1.data(),
549 message1.size()) == message1.size());
550
551 // Read the message from #2
552 QVERIFY(udpSocket.waitForRead());
553 QVERIFY(udpSocket.hasPendingDatagrams());
554 qint64 available = udpSocket.pendingDatagramSize();
555 QVERIFY(available > 0);
556 QByteArray answer;
557 answer.resize(size: available);
558 QIpPacketHeader header;
559 QCOMPARE(udpSocket.readDatagram(answer.data(), answer.size(),
560 &header, QAbstractSocketEngine::WantDatagramSender),
561 qint64(message1.size()));
562 QVERIFY(header.senderAddress == udpSocket2.localAddress());
563 QCOMPARE(header.senderAddress, udpSocket2.localAddress());
564 QCOMPARE(header.senderPort, udpSocket2.localPort());
565}
566
567void tst_QSocks5SocketEngine::tcpSocketBlockingTest()
568{
569 QSocks5SocketEngineHandler socks5;
570
571 QTcpSocket socket;
572
573 // Connect
574 socket.connectToHost(hostName: QtNetworkSettings::imapServerName(), port: 143);
575 QVERIFY(socket.waitForConnected());
576 QCOMPARE(socket.state(), QTcpSocket::ConnectedState);
577
578 // Read greeting
579 QVERIFY(socket.waitForReadyRead(5000));
580 QByteArray s = socket.readLine();
581 QVERIFY2(QtNetworkSettings::compareReplyIMAP(s), s.constData());
582
583 // Write NOOP
584 QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8);
585
586 if (!socket.canReadLine())
587 QVERIFY(socket.waitForReadyRead(5000));
588
589 // Read response
590 s = socket.readLine();
591 QCOMPARE(s, QByteArrayLiteral("1 OK Completed\r\n"));
592
593 // Write LOGOUT
594 QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10);
595
596 if (!socket.canReadLine())
597 QVERIFY(socket.waitForReadyRead(5000));
598
599 // Read two lines of respose
600 s = socket.readLine();
601 QCOMPARE(s, QByteArrayLiteral("* BYE LOGOUT received\r\n"));
602
603 if (!socket.canReadLine())
604 QVERIFY(socket.waitForReadyRead(5000));
605
606 s = socket.readLine();
607 QCOMPARE(s, QByteArrayLiteral("2 OK Completed\r\n"));
608
609 // Close the socket
610 socket.close();
611
612 // Check that it's closed
613 QCOMPARE(socket.state(), QTcpSocket::UnconnectedState);
614}
615
616//----------------------------------------------------------------------------------
617
618void tst_QSocks5SocketEngine::tcpSocketNonBlockingTest()
619{
620 QSocks5SocketEngineHandler socks5;
621
622 qint64 tcpSocketNonBlocking_totalWritten = 0;
623 QStringList tcpSocketNonBlocking_data;
624 QTcpSocket socket;
625 connect(sender: &socket, signal: &QAbstractSocket::hostFound,
626 receiver: &QTestEventLoop::instance(), slot: &QTestEventLoop::exitLoop);
627 connect(sender: &socket, signal: &QAbstractSocket::connected,
628 receiver: &QTestEventLoop::instance(), slot: &QTestEventLoop::exitLoop);
629 connect(sender: &socket, signal: &QIODevice::bytesWritten,
630 slot: [&tcpSocketNonBlocking_totalWritten] (qint64 written)
631 {
632 tcpSocketNonBlocking_totalWritten += written;
633 QTestEventLoop::instance().exitLoop();
634 });
635
636 connect(sender: &socket, signal: &QIODevice::readyRead,
637 slot: [&tcpSocketNonBlocking_data, &socket] ()
638 {
639 while (socket.canReadLine())
640 tcpSocketNonBlocking_data.append(t: socket.readLine());
641 QTestEventLoop::instance().exitLoop();
642 });
643
644 // Connect
645 socket.connectToHost(hostName: QtNetworkSettings::imapServerName(), port: 143);
646 QVERIFY(socket.state() == QTcpSocket::HostLookupState ||
647 socket.state() == QTcpSocket::ConnectingState);
648
649 QTestEventLoop::instance().enterLoop(secs: 30);
650 if (QTestEventLoop::instance().timeout()) {
651 QFAIL("Timed out");
652 }
653
654 if (socket.state() == QTcpSocket::ConnectingState) {
655 QTestEventLoop::instance().enterLoop(secs: 30);
656 if (QTestEventLoop::instance().timeout()) {
657 QFAIL("Timed out");
658 }
659 }
660
661 QCOMPARE(socket.state(), QTcpSocket::ConnectedState);
662
663 QTestEventLoop::instance().enterLoop(secs: 30);
664 if (QTestEventLoop::instance().timeout()) {
665 QFAIL("Timed out");
666 }
667
668 // Read greeting
669 QVERIFY(!tcpSocketNonBlocking_data.isEmpty());
670 QByteArray data = tcpSocketNonBlocking_data.at(i: 0).toLatin1();
671 QVERIFY2(QtNetworkSettings::compareReplyIMAP(data), data.constData());
672
673 tcpSocketNonBlocking_data.clear();
674
675 tcpSocketNonBlocking_totalWritten = 0;
676
677 // Write NOOP
678 QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8);
679
680
681 QTestEventLoop::instance().enterLoop(secs: 30);
682 if (QTestEventLoop::instance().timeout()) {
683 QFAIL("Timed out");
684 }
685
686 QCOMPARE(tcpSocketNonBlocking_totalWritten, 8);
687
688
689 QTestEventLoop::instance().enterLoop(secs: 30);
690 if (QTestEventLoop::instance().timeout()) {
691 QFAIL("Timed out");
692 }
693
694 // Read response
695 QVERIFY(!tcpSocketNonBlocking_data.isEmpty());
696 QCOMPARE(tcpSocketNonBlocking_data.at(0), QLatin1String("1 OK Completed\r\n"));
697 tcpSocketNonBlocking_data.clear();
698
699
700 tcpSocketNonBlocking_totalWritten = 0;
701
702 // Write LOGOUT
703 QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10);
704
705 QTestEventLoop::instance().enterLoop(secs: 30);
706 if (QTestEventLoop::instance().timeout()) {
707 QFAIL("Timed out");
708 }
709
710 QCOMPARE(tcpSocketNonBlocking_totalWritten, 10);
711
712 // Wait for greeting
713 QTestEventLoop::instance().enterLoop(secs: 30);
714 if (QTestEventLoop::instance().timeout()) {
715 QFAIL("Timed out");
716 }
717
718 // Read two lines of respose
719 QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n");
720 QCOMPARE(tcpSocketNonBlocking_data.at(1).toLatin1().constData(), "2 OK Completed\r\n");
721 tcpSocketNonBlocking_data.clear();
722
723 // Close the socket
724 socket.close();
725
726 // Check that it's closed
727 QCOMPARE(socket.state(), QTcpSocket::UnconnectedState);
728}
729
730//----------------------------------------------------------------------------------
731
732void tst_QSocks5SocketEngine::downloadBigFile()
733{
734 QSocks5SocketEngineHandler socks5;
735
736 QTcpSocket socket;
737 qint64 bytesAvailable = 0;
738
739 QElapsedTimer stopWatch;
740 stopWatch.start();
741
742 connect(sender: &socket, signal: &QAbstractSocket::connected,
743 receiver: &QTestEventLoop::instance(), slot: &QTestEventLoop::exitLoop);
744 connect(sender: &socket, signal: &QIODevice::readyRead,
745 slot: [&socket, &bytesAvailable] ()
746 {
747 const QByteArray tmp = socket.readAll();
748 int correction = tmp.indexOf(c: char(0), from: 0); //skip header
749 if (correction == -1)
750 correction = 0;
751 bytesAvailable += (tmp.size() - correction);
752 if (bytesAvailable >= 10000000)
753 QTestEventLoop::instance().exitLoop();
754 });
755
756 connect(sender: &socket, signal: &QAbstractSocket::errorOccurred,
757 slot: [&socket, &stopWatch] (QAbstractSocket::SocketError errorCode)
758 {
759 qWarning().noquote().nospace() << QTest::currentTestFunction()
760 << ": error " << errorCode << ": " << socket.errorString()
761 << " (" << stopWatch.elapsed() << "ms)";
762 });
763
764 socket.connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 80);
765
766 QTestEventLoop::instance().enterLoop(secs: 30);
767 if (QTestEventLoop::instance().timeout())
768 QFAIL("Network operation timed out");
769
770 QByteArray hostName = QtNetworkSettings::httpServerName().toLatin1();
771 QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
772 QVERIFY(socket.write("GET /qtest/mediumfile HTTP/1.0\r\n") > 0);
773 QVERIFY(socket.write("HOST: ") > 0);
774 QVERIFY(socket.write(hostName.data()) > 0);
775 QVERIFY(socket.write("\r\n") > 0);
776 QVERIFY(socket.write("\r\n") > 0);
777
778 stopWatch.restart();
779 QTestEventLoop::instance().enterLoop(secs: 60);
780 if (QTestEventLoop::instance().timeout())
781 QFAIL("Network operation timed out");
782
783 QCOMPARE(bytesAvailable, qint64(10000000));
784
785 QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
786
787 /*qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s",
788 bytesAvailable / (1024.0 * 1024.0),
789 stopWatch.elapsed() / 1024.0,
790 (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024));*/
791}
792
793void tst_QSocks5SocketEngine::passwordAuth()
794{
795 QSocks5SocketEngine socketDevice;
796
797 // Initialize device
798 QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol));
799 QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
800
801 socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080, "qsockstest", "password"));
802
803 // Connect to imap.trolltech.com's IP
804 QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143));
805 QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState);
806 QVERIFY(socketDevice.waitForWrite());
807 if (!socketDevice.connectToHost(address: QtNetworkSettings::imapServerIp(), port: 143)) {
808 qDebug(msg: "%d, %s", socketDevice.error(), socketDevice.errorString().toLatin1().constData());
809 }
810 QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectedState);
811 QCOMPARE(socketDevice.peerAddress(), QtNetworkSettings::serverIP());
812
813 // Wait for the greeting
814 QVERIFY(socketDevice.waitForRead());
815
816 // Read the greeting
817 qint64 available = socketDevice.bytesAvailable();
818 QVERIFY(available > 0);
819 QByteArray array;
820 array.resize(size: available);
821 QVERIFY(socketDevice.read(array.data(), array.size()) == available);
822
823 // Check that the greeting is what we expect it to be
824 QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData());
825
826 // Write a logout message
827 QByteArray array2 = "XXXX LOGOUT\r\n";
828 QVERIFY(socketDevice.write(array2.data(),
829 array2.size()) == array2.size());
830
831 // Wait for the response
832 QVERIFY(socketDevice.waitForRead());
833
834 available = socketDevice.bytesAvailable();
835 QVERIFY(available > 0);
836 array.resize(size: available);
837 QVERIFY(socketDevice.read(array.data(), array.size()) == available);
838
839 // Check that the greeting is what we expect it to be
840 QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n");
841
842 // Wait for the response
843 QVERIFY(socketDevice.waitForRead());
844 char c;
845 QVERIFY(socketDevice.read(&c, sizeof(c)) == -1);
846 QCOMPARE(socketDevice.error(), QAbstractSocket::RemoteHostClosedError);
847 QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
848}
849
850//----------------------------------------------------------------------------------
851
852void tst_QSocks5SocketEngine::proxyAuthenticationRequired(const QNetworkProxy &,
853 QAuthenticator *auth)
854{
855 auth->setUser("qsockstest");
856 auth->setPassword("password");
857}
858
859void tst_QSocks5SocketEngine::passwordAuth2()
860{
861 QSocks5SocketEngine socketDevice;
862
863 // Initialize device
864 QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol));
865 QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
866
867 socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081));
868 socketDevice.setReceiver(this);
869
870 QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143));
871 QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState);
872 while (socketDevice.state() == QAbstractSocket::ConnectingState) {
873 QVERIFY(socketDevice.waitForWrite());
874 socketDevice.connectToHost(address: QtNetworkSettings::imapServerIp(), port: 143);
875 }
876 if (socketDevice.state() != QAbstractSocket::ConnectedState)
877 qDebug(msg: "%d, %s", socketDevice.error(), socketDevice.errorString().toLatin1().constData());
878 QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectedState);
879 QCOMPARE(socketDevice.peerAddress(), QtNetworkSettings::imapServerIp());
880
881 // Wait for the greeting
882 QVERIFY(socketDevice.waitForRead());
883
884 // Read the greeting
885 qint64 available = socketDevice.bytesAvailable();
886 QVERIFY(available > 0);
887 QByteArray array;
888 array.resize(size: available);
889 QVERIFY(socketDevice.read(array.data(), array.size()) == available);
890
891 // Check that the greeting is what we expect it to be
892 QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData());
893
894 // Write a logout message
895 QByteArray array2 = "XXXX LOGOUT\r\n";
896 QVERIFY(socketDevice.write(array2.data(),
897 array2.size()) == array2.size());
898
899 // Wait for the response
900 QVERIFY(socketDevice.waitForRead());
901
902 available = socketDevice.bytesAvailable();
903 QVERIFY(available > 0);
904 array.resize(size: available);
905 QVERIFY(socketDevice.read(array.data(), array.size()) == available);
906
907 // Check that the greeting is what we expect it to be
908 QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n");
909
910 // Wait for the response
911 QVERIFY(socketDevice.waitForRead());
912 char c;
913 QVERIFY(socketDevice.read(&c, sizeof(c)) == -1);
914 QCOMPARE(socketDevice.error(), QAbstractSocket::RemoteHostClosedError);
915 QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
916}
917
918void tst_QSocks5SocketEngine::fragmentation_data()
919{
920 QTest::addColumn<QQueue<QByteArray> >(name: "responses");
921
922 QByteArray authMethodNone = QByteArray::fromRawData("\5\0", size: 2);
923 QByteArray authMethodBasic = QByteArray::fromRawData("\5\2", size: 2);
924 QByteArray authSuccess = QByteArray::fromRawData("\1\0", size: 2);
925 QByteArray connectResponseIPv4 = QByteArray::fromRawData("\5\0\0\1\1\2\3\4\5\6", size: 10);
926 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", size: 22);
927
928 QQueue<QByteArray> responses;
929 responses << authMethodNone.left(len: 1) << authMethodNone.mid(index: 1) << connectResponseIPv4;
930 QTest::newRow(dataTag: "auth-method") << responses;
931
932 responses.clear();
933 responses << authMethodBasic << authSuccess.left(len: 1) << authSuccess.mid(index: 1) << connectResponseIPv4;
934 QTest::newRow(dataTag: "auth-response") << responses;
935
936 for (int i = 1; i < connectResponseIPv4.length() - 1; i++) {
937 responses.clear();
938 responses << authMethodNone << connectResponseIPv4.left(len: i) << connectResponseIPv4.mid(index: i);
939 QTest::newRow(qPrintable(QString("connect-response-ipv4-") + QString::number(i))) << responses;
940 }
941
942 for (int i = 1; i < connectResponseIPv6.length() - 1; i++) {
943 responses.clear();
944 responses << authMethodNone << connectResponseIPv6.left(len: i) << connectResponseIPv6.mid(index: i);
945 QTest::newRow(qPrintable(QString("connect-response-ipv6-") + QString::number(i))) << responses;
946 }
947}
948
949void tst_QSocks5SocketEngine::fragmentation()
950{
951 QFETCH(QQueue<QByteArray>, responses);
952 MiniSocks5Server server(responses, 500);
953
954 QTcpSocket socket;
955 socket.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, "localhost", server.serverPort(), "user", "password"));
956 socket.connectToHost(hostName: "0.1.2.3", port: 12345);
957
958 connect(sender: &socket, SIGNAL(connected()),
959 receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
960 QTestEventLoop::instance().enterLoop(secs: 10);
961 QVERIFY(!QTestEventLoop::instance().timeout());
962
963 QVERIFY(socket.localAddress() == QHostAddress("1.2.3.4") || socket.localAddress() == QHostAddress("0123:4567:89ab:cdef:0123:4567:89ab:cdef"));
964 QCOMPARE(socket.localPort(), quint16(0x0506));
965}
966
967void tst_QSocks5SocketEngine::incomplete_data()
968{
969 QTest::addColumn<QQueue<QByteArray> >(name: "responses");
970
971 QByteArray authMethodNone = QByteArray::fromRawData("\5\0", size: 2);
972 QByteArray authMethodBasic = QByteArray::fromRawData("\5\2", size: 2);
973 QByteArray authSuccess = QByteArray::fromRawData("\1\0", size: 2);
974 QByteArray connectResponseIPv4 = QByteArray::fromRawData("\5\0\0\1\1\2\3\4\5\6", size: 10);
975 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", size: 22);
976
977 QQueue<QByteArray> responses;
978 responses << authMethodNone.left(len: 1);
979 QTest::newRow(dataTag: "auth-method") << responses;
980
981 responses.clear();
982 responses << authMethodBasic << authSuccess.left(len: 1);
983 QTest::newRow(dataTag: "auth-response") << responses;
984
985 for (int i = 1; i < connectResponseIPv4.length() - 1; i++) {
986 responses.clear();
987 responses << authMethodNone << connectResponseIPv4.left(len: i);
988 QTest::newRow(qPrintable(QString("connect-response-ipv4-") + QString::number(i))) << responses;
989 }
990
991 for (int i = 1; i < connectResponseIPv6.length() - 1; i++) {
992 responses.clear();
993 responses << authMethodNone << connectResponseIPv6.left(len: i);
994 QTest::newRow(qPrintable(QString("connect-response-ipv6-") + QString::number(i))) << responses;
995 }
996}
997
998void tst_QSocks5SocketEngine::incomplete()
999{
1000 QFETCH(QQueue<QByteArray>, responses);
1001 MiniSocks5Server server(responses, 500);
1002
1003 QTcpSocket socket;
1004 socket.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, "127.0.0.1", server.serverPort(), "user", "password"));
1005 socket.connectToHost(hostName: "0.1.2.3", port: 12345);
1006
1007 connect(sender: &socket, SIGNAL(connected()),
1008 receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
1009 connect(sender: &socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
1010 receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
1011 QTestEventLoop::instance().enterLoop(secs: 70);
1012 QVERIFY(!QTestEventLoop::instance().timeout());
1013
1014 QCOMPARE(socket.error(), QAbstractSocket::ProxyConnectionClosedError);
1015}
1016
1017//----------------------------------------------------------------------------------
1018
1019QTEST_MAIN(tst_QSocks5SocketEngine)
1020#include "tst_qsocks5socketengine.moc"
1021

source code of qtbase/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp