Coverage Report

Created: 2026-03-12 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/network/socket/qsocks5socketengine.cpp
Line
Count
Source
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
// Qt-Security score:critical reason:network-protocol
4
5
#include "qsocks5socketengine_p.h"
6
7
#include "qtcpsocket.h"
8
#include "qudpsocket.h"
9
#include "qtcpserver.h"
10
#include "qdebug.h"
11
#include "qhash.h"
12
#include "qqueue.h"
13
#include "qdeadlinetimer.h"
14
#include "qelapsedtimer.h"
15
#include "qmutex.h"
16
#include "qthread.h"
17
#include "qcoreapplication.h"
18
#include "qurl.h"
19
#include "qauthenticator.h"
20
#include "private/qiodevice_p.h"
21
#include "private/qringbuffer_p.h"
22
#include <qendian.h>
23
#include <qnetworkinterface.h>
24
25
#include <QtCore/qbasictimer.h>
26
#include <QtCore/qpointer.h>
27
28
#include <memory>
29
30
QT_BEGIN_NAMESPACE
31
32
using namespace Qt::StringLiterals;
33
using namespace std::chrono_literals;
34
35
static const int MaxWriteBufferSize = 128*1024;
36
37
//#define QSOCKS5SOCKETLAYER_DEBUG
38
39
#define MAX_DATA_DUMP 256
40
static constexpr auto Socks5BlockingBindTimeout = 5s;
41
42
0
#define Q_INIT_CHECK(returnValue) do { \
43
0
    if (!d->data) { \
44
0
        return returnValue; \
45
0
    } } while (0)
46
47
0
#define S5_VERSION_5 0x05
48
0
#define S5_CONNECT 0x01
49
0
#define S5_BIND 0x02
50
0
#define S5_UDP_ASSOCIATE 0x03
51
0
#define S5_IP_V4 0x01
52
0
#define S5_DOMAINNAME 0x03
53
0
#define S5_IP_V6 0x04
54
0
#define S5_SUCCESS 0x00
55
#define S5_R_ERROR_SOCKS_FAILURE 0x01
56
#define S5_R_ERROR_CON_NOT_ALLOWED 0x02
57
#define S5_R_ERROR_NET_UNREACH 0x03
58
#define S5_R_ERROR_HOST_UNREACH 0x04
59
#define S5_R_ERROR_CONN_REFUSED 0x05
60
#define S5_R_ERROR_TTL 0x06
61
#define S5_R_ERROR_CMD_NOT_SUPPORTED 0x07
62
#define S5_R_ERROR_ADD_TYPE_NOT_SUPORTED 0x08
63
64
0
#define S5_AUTHMETHOD_NONE 0x00
65
#define S5_AUTHMETHOD_PASSWORD 0x02
66
0
#define S5_AUTHMETHOD_NOTACCEPTABLE 0xFF
67
68
0
#define S5_PASSWORDAUTH_VERSION 0x01
69
70
#ifdef QSOCKS5SOCKETLAYER_DEBUG
71
#  define QSOCKS5_Q_DEBUG qDebug() << this
72
#  define QSOCKS5_D_DEBUG qDebug() << q_ptr
73
#  define QSOCKS5_DEBUG qDebug() << "[QSocks5]"
74
static QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State s)
75
{
76
    switch (s) {
77
    case QSocks5SocketEnginePrivate::Uninitialized: return "Uninitialized"_L1;
78
    case QSocks5SocketEnginePrivate::ConnectError: return "ConnectError"_L1;
79
    case QSocks5SocketEnginePrivate::AuthenticationMethodsSent: return "AuthenticationMethodsSent"_L1;
80
    case QSocks5SocketEnginePrivate::Authenticating: return "Authenticating"_L1;
81
    case QSocks5SocketEnginePrivate::AuthenticatingError: return "AuthenticatingError"_L1;
82
    case QSocks5SocketEnginePrivate::RequestMethodSent: return "RequestMethodSent"_L1;
83
    case QSocks5SocketEnginePrivate::RequestError: return "RequestError"_L1;
84
    case QSocks5SocketEnginePrivate::Connected: return "Connected"_L1;
85
    case QSocks5SocketEnginePrivate::UdpAssociateSuccess: return "UdpAssociateSuccess"_L1;
86
    case QSocks5SocketEnginePrivate::BindSuccess: return "BindSuccess"_L1;
87
    case QSocks5SocketEnginePrivate::ControlSocketError: return "ControlSocketError"_L1;
88
    case QSocks5SocketEnginePrivate::SocksError: return "SocksError"_L1;
89
    case QSocks5SocketEnginePrivate::HostNameLookupError: return "HostNameLookupError"_L1;
90
    default: break;
91
    }
92
    return "unknown state"_L1;
93
}
94
95
static QString dump(const QByteArray &buf)
96
{
97
    QString data;
98
    for (int i = 0; i < qMin<int>(MAX_DATA_DUMP, buf.size()); ++i) {
99
        if (i) data += u' ';
100
        uint val = (unsigned char)buf.at(i);
101
       // data += QString("0x%1").arg(val, 3, 16, u'0');
102
        data += QString::number(val);
103
    }
104
    if (buf.size() > MAX_DATA_DUMP)
105
        data += " ..."_L1;
106
107
    return QString::fromLatin1("size: %1 data: { %2 }").arg(buf.size()).arg(data);
108
}
109
110
#else
111
0
#  define QSOCKS5_DEBUG if (0) qDebug()
112
0
#  define QSOCKS5_Q_DEBUG if (0) qDebug()
113
0
#  define QSOCKS5_D_DEBUG if (0) qDebug()
114
115
0
static inline QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State) { return QString(); }
116
0
static inline QString dump(const QByteArray &) { return QString(); }
117
#endif
118
119
/*
120
   inserts the host address in buf at pos and updates pos.
121
   if the func fails the data in buf and the value of pos is undefined
122
*/
123
static bool qt_socks5_set_host_address_and_port(const QHostAddress &address, quint16 port, QByteArray *pBuf)
124
0
{
125
0
    QSOCKS5_DEBUG << "setting [" << address << ':' << port << ']';
126
127
0
    union {
128
0
        quint16 port;
129
0
        quint32 ipv4;
130
0
        QIPv6Address ipv6;
131
0
        char ptr;
132
0
    } data;
133
134
    // add address
135
0
    if (address.protocol() == QAbstractSocket::IPv4Protocol) {
136
0
        data.ipv4 = qToBigEndian<quint32>(address.toIPv4Address());
137
0
        pBuf->append(S5_IP_V4);
138
0
        pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv4));
139
0
    } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
140
0
        data.ipv6 = address.toIPv6Address();
141
0
        pBuf->append(S5_IP_V6);
142
0
        pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv6));
143
0
    } else {
144
0
        return false;
145
0
    }
146
147
    // add port
148
0
    data.port = qToBigEndian<quint16>(port);
149
0
    pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
150
0
    return true;
