Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
5
6#include "qtcpsocket.h"
7#include "qudpsocket.h"
8#include "qtcpserver.h"
9#include "qdebug.h"
10#include "qhash.h"
11#include "qqueue.h"
12#include "qdeadlinetimer.h"
13#include "qelapsedtimer.h"
14#include "qmutex.h"
15#include "qthread.h"
16#include "qcoreapplication.h"
17#include "qurl.h"
18#include "qauthenticator.h"
19#include "private/qiodevice_p.h"
20#include "private/qringbuffer_p.h"
21#include <qendian.h>
22#include <qnetworkinterface.h>
23
24#include <QtCore/qpointer.h>
25
26#include <memory>
27
29
30using namespace Qt::StringLiterals;
31using namespace std::chrono_literals;
32
33static const int MaxWriteBufferSize = 128*1024;
34
35//#define QSOCKS5SOCKETLAYER_DEBUG
36
37#define MAX_DATA_DUMP 256
38static constexpr auto Socks5BlockingBindTimeout = 5s;
39
40#define Q_INIT_CHECK(returnValue) do { \
41 if (!d->data) { \
42 return returnValue; \
43 } } while (0)
44
45#define S5_VERSION_5 0x05
46#define S5_CONNECT 0x01
47#define S5_BIND 0x02
48#define S5_UDP_ASSOCIATE 0x03
49#define S5_IP_V4 0x01
50#define S5_DOMAINNAME 0x03
51#define S5_IP_V6 0x04
52#define S5_SUCCESS 0x00
53#define S5_R_ERROR_SOCKS_FAILURE 0x01
54#define S5_R_ERROR_CON_NOT_ALLOWED 0x02
55#define S5_R_ERROR_NET_UNREACH 0x03
56#define S5_R_ERROR_HOST_UNREACH 0x04
57#define S5_R_ERROR_CONN_REFUSED 0x05
58#define S5_R_ERROR_TTL 0x06
59#define S5_R_ERROR_CMD_NOT_SUPPORTED 0x07
60#define S5_R_ERROR_ADD_TYPE_NOT_SUPORTED 0x08
61
62#define S5_AUTHMETHOD_NONE 0x00
63#define S5_AUTHMETHOD_PASSWORD 0x02
64#define S5_AUTHMETHOD_NOTACCEPTABLE 0xFF
65
66#define S5_PASSWORDAUTH_VERSION 0x01
67
68#ifdef QSOCKS5SOCKETLAYER_DEBUG
69# define QSOCKS5_Q_DEBUG qDebug() << this
70# define QSOCKS5_D_DEBUG qDebug() << q_ptr
71# define QSOCKS5_DEBUG qDebug() << "[QSocks5]"
73{
74 switch (s) {
75 case QSocks5SocketEnginePrivate::Uninitialized: return "Uninitialized"_L1;
76 case QSocks5SocketEnginePrivate::ConnectError: return "ConnectError"_L1;
77 case QSocks5SocketEnginePrivate::AuthenticationMethodsSent: return "AuthenticationMethodsSent"_L1;
78 case QSocks5SocketEnginePrivate::Authenticating: return "Authenticating"_L1;
79 case QSocks5SocketEnginePrivate::AuthenticatingError: return "AuthenticatingError"_L1;
80 case QSocks5SocketEnginePrivate::RequestMethodSent: return "RequestMethodSent"_L1;
81 case QSocks5SocketEnginePrivate::RequestError: return "RequestError"_L1;
82 case QSocks5SocketEnginePrivate::Connected: return "Connected"_L1;
83 case QSocks5SocketEnginePrivate::UdpAssociateSuccess: return "UdpAssociateSuccess"_L1;
84 case QSocks5SocketEnginePrivate::BindSuccess: return "BindSuccess"_L1;
85 case QSocks5SocketEnginePrivate::ControlSocketError: return "ControlSocketError"_L1;
86 case QSocks5SocketEnginePrivate::SocksError: return "SocksError"_L1;
87 case QSocks5SocketEnginePrivate::HostNameLookupError: return "HostNameLookupError"_L1;
88 default: break;
89 }
90 return "unknown state"_L1;
91}
92
93static QString dump(const QByteArray &buf)
94{
96 for (int i = 0; i < qMin<int>(MAX_DATA_DUMP, buf.size()); ++i) {
97 if (i) data += u' ';
98 uint val = (unsigned char)buf.at(i);
99 // data += QString("0x%1").arg(val, 3, 16, u'0');
101 }
102 if (buf.size() > MAX_DATA_DUMP)
103 data += " ..."_L1;
104
105 return QString::fromLatin1("size: %1 data: { %2 }").arg(buf.size()).arg(data);
106}
107
108#else
109# define QSOCKS5_DEBUG if (0) qDebug()
110# define QSOCKS5_Q_DEBUG if (0) qDebug()
111# define QSOCKS5_D_DEBUG if (0) qDebug()
112
114static inline QString dump(const QByteArray &) { return QString(); }
115#endif
116
117/*
118 inserts the host address in buf at pos and updates pos.
119 if the func fails the data in buf and the value of pos is undefined
120*/
122{
123 QSOCKS5_DEBUG << "setting [" << address << ':' << port << ']';
124
125 union {
127 quint32 ipv4;
128 QIPv6Address ipv6;
129 char ptr;
130 } data;
131
132 // add address
133 if (address.protocol() == QAbstractSocket::IPv4Protocol) {
134 data.ipv4 = qToBigEndian<quint32>(address.toIPv4Address());
135 pBuf->append(S5_IP_V4);
136 pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv4));
137 } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
138 data.ipv6 = address.toIPv6Address();
139 pBuf->append(S5_IP_V6);
140 pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv6));
141 } else {
142 return false;
143 }
144
145 // add port
146 data.port = qToBigEndian<quint16>(port);
147 pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
148 return true;
149}
150
151/*
152 like above, but for a hostname
153*/
155{
156 QSOCKS5_DEBUG << "setting [" << hostname << ':' << port << ']';
157
158 QByteArray encodedHostName = QUrl::toAce(hostname);
159 QByteArray &buf = *pBuf;
160
161 if (encodedHostName.size() > 255)
162 return false;
163
165 buf.append(uchar(encodedHostName.size()));
166 buf.append(encodedHostName);
167
168 // add port
169 union {
171 char ptr;
172 } data;
173 data.port = qToBigEndian<quint16>(port);
174 buf.append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
175
176 return true;
177}
178
179
180/*
181 retrieves the host address in buf at pos and updates pos.
182 return 1 if OK, 0 if need more data, -1 if error
183 if the func fails the value of the address and the pos is undefined
184*/
185static int qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddress *pAddress, quint16 *pPort, int *pPos)
186{
187 int ret = -1;
188 int pos = *pPos;
189 const unsigned char *pBuf = reinterpret_cast<const unsigned char*>(buf.constData());
191 quint16 port = 0;
192
193 if (buf.size() - pos < 1) {
194 QSOCKS5_DEBUG << "need more data address/port";
195 return 0;
196 }
197 if (pBuf[pos] == S5_IP_V4) {
198 pos++;
199 if (buf.size() - pos < 4) {
200 QSOCKS5_DEBUG << "need more data for ip4 address";
201 return 0;
202 }
203 address.setAddress(qFromBigEndian<quint32>(&pBuf[pos]));
204 pos += 4;
205 ret = 1;
206 } else if (pBuf[pos] == S5_IP_V6) {
207 pos++;
208 if (buf.size() - pos < 16) {
209 QSOCKS5_DEBUG << "need more data for ip6 address";
210 return 0;
211 }
212 QIPv6Address add;
213 for (int i = 0; i < 16; ++i)
214 add[i] = buf[pos++];
215 address.setAddress(add);
216 ret = 1;
217 } else if (pBuf[pos] == S5_DOMAINNAME){
218 // just skip it
219 pos++;
220 qDebug() << "skipping hostname of len" << uint(pBuf[pos]);
221 pos += uchar(pBuf[pos]);
222 } else {
223 QSOCKS5_DEBUG << "invalid address type" << (int)pBuf[pos];
224 ret = -1;
225 }
226
227 if (ret == 1) {
228 if (buf.size() - pos < 2) {
229 QSOCKS5_DEBUG << "need more data for port";
230 return 0;
231 }
232 port = qFromBigEndian<quint16>(&pBuf[pos]);
233 pos += 2;
234 }
235
236 if (ret == 1) {
237 QSOCKS5_DEBUG << "got [" << address << ':' << port << ']';
238 *pAddress = address;
239 *pPort = port;
240 *pPos = pos;
241 }
242
243 return ret;
244}
245
251
256
265
272
273#ifndef QT_NO_UDPSOCKET
281#endif
282
283// needs to be thread safe
285{
286public:
289
290 void add(qintptr socketDescriptor, QSocks5BindData *bindData);
291 bool contains(qintptr socketDescriptor);
292 QSocks5BindData *retrieve(qintptr socketDescriptor);
293
294protected:
295 void timerEvent(QTimerEvent * event) override;
296
298 int sweepTimerId = -1;
299 //socket descriptor, data, timestamp
300 QHash<qintptr, QSocks5BindData *> store;
301};
302
303Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore)
304
311
315
316void QSocks5BindStore::add(qintptr socketDescriptor, QSocks5BindData *bindData)
317{
319 if (store.contains(socketDescriptor)) {
320 // qDebug("delete it");
321 }
322 bindData->timeStamp.start();
323 store.insert(socketDescriptor, bindData);
324
325 // start sweep timer if not started
326 if (sweepTimerId == -1)
327 sweepTimerId = startTimer(1min);
328}
329
331{
333 return store.contains(socketDescriptor);
334}
335
337{
339 const auto it = store.constFind(socketDescriptor);
340 if (it == store.cend())
341 return nullptr;
342 QSocks5BindData *bindData = it.value();
343 store.erase(it);
344 if (bindData) {
345 if (bindData->controlSocket->thread() != QThread::currentThread()) {
346 qWarning("Cannot access socks5 bind data from different thread");
347 return nullptr;
348 }
349 } else {
350 QSOCKS5_DEBUG << "__ERROR__ binddata == 0";
351 }
352 // stop the sweep timer if not needed
353 if (store.isEmpty()) {
355 sweepTimerId = -1;
356 }
357 return bindData;
358}
359
361{
363 if (event->timerId() == sweepTimerId) {
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
390{
392 *completed = true;
393 return true;
394}
395
397{
399 *completed = true;
400 return true;
401}
402
404{
405 *sealedBuf = buf;
406 return true;
407}
408
410{
411 *buf = sealedBuf;
412 return true;
413}
414
416{
417 return unSeal(sealedSocket->readAll(), buf);
418}
419
421{
422 this->userName = userName;
423 this->password = password;
424}
425
427{
428 return 0x02;
429}
430
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;
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
449{
450 *completed = false;
451
452 if (socket->bytesAvailable() < 2)
453 return true;
454
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
474 : socks5State(Uninitialized)
475 , readNotificationEnabled(false)
476 , writeNotificationEnabled(false)
477 , exceptNotificationEnabled(false)
478 , socketDescriptor(-1)
479 , data(nullptr)
480 , connectData(nullptr)
481#ifndef QT_NO_UDPSOCKET
482 , udpData(nullptr)
483#endif
484 , bindData(nullptr)
485 , readNotificationActivated(false)
486 , writeNotificationActivated(false)
487 , readNotificationPending(false)
488 , writeNotificationPending(false)
489 , connectionNotificationPending(false)
490{
491 mode = NoMode;
492}
493
497
499{
501
502 mode = socks5Mode;
503 if (mode == ConnectMode) {
506#ifndef QT_NO_UDPSOCKET
507 } else if (mode == UdpAssociateMode) {
509 data = udpData;
515#endif // QT_NO_UDPSOCKET
516 } else if (mode == BindMode) {
518 data = bindData;
519 }
520
522 data->controlSocket->setProxy(QNetworkProxy::NoProxy);
525 QObject::connect(data->controlSocket, SIGNAL(readyRead()), q, SLOT(_q_controlSocketReadNotification()),
529 QObject::connect(data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
532 QObject::connect(data->controlSocket, SIGNAL(disconnected()), q, SLOT(_q_controlSocketDisconnected()),
534 QObject::connect(data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
537
538 if (!proxyInfo.user().isEmpty() || !proxyInfo.password().isEmpty()) {
539 QSOCKS5_D_DEBUG << "using username/password authentication; user =" << proxyInfo.user();
541 } else {
542 QSOCKS5_D_DEBUG << "not using authentication";
543 data->authenticator = new QSocks5Authenticator();
544 }
545}
546
548{
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) {
569 QSocks5SocketEngine::tr("Connection to proxy refused"));
570 break;
573 QSocks5SocketEngine::tr("Connection to proxy closed prematurely"));
574 break;
577 QSocks5SocketEngine::tr("Proxy host not found"));
578 break;
580 if (state == ConnectError) {
582 QSocks5SocketEngine::tr("Connection to proxy timed out"));
583 break;
584 }
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
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:
609 QSocks5SocketEngine::tr("SOCKS version 5 protocol error"));
610 break;
611
614 QAbstractSocket::tr("Host not found"));
615 break;
616 }
617
620}
621
623{
625 switch (socks5error) {
626 case SocksFailure:
628 QSocks5SocketEngine::tr("General SOCKSv5 server failure"));
629 break;
632 QSocks5SocketEngine::tr("Connection not allowed by SOCKSv5 server"));
633 break;
636 QAbstractSocket::tr("Network unreachable"));
637 break;
638 case HostUnreachable:
640 QAbstractSocket::tr("Host not found"));
641 break;
644 QAbstractSocket::tr("Connection refused"));
645 break;
646 case TTLExpired:
648 QSocks5SocketEngine::tr("TTL expired"));
649 break;
652 QSocks5SocketEngine::tr("SOCKSv5 command not supported"));
653 break;
656 QSocks5SocketEngine::tr("Address type not supported"));
657 break;
658
659 default:
661 QSocks5SocketEngine::tr("Unknown SOCKSv5 proxy error code 0x%1").arg(int(socks5error), 16));
662 break;
663 }
664
666}
667
669{
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());
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()
722 || !data->authenticator->beginAuthenticate(data->controlSocket, &authComplete)) {
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;
738 if (!data->authenticator->continueAuthenticate(data->controlSocket, &authComplete)) {
740 return;
741 }
742 if (authComplete)
744}
745
747{
749 quint16 port = 0;
750 char command = 0;
751 if (mode == ConnectMode) {
752 command = S5_CONNECT;
754 port = peerPort;
755 } else if (mode == BindMode) {
756 command = S5_BIND;
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
768 buf.reserve(270); // big enough for domain name;
769 buf.append(char(S5_VERSION_5));
770 buf.append(command);
771 buf.append('\0');
773 QSOCKS5_DEBUG << "error setting address" << address << " : " << port;
774 //### set error code ....
775 return;
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{
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);
805 QSOCKS5_DEBUG << dump(inBuf);
806 if (inBuf.size() < 3) {
807 QSOCKS5_DEBUG << "need more data for request reply header .. put this data somewhere";
809 return;
810 }
811
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 {
826 setErrorState(RequestError, socks5Error);
827 }
828 } else {
829 // connection success, retrieve the remote addresses
830 int pos = 3;
832 if (err == -1) {
833 QSOCKS5_DEBUG << "error getting address";
835 } else if (err == 0) {
836 //need more data
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
849 localPort = port;
850
851 if (mode == ConnectMode) {
854 // notify the upper layer that we're done
857 } else if (mode == BindMode) {
860 } else {
862 }
863 } else if (socks5State == BindSuccess) {
864 // no error and we got a connection
867
869 } else {
870 // got an error
871 data->controlSocket->close();
873 }
874}
875
877{
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) {
891 }
892 }
893}
894
896{
900 QSOCKS5_D_DEBUG << "queueing readNotification";
902 QMetaObject::invokeMethod(q, "_q_emitPendingReadNotification", Qt::QueuedConnection);
903 }
904}
905
907{
911 QSOCKS5_D_DEBUG << "emitting writeNotification";
912 q->writeNotification();
913 }
914}
915
917{
921 QSOCKS5_D_DEBUG << "queueing writeNotification";
923 QMetaObject::invokeMethod(q, "_q_emitPendingWriteNotification", Qt::QueuedConnection);
924 }
925}
926
928{
931 QSOCKS5_D_DEBUG << "emitting connectionNotification";
932 q->connectionNotification();
933}
934
936{
938 QSOCKS5_D_DEBUG << "queueing connectionNotification";
940 QMetaObject::invokeMethod(q, "_q_emitPendingConnectionNotification", Qt::QueuedConnection);
941}
942
947
949{
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{
971 return 1 + counter.fetchAndAddRelaxed(1);
972}
973
975{
977
978 d->socketDescriptor = nextDescriptor();
979
980 d->socketType = type;
981 d->socketProtocol = protocol;
982
983 return true;
984}
985
987{
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;
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()),
1022 QObject::connect(d->data->controlSocket, SIGNAL(readyRead()), this, SLOT(_q_controlSocketReadNotification()),
1024 QObject::connect(d->data->controlSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_controlSocketBytesWritten()),
1026 QObject::connect(d->data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketErrorOccurred(QAbstractSocket::SocketError)),
1028 QObject::connect(d->data->controlSocket, SIGNAL(disconnected()), this, SLOT(_q_controlSocketDisconnected()),
1030 QObject::connect(d->data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
1031 this, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
1033
1035
1036 if (d->data->controlSocket->bytesAvailable() != 0)
1037 d->_q_controlSocketReadNotification();
1038 return true;
1039 }
1040 return false;
1041}
1042
1044{
1046 d->proxyInfo = networkProxy;
1047}
1048
1050{
1051 Q_D(const QSocks5SocketEngine);
1052 return d->socketDescriptor;
1053}
1054
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
1063}
1064
1066{
1068
1069 if (!d->data) {
1072#ifndef QT_NO_UDPSOCKET
1073 } else if (socketType() == QAbstractSocket::UdpSocket) {
1075 // all udp needs to be bound
1076 if (!bind(QHostAddress("0.0.0.0"_L1), 0))
1077 return false;
1078
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) {
1090 // We may have new auth credentials since an earlier failure:
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
1104{
1106 QSOCKS5_DEBUG << "connectToHost" << address << ':' << port;
1107
1110 d->peerName.clear();
1111
1112 return connectInternal();
1113}
1114
1116{
1118
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;
1132 buf[2] = data->authenticator->methodId();
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: {
1161 if (!data->authenticator->unSeal(data->controlSocket, &buf)) {
1162 // qDebug("unseal error maybe need to wait for more data");
1163 }
1164 if (buf.size()) {
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
1202{
1203 QSOCKS5_D_DEBUG << "controlSocketError" << error << data->controlSocket->errorString();
1204
1206 return; // ignore this error -- comes from the waitFor* functions
1207
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
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
1241
1242#ifndef QT_NO_UDPSOCKET
1244{
1245 QSOCKS5_D_DEBUG << "_q_udpSocketReadNotification()";
1246
1247 // check some state stuff
1249 QSOCKS5_D_DEBUG << "false read ??";
1250 return;
1251 }
1252
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
1290{
1292
1293 // when bind we will block until the bind is finished as the info from the proxy server is needed
1294
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) {
1304#ifndef QT_NO_UDPSOCKET
1305 } else if (socketType() == QAbstractSocket::UdpSocket) {
1307#endif
1308 } else {
1309 //### something invalid
1310 return false;
1311 }
1312 }
1313
1314#ifndef QT_NO_UDPSOCKET
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
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) {
1342 return true;
1343#ifndef QT_NO_UDPSOCKET
1344 } else if (d->socks5State == QSocks5SocketEnginePrivate::UdpAssociateSuccess) {
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
1356 QLatin1StringView(QT_TRANSLATE_NOOP("QSocks5SocketEngine", "Network operation timed out")));
1357
1360 return false;
1361}
1362
1363
1365{
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
1385{
1387 // check we are listing ---
1388
1389 QSOCKS5_Q_DEBUG << "accept()";
1390
1391 qintptr sd = -1;
1392 switch (d->socks5State) {
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;
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
1420{
1421 QSOCKS5_Q_DEBUG << "close()";
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
1441{
1442 Q_D(const QSocks5SocketEngine);
1444 return d->connectData->readBuffer.size();
1445#ifndef QT_NO_UDPSOCKET
1447 && !d->udpData->pendingDatagrams.isEmpty())
1448 return d->udpData->pendingDatagrams.constFirst().data.size();
1449#endif
1450 return 0;
1451}
1452
1454{
1456 QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ')';
1458 if (d->connectData->readBuffer.isEmpty()) {
1459 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1460 //imitate remote closed
1461 close();
1463 "Remote host closed connection"_L1);
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
1482{
1484 QSOCKS5_Q_DEBUG << "write" << dump(QByteArray(data, len));
1485
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
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
1523 const QNetworkInterface &)
1524{
1526 "Operation on socket is not supported"_L1);
1527 return false;
1528}
1529
1531 const QNetworkInterface &)
1532{
1534 "Operation on socket is not supported"_L1);
1535 return false;
1536}
1537
1538
1543
1545{
1547 "Operation on socket is not supported"_L1);
1548 return false;
1549}
1550#endif // QT_NO_NETWORKINTERFACE
1551
1553{
1554 Q_D(const QSocks5SocketEngine);
1555 Q_INIT_CHECK(false);
1556
1557 return !d->udpData->pendingDatagrams.isEmpty();
1558}
1559
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
1571{
1572#ifndef QT_NO_UDPSOCKET
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);
1590 return -1;
1591#endif // QT_NO_UDPSOCKET
1592}
1593
1595{
1596#ifndef QT_NO_UDPSOCKET
1598
1599 // it is possible to send with out first binding with udp, but socks5 requires a bind.
1600 if (!d->data) {
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);
1639 return -1;
1640#endif // QT_NO_UDPSOCKET
1641}
1642
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
1654{
1655 Q_D(const QSocks5SocketEngine);
1656 if (d->data && d->data->controlSocket) {
1657 // convert the enum and call the real socket
1659 return d->data->controlSocket->socketOption(QAbstractSocket::LowDelayOption).toInt();
1661 return d->data->controlSocket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
1662 }
1663 return -1;
1664}
1665
1667{
1669 if (d->data && d->data->controlSocket) {
1670 // convert the enum and call the real socket
1672 d->data->controlSocket->setSocketOption(QAbstractSocket::LowDelayOption, value);
1674 d->data->controlSocket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
1675 return true;
1676 }
1677 return false;
1678}
1679
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
1706{
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
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
1760{
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
1806{
1807 Q_D(const QSocks5SocketEngine);
1808 return d->readNotificationEnabled;
1809}
1810
1812{
1814
1815 QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ')';
1816
1817 bool emitSignal = false;
1818 if (!d->readNotificationEnabled
1819 && enable) {
1821 emitSignal = !d->connectData->readBuffer.isEmpty();
1822#ifndef QT_NO_UDPSOCKET
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
1839{
1840 Q_D(const QSocks5SocketEngine);
1841 return d->writeNotificationEnabled;
1842}
1843
1845{
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
1857{
1858 Q_D(const QSocks5SocketEngine);
1859 return d->exceptNotificationEnabled;
1860}
1861
1863{
1865 d->exceptNotificationEnabled = enable;
1866}
1867
1870 const QNetworkProxy &proxy, QObject *parent)
1871{
1873
1874 // proxy type must have been resolved by now
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
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
1895
1896#include "moc_qsocks5socketengine_p.cpp"
bool connected
QAbstractSocket::SocketError socketError
QAbstractSocket::NetworkLayerProtocol protocol() const
void setPeerAddress(const QHostAddress &address)
void setState(QAbstractSocket::SocketState state)
QAbstractSocket::SocketType socketType() const
void setError(QAbstractSocket::SocketError error, const QString &errorString) const
SocketState
This enum describes the different states in which a socket can be.
static constexpr auto IPv4Protocol
qint64 bytesAvailable() const override
Returns the number of incoming bytes that are waiting to be read.
void setProxy(const QNetworkProxy &networkProxy)
static constexpr auto AnyIPProtocol
void close() override
Closes the I/O device for the socket and calls disconnectFromHost() to close the socket's connection.
SocketError
This enum describes the socket errors that can occur.
static constexpr auto IPv6Protocol
SocketType
This enum describes the transport layer protocol.
The QAuthenticator class provides an authentication object.
T fetchAndAddRelaxed(T valueToAdd) noexcept
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
void reserve(qsizetype size)
Attempts to allocate memory for at least size bytes.
Definition qbytearray.h:634
void clear()
Clears the contents of the byte array and makes it null.
QByteArray & remove(qsizetype index, qsizetype len)
Removes len bytes from the array, starting at index position pos, and returns a reference to the arra...
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:409
\inmodule QtCore
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
\inmodule QtCore
qint64 remainingTime() const noexcept
Returns the remaining time in this QDeadlineTimer object in milliseconds.
std::chrono::nanoseconds remainingTimeAsDuration() const noexcept
Returns the time remaining before the deadline.
\inmodule QtCore
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1212
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
iterator erase(const_iterator it)
Definition qhash.h:1233
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
const_iterator cend() const noexcept
Definition qhash.h:1218
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:928
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
The QHostAddress class provides an IP address.
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
\inmodule QtCore
Definition qmutex.h:313
The QNetworkInterface class provides a listing of the host's IP addresses and network interfaces.
The QNetworkProxy class provides a network layer proxy.
QString user() const
Returns the user name used for authentication.
void setUser(const QString &userName)
Sets the user name for proxy authentication to be user.
QNetworkProxy::ProxyType type() const
Returns the proxy type for this instance.
QString password() const
Returns the password used for authentication.
QString hostName() const
Returns the host name of the proxy host.
void setPassword(const QString &password)
Sets the password for proxy authentication to be password.
quint16 port() const
Returns the port of the proxy host.
\inmodule QtCore
Definition qobject.h:103
int startTimer(int interval, Qt::TimerType timerType=Qt::CoarseTimer)
This is an overloaded function that will start a timer of type timerType and a timeout of interval mi...
Definition qobject.cpp:1817
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void setParent(QObject *parent)
Makes the object a child of parent.
Definition qobject.cpp:2195
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
void killTimer(int id)
Kills the timer with timer identifier, id.
Definition qobject.cpp:1912
void enqueue(const T &t)
Adds value t to the tail of the queue.
Definition qqueue.h:18
\inmodule QtCore
Definition qmutex.h:309
Q_CORE_EXPORT void append(const char *data, qint64 size)
Q_CORE_EXPORT void clear()
Exception-safe wrapper around QObject::blockSignals().
Definition qobject.h:483
virtual bool beginAuthenticate(QTcpSocket *socket, bool *completed)
virtual bool continueAuthenticate(QTcpSocket *socket, bool *completed)
bool seal(const QByteArray &buf, QByteArray *sealedBuf)
bool unSeal(const QByteArray &sealedBuf, QByteArray *buf)
void add(qintptr socketDescriptor, QSocks5BindData *bindData)
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)
QHash< qintptr, QSocks5BindData * > store
bool beginAuthenticate(QTcpSocket *socket, bool *completed) override
bool continueAuthenticate(QTcpSocket *socket, bool *completed) override
QSocks5PasswordAuthenticator(const QString &userName, const QString &password)
virtual QAbstractSocketEngine * createSocketEngine(QAbstractSocket::SocketType socketType, const QNetworkProxy &, QObject *parent) override
QSocks5UdpAssociateData * udpData
void setErrorState(Socks5State state, const QString &extraMessage=QString())
void _q_controlSocketStateChanged(QAbstractSocket::SocketState)
void _q_controlSocketErrorOccurred(QAbstractSocket::SocketError)
bool waitForConnected(QDeadlineTimer deadline, bool *timedOut)
void initialize(Socks5Mode socks5Mode)
bool waitForWrite(QDeadlineTimer deadline=QDeadlineTimer{DefaultTimeout}, bool *timedOut=nullptr) override
qint64 pendingDatagramSize() const override
bool isExceptionNotificationEnabled() const override
void setExceptionNotificationEnabled(bool enable) override
bool setOption(SocketOption option, int value) override
bool joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &interface) override
bool listen(int backlog) override
bool isReadNotificationEnabled() const override
qintptr socketDescriptor() const override
qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *=nullptr, PacketHeaderOptions=WantNone) override
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, QDeadlineTimer deadline=QDeadlineTimer{DefaultTimeout}, bool *timedOut=nullptr) override
bool isWriteNotificationEnabled() const override
void setProxy(const QNetworkProxy &networkProxy)
bool isValid() const override
bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &interface) override
bool connectToHostByName(const QString &name, quint16 port) override
QNetworkInterface multicastInterface() const override
int option(SocketOption option) const override
qint64 bytesToWrite() const override
bool hasPendingDatagrams() const override
qint64 bytesAvailable() const override
QSocks5SocketEngine(QObject *parent=nullptr)
void setReadNotificationEnabled(bool enable) override
qint64 read(char *data, qint64 maxlen) override
bool setMulticastInterface(const QNetworkInterface &iface) override
bool connectToHost(const QHostAddress &address, quint16 port) override
qint64 write(const char *data, qint64 len) override
bool bind(const QHostAddress &address, quint16 port) override
bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol=QAbstractSocket::IPv4Protocol) override
bool waitForRead(QDeadlineTimer deadline=QDeadlineTimer{DefaultTimeout}, bool *timedOut=nullptr) override
void setWriteNotificationEnabled(bool enable) override
qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) override
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toLatin1() const &
Definition qstring.h:630
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
The QTcpSocket class provides a TCP socket.
Definition qtcpsocket.h:18
static QThread * currentThread()
Definition qthread.cpp:1039
\inmodule QtCore
Definition qcoreevent.h:366
\reentrant
Definition qudpsocket.h:21
bool hasPendingDatagrams() const
Returns true if at least one datagram is waiting to be read; otherwise returns false.
qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host=nullptr, quint16 *port=nullptr)
Receives a datagram no larger than maxSize bytes and stores it in data.
qint64 pendingDatagramSize() const
Returns the size of the first pending UDP datagram.
static QByteArray toAce(const QString &domain, AceProcessingOptions options={})
Definition qurl.cpp:3064
QSet< QString >::iterator it
else opt state
[0]
Combined button and popup list for selecting options.
@ QueuedConnection
@ DirectConnection
static jboolean copy(JNIEnv *, jobject)
#define Q_FALLTHROUGH()
DBusConnection const char DBusError * error
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputPortEXT port
static QT_BEGIN_NAMESPACE const char * socketType(QSocketNotifier::Type type)
constexpr int Uninitialized
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
return ret
static ControlElement< T > * ptr(QWidget *widget)
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLenum GLsizei GLuint GLint * bytesWritten
GLenum mode
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLboolean enable
GLuint counter
struct _cl_event * event
GLdouble s
[6]
Definition qopenglext.h:235
GLuint GLfloat * val
GLenum const void * addr
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint GLenum option
GLenum GLsizei len
static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
#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 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 MAX_DATA_DUMP
#define S5_SUCCESS
static QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State)
static QString dump(const QByteArray &)
#define S5_CONNECT
#define S5_PASSWORDAUTH_VERSION
#define Q_INIT_CHECK(returnValue)
#define S5_AUTHMETHOD_NOTACCEPTABLE
static const int MaxWriteBufferSize
static int nextDescriptor()
#define QSOCKS5_D_DEBUG
#define S5_UDP_ASSOCIATE
#define QSOCKS5_DEBUG
static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 port, QByteArray *pBuf)
static constexpr auto Socks5BlockingBindTimeout
SSL_CTX int void * arg
#define Q_UNUSED(x)
#define QT_TRANSLATE_NOOP(scope, x)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
unsigned short quint16
Definition qtypes.h:48
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
ptrdiff_t qintptr
Definition qtypes.h:166
QDeadlineTimer deadline(30s)
QObject::connect nullptr
QTcpSocket * socket
[1]
myObject moveToThread(QApplication::instance() ->thread())
[6]
QReadWriteLock lock
[0]
QApplication app(argc, argv)
[0]
QNetworkProxy proxy
[0]
socketLayer waitForWrite()
QJSEngine engine
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
QTcpSocket * controlSocket
QSocks5Authenticator * authenticator
QQueue< QSocks5RevivedDatagram > pendingDatagrams