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

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