151
0
}
152
153
/*
154
   like above, but for a hostname
155
*/
156
static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 port, QByteArray *pBuf)
157
0
{
158
0
    QSOCKS5_DEBUG << "setting [" << hostname << ':' << port << ']';
159
160
0
    QByteArray encodedHostName = QUrl::toAce(hostname);
161
0
    QByteArray &buf = *pBuf;
162
163
0
    if (encodedHostName.size() > 255)
164
0
        return false;
165
166
0
    buf.append(S5_DOMAINNAME);
167
0
    buf.append(uchar(encodedHostName.size()));
168
0
    buf.append(encodedHostName);
169
170
    // add port
171
0
    union {
172
0
        quint16 port;
173
0
        char ptr;
174
0
    } data;
175
0
    data.port = qToBigEndian<quint16>(port);
176
0
    buf.append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
177
178
0
    return true;
179
0
}
180
181
182
/*
183
   retrieves the host address in buf at pos and updates pos.
184
   return 1 if OK, 0 if need more data, -1 if error
185
   if the func fails the value of the address and the pos is undefined
186
*/
187
static int qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddress *pAddress, quint16 *pPort, int *pPos)
188
0
{
189
0
    int ret = -1;
190
0
    int pos = *pPos;
191
0
    const unsigned char *pBuf = reinterpret_cast<const unsigned char*>(buf.constData());
192
0
    QHostAddress address;
193
0
    quint16 port = 0;
194
195
0
    if (buf.size() - pos < 1) {
196
0
        QSOCKS5_DEBUG << "need more data address/port";
197
0
        return 0;
198
0
    }
199
0
    if (pBuf[pos] == S5_IP_V4) {
200
0
        pos++;
201
0
        if (buf.size() - pos < 4) {
202
0
            QSOCKS5_DEBUG << "need more data for ip4 address";
203
0
            return 0;
204
0
        }
205
0
        address.setAddress(qFromBigEndian<quint32>(&pBuf[pos]));
206
0
        pos += 4;
207
0
        ret = 1;
208
0
    } else if (pBuf[pos] == S5_IP_V6) {
209
0
        pos++;
210
0
        if (buf.size() - pos < 16) {
211
0
            QSOCKS5_DEBUG << "need more data for ip6 address";
212
0
            return 0;
213
0
        }
214
0
        QIPv6Address add;
215
0
        for (int i = 0; i < 16; ++i)
216
0
            add[i] = buf[pos++];
217
0
        address.setAddress(add);
218
0
        ret = 1;
219
0
    } else if (pBuf[pos] == S5_DOMAINNAME){
220
        // just skip it
221
0
        pos++;
222
0
        qDebug() << "skipping hostname of len" << uint(pBuf[pos]);
223
0
        pos += uchar(pBuf[pos]);
224
0
    } else {
225
0
        QSOCKS5_DEBUG << "invalid address type" << (int)pBuf[pos];
226
0
        ret = -1;
227
0
    }
228
229
0
    if (ret == 1) {
230
0
        if (buf.size() - pos < 2) {
231
0
            QSOCKS5_DEBUG << "need more data for port";
232
0
            return 0;
233
0
        }
234
0
        port = qFromBigEndian<quint16>(&pBuf[pos]);
235
0
        pos += 2;
236
0
    }
237
238
0
    if (ret == 1) {
239
0
        QSOCKS5_DEBUG << "got [" << address << ':' << port << ']';
240
0
        *pAddress = address;
241
0
        *pPort = port;
242
0
        *pPos = pos;
243
0
    }
244
245
0
    return ret;
246
0
}
247
248
struct QSocks5Data
249
{
250
    QTcpSocket *controlSocket;
251
    QSocks5Authenticator *authenticator;
252
};
253
254
struct QSocks5ConnectData : public QSocks5Data
255
{
256
    QRingBuffer readBuffer;
257
};
258
259
struct QSocks5BindData : public QSocks5Data
260
{
261
    QHostAddress localAddress;
262
    quint16 localPort = 0;
263
    QHostAddress peerAddress;
264
    quint16 peerPort = 0;
265
    QElapsedTimer timeStamp;
266
};
267
268
struct QSocks5RevivedDatagram
269
{
270
    QByteArray data;
271
    QHostAddress address;
272
    quint16 port = 0;
273
};
274
275
#ifndef QT_NO_UDPSOCKET
276
struct QSocks5UdpAssociateData : public QSocks5Data
277
{
278
    QUdpSocket *udpSocket = nullptr;
279
    QHostAddress associateAddress;
280
    quint16 associatePort = 0;
281
    QQueue<QSocks5RevivedDatagram> pendingDatagrams;
282
};
283
#endif
284
285
// needs to be thread safe
286
class QSocks5BindStore : public QObject
287
{
288
public:
289
    QSocks5BindStore();
290
    ~QSocks5BindStore();
291
292
    void add(qintptr socketDescriptor, QSocks5BindData *bindData);
293
    bool contains(qintptr socketDescriptor);
294
    QSocks5BindData *retrieve(qintptr socketDescriptor);
295
296
protected:
297
    void timerEvent(QTimerEvent * event) override;
298
299
    QRecursiveMutex mutex;
300
    QBasicTimer sweepTimer;
301
    //socket descriptor, data, timestamp
302
    QHash<qintptr, QSocks5BindData *> store;
303
};
304
305
Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore)
306
307
QSocks5BindStore::QSocks5BindStore()
308
0
{
309
0
    QCoreApplication *app = QCoreApplication::instance();
310
0
    if (app && app->thread() != thread())
311
0
        moveToThread(app->thread());
312
0
}
313
314
QSocks5BindStore::~QSocks5BindStore()
315
0
{
316
0
}
317
318
void QSocks5BindStore::add(qintptr socketDescriptor, QSocks5BindData *bindData)
319
0
{
320
0
    QMutexLocker lock(&mutex);
321
0
    if (store.contains(socketDescriptor)) {
322
        // qDebug("delete it");
323
0
    }
324
0
    bindData->timeStamp.start();
325
0
    store.insert(socketDescriptor, bindData);
326
327
    // start sweep timer if not started
328
0
    if (!sweepTimer.isActive())
329
0
        sweepTimer.start(1min, this);
330
0
}
331
332
bool QSocks5BindStore::contains(qintptr socketDescriptor)
333
0
{
334
0
    QMutexLocker lock(&mutex);
335
0
    return store.contains(socketDescriptor);
336
0
}
337
338
QSocks5BindData *QSocks5BindStore::retrieve(qintptr socketDescriptor)
339
0
{
340
0
    QMutexLocker lock(&mutex);
341
0
    const auto it = store.constFind(socketDescriptor);
342
0
    if (it == store.cend())
343
0
        return nullptr;
344
0
    QSocks5BindData *bindData = it.value();
345
0
    store.erase(it);
346
0
    if (bindData) {
347
0
        if (bindData->controlSocket->thread() != QThread::currentThread()) {
348
0
            qWarning("Cannot access socks5 bind data from different thread");
349
0
            return nullptr;
350
0
        }
351
0
    } else {
352
0
        QSOCKS5_DEBUG << "__ERROR__ binddata == 0";
353
0
    }
354
    // stop the sweep timer if not needed
355
0
    if (store.isEmpty())
356
0
        sweepTimer.stop();
357
0
    return bindData;
358
0
}
359
360
void QSocks5BindStore::timerEvent(QTimerEvent * event)
361
0
{
362
0
    QMutexLocker lock(&mutex);
363
0
    if (event->id() == sweepTimer.id()) {
364
0
        QSOCKS5_DEBUG << "QSocks5BindStore performing sweep";
365
0
        for (auto it = store.begin(), end = store.end(); it != end;) {
366
0
            if (it.value()->timeStamp.hasExpired(350000)) {
367
0
                QSOCKS5_DEBUG << "QSocks5BindStore removing JJJJ";
368
0
                it = store.erase(it);
369
0
            } else {
370
0
                ++it;
371
0
            }
372
0
        }
373
0
    }
374
0
}
375
376
QSocks5Authenticator::QSocks5Authenticator()
377
0
{
378
0
}
379
380
QSocks5Authenticator::~QSocks5Authenticator()
381
0
{
382
0
}
383
384
char QSocks5Authenticator::methodId()
385
0
{
386
0
    return 0x00;
387
0
}
388
389
bool QSocks5Authenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
390
0
{
391
0
    Q_UNUSED(socket);
392
0
    *completed = true;
393
0
    return true;
394
0
}
395
396
bool QSocks5Authenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
397
0
{
398
0
    Q_UNUSED(socket);
399
0
    *completed = true;
400
0
    return true;
401
0
}
402
403
bool QSocks5Authenticator::seal(const QByteArray &buf, QByteArray *sealedBuf)
404
0
{
405
0
    *sealedBuf = buf;
406
0
    return true;
407
0
}
408
409
bool QSocks5Authenticator::unSeal(const QByteArray &sealedBuf, QByteArray *buf)
410
0
{
411
0
    *buf = sealedBuf;
412
0
    return true;
413
0
}
414
415
bool QSocks5Authenticator::unSeal(QTcpSocket *sealedSocket, QByteArray *buf)
416
0
{
417
0
    return unSeal(sealedSocket->readAll(), buf);
418
0
}
419
420
QSocks5PasswordAuthenticator::QSocks5PasswordAuthenticator(const QString &userName, const QString &password)
421
0
{
422
0
    this->userName = userName;
423
0
    this->password = password;
424
0
}
425
426
char QSocks5PasswordAuthenticator::methodId()
427
0
{
428
0
    return 0x02;
429
0
}
430
431
bool QSocks5PasswordAuthenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
432
0
{
433
0
    *completed = false;
434
0
    QByteArray uname = userName.toLatin1();
435
0
    QByteArray passwd = password.toLatin1();
436
0
    QByteArray dataBuf(3 + uname.size() + passwd.size(), 0);
437
0
    char *buf = dataBuf.data();
438
0
    int pos = 0;
439
0
    buf[pos++] = S5_PASSWORDAUTH_VERSION;
440
0
    buf[pos++] = uname.size();
441
0
    memcpy(&buf[pos], uname.data(), uname.size());
442
0
    pos += uname.size();
443
0
    buf[pos++] = passwd.size();
444
0
    memcpy(&buf[pos], passwd.data(), passwd.size());
445
0
    return socket->write(dataBuf) == dataBuf.size();
446
0
}
447
448
bool QSocks5PasswordAuthenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
449
0
{
450
0
    *completed = false;
451
452
0
    if (socket->bytesAvailable() < 2)
453
0
        return true;
454
455
0
    QByteArray buf = socket->read(2);
456
0
    if (buf.at(0) == S5_PASSWORDAUTH_VERSION && buf.at(1) == 0x00) {
457
0
        *completed = true;
458
0
        return true;
459
0
    }
460
461
    // must disconnect
462
0
    socket->close();
463
0
    return false;
464
0
}
465
466
QString QSocks5PasswordAuthenticator::errorString()
467
0
{
468
0
    return "Socks5 user name or password incorrect"_L1;
469
0
}
470
471
472
473
QSocks5SocketEnginePrivate::QSocks5SocketEnginePrivate()
474
0
    : socks5State(Uninitialized)
475
0
    , readNotificationEnabled(false)
476
0
    , writeNotificationEnabled(false)
477
0
    , exceptNotificationEnabled(false)
478
0
    , socketDescriptor(-1)
479
0
    , data(nullptr)
480
0
    , connectData(nullptr)
481
#ifndef QT_NO_UDPSOCKET
482
0
    , udpData(nullptr)
483
#endif
484
0
    , bindData(nullptr)
485
0
    , readNotificationActivated(false)
486
0
    , writeNotificationActivated(false)
487
0
    , readNotificationPending(false)
488
0
    , writeNotificationPending(false)
489
0
    , connectionNotificationPending(false)
490
0
{
491
0
    mode = NoMode;
492
0
}
493
494
QSocks5SocketEnginePrivate::~QSocks5SocketEnginePrivate()
495
0
{
496
0
}
497
498
void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode)
499
0
{
500
0
    Q_Q(QSocks5SocketEngine);
501
502
0
    mode = socks5Mode;
503
0
    if (mode == ConnectMode) {
504
0
        connectData = new QSocks5ConnectData;
505
0
        data = connectData;
506
0
#ifndef QT_NO_UDPSOCKET
507
0
    } else if (mode == UdpAssociateMode) {
508
0
        udpData = new QSocks5UdpAssociateData;
509
0
        data = udpData;
510
0
        udpData->udpSocket = new QUdpSocket(q);
511
0
        udpData->udpSocket->setProxy(QNetworkProxy::NoProxy);
512
0
        QObject::connect(udpData->udpSocket, SIGNAL(readyRead()),
513
0
                         q, SLOT(_q_udpSocketReadNotification()),
514
0
                         Qt::DirectConnection);
515
0
#endif // QT_NO_UDPSOCKET
516
0
    } else if (mode == BindMode) {
517
0
        bindData = new QSocks5BindData;
518
0
        data = bindData;
519
0
    }
520
521
0
    data->controlSocket = new QTcpSocket(q);
522
0
    data->controlSocket->setProxy(QNetworkProxy::NoProxy);
523
0
    QObject::connect(data->controlSocket, SIGNAL(connected()), q, SLOT(_q_controlSocketConnected()),
524
0
                     Qt::DirectConnection);
525
0
    QObject::connect(data->controlSocket, SIGNAL(readyRead()), q, SLOT(_q_controlSocketReadNotification()),
526
0
                     Qt::DirectConnection);
527
0
    QObject::connect(data->controlSocket, SIGNAL(bytesWritten(qint64)), q, SLOT(_q_controlSocketBytesWritten()),
528
0
                     Qt::DirectConnection);
529
0
    QObject::connect(data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
530
0
                     q, SLOT(_q_controlSocketErrorOccurred(QAbstractSocket::SocketError)),
531
0
                     Qt::DirectConnection);
532
0
    QObject::connect(data->controlSocket, SIGNAL(disconnected()), q, SLOT(_q_controlSocketDisconnected()),
533
0
                     Qt::DirectConnection);
534
0
    QObject::connect(data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
535
0
                     q, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
536
0
                     Qt::DirectConnection);
537
538
0
    if (!proxyInfo.user().isEmpty() || !proxyInfo.password().isEmpty()) {
539
0
        QSOCKS5_D_DEBUG << "using username/password authentication; user =" << proxyInfo.user();
540
0
        data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
541
0
    } else {
542
0
        QSOCKS5_D_DEBUG << "not using authentication";
543
0
        data->authenticator = new QSocks5Authenticator();
544
0
    }
545
0
}
546
547
void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, const QString &extraMessage)
548
0
{
549
0
    Q_Q(QSocks5SocketEngine);
550
551
0
    switch (state) {
552
0
    case Uninitialized:
553
0
    case Authenticating:
554
0
    case AuthenticationMethodsSent:
555
0
    case RequestMethodSent:
556
0
    case Connected:
557
0
    case UdpAssociateSuccess:
558
0
    case BindSuccess:
559
        // these aren't error states
560
0
        return;
561
562
0
    case ConnectError:
563
0
    case ControlSocketError: {
564
0
        QAbstractSocket::SocketError controlSocketError = data->controlSocket->error();
565
0
        if (socks5State != Connected) {
566
0
            switch (controlSocketError) {
567
0
            case QAbstractSocket::ConnectionRefusedError:
568
0
                q->setError(QAbstractSocket::ProxyConnectionRefusedError,
569
0
                             QSocks5SocketEngine::tr("Connection to proxy refused"));
570
0
                break;
571
0
            case QAbstractSocket::RemoteHostClosedError:
572
0
                q->setError(QAbstractSocket::ProxyConnectionClosedError,
573
0
                             QSocks5SocketEngine::tr("Connection to proxy closed prematurely"));
574
0
                break;
575
0
            case QAbstractSocket::HostNotFoundError:
576
0
                q->setError(QAbstractSocket::ProxyNotFoundError,
577
0
                             QSocks5SocketEngine::tr("Proxy host not found"));
578
0
                break;
579
0
            case QAbstractSocket::SocketTimeoutError:
580
0
                if (state == ConnectError) {
581
0
                    q->setError(QAbstractSocket::ProxyConnectionTimeoutError,
582
0
                                 QSocks5SocketEngine::tr("Connection to proxy timed out"));
583
0
                    break;
584
0
                }
585
0
                Q_FALLTHROUGH();
586
0
            default:
587
0
                q->setError(controlSocketError, data->controlSocket->errorString());
588
0
                break;
589
0
            }
590
0
        } else {
591
0
            q->setError(controlSocketError, data->controlSocket->errorString());
592
0
        }
593
0
        break;
594
0
    }
595
596
0
    case AuthenticatingError:
597
0
        q->setError(QAbstractSocket::ProxyAuthenticationRequiredError,
598
0
                    extraMessage.isEmpty() ?
599
0
                     QSocks5SocketEngine::tr("Proxy authentication failed") :
600
0
                     QSocks5SocketEngine::tr("Proxy authentication failed: %1").arg(extraMessage));
601
0
        break;
602
603
0
    case RequestError:
604
        // error code set by caller (overload)
605
0
        break;
606
607
0
    case SocksError:
608
0
        q->setError(QAbstractSocket::ProxyProtocolError,
609
0
                     QSocks5SocketEngine::tr("SOCKS version 5 protocol error"));
610
0
        break;
611
612
0
    case HostNameLookupError:
613
0
        q->setError(QAbstractSocket::HostNotFoundError,
614
0
                    QAbstractSocket::tr("Host not found"));
615
0
        break;
616
0
    }
617
618
0
    q->setState(QAbstractSocket::UnconnectedState);
619
0
    socks5State = state;
620
0
}
621
622
void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, Socks5Error socks5error)
623
0
{
624
0
    Q_Q(QSocks5SocketEngine);
625
0
    switch (socks5error) {
626
0
    case SocksFailure:
627
0
        q->setError(QAbstractSocket::NetworkError,
628
0
                     QSocks5SocketEngine::tr("General SOCKSv5 server failure"));
629
0
        break;
630
0
    case ConnectionNotAllowed:
631
0
        q->setError(QAbstractSocket::SocketAccessError,
632
0
                     QSocks5SocketEngine::tr("Connection not allowed by SOCKSv5 server"));
633
0
        break;
634
0
    case NetworkUnreachable:
635
0
        q->setError(QAbstractSocket::NetworkError,
636
0
                    QAbstractSocket::tr("Network unreachable"));
637
0
        break;
638
0
    case HostUnreachable:
639
0
        q->setError(QAbstractSocket::HostNotFoundError,
640
0
                    QAbstractSocket::tr("Host not found"));
641
0
        break;
642
0
    case ConnectionRefused:
643
0
        q->setError(QAbstractSocket::ConnectionRefusedError,
644
0
                    QAbstractSocket::tr("Connection refused"));
645
0
        break;
646
0
    case TTLExpired:
647
0
        q->setError(QAbstractSocket::NetworkError,
648
0
                     QSocks5SocketEngine::tr("TTL expired"));
649
0
        break;
650
0
    case CommandNotSupported:
651
0
        q->setError(QAbstractSocket::UnsupportedSocketOperationError,
652
0
                     QSocks5SocketEngine::tr("SOCKSv5 command not supported"));
653
0
        break;
654
0
    case AddressTypeNotSupported:
655
0
        q->setError(QAbstractSocket::UnsupportedSocketOperationError,
656
0
                     QSocks5SocketEngine::tr("Address type not supported"));
657
0
        break;
658
659
0
    default:
660
0
        q->setError(QAbstractSocket::UnknownSocketError,
661
0
                     QSocks5SocketEngine::tr("Unknown SOCKSv5 proxy error code 0x%1").arg(int(socks5error), 16));
662
0
        break;
663
0
    }
664
665
0
    setErrorState(state, QString());
666
0
}
667
668
void QSocks5SocketEnginePrivate::reauthenticate()
669
0
{
670
0
    Q_Q(QSocks5SocketEngine);
671
672
    // we require authentication
673
0
    QAuthenticator auth;
674
0
    q->proxyAuthenticationRequired(proxyInfo, &auth);
675
676
0
    if (!auth.user().isEmpty() || !auth.password().isEmpty()) {
677
        // we have new credentials, let's try again
678
0
        QSOCKS5_DEBUG << "authentication failure: retrying connection";
679
0
        socks5State = QSocks5SocketEnginePrivate::Uninitialized;
680
681
0
        delete data->authenticator;
682
0
        proxyInfo.setUser(auth.user());
683
0
        proxyInfo.setPassword(auth.password());
684
0
        data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
685
686
0
        {
687
0
            const QSignalBlocker blocker(data->controlSocket);
688
0
            data->controlSocket->abort();
689
0
        }
690
0
        data->controlSocket->connectToHost(proxyInfo.hostName(), proxyInfo.port());
691
0
    } else {
692
        // authentication failure
693
694
0
        setErrorState(AuthenticatingError);
695
0
        data->controlSocket->close();
696
0
        emitConnectionNotification();
697
0
    }
698
0
}
699
700
void QSocks5SocketEnginePrivate::parseAuthenticationMethodReply()
701
0
{
702
    // not enough data to begin
703
0
    if (data->controlSocket->bytesAvailable() < 2)
704
0
        return;
705
706
0
    QByteArray buf = data->controlSocket->read(2);
707
0
    if (buf.at(0) != S5_VERSION_5) {
708
0
        QSOCKS5_D_DEBUG << "Socks5 version incorrect";
709
0
        setErrorState(SocksError);
710
0
        data->controlSocket->close();
711
0
        emitConnectionNotification();
712
0
        return;
713
0
    }
714
715
0
    bool authComplete = false;
716
0
    if (uchar(buf.at(1)) == S5_AUTHMETHOD_NONE) {
717
0
        authComplete = true;
718
0
    } else if (uchar(buf.at(1)) == S5_AUTHMETHOD_NOTACCEPTABLE) {
719
0
        reauthenticate();
720
0
        return;
721
0
    } else if (buf.at(1) != data->authenticator->methodId()
722
0
               || !data->authenticator->beginAuthenticate(data->controlSocket, &authComplete)) {
723
0
        setErrorState(AuthenticatingError, "Socks5 host did not support authentication method."_L1);
724
0
        socketError = QAbstractSocket::SocketAccessError; // change the socket error
725
0
        emitConnectionNotification();
726
0
        return;
727
0
    }
728
729
0
    if (authComplete)
730
0
        sendRequestMethod();
731
0
    else
732
0
        socks5State = Authenticating;
733
0
}
734
735
void QSocks5SocketEnginePrivate::parseAuthenticatingReply()
736
0
{
737
0
    bool authComplete = false;
738
0
    if (!data->authenticator->continueAuthenticate(data->controlSocket, &authComplete)) {
739
0
        reauthenticate();
740
0
        return;
741
0
    }
742
0
    if (authComplete)
743
0
        sendRequestMethod();
744
0
}
745
746
void QSocks5SocketEnginePrivate::sendRequestMethod()
747
0
{
748
0
    QHostAddress address;
749
0
    quint16 port = 0;
750
0
    char command = 0;
751
0
    if (mode == ConnectMode) {
752
0
        command = S5_CONNECT;
753
0
        address = peerAddress;
754
0
        port = peerPort;
755
0
    } else if (mode == BindMode) {
756
0
        command = S5_BIND;
757
0
        address = localAddress;
758
0
        port = localPort;
759
0
    } else {
760
0
#ifndef QT_NO_UDPSOCKET
761
0
        command = S5_UDP_ASSOCIATE;
762
0
        address = localAddress; //data->controlSocket->localAddress();
763
0
        port = localPort;
764
0
#endif
765
0
    }
766
767
0
    QByteArray buf;
768
0
    buf.reserve(270); // big enough for domain name;
769
0
    buf.append(char(S5_VERSION_5));
770
0
    buf.append(command);
771
0
    buf.append('\0');
772
0
    if (peerName.isEmpty() && !qt_socks5_set_host_address_and_port(address, port, &buf)) {
773
0
        QSOCKS5_DEBUG << "error setting address" << address << " : " << port;
774
        //### set error code ....
775
0
        return;
776
0
    } else if (!peerName.isEmpty() && !qt_socks5_set_host_name_and_port(peerName, port, &buf)) {
777
0
        QSOCKS5_DEBUG << "error setting peer name" << peerName << " : " << port;
778
        //### set error code ....
779
0
        return;
780
0
    }
781
0
    QSOCKS5_DEBUG << "sending" << dump(buf);
782
0
    QByteArray sealedBuf;
783
0
    if (!data->authenticator->seal(buf, &sealedBuf)) {
784
        // ### Handle this error.
785
0
    }
786
0
    data->controlSocket->write(sealedBuf);
787
0
    data->controlSocket->flush();
788
0
    socks5State = RequestMethodSent;
789
0
}
790
791
void QSocks5SocketEnginePrivate::parseRequestMethodReply()
792
0
{
793
0
    Q_Q(QSocks5SocketEngine);
794
0
    QSOCKS5_DEBUG << "parseRequestMethodReply()";
795
796
0
    QByteArray inBuf;
797
0
    if (!data->authenticator->unSeal(data->controlSocket, &inBuf)) {
798
        // ### check error and not just not enough data
799
0
        QSOCKS5_DEBUG << "unSeal failed, needs more data";
800
0
        return;
801
0
    }
802
803
0
    inBuf.prepend(receivedHeaderFragment);
804
0
    receivedHeaderFragment.clear();
805
0
    QSOCKS5_DEBUG << dump(inBuf);
806
0
    if (inBuf.size() < 3) {
807
0
        QSOCKS5_DEBUG << "need more data for request reply header .. put this data somewhere";
808
0
        receivedHeaderFragment = inBuf;
809
0
        return;
810
0
    }
811
812
0
    QHostAddress address;
813
0
    quint16 port = 0;
814
815
0
    if (inBuf.at(0) != S5_VERSION_5 || inBuf.at(2) != 0x00) {
816
0
        QSOCKS5_DEBUG << "socks protocol error";
817
0
        setErrorState(SocksError);
818
0
    } else if (inBuf.at(1) != S5_SUCCESS) {
819
0
        Socks5Error socks5Error = Socks5Error(inBuf.at(1));
820
0
        QSOCKS5_DEBUG <<  "Request error :" << socks5Error;
821
0
        if ((socks5Error == SocksFailure || socks5Error == ConnectionNotAllowed)
822
0
            && !peerName.isEmpty()) {
823
            // Dante seems to use this error code to indicate hostname resolution failure
824
0
            setErrorState(HostNameLookupError);
825
0
        } else {
826
0
            setErrorState(RequestError, socks5Error);
827
0
        }
828
0
    } else {
829
        // connection success, retrieve the remote addresses
830
0
        int pos = 3;
831
0
        int err = qt_socks5_get_host_address_and_port(inBuf, &address, &port, &pos);
832
0
        if (err == -1) {
833
0
            QSOCKS5_DEBUG << "error getting address";
834
0
            setErrorState(SocksError);
835
0
        } else if (err == 0) {
836
            //need more data
837
0
            receivedHeaderFragment = inBuf;
838
0
            return;
839
0
        } else {
840
0
            inBuf.remove(0, pos);
841
0
            for (int i = inBuf.size() - 1; i >= 0 ; --i)
842
0
                data->controlSocket->ungetChar(inBuf.at(i));
843
0
        }
844
0
    }
845
846
0
    if (socks5State == RequestMethodSent) {
847
        // no error
848
0
        localAddress = address;
849
0
        localPort = port;
850
851
0
        if (mode == ConnectMode) {
852
0
            inboundStreamCount = outboundStreamCount = 1;
853
0
            socks5State = Connected;
854
            // notify the upper layer that we're done
855
0
            q->setState(QAbstractSocket::ConnectedState);
856
0
            emitConnectionNotification();
857
0
        } else if (mode == BindMode) {
858
0
            socks5State = BindSuccess;
859
0
            q->setState(QAbstractSocket::ListeningState);
860
0
        } else {
861
0
            socks5State = UdpAssociateSuccess;
862
0
        }
863
0
    } else if (socks5State == BindSuccess) {
864
        // no error and we got a connection
865
0
        bindData->peerAddress = address;
866
0
        bindData->peerPort = port;
867
868
0
        emitReadNotification();
869
0
    } else {
870
        // got an error
871
0
        data->controlSocket->close();
872
0
        emitConnectionNotification();
873
0
    }
874
0
}
875
876
void QSocks5SocketEnginePrivate::_q_emitPendingReadNotification()
877
0
{
878
0
    Q_Q(QSocks5SocketEngine);
879
0
    readNotificationPending = false;
880
0
    if (readNotificationEnabled) {
881
0
        QSOCKS5_D_DEBUG << "emitting readNotification";
882
0
        QPointer<QSocks5SocketEngine> qq = q;
883
0
        q->readNotification();
884
0
        if (!qq)
885
0
            return;
886
        // check if there needs to be a new zero read notification
887
0
        if (data && data->controlSocket->state() == QAbstractSocket::UnconnectedState
888
0
                && data->controlSocket->error() == QAbstractSocket::RemoteHostClosedError) {
889
0
            connectData->readBuffer.clear();
890
0
            emitReadNotification();
891
0
        }
892
0
    }
893
0
}
894
895
void QSocks5SocketEnginePrivate::emitReadNotification()
896
0
{
897
0
    Q_Q(QSocks5SocketEngine);
898
0
    readNotificationActivated = true;
899
0
    if (readNotificationEnabled && !readNotificationPending) {
900
0
        QSOCKS5_D_DEBUG << "queueing readNotification";
901
0
        readNotificationPending = true;
902
0
        QMetaObject::invokeMethod(q, "_q_emitPendingReadNotification", Qt::QueuedConnection);
903
0
    }
904
0
}
905
906
void QSocks5SocketEnginePrivate::_q_emitPendingWriteNotification()
907
0
{
908
0
    writeNotificationPending = false;
909
0
    Q_Q(QSocks5SocketEngine);
910
0
    if (writeNotificationEnabled) {
911
0
        QSOCKS5_D_DEBUG << "emitting writeNotification";
912
0
        q->writeNotification();
913
0
    }
914
0
}
915
916
void QSocks5SocketEnginePrivate::emitWriteNotification()
917
0
{
918
0
    Q_Q(QSocks5SocketEngine);
919
0
    writeNotificationActivated = true;
920
0
    if (writeNotificationEnabled && !writeNotificationPending) {
921
0
        QSOCKS5_D_DEBUG << "queueing writeNotification";
922
0
        writeNotificationPending = true;
923
0
        QMetaObject::invokeMethod(q, "_q_emitPendingWriteNotification", Qt::QueuedConnection);
924
0
    }
925
0
}
926
927
void QSocks5SocketEnginePrivate::_q_emitPendingConnectionNotification()
928
0
{
929
0
    connectionNotificationPending = false;
930
0
    Q_Q(QSocks5SocketEngine);
931
0
    QSOCKS5_D_DEBUG << "emitting connectionNotification";
932
0
    q->connectionNotification();
933
0
}
934
935
void QSocks5SocketEnginePrivate::emitConnectionNotification()
936
0
{
937
0
    Q_Q(QSocks5SocketEngine);
938
0
    QSOCKS5_D_DEBUG << "queueing connectionNotification";
939
0
    connectionNotificationPending = true;
940
0
    QMetaObject::invokeMethod(q, "_q_emitPendingConnectionNotification", Qt::QueuedConnection);
941
0
}
942
943
QSocks5SocketEngine::QSocks5SocketEngine(QObject *parent)
944
0
:QAbstractSocketEngine(*new QSocks5SocketEnginePrivate(), parent)
945
0
{
946
0
}
947
948
QSocks5SocketEngine::~QSocks5SocketEngine()
949
0
{
950
0
    Q_D(QSocks5SocketEngine);
951
952
0
    if (d->data) {
953
0
        delete d->data->authenticator;
954
0
        delete d->data->controlSocket;
955
0
    }
956
0
    if (d->connectData)
957
0
        delete d->connectData;
958
0
#ifndef QT_NO_UDPSOCKET
959
0
    if (d->udpData) {
960
0
        delete d->udpData->udpSocket;
961
0
        delete d->udpData;
962
0
    }
963
0
#endif
964
0
    if (d->bindData)
965
0
        delete d->bindData;
966
0
}
967
968
static int nextDescriptor()
969
0
{
970
0
    Q_CONSTINIT static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
971
0
    return 1 + counter.fetchAndAddRelaxed(1);
972
0
}
973
974
bool QSocks5SocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
975
0
{
976
0
    Q_D(QSocks5SocketEngine);
977
978
0
    d->socketDescriptor = nextDescriptor();
979
980
0
    d->socketType = type;
981
0
    d->socketProtocol = protocol;
982
983
0
    return true;
984
0
}
985
986
bool QSocks5SocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState)
987
0
{
988
0
    Q_D(QSocks5SocketEngine);
989
990
0
    QSOCKS5_Q_DEBUG << "initialize" << socketDescriptor;
991
992
    // this is only valid for the other side of a bind, nothing else is supported
993
994
0
    if (socketState != QAbstractSocket::ConnectedState) {
995
        //### must be connected state ???
996
0
        return false;
997
0
    }
998
999
0
    QSocks5BindData *bindData = socks5BindStore()->retrieve(socketDescriptor);
1000
0
    if (bindData) {
1001
1002
0
        d->socketState = QAbstractSocket::ConnectedState;
1003
0
        d->socketType = QAbstractSocket::TcpSocket;
1004
0
        d->connectData = new QSocks5ConnectData;
1005
0
        d->data = d->connectData;
1006
0
        d->mode = QSocks5SocketEnginePrivate::ConnectMode;
1007
0
        d->data->controlSocket = bindData->controlSocket;
1008
0
        bindData->controlSocket = nullptr;
1009
0
        d->data->controlSocket->setParent(this);
1010
0
        d->socketProtocol = d->data->controlSocket->localAddress().protocol();
1011
0
        d->data->authenticator = bindData->authenticator;
1012
0
        bindData->authenticator = nullptr;
1013
0
        d->localPort = bindData->localPort;
1014
0
        d->localAddress = bindData->localAddress;
1015
0
        d->peerPort = bindData->peerPort;
1016
0
        d->peerAddress = bindData->peerAddress;
1017
0
        d->inboundStreamCount = d->outboundStreamCount = 1;
1018
0
        delete bindData;
1019
1020
0
        QObject::connect(d->data->controlSocket, SIGNAL(connected()), this, SLOT(_q_controlSocketConnected()),
1021
0
                         Qt::DirectConnection);
1022
0
        QObject::connect(d->data->controlSocket, SIGNAL(readyRead()), this, SLOT(_q_controlSocketReadNotification()),
1023
0
                         Qt::DirectConnection);
1024
0
        QObject::connect(d->data->controlSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_controlSocketBytesWritten()),
1025
0
                         Qt::DirectConnection);
1026
0
        QObject::connect(d->data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketErrorOccurred(QAbstractSocket::SocketError)),
1027
0
                         Qt::DirectConnection);
