1
/****************************************************************************
2
**
3
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the test suite of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include <QtTest/QtTest>
43
#include <QtNetwork/QtNetwork>
44
45
#ifdef Q_OS_SYMBIAN
46
// In Symbian OS test data is located in applications private dir
47
// Current path (C:\private\<UID>) contains only ascii chars
48
//#define SRCDIR QDir::currentPath()
49
#define SRCDIR "."
50
#endif
51
52
#include "../network-settings.h"
53
54
class tst_NetworkSelfTest: public QObject
55
{
56
    Q_OBJECT
57
    QHostAddress cachedIpAddress;
58
public:
59
    tst_NetworkSelfTest();
60
    virtual ~tst_NetworkSelfTest();
61
62
    QHostAddress serverIpAddress();
63
64
private slots:
65
    void hostTest();
66
    void dnsResolution_data();
67
    void dnsResolution();
68
    void serverReachability();
69
    void remotePortsOpen_data();
70
    void remotePortsOpen();
71
    void fileLineEndingTest();
72
73
    // specific protocol tests
74
    void ftpServer();
75
    void imapServer();
76
    void httpServer();
77
    void httpsServer();
78
    void httpProxy();
79
    void httpProxyBasicAuth();
80
    void httpProxyNtlmAuth();
81
    void socks5Proxy();
82
    void socks5ProxyAuth();
83
84
    // ssl supported test
85
    void supportsSsl();
86
};
87
88
class Chat
89
{
90
public:
91
    enum Type {
92
        Reconnect,
93
        Send,
94
        Expect,
95
        SkipBytes,
96
        DiscardUntil,
97
        DiscardUntilDisconnect,
98
        Disconnect,
99
        RemoteDisconnect,
100
        StartEncryption
101
    };
102
    Chat(Type t, const QByteArray &d)
103
        : data(d), type(t)
104
    {
105
    }
106
    Chat(Type t, int val = 0)
107
        : value(val), type(t)
108
    {
109
    }
110
111
    static inline Chat send(const QByteArray &data)
112
    { return Chat(Send, data); }
113
    static inline Chat expect(const QByteArray &data)
114
    { return Chat(Expect, data); }
115
    static inline Chat discardUntil(const QByteArray &data)
116
    { return Chat(DiscardUntil, data); }
117
    static inline Chat skipBytes(int count)
118
    { return Chat(SkipBytes, count); }
119
120
    QByteArray data;
121
    int value;
122
    Type type;
123
};
124
125
static QString prettyByteArray(const QByteArray &array)
126
{
127
    // any control chars?
128
    QString result;
129
    result.reserve(array.length() + array.length() / 3);
130
    for (int i = 0; i < array.length(); ++i) {
131
        char c = array.at(i);
132
        switch (c) {
133
        case '\n':
134
            result += "\\n";
135
            continue;
136
        case '\r':
137
            result += "\\r";
138
            continue;
139
        case '\t':
140
            result += "\\t";
141
            continue;
142
        case '"':
143
            result += "\\\"";
144
            continue;
145
        default:
146
            break;
147
        }
148
149
        if (c < 0x20 || uchar(c) >= 0x7f) {
150
            result += '\\';
151
            result += QString::number(uchar(c), 8);
152
        } else {
153
            result += c;
154
        }
155
    }
156
    return result;
157
}
158
159
static bool doSocketRead(QTcpSocket *socket, int minBytesAvailable, int timeout = 4000)
160
{
161
    QTime timer;
162
    timer.start();
163
    forever {
164
        if (socket->bytesAvailable() >= minBytesAvailable)
165
            return true;
166
        if (socket->state() == QAbstractSocket::UnconnectedState
167
            || timer.elapsed() >= timeout)
168
            return false;
169
        if (!socket->waitForReadyRead(timeout - timer.elapsed()))
170
            return false;
171
    }
172
}
173
174
static bool doSocketFlush(QTcpSocket *socket, int timeout = 4000)
175
{
176
#ifndef QT_NO_OPENSSL
177
    QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
178
#endif
179
    QTime timer;
180
    timer.start();
181
    forever {
182
        if (socket->bytesToWrite() == 0
183
#ifndef QT_NO_OPENSSL
184
            && sslSocket->encryptedBytesToWrite() == 0
185
#endif
186
            )
187
            return true;
188
        if (socket->state() == QAbstractSocket::UnconnectedState
189
            || timer.elapsed() >= timeout)
190
            return false;
191
        if (!socket->waitForBytesWritten(timeout - timer.elapsed()))
192
            return false;
193
    }
194
}
195
196
static void netChat(int port, const QList<Chat> &chat)
197
{
198
#ifndef QT_NO_OPENSSL
199
    QSslSocket socket;
200
#else
201
    QTcpSocket socket;
202
#endif
203
204
    socket.connectToHost(QtNetworkSettings::serverName(), port);
205
    qDebug() << 0 << "Connecting to server on port" << port;
206
    QVERIFY2(socket.waitForConnected(10000),
207
             QString("Failed to connect to server in step 0: %1").arg(socket.errorString()).toLocal8Bit());
208
209
    // now start the chat
210
    QList<Chat>::ConstIterator it = chat.constBegin();
211
    for (int i = 1; it != chat.constEnd(); ++it, ++i) {
212
        switch (it->type) {
213
            case Chat::Expect: {
214
                    qDebug() << i << "Expecting" << prettyByteArray(it->data);
215
                    if (!doSocketRead(&socket, it->data.length()))
216
                        QFAIL(QString("Failed to receive data in step %1: timeout").arg(i).toLocal8Bit());
217
218
                    // pop that many bytes off the socket
219
                    QByteArray received = socket.read(it->data.length());
220
221
                    // is it what we expected?
222
                    QVERIFY2(received == it->data,
223
                             QString("Did not receive expected data in step %1: data received was:\n%2")
224
                             .arg(i).arg(prettyByteArray(received)).toLocal8Bit());
225
226
                    break;
227
                }
228
229
            case Chat::DiscardUntil:
230
                qDebug() << i << "Discarding until" << prettyByteArray(it->data);
231
                while (true) {
232
                    // scan the buffer until we have our string
233
                    if (!doSocketRead(&socket, it->data.length()))
234
                        QFAIL(QString("Failed to receive data in step %1: timeout").arg(i).toLocal8Bit());
235
236
                    QByteArray buffer;
237
                    buffer.resize(socket.bytesAvailable());
238
                    socket.peek(buffer.data(), socket.bytesAvailable());
239
240
                    int pos = buffer.indexOf(it->data);
241
                    if (pos == -1) {
242
                        // data not found, keep trying
243
                        continue;
244
                    }
245
246
                    buffer = socket.read(pos + it->data.length());
247
                    qDebug() << i << "Discarded" << prettyByteArray(buffer);
248
                    break;
249
                }
250
                break;
251
252
            case Chat::SkipBytes: {
253
                    qDebug() << i << "Skipping" << it->value << "bytes";
254
                    if (!doSocketRead(&socket, it->value))
255
                        QFAIL(QString("Failed to receive data in step %1: timeout").arg(i).toLocal8Bit());
256
257
                    // now discard the bytes
258
                    QByteArray buffer = socket.read(it->value);
259
                    qDebug() << i << "Skipped" << prettyByteArray(buffer);
260
                    break;
261
                }
262
263
            case Chat::Send: {
264
                    qDebug() << i << "Sending" << prettyByteArray(it->data);
265
                    socket.write(it->data);
266
                    if (!doSocketFlush(&socket)) {
267
                        QVERIFY2(socket.state() == QAbstractSocket::ConnectedState,
268
                                 QString("Socket disconnected while sending data in step %1").arg(i).toLocal8Bit());
269
                        QFAIL(QString("Failed to send data in step %1: timeout").arg(i).toLocal8Bit());
270
                    }
271
                    break;
272
                }
273
274
            case Chat::Disconnect:
275
                qDebug() << i << "Disconnecting from host";
276
                socket.disconnectFromHost();
277
278
                // is this the last command?
279
                if (it + 1 != chat.constEnd())
280
                    break;
281
282
                // fall through:
283
            case Chat::RemoteDisconnect:
284
            case Chat::DiscardUntilDisconnect:
285
                qDebug() << i << "Waiting for remote disconnect";
286
                if (socket.state() != QAbstractSocket::UnconnectedState)
287
                    socket.waitForDisconnected(10000);
288
                QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState,
289
                         QString("Socket did not disconnect as expected in step %1").arg(i).toLocal8Bit());
290
291
                // any data left?
292
                if (it->type == Chat::DiscardUntilDisconnect) {
293
                    QByteArray buffer = socket.readAll();
294
                    qDebug() << i << "Discarded in the process:" << prettyByteArray(buffer);
295
                }
296
297
                if (socket.bytesAvailable() != 0)
298
                    QFAIL(QString("Unexpected bytes still on buffer when disconnecting in step %1:\n%2")
299
                          .arg(i).arg(prettyByteArray(socket.readAll())).toLocal8Bit());
300
                break;
301
302
            case Chat::Reconnect:
303
                qDebug() << i << "Reconnecting to server on port" << port;
304
                socket.connectToHost(QtNetworkSettings::serverName(), port);
305
                QVERIFY2(socket.waitForConnected(10000),
306
                         QString("Failed to reconnect to server in step %1: %2").arg(i).arg(socket.errorString()).toLocal8Bit());
307
                break;
308
309
            case Chat::StartEncryption:
310
#ifdef QT_NO_OPENSSL
311
                QFAIL("Internal error: SSL required for this test");
312
#else
313
                qDebug() << i << "Starting client encryption";
314
                socket.ignoreSslErrors();
315
                socket.startClientEncryption();
316
                QVERIFY2(socket.waitForEncrypted(5000),
317
                         QString("Failed to start client encryption in step %1: %2").arg(i)
318
                         .arg(socket.errorString()).toLocal8Bit());
319
                break;
320
#endif
321
            }
322
    }
323
}
324
325
tst_NetworkSelfTest::tst_NetworkSelfTest()
326
{
327
    Q_SET_DEFAULT_IAP
328
}
329
330
tst_NetworkSelfTest::~tst_NetworkSelfTest()
331
{
332
}
333
334
QHostAddress tst_NetworkSelfTest::serverIpAddress()
335
{
336
    if (cachedIpAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
337
        // need resolving
338
        QHostInfo resolved = QHostInfo::fromName(QtNetworkSettings::serverName());
339
        if(resolved.error() != QHostInfo::NoError ||
340
            resolved.addresses().isEmpty()) {
341
            qWarning("QHostInfo::fromName failed (%d).", resolved.error());
342
            return QHostAddress(QHostAddress::Null);
343
        }
344
        cachedIpAddress = resolved.addresses().first();
345
    }
346
    return cachedIpAddress;
347
}
348
349
void tst_NetworkSelfTest::hostTest()
350
{
351
    // this is a localhost self-test
352
    QHostInfo localhost = QHostInfo::fromName("localhost");
353
    QCOMPARE(localhost.error(), QHostInfo::NoError);
354
    QVERIFY(!localhost.addresses().isEmpty());
355
356
    QTcpServer server;
357
    QVERIFY(server.listen());
358
359
    QTcpSocket socket;
360
    socket.connectToHost("127.0.0.1", server.serverPort());
361
    QVERIFY(socket.waitForConnected(10000));
362
}
363
364
void tst_NetworkSelfTest::dnsResolution_data()
365
{
366
    QTest::addColumn<QString>("hostName");
367
    QTest::newRow("local-name") << QtNetworkSettings::serverLocalName();
368
    QTest::newRow("fqdn") << QtNetworkSettings::serverName();
369
}
370
371
void tst_NetworkSelfTest::dnsResolution()
372
{
373
    QFETCH(QString, hostName);
374
    QHostInfo resolved = QHostInfo::fromName(hostName);
375
    QVERIFY2(resolved.error() == QHostInfo::NoError,
376
             QString("Failed to resolve hostname %1: %2").arg(hostName, resolved.errorString()).toLocal8Bit());
377
    QVERIFY2(resolved.addresses().size() > 0, "Got 0 addresses for server IP");
378
379
    cachedIpAddress = resolved.addresses().first();
380
}
381
382
void tst_NetworkSelfTest::serverReachability()
383
{
384
    // check that we get a proper error connecting to port 12346
385
    QTcpSocket socket;
386
    socket.connectToHost(QtNetworkSettings::serverName(), 12346);
387
388
    QTime timer;
389
    timer.start();
390
    socket.waitForConnected(10000);
391
    QVERIFY2(timer.elapsed() < 9900, "Connection to closed port timed out instead of refusing, something is wrong");
392
393
    QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!");
394
    QVERIFY2(socket.error() == QAbstractSocket::ConnectionRefusedError,
395
             QString("Could not reach server: %1").arg(socket.errorString()).toLocal8Bit());
396
}
397
398
void tst_NetworkSelfTest::remotePortsOpen_data()
399
{
400
    QTest::addColumn<int>("portNumber");
401
    QTest::newRow("echo") << 7;
402
    QTest::newRow("daytime") << 13;
403
    QTest::newRow("ftp") << 21;
404
    QTest::newRow("ssh") << 22;
405
    QTest::newRow("imap") << 143;
406
    QTest::newRow("http") << 80;
407
    QTest::newRow("https") << 443;
408
    QTest::newRow("http-proxy") << 3128;
409
    QTest::newRow("http-proxy-auth-basic") << 3129;
410
    QTest::newRow("http-proxy-auth-ntlm") << 3130;
411
    QTest::newRow("socks5-proxy") << 1080;
412
    QTest::newRow("socks5-proxy-auth") << 1081;
413
}
414
415
void tst_NetworkSelfTest::remotePortsOpen()
416
{
417
#ifdef Q_OS_SYMBIAN
418
    if (qstrcmp(QTest::currentDataTag(), "http-proxy-auth-ntlm") == 0)
419
        QSKIP("NTML authentication not yet supported in Symbian", SkipSingle);
420
#endif
421
422
    QFETCH(int, portNumber);
423
    QTcpSocket socket;
424
    socket.connectToHost(QtNetworkSettings::serverName(), portNumber);
425
426
    if (!socket.waitForConnected(10000)) {
427
        if (socket.error() == QAbstractSocket::SocketTimeoutError)
428
            QFAIL(QString("Network timeout connecting to the server on port %1").arg(portNumber).toLocal8Bit());
429
        else
430
            QFAIL(QString("Error connecting to server on port %1: %2").arg(portNumber).arg(socket.errorString()).toLocal8Bit());
431
    }
432
    QVERIFY(socket.state() == QAbstractSocket::ConnectedState);
433
}
434
435
436
void tst_NetworkSelfTest::fileLineEndingTest()
437
{
438
    QString referenceName = SRCDIR "/rfc3252.txt";
439
    long long expectedReferenceSize = 25962;
440
441
    QString lineEndingType("LF");
442
443
    QFile reference(referenceName);
444
    QVERIFY(reference.open(QIODevice::ReadOnly));
445
    QByteArray byteLine = reference.readLine();
446
    if(byteLine.endsWith("\r\n"))
447
        lineEndingType = "CRLF";
448
    else if(byteLine.endsWith("\r"))
449
        lineEndingType = "CR";
450
451
    QString referenceAsTextData;
452
    QFile referenceAsText(referenceName);
453
    QVERIFY(referenceAsText.open(QIODevice::ReadOnly));
454
    referenceAsTextData = referenceAsText.readAll();
455
456
    QVERIFY2(expectedReferenceSize == referenceAsTextData.length(), QString("Reference file %1 has %2 as line ending and file size not matching - Git checkout issue !?!").arg(referenceName, lineEndingType).toLocal8Bit());
457
    QVERIFY2(!lineEndingType.compare("LF"), QString("Reference file %1 has %2 as line ending - Git checkout issue !?!").arg(referenceName, lineEndingType).toLocal8Bit());
458
}
459
460
static QList<Chat> ftpChat()
461
{
462
    return QList<Chat>() << Chat::expect("220")
463
            << Chat::discardUntil("\r\n")
464
            << Chat::send("USER anonymous\r\n")
465
            << Chat::expect("331")
466
            << Chat::discardUntil("\r\n")
467
            << Chat::send("PASS user@hostname\r\n")
468
            << Chat::expect("230")
469
            << Chat::discardUntil("\r\n")
470
            << Chat::send("QUIT\r\n")
471
            << Chat::expect("221")
472
            << Chat::discardUntil("\r\n")
473
            << Chat::RemoteDisconnect;
474
}
475
476
void tst_NetworkSelfTest::ftpServer()
477
{
478
    netChat(21, ftpChat());
479
}
480
481
void tst_NetworkSelfTest::imapServer()
482
{
483
    netChat(143, QList<Chat>()
484
            << Chat::expect("* OK ")
485
            << Chat::discardUntil("\r\n")
486
            << Chat::send("1 CAPABILITY\r\n")
487
            << Chat::expect("* CAPABILITY ")
488
            << Chat::discardUntil("1 OK")
489
            << Chat::discardUntil("\r\n")
490
            << Chat::send("2 LOGOUT\r\n")
491
            << Chat::discardUntil("2 OK")
492
            << Chat::discardUntil("\r\n")
493
            << Chat::RemoteDisconnect);
494
}
495
496
void tst_NetworkSelfTest::httpServer()
497
{
498
    netChat(80, QList<Chat>()
499
            // HTTP/0.9 chat:
500
            << Chat::send("GET /\r\n")
501
            << Chat::DiscardUntilDisconnect
502
503
            // HTTP/1.0 chat:
504
            << Chat::Reconnect
505
            << Chat::send("GET / HTTP/1.0\r\n"
506
                          "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
507
                          "Connection: close\r\n"
508
                          "\r\n")
509
            << Chat::expect("HTTP/1.")
510
            << Chat::discardUntil(" ")
511
            << Chat::expect("200 ")
512
            << Chat::DiscardUntilDisconnect
513
514
            // HTTP/1.0 POST:
515
            << Chat::Reconnect
516
            << Chat::send("POST / HTTP/1.0\r\n"
517
                          "Content-Length: 5\r\n"
518
                          "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
519
                          "Connection: close\r\n"
520
                          "\r\n"
521
                          "Hello")
522
            << Chat::expect("HTTP/1.")
523
            << Chat::discardUntil(" ")
524
            << Chat::expect("200 ")
525
            << Chat::DiscardUntilDisconnect
526
            );
527
}
528
529
void tst_NetworkSelfTest::httpsServer()
530
{
531
#ifndef QT_NO_OPENSSL
532
    netChat(443, QList<Chat>()
533
            << Chat::StartEncryption
534
            << Chat::send("GET / HTTP/1.0\r\n"
535
                          "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
536
                          "Connection: close\r\n"
537
                          "\r\n")
538
            << Chat::expect("HTTP/1.")
539
            << Chat::discardUntil(" ")
540
            << Chat::expect("200 ")
541
            << Chat::DiscardUntilDisconnect);
542
#else
543
    QSKIP("SSL not enabled, cannot test", SkipAll);
544
#endif
545
}
546
547
void tst_NetworkSelfTest::httpProxy()
548
{
549
    netChat(3128, QList<Chat>()
550
            // proxy GET by IP
551
            << Chat::send("GET http://" + serverIpAddress().toString().toLatin1() + "/ HTTP/1.0\r\n"
552
                          "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
553
                          "Proxy-connection: close\r\n"
554
                          "\r\n")
555
            << Chat::expect("HTTP/1.")
556
            << Chat::discardUntil(" ")
557
            << Chat::expect("200 ")
558
            << Chat::DiscardUntilDisconnect
559
560
            // proxy GET by hostname
561
            << Chat::Reconnect
562
            << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n"
563
                          "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
564
                          "Proxy-connection: close\r\n"
565
                          "\r\n")
566
            << Chat::expect("HTTP/1.")
567
            << Chat::discardUntil(" ")
568
            << Chat::expect("200 ")
569
            << Chat::DiscardUntilDisconnect
570
571
            // proxy CONNECT by IP
572
            << Chat::Reconnect
573
            << Chat::send("CONNECT " + serverIpAddress().toString().toLatin1() + ":21 HTTP/1.0\r\n"
574
                          "\r\n")
575
            << Chat::expect("HTTP/1.")
576
            << Chat::discardUntil(" ")
577
            << Chat::expect("200 ")
578
            << Chat::discardUntil("\r\n\r\n")
579
            << ftpChat()
580
581
            // proxy CONNECT by hostname
582
            << Chat::Reconnect
583
            << Chat::send("CONNECT " + QtNetworkSettings::serverName().toLatin1() + ":21 HTTP/1.0\r\n"
584
                          "\r\n")
585
            << Chat::expect("HTTP/1.")
586
            << Chat::discardUntil(" ")
587
            << Chat::expect("200 ")
588
            << Chat::discardUntil("\r\n\r\n")
589
            << ftpChat()
590
            );
591
}
592
593
void tst_NetworkSelfTest::httpProxyBasicAuth()
594
{
595
    netChat(3129, QList<Chat>()
596
            // test auth required response
597
            << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n"
598
                          "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
599
                          "Proxy-connection: close\r\n"
600
                          "\r\n")
601
            << Chat::expect("HTTP/1.")
602
            << Chat::discardUntil(" ")
603
            << Chat::expect("407 ")
604
            << Chat::discardUntil("\r\nProxy-Authenticate: Basic realm=\"")
605
            << Chat::DiscardUntilDisconnect
606
607
            // now try sending our credentials
608
            << Chat::Reconnect
609
            << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n"
610
                          "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
611
                          "Proxy-connection: close\r\n"
612
                          "Proxy-Authorization: Basic cXNvY2tzdGVzdDpwYXNzd29yZA==\r\n"
613
                          "\r\n")
614
            << Chat::expect("HTTP/1.")
615
            << Chat::discardUntil(" ")
616
            << Chat::expect("200 ")
617
            << Chat::DiscardUntilDisconnect);
618
}
619
620
void tst_NetworkSelfTest::httpProxyNtlmAuth()
621
{
622
#ifdef Q_OS_SYMBIAN
623
    QSKIP("NTML authentication not yet supported in Symbian", SkipAll);
624
#else
625
    netChat(3130, QList<Chat>()
626
            // test auth required response
627
            << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n"
628
                          "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n"
629
                          "Proxy-connection: keep-alive\r\n" // NTLM auth will disconnect
630
                          "\r\n")
631
            << Chat::expect("HTTP/1.")
632
            << Chat::discardUntil(" ")
633
            << Chat::expect("407 ")
634
            << Chat::discardUntil("\r\nProxy-Authenticate: NTLM\r\n")
635
            << Chat::DiscardUntilDisconnect
636
            );
637
#endif
638
}
639
640
// SOCKSv5 is a binary protocol
641
static const char handshakeNoAuth[] = "\5\1\0";
642
static const char handshakeOkNoAuth[] = "\5\0";
643
static const char handshakeAuthPassword[] = "\5\1\2\1\12qsockstest\10password";
644
static const char handshakeOkPasswdAuth[] = "\5\2\1\0";
645
static const char handshakeAuthNotOk[] = "\5\377";
646
static const char connect1[] = "\5\1\0\1\177\0\0\1\0\25"; // Connect IPv4 127.0.0.1 port 21
647
static const char connect1a[] = "\5\1\0\1"; // just "Connect to IPv4"
648
static const char connect1b[] = "\0\25"; // just "port 21"
649
static const char connect2[] = "\5\1\0\3\11localhost\0\25"; // Connect hostname localhost 21
650
static const char connect2a[] = "\5\1\0\3"; // just "Connect to hostname"
651
static const char connected[] = "\5\0\0";
652
653
#define QBA(x) (QByteArray::fromRawData(x, -1 + sizeof(x)))
654
655
void tst_NetworkSelfTest::socks5Proxy()
656
{
657
    union {
658
        char buf[4];
659
        quint32 data;
660
    } ip4Address;
661
    ip4Address.data = qToBigEndian(serverIpAddress().toIPv4Address());
662
663
    netChat(1080, QList<Chat>()
664
            // IP address connection
665
            << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
666
            << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth))
667
            << Chat::send(QByteArray(connect1, -1 + sizeof connect1))
668
            << Chat::expect(QByteArray(connected, -1 + sizeof connected))
669
            << Chat::expect("\1") // IPv4 address following
670
            << Chat::skipBytes(6) // the server's local address and port
671
            << ftpChat()
672
673
            // connect by IP
674
            << Chat::Reconnect
675
            << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
676
            << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth))
677
            << Chat::send(QBA(connect1a) + QByteArray::fromRawData(ip4Address.buf, 4) + QBA(connect1b))
678
            << Chat::expect(QByteArray(connected, -1 + sizeof connected))
679
            << Chat::expect("\1") // IPv4 address following
680
            << Chat::skipBytes(6) // the server's local address and port
681
            << ftpChat()
682
683
            // connect to "localhost" by hostname
684
            << Chat::Reconnect
685
            << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
686
            << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth))
687
            << Chat::send(QByteArray(connect2, -1 + sizeof connect2))
688
            << Chat::expect(QByteArray(connected, -1 + sizeof connected))
689
            << Chat::expect("\1") // IPv4 address following
690
            << Chat::skipBytes(6) // the server's local address and port
691
            << ftpChat()
692
693
            // connect to server by its official name
694
            << Chat::Reconnect
695
            << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
696
            << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth))
697
            << Chat::send(QBA(connect2a) + char(QtNetworkSettings::serverName().size()) + QtNetworkSettings::serverName().toLatin1() + QBA(connect1b))
698
            << Chat::expect(QByteArray(connected, -1 + sizeof connected))
699
            << Chat::expect("\1") // IPv4 address following
700
            << Chat::skipBytes(6) // the server's local address and port
701
            << ftpChat()
702
            );
703
}
704
705
void tst_NetworkSelfTest::socks5ProxyAuth()
706
{
707
    netChat(1081, QList<Chat>()
708
            // unauthenticated connect -- will get error
709
            << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth))
710
            << Chat::expect(QByteArray(handshakeAuthNotOk, -1 + sizeof handshakeAuthNotOk))
711
            << Chat::RemoteDisconnect
712
713
            // now try to connect with authentication
714
            << Chat::Reconnect
715
            << Chat::send(QByteArray(handshakeAuthPassword, -1 + sizeof handshakeAuthPassword))
716
            << Chat::expect(QByteArray(handshakeOkPasswdAuth, -1 + sizeof handshakeOkPasswdAuth))
717
            << Chat::send(QByteArray(connect1, -1 + sizeof connect1))
718
            << Chat::expect(QByteArray(connected, -1 + sizeof connected))
719
            << Chat::expect("\1") // IPv4 address following
720
            << Chat::skipBytes(6) // the server's local address and port
721
            << ftpChat()
722
            );
723
}
724
725
void tst_NetworkSelfTest::supportsSsl()
726
{
727
#ifdef QT_NO_OPENSSL
728
    QFAIL("SSL not compiled in");
729
#else
730
    QVERIFY(QSslSocket::supportsSsl());
731
#endif
732
}
733
734
QTEST_MAIN(tst_NetworkSelfTest)
735
#include "tst_networkselftest.moc"