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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/network/socket/qsocks5socketengine.cpp