1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <QtNetwork/QtNetwork>
44
45 #include <time.h>
46
47 #ifndef QT_NO_BEARERMANAGEMENT
48 #include <QtNetwork/qnetworkconfigmanager.h>
49 #include <QtNetwork/qnetworkconfiguration.h>
50 #include <QtNetwork/qnetworksession.h>
51 #endif
52
53 #ifdef Q_OS_SYMBIAN
54 // In Symbian OS test data is located in applications private dir
55 // Current path (C:\private\<UID>) contains only ascii chars
56 //#define SRCDIR QDir::currentPath()
57 #define SRCDIR "."
58 #endif
59
60 #include "../network-settings.h"
61
62 class tst_NetworkSelfTest: public QObject
63 {
64 Q_OBJECT
65 QHostAddress cachedIpAddress;
66 public:
67 tst_NetworkSelfTest();
68 virtual ~tst_NetworkSelfTest();
69
70 QHostAddress serverIpAddress();
71
72 private slots:
73 void initTestCase();
74 void hostTest();
75 void dnsResolution_data();
76 void dnsResolution();
77 void serverReachability();
78 void remotePortsOpen_data();
79 void remotePortsOpen();
80
81 // specific protocol tests
82 void ftpServer();
83 void ftpProxyServer();
84 void imapServer();
85 void httpServer();
86 void httpServerFiles_data();
87 void httpServerFiles();
88 void httpServerCGI_data();
89 void httpServerCGI();
90 void httpsServer();
91 void httpProxy();
92 void httpProxyBasicAuth();
93 void httpProxyNtlmAuth();
94 void socks5Proxy();
95 void socks5ProxyAuth();
96 void smbServer();
97
98 // ssl supported test
99 void supportsSsl();
100 private:
101 #ifndef QT_NO_BEARERMANAGEMENT
102 QNetworkConfigurationManager *netConfMan;
103 QNetworkConfiguration networkConfiguration;
104 QScopedPointer<QNetworkSession> networkSession;
105 #endif
106 };
107
108 class Chat
109 {
110 public:
111 enum Type {
112 Reconnect,
113 Send,
114 Expect,
115 SkipBytes,
116 DiscardUntil,
117 DiscardUntilDisconnect,
118 Disconnect,
119 RemoteDisconnect,
120 StartEncryption
121 };
Chat(Type t,const QByteArray & d)122 Chat(Type t, const QByteArray &d)
123 : data(d), type(t)
124 {
125 }
Chat(Type t,int val=0)126 Chat(Type t, int val = 0)
127 : value(val), type(t)
128 {
129 }
130
send(const QByteArray & data)131 static inline Chat send(const QByteArray &data)
132 { return Chat(Send, data); }
expect(const QByteArray & data)133 static inline Chat expect(const QByteArray &data)
134 { return Chat(Expect, data); }
discardUntil(const QByteArray & data)135 static inline Chat discardUntil(const QByteArray &data)
136 { return Chat(DiscardUntil, data); }
skipBytes(int count)137 static inline Chat skipBytes(int count)
138 { return Chat(SkipBytes, count); }
139
140 QByteArray data;
141 int value;
142 Type type;
143 };
144
prettyByteArray(const QByteArray & array)145 static QString prettyByteArray(const QByteArray &array)
146 {
147 // any control chars?
148 QString result;
149 result.reserve(array.length() + array.length() / 3);
150 for (int i = 0; i < array.length(); ++i) {
151 char c = array.at(i);
152 switch (c) {
153 case '\n':
154 result += "\\n";
155 continue;
156 case '\r':
157 result += "\\r";
158 continue;
159 case '\t':
160 result += "\\t";
161 continue;
162 case '"':
163 result += "\\\"";
164 continue;
165 default:
166 break;
167 }
168
169 if (c < 0x20 || uchar(c) >= 0x7f) {
170 result += '\\';
171 result += QString::number(uchar(c), 8);
172 } else {
173 result += c;
174 }
175 }
176 return result;
177 }
178
doSocketRead(QTcpSocket * socket,int minBytesAvailable,int timeout=30000)179 static bool doSocketRead(QTcpSocket *socket, int minBytesAvailable, int timeout = 30000)
180 {
181 QElapsedTimer timer;
182 timer.start();
183 forever {
184 if (socket->bytesAvailable() >= minBytesAvailable)
185 return true;
186 if (socket->state() == QAbstractSocket::UnconnectedState
187 || timer.elapsed() >= timeout)
188 return false;
189 if (!socket->waitForReadyRead(timeout - timer.elapsed()))
190 return false;
191 }
192 }
193
doSocketFlush(QTcpSocket * socket,int timeout=30000)194 static bool doSocketFlush(QTcpSocket *socket, int timeout = 30000)
195 {
196 #ifndef QT_NO_OPENSSL
197 QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
198 #endif
199 QTime timer;
200 timer.start();
201 forever {
202 if (socket->bytesToWrite() == 0
203 #ifndef QT_NO_OPENSSL
204 && sslSocket->encryptedBytesToWrite() == 0
205 #endif
206 )
207 return true;
208 if (socket->state() == QAbstractSocket::UnconnectedState
209 || timer.elapsed() >= timeout)
210 return false;
211 if (!socket->waitForBytesWritten(timeout - timer.elapsed()))
212 return false;
213 }
214 }
215
netChat(int port,const QList<Chat> & chat)216 static void netChat(int port, const QList<Chat> &chat)
217 {
218 #ifndef QT_NO_OPENSSL
219 QSslSocket socket;
220 #else
221 QTcpSocket socket;
222 #endif
223
224 socket.connectToHost(QtNetworkSettings::serverName(), port);
225 qDebug() << 0 << "Connecting to server on port" << port;
226 QVERIFY2(socket.waitForConnected(30000),
227 QString("Failed to connect to server in step 0: %1").arg(socket.errorString()).toLocal8Bit());
228
229 // now start the chat
230 QList<Chat>::ConstIterator it = chat.constBegin();
231 for (int i = 1; it != chat.constEnd(); ++it, ++i) {
232 switch (it->type) {
233 case Chat::Expect: {
234 qDebug() << i << "Expecting" << prettyByteArray(it->data);
235 if (!doSocketRead(&socket, it->data.length()))
236 QFAIL(QString("Failed to receive data in step %1: timeout").arg(i).toLocal8Bit());
237
238 // pop that many bytes off the socket
239 QByteArray received = socket.read(it->data.length());
240
241 // is it what we expected?
242 QVERIFY2(received == it->data,
243 QString("Did not receive expected data in step %1: data received was:\n%2")
244 .arg(i).arg(prettyByteArray(received)).toLocal8Bit());
245
246 break;
247 }
248
249 case Chat::DiscardUntil:
250 qDebug() << i << "Discarding until" << prettyByteArray(it->data);
251 while (true) {
252 // scan the buffer until we have our string
253 if (!doSocketRead(&socket, it->data.length()))
254 QFAIL(QString("Failed to receive data in step %1: timeout").arg(i).toLocal8Bit());
255
256 QByteArray buffer;
257 buffer.resize(socket.bytesAvailable());
258 socket.peek(buffer.data(), socket.bytesAvailable());
259
260 int pos = buffer.indexOf(it->data);
261 if (pos == -1) {
262 // data not found, keep trying
263 continue;
264 }
265
266 buffer = socket.read(pos + it->data.length());
267 qDebug() << i << "Discarded" << prettyByteArray(buffer);
268 break;
269 }
270 break;
271
272 case Chat::SkipBytes: {
273 qDebug() << i << "Skipping" << it->value << "bytes";
274 if (!doSocketRead(&socket, it->value))
275 QFAIL(QString("Failed to receive data in step %1: timeout").arg(i).toLocal8Bit());
276
277 // now discard the bytes
278 QByteArray buffer = socket.read(it->value);
279 qDebug() << i << "Skipped" << prettyByteArray(buffer);
280 break;
281 }
282
283 case Chat::Send: {
284 qDebug() << i << "Sending" << prettyByteArray(it->data);
285 socket.write(it->data);
286 if (!doSocketFlush(&socket)) {
287 QVERIFY2(socket.state() == QAbstractSocket::ConnectedState,
288 QString("Socket disconnected while sending data in step %1").arg(i).toLocal8Bit());
289 QFAIL(QString("Failed to send data in step %1: timeout").arg(i).toLocal8Bit());
290 }
291 break;
292 }
293
294 case Chat::Disconnect:
295 qDebug() << i << "Disconnecting from host";
296 socket.disconnectFromHost();
297
298 // is this the last command?
299 if (it + 1 != chat.constEnd())
300 break;
301
302 // fall through:
303 case Chat::RemoteDisconnect:
304 case Chat::DiscardUntilDisconnect:
305 qDebug() << i << "Waiting for remote disconnect";
306 if (socket.state() != QAbstractSocket::UnconnectedState)
307 socket.waitForDisconnected(30000);
308 QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState,
309 QString("Socket did not disconnect as expected in step %1").arg(i).toLocal8Bit());
310
311 // any data left?
312 if (it->type == Chat::DiscardUntilDisconnect) {
313 QByteArray buffer = socket.readAll();
314 qDebug() << i << "Discarded in the process:" << prettyByteArray(buffer);
315 }
316
317 if (socket.bytesAvailable() != 0)
318 QFAIL(QString("Unexpected bytes still on buffer when disconnecting in step %1:\n%2")
319 .arg(i).arg(prettyByteArray(socket.readAll())).toLocal8Bit());
320 break;
321
322 case Chat::Reconnect:
323 qDebug() << i << "Reconnecting to server on port" << port;
324 socket.connectToHost(QtNetworkSettings::serverName(), port);
325 QVERIFY2(socket.waitForConnected(30000),
326 QString("Failed to reconnect to server in step %1: %2").arg(i).arg(socket.errorString()).toLocal8Bit());
327 break;
328
329 case Chat::StartEncryption:
330 #ifdef QT_NO_OPENSSL
331 QFAIL("Internal error: SSL required for this test");
332 #else
333 qDebug() << i << "Starting client encryption";
334 socket.ignoreSslErrors();
335 socket.startClientEncryption();
336 QVERIFY2(socket.waitForEncrypted(30000),
337 QString("Failed to start client encryption in step %1: %2").arg(i)
338 .arg(socket.errorString()).toLocal8Bit());
339 break;
340 #endif
341 }
342 }
343 }
344
tst_NetworkSelfTest()345 tst_NetworkSelfTest::tst_NetworkSelfTest()
346 {
347 Q_SET_DEFAULT_IAP
348 }
349
~tst_NetworkSelfTest()350 tst_NetworkSelfTest::~tst_NetworkSelfTest()
351 {
352 }
353
serverIpAddress()354 QHostAddress tst_NetworkSelfTest::serverIpAddress()
355 {
356 if (cachedIpAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
357 // need resolving
358 QHostInfo resolved = QHostInfo::fromName(QtNetworkSettings::serverName());
359 if(resolved.error() != QHostInfo::NoError ||
360 resolved.addresses().isEmpty()) {
361 qWarning("QHostInfo::fromName failed (%d).", resolved.error());
362 return QHostAddress(QHostAddress::Null);
363 }
364 cachedIpAddress = resolved.addresses().first();
365 }
366 return cachedIpAddress;
367 }
368
initTestCase()369 void tst_NetworkSelfTest::initTestCase()
370 {
371 #ifndef QT_NO_BEARERMANAGEMENT
372 netConfMan = new QNetworkConfigurationManager(this);
373 netConfMan->updateConfigurations();
374 connect(netConfMan, SIGNAL(updateCompleted()), &QTestEventLoop::instance(), SLOT(exitLoop()));
375 QTestEventLoop::instance().enterLoop(10);
376 networkConfiguration = netConfMan->defaultConfiguration();
377 if (networkConfiguration.isValid()) {
378 networkSession.reset(new QNetworkSession(networkConfiguration));
379 if (!networkSession->isOpen()) {
380 networkSession->open();
381 QVERIFY(networkSession->waitForOpened(30000));
382 }
383 } else {
384 QVERIFY(!(netConfMan->capabilities() & QNetworkConfigurationManager::NetworkSessionRequired));
385 }
386 #endif
387 }
388
hostTest()389 void tst_NetworkSelfTest::hostTest()
390 {
391 // this is a localhost self-test
392 QHostInfo localhost = QHostInfo::fromName("localhost");
393 QCOMPARE(localhost.error(), QHostInfo::NoError);
394 QVERIFY(!localhost.addresses().isEmpty());
395
396 QTcpServer server;
397 QVERIFY(server.listen());
398
399 QTcpSocket socket;
400 socket.connectToHost("127.0.0.1", server.serverPort());
401 QVERIFY(socket.waitForConnected(10000));
402 }
403
dnsResolution_data()404 void tst_NetworkSelfTest::dnsResolution_data()
405 {
406 QTest::addColumn<QString>("hostName");
407 QTest::newRow("local-name") << QtNetworkSettings::serverLocalName();
408 QTest::newRow("fqdn") << QtNetworkSettings::serverName();
409 }
410
dnsResolution()411 void tst_NetworkSelfTest::dnsResolution()
412 {
413 QFETCH(QString, hostName);
414 QHostInfo resolved = QHostInfo::fromName(hostName);
415 QVERIFY2(resolved.error() == QHostInfo::NoError,
416 QString("Failed to resolve hostname %1: %2").arg(hostName, resolved.errorString()).toLocal8Bit());
417 QVERIFY2(resolved.addresses().size() > 0, "Got 0 addresses for server IP");
418
419 cachedIpAddress = resolved.addresses().first();
420 }
421
serverReachability()422 void tst_NetworkSelfTest::serverReachability()
423 {
424 // check that we get a proper error connecting to port 12346
425 QTcpSocket socket;
426 socket.connectToHost(QtNetworkSettings::serverName(), 12346);
427
428 QTime timer;
429 timer.start();
430 socket.waitForConnected(30000);
431 QVERIFY2(timer.elapsed() < 9900, "Connection to closed port timed out instead of refusing, something is wrong");
432
433 QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!");
434 QVERIFY2(socket.error() == QAbstractSocket::ConnectionRefusedError,
435 QString("Could not reach server: %1").arg(socket.errorString()).toLocal8Bit());
436 }
437
remotePortsOpen_data()438 void tst_NetworkSelfTest::remotePortsOpen_data()
439 {
440 QTest::addColumn<int>("portNumber");
441 QTest::newRow("echo") << 7;
442 QTest::newRow("daytime") << 13;
443 QTest::newRow("ftp") << 21;
444 QTest::newRow("ssh") << 22;
445 QTest::newRow("imap") << 143;
446 QTest::newRow("http") << 80;
447 QTest::newRow("https") << 443;
448 QTest::newRow("http-proxy") << 3128;
449 QTest::newRow("http-proxy-auth-basic") << 3129;
450 QTest::newRow("http-proxy-auth-ntlm") << 3130;
451 QTest::newRow("socks5-proxy") << 1080;
452 QTest::newRow("socks5-proxy-auth") << 1081;
453 QTest::newRow("ftp-proxy") << 2121;
454 QTest::newRow("smb") << 139;
455 }
456
remotePortsOpen()457 void tst_NetworkSelfTest::remotePortsOpen()
458 {
459 #ifdef Q_OS_SYMBIAN
460 if (qstrcmp(QTest::currentDataTag(), "http-proxy-auth-ntlm") == 0)
461 QSKIP("NTML authentication not yet supported in Symbian", SkipSingle);
462 #endif
463
464 QFETCH(int, portNumber);
465 QTcpSocket socket;
466 socket.connectToHost(QtNetworkSettings::serverName(), portNumber);
467
468 if (!socket.waitForConnected(30000)) {
469 if (socket.error() == QAbstractSocket::SocketTimeoutError)
470 QFAIL(QString("Network timeout connecting to the server on port %1").arg(portNumber).toLocal8Bit());
471 else
472 QFAIL(QString("Error connecting to server on port %1: %2").arg(portNumber).arg(socket.errorString()).toLocal8Bit());
473 }
474 QVERIFY(socket.state() == QAbstractSocket::ConnectedState);
475 }
476
ftpChat(const QByteArray & userSuffix=QByteArray ())477 static QList<Chat> ftpChat(const QByteArray &userSuffix = QByteArray())
478 {
479 QList<Chat> rv;
480 rv << Chat::expect("220")
481 << Chat::discardUntil("\r\n")
482 << Chat::send("USER anonymous" + userSuffix + "\r\n")
483 << Chat::expect("331")
484 << Chat::discardUntil("\r\n")
485 << Chat::send("PASS user@hostname\r\n")
486 << Chat::expect("230")
487 << Chat::discardUntil("\r\n")
488
489 << Chat::send("CWD pub\r\n")
490 << Chat::expect("250")
491 << Chat::discardUntil("\r\n")
492 << Chat::send("CWD dir-not-readable\r\n")
493 << Chat::expect("550")
494 << Chat::discardUntil("\r\n")
495 << Chat::send("PWD\r\n")
496 << Chat::expect("257 \"/pub\"\r\n")
497 << Chat::send("SIZE file-not-readable.txt\r\n")
498 << Chat::expect("213 41\r\n")
499 << Chat::send("CWD qxmlquery\r\n")
500 << Chat::expect("250")
501 << Chat::discardUntil("\r\n")
502
503 << Chat::send("CWD /qtest\r\n")
504 << Chat::expect("250")
505 << Chat::discardUntil("\r\n")
506 << Chat::send("SIZE bigfile\r\n")
507 << Chat::expect("213 519240\r\n")
508 << Chat::send("SIZE rfc3252\r\n")
509 << Chat::expect("213 25962\r\n")
510 << Chat::send("SIZE rfc3252.txt\r\n")
511 << Chat::expect("213 25962\r\n")
512 // << Chat::send("SIZE nonASCII/german_\344\366\374\304\326\334\337\r\n")
513 // << Chat::expect("213 40\r\n")
514
515 << Chat::send("QUIT\r\n");
516 #ifdef Q_OS_SYMBIAN
517 if (userSuffix.length() == 0) // received but unacknowledged packets are discarded by TCP RST, so this doesn't work with frox proxy
518 #endif
519 rv << Chat::expect("221")
520 << Chat::discardUntil("\r\n");
521
522 rv << Chat::RemoteDisconnect;
523 return rv;
524 }
525
ftpServer()526 void tst_NetworkSelfTest::ftpServer()
527 {
528 netChat(21, ftpChat());
529 }
530
ftpProxyServer()531 void tst_NetworkSelfTest::ftpProxyServer()
532 {
533 netChat(2121, ftpChat("@" + QtNetworkSettings::serverName().toLatin1()));
534 }
535
imapServer()536 void tst_NetworkSelfTest::imapServer()
537 {
538 netChat(143, QList<Chat>()
539 << Chat::expect("* OK ")
540 << Chat::discardUntil("\r\n")
541 << Chat::send("1 CAPABILITY\r\n")
542 << Chat::expect("* CAPABILITY ")
543 << Chat::discardUntil("1 OK")
544 << Chat::discardUntil("\r\n")
545 << Chat::send("2 LOGOUT\r\n")
546 << Chat::discardUntil("2 OK")
547 << Chat::discardUntil("\r\n")
548 << Chat::RemoteDisconnect);
549 }
550
httpServer()551 void tst_NetworkSelfTest::httpServer()
552 {
553 QString uniqueExtension;
554 qsrand(time(0));
555 #ifndef Q_OS_WINCE
556 uniqueExtension = QString("%1%2%3").arg((qulonglong)this).arg(qrand()).arg((qulonglong)time(0));
557 #else
558 uniqueExtension = QString("%1%2").arg((qulonglong)this).arg(qrand());
559 #endif
560
561 netChat(80, QList<Chat>()
562 // HTTP/0.9 chat:
563 << Chat::send("GET /\r\n")
564 << Chat::DiscardUntilDisconnect
565
566 // HTTP/1.0 chat:
567 << Chat::Reconnect
568 << Chat::send("GET / HTTP/1.0\r\n"
569 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
570 "Connection: close\r\n"
571 "\r\n")
572 << Chat::expect("HTTP/1.")
573 << Chat::discardUntil(" ")
574 << Chat::expect("200 ")
575 << Chat::DiscardUntilDisconnect
576
577 // HTTP/1.0 POST:
578 << Chat::Reconnect
579 << Chat::send("POST / HTTP/1.0\r\n"
580 "Content-Length: 5\r\n"
581 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
582 "Connection: close\r\n"
583 "\r\n"
584 "Hello")
585 << Chat::expect("HTTP/1.")
586 << Chat::discardUntil(" ")
587 << Chat::expect("200 ")
588 << Chat::DiscardUntilDisconnect
589
590 // HTTP protected area
591 << Chat::Reconnect
592 << Chat::send("GET /qtest/protected/rfc3252.txt HTTP/1.0\r\n"
593 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
594 "Connection: close\r\n"
595 "\r\n")
596 << Chat::expect("HTTP/1.")
597 << Chat::discardUntil(" ")
598 << Chat::expect("401 ")
599 << Chat::DiscardUntilDisconnect
600
601 << Chat::Reconnect
602 << Chat::send("HEAD /qtest/protected/rfc3252.txt HTTP/1.0\r\n"
603 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
604 "Connection: close\r\n"
605 "Authorization: Basic cXNvY2tzdGVzdDpwYXNzd29yZA==\r\n"
606 "\r\n")
607 << Chat::expect("HTTP/1.")
608 << Chat::discardUntil(" ")
609 << Chat::expect("200 ")
610 << Chat::DiscardUntilDisconnect
611
612 // DAV area
613 << Chat::Reconnect
614 << Chat::send("HEAD /dav/ HTTP/1.0\r\n"
615 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
616 "Connection: close\r\n"
617 "\r\n")
618 << Chat::expect("HTTP/1.")
619 << Chat::discardUntil(" ")
620 << Chat::expect("200 ")
621 << Chat::DiscardUntilDisconnect
622
623 // HTTP/1.0 PUT
624 << Chat::Reconnect
625 << Chat::send("PUT /dav/networkselftest-" + uniqueExtension.toLatin1() + ".txt HTTP/1.0\r\n"
626 "Content-Length: 5\r\n"
627 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
628 "Connection: close\r\n"
629 "\r\n"
630 "Hello")
631 << Chat::expect("HTTP/1.")
632 << Chat::discardUntil(" ")
633 << Chat::expect("201 ")
634 << Chat::DiscardUntilDisconnect
635
636 // check that the file did get uploaded
637 << Chat::Reconnect
638 << Chat::send("HEAD /dav/networkselftest-" + uniqueExtension.toLatin1() + ".txt HTTP/1.0\r\n"
639 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
640 "Connection: close\r\n"
641 "\r\n")
642 << Chat::expect("HTTP/1.")
643 << Chat::discardUntil(" ")
644 << Chat::expect("200 ")
645 << Chat::discardUntil("\r\nContent-Length: 5\r\n")
646 << Chat::DiscardUntilDisconnect
647
648 // HTTP/1.0 DELETE
649 << Chat::Reconnect
650 << Chat::send("DELETE /dav/networkselftest-" + uniqueExtension.toLatin1() + ".txt HTTP/1.0\r\n"
651 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
652 "Connection: close\r\n"
653 "\r\n")
654 << Chat::expect("HTTP/1.")
655 << Chat::discardUntil(" ")
656 << Chat::expect("204 ")
657 << Chat::DiscardUntilDisconnect
658 );
659 }
660
httpServerFiles_data()661 void tst_NetworkSelfTest::httpServerFiles_data()
662 {
663 QTest::addColumn<QString>("uri");
664 QTest::addColumn<int>("size");
665
666 QTest::newRow("fluke.gif") << "/qtest/fluke.gif" << -1;
667 QTest::newRow("bigfile") << "/qtest/bigfile" << 519240;
668 QTest::newRow("rfc3252.txt") << "/qtest/rfc3252.txt" << 25962;
669 QTest::newRow("protected/rfc3252.txt") << "/qtest/protected/rfc3252.txt" << 25962;
670 QTest::newRow("completelyEmptyQuery.xq") << "/qtest/qxmlquery/completelyEmptyQuery.xq" << -1;
671 QTest::newRow("notWellformedViaHttps.xml") << "/qtest/qxmlquery/notWellformedViaHttps.xml" << -1;
672 QTest::newRow("notWellformed.xml") << "/qtest/qxmlquery/notWellformed.xml" << -1;
673 QTest::newRow("viaHttp.xq") << "/qtest/qxmlquery/viaHttp.xq" << -1;
674 QTest::newRow("wellFormedViaHttps.xml") << "/qtest/qxmlquery/wellFormedViaHttps.xml" << -1;
675 QTest::newRow("wellFormed.xml") << "/qtest/qxmlquery/wellFormed.xml" << -1;
676 }
677
httpServerFiles()678 void tst_NetworkSelfTest::httpServerFiles()
679 {
680 QFETCH(QString, uri);
681 QFETCH(int, size);
682
683 QList<Chat> chat;
684 chat << Chat::send("HEAD " + QUrl::toPercentEncoding(uri, "/") + " HTTP/1.0\r\n"
685 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
686 "Connection: close\r\n"
687 "Authorization: Basic cXNvY2tzdGVzdDpwYXNzd29yZA==\r\n"
688 "\r\n")
689 << Chat::expect("HTTP/1.")
690 << Chat::skipBytes(1) // HTTP/1.0 or 1.1 reply
691 << Chat::expect(" 200 ");
692 if (size != -1)
693 chat << Chat::discardUntil("\r\nContent-Length: " + QByteArray::number(size) + "\r\n");
694 chat << Chat::DiscardUntilDisconnect;
695 netChat(80, chat);
696 }
697
httpServerCGI_data()698 void tst_NetworkSelfTest::httpServerCGI_data()
699 {
700 QTest::addColumn<QByteArray>("request");
701 QTest::addColumn<QByteArray>("result");
702 QTest::addColumn<QByteArray>("additionalHeader");
703
704 QTest::newRow("echo.cgi")
705 << QByteArray("GET /qtest/cgi-bin/echo.cgi?Hello+World HTTP/1.0\r\n"
706 "Connection: close\r\n"
707 "\r\n")
708 << QByteArray("Hello+World")
709 << QByteArray();
710
711 QTest::newRow("echo.cgi(POST)")
712 << QByteArray("POST /qtest/cgi-bin/echo.cgi?Hello+World HTTP/1.0\r\n"
713 "Connection: close\r\n"
714 "Content-Length: 15\r\n"
715 "\r\n"
716 "Hello, World!\r\n")
717 << QByteArray("Hello, World!\r\n")
718 << QByteArray();
719
720 QTest::newRow("md5sum.cgi")
721 << QByteArray("POST /qtest/cgi-bin/md5sum.cgi HTTP/1.0\r\n"
722 "Connection: close\r\n"
723 "Content-Length: 15\r\n"
724 "\r\n"
725 "Hello, World!\r\n")
726 << QByteArray("29b933a8d9a0fcef0af75f1713f4940e\n")
727 << QByteArray();
728
729 QTest::newRow("protected/md5sum.cgi")
730 << QByteArray("POST /qtest/protected/cgi-bin/md5sum.cgi HTTP/1.0\r\n"
731 "Connection: close\r\n"
732 "Authorization: Basic cXNvY2tzdGVzdDpwYXNzd29yZA==\r\n"
733 "Content-Length: 15\r\n"
734 "\r\n"
735 "Hello, World!\r\n")
736 << QByteArray("29b933a8d9a0fcef0af75f1713f4940e\n")
737 << QByteArray();
738
739 QTest::newRow("set-cookie.cgi")
740 << QByteArray("POST /qtest/cgi-bin/set-cookie.cgi HTTP/1.0\r\n"
741 "Connection: close\r\n"
742 "Content-Length: 8\r\n"
743 "\r\n"
744 "foo=bar\n")
745 << QByteArray("Success\n")
746 << QByteArray("\r\nSet-Cookie: foo=bar\r\n");
747 }
748
httpServerCGI()749 void tst_NetworkSelfTest::httpServerCGI()
750 {
751 QFETCH(QByteArray, request);
752 QFETCH(QByteArray, result);
753 QFETCH(QByteArray, additionalHeader);
754 QList<Chat> chat;
755 chat << Chat::send(request)
756 << Chat::expect("HTTP/1.") << Chat::skipBytes(1)
757 << Chat::expect(" 200 ");
758
759 if (!additionalHeader.isEmpty())
760 chat << Chat::discardUntil(additionalHeader);
761
762 chat << Chat::discardUntil("\r\n\r\n")
763 << Chat::expect(result)
764 << Chat::RemoteDisconnect;
765 netChat(80, chat);
766 }
767
httpsServer()768 void tst_NetworkSelfTest::httpsServer()
769 {
770 #ifndef QT_NO_OPENSSL
771 netChat(443, QList<Chat>()
772 << Chat::StartEncryption
773 << Chat::send("GET / HTTP/1.0\r\n"
774 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
775 "Connection: close\r\n"
776 "\r\n")
777 << Chat::expect("HTTP/1.")
778 << Chat::discardUntil(" ")
779 << Chat::expect("200 ")
780 << Chat::DiscardUntilDisconnect);
781 #else
782 QSKIP("SSL not enabled, cannot test", SkipAll);
783 #endif
784 }
785
httpProxy()786 void tst_NetworkSelfTest::httpProxy()
787 {
788 netChat(3128, QList<Chat>()
789 // proxy GET by IP
790 << Chat::send("GET http://" + serverIpAddress().toString().toLatin1() + "/ HTTP/1.0\r\n"
791 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
792 "Proxy-connection: close\r\n"
793 "\r\n")
794 << Chat::expect("HTTP/1.")
795 << Chat::discardUntil(" ")
796 << Chat::expect("200 ")
797 << Chat::DiscardUntilDisconnect
798
799 // proxy GET by hostname
800 << Chat::Reconnect
801 << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n"
802 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
803 "Proxy-connection: close\r\n"
804 "\r\n")
805 << Chat::expect("HTTP/1.")
806 << Chat::discardUntil(" ")
807 << Chat::expect("200 ")
808 << Chat::DiscardUntilDisconnect
809
810 // proxy CONNECT by IP
811 << Chat::Reconnect
812 << Chat::send("CONNECT " + serverIpAddress().toString().toLatin1() + ":21 HTTP/1.0\r\n"
813 "\r\n")
814 << Chat::expect("HTTP/1.")
815 << Chat::discardUntil(" ")
816 << Chat::expect("200 ")
817 << Chat::discardUntil("\r\n\r\n")
818 << ftpChat()
819
820 // proxy CONNECT by hostname
821 << Chat::Reconnect
822 << Chat::send("CONNECT " + QtNetworkSettings::serverName().toLatin1() + ":21 HTTP/1.0\r\n"
823 "\r\n")
824 << Chat::expect("HTTP/1.")
825 << Chat::discardUntil(" ")
826 << Chat::expect("200 ")
827 << Chat::discardUntil("\r\n\r\n")
828 << ftpChat()
829 );
830 }
831
httpProxyBasicAuth()832 void tst_NetworkSelfTest::httpProxyBasicAuth()
833 {
834 netChat(3129, QList<Chat>()
835 // test auth required response
836 << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n"
837 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
838 "Proxy-connection: close\r\n"
839 "\r\n")
840 << Chat::expect("HTTP/1.")
841 << Chat::discardUntil(" ")
842 << Chat::expect("407 ")
843 << Chat::discardUntil("\r\nProxy-Authenticate: Basic realm=\"")
844 << Chat::DiscardUntilDisconnect
845
846 // now try sending our credentials
847 << Chat::Reconnect
848 << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n"
849 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
850 "Proxy-connection: close\r\n"
851 "Proxy-Authorization: Basic cXNvY2tzdGVzdDpwYXNzd29yZA==\r\n"
852 "\r\n")
853 << Chat::expect("HTTP/1.")
854 << Chat::discardUntil(" ")
855 << Chat::expect("200 ")
856 << Chat::DiscardUntilDisconnect);
857 }
858
httpProxyNtlmAuth()859 void tst_NetworkSelfTest::httpProxyNtlmAuth()
860 {
861 #ifdef Q_OS_SYMBIAN
862 QSKIP("NTML authentication not yet supported in Symbian", SkipAll);
863 #else
864 netChat(3130, QList<Chat>()
865 // test auth required response
866 << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n"
867 "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
868 "Proxy-connection: keep-alive\r\n" // NTLM auth will disconnect
869 "\r\n")
870 << Chat::expect("HTTP/1.")
871 << Chat::discardUntil(" ")
872 << Chat::expect("407 ")
873 << Chat::discardUntil("\r\nProxy-Authenticate: NTLM\r\n")
874 << Chat::DiscardUntilDisconnect
875 );
876 #endif
877 }
878
879 // SOCKSv5 is a binary protocol
880 static const char handshakeNoAuth[] = "\5\1\0";
881 static const char handshakeOkNoAuth[] = "\5\0";
882 static const char handshakeAuthPassword[] = "\5\1\2\1\12qsockstest\10password";
883 static const char handshakeOkPasswdAuth[] = "\5\2\1\0";
884 static const char handshakeAuthNotOk[] = "\5\377";
885 static const char connect1[] = "\5\1\0\1\177\0\0\1\0\25"; // Connect IPv4 127.0.0.1 port 21
886 static const char connect1a[] = "\5\1\0\1"; // just "Connect to IPv4"
887 static const char connect1b[] = "\0\25"; // just "port 21"
888 static const char connect2[] = "\5\1\0\3\11localhost\0\25"; // Connect hostname localhost 21
889 static const char connect2a[] = "\5\1\0\3"; // just "Connect to hostname"
890 static const char connected[] = "\5\0\0";
891
892 #define QBA(x) (QByteArray::fromRawData(x, -1 + sizeof(x)))
893
socks5Proxy()894 void tst_NetworkSelfTest::socks5Proxy()
895 {
896 union {
897 char buf[4];
898 quint32 data;
899 } ip4Address;
900 ip4Address.data = qToBigEndian(serverIpAddress().toIPv4Address());
901
902 netChat(1080, QList<Chat>()
903 // IP address connection
904 << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
905 << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth))
906 << Chat::send(QByteArray(connect1, -1 + sizeof connect1))
907 << Chat::expect(QByteArray(connected, -1 + sizeof connected))
908 << Chat::expect("\1") // IPv4 address following
909 << Chat::skipBytes(6) // the server's local address and port
910 << ftpChat()
911
912 // connect by IP
913 << Chat::Reconnect
914 << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
915 << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth))
916 << Chat::send(QBA(connect1a) + QByteArray::fromRawData(ip4Address.buf, 4) + QBA(connect1b))
917 << Chat::expect(QByteArray(connected, -1 + sizeof connected))
918 << Chat::expect("\1") // IPv4 address following
919 << Chat::skipBytes(6) // the server's local address and port
920 << ftpChat()
921
922 // connect to "localhost" by hostname
923 << Chat::Reconnect
924 << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
925 << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth))
926 << Chat::send(QByteArray(connect2, -1 + sizeof connect2))
927 << Chat::expect(QByteArray(connected, -1 + sizeof connected))
928 << Chat::expect("\1") // IPv4 address following
929 << Chat::skipBytes(6) // the server's local address and port
930 << ftpChat()
931
932 // connect to server by its official name
933 << Chat::Reconnect
934 << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
935 << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth))
936 << Chat::send(QBA(connect2a) + char(QtNetworkSettings::serverName().size()) + QtNetworkSettings::serverName().toLatin1() + QBA(connect1b))
937 << Chat::expect(QByteArray(connected, -1 + sizeof connected))
938 << Chat::expect("\1") // IPv4 address following
939 << Chat::skipBytes(6) // the server's local address and port
940 << ftpChat()
941 );
942 }
943
socks5ProxyAuth()944 void tst_NetworkSelfTest::socks5ProxyAuth()
945 {
946 netChat(1081, QList<Chat>()
947 // unauthenticated connect -- will get error
948 << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
949 << Chat::expect(QByteArray(handshakeAuthNotOk, -1 + sizeof handshakeAuthNotOk))
950 << Chat::RemoteDisconnect
951
952 // now try to connect with authentication
953 << Chat::Reconnect
954 << Chat::send(QByteArray(handshakeAuthPassword, -1 + sizeof handshakeAuthPassword))
955 << Chat::expect(QByteArray(handshakeOkPasswdAuth, -1 + sizeof handshakeOkPasswdAuth))
956 << Chat::send(QByteArray(connect1, -1 + sizeof connect1))
957 << Chat::expect(QByteArray(connected, -1 + sizeof connected))
958 << Chat::expect("\1") // IPv4 address following
959 << Chat::skipBytes(6) // the server's local address and port
960 << ftpChat()
961 );
962 }
963
supportsSsl()964 void tst_NetworkSelfTest::supportsSsl()
965 {
966 #ifdef QT_NO_OPENSSL
967 QFAIL("SSL not compiled in");
968 #else
969 QVERIFY2(QSslSocket::supportsSsl(), "Could not load SSL libraries");
970 #endif
971 }
972
smbServer()973 void tst_NetworkSelfTest::smbServer()
974 {
975 static const char contents[] = "This is 34 bytes. Do not change...";
976 #ifdef Q_OS_WIN
977 // use Windows's native UNC support to try and open a file on the server
978 QString filepath = QString("\\\\%1\\testshare\\test.pri").arg(QtNetworkSettings::winServerName());
979 FILE *f = fopen(filepath.toLatin1(), "rb");
980 QVERIFY2(f, qt_error_string().toLocal8Bit());
981
982 char buf[128];
983 size_t ret = fread(buf, 1, sizeof buf, f);
984 fclose(f);
985
986 QCOMPARE(ret, strlen(contents));
987 QVERIFY(memcmp(buf, contents, strlen(contents)) == 0);
988 #else
989 // try to use Samba
990 QString progname = "smbclient";
991 QProcess smbclient;
992 smbclient.start(progname, QIODevice::ReadOnly);
993 if (!smbclient.waitForStarted())
994 QSKIP("Could not find smbclient (from Samba), cannot continue testing", SkipAll);
995 if (!smbclient.waitForFinished() || smbclient.exitStatus() != QProcess::NormalExit)
996 QSKIP("smbclient isn't working, cannot continue testing", SkipAll);
997 smbclient.close();
998
999 // try listing the server
1000 smbclient.start(progname, QStringList() << "-g" << "-N" << "-L" << QtNetworkSettings::winServerName(), QIODevice::ReadOnly);
1001 QVERIFY(smbclient.waitForFinished());
1002 if (smbclient.exitStatus() != QProcess::NormalExit)
1003 QSKIP("smbclient crashed", SkipAll);
1004 QVERIFY2(smbclient.exitCode() == 0, "Test server not found");
1005
1006 QByteArray output = smbclient.readAll();
1007 QVERIFY(output.contains("Disk|testshare|"));
1008 QVERIFY(output.contains("Disk|testsharewritable|"));
1009 QVERIFY(output.contains("Disk|testsharelargefile|"));
1010 qDebug() << "Test server found and shares are correct";
1011
1012 // try getting a file
1013 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
1014 env.insert("PAGER", "/bin/cat"); // just in case
1015 smbclient.setProcessEnvironment(env);
1016 smbclient.start(progname, QStringList() << "-N" << "-c" << "more test.pri"
1017 << QString("\\\\%1\\testshare").arg(QtNetworkSettings::winServerName()), QIODevice::ReadOnly);
1018 QVERIFY(smbclient.waitForFinished());
1019 if (smbclient.exitStatus() != QProcess::NormalExit)
1020 QSKIP("smbclient crashed", SkipAll);
1021 QVERIFY2(smbclient.exitCode() == 0, "File //qt-test-server/testshare/test.pri not found");
1022
1023 output = smbclient.readAll();
1024 QCOMPARE(output.constData(), contents);
1025 qDebug() << "Test file is correct";
1026 #endif
1027 }
1028
1029 QTEST_MAIN(tst_NetworkSelfTest)
1030 #include "tst_networkselftest.moc"
1031