1028
0
        QObject::connect(d->data->controlSocket, SIGNAL(disconnected()), this, SLOT(_q_controlSocketDisconnected()),
1029
0
                         Qt::DirectConnection);
1030
0
        QObject::connect(d->data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
1031
0
                         this, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
1032
0
                         Qt::DirectConnection);
1033
1034
0
        d->socks5State = QSocks5SocketEnginePrivate::Connected;
1035
1036
0
        if (d->data->controlSocket->bytesAvailable() != 0)
1037
0
            d->_q_controlSocketReadNotification();
1038
0
        return true;
1039
0
    }
1040
0
    return false;
1041
0
}
1042
1043
void QSocks5SocketEngine::setProxy(const QNetworkProxy &networkProxy)
1044
0
{
1045
0
    Q_D(QSocks5SocketEngine);
1046
0
    d->proxyInfo = networkProxy;
1047
0
}
1048
1049
qintptr QSocks5SocketEngine::socketDescriptor() const
1050
0
{
1051
0
    Q_D(const QSocks5SocketEngine);
1052
0
    return d->socketDescriptor;
1053
0
}
1054
1055
bool QSocks5SocketEngine::isValid() const
1056
0
{
1057
0
    Q_D(const QSocks5SocketEngine);
1058
0
    return d->socketType != QAbstractSocket::UnknownSocketType
1059
0
           && d->socks5State != QSocks5SocketEnginePrivate::SocksError
1060
0
           && (d->socketError == QAbstractSocket::UnknownSocketError
1061
0
               || d->socketError == QAbstractSocket::SocketTimeoutError
1062
0
               || d->socketError == QAbstractSocket::UnfinishedSocketOperationError);
1063
0
}
1064
1065
bool QSocks5SocketEngine::connectInternal()
1066
0
{
1067
0
    Q_D(QSocks5SocketEngine);
1068
1069
0
    if (!d->data) {
1070
0
        if (socketType() == QAbstractSocket::TcpSocket) {
1071
0
            d->initialize(QSocks5SocketEnginePrivate::ConnectMode);
1072
0
#ifndef QT_NO_UDPSOCKET
1073
0
        } else if (socketType() == QAbstractSocket::UdpSocket) {
1074
0
            d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1075
            // all udp needs to be bound
1076
0
            if (!bind(QHostAddress("0.0.0.0"_L1), 0))
1077
0
                return false;
1078
1079
0
            setState(QAbstractSocket::ConnectedState);
1080
0
            return true;
1081
0
#endif
1082
0
        } else {
1083
0
            qFatal("QSocks5SocketEngine::connectToHost: in QTcpServer mode");
1084
0
            return false;
1085
0
        }
1086
0
    }
1087
1088
0
    if (d->socketState != QAbstractSocket::ConnectingState) {
1089
0
        if (d->socks5State == QSocks5SocketEnginePrivate::Uninitialized
1090
            // We may have new auth credentials since an earlier failure:
1091
0
         || d->socks5State == QSocks5SocketEnginePrivate::AuthenticatingError) {
1092
0
            setState(QAbstractSocket::ConnectingState);
1093
            //limit buffer in internal socket, data is buffered in the external socket under application control
1094
0
            d->data->controlSocket->setReadBufferSize(65536);
1095
0
        }
1096
1097
0
        d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1098
0
    }
1099
1100
0
    return false;
1101
0
}
1102
1103
bool QSocks5SocketEngine::connectToHost(const QHostAddress &address, quint16 port)
1104
0
{
1105
0
    Q_D(QSocks5SocketEngine);
1106
0
    QSOCKS5_DEBUG << "connectToHost" << address << ':' << port;
1107
1108
0
    setPeerAddress(address);
1109
0
    setPeerPort(port);
1110
0
    d->peerName.clear();
1111
1112
0
    return connectInternal();
1113
0
}
1114
1115
bool QSocks5SocketEngine::connectToHostByName(const QString &hostname, quint16 port)
1116
0
{
1117
0
    Q_D(QSocks5SocketEngine);
1118
1119
0
    setPeerAddress(QHostAddress());
1120
0
    setPeerPort(port);
1121
0
    d->peerName = hostname;
1122
1123
0
    return connectInternal();
1124
0
}
1125
1126
void QSocks5SocketEnginePrivate::_q_controlSocketConnected()
1127
0
{
1128
0
    QSOCKS5_DEBUG << "_q_controlSocketConnected";
1129
0
    QByteArray buf(3, 0);
1130
0
    buf[0] = S5_VERSION_5;
1131
0
    buf[1] = 0x01;
1132
0
    buf[2] = data->authenticator->methodId();
1133
0
    data->controlSocket->write(buf);
1134
0
    socks5State = AuthenticationMethodsSent;
1135
0
}
1136
1137
void QSocks5SocketEnginePrivate::_q_controlSocketReadNotification()
1138
0
{
1139
0
    QSOCKS5_D_DEBUG << "_q_controlSocketReadNotification socks5state" <<  s5StateToString(socks5State)
1140
0
                    << "bytes available" << data->controlSocket->bytesAvailable();
1141
1142
0
    if (data->controlSocket->bytesAvailable() == 0) {
1143
0
        QSOCKS5_D_DEBUG << "########## bogus read why do we get these ... on windows only";
1144
0
        return;
1145
0
    }
1146
1147
0
    switch (socks5State) {
1148
0
        case AuthenticationMethodsSent:
1149
0
            parseAuthenticationMethodReply();
1150
0
            break;
1151
0
        case Authenticating:
1152
0
            parseAuthenticatingReply();
1153
0
            break;
1154
0
        case RequestMethodSent:
1155
0
            parseRequestMethodReply();
1156
0
            if (socks5State == Connected && data->controlSocket->bytesAvailable())
1157
0
                _q_controlSocketReadNotification();
1158
0
            break;
1159
0
        case Connected: {
1160
0
            QByteArray buf;
1161
0
            if (!data->authenticator->unSeal(data->controlSocket, &buf)) {
1162
                // qDebug("unseal error maybe need to wait for more data");
1163
0
            }
1164
0
            if (buf.size()) {
1165
0
                QSOCKS5_DEBUG << dump(buf);
1166
0
                connectData->readBuffer.append(std::move(buf));
1167
0
                emitReadNotification();
1168
0
            }
1169
0
            break;
1170
0
        }
1171
0
        case BindSuccess:
1172
            // only get here if command is bind
1173
0
            if (mode == BindMode) {
1174
0
                parseRequestMethodReply();
1175
0
                break;
1176
0
            }
1177
1178
0
            Q_FALLTHROUGH();
1179
0
        default:
1180
0
            qWarning("QSocks5SocketEnginePrivate::_q_controlSocketReadNotification: "
1181
0
                     "Unexpectedly received data while in state=%d and mode=%d",
1182
0
                     socks5State, mode);
1183
0
            break;
1184
0
    };
1185
0
}
1186
1187
void QSocks5SocketEnginePrivate::_q_controlSocketBytesWritten()
1188
0
{
1189
0
    QSOCKS5_DEBUG << "_q_controlSocketBytesWritten";
1190
1191
0
    if (socks5State != Connected
1192
0
        || (mode == ConnectMode
1193
0
        && data->controlSocket->bytesToWrite()))
1194
0
        return;
1195
0
    if (data->controlSocket->bytesToWrite() < MaxWriteBufferSize) {
1196
0
        emitWriteNotification();
1197
0
        writeNotificationActivated = false;
1198
0
    }
1199
0
}
1200
1201
void QSocks5SocketEnginePrivate::_q_controlSocketErrorOccurred(QAbstractSocket::SocketError error)
1202
0
{
1203
0
    QSOCKS5_D_DEBUG << "controlSocketError" << error << data->controlSocket->errorString();
1204
1205
0
    if (error == QAbstractSocket::SocketTimeoutError)
1206
0
        return;                 // ignore this error -- comes from the waitFor* functions
1207
1208
0
    if (error == QAbstractSocket::RemoteHostClosedError
1209
0
        && socks5State == Connected) {
1210
        // clear the read buffer in connect mode so that bytes available returns 0
1211
        // if there already is a read notification pending then this will be processed first
1212
0
        if (!readNotificationPending)
1213
0
            connectData->readBuffer.clear();
1214
0
        emitReadNotification();
1215
0
        data->controlSocket->close();
1216
        // cause a disconnect in the outer socket
1217
0
        emitWriteNotification();
1218
0
    } else if (socks5State == Uninitialized
1219
0
               || socks5State == AuthenticationMethodsSent
1220
0
               || socks5State == Authenticating
1221
0
               || socks5State == RequestMethodSent) {
1222
0
        setErrorState(socks5State == Uninitialized ? ConnectError : ControlSocketError);
1223
0
        data->controlSocket->close();
1224
0
        emitConnectionNotification();
1225
0
    } else {
1226
0
        q_func()->setError(data->controlSocket->error(), data->controlSocket->errorString());
1227
0
        emitReadNotification();
1228
0
        emitWriteNotification();
1229
0
    }
1230
0
}
1231
1232
void QSocks5SocketEnginePrivate::_q_controlSocketDisconnected()
1233
0
{
1234
0
    QSOCKS5_D_DEBUG << "_q_controlSocketDisconnected";
1235
0
}
1236
1237
void QSocks5SocketEnginePrivate::_q_controlSocketStateChanged(QAbstractSocket::SocketState state)
1238
0
{
1239
0
    QSOCKS5_D_DEBUG << "_q_controlSocketStateChanged" << state;
1240
0
}
1241
1242
#ifndef QT_NO_UDPSOCKET
1243
void QSocks5SocketEnginePrivate::_q_udpSocketReadNotification()
1244
0
{
1245
0
    QSOCKS5_D_DEBUG << "_q_udpSocketReadNotification()";
1246
1247
    // check some state stuff
1248
0
    if (!udpData->udpSocket->hasPendingDatagrams()) {
1249
0
        QSOCKS5_D_DEBUG << "false read ??";
1250
0
        return;
1251
0
    }
1252
1253
0
    while (udpData->udpSocket->hasPendingDatagrams()) {
1254
0
        QByteArray sealedBuf(udpData->udpSocket->pendingDatagramSize(), 0);
1255
0
        QSOCKS5_D_DEBUG << "new datagram";
1256
0
        udpData->udpSocket->readDatagram(sealedBuf.data(), sealedBuf.size());
1257
0
        QByteArray inBuf;
1258
0
        if (!data->authenticator->unSeal(sealedBuf, &inBuf)) {
1259
0
            QSOCKS5_D_DEBUG << "failed unsealing datagram discarding";
1260
0
            return;
1261
0
        }
1262
0
        QSOCKS5_DEBUG << dump(inBuf);
1263
0
        int pos = 0;
1264
0
        const char *buf = inBuf.constData();
1265
0
        if (inBuf.size() < 4) {
1266
0
            QSOCKS5_D_DEBUG << "bogus udp data, discarding";
1267
0
            return;
1268
0
        }
1269
0
        QSocks5RevivedDatagram datagram;
1270
0
        if (buf[pos++] != 0 || buf[pos++] != 0) {
1271
0
            QSOCKS5_D_DEBUG << "invalid datagram discarding";
1272
0
            return;
1273
0
        }
1274
0
        if (buf[pos++] != 0) { //### add fragmentation reading support
1275
0
            QSOCKS5_D_DEBUG << "don't support fragmentation yet disgarding";
1276
0
            return;
1277
0
        }
1278
0
        if (qt_socks5_get_host_address_and_port(inBuf, &datagram.address, &datagram.port, &pos) != 1) {
1279
0
            QSOCKS5_D_DEBUG << "failed to get address from datagram disgarding";
1280
0
            return;
1281
0
        }
1282
0
        datagram.data = QByteArray(&buf[pos], inBuf.size() - pos);
1283
0
        udpData->pendingDatagrams.enqueue(datagram);
1284
0
    }
1285
0
    emitReadNotification();
1286
0
}
1287
#endif // QT_NO_UDPSOCKET
1288
1289
bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
1290
0
{
1291
0
    Q_D(QSocks5SocketEngine);
1292
1293
    // when bind we will block until the bind is finished as the info from the proxy server is needed
1294
1295
0
    QHostAddress address;
1296
0
    if (addr.protocol() == QAbstractSocket::AnyIPProtocol)
1297
0
        address = QHostAddress::AnyIPv4; //SOCKS5 doesn't support dual stack, and there isn't any implementation of udp on ipv6 yet
1298
0
    else
1299
0
        address = addr;
1300
1301
0
    if (!d->data) {
1302
0
        if (socketType() == QAbstractSocket::TcpSocket) {
1303
0
            d->initialize(QSocks5SocketEnginePrivate::BindMode);
1304
0
#ifndef QT_NO_UDPSOCKET
1305
0
        } else if (socketType() == QAbstractSocket::UdpSocket) {
1306
0
            d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1307
0
#endif
1308
0
        } else {
1309
            //### something invalid
1310
0
            return false;
1311
0
        }
1312
0
    }
1313
1314
0
#ifndef QT_NO_UDPSOCKET
1315
0
    if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1316
0
        if (!d->udpData->udpSocket->bind(address, port)) {
1317
0
            QSOCKS5_Q_DEBUG << "local udp bind failed";
1318
0
            setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1319
0
            return false;
1320
0
        }
1321
0
        d->localAddress = d->udpData->udpSocket->localAddress();
1322
0
        d->localPort = d->udpData->udpSocket->localPort();
1323
0
    } else
