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

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