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