1324
0
#endif
1325
0
    if (d->mode == QSocks5SocketEnginePrivate::BindMode) {
1326
0
        d->localAddress = address;
1327
0
        d->localPort = port;
1328
0
    } else {
1329
        //### something invalid
1330
0
        return false;
1331
0
    }
1332
1333
0
    d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1334
0
    if (!d->waitForConnected(QDeadlineTimer{Socks5BlockingBindTimeout}, nullptr) ||
1335
0
        d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1336
        // waitForConnected sets the error state and closes the socket
1337
0
        QSOCKS5_Q_DEBUG << "waitForConnected to proxy server" << d->data->controlSocket->errorString();
1338
0
        return false;
1339
0
    }
1340
0
    if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) {
1341
0
        setState(QAbstractSocket::BoundState);
1342
0
        return true;
1343
0
#ifndef QT_NO_UDPSOCKET
1344
0
    } else if (d->socks5State == QSocks5SocketEnginePrivate::UdpAssociateSuccess) {
1345
0
        setState(QAbstractSocket::BoundState);
1346
0
        d->udpData->associateAddress = d->localAddress;
1347
0
        d->localAddress = QHostAddress();
1348
0
        d->udpData->associatePort = d->localPort;
1349
0
        d->localPort = 0;
1350
0
        return true;
1351
0
#endif // QT_NO_UDPSOCKET
1352
0
    }
