Qt 6.x
The Qt SDK
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 "qelapsedtimer.h"
13#include "qmutex.h"
14#include "qthread.h"
15#include "qcoreapplication.h"
16#include "qurl.h"
17#include "qauthenticator.h"
18#include "private/qiodevice_p.h"
19#include "private/qringbuffer_p.h"
20#include <qendian.h>
21#include <qnetworkinterface.h>
22
23#include <memory>
24
26
27using namespace Qt::StringLiterals;
28
29static const int MaxWriteBufferSize = 128*1024;
30
31//#define QSOCKS5SOCKETLAYER_DEBUG
32
33#define MAX_DATA_DUMP 256
34#define SOCKS5_BLOCKING_BIND_TIMEOUT 5000
35
36#define Q_INIT_CHECK(returnValue) do { \
37 if (!d->data) { \
38 return returnValue; \
39 } } while (0)
40
41#define S5_VERSION_5 0x05
42#define S5_CONNECT 0x01
43#define S5_BIND 0x02
44#define S5_UDP_ASSOCIATE 0x03
45#define S5_IP_V4 0x01
46#define S5_DOMAINNAME 0x03
47#define S5_IP_V6 0x04
48#define S5_SUCCESS 0x00
49#define S5_R_ERROR_SOCKS_FAILURE 0x01
50#define S5_R_ERROR_CON_NOT_ALLOWED 0x02
51#define S5_R_ERROR_NET_UNREACH 0x03
52#define S5_R_ERROR_HOST_UNREACH 0x04
53#define S5_R_ERROR_CONN_REFUSED 0x05
54#define S5_R_ERROR_TTL 0x06
55#define S5_R_ERROR_CMD_NOT_SUPPORTED 0x07
56#define S5_R_ERROR_ADD_TYPE_NOT_SUPORTED 0x08
57
58#define S5_AUTHMETHOD_NONE 0x00
59#define S5_AUTHMETHOD_PASSWORD 0x02
60#define S5_AUTHMETHOD_NOTACCEPTABLE 0xFF
61
62#define S5_PASSWORDAUTH_VERSION 0x01
63
64#ifdef QSOCKS5SOCKETLAYER_DEBUG
65# define QSOCKS5_Q_DEBUG qDebug() << this
66# define QSOCKS5_D_DEBUG qDebug() << q_ptr
67# define QSOCKS5_DEBUG qDebug() << "[QSocks5]"
69{
70 switch (s) {
71 case QSocks5SocketEnginePrivate::Uninitialized: return "Uninitialized"_L1;
72 case QSocks5SocketEnginePrivate::ConnectError: return "ConnectError"_L1;
73 case QSocks5SocketEnginePrivate::AuthenticationMethodsSent: return "AuthenticationMethodsSent"_L1;
74 case QSocks5SocketEnginePrivate::Authenticating: return "Authenticating"_L1;
75 case QSocks5SocketEnginePrivate::AuthenticatingError: return "AuthenticatingError"_L1;
76 case QSocks5SocketEnginePrivate::RequestMethodSent: return "RequestMethodSent"_L1;
77 case QSocks5SocketEnginePrivate::RequestError: return "RequestError"_L1;
78 case QSocks5SocketEnginePrivate::Connected: return "Connected"_L1;
79 case QSocks5SocketEnginePrivate::UdpAssociateSuccess: return "UdpAssociateSuccess"_L1;
80 case QSocks5SocketEnginePrivate::BindSuccess: return "BindSuccess"_L1;
81 case QSocks5SocketEnginePrivate::ControlSocketError: return "ControlSocketError"_L1;
82 case QSocks5SocketEnginePrivate::SocksError: return "SocksError"_L1;
83 case QSocks5SocketEnginePrivate::HostNameLookupError: return "HostNameLookupError"_L1;
84 default: break;
85 }
86 return "unknown state"_L1;
87}
88
89static QString dump(const QByteArray &buf)
90{
92 for (int i = 0; i < qMin<int>(MAX_DATA_DUMP, buf.size()); ++i) {
93 if (i) data += u' ';
94 uint val = (unsigned char)buf.at(i);
95 // data += QString("0x%1").arg(val, 3, 16, u'0');
97 }
98 if (buf.size() > MAX_DATA_DUMP)
99 data += " ..."_L1;
100
101 return QString::fromLatin1("size: %1 data: { %2 }").arg(buf.size()).arg(data);
102}
103
104#else
105# define QSOCKS5_DEBUG if (0) qDebug()
106# define QSOCKS5_Q_DEBUG if (0) qDebug()
107# define QSOCKS5_D_DEBUG if (0) qDebug()
108
110static inline QString dump(const QByteArray &) { return QString(); }
111#endif
112
113/*
114 inserts the host address in buf at pos and updates pos.
115 if the func fails the data in buf and the value of pos is undefined
116*/
118{
119 QSOCKS5_DEBUG << "setting [" << address << ':' << port << ']';
120
121 union {
123 quint32 ipv4;
124 QIPv6Address ipv6;
125 char ptr;
126 } data;
127
128 // add address
129 if (address.protocol() == QAbstractSocket::IPv4Protocol) {
130 data.ipv4 = qToBigEndian<quint32>(address.toIPv4Address());
131 pBuf->append(S5_IP_V4);
132 pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv4));
133 } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
134 data.ipv6 = address.toIPv6Address();
135 pBuf->append(S5_IP_V6);
136 pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv6));
137 } else {
138 return false;
139 }
140
141 // add port
142 data.port = qToBigEndian<quint16>(port);
143 pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
144 return true;
145}
146
147/*
148 like above, but for a hostname
149*/
151{
152 QSOCKS5_DEBUG << "setting [" << hostname << ':' << port << ']';
153
154 QByteArray encodedHostName = QUrl::toAce(hostname);
155 QByteArray &buf = *pBuf;
156
157 if (encodedHostName.size() > 255)
158 return false;
159
161 buf.append(uchar(encodedHostName.size()));
162 buf.append(encodedHostName);
163
164 // add port
165 union {
167 char ptr;
168 } data;
169 data.port = qToBigEndian<quint16>(port);
170 buf.append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
171
172 return true;
173}
174
175
176/*
177 retrieves the host address in buf at pos and updates pos.
178 return 1 if OK, 0 if need more data, -1 if error
179 if the func fails the value of the address and the pos is undefined
180*/
181static int qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddress *pAddress, quint16 *pPort, int *pPos)
182{
183 int ret = -1;
184 int pos = *pPos;
185 const unsigned char *pBuf = reinterpret_cast<const unsigned char*>(buf.constData());
187 quint16 port = 0;
188
189 if (buf.size() - pos < 1) {
190 QSOCKS5_DEBUG << "need more data address/port";
191 return 0;
192 }
193 if (pBuf[pos] == S5_IP_V4) {
194 pos++;
195 if (buf.size() - pos < 4) {
196 QSOCKS5_DEBUG << "need more data for ip4 address";
197 return 0;
198 }
199 address.setAddress(qFromBigEndian<quint32>(&pBuf[pos]));
200 pos += 4;
201 ret = 1;
202 } else if (pBuf[pos] == S5_IP_V6) {
203 pos++;
204 if (buf.size() - pos < 16) {
205 QSOCKS5_DEBUG << "need more data for ip6 address";
206 return 0;
207 }
208 QIPv6Address add;
209 for (int i = 0; i < 16; ++i)
210 add[i] = buf[pos++];
211 address.setAddress(add);
212 ret = 1;
213 } else if (pBuf[pos] == S5_DOMAINNAME){
214 // just skip it
215 pos++;
216 qDebug() << "skipping hostname of len" << uint(pBuf[pos]);
217 pos += uchar(pBuf[pos]);
218 } else {
219 QSOCKS5_DEBUG << "invalid address type" << (int)pBuf[pos];
220 ret = -1;
221 }
222
223 if (ret == 1) {
224 if (buf.size() - pos < 2) {
225 QSOCKS5_DEBUG << "need more data for port";
226 return 0;
227 }
228 port = qFromBigEndian<quint16>(&pBuf[pos]);
229 pos += 2;
230 }
231
232 if (ret == 1) {
233 QSOCKS5_DEBUG << "got [" << address << ':' << port << ']';
234 *pAddress = address;
235 *pPort = port;
236 *pPos = pos;
237 }
238
239 return ret;
240}
241
243{
246};
247
249{
251};
252
254{
260};
261
263{
267};
268
269#ifndef QT_NO_UDPSOCKET
271{
276};
277#endif
278
279// needs to be thread safe
281{
282public:
285
286 void add(qintptr socketDescriptor, QSocks5BindData *bindData);
287 bool contains(qintptr socketDescriptor);
288 QSocks5BindData *retrieve(qintptr socketDescriptor);
289
290protected:
291 void timerEvent(QTimerEvent * event) override;
292
294 int sweepTimerId = -1;
295 //socket descriptor, data, timestamp
297};
298
299Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore)
300
302{
304 if (app && app->thread() != thread())
306}
307
309{
310}
311
312void QSocks5BindStore::add(qintptr socketDescriptor, QSocks5BindData *bindData)
313{
315 if (store.contains(socketDescriptor)) {
316 // qDebug("delete it");
317 }
318 bindData->timeStamp.start();
319 store.insert(socketDescriptor, bindData);
320
321 using namespace std::chrono_literals;
322 // start sweep timer if not started
323 if (sweepTimerId == -1)
324 sweepTimerId = startTimer(1min);
325}
326
328{
330 return store.contains(socketDescriptor);
331}
332
334{
336 const auto it = store.constFind(socketDescriptor);
337 if (it == store.cend())
338 return nullptr;
339 QSocks5BindData *bindData = it.value();
340 store.erase(it);
341 if (bindData) {
342 if (bindData->controlSocket->thread() != QThread::currentThread()) {
343 qWarning("Cannot access socks5 bind data from different thread");
344 return nullptr;
345 }
346 } else {
347 QSOCKS5_DEBUG << "__ERROR__ binddata == 0";
348 }
349 // stop the sweep timer if not needed
350 if (store.isEmpty()) {
352 sweepTimerId = -1;
353 }
354 return bindData;
355}
356
358{
360 if (event->timerId() == sweepTimerId) {
361 QSOCKS5_DEBUG << "QSocks5BindStore performing sweep";
362 for (auto it = store.begin(), end = store.end(); it != end;) {
363 if (it.value()->timeStamp.hasExpired(350000)) {
364 QSOCKS5_DEBUG << "QSocks5BindStore removing JJJJ";
365 it = store.erase(it);
366 } else {
367 ++it;
368 }
369 }
370 }
371}
372
374{
375}
376
378{
379}
380
382{
383 return 0x00;
384}
385
387{
389 *completed = true;
390 return true;
391}
392
394{
396 *completed = true;
397 return true;
398}
399
401{
402 *sealedBuf = buf;
403 return true;
404}
405
407{
408 *buf = sealedBuf;
409 return true;
410}
411
413{
414 return unSeal(sealedSocket->readAll(), buf);
415}
416
418{
419 this->userName = userName;
420 this->password = password;
421}
422
424{
425 return 0x02;
426}
427
429{
430 *completed = false;
431 QByteArray uname = userName.toLatin1();
432 QByteArray passwd = password.toLatin1();
433 QByteArray dataBuf(3 + uname.size() + passwd.size(), 0);
434 char *buf = dataBuf.data();
435 int pos = 0;
437 buf[pos++] = uname.size();
438 memcpy(&buf[pos], uname.data(), uname.size());
439 pos += uname.size();
440 buf[pos++] = passwd.size();
441 memcpy(&buf[pos], passwd.data(), passwd.size());
442 return socket->write(dataBuf) == dataBuf.size();
443}
444
446{
447 *completed = false;
448
449 if (socket->bytesAvailable() < 2)
450 return true;
451
453 if (buf.at(0) == S5_PASSWORDAUTH_VERSION && buf.at(1) == 0x00) {
454 *completed = true;
455 return true;
456 }
457
458 // must disconnect
459 socket->close();
460 return false;
461}
462
464{
465 return "Socks5 user name or password incorrect"_L1;
466}
467
468
469
471 : socks5State(Uninitialized)
472 , readNotificationEnabled(false)
473 , writeNotificationEnabled(false)
474 , exceptNotificationEnabled(false)
475 , socketDescriptor(-1)
476 , data(nullptr)
477 , connectData(nullptr)
478#ifndef QT_NO_UDPSOCKET
479 , udpData(nullptr)
480#endif
481 , bindData(nullptr)
482 , readNotificationActivated(false)
483 , writeNotificationActivated(false)
484 , readNotificationPending(false)
485 , writeNotificationPending(false)
486 , connectionNotificationPending(false)
487{
488 mode = NoMode;
489}
490
492{
493}
494
496{
498
499 mode = socks5Mode;
500 if (mode == ConnectMode) {
503#ifndef QT_NO_UDPSOCKET
504 } else if (mode == UdpAssociateMode) {
506 data = udpData;
512#endif // QT_NO_UDPSOCKET
513 } else if (mode == BindMode) {
515 data = bindData;
516 }
517
519 data->controlSocket->setProxy(QNetworkProxy::NoProxy);
522 QObject::connect(data->controlSocket, SIGNAL(readyRead()), q, SLOT(_q_controlSocketReadNotification()),
526 QObject::connect(data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
529 QObject::connect(data->controlSocket, SIGNAL(disconnected()), q, SLOT(_q_controlSocketDisconnected()),
531 QObject::connect(data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
534
535 if (!proxyInfo.user().isEmpty() || !proxyInfo.password().isEmpty()) {
536 QSOCKS5_D_DEBUG << "using username/password authentication; user =" << proxyInfo.user();
538 } else {
539 QSOCKS5_D_DEBUG << "not using authentication";
540 data->authenticator = new QSocks5Authenticator();
541 }
542}
543
545{
547
548 switch (state) {
549 case Uninitialized:
550 case Authenticating:
553 case Connected:
555 case BindSuccess:
556 // these aren't error states
557 return;
558
559 case ConnectError:
560 case ControlSocketError: {
561 QAbstractSocket::SocketError controlSocketError = data->controlSocket->error();
562 if (socks5State != Connected) {
563 switch (controlSocketError) {
566 QSocks5SocketEngine::tr("Connection to proxy refused"));
567 break;
570 QSocks5SocketEngine::tr("Connection to proxy closed prematurely"));
571 break;
574 QSocks5SocketEngine::tr("Proxy host not found"));
575 break;
577 if (state == ConnectError) {
579 QSocks5SocketEngine::tr("Connection to proxy timed out"));
580 break;
581 }
583 default:
584 q->setError(controlSocketError, data->controlSocket->errorString());
585 break;
586 }
587 } else {
588 q->setError(controlSocketError, data->controlSocket->errorString());
589 }
590 break;
591 }
592
595 extraMessage.isEmpty() ?
596 QSocks5SocketEngine::tr("Proxy authentication failed") :
597 QSocks5SocketEngine::tr("Proxy authentication failed: %1").arg(extraMessage));
598 break;
599
600 case RequestError:
601 // error code set by caller (overload)
602 break;
603
604 case SocksError:
606 QSocks5SocketEngine::tr("SOCKS version 5 protocol error"));
607 break;
608
611 QAbstractSocket::tr("Host not found"));
612 break;
613 }
614
617}
618
620{
622 switch (socks5error) {
623 case SocksFailure:
625 QSocks5SocketEngine::tr("General SOCKSv5 server failure"));
626 break;
629 QSocks5SocketEngine::tr("Connection not allowed by SOCKSv5 server"));
630 break;
633 QAbstractSocket::tr("Network unreachable"));
634 break;
635 case HostUnreachable:
637 QAbstractSocket::tr("Host not found"));
638 break;
641 QAbstractSocket::tr("Connection refused"));
642 break;
643 case TTLExpired:
645 QSocks5SocketEngine::tr("TTL expired"));
646 break;
649 QSocks5SocketEngine::tr("SOCKSv5 command not supported"));
650 break;
653 QSocks5SocketEngine::tr("Address type not supported"));
654 break;
655
656 default:
658 QSocks5SocketEngine::tr("Unknown SOCKSv5 proxy error code 0x%1").arg(int(socks5error), 16));
659 break;
660 }
661
663}
664
666{
668
669 // we require authentication
670 QAuthenticator auth;
671 q->proxyAuthenticationRequired(proxyInfo, &auth);
672
673 if (!auth.user().isEmpty() || !auth.password().isEmpty()) {
674 // we have new credentials, let's try again
675 QSOCKS5_DEBUG << "authentication failure: retrying connection";
677
678 delete data->authenticator;
679 proxyInfo.setUser(auth.user());
682
683 {
684 const QSignalBlocker blocker(data->controlSocket);
685 data->controlSocket->abort();
686 }
687 data->controlSocket->connectToHost(proxyInfo.hostName(), proxyInfo.port());
688 } else {
689 // authentication failure
690
692 data->controlSocket->close();
694 }
695}
696
698{
699 // not enough data to begin
700 if (data->controlSocket->bytesAvailable() < 2)
701 return;
702
703 QByteArray buf = data->controlSocket->read(2);
704 if (buf.at(0) != S5_VERSION_5) {
705 QSOCKS5_D_DEBUG << "Socks5 version incorrect";
707 data->controlSocket->close();
709 return;
710 }
711
712 bool authComplete = false;
713 if (uchar(buf.at(1)) == S5_AUTHMETHOD_NONE) {
714 authComplete = true;
715 } else if (uchar(buf.at(1)) == S5_AUTHMETHOD_NOTACCEPTABLE) {
717 return;
718 } else if (buf.at(1) != data->authenticator->methodId()
719 || !data->authenticator->beginAuthenticate(data->controlSocket, &authComplete)) {
720 setErrorState(AuthenticatingError, "Socks5 host did not support authentication method."_L1);
721 socketError = QAbstractSocket::SocketAccessError; // change the socket error
723 return;
724 }
725
726 if (authComplete)
728 else
730}
731
733{
734 bool authComplete = false;
735 if (!data->authenticator->continueAuthenticate(data->controlSocket, &authComplete)) {
737 return;
738 }
739 if (authComplete)
741}
742
744{
746 quint16 port = 0;
747 char command = 0;
748 if (mode == ConnectMode) {
749 command = S5_CONNECT;
751 port = peerPort;
752 } else if (mode == BindMode) {
753 command = S5_BIND;
755 port = localPort;
756 } else {
757#ifndef QT_NO_UDPSOCKET
758 command = S5_UDP_ASSOCIATE;
759 address = localAddress; //data->controlSocket->localAddress();
760 port = localPort;
761#endif
762 }
763
765 buf.reserve(270); // big enough for domain name;
766 buf.append(char(S5_VERSION_5));
767 buf.append(command);
768 buf.append('\0');
770 QSOCKS5_DEBUG << "error setting address" << address << " : " << port;
771 //### set error code ....
772 return;
774 QSOCKS5_DEBUG << "error setting peer name" << peerName << " : " << port;
775 //### set error code ....
776 return;
777 }
778 QSOCKS5_DEBUG << "sending" << dump(buf);
779 QByteArray sealedBuf;
780 if (!data->authenticator->seal(buf, &sealedBuf)) {
781 // ### Handle this error.
782 }
783 data->controlSocket->write(sealedBuf);
784 data->controlSocket->flush();
786}
787
789{
791 QSOCKS5_DEBUG << "parseRequestMethodReply()";
792
793 QByteArray inBuf;
794 if (!data->authenticator->unSeal(data->controlSocket, &inBuf)) {
795 // ### check error and not just not enough data
796 QSOCKS5_DEBUG << "unSeal failed, needs more data";
797 return;
798 }
799
802 QSOCKS5_DEBUG << dump(inBuf);
803 if (inBuf.size() < 3) {
804 QSOCKS5_DEBUG << "need more data for request reply header .. put this data somewhere";
806 return;
807 }
808
810 quint16 port = 0;
811
812 if (inBuf.at(0) != S5_VERSION_5 || inBuf.at(2) != 0x00) {
813 QSOCKS5_DEBUG << "socks protocol error";
815 } else if (inBuf.at(1) != S5_SUCCESS) {
816 Socks5Error socks5Error = Socks5Error(inBuf.at(1));
817 QSOCKS5_DEBUG << "Request error :" << socks5Error;
818 if ((socks5Error == SocksFailure || socks5Error == ConnectionNotAllowed)
819 && !peerName.isEmpty()) {
820 // Dante seems to use this error code to indicate hostname resolution failure
822 } else {
823 setErrorState(RequestError, socks5Error);
824 }
825 } else {
826 // connection success, retrieve the remote addresses
827 int pos = 3;
829 if (err == -1) {
830 QSOCKS5_DEBUG << "error getting address";
832 } else if (err == 0) {
833 //need more data
835 return;
836 } else {
837 inBuf.remove(0, pos);
838 for (int i = inBuf.size() - 1; i >= 0 ; --i)
839 data->controlSocket->ungetChar(inBuf.at(i));
840 }
841 }
842
844 // no error
846 localPort = port;
847
848 if (mode == ConnectMode) {
851 // notify the upper layer that we're done
854 } else if (mode == BindMode) {
857 } else {
859 }
860 } else if (socks5State == BindSuccess) {
861 // no error and we got a connection
864
866 } else {
867 // got an error
868 data->controlSocket->close();
870 }
871}
872
874{
878 QSOCKS5_D_DEBUG << "emitting readNotification";
880 q->readNotification();
881 if (!qq)
882 return;
883 // check if there needs to be a new zero read notification
884 if (data && data->controlSocket->state() == QAbstractSocket::UnconnectedState
885 && data->controlSocket->error() == QAbstractSocket::RemoteHostClosedError) {
888 }
889 }
890}
891
893{
897 QSOCKS5_D_DEBUG << "queueing readNotification";
899 QMetaObject::invokeMethod(q, "_q_emitPendingReadNotification", Qt::QueuedConnection);
900 }
901}
902
904{
908 QSOCKS5_D_DEBUG << "emitting writeNotification";
909 q->writeNotification();
910 }
911}
912
914{
918 QSOCKS5_D_DEBUG << "queueing writeNotification";
920 QMetaObject::invokeMethod(q, "_q_emitPendingWriteNotification", Qt::QueuedConnection);
921 }
922}
923
925{
928 QSOCKS5_D_DEBUG << "emitting connectionNotification";
929 q->connectionNotification();
930}
931
933{
935 QSOCKS5_D_DEBUG << "queueing connectionNotification";
937 QMetaObject::invokeMethod(q, "_q_emitPendingConnectionNotification", Qt::QueuedConnection);
938}
939
942{
943}
944
946{
948
949 if (d->data) {
950 delete d->data->authenticator;
951 delete d->data->controlSocket;
952 }
953 if (d->connectData)
954 delete d->connectData;
955#ifndef QT_NO_UDPSOCKET
956 if (d->udpData) {
957 delete d->udpData->udpSocket;
958 delete d->udpData;
959 }
960#endif
961 if (d->bindData)
962 delete d->bindData;
963}
964
965static int nextDescriptor()
966{
968 return 1 + counter.fetchAndAddRelaxed(1);
969}
970
972{
974
975 d->socketDescriptor = nextDescriptor();
976
977 d->socketType = type;
978 d->socketProtocol = protocol;
979
980 return true;
981}
982
984{
986
987 QSOCKS5_Q_DEBUG << "initialize" << socketDescriptor;
988
989 // this is only valid for the other side of a bind, nothing else is supported
990
991 if (socketState != QAbstractSocket::ConnectedState) {
992 //### must be connected state ???
993 return false;
994 }
995
996 QSocks5BindData *bindData = socks5BindStore()->retrieve(socketDescriptor);
997 if (bindData) {
998
999 d->socketState = QAbstractSocket::ConnectedState;
1000 d->socketType = QAbstractSocket::TcpSocket;
1001 d->connectData = new QSocks5ConnectData;
1002 d->data = d->connectData;
1004 d->data->controlSocket = bindData->controlSocket;
1005 bindData->controlSocket = nullptr;
1006 d->data->controlSocket->setParent(this);
1007 d->socketProtocol = d->data->controlSocket->localAddress().protocol();
1008 d->data->authenticator = bindData->authenticator;
1009 bindData->authenticator = nullptr;
1010 d->localPort = bindData->localPort;
1011 d->localAddress = bindData->localAddress;
1012 d->peerPort = bindData->peerPort;
1013 d->peerAddress = bindData->peerAddress;
1014 d->inboundStreamCount = d->outboundStreamCount = 1;
1015 delete bindData;
1016
1017 QObject::connect(d->data->controlSocket, SIGNAL(connected()), this, SLOT(_q_controlSocketConnected()),
1019 QObject::connect(d->data->controlSocket, SIGNAL(readyRead()), this, SLOT(_q_controlSocketReadNotification()),
1021 QObject::connect(d->data->controlSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_controlSocketBytesWritten()),
1023 QObject::connect(d->data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketErrorOccurred(QAbstractSocket::SocketError)),
1025 QObject::connect(d->data->controlSocket, SIGNAL(disconnected()), this, SLOT(_q_controlSocketDisconnected()),
1027 QObject::connect(d->data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
1028 this, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
1030
1032
1033 if (d->data->controlSocket->bytesAvailable() != 0)
1034 d->_q_controlSocketReadNotification();
1035 return true;
1036 }
1037 return false;
1038}
1039
1041{
1043 d->proxyInfo = networkProxy;
1044}
1045
1047{
1048 Q_D(const QSocks5SocketEngine);
1049 return d->socketDescriptor;
1050}
1051
1053{
1054 Q_D(const QSocks5SocketEngine);
1055 return d->socketType != QAbstractSocket::UnknownSocketType
1056 && d->socks5State != QSocks5SocketEnginePrivate::SocksError
1057 && (d->socketError == QAbstractSocket::UnknownSocketError
1058 || d->socketError == QAbstractSocket::SocketTimeoutError
1060}
1061
1063{
1065
1066 if (!d->data) {
1069#ifndef QT_NO_UDPSOCKET
1070 } else if (socketType() == QAbstractSocket::UdpSocket) {
1072 // all udp needs to be bound
1073 if (!bind(QHostAddress("0.0.0.0"_L1), 0))
1074 return false;
1075
1077 return true;
1078#endif
1079 } else {
1080 qFatal("QSocks5SocketEngine::connectToHost: in QTcpServer mode");
1081 return false;
1082 }
1083 }
1084
1085 if (d->socketState != QAbstractSocket::ConnectingState) {
1087 // We may have new auth credentials since an earlier failure:
1090 //limit buffer in internal socket, data is buffered in the external socket under application control
1091 d->data->controlSocket->setReadBufferSize(65536);
1092 }
1093
1094 d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1095 }
1096
1097 return false;
1098}
1099
1101{
1103 QSOCKS5_DEBUG << "connectToHost" << address << ':' << port;
1104
1107 d->peerName.clear();
1108
1109 return connectInternal();
1110}
1111
1113{
1115
1118 d->peerName = hostname;
1119
1120 return connectInternal();
1121}
1122
1124{
1125 QSOCKS5_DEBUG << "_q_controlSocketConnected";
1126 QByteArray buf(3, 0);
1127 buf[0] = S5_VERSION_5;
1128 buf[1] = 0x01;
1129 buf[2] = data->authenticator->methodId();
1130 data->controlSocket->write(buf);
1132}
1133
1135{
1136 QSOCKS5_D_DEBUG << "_q_controlSocketReadNotification socks5state" << s5StateToString(socks5State)
1137 << "bytes available" << data->controlSocket->bytesAvailable();
1138
1139 if (data->controlSocket->bytesAvailable() == 0) {
1140 QSOCKS5_D_DEBUG << "########## bogus read why do we get these ... on windows only";
1141 return;
1142 }
1143
1144 switch (socks5State) {
1147 break;
1148 case Authenticating:
1150 break;
1151 case RequestMethodSent:
1153 if (socks5State == Connected && data->controlSocket->bytesAvailable())
1155 break;
1156 case Connected: {
1158 if (!data->authenticator->unSeal(data->controlSocket, &buf)) {
1159 // qDebug("unseal error maybe need to wait for more data");
1160 }
1161 if (buf.size()) {
1163 connectData->readBuffer.append(std::move(buf));
1165 }
1166 break;
1167 }
1168 case BindSuccess:
1169 // only get here if command is bind
1170 if (mode == BindMode) {
1172 break;
1173 }
1174
1175 Q_FALLTHROUGH();
1176 default:
1177 qWarning("QSocks5SocketEnginePrivate::_q_controlSocketReadNotification: "
1178 "Unexpectedly received data while in state=%d and mode=%d",
1179 socks5State, mode);
1180 break;
1181 };
1182}
1183
1185{
1186 QSOCKS5_DEBUG << "_q_controlSocketBytesWritten";
1187
1188 if (socks5State != Connected
1189 || (mode == ConnectMode
1190 && data->controlSocket->bytesToWrite()))
1191 return;
1192 if (data->controlSocket->bytesToWrite() < MaxWriteBufferSize) {
1195 }
1196}
1197
1199{
1200 QSOCKS5_D_DEBUG << "controlSocketError" << error << data->controlSocket->errorString();
1201
1203 return; // ignore this error -- comes from the waitFor* functions
1204
1206 && socks5State == Connected) {
1207 // clear the read buffer in connect mode so that bytes available returns 0
1208 // if there already is a read notification pending then this will be processed first
1212 data->controlSocket->close();
1213 // cause a disconnect in the outer socket
1215 } else if (socks5State == Uninitialized
1220 data->controlSocket->close();
1222 } else {
1223 q_func()->setError(data->controlSocket->error(), data->controlSocket->errorString());
1226 }
1227}
1228
1230{
1231 QSOCKS5_D_DEBUG << "_q_controlSocketDisconnected";
1232}
1233
1235{
1236 QSOCKS5_D_DEBUG << "_q_controlSocketStateChanged" << state;
1237}
1238
1239#ifndef QT_NO_UDPSOCKET
1241{
1242 QSOCKS5_D_DEBUG << "_q_udpSocketReadNotification()";
1243
1244 // check some state stuff
1246 QSOCKS5_D_DEBUG << "false read ??";
1247 return;
1248 }
1249
1252 QSOCKS5_D_DEBUG << "new datagram";
1253 udpData->udpSocket->readDatagram(sealedBuf.data(), sealedBuf.size());
1254 QByteArray inBuf;
1255 if (!data->authenticator->unSeal(sealedBuf, &inBuf)) {
1256 QSOCKS5_D_DEBUG << "failed unsealing datagram discarding";
1257 return;
1258 }
1259 QSOCKS5_DEBUG << dump(inBuf);
1260 int pos = 0;
1261 const char *buf = inBuf.constData();
1262 if (inBuf.size() < 4) {
1263 QSOCKS5_D_DEBUG << "bogus udp data, discarding";
1264 return;
1265 }
1266 QSocks5RevivedDatagram datagram;
1267 if (buf[pos++] != 0 || buf[pos++] != 0) {
1268 QSOCKS5_D_DEBUG << "invalid datagram discarding";
1269 return;
1270 }
1271 if (buf[pos++] != 0) { //### add fragmentation reading support
1272 QSOCKS5_D_DEBUG << "don't support fragmentation yet disgarding";
1273 return;
1274 }
1275 if (qt_socks5_get_host_address_and_port(inBuf, &datagram.address, &datagram.port, &pos) != 1) {
1276 QSOCKS5_D_DEBUG << "failed to get address from datagram disgarding";
1277 return;
1278 }
1279 datagram.data = QByteArray(&buf[pos], inBuf.size() - pos);
1280 udpData->pendingDatagrams.enqueue(datagram);
1281 }
1283}
1284#endif // QT_NO_UDPSOCKET
1285
1287{
1289
1290 // when bind we will block until the bind is finished as the info from the proxy server is needed
1291
1293 if (addr.protocol() == QAbstractSocket::AnyIPProtocol)
1294 address = QHostAddress::AnyIPv4; //SOCKS5 doesn't support dual stack, and there isn't any implementation of udp on ipv6 yet
1295 else
1296 address = addr;
1297
1298 if (!d->data) {
1301#ifndef QT_NO_UDPSOCKET
1302 } else if (socketType() == QAbstractSocket::UdpSocket) {
1304#endif
1305 } else {
1306 //### something invalid
1307 return false;
1308 }
1309 }
1310
1311#ifndef QT_NO_UDPSOCKET
1313 if (!d->udpData->udpSocket->bind(address, port)) {
1314 QSOCKS5_Q_DEBUG << "local udp bind failed";
1315 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1316 return false;
1317 }
1318 d->localAddress = d->udpData->udpSocket->localAddress();
1319 d->localPort = d->udpData->udpSocket->localPort();
1320 } else
1321#endif
1323 d->localAddress = address;
1324 d->localPort = port;
1325 } else {
1326 //### something invalid
1327 return false;
1328 }
1329
1330 int msecs = SOCKS5_BLOCKING_BIND_TIMEOUT;
1331 QElapsedTimer stopWatch;
1332 stopWatch.start();
1333 d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1334 if (!d->waitForConnected(msecs, 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 int msecs = 100;
1426 QElapsedTimer stopWatch;
1427 stopWatch.start();
1428 while (!d->data->controlSocket->bytesToWrite()) {
1429 if (!d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed())))
1430 break;
1431 }
1432 }
1433 d->data->controlSocket->close();
1434 }
1435 d->inboundStreamCount = d->outboundStreamCount = 0;
1436#ifndef QT_NO_UDPSOCKET
1437 if (d->udpData && d->udpData->udpSocket)
1438 d->udpData->udpSocket->close();
1439#endif
1440}
1441
1443{
1444 Q_D(const QSocks5SocketEngine);
1446 return d->connectData->readBuffer.size();
1447#ifndef QT_NO_UDPSOCKET
1449 && !d->udpData->pendingDatagrams.isEmpty())
1450 return d->udpData->pendingDatagrams.first().data.size();
1451#endif
1452 return 0;
1453}
1454
1456{
1458 QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ')';
1460 if (d->connectData->readBuffer.isEmpty()) {
1461 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1462 //imitate remote closed
1463 close();
1465 "Remote host closed connection###"_L1);
1467 return -1;
1468 } else {
1469 return 0; // nothing to be read
1470 }
1471 }
1472 const qint64 copy = d->connectData->readBuffer.read(data, maxlen);
1473 QSOCKS5_DEBUG << "read" << dump(QByteArray(data, copy));
1474 return copy;
1475#ifndef QT_NO_UDPSOCKET
1476 } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1477 return readDatagram(data, maxlen);
1478#endif
1479 }
1480 return 0;
1481}
1482
1484{
1486 QSOCKS5_Q_DEBUG << "write" << dump(QByteArray(data, len));
1487
1489 // clamp down the amount of bytes to transfer at once
1490 len = qMin<qint64>(len, MaxWriteBufferSize) - d->data->controlSocket->bytesToWrite();
1491 if (len <= 0)
1492 return 0;
1493
1495 QByteArray sealedBuf;
1496 if (!d->data->authenticator->seal(buf, &sealedBuf)) {
1497 // ### Handle this error.
1498 }
1499 // We pass pointer and size because 'sealedBuf' is (most definitely) raw data:
1500 // QIODevice might have to cache the byte array if the socket cannot write the data.
1501 // If the _whole_ array needs to be cached then it would simply store a copy of the
1502 // array whose data will go out of scope and be deallocated before it can be used.
1503 qint64 written = d->data->controlSocket->write(sealedBuf.constData(), sealedBuf.size());
1504
1505 if (written <= 0) {
1506 QSOCKS5_Q_DEBUG << "native write returned" << written;
1507 return written;
1508 }
1509 d->data->controlSocket->waitForBytesWritten(0);
1510 //NB: returning len rather than written for the OK case, because the "sealing" may increase the length
1511 return len;
1512#ifndef QT_NO_UDPSOCKET
1513 } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1514 // send to connected address
1515 return writeDatagram(data, len, QIpPacketHeader(d->peerAddress, d->peerPort));
1516#endif
1517 }
1518 //### set an error ???
1519 return -1;
1520}
1521
1522#ifndef QT_NO_UDPSOCKET
1523#ifndef QT_NO_NETWORKINTERFACE
1525 const QNetworkInterface &)
1526{
1528 "Operation on socket is not supported"_L1);
1529 return false;
1530}
1531
1533 const QNetworkInterface &)
1534{
1536 "Operation on socket is not supported"_L1);
1537 return false;
1538}
1539
1540
1542{
1543 return QNetworkInterface();
1544}
1545
1547{
1549 "Operation on socket is not supported"_L1);
1550 return false;
1551}
1552#endif // QT_NO_NETWORKINTERFACE
1553
1555{
1556 Q_D(const QSocks5SocketEngine);
1557 Q_INIT_CHECK(false);
1558
1559 return !d->udpData->pendingDatagrams.isEmpty();
1560}
1561
1563{
1564 Q_D(const QSocks5SocketEngine);
1565
1566 if (!d->udpData->pendingDatagrams.isEmpty())
1567 return d->udpData->pendingDatagrams.head().data.size();
1568 return 0;
1569}
1570#endif // QT_NO_UDPSOCKET
1571
1573{
1574#ifndef QT_NO_UDPSOCKET
1576
1577 if (d->udpData->pendingDatagrams.isEmpty())
1578 return 0;
1579
1580 QSocks5RevivedDatagram datagram = d->udpData->pendingDatagrams.dequeue();
1581 int copyLen = qMin<int>(maxlen, datagram.data.size());
1582 memcpy(data, datagram.data.constData(), copyLen);
1583 if (header) {
1584 header->senderAddress = datagram.address;
1585 header->senderPort = datagram.port;
1586 }
1587 return copyLen;
1588#else
1589 Q_UNUSED(data);
1590 Q_UNUSED(maxlen);
1592 return -1;
1593#endif // QT_NO_UDPSOCKET
1594}
1595
1597{
1598#ifndef QT_NO_UDPSOCKET
1600
1601 // it is possible to send with out first binding with udp, but socks5 requires a bind.
1602 if (!d->data) {
1604 // all udp needs to be bound
1605 if (!bind(QHostAddress("0.0.0.0"_L1), 0)) {
1606 //### set error
1607 return -1;
1608 }
1609 }
1610
1611 QByteArray outBuf;
1612 outBuf.reserve(270 + len);
1613 outBuf.append(3, '\0');
1614 if (!qt_socks5_set_host_address_and_port(header.destinationAddress, header.destinationPort, &outBuf)) {
1615 QSOCKS5_DEBUG << "error setting address" << header.destinationAddress << " : "
1616 << header.destinationPort;
1617 //### set error code ....
1618 return -1;
1619 }
1620 outBuf += QByteArray(data, len);
1621 QSOCKS5_DEBUG << "sending" << dump(outBuf);
1622 QByteArray sealedBuf;
1623 if (!d->data->authenticator->seal(outBuf, &sealedBuf)) {
1624 QSOCKS5_DEBUG << "sealing data failed";
1625 setError(QAbstractSocket::SocketAccessError, d->data->authenticator->errorString());
1626 return -1;
1627 }
1628 if (d->udpData->udpSocket->writeDatagram(sealedBuf, d->udpData->associateAddress, d->udpData->associatePort) != sealedBuf.size()) {
1629 //### try frgamenting
1630 if (d->udpData->udpSocket->error() == QAbstractSocket::DatagramTooLargeError)
1631 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1632 //### else maybe more serious error
1633 return -1;
1634 }
1635
1636 return len;
1637#else
1638 Q_UNUSED(data);
1639 Q_UNUSED(len);
1641 return -1;
1642#endif // QT_NO_UDPSOCKET
1643}
1644
1646{
1647 Q_D(const QSocks5SocketEngine);
1648 if (d->data && d->data->controlSocket) {
1649 return d->data->controlSocket->bytesToWrite();
1650 } else {
1651 return 0;
1652 }
1653}
1654
1656{
1657 Q_D(const QSocks5SocketEngine);
1658 if (d->data && d->data->controlSocket) {
1659 // convert the enum and call the real socket
1661 return d->data->controlSocket->socketOption(QAbstractSocket::LowDelayOption).toInt();
1663 return d->data->controlSocket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
1664 }
1665 return -1;
1666}
1667
1669{
1671 if (d->data && d->data->controlSocket) {
1672 // convert the enum and call the real socket
1674 d->data->controlSocket->setSocketOption(QAbstractSocket::LowDelayOption, value);
1676 d->data->controlSocket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
1677 return true;
1678 }
1679 return false;
1680}
1681
1683{
1684 if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1685 return false;
1686
1687 const Socks5State wantedState =
1691
1692 QElapsedTimer stopWatch;
1693 stopWatch.start();
1694
1695 while (socks5State != wantedState) {
1696 if (!data->controlSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
1697 if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1698 return true;
1699
1701 if (timedOut && data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1702 *timedOut = true;
1703 return false;
1704 }
1705 }
1706
1707 return true;
1708}
1709
1710bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
1711{
1713 QSOCKS5_DEBUG << "waitForRead" << msecs;
1714
1715 d->readNotificationActivated = false;
1716
1717 QElapsedTimer stopWatch;
1718 stopWatch.start();
1719
1720 // are we connected yet?
1721 if (!d->waitForConnected(msecs, timedOut))
1722 return false;
1723 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1724 return true;
1725 if (bytesAvailable() && d->readNotificationPending) {
1726 // We've got some data incoming, but the queued call hasn't been performed yet.
1727 // The data is where we expect it to be already, so just return true.
1728 return true;
1729 }
1730
1731 // we're connected
1734 while (!d->readNotificationActivated) {
1735 if (!d->data->controlSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
1736 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1737 return true;
1738
1739 setError(d->data->controlSocket->error(), d->data->controlSocket->errorString());
1740 if (timedOut && d->data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1741 *timedOut = true;
1742 return false;
1743 }
1744 }
1745#ifndef QT_NO_UDPSOCKET
1746 } else {
1747 while (!d->readNotificationActivated) {
1748 if (!d->udpData->udpSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
1749 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1750 if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
1751 *timedOut = true;
1752 return false;
1753 }
1754 }
1755#endif // QT_NO_UDPSOCKET
1756 }
1757
1758
1759 bool ret = d->readNotificationActivated;
1760 d->readNotificationActivated = false;
1761
1762 QSOCKS5_DEBUG << "waitForRead returned" << ret;
1763 return ret;
1764}
1765
1766
1767bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
1768{
1770 QSOCKS5_DEBUG << "waitForWrite" << msecs;
1771
1772 QElapsedTimer stopWatch;
1773 stopWatch.start();
1774
1775 // are we connected yet?
1776 if (!d->waitForConnected(msecs, timedOut))
1777 return false;
1778 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1779 return true;
1780
1781 // we're connected
1782
1783 // flush any bytes we may still have buffered in the time that we have left
1784 if (d->data->controlSocket->bytesToWrite())
1785 d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
1786 while ((msecs == -1 || stopWatch.elapsed() < msecs)
1787 && d->data->controlSocket->state() == QAbstractSocket::ConnectedState
1788 && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize)
1789 d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
1790 return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
1791}
1792
1793bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
1794 bool checkRead, bool checkWrite,
1795 int msecs, bool *timedOut)
1796{
1797 Q_UNUSED(checkRead);
1798 if (!checkWrite) {
1799 bool canRead = waitForRead(msecs, timedOut);
1800 if (readyToRead)
1801 *readyToRead = canRead;
1802 return canRead;
1803 }
1804
1805 bool canWrite = waitForWrite(msecs, timedOut);
1806 if (readyToWrite)
1807 *readyToWrite = canWrite;
1808 return canWrite;
1809}
1810
1812{
1813 Q_D(const QSocks5SocketEngine);
1814 return d->readNotificationEnabled;
1815}
1816
1818{
1820
1821 QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ')';
1822
1823 bool emitSignal = false;
1824 if (!d->readNotificationEnabled
1825 && enable) {
1827 emitSignal = !d->connectData->readBuffer.isEmpty();
1828#ifndef QT_NO_UDPSOCKET
1830 emitSignal = !d->udpData->pendingDatagrams.isEmpty();
1831#endif
1832 else if (d->mode == QSocks5SocketEnginePrivate::BindMode
1833 && d->socketState == QAbstractSocket::ListeningState
1834 && d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
1835 emitSignal = true;
1836 }
1837
1838 d->readNotificationEnabled = enable;
1839
1840 if (emitSignal)
1841 d->emitReadNotification();
1842}
1843
1845{
1846 Q_D(const QSocks5SocketEngine);
1847 return d->writeNotificationEnabled;
1848}
1849
1851{
1853 d->writeNotificationEnabled = enable;
1854 if (enable && d->socketState == QAbstractSocket::ConnectedState) {
1855 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode && d->data->controlSocket->bytesToWrite())
1856 return; // will be emitted as a result of bytes written
1857 d->emitWriteNotification();
1858 d->writeNotificationActivated = false;
1859 }
1860}
1861
1863{
1864 Q_D(const QSocks5SocketEngine);
1865 return d->exceptNotificationEnabled;
1866}
1867
1869{
1871 d->exceptNotificationEnabled = enable;
1872}
1873
1877{
1879
1880 // proxy type must have been resolved by now
1882 QSOCKS5_DEBUG << "not proxying";
1883 return nullptr;
1884 }
1885 auto engine = std::make_unique<QSocks5SocketEngine>(parent);
1886 engine->setProxy(proxy);
1887 return engine.release();
1888}
1889
1891{
1892 QSOCKS5_DEBUG << "createSocketEngine" << socketDescriptor;
1893 if (socks5BindStore()->contains(socketDescriptor)) {
1894 QSOCKS5_DEBUG << "bind store contains" << socketDescriptor;
1895 return new QSocks5SocketEngine(parent);
1896 }
1897 return nullptr;
1898}
1899
1901
1902#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.
QString user() const
Returns the user used for authentication.
QString password() const
Returns the password used for authentication.
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
QByteArray & prepend(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:216
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
void reserve(qsizetype size)
Attempts to allocate memory for at least size bytes.
Definition qbytearray.h:557
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
char at(qsizetype i) const
Returns the byte at index position i in the byte array.
Definition qbytearray.h:523
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:394
\inmodule QtCore
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
\inmodule QtCore
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
void start() noexcept
Starts this timer.
\inmodule QtCore
Definition qhash.h:818
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1202
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
iterator erase(const_iterator it)
Definition qhash.h:1223
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:991
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
const_iterator cend() const noexcept
Definition qhash.h:1208
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:926
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
The QHostAddress class provides an IP address.
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
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:317
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:90
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:1792
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
void killTimer(int id)
Kills the timer with timer identifier, id.
Definition qobject.cpp:1872
\inmodule QtCore
Definition qpointer.h:18
\inmodule QtCore
Definition qqueue.h:14
void enqueue(const T &t)
Adds value t to the tail of the queue.
Definition qqueue.h:18
\inmodule QtCore
Definition qmutex.h:313
Q_CORE_EXPORT void append(const char *data, qint64 size)
Q_CORE_EXPORT void clear()
Exception-safe wrapper around QObject::blockSignals().
Definition qobject.h:443
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)
bool waitForConnected(int msecs, bool *timedOut)
void _q_controlSocketErrorOccurred(QAbstractSocket::SocketError)
void initialize(Socks5Mode socks5Mode)
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 waitForWrite(int msecs=30000, bool *timedOut=nullptr) override
bool waitForRead(int msecs=30000, bool *timedOut=nullptr) override
bool isWriteNotificationEnabled() const override
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, int msecs=30000, bool *timedOut=nullptr) 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
void setWriteNotificationEnabled(bool enable) override
qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) override
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLatin1() const &
Definition qstring.h:559
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
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:7822
The QTcpSocket class provides a TCP socket.
Definition qtcpsocket.h:18
static QThread * currentThread()
Definition qthread.cpp:966
\inmodule QtCore
Definition qcoreevent.h:359
\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:3061
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)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputPortEXT port
static QT_BEGIN_NAMESPACE const char * socketType(QSocketNotifier::Type type)
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
int qt_subtract_from_timeout(int timeout, int elapsed)
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
#define qFatal
Definition qlogging.h:164
return ret
static ControlElement< T > * ptr(QWidget *widget)
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLenum GLsizei GLuint GLint * bytesWritten
GLenum mode
GLuint GLuint end
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLboolean enable
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint counter
struct _cl_event * event
GLuint GLfloat * val
GLenum GLsizei len
GLenum const void * addr
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLdouble s
[6]
Definition qopenglext.h:235
GLuint GLenum option
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
#define SOCKS5_BLOCKING_BIND_TIMEOUT
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)
SSL_CTX int(*) void arg)
#define Q_UNUSED(x)
#define QT_TRANSLATE_NOOP(scope, x)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
unsigned short quint16
Definition qtypes.h:43
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
ptrdiff_t qintptr
Definition qtypes.h:71
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
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent