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 // just skip it
221 pos++;
222 qDebug() << "skipping hostname of len" << uint(pBuf[pos]);
223 pos += uchar(pBuf[pos]);
224 } else {
225 QSOCKS5_DEBUG << "invalid address type" << (int)pBuf[pos];
226 ret = -1;
227 }
228
229 if (ret == 1) {
230 if (buf.size() - pos < 2) {
231 QSOCKS5_DEBUG << "need more data for port";
232 return 0;
233 }
234 port = qFromBigEndian<quint16>(&pBuf[pos]);
235 pos += 2;
236 }
237
238 if (ret == 1) {
239 QSOCKS5_DEBUG << "got [" << address << ':' << port << ']';
240 *pAddress = address;
241 *pPort = port;
242 *pPos = pos;
243 }
244
245 return ret;
246}
247
253
258
267
274
275#ifndef QT_NO_UDPSOCKET
283#endif
284
285// needs to be thread safe
287{
288public:
291
292 void add(qintptr socketDescriptor, QSocks5BindData *bindData);
293 bool contains(qintptr socketDescriptor);
294 QSocks5BindData *retrieve(qintptr socketDescriptor);
295
296protected:
297 void timerEvent(QTimerEvent * event) override;
298
301 //socket descriptor, data, timestamp
303};
304
305Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore)
306
307QSocks5BindStore::QSocks5BindStore()
308{
309 QCoreApplication *app = QCoreApplication::instance();
310 if (app && app->thread() != thread())
311 moveToThread(app->thread());
312}
313
317
318void QSocks5BindStore::add(qintptr socketDescriptor, QSocks5BindData *bindData)
319{
320 QMutexLocker lock(&mutex);
321 if (store.contains(socketDescriptor)) {
322 // qDebug("delete it");
323 }
324 bindData->timeStamp.start();
325 store.insert(socketDescriptor, bindData);
326
327 // start sweep timer if not started
328 if (!sweepTimer.isActive())
329 sweepTimer.start(1min, this);
330}
331
332bool QSocks5BindStore::contains(qintptr socketDescriptor)
333{
334 QMutexLocker lock(&mutex);
335 return store.contains(socketDescriptor);
336}
337
338QSocks5BindData *QSocks5BindStore::retrieve(qintptr socketDescriptor)
339{
340 QMutexLocker lock(&mutex);
341 const auto it = store.constFind(socketDescriptor);
342 if (it == store.cend())
343 return nullptr;
344 QSocks5BindData *bindData = it.value();
345 store.erase(it);
346 if (bindData) {
347 if (bindData->controlSocket->thread() != QThread::currentThread()) {
348 qWarning("Cannot access socks5 bind data from different thread");
349 return nullptr;
350 }
351 } else {
352 QSOCKS5_DEBUG << "__ERROR__ binddata == 0";
353 }
354 // stop the sweep timer if not needed
355 if (store.isEmpty())
356 sweepTimer.stop();
357 return bindData;
358}
359
360void QSocks5BindStore::timerEvent(QTimerEvent * event)
361{
362 QMutexLocker lock(&mutex);
363 if (event->id() == sweepTimer.id()) {
364 QSOCKS5_DEBUG << "QSocks5BindStore performing sweep";
365 for (auto it = store.begin(), end = store.end(); it != end;) {
366 if (it.value()->timeStamp.hasExpired(350000)) {
367 QSOCKS5_DEBUG << "QSocks5BindStore removing JJJJ";
368 it = store.erase(it);
369 } else {
370 ++it;
371 }
372 }
373 }
374}
375
379
383
385{
386 return 0x00;
387}
388
389bool QSocks5Authenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
390{
391 Q_UNUSED(socket);
392 *completed = true;
393 return true;
394}
395
396bool QSocks5Authenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
397{
398 Q_UNUSED(socket);
399 *completed = true;
400 return true;
401}
402
403bool QSocks5Authenticator::seal(const QByteArray &buf, QByteArray *sealedBuf)
404{
405 *sealedBuf = buf;
406 return true;
407}
408
409bool QSocks5Authenticator::unSeal(const QByteArray &sealedBuf, QByteArray *buf)
410{
411 *buf = sealedBuf;
412 return true;
413}
414
415bool QSocks5Authenticator::unSeal(QTcpSocket *sealedSocket, QByteArray *buf)
416{
417 return unSeal(sealedSocket->readAll(), buf);
418}
419
420QSocks5PasswordAuthenticator::QSocks5PasswordAuthenticator(const QString &userName, const QString &password)
421{
422 this->userName = userName;
423 this->password = password;
424}
425
427{
428 return 0x02;
429}
430
431bool QSocks5PasswordAuthenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
432{
433 *completed = false;
434 QByteArray uname = userName.toLatin1();
435 QByteArray passwd = password.toLatin1();
436 QByteArray dataBuf(3 + uname.size() + passwd.size(), 0);
437 char *buf = dataBuf.data();
438 int pos = 0;
439 buf[pos++] = S5_PASSWORDAUTH_VERSION;
440 buf[pos++] = uname.size();
441 memcpy(&buf[pos], uname.data(), uname.size());
442 pos += uname.size();
443 buf[pos++] = passwd.size();
444 memcpy(&buf[pos], passwd.data(), passwd.size());
445 return socket->write(dataBuf) == dataBuf.size();
446}
447
448bool QSocks5PasswordAuthenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
449{
450 *completed = false;
451
452 if (socket->bytesAvailable() < 2)
453 return true;
454
455 QByteArray buf = socket->read(2);
456 if (buf.at(0) == S5_PASSWORDAUTH_VERSION && buf.at(1) == 0x00) {
457 *completed = true;
458 return true;
459 }
460
461 // must disconnect
462 socket->close();
463 return false;
464}
465
467{
468 return "Socks5 user name or password incorrect"_L1;
469}
470
471
472
473QSocks5SocketEnginePrivate::QSocks5SocketEnginePrivate()
478 , socketDescriptor(-1)
479 , data(nullptr)
480 , connectData(nullptr)
481#ifndef QT_NO_UDPSOCKET
482 , udpData(nullptr)
483#endif
484 , bindData(nullptr)
490{
491 mode = NoMode;
492}
493
497
499{
500 Q_Q(QSocks5SocketEngine);
501
502 mode = socks5Mode;
503 if (mode == ConnectMode) {
506#ifndef QT_NO_UDPSOCKET
507 } else if (mode == UdpAssociateMode) {
509 data = udpData;
510 udpData->udpSocket = new QUdpSocket(q);
511 udpData->udpSocket->setProxy(QNetworkProxy::NoProxy);
512 QObject::connect(udpData->udpSocket, SIGNAL(readyRead()),
513 q, SLOT(_q_udpSocketReadNotification()),
514 Qt::DirectConnection);
515#endif // QT_NO_UDPSOCKET
516 } else if (mode == BindMode) {
518 data = bindData;
519 }
520
521 data->controlSocket = new QTcpSocket(q);
522 data->controlSocket->setProxy(QNetworkProxy::NoProxy);
523 QObject::connect(data->controlSocket, SIGNAL(connected()), q, SLOT(_q_controlSocketConnected()),
524 Qt::DirectConnection);
525 QObject::connect(data->controlSocket, SIGNAL(readyRead()), q, SLOT(_q_controlSocketReadNotification()),
526 Qt::DirectConnection);
527 QObject::connect(data->controlSocket, SIGNAL(bytesWritten(qint64)), q, SLOT(_q_controlSocketBytesWritten()),
528 Qt::DirectConnection);
529 QObject::connect(data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
530 q, SLOT(_q_controlSocketErrorOccurred(QAbstractSocket::SocketError)),
531 Qt::DirectConnection);
532 QObject::connect(data->controlSocket, SIGNAL(disconnected()), q, SLOT(_q_controlSocketDisconnected()),
533 Qt::DirectConnection);
534 QObject::connect(data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
535 q, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
536 Qt::DirectConnection);
537
538 if (!proxyInfo.user().isEmpty() || !proxyInfo.password().isEmpty()) {
539 QSOCKS5_D_DEBUG << "using username/password authentication; user =" << proxyInfo.user();
540 data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
541 } else {
542 QSOCKS5_D_DEBUG << "not using authentication";
544 }
545}
546
547void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, const QString &extraMessage)
548{
549 Q_Q(QSocks5SocketEngine);
550
551 switch (state) {
552 case Uninitialized:
553 case Authenticating:
556 case Connected:
558 case BindSuccess:
559 // these aren't error states
560 return;
561
562 case ConnectError:
563 case ControlSocketError: {
564 QAbstractSocket::SocketError controlSocketError = data->controlSocket->error();
565 if (socks5State != Connected) {
566 switch (controlSocketError) {
567 case QAbstractSocket::ConnectionRefusedError:
568 q->setError(QAbstractSocket::ProxyConnectionRefusedError,
569 QSocks5SocketEngine::tr("Connection to proxy refused"));
570 break;
571 case QAbstractSocket::RemoteHostClosedError:
572 q->setError(QAbstractSocket::ProxyConnectionClosedError,
573 QSocks5SocketEngine::tr("Connection to proxy closed prematurely"));
574 break;
575 case QAbstractSocket::HostNotFoundError:
576 q->setError(QAbstractSocket::ProxyNotFoundError,
577 QSocks5SocketEngine::tr("Proxy host not found"));
578 break;
579 case QAbstractSocket::SocketTimeoutError:
580 if (state == ConnectError) {
581 q->setError(QAbstractSocket::ProxyConnectionTimeoutError,
582 QSocks5SocketEngine::tr("Connection to proxy timed out"));
583 break;
584 }
585 Q_FALLTHROUGH();
586 default:
587 q->setError(controlSocketError, data->controlSocket->errorString());
588 break;
589 }
590 } else {
591 q->setError(controlSocketError, data->controlSocket->errorString());
592 }
593 break;
594 }
595
596 case AuthenticatingError:
597 q->setError(QAbstractSocket::ProxyAuthenticationRequiredError,
598 extraMessage.isEmpty() ?
599 QSocks5SocketEngine::tr("Proxy authentication failed") :
600 QSocks5SocketEngine::tr("Proxy authentication failed: %1").arg(extraMessage));
601 break;
602
603 case RequestError:
604 // error code set by caller (overload)
605 break;
606
607 case SocksError:
608 q->setError(QAbstractSocket::ProxyProtocolError,
609 QSocks5SocketEngine::tr("SOCKS version 5 protocol error"));
610 break;
611
612 case HostNameLookupError:
613 q->setError(QAbstractSocket::HostNotFoundError,
614 QAbstractSocket::tr("Host not found"));
615 break;
616 }
617
618 q->setState(QAbstractSocket::UnconnectedState);
619 socks5State = state;
620}
621
623{
624 Q_Q(QSocks5SocketEngine);
625 switch (socks5error) {
626 case SocksFailure:
627 q->setError(QAbstractSocket::NetworkError,
628 QSocks5SocketEngine::tr("General SOCKSv5 server failure"));
629 break;
630 case ConnectionNotAllowed:
631 q->setError(QAbstractSocket::SocketAccessError,
632 QSocks5SocketEngine::tr("Connection not allowed by SOCKSv5 server"));
633 break;
634 case NetworkUnreachable:
635 q->setError(QAbstractSocket::NetworkError,
636 QAbstractSocket::tr("Network unreachable"));
637 break;
638 case HostUnreachable:
639 q->setError(QAbstractSocket::HostNotFoundError,
640 QAbstractSocket::tr("Host not found"));
641 break;
642 case ConnectionRefused:
643 q->setError(QAbstractSocket::ConnectionRefusedError,
644 QAbstractSocket::tr("Connection refused"));
645 break;
646 case TTLExpired:
647 q->setError(QAbstractSocket::NetworkError,
648 QSocks5SocketEngine::tr("TTL expired"));
649 break;
650 case CommandNotSupported:
651 q->setError(QAbstractSocket::UnsupportedSocketOperationError,
652 QSocks5SocketEngine::tr("SOCKSv5 command not supported"));
653 break;
654 case AddressTypeNotSupported:
655 q->setError(QAbstractSocket::UnsupportedSocketOperationError,
656 QSocks5SocketEngine::tr("Address type not supported"));
657 break;
658
659 default:
660 q->setError(QAbstractSocket::UnknownSocketError,
661 QSocks5SocketEngine::tr("Unknown SOCKSv5 proxy error code 0x%1").arg(int(socks5error), 16));
662 break;
663 }
664
665 setErrorState(state, QString());
666}
667
669{
670 Q_Q(QSocks5SocketEngine);
671
672 // we require authentication
673 QAuthenticator auth;
674 q->proxyAuthenticationRequired(proxyInfo, &auth);
675
676 if (!auth.user().isEmpty() || !auth.password().isEmpty()) {
677 // we have new credentials, let's try again
678 QSOCKS5_DEBUG << "authentication failure: retrying connection";
680
681 delete data->authenticator;
682 proxyInfo.setUser(auth.user());
683 proxyInfo.setPassword(auth.password());
684 data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
685
686 {
687 const QSignalBlocker blocker(data->controlSocket);
688 data->controlSocket->abort();
689 }
690 data->controlSocket->connectToHost(proxyInfo.hostName(), proxyInfo.port());
691 } else {
692 // authentication failure
693
695 data->controlSocket->close();
697 }
698}
699
701{
702 // not enough data to begin
703 if (data->controlSocket->bytesAvailable() < 2)
704 return;
705
706 QByteArray buf = data->controlSocket->read(2);
707 if (buf.at(0) != S5_VERSION_5) {
708 QSOCKS5_D_DEBUG << "Socks5 version incorrect";
710 data->controlSocket->close();
712 return;
713 }
714
715 bool authComplete = false;
716 if (uchar(buf.at(1)) == S5_AUTHMETHOD_NONE) {
717 authComplete = true;
718 } else if (uchar(buf.at(1)) == S5_AUTHMETHOD_NOTACCEPTABLE) {
720 return;
721 } else if (buf.at(1) != data->authenticator->methodId()
723 setErrorState(AuthenticatingError, "Socks5 host did not support authentication method."_L1);
724 socketError = QAbstractSocket::SocketAccessError; // change the socket error
726 return;
727 }
728
729 if (authComplete)
731 else
733}
734
736{
737 bool authComplete = false;
740 return;
741 }
742 if (authComplete)
744}
745
747{
748 QHostAddress address;
749 quint16 port = 0;
750 char command = 0;
751 if (mode == ConnectMode) {
752 command = S5_CONNECT;
753 address = peerAddress;
754 port = peerPort;
755 } else if (mode == BindMode) {
756 command = S5_BIND;
757 address = localAddress;
758 port = localPort;
759 } else {
760#ifndef QT_NO_UDPSOCKET
761 command = S5_UDP_ASSOCIATE;
762 address = localAddress; //data->controlSocket->localAddress();
763 port = localPort;
764#endif
765 }
766
767 QByteArray buf;
768 buf.reserve(270); // big enough for domain name;
769 buf.append(char(S5_VERSION_5));
770 buf.append(command);
771 buf.append('\0');
772 if (peerName.isEmpty() && !qt_socks5_set_host_address_and_port(address, port, &buf)) {
773 QSOCKS5_DEBUG << "error setting address" << address << " : " << port;
774 //### set error code ....
775 return;
776 } else if (!peerName.isEmpty() && !qt_socks5_set_host_name_and_port(peerName, port, &buf)) {
777 QSOCKS5_DEBUG << "error setting peer name" << peerName << " : " << port;
778 //### set error code ....
779 return;
780 }
781 QSOCKS5_DEBUG << "sending" << dump(buf);
782 QByteArray sealedBuf;
783 if (!data->authenticator->seal(buf, &sealedBuf)) {
784 // ### Handle this error.
785 }
786 data->controlSocket->write(sealedBuf);
787 data->controlSocket->flush();
789}
790
792{
793 Q_Q(QSocks5SocketEngine);
794 QSOCKS5_DEBUG << "parseRequestMethodReply()";
795
796 QByteArray inBuf;
797 if (!data->authenticator->unSeal(data->controlSocket, &inBuf)) {
798 // ### check error and not just not enough data
799 QSOCKS5_DEBUG << "unSeal failed, needs more data";
800 return;
801 }
802
803 inBuf.prepend(receivedHeaderFragment);
804 receivedHeaderFragment.clear();
805 QSOCKS5_DEBUG << dump(inBuf);
806 if (inBuf.size() < 3) {
807 QSOCKS5_DEBUG << "need more data for request reply header .. put this data somewhere";
808 receivedHeaderFragment = inBuf;
809 return;
810 }
811
812 QHostAddress address;
813 quint16 port = 0;
814
815 if (inBuf.at(0) != S5_VERSION_5 || inBuf.at(2) != 0x00) {
816 QSOCKS5_DEBUG << "socks protocol error";
818 } else if (inBuf.at(1) != S5_SUCCESS) {
819 Socks5Error socks5Error = Socks5Error(inBuf.at(1));
820 QSOCKS5_DEBUG << "Request error :" << socks5Error;
821 if ((socks5Error == SocksFailure || socks5Error == ConnectionNotAllowed)
822 && !peerName.isEmpty()) {
823 // Dante seems to use this error code to indicate hostname resolution failure
825 } else {
827 }
828 } else {
829 // connection success, retrieve the remote addresses
830 int pos = 3;
831 int err = qt_socks5_get_host_address_and_port(inBuf, &address, &port, &pos);
832 if (err == -1) {
833 QSOCKS5_DEBUG << "error getting address";
835 } else if (err == 0) {
836 //need more data
837 receivedHeaderFragment = inBuf;
838 return;
839 } else {
840 inBuf.remove(0, pos);
841 for (int i = inBuf.size() - 1; i >= 0 ; --i)
842 data->controlSocket->ungetChar(inBuf.at(i));
843 }
844 }
845
847 // no error
848 localAddress = address;
849 localPort = port;
850
851 if (mode == ConnectMode) {
854 // notify the upper layer that we're done
855 q->setState(QAbstractSocket::ConnectedState);
857 } else if (mode == BindMode) {
859 q->setState(QAbstractSocket::ListeningState);
860 } else {
862 }
863 } else if (socks5State == BindSuccess) {
864 // no error and we got a connection
865 bindData->peerAddress = address;
866 bindData->peerPort = port;
867
869 } else {
870 // got an error
871 data->controlSocket->close();
873 }
874}
875
877{
878 Q_Q(QSocks5SocketEngine);
881 QSOCKS5_D_DEBUG << "emitting readNotification";
882 QPointer<QSocks5SocketEngine> qq = q;
883 q->readNotification();
884 if (!qq)
885 return;
886 // check if there needs to be a new zero read notification
887 if (data && data->controlSocket->state() == QAbstractSocket::UnconnectedState
888 && data->controlSocket->error() == QAbstractSocket::RemoteHostClosedError) {
889 connectData->readBuffer.clear();
891 }
892 }
893}
894
896{
897 Q_Q(QSocks5SocketEngine);
900 QSOCKS5_D_DEBUG << "queueing readNotification";
902 QMetaObject::invokeMethod(q, "_q_emitPendingReadNotification", Qt::QueuedConnection);
903 }
904}
905
907{
909 Q_Q(QSocks5SocketEngine);
911 QSOCKS5_D_DEBUG << "emitting writeNotification";
912 q->writeNotification();
913 }
914}
915
917{
918 Q_Q(QSocks5SocketEngine);
921 QSOCKS5_D_DEBUG << "queueing writeNotification";
923 QMetaObject::invokeMethod(q, "_q_emitPendingWriteNotification", Qt::QueuedConnection);
924 }
925}
926
928{
930 Q_Q(QSocks5SocketEngine);
931 QSOCKS5_D_DEBUG << "emitting connectionNotification";
932 q->connectionNotification();
933}
934
936{
937 Q_Q(QSocks5SocketEngine);
938 QSOCKS5_D_DEBUG << "queueing connectionNotification";
940 QMetaObject::invokeMethod(q, "_q_emitPendingConnectionNotification", Qt::QueuedConnection);
941}
942
943QSocks5SocketEngine::QSocks5SocketEngine(QObject *parent)
944:QAbstractSocketEngine(*new QSocks5SocketEnginePrivate(), parent)
945{
946}
947
948QSocks5SocketEngine::~QSocks5SocketEngine()
949{
950 Q_D(QSocks5SocketEngine);
951
952 if (d->data) {
953 delete d->data->authenticator;
954 delete d->data->controlSocket;
955 }
956 if (d->connectData)
957 delete d->connectData;
958#ifndef QT_NO_UDPSOCKET
959 if (d->udpData) {
960 delete d->udpData->udpSocket;
961 delete d->udpData;
962 }
963#endif
964 if (d->bindData)
965 delete d->bindData;
966}
967
968static int nextDescriptor()
969{
970 Q_CONSTINIT static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
971 return 1 + counter.fetchAndAddRelaxed(1);
972}
973
974bool QSocks5SocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
975{
976 Q_D(QSocks5SocketEngine);
977
978 d->socketDescriptor = nextDescriptor();
979
980 d->socketType = type;
981 d->socketProtocol = protocol;
982
983 return true;
984}
985
986bool QSocks5SocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState)
987{
988 Q_D(QSocks5SocketEngine);
989
990 QSOCKS5_Q_DEBUG << "initialize" << socketDescriptor;
991
992 // this is only valid for the other side of a bind, nothing else is supported
993
994 if (socketState != QAbstractSocket::ConnectedState) {
995 //### must be connected state ???
996 return false;
997 }
998
999 QSocks5BindData *bindData = socks5BindStore()->retrieve(socketDescriptor);
1000 if (bindData) {
1001
1002 d->socketState = QAbstractSocket::ConnectedState;
1003 d->socketType = QAbstractSocket::TcpSocket;
1004 d->connectData = new QSocks5ConnectData;
1005 d->data = d->connectData;
1006 d->mode = QSocks5SocketEnginePrivate::ConnectMode;
1007 d->data->controlSocket = bindData->controlSocket;
1008 bindData->controlSocket = nullptr;
1009 d->data->controlSocket->setParent(this);
1010 d->socketProtocol = d->data->controlSocket->localAddress().protocol();
1011 d->data->authenticator = bindData->authenticator;
1012 bindData->authenticator = nullptr;
1013 d->localPort = bindData->localPort;
1014 d->localAddress = bindData->localAddress;
1015 d->peerPort = bindData->peerPort;
1016 d->peerAddress = bindData->peerAddress;
1017 d->inboundStreamCount = d->outboundStreamCount = 1;
1018 delete bindData;
1019
1020 QObject::connect(d->data->controlSocket, SIGNAL(connected()), this, SLOT(_q_controlSocketConnected()),
1021 Qt::DirectConnection);
1022 QObject::connect(d->data->controlSocket, SIGNAL(readyRead()), this, SLOT(_q_controlSocketReadNotification()),
1023 Qt::DirectConnection);
1024 QObject::connect(d->data->controlSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_controlSocketBytesWritten()),
1025 Qt::DirectConnection);
1026 QObject::connect(d->data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketErrorOccurred(QAbstractSocket::SocketError)),
1027 Qt::DirectConnection);
1028 QObject::connect(d->data->controlSocket, SIGNAL(disconnected()), this, SLOT(_q_controlSocketDisconnected()),
1029 Qt::DirectConnection);
1030 QObject::connect(d->data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
1031 this, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
1032 Qt::DirectConnection);
1033
1034 d->socks5State = QSocks5SocketEnginePrivate::Connected;
1035
1036 if (d->data->controlSocket->bytesAvailable() != 0)
1037 d->_q_controlSocketReadNotification();
1038 return true;
1039 }
1040 return false;
1041}
1042
1043void QSocks5SocketEngine::setProxy(const QNetworkProxy &networkProxy)
1044{
1045 Q_D(QSocks5SocketEngine);
1046 d->proxyInfo = networkProxy;
1047}
1048
1049qintptr QSocks5SocketEngine::socketDescriptor() const
1050{
1051 Q_D(const QSocks5SocketEngine);
1052 return d->socketDescriptor;
1053}
1054
1055bool QSocks5SocketEngine::isValid() const
1056{
1057 Q_D(const QSocks5SocketEngine);
1058 return d->socketType != QAbstractSocket::UnknownSocketType
1059 && d->socks5State != QSocks5SocketEnginePrivate::SocksError
1060 && (d->socketError == QAbstractSocket::UnknownSocketError
1061 || d->socketError == QAbstractSocket::SocketTimeoutError
1062 || d->socketError == QAbstractSocket::UnfinishedSocketOperationError);
1063}
1064
1065bool QSocks5SocketEngine::connectInternal()
1066{
1067 Q_D(QSocks5SocketEngine);
1068
1069 if (!d->data) {
1070 if (socketType() == QAbstractSocket::TcpSocket) {
1071 d->initialize(QSocks5SocketEnginePrivate::ConnectMode);
1072#ifndef QT_NO_UDPSOCKET
1073 } else if (socketType() == QAbstractSocket::UdpSocket) {
1074 d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1075 // all udp needs to be bound
1076 if (!bind(QHostAddress("0.0.0.0"_L1), 0))
1077 return false;
1078
1079 setState(QAbstractSocket::ConnectedState);
1080 return true;
1081#endif
1082 } else {
1083 qFatal("QSocks5SocketEngine::connectToHost: in QTcpServer mode");
1084 return false;
1085 }
1086 }
1087
1088 if (d->socketState != QAbstractSocket::ConnectingState) {
1089 if (d->socks5State == QSocks5SocketEnginePrivate::Uninitialized
1090 // We may have new auth credentials since an earlier failure:
1091 || d->socks5State == QSocks5SocketEnginePrivate::AuthenticatingError) {
1092 setState(QAbstractSocket::ConnectingState);
1093 //limit buffer in internal socket, data is buffered in the external socket under application control
1094 d->data->controlSocket->setReadBufferSize(65536);
1095 }
1096
1097 d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1098 }
1099
1100 return false;
1101}
1102
1103bool QSocks5SocketEngine::connectToHost(const QHostAddress &address, quint16 port)
1104{
1105 Q_D(QSocks5SocketEngine);
1106 QSOCKS5_DEBUG << "connectToHost" << address << ':' << port;
1107
1108 setPeerAddress(address);
1109 setPeerPort(port);
1110 d->peerName.clear();
1111
1112 return connectInternal();
1113}
1114
1115bool QSocks5SocketEngine::connectToHostByName(const QString &hostname, quint16 port)
1116{
1117 Q_D(QSocks5SocketEngine);
1118
1119 setPeerAddress(QHostAddress());
1120 setPeerPort(port);
1121 d->peerName = hostname;
1122
1123 return connectInternal();
1124}
1125
1127{
1128 QSOCKS5_DEBUG << "_q_controlSocketConnected";
1129 QByteArray buf(3, 0);
1130 buf[0] = S5_VERSION_5;
1131 buf[1] = 0x01;
1133 data->controlSocket->write(buf);
1135}
1136
1138{
1139 QSOCKS5_D_DEBUG << "_q_controlSocketReadNotification socks5state" << s5StateToString(socks5State)
1140 << "bytes available" << data->controlSocket->bytesAvailable();
1141
1142 if (data->controlSocket->bytesAvailable() == 0) {
1143 QSOCKS5_D_DEBUG << "########## bogus read why do we get these ... on windows only";
1144 return;
1145 }
1146
1147 switch (socks5State) {
1150 break;
1151 case Authenticating:
1153 break;
1154 case RequestMethodSent:
1156 if (socks5State == Connected && data->controlSocket->bytesAvailable())
1158 break;
1159 case Connected: {
1160 QByteArray buf;
1161 if (!data->authenticator->unSeal(data->controlSocket, &buf)) {
1162 // qDebug("unseal error maybe need to wait for more data");
1163 }
1164 if (buf.size()) {
1165 QSOCKS5_DEBUG << dump(buf);
1166 connectData->readBuffer.append(std::move(buf));
1168 }
1169 break;
1170 }
1171 case BindSuccess:
1172 // only get here if command is bind
1173 if (mode == BindMode) {
1175 break;
1176 }
1177
1178 Q_FALLTHROUGH();
1179 default:
1180 qWarning("QSocks5SocketEnginePrivate::_q_controlSocketReadNotification: "
1181 "Unexpectedly received data while in state=%d and mode=%d",
1182 socks5State, mode);
1183 break;
1184 };
1185}
1186
1188{
1189 QSOCKS5_DEBUG << "_q_controlSocketBytesWritten";
1190
1191 if (socks5State != Connected
1192 || (mode == ConnectMode
1193 && data->controlSocket->bytesToWrite()))
1194 return;
1195 if (data->controlSocket->bytesToWrite() < MaxWriteBufferSize) {
1198 }
1199}
1200
1201void QSocks5SocketEnginePrivate::_q_controlSocketErrorOccurred(QAbstractSocket::SocketError error)
1202{
1203 QSOCKS5_D_DEBUG << "controlSocketError" << error << data->controlSocket->errorString();
1204
1205 if (error == QAbstractSocket::SocketTimeoutError)
1206 return; // ignore this error -- comes from the waitFor* functions
1207
1208 if (error == QAbstractSocket::RemoteHostClosedError
1209 && socks5State == Connected) {
1210 // clear the read buffer in connect mode so that bytes available returns 0
1211 // if there already is a read notification pending then this will be processed first
1213 connectData->readBuffer.clear();
1215 data->controlSocket->close();
1216 // cause a disconnect in the outer socket
1218 } else if (socks5State == Uninitialized
1223 data->controlSocket->close();
1225 } else {
1226 q_func()->setError(data->controlSocket->error(), data->controlSocket->errorString());
1229 }
1230}
1231
1233{
1234 QSOCKS5_D_DEBUG << "_q_controlSocketDisconnected";
1235}
1236
1237void QSocks5SocketEnginePrivate::_q_controlSocketStateChanged(QAbstractSocket::SocketState state)
1238{
1239 QSOCKS5_D_DEBUG << "_q_controlSocketStateChanged" << state;
1240}
1241
1242#ifndef QT_NO_UDPSOCKET
1244{
1245 QSOCKS5_D_DEBUG << "_q_udpSocketReadNotification()";
1246
1247 // check some state stuff
1248 if (!udpData->udpSocket->hasPendingDatagrams()) {
1249 QSOCKS5_D_DEBUG << "false read ??";
1250 return;
1251 }
1252
1253 while (udpData->udpSocket->hasPendingDatagrams()) {
1254 QByteArray sealedBuf(udpData->udpSocket->pendingDatagramSize(), 0);
1255 QSOCKS5_D_DEBUG << "new datagram";
1256 udpData->udpSocket->readDatagram(sealedBuf.data(), sealedBuf.size());
1257 QByteArray inBuf;
1258 if (!data->authenticator->unSeal(sealedBuf, &inBuf)) {
1259 QSOCKS5_D_DEBUG << "failed unsealing datagram discarding";
1260 return;
1261 }
1262 QSOCKS5_DEBUG << dump(inBuf);
1263 int pos = 0;
1264 const char *buf = inBuf.constData();
1265 if (inBuf.size() < 4) {
1266 QSOCKS5_D_DEBUG << "bogus udp data, discarding";
1267 return;
1268 }
1269 QSocks5RevivedDatagram datagram;
1270 if (buf[pos++] != 0 || buf[pos++] != 0) {
1271 QSOCKS5_D_DEBUG << "invalid datagram discarding";
1272 return;
1273 }
1274 if (buf[pos++] != 0) { //### add fragmentation reading support
1275 QSOCKS5_D_DEBUG << "don't support fragmentation yet disgarding";
1276 return;
1277 }
1278 if (qt_socks5_get_host_address_and_port(inBuf, &datagram.address, &datagram.port, &pos) != 1) {
1279 QSOCKS5_D_DEBUG << "failed to get address from datagram disgarding";
1280 return;
1281 }
1282 datagram.data = QByteArray(&buf[pos], inBuf.size() - pos);
1283 udpData->pendingDatagrams.enqueue(datagram);
1284 }
1286}
1287#endif // QT_NO_UDPSOCKET
1288
1289bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
1290{
1291 Q_D(QSocks5SocketEngine);
1292
1293 // when bind we will block until the bind is finished as the info from the proxy server is needed
1294
1295 QHostAddress address;
1296 if (addr.protocol() == QAbstractSocket::AnyIPProtocol)
1297 address = QHostAddress::AnyIPv4; //SOCKS5 doesn't support dual stack, and there isn't any implementation of udp on ipv6 yet
1298 else
1299 address = addr;
1300
1301 if (!d->data) {
1302 if (socketType() == QAbstractSocket::TcpSocket) {
1303 d->initialize(QSocks5SocketEnginePrivate::BindMode);
1304#ifndef QT_NO_UDPSOCKET
1305 } else if (socketType() == QAbstractSocket::UdpSocket) {
1306 d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1307#endif
1308 } else {
1309 //### something invalid
1310 return false;
1311 }
1312 }
1313
1314#ifndef QT_NO_UDPSOCKET
1315 if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1316 if (!d->udpData->udpSocket->bind(address, port)) {
1317 QSOCKS5_Q_DEBUG << "local udp bind failed";
1318 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1319 return false;
1320 }
1321 d->localAddress = d->udpData->udpSocket->localAddress();
1322 d->localPort = d->udpData->udpSocket->localPort();
1323 } else
1324#endif
1325 if (d->mode == QSocks5SocketEnginePrivate::BindMode) {
1326 d->localAddress = address;
1327 d->localPort = port;
1328 } else {
1329 //### something invalid
1330 return false;
1331 }
1332
1333 d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1334 if (!d->waitForConnected(QDeadlineTimer{Socks5BlockingBindTimeout}, nullptr) ||
1335 d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1336 // waitForConnected sets the error state and closes the socket
1337 QSOCKS5_Q_DEBUG << "waitForConnected to proxy server" << d->data->controlSocket->errorString();
1338 return false;
1339 }
1340 if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) {
1341 setState(QAbstractSocket::BoundState);
1342 return true;
1343#ifndef QT_NO_UDPSOCKET
1344 } else if (d->socks5State == QSocks5SocketEnginePrivate::UdpAssociateSuccess) {
1345 setState(QAbstractSocket::BoundState);
1346 d->udpData->associateAddress = d->localAddress;
1347 d->localAddress = QHostAddress();
1348 d->udpData->associatePort = d->localPort;
1349 d->localPort = 0;
1350 return true;
1351#endif // QT_NO_UDPSOCKET
1352 }
1353
1354 // binding timed out
1355 setError(QAbstractSocket::SocketTimeoutError,
1356 QLatin1StringView(QT_TRANSLATE_NOOP("QSocks5SocketEngine", "Network operation timed out")));
1357
1358///### delete d->udpSocket;
1359///### d->udpSocket = 0;
1360 return false;
1361}
1362
1363
1364bool QSocks5SocketEngine::listen(int backlog)
1365{
1366 Q_D(QSocks5SocketEngine);
1367 Q_UNUSED(backlog);
1368
1369 QSOCKS5_Q_DEBUG << "listen()";
1370
1371 // check that we are in bound and then go to listening.
1372 if (d->socketState == QAbstractSocket::BoundState) {
1373 d->socketState = QAbstractSocket::ListeningState;
1374
1375 // check if we already have a connection
1376 if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
1377 d->emitReadNotification();
1378
1379 return true;
1380 }
1381 return false;
1382}
1383
1384qintptr QSocks5SocketEngine::accept()
1385{
1386 Q_D(QSocks5SocketEngine);
1387 // check we are listing ---
1388
1389 QSOCKS5_Q_DEBUG << "accept()";
1390
1391 qintptr sd = -1;
1392 switch (d->socks5State) {
1393 case QSocks5SocketEnginePrivate::BindSuccess:
1394 QSOCKS5_Q_DEBUG << "BindSuccess adding" << d->socketDescriptor << "to the bind store";
1395 d->data->controlSocket->disconnect();
1396 d->data->controlSocket->setParent(nullptr);
1397 d->bindData->localAddress = d->localAddress;
1398 d->bindData->localPort = d->localPort;
1399 sd = d->socketDescriptor;
1400 socks5BindStore()->add(sd, d->bindData);
1401 d->data = nullptr;
1402 d->bindData = nullptr;
1403 d->socketDescriptor = 0;
1404 //### do something about this socket layer ... set it closed and an error about why ...
1405 // reset state and local port/address
1406 d->socks5State = QSocks5SocketEnginePrivate::Uninitialized; // ..??
1407 d->socketState = QAbstractSocket::UnconnectedState;
1408 break;
1409 case QSocks5SocketEnginePrivate::ControlSocketError:
1410 setError(QAbstractSocket::ProxyProtocolError, "Control socket error"_L1);
1411 break;
1412 default:
1413 setError(QAbstractSocket::ProxyProtocolError, "SOCKS5 proxy error"_L1);
1414 break;
1415 }
1416 return sd;
1417}
1418
1419void QSocks5SocketEngine::close()
1420{
1421 QSOCKS5_Q_DEBUG << "close()";
1422 Q_D(QSocks5SocketEngine);
1423 if (d->data && d->data->controlSocket) {
1424 if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) {
1425 QDeadlineTimer deadline(100ms);
1426 while (!d->data->controlSocket->bytesToWrite()) {
1427 if (!d->data->controlSocket->waitForBytesWritten(deadline.remainingTime()))
1428 break;
1429 }
1430 }
1431 d->data->controlSocket->close();
1432 }
1433 d->inboundStreamCount = d->outboundStreamCount = 0;
1434#ifndef QT_NO_UDPSOCKET
1435 if (d->udpData && d->udpData->udpSocket)
1436 d->udpData->udpSocket->close();
1437#endif
1438}
1439
1440qint64 QSocks5SocketEngine::bytesAvailable() const
1441{
1442 Q_D(const QSocks5SocketEngine);
1443 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
1444 return d->connectData->readBuffer.size();
1445#ifndef QT_NO_UDPSOCKET
1446 else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode
1447 && !d->udpData->pendingDatagrams.isEmpty())
1448 return d->udpData->pendingDatagrams.constFirst().data.size();
1449#endif
1450 return 0;
1451}
1452
1453qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
1454{
1455 Q_D(QSocks5SocketEngine);
1456 QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ')';
1457 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
1458 if (d->connectData->readBuffer.isEmpty()) {
1459 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1460 //imitate remote closed
1461 close();
1462 setError(QAbstractSocket::RemoteHostClosedError,
1463 "Remote host closed connection"_L1);
1464 setState(QAbstractSocket::UnconnectedState);
1465 return -1;
1466 } else {
1467 return 0; // nothing to be read
1468 }
1469 }
1470 const qint64 copy = d->connectData->readBuffer.read(data, maxlen);
1471 QSOCKS5_DEBUG << "read" << dump(QByteArray(data, copy));
1472 return copy;
1473#ifndef QT_NO_UDPSOCKET
1474 } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1475 return readDatagram(data, maxlen);
1476#endif
1477 }
1478 return 0;
1479}
1480
1481qint64 QSocks5SocketEngine::write(const char *data, qint64 len)
1482{
1483 Q_D(QSocks5SocketEngine);
1484 QSOCKS5_Q_DEBUG << "write" << dump(QByteArray(data, len));
1485
1486 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
1487 // clamp down the amount of bytes to transfer at once
1488 len = qMin<qint64>(len, MaxWriteBufferSize) - d->data->controlSocket->bytesToWrite();
1489 if (len <= 0)
1490 return 0;
1491
1492 QByteArray buf = QByteArray::fromRawData(data, len);
1493 QByteArray sealedBuf;
1494 if (!d->data->authenticator->seal(buf, &sealedBuf)) {
1495 // ### Handle this error.
1496 }
1497 // We pass pointer and size because 'sealedBuf' is (most definitely) raw data:
1498 // QIODevice might have to cache the byte array if the socket cannot write the data.
1499 // If the _whole_ array needs to be cached then it would simply store a copy of the
1500 // array whose data will go out of scope and be deallocated before it can be used.
1501 qint64 written = d->data->controlSocket->write(sealedBuf.constData(), sealedBuf.size());
1502
1503 if (written <= 0) {
1504 QSOCKS5_Q_DEBUG << "native write returned" << written;
1505 return written;
1506 }
1507 d->data->controlSocket->waitForBytesWritten(0);
1508 //NB: returning len rather than written for the OK case, because the "sealing" may increase the length
1509 return len;
1510#ifndef QT_NO_UDPSOCKET
1511 } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1512 // send to connected address
1513 return writeDatagram(data, len, QIpPacketHeader(d->peerAddress, d->peerPort));
1514#endif
1515 }
1516 //### set an error ???
1517 return -1;
1518}
1519
1520#ifndef QT_NO_UDPSOCKET
1521#ifndef QT_NO_NETWORKINTERFACE
1522bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &,
1523 const QNetworkInterface &)
1524{
1525 setError(QAbstractSocket::UnsupportedSocketOperationError,
1526 "Operation on socket is not supported"_L1);
1527 return false;
1528}
1529
1530bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &,
1531 const QNetworkInterface &)
1532{
1533 setError(QAbstractSocket::UnsupportedSocketOperationError,
1534 "Operation on socket is not supported"_L1);
1535 return false;
1536}
1537
1538
1539QNetworkInterface QSocks5SocketEngine::multicastInterface() const
1540{
1541 return QNetworkInterface();
1542}
1543
1544bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &)
1545{
1546 setError(QAbstractSocket::UnsupportedSocketOperationError,
1547 "Operation on socket is not supported"_L1);
1548 return false;
1549}
1550#endif // QT_NO_NETWORKINTERFACE
1551
1552bool QSocks5SocketEngine::hasPendingDatagrams() const
1553{
1554 Q_D(const QSocks5SocketEngine);
1555 Q_INIT_CHECK(false);
1556
1557 return !d->udpData->pendingDatagrams.isEmpty();
1558}
1559
1560qint64 QSocks5SocketEngine::pendingDatagramSize() const
1561{
1562 Q_D(const QSocks5SocketEngine);
1563
1564 if (!d->udpData->pendingDatagrams.isEmpty())
1565 return d->udpData->pendingDatagrams.head().data.size();
1566 return 0;
1567}
1568#endif // QT_NO_UDPSOCKET
1569
1570qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, PacketHeaderOptions)
1571{
1572#ifndef QT_NO_UDPSOCKET
1573 Q_D(QSocks5SocketEngine);
1574
1575 if (d->udpData->pendingDatagrams.isEmpty())
1576 return 0;
1577
1578 QSocks5RevivedDatagram datagram = d->udpData->pendingDatagrams.dequeue();
1579 int copyLen = qMin<int>(maxlen, datagram.data.size());
1580 memcpy(data, datagram.data.constData(), copyLen);
1581 if (header) {
1582 header->senderAddress = datagram.address;
1583 header->senderPort = datagram.port;
1584 }
1585 return copyLen;
1586#else
1587 Q_UNUSED(data);
1588 Q_UNUSED(maxlen);
1589 Q_UNUSED(header);
1590 return -1;
1591#endif // QT_NO_UDPSOCKET
1592}
1593
1594qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
1595{
1596#ifndef QT_NO_UDPSOCKET
1597 Q_D(QSocks5SocketEngine);
1598
1599 // it is possible to send with out first binding with udp, but socks5 requires a bind.
1600 if (!d->data) {
1601 d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1602 // all udp needs to be bound
1603 if (!bind(QHostAddress("0.0.0.0"_L1), 0)) {
1604 //### set error
1605 return -1;
1606 }
1607 }
1608
1609 QByteArray outBuf;
1610 outBuf.reserve(270 + len);
1611 outBuf.append(3, '\0');
1612 if (!qt_socks5_set_host_address_and_port(header.destinationAddress, header.destinationPort, &outBuf)) {
1613 QSOCKS5_DEBUG << "error setting address" << header.destinationAddress << " : "
1614 << header.destinationPort;
1615 //### set error code ....
1616 return -1;
1617 }
1618 outBuf += QByteArray(data, len);
1619 QSOCKS5_DEBUG << "sending" << dump(outBuf);
1620 QByteArray sealedBuf;
1621 if (!d->data->authenticator->seal(outBuf, &sealedBuf)) {
1622 QSOCKS5_DEBUG << "sealing data failed";
1623 setError(QAbstractSocket::SocketAccessError, d->data->authenticator->errorString());
1624 return -1;
1625 }
1626 if (d->udpData->udpSocket->writeDatagram(sealedBuf, d->udpData->associateAddress, d->udpData->associatePort) != sealedBuf.size()) {
1627 //### try frgamenting
1628 if (d->udpData->udpSocket->error() == QAbstractSocket::DatagramTooLargeError)
1629 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1630 //### else maybe more serious error
1631 return -1;
1632 }
1633
1634 return len;
1635#else
1636 Q_UNUSED(data);
1637 Q_UNUSED(len);
1638 Q_UNUSED(header);
1639 return -1;
1640#endif // QT_NO_UDPSOCKET
1641}
1642
1643qint64 QSocks5SocketEngine::bytesToWrite() const
1644{
1645 Q_D(const QSocks5SocketEngine);
1646 if (d->data && d->data->controlSocket) {
1647 return d->data->controlSocket->bytesToWrite();
1648 } else {
1649 return 0;
1650 }
1651}
1652
1653int QSocks5SocketEngine::option(SocketOption option) const
1654{
1655 Q_D(const QSocks5SocketEngine);
1656 if (d->data && d->data->controlSocket) {
1657 // convert the enum and call the real socket
1658 if (option == QAbstractSocketEngine::LowDelayOption)
1659 return d->data->controlSocket->socketOption(QAbstractSocket::LowDelayOption).toInt();
1660 if (option == QAbstractSocketEngine::KeepAliveOption)
1661 return d->data->controlSocket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
1662 }
1663 return -1;
1664}
1665
1666bool QSocks5SocketEngine::setOption(SocketOption option, int value)
1667{
1668 Q_D(QSocks5SocketEngine);
1669 if (d->data && d->data->controlSocket) {
1670 // convert the enum and call the real socket
1671 if (option == QAbstractSocketEngine::LowDelayOption)
1672 d->data->controlSocket->setSocketOption(QAbstractSocket::LowDelayOption, value);
1673 if (option == QAbstractSocketEngine::KeepAliveOption)
1674 d->data->controlSocket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
1675 return true;
1676 }
1677 return false;
1678}
1679
1680bool QSocks5SocketEnginePrivate::waitForConnected(QDeadlineTimer deadline, bool *timedOut)
1681{
1682 if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1683 return false;
1684
1685 const Socks5State wantedState =
1689
1690 while (socks5State != wantedState) {
1691 if (!data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
1692 if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1693 return true;
1694
1696 if (timedOut && data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1697 *timedOut = true;
1698 return false;
1699 }
1700 }
1701
1702 return true;
1703}
1704
1705bool QSocks5SocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
1706{
1707 Q_D(QSocks5SocketEngine);
1708 QSOCKS5_DEBUG << "waitForRead" << deadline.remainingTimeAsDuration();
1709
1710 d->readNotificationActivated = false;
1711
1712 // are we connected yet?
1713 if (!d->waitForConnected(deadline, timedOut))
1714 return false;
1715 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1716 return true;
1717 if (bytesAvailable() && d->readNotificationPending) {
1718 // We've got some data incoming, but the queued call hasn't been performed yet.
1719 // The data is where we expect it to be already, so just return true.
1720 return true;
1721 }
1722
1723 // we're connected
1724 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode ||
1725 d->mode == QSocks5SocketEnginePrivate::BindMode) {
1726 while (!d->readNotificationActivated) {
1727 if (!d->data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
1728 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1729 return true;
1730
1731 setError(d->data->controlSocket->error(), d->data->controlSocket->errorString());
1732 if (timedOut && d->data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1733 *timedOut = true;
1734 return false;
1735 }
1736 }
1737#ifndef QT_NO_UDPSOCKET
1738 } else {
1739 while (!d->readNotificationActivated) {
1740 if (!d->udpData->udpSocket->waitForReadyRead(deadline.remainingTime())) {
1741 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1742 if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
1743 *timedOut = true;
1744 return false;
1745 }
1746 }
1747#endif // QT_NO_UDPSOCKET
1748 }
1749
1750
1751 bool ret = d->readNotificationActivated;
1752 d->readNotificationActivated = false;
1753
1754 QSOCKS5_DEBUG << "waitForRead returned" << ret;
1755 return ret;
1756}
1757
1758
1759bool QSocks5SocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
1760{
1761 Q_D(QSocks5SocketEngine);
1762 QSOCKS5_DEBUG << "waitForWrite" << deadline.remainingTimeAsDuration();
1763
1764 // are we connected yet?
1765 if (!d->waitForConnected(deadline, timedOut))
1766 return false;
1767 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1768 return true;
1769
1770 // we're connected
1771
1772 // flush any bytes we may still have buffered in the time that we have left
1773 if (d->data->controlSocket->bytesToWrite())
1774 d->data->controlSocket->waitForBytesWritten(deadline.remainingTime());
1775
1776 auto shouldWriteBytes = [&]() {
1777 return d->data->controlSocket->state() == QAbstractSocket::ConnectedState
1778 && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize;
1779 };
1780
1781 qint64 remainingTime = deadline.remainingTime();
1782 for (; remainingTime > 0 && shouldWriteBytes(); remainingTime = deadline.remainingTime())
1783 d->data->controlSocket->waitForBytesWritten(remainingTime);
1784 return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
1785}
1786
1787bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
1788 bool checkRead, bool checkWrite,
1789 QDeadlineTimer deadline, bool *timedOut)
1790{
1791 Q_UNUSED(checkRead);
1792 if (!checkWrite) {
1793 bool canRead = waitForRead(deadline, timedOut);
1794 if (readyToRead)
1795 *readyToRead = canRead;
1796 return canRead;
1797 }
1798
1799 bool canWrite = waitForWrite(deadline, timedOut);
1800 if (readyToWrite)
1801 *readyToWrite = canWrite;
1802 return canWrite;
1803}
1804
1805bool QSocks5SocketEngine::isReadNotificationEnabled() const
1806{
1807 Q_D(const QSocks5SocketEngine);
1808 return d->readNotificationEnabled;
1809}
1810
1811void QSocks5SocketEngine::setReadNotificationEnabled(bool enable)
1812{
1813 Q_D(QSocks5SocketEngine);
1814
1815 QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ')';
1816
1817 bool emitSignal = false;
1818 if (!d->readNotificationEnabled
1819 && enable) {
1820 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
1821 emitSignal = !d->connectData->readBuffer.isEmpty();
1822#ifndef QT_NO_UDPSOCKET
1823 else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode)
1824 emitSignal = !d->udpData->pendingDatagrams.isEmpty();
1825#endif
1826 else if (d->mode == QSocks5SocketEnginePrivate::BindMode
1827 && d->socketState == QAbstractSocket::ListeningState
1828 && d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
1829 emitSignal = true;
1830 }
1831
1832 d->readNotificationEnabled = enable;
1833
1834 if (emitSignal)
1835 d->emitReadNotification();
1836}
1837
1838bool QSocks5SocketEngine::isWriteNotificationEnabled() const
1839{
1840 Q_D(const QSocks5SocketEngine);
1841 return d->writeNotificationEnabled;
1842}
1843
1844void QSocks5SocketEngine::setWriteNotificationEnabled(bool enable)
1845{
1846 Q_D(QSocks5SocketEngine);
1847 d->writeNotificationEnabled = enable;
1848 if (enable && d->socketState == QAbstractSocket::ConnectedState) {
1849 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode && d->data->controlSocket->bytesToWrite())
1850 return; // will be emitted as a result of bytes written
1851 d->emitWriteNotification();
1852 d->writeNotificationActivated = false;
1853 }
1854}
1855
1856bool QSocks5SocketEngine::isExceptionNotificationEnabled() const
1857{
1858 Q_D(const QSocks5SocketEngine);
1859 return d->exceptNotificationEnabled;
1860}
1861
1862void QSocks5SocketEngine::setExceptionNotificationEnabled(bool enable)
1863{
1864 Q_D(QSocks5SocketEngine);
1865 d->exceptNotificationEnabled = enable;
1866}
1867
1868QAbstractSocketEngine *
1869QSocks5SocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
1870 const QNetworkProxy &proxy, QObject *parent)
1871{
1872 Q_UNUSED(socketType);
1873
1874 // proxy type must have been resolved by now
1875 if (proxy.type() != QNetworkProxy::Socks5Proxy) {
1876 QSOCKS5_DEBUG << "not proxying";
1877 return nullptr;
1878 }
1879 auto engine = std::make_unique<QSocks5SocketEngine>(parent);
1880 engine->setProxy(proxy);
1881 return engine.release();
1882}
1883
1884QAbstractSocketEngine *QSocks5SocketEngineHandler::createSocketEngine(qintptr socketDescriptor, QObject *parent)
1885{
1886 QSOCKS5_DEBUG << "createSocketEngine" << socketDescriptor;
1887 if (socks5BindStore()->contains(socketDescriptor)) {
1888 QSOCKS5_DEBUG << "bind store contains" << socketDescriptor;
1889 return new QSocks5SocketEngine(parent);
1890 }
1891 return nullptr;
1892}
1893
1894QT_END_NAMESPACE
1895
1896#include "moc_qsocks5socketengine_p.cpp"
\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