1353
1354
    // binding timed out
1355
0
    setError(QAbstractSocket::SocketTimeoutError,
1356
0
             QLatin1StringView(QT_TRANSLATE_NOOP("QSocks5SocketEngine", "Network operation timed out")));
1357
1358
///###    delete d->udpSocket;
1359
///###    d->udpSocket = 0;
1360
0
    return false;
1361
0
}
1362
1363
1364
bool QSocks5SocketEngine::listen(int backlog)
1365
0
{
1366
0
    Q_D(QSocks5SocketEngine);
1367
0
    Q_UNUSED(backlog);
1368
1369
0
    QSOCKS5_Q_DEBUG << "listen()";
1370
1371
    // check that we are in bound and then go to listening.
1372
0
    if (d->socketState == QAbstractSocket::BoundState) {
1373
0
        d->socketState = QAbstractSocket::ListeningState;
1374
1375
        // check if we already have a connection
1376
0
        if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
1377
0
            d->emitReadNotification();
1378
1379
0
        return true;
1380
0
    }
1381
0
    return false;
1382
0
}
1383
1384
qintptr QSocks5SocketEngine::accept()
1385
0
{
1386
0
    Q_D(QSocks5SocketEngine);
1387
    // check we are listing ---
1388
1389
0
    QSOCKS5_Q_DEBUG << "accept()";
1390
1391
0
    qintptr sd = -1;
1392
0
    switch (d->socks5State) {
1393
0
    case QSocks5SocketEnginePrivate::BindSuccess:
1394
0
        QSOCKS5_Q_DEBUG << "BindSuccess adding" << d->socketDescriptor << "to the bind store";
1395
0
        d->data->controlSocket->disconnect();
1396
0
        d->data->controlSocket->setParent(nullptr);
1397
0
        d->bindData->localAddress = d->localAddress;
1398
0
        d->bindData->localPort = d->localPort;
1399
0
        sd = d->socketDescriptor;
1400
0
        socks5BindStore()->add(sd, d->bindData);
1401
0
        d->data = nullptr;
1402
0
        d->bindData = nullptr;
1403
0
        d->socketDescriptor = 0;
1404
        //### do something about this socket layer ... set it closed and an error about why ...
1405
        // reset state and local port/address
1406
0
        d->socks5State = QSocks5SocketEnginePrivate::Uninitialized; // ..??
1407
0
        d->socketState = QAbstractSocket::UnconnectedState;
1408
0
        break;
1409
0
    case QSocks5SocketEnginePrivate::ControlSocketError:
1410
0
        setError(QAbstractSocket::ProxyProtocolError, "Control socket error"_L1);
1411
0
        break;
1412
0
    default:
1413
0
        setError(QAbstractSocket::ProxyProtocolError, "SOCKS5 proxy error"_L1);
1414
0
        break;
1415
0
    }
1416
0
    return sd;
1417
0
}
1418
1419
void QSocks5SocketEngine::close()
1420
0
{
1421
0
    QSOCKS5_Q_DEBUG << "close()";
1422
0
    Q_D(QSocks5SocketEngine);
1423
0
    if (d->data && d->data->controlSocket) {
1424
0
        if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) {
1425
0
            QDeadlineTimer deadline(100ms);
1426
0
            while (!d->data->controlSocket->bytesToWrite()) {
1427
0
               if (!d->data->controlSocket->waitForBytesWritten(deadline.remainingTime()))
1428
0
                   break;
1429
0
            }
1430
0
        }
1431
0
        d->data->controlSocket->close();
1432
0
    }
