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 (bindData->controlSocket->thread() != QThread::currentThread()) {
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 // check if there needs to be a new zero read notification
900 if (data && data->controlSocket->state() == QAbstractSocket::UnconnectedState
901 && data->controlSocket->error() == QAbstractSocket::RemoteHostClosedError) {
902 connectData->readBuffer.clear();
904 }
905 }
906}
907
909{
910 Q_Q(QSocks5SocketEngine);
913 QSOCKS5_D_DEBUG << "queueing readNotification";
915 QMetaObject::invokeMethod(q, "_q_emitPendingReadNotification", Qt::QueuedConnection);
916 }
917}
918
920{
922 Q_Q(QSocks5SocketEngine);
924 QSOCKS5_D_DEBUG << "emitting writeNotification";
925 q->writeNotification();
926 }
927}
928
930{
931 Q_Q(QSocks5SocketEngine);
934 QSOCKS5_D_DEBUG << "queueing writeNotification";
936 QMetaObject::invokeMethod(q, "_q_emitPendingWriteNotification", Qt::QueuedConnection);
937 }
938}
939
941{
943 Q_Q(QSocks5SocketEngine);
944 QSOCKS5_D_DEBUG << "emitting connectionNotification";
945 q->connectionNotification();
946}
947
949{
950 Q_Q(QSocks5SocketEngine);
951 QSOCKS5_D_DEBUG << "queueing connectionNotification";
953 QMetaObject::invokeMethod(q, "_q_emitPendingConnectionNotification", Qt::QueuedConnection);
954}
955
956QSocks5SocketEngine::QSocks5SocketEngine(QObject *parent)
957:QAbstractSocketEngine(*new QSocks5SocketEnginePrivate(), parent)
958{
959}
960
961QSocks5SocketEngine::~QSocks5SocketEngine()
962{
963 Q_D(QSocks5SocketEngine);
964
965 if (d->data) {
966 delete d->data->authenticator;
967 delete d->data->controlSocket;
968 }
969 if (d->connectData)
970 delete d->connectData;
971#ifndef QT_NO_UDPSOCKET
972 if (d->udpData) {
973 delete d->udpData->udpSocket;
974 delete d->udpData;
975 }
976#endif
977 if (d->bindData)
978 delete d->bindData;
979}
980
981static int nextDescriptor()
982{
983 Q_CONSTINIT static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
984 return 1 + counter.fetchAndAddRelaxed(1);
985}
986
987bool QSocks5SocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
988{
989 Q_D(QSocks5SocketEngine);
990
991 d->socketDescriptor = nextDescriptor();
992
993 d->socketType = type;
994 d->socketProtocol = protocol;
995
996 return true;
997}
998
999bool QSocks5SocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState)
1000{
1001 Q_D(QSocks5SocketEngine);
1002
1003 QSOCKS5_Q_DEBUG << "initialize" << socketDescriptor;
1004
1005 // this is only valid for the other side of a bind, nothing else is supported
1006
1007 if (socketState != QAbstractSocket::ConnectedState) {
1008 //### must be connected state ???
1009 return false;
1010 }
1011
1012 QSocks5BindData *bindData = socks5BindStore()->retrieve(socketDescriptor);
1013 if (bindData) {
1014
1015 d->socketState = QAbstractSocket::ConnectedState;
1016 d->socketType = QAbstractSocket::TcpSocket;
1017 d->connectData = new QSocks5ConnectData;
1018 d->data = d->connectData;
1019 d->mode = QSocks5SocketEnginePrivate::ConnectMode;
1020 d->data->controlSocket = bindData->controlSocket;
1021 bindData->controlSocket = nullptr;
1022 d->data->controlSocket->setParent(this);
1023 d->socketProtocol = d->data->controlSocket->localAddress().protocol();
1024 d->data->authenticator = bindData->authenticator;
1025 bindData->authenticator = nullptr;
1026 d->localPort = bindData->localPort;
1027 d->localAddress = bindData->localAddress;
1028 d->peerPort = bindData->peerPort;
1029 d->peerAddress = bindData->peerAddress;
1030 d->inboundStreamCount = d->outboundStreamCount = 1;
1031 delete bindData;
1032
1033 QObject::connect(d->data->controlSocket, SIGNAL(connected()), this, SLOT(_q_controlSocketConnected()),
1034 Qt::DirectConnection);
1035 QObject::connect(d->data->controlSocket, SIGNAL(readyRead()), this, SLOT(_q_controlSocketReadNotification()),
1036 Qt::DirectConnection);
1037 QObject::connect(d->data->controlSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_controlSocketBytesWritten()),
1038 Qt::DirectConnection);
1039 QObject::connect(d->data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketErrorOccurred(QAbstractSocket::SocketError)),
1040 Qt::DirectConnection);
1041 QObject::connect(d->data->controlSocket, SIGNAL(disconnected()), this, SLOT(_q_controlSocketDisconnected()),
1042 Qt::DirectConnection);
1043 QObject::connect(d->data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
1044 this, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
1045 Qt::DirectConnection);
1046
1047 d->socks5State = QSocks5SocketEnginePrivate::Connected;
1048
1049 if (d->data->controlSocket->bytesAvailable() != 0)
1050 d->_q_controlSocketReadNotification();
1051 return true;
1052 }
1053 return false;
1054}
1055
1056void QSocks5SocketEngine::setProxy(const QNetworkProxy &networkProxy)
1057{
1058 Q_D(QSocks5SocketEngine);
1059 d->proxyInfo = networkProxy;
1060}
1061
1062qintptr QSocks5SocketEngine::socketDescriptor() const
1063{
1064 Q_D(const QSocks5SocketEngine);
1065 return d->socketDescriptor;
1066}
1067
1068bool QSocks5SocketEngine::isValid() const
1069{
1070 Q_D(const QSocks5SocketEngine);
1071 return d->socketType != QAbstractSocket::UnknownSocketType
1072 && d->socks5State != QSocks5SocketEnginePrivate::SocksError
1073 && (d->socketError == QAbstractSocket::UnknownSocketError
1074 || d->socketError == QAbstractSocket::SocketTimeoutError
1075 || d->socketError == QAbstractSocket::UnfinishedSocketOperationError);
1076}
1077
1078bool QSocks5SocketEngine::connectInternal()
1079{
1080 Q_D(QSocks5SocketEngine);
1081
1082 if (!d->data) {
1083 if (socketType() == QAbstractSocket::TcpSocket) {
1084 d->initialize(QSocks5SocketEnginePrivate::ConnectMode);
1085#ifndef QT_NO_UDPSOCKET
1086 } else if (socketType() == QAbstractSocket::UdpSocket) {
1087 d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1088 // all udp needs to be bound
1089 if (!bind(QHostAddress("0.0.0.0"_L1), 0))
1090 return false;
1091
1092 setState(QAbstractSocket::ConnectedState);
1093 return true;
1094#endif
1095 } else {
1096 qFatal("QSocks5SocketEngine::connectToHost: in QTcpServer mode");
1097 return false;
1098 }
1099 }
1100
1101 if (d->socketState != QAbstractSocket::ConnectingState) {
1102 if (d->socks5State == QSocks5SocketEnginePrivate::Uninitialized
1103 // We may have new auth credentials since an earlier failure:
1104 || d->socks5State == QSocks5SocketEnginePrivate::AuthenticatingError) {
1105 setState(QAbstractSocket::ConnectingState);
1106 //limit buffer in internal socket, data is buffered in the external socket under application control
1107 d->data->controlSocket->setReadBufferSize(65536);
1108 }
1109
1110 d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1111 }
1112
1113 return false;
1114}
1115
1116bool QSocks5SocketEngine::connectToHost(const QHostAddress &address, quint16 port)
1117{
1118 Q_D(QSocks5SocketEngine);
1119 QSOCKS5_DEBUG << "connectToHost" << address << ':' << port;
1120
1121 setPeerAddress(address);
1122 setPeerPort(port);
1123 d->peerName.clear();
1124
1125 return connectInternal();
1126}
1127
1128bool QSocks5SocketEngine::connectToHostByName(const QString &hostname, quint16 port)
1129{
1130 Q_D(QSocks5SocketEngine);
1131
1132 setPeerAddress(QHostAddress());
1133 setPeerPort(port);
1134 d->peerName = hostname;
1135
1136 return connectInternal();
1137}
1138
1140{
1141 QSOCKS5_DEBUG << "_q_controlSocketConnected";
1142 QByteArray buf(3, 0);
1143 buf[0] = S5_VERSION_5;
1144 buf[1] = 0x01;
1146 data->controlSocket->write(buf);
1148}
1149
1151{
1152 QSOCKS5_D_DEBUG << "_q_controlSocketReadNotification socks5state" << s5StateToString(socks5State)
1153 << "bytes available" << data->controlSocket->bytesAvailable();
1154
1155 if (data->controlSocket->bytesAvailable() == 0) {
1156 QSOCKS5_D_DEBUG << "########## bogus read why do we get these ... on windows only";
1157 return;
1158 }
1159
1160 switch (socks5State) {
1163 break;
1164 case Authenticating:
1166 break;
1167 case RequestMethodSent:
1169 if (socks5State == Connected && data->controlSocket->bytesAvailable())
1171 break;
1172 case Connected: {
1173 QByteArray buf;
1174 if (!data->authenticator->unSeal(data->controlSocket, &buf)) {
1175 // qDebug("unseal error maybe need to wait for more data");
1176 }
1177 if (buf.size()) {
1178 QSOCKS5_DEBUG << dump(buf);
1179 connectData->readBuffer.append(std::move(buf));
1181 }
1182 break;
1183 }
1184 case BindSuccess:
1185 // only get here if command is bind
1186 if (mode == BindMode) {
1188 break;
1189 }
1190
1191 Q_FALLTHROUGH();
1192 default:
1193 qWarning("QSocks5SocketEnginePrivate::_q_controlSocketReadNotification: "
1194 "Unexpectedly received data while in state=%d and mode=%d",
1195 socks5State, mode);
1196 break;
1197 };
1198}
1199
1201{
1202 QSOCKS5_DEBUG << "_q_controlSocketBytesWritten";
1203
1204 if (socks5State != Connected
1205 || (mode == ConnectMode
1206 && data->controlSocket->bytesToWrite()))
1207 return;
1208 if (data->controlSocket->bytesToWrite() < MaxWriteBufferSize) {
1211 }
1212}
1213
1214void QSocks5SocketEnginePrivate::_q_controlSocketErrorOccurred(QAbstractSocket::SocketError error)
1215{
1216 QSOCKS5_D_DEBUG << "controlSocketError" << error << data->controlSocket->errorString();
1217
1218 if (error == QAbstractSocket::SocketTimeoutError)
1219 return; // ignore this error -- comes from the waitFor* functions
1220
1221 if (error == QAbstractSocket::RemoteHostClosedError
1222 && socks5State == Connected) {
1223 // clear the read buffer in connect mode so that bytes available returns 0
1224 // if there already is a read notification pending then this will be processed first
1226 connectData->readBuffer.clear();
1228 data->controlSocket->close();
1229 // cause a disconnect in the outer socket
1231 } else if (socks5State == Uninitialized
1236 data->controlSocket->close();
1238 } else {
1239 q_func()->setError(data->controlSocket->error(), data->controlSocket->errorString());
1242 }
1243}
1244
1246{
1247 QSOCKS5_D_DEBUG << "_q_controlSocketDisconnected";
1248}
1249
1250void QSocks5SocketEnginePrivate::_q_controlSocketStateChanged(QAbstractSocket::SocketState state)
1251{
1252 QSOCKS5_D_DEBUG << "_q_controlSocketStateChanged" << state;
1253}
1254
1255#ifndef QT_NO_UDPSOCKET
1257{
1258 QSOCKS5_D_DEBUG << "_q_udpSocketReadNotification()";
1259
1260 // check some state stuff
1261 if (!udpData->udpSocket->hasPendingDatagrams()) {
1262 QSOCKS5_D_DEBUG << "false read ??";
1263 return;
1264 }
1265
1266 while (udpData->udpSocket->hasPendingDatagrams()) {
1267 QByteArray sealedBuf(udpData->udpSocket->pendingDatagramSize(), 0);
1268 QSOCKS5_D_DEBUG << "new datagram";
1269 udpData->udpSocket->readDatagram(sealedBuf.data(), sealedBuf.size());
1270 QByteArray inBuf;
1271 if (!data->authenticator->unSeal(sealedBuf, &inBuf)) {
1272 QSOCKS5_D_DEBUG << "failed unsealing datagram discarding";
1273 return;
1274 }
1275 QSOCKS5_DEBUG << dump(inBuf);
1276 int pos = 0;
1277 const char *buf = inBuf.constData();
1278 if (inBuf.size() < 4) {
1279 QSOCKS5_D_DEBUG << "bogus udp data, discarding";
1280 return;
1281 }
1282 QSocks5RevivedDatagram datagram;
1283 if (buf[pos++] != 0 || buf[pos++] != 0) {
1284 QSOCKS5_D_DEBUG << "invalid datagram discarding";
1285 return;
1286 }
1287 if (buf[pos++] != 0) { //### add fragmentation reading support
1288 QSOCKS5_D_DEBUG << "don't support fragmentation yet disgarding";
1289 return;
1290 }
1291 if (qt_socks5_get_host_address_and_port(inBuf, &datagram.address, &datagram.port, &pos) != 1) {
1292 QSOCKS5_D_DEBUG << "failed to get address from datagram disgarding";
1293 return;
1294 }
1295 datagram.data = QByteArray(&buf[pos], inBuf.size() - pos);
1296 udpData->pendingDatagrams.enqueue(datagram);
1297 }
1299}
1300#endif // QT_NO_UDPSOCKET
1301
1302bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
1303{
1304 Q_D(QSocks5SocketEngine);
1305
1306 // when bind we will block until the bind is finished as the info from the proxy server is needed
1307
1308 QHostAddress address;
1309 if (addr.protocol() == QAbstractSocket::AnyIPProtocol)
1310 address = QHostAddress::AnyIPv4; //SOCKS5 doesn't support dual stack, and there isn't any implementation of udp on ipv6 yet
1311 else
1312 address = addr;
1313
1314 if (!d->data) {
1315 if (socketType() == QAbstractSocket::TcpSocket) {
1316 d->initialize(QSocks5SocketEnginePrivate::BindMode);
1317#ifndef QT_NO_UDPSOCKET
1318 } else if (socketType() == QAbstractSocket::UdpSocket) {
1319 d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1320#endif
1321 } else {
1322 //### something invalid
1323 return false;
1324 }
1325 }
1326
1327#ifndef QT_NO_UDPSOCKET
1328 if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1329 if (!d->udpData->udpSocket->bind(address, port)) {
1330 QSOCKS5_Q_DEBUG << "local udp bind failed";
1331 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1332 return false;
1333 }
1334 d->localAddress = d->udpData->udpSocket->localAddress();
1335 d->localPort = d->udpData->udpSocket->localPort();
1336 } else
1337#endif
1338 if (d->mode == QSocks5SocketEnginePrivate::BindMode) {
1339 d->localAddress = address;
1340 d->localPort = port;
1341 } else {
1342 //### something invalid
1343 return false;
1344 }
1345
1346 d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1347 if (!d->waitForConnected(QDeadlineTimer{Socks5BlockingBindTimeout}, nullptr) ||
1348 d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1349 // waitForConnected sets the error state and closes the socket
1350 QSOCKS5_Q_DEBUG << "waitForConnected to proxy server" << d->data->controlSocket->errorString();
1351 return false;
1352 }
1353 if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) {
1354 setState(QAbstractSocket::BoundState);
1355 return true;
1356#ifndef QT_NO_UDPSOCKET
1357 } else if (d->socks5State == QSocks5SocketEnginePrivate::UdpAssociateSuccess) {
1358 setState(QAbstractSocket::BoundState);
1359 d->udpData->associateAddress = d->localAddress;
1360 d->localAddress = QHostAddress();
1361 d->udpData->associatePort = d->localPort;
1362 d->localPort = 0;
1363 return true;
1364#endif // QT_NO_UDPSOCKET
1365 }
1366
1367 // binding timed out
1368 setError(QAbstractSocket::SocketTimeoutError,
1369 QLatin1StringView(QT_TRANSLATE_NOOP("QSocks5SocketEngine", "Network operation timed out")));
1370
1371///### delete d->udpSocket;
1372///### d->udpSocket = 0;
1373 return false;
1374}
1375
1376
1377bool QSocks5SocketEngine::listen(int backlog)
1378{
1379 Q_D(QSocks5SocketEngine);
1380 Q_UNUSED(backlog);
1381
1382 QSOCKS5_Q_DEBUG << "listen()";
1383
1384 // check that we are in bound and then go to listening.
1385 if (d->socketState == QAbstractSocket::BoundState) {
1386 d->socketState = QAbstractSocket::ListeningState;
1387
1388 // check if we already have a connection
1389 if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
1390 d->emitReadNotification();
1391
1392 return true;
1393 }
1394 return false;
1395}
1396
1397qintptr QSocks5SocketEngine::accept()
1398{
1399 Q_D(QSocks5SocketEngine);
1400 // check we are listing ---
1401
1402 QSOCKS5_Q_DEBUG << "accept()";
1403
1404 qintptr sd = -1;
1405 switch (d->socks5State) {
1406 case QSocks5SocketEnginePrivate::BindSuccess:
1407 QSOCKS5_Q_DEBUG << "BindSuccess adding" << d->socketDescriptor << "to the bind store";
1408 d->data->controlSocket->disconnect();
1409 d->data->controlSocket->setParent(nullptr);
1410 d->bindData->localAddress = d->localAddress;
1411 d->bindData->localPort = d->localPort;
1412 sd = d->socketDescriptor;
1413 socks5BindStore()->add(sd, d->bindData);
1414 d->data = nullptr;
1415 d->bindData = nullptr;
1416 d->socketDescriptor = 0;
1417 //### do something about this socket layer ... set it closed and an error about why ...
1418 // reset state and local port/address
1419 d->socks5State = QSocks5SocketEnginePrivate::Uninitialized; // ..??
1420 d->socketState = QAbstractSocket::UnconnectedState;
1421 break;
1422 case QSocks5SocketEnginePrivate::ControlSocketError:
1423 setError(QAbstractSocket::ProxyProtocolError, "Control socket error"_L1);
1424 break;
1425 default:
1426 setError(QAbstractSocket::ProxyProtocolError, "SOCKS5 proxy error"_L1);
1427 break;
1428 }
1429 return sd;
1430}
1431
1432void QSocks5SocketEngine::close()
1433{
1434 QSOCKS5_Q_DEBUG << "close()";
1435 Q_D(QSocks5SocketEngine);
1436 if (d->data && d->data->controlSocket) {
1437 if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) {
1438 QDeadlineTimer deadline(100ms);
1439 while (!d->data->controlSocket->bytesToWrite()) {
1440 if (!d->data->controlSocket->waitForBytesWritten(deadline.remainingTime()))
1441 break;
1442 }
1443 }
1444 d->data->controlSocket->close();
1445 }
1446 d->inboundStreamCount = d->outboundStreamCount = 0;
1447#ifndef QT_NO_UDPSOCKET
1448 if (d->udpData && d->udpData->udpSocket)
1449 d->udpData->udpSocket->close();
1450#endif
1451}
1452
1453qint64 QSocks5SocketEngine::bytesAvailable() const
1454{
1455 Q_D(const QSocks5SocketEngine);
1456 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
1457 return d->connectData->readBuffer.size();
1458#ifndef QT_NO_UDPSOCKET
1459 else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode
1460 && !d->udpData->pendingDatagrams.isEmpty())
1461 return d->udpData->pendingDatagrams.constFirst().data.size();
1462#endif
1463 return 0;
1464}
1465
1466qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
1467{
1468 Q_D(QSocks5SocketEngine);
1469 QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ')';
1470 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
1471 if (d->connectData->readBuffer.isEmpty()) {
1472 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1473 //imitate remote closed
1474 close();
1475 setError(QAbstractSocket::RemoteHostClosedError,
1476 "Remote host closed connection"_L1);
1477 setState(QAbstractSocket::UnconnectedState);
1478 return -1;
1479 } else {
1480 return 0; // nothing to be read
1481 }
1482 }
1483 const qint64 copy = d->connectData->readBuffer.read(data, maxlen);
1484 QSOCKS5_DEBUG << "read" << dump(QByteArray(data, copy));
1485 return copy;
1486#ifndef QT_NO_UDPSOCKET
1487 } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1488 return readDatagram(data, maxlen);
1489#endif
1490 }
1491 return 0;
1492}
1493
1494qint64 QSocks5SocketEngine::write(const char *data, qint64 len)
1495{
1496 Q_D(QSocks5SocketEngine);
1497 QSOCKS5_Q_DEBUG << "write" << dump(QByteArray(data, len));
1498
1499 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
1500 // clamp down the amount of bytes to transfer at once
1501 len = qMin<qint64>(len, MaxWriteBufferSize) - d->data->controlSocket->bytesToWrite();
1502 if (len <= 0)
1503 return 0;
1504
1505 QByteArray buf = QByteArray::fromRawData(data, len);
1506 QByteArray sealedBuf;
1507 if (!d->data->authenticator->seal(buf, &sealedBuf)) {
1508 // ### Handle this error.
1509 }
1510 // We pass pointer and size because 'sealedBuf' is (most definitely) raw data:
1511 // QIODevice might have to cache the byte array if the socket cannot write the data.
1512 // If the _whole_ array needs to be cached then it would simply store a copy of the
1513 // array whose data will go out of scope and be deallocated before it can be used.
1514 qint64 written = d->data->controlSocket->write(sealedBuf.constData(), sealedBuf.size());
1515
1516 if (written <= 0) {
1517 QSOCKS5_Q_DEBUG << "native write returned" << written;
1518 return written;
1519 }
1520 d->data->controlSocket->waitForBytesWritten(0);
1521 //NB: returning len rather than written for the OK case, because the "sealing" may increase the length
1522 return len;
1523#ifndef QT_NO_UDPSOCKET
1524 } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1525 // send to connected address
1526 return writeDatagram(data, len, QIpPacketHeader(d->peerAddress, d->peerPort));
1527#endif
1528 }
1529 //### set an error ???
1530 return -1;
1531}
1532
1533#ifndef QT_NO_UDPSOCKET
1534#ifndef QT_NO_NETWORKINTERFACE
1535bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &,
1536 const QNetworkInterface &)
1537{
1538 setError(QAbstractSocket::UnsupportedSocketOperationError,
1539 "Operation on socket is not supported"_L1);
1540 return false;
1541}
1542
1543bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &,
1544 const QNetworkInterface &)
1545{
1546 setError(QAbstractSocket::UnsupportedSocketOperationError,
1547 "Operation on socket is not supported"_L1);
1548 return false;
1549}
1550
1551
1552QNetworkInterface QSocks5SocketEngine::multicastInterface() const
1553{
1554 return QNetworkInterface();
1555}
1556
1557bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &)
1558{
1559 setError(QAbstractSocket::UnsupportedSocketOperationError,
1560 "Operation on socket is not supported"_L1);
1561 return false;
1562}
1563#endif // QT_NO_NETWORKINTERFACE
1564
1565bool QSocks5SocketEngine::hasPendingDatagrams() const
1566{
1567 Q_D(const QSocks5SocketEngine);
1568 Q_INIT_CHECK(false);
1569
1570 return !d->udpData->pendingDatagrams.isEmpty();
1571}
1572
1573qint64 QSocks5SocketEngine::pendingDatagramSize() const
1574{
1575 Q_D(const QSocks5SocketEngine);
1576
1577 if (!d->udpData->pendingDatagrams.isEmpty())
1578 return d->udpData->pendingDatagrams.head().data.size();
1579 return 0;
1580}
1581#endif // QT_NO_UDPSOCKET
1582
1583qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, PacketHeaderOptions)
1584{
1585#ifndef QT_NO_UDPSOCKET
1586 Q_D(QSocks5SocketEngine);
1587
1588 if (d->udpData->pendingDatagrams.isEmpty())
1589 return 0;
1590
1591 QSocks5RevivedDatagram datagram = d->udpData->pendingDatagrams.dequeue();
1592 int copyLen = qMin<int>(maxlen, datagram.data.size());
1593 memcpy(data, datagram.data.constData(), copyLen);
1594 if (header) {
1595 header->senderAddress = datagram.address;
1596 header->senderPort = datagram.port;
1597 }
1598 return copyLen;
1599#else
1600 Q_UNUSED(data);
1601 Q_UNUSED(maxlen);
1602 Q_UNUSED(header);
1603 return -1;
1604#endif // QT_NO_UDPSOCKET
1605}
1606
1607qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
1608{
1609#ifndef QT_NO_UDPSOCKET
1610 Q_D(QSocks5SocketEngine);
1611
1612 // it is possible to send with out first binding with udp, but socks5 requires a bind.
1613 if (!d->data) {
1614 d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1615 // all udp needs to be bound
1616 if (!bind(QHostAddress("0.0.0.0"_L1), 0)) {
1617 //### set error
1618 return -1;
1619 }
1620 }
1621
1622 QByteArray outBuf;
1623 outBuf.reserve(270 + len);
1624 outBuf.append(3, '\0');
1625 if (!qt_socks5_set_host_address_and_port(header.destinationAddress, header.destinationPort, &outBuf)) {
1626 QSOCKS5_DEBUG << "error setting address" << header.destinationAddress << " : "
1627 << header.destinationPort;
1628 //### set error code ....
1629 return -1;
1630 }
1631 outBuf += QByteArray(data, len);
1632 QSOCKS5_DEBUG << "sending" << dump(outBuf);
1633 QByteArray sealedBuf;
1634 if (!d->data->authenticator->seal(outBuf, &sealedBuf)) {
1635 QSOCKS5_DEBUG << "sealing data failed";
1636 setError(QAbstractSocket::SocketAccessError, d->data->authenticator->errorString());
1637 return -1;
1638 }
1639 if (d->udpData->udpSocket->writeDatagram(sealedBuf, d->udpData->associateAddress, d->udpData->associatePort) != sealedBuf.size()) {
1640 //### try frgamenting
1641 if (d->udpData->udpSocket->error() == QAbstractSocket::DatagramTooLargeError)
1642 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1643 //### else maybe more serious error
1644 return -1;
1645 }
1646
1647 return len;
1648#else
1649 Q_UNUSED(data);
1650 Q_UNUSED(len);
1651 Q_UNUSED(header);
1652 return -1;
1653#endif // QT_NO_UDPSOCKET
1654}
1655
1656qint64 QSocks5SocketEngine::bytesToWrite() const
1657{
1658 Q_D(const QSocks5SocketEngine);
1659 if (d->data && d->data->controlSocket) {
1660 return d->data->controlSocket->bytesToWrite();
1661 } else {
1662 return 0;
1663 }
1664}
1665
1666int QSocks5SocketEngine::option(SocketOption option) const
1667{
1668 Q_D(const QSocks5SocketEngine);
1669 if (d->data && d->data->controlSocket) {
1670 // convert the enum and call the real socket
1671 if (option == QAbstractSocketEngine::LowDelayOption)
1672 return d->data->controlSocket->socketOption(QAbstractSocket::LowDelayOption).toInt();
1673 if (option == QAbstractSocketEngine::KeepAliveOption)
1674 return d->data->controlSocket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
1675 }
1676 return -1;
1677}
1678
1679bool QSocks5SocketEngine::setOption(SocketOption option, int value)
1680{
1681 Q_D(QSocks5SocketEngine);
1682 if (d->data && d->data->controlSocket) {
1683 // convert the enum and call the real socket
1684 if (option == QAbstractSocketEngine::LowDelayOption)
1685 d->data->controlSocket->setSocketOption(QAbstractSocket::LowDelayOption, value);
1686 if (option == QAbstractSocketEngine::KeepAliveOption)
1687 d->data->controlSocket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
1688 return true;
1689 }
1690 return false;
1691}
1692
1693bool QSocks5SocketEnginePrivate::waitForConnected(QDeadlineTimer deadline, bool *timedOut)
1694{
1695 if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1696 return false;
1697
1698 const Socks5State wantedState =
1702
1703 while (socks5State != wantedState) {
1704 if (!data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
1705 if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1706 return true;
1707
1709 if (timedOut && data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1710 *timedOut = true;
1711 return false;
1712 }
1713 }
1714
1715 return true;
1716}
1717
1718bool QSocks5SocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
1719{
1720 Q_D(QSocks5SocketEngine);
1721 QSOCKS5_DEBUG << "waitForRead" << deadline.remainingTimeAsDuration();
1722
1723 d->readNotificationActivated = false;
1724
1725 // are we connected yet?
1726 if (!d->waitForConnected(deadline, timedOut))
1727 return false;
1728 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1729 return true;
1730 if (bytesAvailable() && d->readNotificationPending) {
1731 // We've got some data incoming, but the queued call hasn't been performed yet.
1732 // The data is where we expect it to be already, so just return true.
1733 return true;
1734 }
1735
1736 // we're connected
1737 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode ||
1738 d->mode == QSocks5SocketEnginePrivate::BindMode) {
1739 while (!d->readNotificationActivated) {
1740 if (!d->data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
1741 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1742 return true;
1743
1744 setError(d->data->controlSocket->error(), d->data->controlSocket->errorString());
1745 if (timedOut && d->data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1746 *timedOut = true;
1747 return false;
1748 }
1749 }
1750#ifndef QT_NO_UDPSOCKET
1751 } else {
1752 while (!d->readNotificationActivated) {
1753 if (!d->udpData->udpSocket->waitForReadyRead(deadline.remainingTime())) {
1754 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1755 if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
1756 *timedOut = true;
1757 return false;
1758 }
1759 }
1760#endif // QT_NO_UDPSOCKET
1761 }
1762
1763
1764 bool ret = d->readNotificationActivated;
1765 d->readNotificationActivated = false;
1766
1767 QSOCKS5_DEBUG << "waitForRead returned" << ret;
1768 return ret;
1769}
1770
1771
1772bool QSocks5SocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
1773{
1774 Q_D(QSocks5SocketEngine);
1775 QSOCKS5_DEBUG << "waitForWrite" << deadline.remainingTimeAsDuration();
1776
1777 // are we connected yet?
1778 if (!d->waitForConnected(deadline, timedOut))
1779 return false;
1780 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1781 return true;
1782
1783 // we're connected
1784
1785 // flush any bytes we may still have buffered in the time that we have left
1786 if (d->data->controlSocket->bytesToWrite())
1787 d->data->controlSocket->waitForBytesWritten(deadline.remainingTime());
1788
1789 auto shouldWriteBytes = [&]() {
1790 return d->data->controlSocket->state() == QAbstractSocket::ConnectedState
1791 && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize;
1792 };
1793
1794 qint64 remainingTime = deadline.remainingTime();
1795 for (; remainingTime > 0 && shouldWriteBytes(); remainingTime = deadline.remainingTime())
1796 d->data->controlSocket->waitForBytesWritten(remainingTime);
1797 return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
1798}
1799
1800bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
1801 bool checkRead, bool checkWrite,
1802 QDeadlineTimer deadline, bool *timedOut)
1803{
1804 Q_UNUSED(checkRead);
1805 if (!checkWrite) {
1806 bool canRead = waitForRead(deadline, timedOut);
1807 if (readyToRead)
1808 *readyToRead = canRead;
1809 return canRead;
1810 }
1811
1812 bool canWrite = waitForWrite(deadline, timedOut);
1813 if (readyToWrite)
1814 *readyToWrite = canWrite;
1815 return canWrite;
1816}
1817
1818bool QSocks5SocketEngine::isReadNotificationEnabled() const
1819{
1820 Q_D(const QSocks5SocketEngine);
1821 return d->readNotificationEnabled;
1822}
1823
1824void QSocks5SocketEngine::setReadNotificationEnabled(bool enable)
1825{
1826 Q_D(QSocks5SocketEngine);
1827
1828 QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ')';
1829
1830 bool emitSignal = false;
1831 if (!d->readNotificationEnabled
1832 && enable) {
1833 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
1834 emitSignal = !d->connectData->readBuffer.isEmpty();
1835#ifndef QT_NO_UDPSOCKET
1836 else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode)
1837 emitSignal = !d->udpData->pendingDatagrams.isEmpty();
1838#endif
1839 else if (d->mode == QSocks5SocketEnginePrivate::BindMode
1840 && d->socketState == QAbstractSocket::ListeningState
1841 && d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
1842 emitSignal = true;
1843 }
1844
1845 d->readNotificationEnabled = enable;
1846
1847 if (emitSignal)
1848 d->emitReadNotification();
1849}
1850
1851bool QSocks5SocketEngine::isWriteNotificationEnabled() const
1852{
1853 Q_D(const QSocks5SocketEngine);
1854 return d->writeNotificationEnabled;
1855}
1856
1857void QSocks5SocketEngine::setWriteNotificationEnabled(bool enable)
1858{
1859 Q_D(QSocks5SocketEngine);
1860 d->writeNotificationEnabled = enable;
1861 if (enable && d->socketState == QAbstractSocket::ConnectedState) {
1862 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode && d->data->controlSocket->bytesToWrite())
1863 return; // will be emitted as a result of bytes written
1864 d->emitWriteNotification();
1865 d->writeNotificationActivated = false;
1866 }
1867}
1868
1869bool QSocks5SocketEngine::isExceptionNotificationEnabled() const
1870{
1871 Q_D(const QSocks5SocketEngine);
1872 return d->exceptNotificationEnabled;
1873}
1874
1875void QSocks5SocketEngine::setExceptionNotificationEnabled(bool enable)
1876{
1877 Q_D(QSocks5SocketEngine);
1878 d->exceptNotificationEnabled = enable;
1879}
1880
1881QAbstractSocketEngine *
1882QSocks5SocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
1883 const QNetworkProxy &proxy, QObject *parent)
1884{
1885 Q_UNUSED(socketType);
1886
1887 // proxy type must have been resolved by now
1888 if (proxy.type() != QNetworkProxy::Socks5Proxy) {
1889 QSOCKS5_DEBUG << "not proxying";
1890 return nullptr;
1891 }
1892 auto engine = std::make_unique<QSocks5SocketEngine>(parent);
1893 engine->setProxy(proxy);
1894 return engine.release();
1895}
1896
1897QAbstractSocketEngine *QSocks5SocketEngineHandler::createSocketEngine(qintptr socketDescriptor, QObject *parent)
1898{
1899 QSOCKS5_DEBUG << "createSocketEngine" << socketDescriptor;
1900 if (socks5BindStore()->contains(socketDescriptor)) {
1901 QSOCKS5_DEBUG << "bind store contains" << socketDescriptor;
1902 return new QSocks5SocketEngine(parent);
1903 }
1904 return nullptr;
1905}
1906
1907QT_END_NAMESPACE
1908
1909#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