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