1433
0
    d->inboundStreamCount = d->outboundStreamCount = 0;
1434
0
#ifndef QT_NO_UDPSOCKET
1435
0
    if (d->udpData && d->udpData->udpSocket)
1436
0
        d->udpData->udpSocket->close();
1437
0
#endif
1438
0
}
1439
1440
qint64 QSocks5SocketEngine::bytesAvailable() const
1441
0
{
1442
0
    Q_D(const QSocks5SocketEngine);
1443
0
    if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
1444
0
        return d->connectData->readBuffer.size();
1445
0
#ifndef QT_NO_UDPSOCKET
1446
0
    else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode
1447
0
             && !d->udpData->pendingDatagrams.isEmpty())
1448
0
        return d->udpData->pendingDatagrams.constFirst().data.size();
1449
0
#endif
1450
0
    return 0;
1451
0
}
1452
1453
qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
1454
0
{
1455
0
    Q_D(QSocks5SocketEngine);
1456
0
    QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ')';
1457
0
    if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
1458
0
        if (d->connectData->readBuffer.isEmpty()) {
1459
0
            if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1460
                //imitate remote closed
1461
0
                close();
1462
0
                setError(QAbstractSocket::RemoteHostClosedError,
1463
0
                         "Remote host closed connection"_L1);
1464
0
                setState(QAbstractSocket::UnconnectedState);
1465
0
                return -1;
1466
0
            } else {
1467
0
                return 0;       // nothing to be read
1468
0
            }
1469
0
        }
1470
0
        const qint64 copy = d->connectData->readBuffer.read(data, maxlen);
1471
0
        QSOCKS5_DEBUG << "read" << dump(QByteArray(data, copy));
1472
0
        return copy;
1473
0
#ifndef QT_NO_UDPSOCKET
1474
0
    } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1475
0
        return readDatagram(data, maxlen);
1476
0
#endif
1477
0
    }
1478
0
    return 0;
1479
0
}
1480
1481
qint64 QSocks5SocketEngine::write(const char *data, qint64 len)
1482
0
{
1483
0
    Q_D(QSocks5SocketEngine);
1484
0
    QSOCKS5_Q_DEBUG << "write" << dump(QByteArray(data, len));
1485
1486
0
    if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
1487
        // clamp down the amount of bytes to transfer at once
1488
0
        len = qMin<qint64>(len, MaxWriteBufferSize) - d->data->controlSocket->bytesToWrite();
1489
0
        if (len <= 0)
1490
0
            return 0;
1491
1492
0
        QByteArray buf = QByteArray::fromRawData(data, len);
1493
0
        QByteArray sealedBuf;
1494
0
        if (!d->data->authenticator->seal(buf, &sealedBuf)) {
1495
            // ### Handle this error.
1496
0
        }
1497
        // We pass pointer and size because 'sealedBuf' is (most definitely) raw data:
1498
        // QIODevice might have to cache the byte array if the socket cannot write the data.
1499
        // If the _whole_ array needs to be cached then it would simply store a copy of the
1500
        // array whose data will go out of scope and be deallocated before it can be used.
1501
0
        qint64 written = d->data->controlSocket->write(sealedBuf.constData(), sealedBuf.size());
1502
1503
0
        if (written <= 0) {
1504
0
            QSOCKS5_Q_DEBUG << "native write returned" << written;
1505
0
            return written;
1506
0
        }
1507
0
        d->data->controlSocket->waitForBytesWritten(0);
1508
        //NB: returning len rather than written for the OK case, because the "sealing" may increase the length
1509
0
        return len;
1510
0
#ifndef QT_NO_UDPSOCKET
1511
0
    } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1512
        // send to connected address
1513
0
        return writeDatagram(data, len, QIpPacketHeader(d->peerAddress, d->peerPort));
1514
0
#endif
1515
0
    }
1516
    //### set an error ???
1517
0
    return -1;
1518
0
}
1519
1520
#ifndef QT_NO_UDPSOCKET
1521
#ifndef QT_NO_NETWORKINTERFACE
1522
bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &,
1523
                                             const QNetworkInterface &)
1524
0
{
1525
0
    setError(QAbstractSocket::UnsupportedSocketOperationError,
1526
0
             "Operation on socket is not supported"_L1);
1527
0
    return false;
1528
0
}
1529
1530
bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &,
1531
                                              const QNetworkInterface &)
1532
0
{
1533
0
    setError(QAbstractSocket::UnsupportedSocketOperationError,
1534
0
             "Operation on socket is not supported"_L1);
1535
0
    return false;
1536
0
}
1537
1538
1539
QNetworkInterface QSocks5SocketEngine::multicastInterface() const
1540
0
{
1541
0
    return QNetworkInterface();
1542
0
}
1543
1544
bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &)
1545
0
{
1546
0
    setError(QAbstractSocket::UnsupportedSocketOperationError,
1547
0
             "Operation on socket is not supported"_L1);
1548
0
    return false;
1549
0
}
1550
#endif // QT_NO_NETWORKINTERFACE
1551
1552
bool QSocks5SocketEngine::hasPendingDatagrams() const
1553
0
{
1554
0
    Q_D(const QSocks5SocketEngine);
1555
0
    Q_INIT_CHECK(false);
1556
1557
0
    return !d->udpData->pendingDatagrams.isEmpty();
1558
0
}
1559
1560
qint64 QSocks5SocketEngine::pendingDatagramSize() const
1561
0
{
1562
0
    Q_D(const QSocks5SocketEngine);
1563
1564
0
    if (!d->udpData->pendingDatagrams.isEmpty())
1565
0
        return d->udpData->pendingDatagrams.head().data.size();
1566
0
    return 0;
1567
0
}
1568
#endif // QT_NO_UDPSOCKET
1569
1570
qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, PacketHeaderOptions)
1571
0
{
1572
0
#ifndef QT_NO_UDPSOCKET
1573
0
    Q_D(QSocks5SocketEngine);
1574
1575
0
    if (d->udpData->pendingDatagrams.isEmpty())
1576
0
        return 0;
1577
1578
0
    QSocks5RevivedDatagram datagram = d->udpData->pendingDatagrams.dequeue();
1579
0
    int copyLen = qMin<int>(maxlen, datagram.data.size());
1580
0
    memcpy(data, datagram.data.constData(), copyLen);
1581
0
    if (header) {
1582
0
        header->senderAddress = datagram.address;
1583
0
        header->senderPort = datagram.port;
1584
0
    }
1585
0
    return copyLen;
1586
#else
1587
    Q_UNUSED(data);
1588
    Q_UNUSED(maxlen);
1589
    Q_UNUSED(header);
1590
    return -1;
1591
#endif // QT_NO_UDPSOCKET
1592
0
}
1593
1594
qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
1595
0
{
1596
0
#ifndef QT_NO_UDPSOCKET
1597
0
    Q_D(QSocks5SocketEngine);
1598
1599
    // it is possible to send with out first binding with udp, but socks5 requires a bind.
1600
0
    if (!d->data) {
1601
0
        d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1602
        // all udp needs to be bound
1603
0
        if (!bind(QHostAddress("0.0.0.0"_L1), 0)) {
1604
            //### set error
1605
0
            return -1;
1606
0
        }
1607
0
    }
1608
1609
0
    QByteArray outBuf;
1610
0
    outBuf.reserve(270 + len);
1611
0
    outBuf.append(3, '\0');
1612
0
    if (!qt_socks5_set_host_address_and_port(header.destinationAddress, header.destinationPort, &outBuf)) {
1613
0
        QSOCKS5_DEBUG << "error setting address" << header.destinationAddress << " : "
1614
0
                      << header.destinationPort;
1615
        //### set error code ....
1616
0
        return -1;
1617
0
    }
1618
0
    outBuf += QByteArray(data, len);
1619
0
    QSOCKS5_DEBUG << "sending" << dump(outBuf);
1620
0
    QByteArray sealedBuf;
1621
0
    if (!d->data->authenticator->seal(outBuf, &sealedBuf)) {
1622
0
        QSOCKS5_DEBUG << "sealing data failed";
1623
0
        setError(QAbstractSocket::SocketAccessError, d->data->authenticator->errorString());
1624
0
        return -1;
1625
0
    }
1626
0
    if (d->udpData->udpSocket->writeDatagram(sealedBuf, d->udpData->associateAddress, d->udpData->associatePort) != sealedBuf.size()) {
1627
        //### try frgamenting
1628
0
        if (d->udpData->udpSocket->error() == QAbstractSocket::DatagramTooLargeError)
1629
0
            setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1630
        //### else maybe more serious error
1631
0
        return -1;
1632
0
    }
1633
1634
0
    return len;
1635
#else
1636
    Q_UNUSED(data);
1637
    Q_UNUSED(len);
1638
    Q_UNUSED(header);
1639
    return -1;
1640
#endif // QT_NO_UDPSOCKET
1641
0
}
1642
1643
qint64 QSocks5SocketEngine::bytesToWrite() const
1644
0
{
1645
0
    Q_D(const QSocks5SocketEngine);
1646
0
    if (d->data && d->data->controlSocket) {
1647
0
        return d->data->controlSocket->bytesToWrite();
1648
0
    } else {
1649
0
        return 0;
1650
0
    }
1651
0
}
1652
1653
int QSocks5SocketEngine::option(SocketOption option) const
1654
0
{
1655
0
    Q_D(const QSocks5SocketEngine);
1656
0
    if (d->data && d->data->controlSocket) {
1657
        // convert the enum and call the real socket
1658
0
        if (option == QAbstractSocketEngine::LowDelayOption)
1659
0
            return d->data->controlSocket->socketOption(QAbstractSocket::LowDelayOption).toInt();
1660
0
        if (option == QAbstractSocketEngine::KeepAliveOption)
1661
0
            return d->data->controlSocket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
1662
0
    }
1663
0
    return -1;
1664
0
}
1665
1666
bool QSocks5SocketEngine::setOption(SocketOption option, int value)
1667
0
{
1668
0
    Q_D(QSocks5SocketEngine);
1669
0
    if (d->data && d->data->controlSocket) {
1670
        // convert the enum and call the real socket
1671
0
        if (option == QAbstractSocketEngine::LowDelayOption)
1672
0
            d->data->controlSocket->setSocketOption(QAbstractSocket::LowDelayOption, value);
1673
0
        if (option == QAbstractSocketEngine::KeepAliveOption)
1674
0
            d->data->controlSocket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
1675
0
        return true;
1676
0
    }
1677
0
    return false;
1678
0
}
1679
1680
bool QSocks5SocketEnginePrivate::waitForConnected(QDeadlineTimer deadline, bool *timedOut)
1681
0
{
1682
0
    if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1683
0
        return false;
1684
1685
0
    const Socks5State wantedState =
1686
0
        mode == ConnectMode ? Connected :
1687
0
        mode == BindMode ? BindSuccess :
1688
0
        UdpAssociateSuccess;
1689
1690
0
    while (socks5State != wantedState) {
1691
0
        if (!data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
1692
0
            if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1693
0
                return true;
1694
1695
0
            setErrorState(QSocks5SocketEnginePrivate::ControlSocketError);
1696
0
            if (timedOut && data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1697
0
                *timedOut = true;
1698
0
            return false;
1699
0
        }
1700
0
    }
1701
1702
0
    return true;
1703
0
}
1704
1705
bool QSocks5SocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
1706
0
{
1707
0
    Q_D(QSocks5SocketEngine);
1708
0
    QSOCKS5_DEBUG << "waitForRead" << deadline.remainingTimeAsDuration();
1709
1710
0
    d->readNotificationActivated = false;
1711
1712
    // are we connected yet?
1713
0
    if (!d->waitForConnected(deadline, timedOut))
1714
0
        return false;
1715
0
    if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1716
0
        return true;
1717
0
    if (bytesAvailable() && d->readNotificationPending) {
1718
        // We've got some data incoming, but the queued call hasn't been performed yet.
1719
        // The data is where we expect it to be already, so just return true.
1720
0
        return true;
1721
0
    }
1722
1723
    // we're connected
1724
0
    if (d->mode == QSocks5SocketEnginePrivate::ConnectMode ||
1725
0
        d->mode == QSocks5SocketEnginePrivate::BindMode) {
1726
0
        while (!d->readNotificationActivated) {
1727
0
            if (!d->data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
1728
0
                if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1729
0
                    return true;
1730
1731
0
                setError(d->data->controlSocket->error(), d->data->controlSocket->errorString());
1732
0
                if (timedOut && d->data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1733
0
                    *timedOut = true;
1734
0
                return false;
1735
0
            }
1736
0
        }
1737
0
#ifndef QT_NO_UDPSOCKET
1738
0
    } else {
1739
0
        while (!d->readNotificationActivated) {
1740
0
            if (!d->udpData->udpSocket->waitForReadyRead(deadline.remainingTime())) {
1741
0
                setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1742
0
                if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
1743
0
                    *timedOut = true;
1744
0
                return false;
1745
0
            }
1746
0
        }
1747
0
#endif // QT_NO_UDPSOCKET
1748
0
    }
1749
1750
1751
0
    bool ret = d->readNotificationActivated;
1752
0
    d->readNotificationActivated = false;
1753
1754
0
    QSOCKS5_DEBUG << "waitForRead returned" << ret;
1755
0
    return ret;
1756
0
}
1757
1758
1759
bool QSocks5SocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
1760
0
{
1761
0
    Q_D(QSocks5SocketEngine);
1762
0
    QSOCKS5_DEBUG << "waitForWrite" << deadline.remainingTimeAsDuration();
1763
1764
    // are we connected yet?
1765
0
    if (!d->waitForConnected(deadline, timedOut))
1766
0
        return false;
1767
0
    if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1768
0
        return true;
1769
1770
    // we're connected
1771
1772
    // flush any bytes we may still have buffered in the time that we have left
1773
0
    if (d->data->controlSocket->bytesToWrite())
1774
0
        d->data->controlSocket->waitForBytesWritten(deadline.remainingTime());
1775
1776
0
    auto shouldWriteBytes = [&]() {
1777
0
        return d->data->controlSocket->state() == QAbstractSocket::ConnectedState
1778
0
                && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize;
1779
0
    };
1780
1781
0
    qint64 remainingTime = deadline.remainingTime();
1782
0
    for (; remainingTime > 0 && shouldWriteBytes(); remainingTime = deadline.remainingTime())
1783
0
        d->data->controlSocket->waitForBytesWritten(remainingTime);
1784
0
    return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
1785
0
}
1786
1787
bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
1788
                                            bool checkRead, bool checkWrite,
1789
                                            QDeadlineTimer deadline, bool *timedOut)
1790
0
{
1791
0
    Q_UNUSED(checkRead);
1792
0
    if (!checkWrite) {
1793
0
        bool canRead = waitForRead(deadline, timedOut);
1794
0
        if (readyToRead)
1795
0
            *readyToRead = canRead;
1796
0
        return canRead;
1797
0
    }
1798
1799
0
    bool canWrite = waitForWrite(deadline, timedOut);
1800
0
    if (readyToWrite)
1801
0
        *readyToWrite = canWrite;
1802
0
    return canWrite;
1803
0
}
1804
1805
bool QSocks5SocketEngine::isReadNotificationEnabled() const
1806
0
{
1807
0
    Q_D(const QSocks5SocketEngine);
1808
0
    return d->readNotificationEnabled;
1809
0
}
1810
1811
void QSocks5SocketEngine::setReadNotificationEnabled(bool enable)
1812
0
{
1813
0
    Q_D(QSocks5SocketEngine);
1814
1815
0
    QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ')';
1816
1817
0
    bool emitSignal = false;
1818
0
    if (!d->readNotificationEnabled
1819
0
        && enable) {
1820
0
        if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
1821
0
            emitSignal = !d->connectData->readBuffer.isEmpty();
1822
0
#ifndef QT_NO_UDPSOCKET
1823
0
        else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode)
1824
0
            emitSignal = !d->udpData->pendingDatagrams.isEmpty();
1825
0
#endif
1826
0
        else if (d->mode == QSocks5SocketEnginePrivate::BindMode
1827
0
            && d->socketState == QAbstractSocket::ListeningState
1828
0
            && d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
1829
0
            emitSignal = true;
1830
0
    }
1831
1832
0
    d->readNotificationEnabled = enable;
1833
1834
0
    if (emitSignal)
1835
0
        d->emitReadNotification();
1836
0
}
1837
1838
bool QSocks5SocketEngine::isWriteNotificationEnabled() const
1839
0
{
1840
0
    Q_D(const QSocks5SocketEngine);
1841
0
    return d->writeNotificationEnabled;
1842
0
}
1843
1844
void QSocks5SocketEngine::setWriteNotificationEnabled(bool enable)
1845
0
{
1846
0
    Q_D(QSocks5SocketEngine);
1847
0
    d->writeNotificationEnabled = enable;
1848
0
    if (enable && d->socketState == QAbstractSocket::ConnectedState) {
1849
0
        if (d->mode == QSocks5SocketEnginePrivate::ConnectMode && d->data->controlSocket->bytesToWrite())
1850
0
            return; // will be emitted as a result of bytes written
1851
0
       d->emitWriteNotification();
1852
0
       d->writeNotificationActivated = false;
1853
0
    }
1854
0
}
1855
1856
bool QSocks5SocketEngine::isExceptionNotificationEnabled() const
1857
0
{
1858
0
    Q_D(const QSocks5SocketEngine);
1859
0
    return d->exceptNotificationEnabled;
1860
0
}
1861
1862
void QSocks5SocketEngine::setExceptionNotificationEnabled(bool enable)
1863
0
{
1864
0
    Q_D(QSocks5SocketEngine);
1865
0
    d->exceptNotificationEnabled = enable;
1866
0
}
1867
1868
QAbstractSocketEngine *
1869
QSocks5SocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
1870
                                               const QNetworkProxy &proxy, QObject *parent)
1871
0
{
1872
0
    Q_UNUSED(socketType);
1873
1874
    // proxy type must have been resolved by now
1875
0
    if (proxy.type() != QNetworkProxy::Socks5Proxy) {
1876
0
        QSOCKS5_DEBUG << "not proxying";
1877
0
        return nullptr;
1878
0
    }
1879
0
    auto engine = std::make_unique<QSocks5SocketEngine>(parent);
1880
0
    engine->setProxy(proxy);
1881
0
    return engine.release();
1882
0
}
1883
1884
QAbstractSocketEngine *QSocks5SocketEngineHandler::createSocketEngine(qintptr socketDescriptor, QObject *parent)
1885
0
{
1886
0
    QSOCKS5_DEBUG << "createSocketEngine" << socketDescriptor;
1887
0
    if (socks5BindStore()->contains(socketDescriptor)) {
1888
0
        QSOCKS5_DEBUG << "bind store contains" << socketDescriptor;
1889
0
        return new QSocks5SocketEngine(parent);
1890
0
    }
1891
0
    return nullptr;
1892
0
}
1893
1894
QT_END_NAMESPACE
1895
1896
#include "moc_qsocks5socketengine_p.cpp"