1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qsocks5socketengine_p.h"
46 #include "qtcpsocket.h"
47 #include "qudpsocket.h"
48 #include "qtcpserver.h"
52 #include "qelapsedtimer.h"
55 #include "qcoreapplication.h"
57 #include "qauthenticator.h"
59 #include <qnetworkinterface.h>
63 static const int MaxWriteBufferSize = 128*1024;
65 //#define QSOCKS5SOCKETLAYER_DEBUG
67 #define MAX_DATA_DUMP 256
68 #if !defined(Q_OS_WINCE)
69 #define SOCKS5_BLOCKING_BIND_TIMEOUT 5000
71 #define SOCKS5_BLOCKING_BIND_TIMEOUT 10000
74 #define Q_INIT_CHECK(returnValue) do { \
79 #define S5_VERSION_5 0x05
80 #define S5_CONNECT 0x01
82 #define S5_UDP_ASSOCIATE 0x03
84 #define S5_DOMAINNAME 0x03
86 #define S5_SUCCESS 0x00
87 #define S5_R_ERROR_SOCKS_FAILURE 0x01
88 #define S5_R_ERROR_CON_NOT_ALLOWED 0x02
89 #define S5_R_ERROR_NET_UNREACH 0x03
90 #define S5_R_ERROR_HOST_UNREACH 0x04
91 #define S5_R_ERROR_CONN_REFUSED 0x05
92 #define S5_R_ERROR_TTL 0x06
93 #define S5_R_ERROR_CMD_NOT_SUPPORTED 0x07
94 #define S5_R_ERROR_ADD_TYPE_NOT_SUPORTED 0x08
96 #define S5_AUTHMETHOD_NONE 0x00
97 #define S5_AUTHMETHOD_PASSWORD 0x02
98 #define S5_AUTHMETHOD_NOTACCEPTABLE 0xFF
100 #define S5_PASSWORDAUTH_VERSION 0x01
102 #ifdef QSOCKS5SOCKETLAYER_DEBUG
103 # define QSOCKS5_Q_DEBUG qDebug() << this
104 # define QSOCKS5_D_DEBUG qDebug() << q_ptr
105 # define QSOCKS5_DEBUG qDebug() << "[QSocks5]"
106 static QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State s)
109 case QSocks5SocketEnginePrivate::Uninitialized: return QLatin1String("Uninitialized");
110 case QSocks5SocketEnginePrivate::ConnectError: return QLatin1String("ConnectError");
111 case QSocks5SocketEnginePrivate::AuthenticationMethodsSent: return QLatin1String("AuthenticationMethodsSent");
112 case QSocks5SocketEnginePrivate::Authenticating: return QLatin1String("Authenticating");
113 case QSocks5SocketEnginePrivate::AuthenticatingError: return QLatin1String("AuthenticatingError");
114 case QSocks5SocketEnginePrivate::RequestMethodSent: return QLatin1String("RequestMethodSent");
115 case QSocks5SocketEnginePrivate::RequestError: return QLatin1String("RequestError");
116 case QSocks5SocketEnginePrivate::Connected: return QLatin1String("Connected");
117 case QSocks5SocketEnginePrivate::UdpAssociateSuccess: return QLatin1String("UdpAssociateSuccess");
118 case QSocks5SocketEnginePrivate::BindSuccess: return QLatin1String("BindSuccess");
119 case QSocks5SocketEnginePrivate::ControlSocketError: return QLatin1String("ControlSocketError");
120 case QSocks5SocketEnginePrivate::SocksError: return QLatin1String("SocksError");
121 case QSocks5SocketEnginePrivate::HostNameLookupError: return QLatin1String("HostNameLookupError");
124 return QLatin1String("unknown state");
127 static QString dump(const QByteArray &buf)
130 for (int i = 0; i < qMin<int>(MAX_DATA_DUMP, buf.size()); ++i) {
131 if (i) data += QLatin1Char(' ');
132 uint val = (unsigned char)buf.at(i);
133 // data += QString("0x%1").arg(val, 3, 16, QLatin1Char('0'));
134 data += QString::number(val);
136 if (buf.size() > MAX_DATA_DUMP)
137 data += QLatin1String(" ...");
139 return QString::fromLatin1("size: %1 data: { %2 }").arg(buf.size()).arg(data);
143 # define QSOCKS5_DEBUG if (0) qDebug()
144 # define QSOCKS5_Q_DEBUG if (0) qDebug()
145 # define QSOCKS5_D_DEBUG if (0) qDebug()
147 static inline QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State) { return QString(); }
148 static inline QString dump(const QByteArray &) { return QString(); }
152 inserts the host address in buf at pos and updates pos.
153 if the func fails the data in buf and the vallue of pos is undefined
155 static bool qt_socks5_set_host_address_and_port(const QHostAddress &address, quint16 port, QByteArray *pBuf)
157 QSOCKS5_DEBUG << "setting [" << address << ':' << port << ']';
167 if (address.protocol() == QAbstractSocket::IPv4Protocol) {
168 data.ipv4 = qToBigEndian<quint32>(address.toIPv4Address());
169 pBuf->append(S5_IP_V4);
170 pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv4));
171 } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
172 data.ipv6 = address.toIPv6Address();
173 pBuf->append(S5_IP_V6);
174 pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv6));
180 data.port = qToBigEndian<quint16>(port);
181 pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
186 like above, but for a hostname
188 static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 port, QByteArray *pBuf)
190 QSOCKS5_DEBUG << "setting [" << hostname << ':' << port << ']';
192 QByteArray encodedHostName = QUrl::toAce(hostname);
193 QByteArray &buf = *pBuf;
195 if (encodedHostName.length() > 255)
198 buf.append(S5_DOMAINNAME);
199 buf.append(uchar(encodedHostName.length()));
200 buf.append(encodedHostName);
207 data.port = qToBigEndian<quint16>(port);
208 buf.append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
215 retrives the host address in buf at pos and updates pos.
216 return 1 if OK, 0 if need more data, -1 if error
217 if the func fails the value of the address and the pos is undefined
219 static int qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddress *pAddress, quint16 *pPort, int *pPos)
223 const unsigned char *pBuf = reinterpret_cast<const unsigned char*>(buf.constData());
224 QHostAddress address;
227 if (buf.size() - pos < 1) {
228 QSOCKS5_DEBUG << "need more data address/port";
231 if (pBuf[pos] == S5_IP_V4) {
233 if (buf.size() - pos < 4) {
234 QSOCKS5_DEBUG << "need more data for ip4 address";
237 address.setAddress(qFromBigEndian<quint32>(&pBuf[pos]));
240 } else if (pBuf[pos] == S5_IP_V6) {
242 if (buf.size() - pos < 16) {
243 QSOCKS5_DEBUG << "need more data for ip6 address";
247 for (int i = 0; i < 16; ++i)
249 address.setAddress(add);
251 } else if (pBuf[pos] == S5_DOMAINNAME){
254 qDebug() << "skipping hostname of len" << uint(pBuf[pos]);
255 pos += uchar(pBuf[pos]);
257 QSOCKS5_DEBUG << "invalid address type" << (int)pBuf[pos];
262 if (buf.size() - pos < 2) {
263 QSOCKS5_DEBUG << "need more data for port";
266 port = qFromBigEndian<quint16>(&pBuf[pos]);
271 QSOCKS5_DEBUG << "got [" << address << ':' << port << ']';
281 Returns the difference between msecs and elapsed. If msecs is -1,
282 however, -1 is returned.
284 static int qt_timeout_value(int msecs, int elapsed)
289 int timeout = msecs - elapsed;
290 return timeout < 0 ? 0 : timeout;
295 QTcpSocket *controlSocket;
296 QSocks5Authenticator *authenticator;
299 struct QSocks5ConnectData : public QSocks5Data
301 QByteArray readBuffer;
304 struct QSocks5BindData : public QSocks5Data
306 QHostAddress localAddress;
308 QHostAddress peerAddress;
310 QElapsedTimer timeStamp;
313 struct QSocks5RevivedDatagram
316 QHostAddress address;
320 #ifndef QT_NO_UDPSOCKET
321 struct QSocks5UdpAssociateData : public QSocks5Data
323 QUdpSocket *udpSocket;
324 QHostAddress associateAddress;
325 quint16 associatePort;
326 QQueue<QSocks5RevivedDatagram> pendingDatagrams;
330 // needs to be thread safe
331 class QSocks5BindStore : public QObject
337 void add(qintptr socketDescriptor, QSocks5BindData *bindData);
338 bool contains(qintptr socketDescriptor);
339 QSocks5BindData *retrieve(qintptr socketDescriptor);
342 void timerEvent(QTimerEvent * event);
346 //socket descriptor, data, timestamp
347 QHash<int, QSocks5BindData *> store;
350 Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore)
352 QSocks5BindStore::QSocks5BindStore()
353 : mutex(QMutex::Recursive)
356 QCoreApplication *app = QCoreApplication::instance();
357 if (app && app->thread() != thread())
358 moveToThread(app->thread());
361 QSocks5BindStore::~QSocks5BindStore()
365 void QSocks5BindStore::add(qintptr socketDescriptor, QSocks5BindData *bindData)
367 QMutexLocker lock(&mutex);
368 if (store.contains(socketDescriptor)) {
369 // qDebug() << "delete it";
371 bindData->timeStamp.start();
372 store.insert(socketDescriptor, bindData);
373 // start sweep timer if not started
374 if (sweepTimerId == -1)
375 sweepTimerId = startTimer(60000);
378 bool QSocks5BindStore::contains(qintptr socketDescriptor)
380 QMutexLocker lock(&mutex);
381 return store.contains(socketDescriptor);
384 QSocks5BindData *QSocks5BindStore::retrieve(qintptr socketDescriptor)
386 QMutexLocker lock(&mutex);
387 if (!store.contains(socketDescriptor))
389 QSocks5BindData *bindData = store.take(socketDescriptor);
391 if (bindData->controlSocket->thread() != QThread::currentThread()) {
392 qWarning("Can not access socks5 bind data from different thread");
396 QSOCKS5_DEBUG << "__ERROR__ binddata == 0";
398 // stop the sweep timer if not needed
399 if (store.isEmpty()) {
400 killTimer(sweepTimerId);
406 void QSocks5BindStore::timerEvent(QTimerEvent * event)
408 QMutexLocker lock(&mutex);
409 if (event->timerId() == sweepTimerId) {
410 QSOCKS5_DEBUG << "QSocks5BindStore performing sweep";
411 QMutableHashIterator<int, QSocks5BindData *> it(store);
412 while (it.hasNext()) {
414 if (it.value()->timeStamp.hasExpired(350000)) {
415 QSOCKS5_DEBUG << "QSocks5BindStore removing JJJJ";
422 QSocks5Authenticator::QSocks5Authenticator()
426 QSocks5Authenticator::~QSocks5Authenticator()
430 char QSocks5Authenticator::methodId()
435 bool QSocks5Authenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
442 bool QSocks5Authenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
449 bool QSocks5Authenticator::seal(const QByteArray buf, QByteArray *sealedBuf)
455 bool QSocks5Authenticator::unSeal(const QByteArray sealedBuf, QByteArray *buf)
461 bool QSocks5Authenticator::unSeal(QTcpSocket *sealedSocket, QByteArray *buf)
463 return unSeal(sealedSocket->readAll(), buf);
466 QSocks5PasswordAuthenticator::QSocks5PasswordAuthenticator(const QString &userName, const QString &password)
468 this->userName = userName;
469 this->password = password;
472 char QSocks5PasswordAuthenticator::methodId()
477 bool QSocks5PasswordAuthenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
480 QByteArray uname = userName.toLatin1();
481 QByteArray passwd = password.toLatin1();
482 QByteArray dataBuf(3 + uname.size() + passwd.size(), 0);
483 char *buf = dataBuf.data();
485 buf[pos++] = S5_PASSWORDAUTH_VERSION;
486 buf[pos++] = uname.size();
487 memcpy(&buf[pos], uname.data(), uname.size());
489 buf[pos++] = passwd.size();
490 memcpy(&buf[pos], passwd.data(), passwd.size());
491 return socket->write(dataBuf) == dataBuf.size();
494 bool QSocks5PasswordAuthenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
498 if (socket->bytesAvailable() < 2)
501 QByteArray buf = socket->read(2);
502 if (buf.at(0) == S5_PASSWORDAUTH_VERSION && buf.at(1) == 0x00) {
512 QString QSocks5PasswordAuthenticator::errorString()
514 return QLatin1String("Socks5 user name or password incorrect");
519 QSocks5SocketEnginePrivate::QSocks5SocketEnginePrivate()
520 : socks5State(Uninitialized)
521 , readNotificationEnabled(false)
522 , writeNotificationEnabled(false)
523 , exceptNotificationEnabled(false)
524 , socketDescriptor(-1)
527 #ifndef QT_NO_UDPSOCKET
531 , readNotificationActivated(false)
532 , writeNotificationActivated(false)
533 , readNotificationPending(false)
534 , writeNotificationPending(false)
535 , connectionNotificationPending(false)
540 QSocks5SocketEnginePrivate::~QSocks5SocketEnginePrivate()
544 void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode)
546 Q_Q(QSocks5SocketEngine);
549 if (mode == ConnectMode) {
550 connectData = new QSocks5ConnectData;
552 #ifndef QT_NO_UDPSOCKET
553 } else if (mode == UdpAssociateMode) {
554 udpData = new QSocks5UdpAssociateData;
556 udpData->udpSocket = new QUdpSocket(q);
557 #ifndef QT_NO_BEARERMANAGEMENT
558 udpData->udpSocket->setProperty("_q_networksession", q->property("_q_networksession"));
560 udpData->udpSocket->setProxy(QNetworkProxy::NoProxy);
561 QObject::connect(udpData->udpSocket, SIGNAL(readyRead()),
562 q, SLOT(_q_udpSocketReadNotification()),
563 Qt::DirectConnection);
564 #endif // QT_NO_UDPSOCKET
565 } else if (mode == BindMode) {
566 bindData = new QSocks5BindData;
570 data->controlSocket = new QTcpSocket(q);
571 #ifndef QT_NO_BEARERMANAGEMENT
572 data->controlSocket->setProperty("_q_networksession", q->property("_q_networksession"));
574 data->controlSocket->setProxy(QNetworkProxy::NoProxy);
575 QObject::connect(data->controlSocket, SIGNAL(connected()), q, SLOT(_q_controlSocketConnected()),
576 Qt::DirectConnection);
577 QObject::connect(data->controlSocket, SIGNAL(readyRead()), q, SLOT(_q_controlSocketReadNotification()),
578 Qt::DirectConnection);
579 QObject::connect(data->controlSocket, SIGNAL(bytesWritten(qint64)), q, SLOT(_q_controlSocketBytesWritten()),
580 Qt::DirectConnection);
581 QObject::connect(data->controlSocket, SIGNAL(error(QAbstractSocket::SocketError)),
582 q, SLOT(_q_controlSocketError(QAbstractSocket::SocketError)),
583 Qt::DirectConnection);
584 QObject::connect(data->controlSocket, SIGNAL(disconnected()), q, SLOT(_q_controlSocketDisconnected()),
585 Qt::DirectConnection);
586 QObject::connect(data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
587 q, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
588 Qt::DirectConnection);
590 if (!proxyInfo.user().isEmpty() || !proxyInfo.password().isEmpty()) {
591 QSOCKS5_D_DEBUG << "using username/password authentication; user =" << proxyInfo.user();
592 data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
594 QSOCKS5_D_DEBUG << "not using authentication";
595 data->authenticator = new QSocks5Authenticator();
599 void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, const QString &extraMessage)
601 Q_Q(QSocks5SocketEngine);
606 case AuthenticationMethodsSent:
607 case RequestMethodSent:
609 case UdpAssociateSuccess:
611 // these aren't error states
615 case ControlSocketError: {
616 QAbstractSocket::SocketError controlSocketError = data->controlSocket->error();
617 if (socks5State != Connected) {
618 switch (controlSocketError) {
619 case QAbstractSocket::ConnectionRefusedError:
620 q->setError(QAbstractSocket::ProxyConnectionRefusedError,
621 QSocks5SocketEngine::tr("Connection to proxy refused"));
623 case QAbstractSocket::RemoteHostClosedError:
624 q->setError(QAbstractSocket::ProxyConnectionClosedError,
625 QSocks5SocketEngine::tr("Connection to proxy closed prematurely"));
627 case QAbstractSocket::HostNotFoundError:
628 q->setError(QAbstractSocket::ProxyNotFoundError,
629 QSocks5SocketEngine::tr("Proxy host not found"));
631 case QAbstractSocket::SocketTimeoutError:
632 if (state == ConnectError) {
633 q->setError(QAbstractSocket::ProxyConnectionTimeoutError,
634 QSocks5SocketEngine::tr("Connection to proxy timed out"));
639 q->setError(controlSocketError, data->controlSocket->errorString());
643 q->setError(controlSocketError, data->controlSocket->errorString());
648 case AuthenticatingError:
649 q->setError(QAbstractSocket::ProxyAuthenticationRequiredError,
650 extraMessage.isEmpty() ?
651 QSocks5SocketEngine::tr("Proxy authentication failed") :
652 QSocks5SocketEngine::tr("Proxy authentication failed: %1").arg(extraMessage));
656 // error code set by caller (overload)
660 q->setError(QAbstractSocket::ProxyProtocolError,
661 QSocks5SocketEngine::tr("SOCKS version 5 protocol error"));
664 case HostNameLookupError:
665 q->setError(QAbstractSocket::HostNotFoundError,
666 QAbstractSocket::tr("Host not found"));
670 q->setState(QAbstractSocket::UnconnectedState);
674 void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, Socks5Error socks5error)
676 Q_Q(QSocks5SocketEngine);
677 switch (socks5error) {
679 q->setError(QAbstractSocket::NetworkError,
680 QSocks5SocketEngine::tr("General SOCKSv5 server failure"));
682 case ConnectionNotAllowed:
683 q->setError(QAbstractSocket::SocketAccessError,
684 QSocks5SocketEngine::tr("Connection not allowed by SOCKSv5 server"));
686 case NetworkUnreachable:
687 q->setError(QAbstractSocket::NetworkError,
688 QAbstractSocket::tr("Network unreachable"));
690 case HostUnreachable:
691 q->setError(QAbstractSocket::HostNotFoundError,
692 QAbstractSocket::tr("Host not found"));
694 case ConnectionRefused:
695 q->setError(QAbstractSocket::ConnectionRefusedError,
696 QAbstractSocket::tr("Connection refused"));
699 q->setError(QAbstractSocket::NetworkError,
700 QSocks5SocketEngine::tr("TTL expired"));
702 case CommandNotSupported:
703 q->setError(QAbstractSocket::UnsupportedSocketOperationError,
704 QSocks5SocketEngine::tr("SOCKSv5 command not supported"));
706 case AddressTypeNotSupported:
707 q->setError(QAbstractSocket::UnsupportedSocketOperationError,
708 QSocks5SocketEngine::tr("Address type not supported"));
712 q->setError(QAbstractSocket::UnknownSocketError,
713 QSocks5SocketEngine::tr("Unknown SOCKSv5 proxy error code 0x%1").arg(int(socks5error), 16));
717 setErrorState(state, QString());
720 void QSocks5SocketEnginePrivate::reauthenticate()
722 Q_Q(QSocks5SocketEngine);
724 // we require authentication
726 emit q->proxyAuthenticationRequired(proxyInfo, &auth);
728 if (!auth.user().isEmpty() || !auth.password().isEmpty()) {
729 // we have new credentials, let's try again
730 QSOCKS5_DEBUG << "authentication failure: retrying connection";
731 socks5State = QSocks5SocketEnginePrivate::Uninitialized;
733 delete data->authenticator;
734 proxyInfo.setUser(auth.user());
735 proxyInfo.setPassword(auth.password());
736 data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
738 data->controlSocket->blockSignals(true);
739 data->controlSocket->abort();
740 data->controlSocket->blockSignals(false);
741 data->controlSocket->connectToHost(proxyInfo.hostName(), proxyInfo.port());
743 // authentication failure
745 setErrorState(AuthenticatingError);
746 data->controlSocket->close();
747 emitConnectionNotification();
751 void QSocks5SocketEnginePrivate::parseAuthenticationMethodReply()
753 // not enough data to begin
754 if (data->controlSocket->bytesAvailable() < 2)
757 QByteArray buf = data->controlSocket->read(2);
758 if (buf.at(0) != S5_VERSION_5) {
759 QSOCKS5_D_DEBUG << "Socks5 version incorrect";
760 setErrorState(SocksError);
761 data->controlSocket->close();
762 emitConnectionNotification();
766 bool authComplete = false;
767 if (uchar(buf.at(1)) == S5_AUTHMETHOD_NONE) {
769 } else if (uchar(buf.at(1)) == S5_AUTHMETHOD_NOTACCEPTABLE) {
772 } else if (buf.at(1) != data->authenticator->methodId()
773 || !data->authenticator->beginAuthenticate(data->controlSocket, &authComplete)) {
774 setErrorState(AuthenticatingError, QLatin1String("Socks5 host did not support authentication method."));
775 socketError = QAbstractSocket::SocketAccessError; // change the socket error
776 emitConnectionNotification();
783 socks5State = Authenticating;
786 void QSocks5SocketEnginePrivate::parseAuthenticatingReply()
788 bool authComplete = false;
789 if (!data->authenticator->continueAuthenticate(data->controlSocket, &authComplete)) {
797 void QSocks5SocketEnginePrivate::sendRequestMethod()
799 QHostAddress address;
802 if (mode == ConnectMode) {
803 command = S5_CONNECT;
804 address = peerAddress;
806 } else if (mode == BindMode) {
808 address = localAddress;
811 #ifndef QT_NO_UDPSOCKET
812 command = S5_UDP_ASSOCIATE;
813 address = localAddress; //data->controlSocket->localAddress();
819 buf.reserve(270); // big enough for domain name;
820 buf[0] = S5_VERSION_5;
823 if (peerName.isEmpty() && !qt_socks5_set_host_address_and_port(address, port, &buf)) {
824 QSOCKS5_DEBUG << "error setting address" << address << " : " << port;
825 //### set error code ....
827 } else if (!peerName.isEmpty() && !qt_socks5_set_host_name_and_port(peerName, port, &buf)) {
828 QSOCKS5_DEBUG << "error setting peer name" << peerName << " : " << port;
829 //### set error code ....
832 QSOCKS5_DEBUG << "sending" << dump(buf);
833 QByteArray sealedBuf;
834 if (!data->authenticator->seal(buf, &sealedBuf)) {
835 // ### Handle this error.
837 data->controlSocket->write(sealedBuf);
838 data->controlSocket->flush();
839 socks5State = RequestMethodSent;
842 void QSocks5SocketEnginePrivate::parseRequestMethodReply()
844 Q_Q(QSocks5SocketEngine);
845 QSOCKS5_DEBUG << "parseRequestMethodReply()";
848 if (!data->authenticator->unSeal(data->controlSocket, &inBuf)) {
849 // ### check error and not just not enough data
850 QSOCKS5_DEBUG << "unSeal failed, needs more data";
854 inBuf.prepend(receivedHeaderFragment);
855 receivedHeaderFragment.clear();
856 QSOCKS5_DEBUG << dump(inBuf);
857 if (inBuf.size() < 3) {
858 QSOCKS5_DEBUG << "need more data for request reply header .. put this data somewhere";
859 receivedHeaderFragment = inBuf;
863 QHostAddress address;
866 if (inBuf.at(0) != S5_VERSION_5 || inBuf.at(2) != 0x00) {
867 QSOCKS5_DEBUG << "socks protocol error";
868 setErrorState(SocksError);
869 } else if (inBuf.at(1) != S5_SUCCESS) {
870 Socks5Error socks5Error = Socks5Error(inBuf.at(1));
871 QSOCKS5_DEBUG << "Request error :" << socks5Error;
872 if ((socks5Error == SocksFailure || socks5Error == ConnectionNotAllowed)
873 && !peerName.isEmpty()) {
874 // Dante seems to use this error code to indicate hostname resolution failure
875 setErrorState(HostNameLookupError);
877 setErrorState(RequestError, socks5Error);
880 // connection success, retrieve the remote addresses
882 int err = qt_socks5_get_host_address_and_port(inBuf, &address, &port, &pos);
884 QSOCKS5_DEBUG << "error getting address";
885 setErrorState(SocksError);
886 } else if (err == 0) {
888 receivedHeaderFragment = inBuf;
891 inBuf.remove(0, pos);
892 for (int i = inBuf.size() - 1; i >= 0 ; --i)
893 data->controlSocket->ungetChar(inBuf.at(i));
897 if (socks5State == RequestMethodSent) {
899 localAddress = address;
902 if (mode == ConnectMode) {
903 socks5State = Connected;
904 // notify the upper layer that we're done
905 q->setState(QAbstractSocket::ConnectedState);
906 emitConnectionNotification();
907 } else if (mode == BindMode) {
908 socks5State = BindSuccess;
909 q->setState(QAbstractSocket::ListeningState);
911 socks5State = UdpAssociateSuccess;
913 } else if (socks5State == BindSuccess) {
914 // no error and we got a connection
915 bindData->peerAddress = address;
916 bindData->peerPort = port;
918 emitReadNotification();
921 data->controlSocket->close();
922 emitConnectionNotification();
926 void QSocks5SocketEnginePrivate::_q_emitPendingReadNotification()
928 Q_Q(QSocks5SocketEngine);
929 readNotificationPending = false;
930 if (readNotificationEnabled) {
931 QSOCKS5_D_DEBUG << "emitting readNotification";
932 QPointer<QSocks5SocketEngine> qq = q;
933 emit q->readNotification();
936 // check if there needs to be a new zero read notification
937 if (data && data->controlSocket->state() == QAbstractSocket::UnconnectedState
938 && data->controlSocket->error() == QAbstractSocket::RemoteHostClosedError) {
939 connectData->readBuffer.clear();
940 emitReadNotification();
945 void QSocks5SocketEnginePrivate::emitReadNotification()
947 Q_Q(QSocks5SocketEngine);
948 readNotificationActivated = true;
949 if (readNotificationEnabled && !readNotificationPending) {
950 QSOCKS5_D_DEBUG << "queueing readNotification";
951 readNotificationPending = true;
952 QMetaObject::invokeMethod(q, "_q_emitPendingReadNotification", Qt::QueuedConnection);
956 void QSocks5SocketEnginePrivate::_q_emitPendingWriteNotification()
958 writeNotificationPending = false;
959 Q_Q(QSocks5SocketEngine);
960 if (writeNotificationEnabled) {
961 QSOCKS5_D_DEBUG << "emitting writeNotification";
962 emit q->writeNotification();
966 void QSocks5SocketEnginePrivate::emitWriteNotification()
968 Q_Q(QSocks5SocketEngine);
969 writeNotificationActivated = true;
970 if (writeNotificationEnabled && !writeNotificationPending) {
971 QSOCKS5_D_DEBUG << "queueing writeNotification";
972 writeNotificationPending = true;
973 QMetaObject::invokeMethod(q, "_q_emitPendingWriteNotification", Qt::QueuedConnection);
977 void QSocks5SocketEnginePrivate::_q_emitPendingConnectionNotification()
979 connectionNotificationPending = false;
980 Q_Q(QSocks5SocketEngine);
981 QSOCKS5_D_DEBUG << "emitting connectionNotification";
982 emit q->connectionNotification();
985 void QSocks5SocketEnginePrivate::emitConnectionNotification()
987 Q_Q(QSocks5SocketEngine);
988 QSOCKS5_D_DEBUG << "queueing connectionNotification";
989 connectionNotificationPending = true;
990 QMetaObject::invokeMethod(q, "_q_emitPendingConnectionNotification", Qt::QueuedConnection);
993 QSocks5SocketEngine::QSocks5SocketEngine(QObject *parent)
994 :QAbstractSocketEngine(*new QSocks5SocketEnginePrivate(), parent)
998 QSocks5SocketEngine::~QSocks5SocketEngine()
1000 Q_D(QSocks5SocketEngine);
1003 delete d->data->authenticator;
1004 delete d->data->controlSocket;
1007 delete d->connectData;
1008 #ifndef QT_NO_UDPSOCKET
1010 delete d->udpData->udpSocket;
1018 static QBasicAtomicInt descriptorCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
1020 bool QSocks5SocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
1022 Q_D(QSocks5SocketEngine);
1024 d->socketDescriptor = descriptorCounter.fetchAndAddRelaxed(1);
1026 d->socketType = type;
1027 d->socketProtocol = protocol;
1032 bool QSocks5SocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState)
1034 Q_D(QSocks5SocketEngine);
1036 QSOCKS5_Q_DEBUG << "initialize" << socketDescriptor;
1038 // this is only valid for the other side of a bind, nothing else is supported
1040 if (socketState != QAbstractSocket::ConnectedState) {
1041 //### must be connected state ???
1045 QSocks5BindData *bindData = socks5BindStore()->retrieve(socketDescriptor);
1048 d->socketState = QAbstractSocket::ConnectedState;
1049 d->socketType = QAbstractSocket::TcpSocket;
1050 d->connectData = new QSocks5ConnectData;
1051 d->data = d->connectData;
1052 d->mode = QSocks5SocketEnginePrivate::ConnectMode;
1053 d->data->controlSocket = bindData->controlSocket;
1054 bindData->controlSocket = 0;
1055 d->data->controlSocket->setParent(this);
1056 d->socketProtocol = d->data->controlSocket->localAddress().protocol();
1057 d->data->authenticator = bindData->authenticator;
1058 bindData->authenticator = 0;
1059 d->localPort = bindData->localPort;
1060 d->localAddress = bindData->localAddress;
1061 d->peerPort = bindData->peerPort;
1062 d->peerAddress = bindData->peerAddress;
1065 QObject::connect(d->data->controlSocket, SIGNAL(connected()), this, SLOT(_q_controlSocketConnected()),
1066 Qt::DirectConnection);
1067 QObject::connect(d->data->controlSocket, SIGNAL(readyRead()), this, SLOT(_q_controlSocketReadNotification()),
1068 Qt::DirectConnection);
1069 QObject::connect(d->data->controlSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_controlSocketBytesWritten()),
1070 Qt::DirectConnection);
1071 QObject::connect(d->data->controlSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketError(QAbstractSocket::SocketError)),
1072 Qt::DirectConnection);
1073 QObject::connect(d->data->controlSocket, SIGNAL(disconnected()), this, SLOT(_q_controlSocketDisconnected()),
1074 Qt::DirectConnection);
1075 QObject::connect(d->data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
1076 this, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
1077 Qt::DirectConnection);
1079 d->socks5State = QSocks5SocketEnginePrivate::Connected;
1081 if (d->data->controlSocket->bytesAvailable() != 0)
1082 d->_q_controlSocketReadNotification();
1088 void QSocks5SocketEngine::setProxy(const QNetworkProxy &networkProxy)
1090 Q_D(QSocks5SocketEngine);
1091 d->proxyInfo = networkProxy;
1094 qintptr QSocks5SocketEngine::socketDescriptor() const
1096 Q_D(const QSocks5SocketEngine);
1097 return d->socketDescriptor;
1100 bool QSocks5SocketEngine::isValid() const
1102 Q_D(const QSocks5SocketEngine);
1103 return d->socketType != QAbstractSocket::UnknownSocketType
1104 && d->socks5State != QSocks5SocketEnginePrivate::SocksError
1105 && (d->socketError == QAbstractSocket::UnknownSocketError
1106 || d->socketError == QAbstractSocket::SocketTimeoutError
1107 || d->socketError == QAbstractSocket::UnfinishedSocketOperationError);
1110 bool QSocks5SocketEngine::connectInternal()
1112 Q_D(QSocks5SocketEngine);
1115 if (socketType() == QAbstractSocket::TcpSocket) {
1116 d->initialize(QSocks5SocketEnginePrivate::ConnectMode);
1117 #ifndef QT_NO_UDPSOCKET
1118 } else if (socketType() == QAbstractSocket::UdpSocket) {
1119 d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1120 // all udp needs to be bound
1121 if (!bind(QHostAddress(QLatin1String("0.0.0.0")), 0))
1124 setState(QAbstractSocket::ConnectedState);
1128 qFatal("QSocks5SocketEngine::connectToHost: in QTcpServer mode");
1133 if (d->socks5State == QSocks5SocketEnginePrivate::Uninitialized
1134 && d->socketState != QAbstractSocket::ConnectingState) {
1135 setState(QAbstractSocket::ConnectingState);
1136 //limit buffer in internal socket, data is buffered in the external socket under application control
1137 d->data->controlSocket->setReadBufferSize(65536);
1138 d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1144 bool QSocks5SocketEngine::connectToHost(const QHostAddress &address, quint16 port)
1146 Q_D(QSocks5SocketEngine);
1147 QSOCKS5_DEBUG << "connectToHost" << address << ':' << port;
1149 setPeerAddress(address);
1151 d->peerName.clear();
1153 return connectInternal();
1156 bool QSocks5SocketEngine::connectToHostByName(const QString &hostname, quint16 port)
1158 Q_D(QSocks5SocketEngine);
1160 setPeerAddress(QHostAddress());
1162 d->peerName = hostname;
1164 return connectInternal();
1167 void QSocks5SocketEnginePrivate::_q_controlSocketConnected()
1169 QSOCKS5_DEBUG << "_q_controlSocketConnected";
1170 QByteArray buf(3, 0);
1171 buf[0] = S5_VERSION_5;
1173 buf[2] = data->authenticator->methodId();
1174 data->controlSocket->write(buf);
1175 socks5State = AuthenticationMethodsSent;
1178 void QSocks5SocketEnginePrivate::_q_controlSocketReadNotification()
1180 QSOCKS5_D_DEBUG << "_q_controlSocketReadNotification socks5state" << s5StateToString(socks5State)
1181 << "bytes available" << data->controlSocket->bytesAvailable();
1183 if (data->controlSocket->bytesAvailable() == 0) {
1184 QSOCKS5_D_DEBUG << "########## bogus read why do we get these ... on windows only";
1188 switch (socks5State) {
1189 case AuthenticationMethodsSent:
1190 parseAuthenticationMethodReply();
1192 case Authenticating:
1193 parseAuthenticatingReply();
1195 case RequestMethodSent:
1196 parseRequestMethodReply();
1200 if (!data->authenticator->unSeal(data->controlSocket, &buf)) {
1201 // qDebug() << "unseal error maybe need to wait for more data";
1204 QSOCKS5_DEBUG << dump(buf);
1205 connectData->readBuffer += buf;
1206 emitReadNotification();
1211 // only get here if command is bind
1212 if (mode == BindMode) {
1213 parseRequestMethodReply();
1219 qWarning("QSocks5SocketEnginePrivate::_q_controlSocketReadNotification: "
1220 "Unexpectedly received data while in state=%d and mode=%d",
1226 void QSocks5SocketEnginePrivate::_q_controlSocketBytesWritten()
1228 QSOCKS5_DEBUG << "_q_controlSocketBytesWritten";
1230 if (socks5State != Connected
1231 || (mode == ConnectMode
1232 && data->controlSocket->bytesToWrite()))
1234 if (data->controlSocket->bytesToWrite() < MaxWriteBufferSize) {
1235 emitWriteNotification();
1236 writeNotificationActivated = false;
1240 void QSocks5SocketEnginePrivate::_q_controlSocketError(QAbstractSocket::SocketError error)
1242 QSOCKS5_D_DEBUG << "controlSocketError" << error << data->controlSocket->errorString();
1244 if (error == QAbstractSocket::SocketTimeoutError)
1245 return; // ignore this error -- comes from the waitFor* functions
1247 if (error == QAbstractSocket::RemoteHostClosedError
1248 && socks5State == Connected) {
1249 // clear the read buffer in connect mode so that bytes available returns 0
1250 // if there already is a read notification pending then this will be processed first
1251 if (!readNotificationPending)
1252 connectData->readBuffer.clear();
1253 emitReadNotification();
1254 data->controlSocket->close();
1255 // cause a disconnect in the outer socket
1256 emitWriteNotification();
1257 } else if (socks5State == Uninitialized
1258 || socks5State == AuthenticationMethodsSent
1259 || socks5State == Authenticating
1260 || socks5State == RequestMethodSent) {
1261 setErrorState(socks5State == Uninitialized ? ConnectError : ControlSocketError);
1262 data->controlSocket->close();
1263 emitConnectionNotification();
1265 q_func()->setError(data->controlSocket->error(), data->controlSocket->errorString());
1266 emitReadNotification();
1267 emitWriteNotification();
1271 void QSocks5SocketEnginePrivate::_q_controlSocketDisconnected()
1273 QSOCKS5_D_DEBUG << "_q_controlSocketDisconnected";
1276 void QSocks5SocketEnginePrivate::_q_controlSocketStateChanged(QAbstractSocket::SocketState state)
1278 QSOCKS5_D_DEBUG << "_q_controlSocketStateChanged" << state;
1281 #ifndef QT_NO_UDPSOCKET
1282 void QSocks5SocketEnginePrivate::checkForDatagrams() const
1284 // udp should be unbuffered so we need to do some polling at certain points
1285 if (udpData->udpSocket->hasPendingDatagrams())
1286 const_cast<QSocks5SocketEnginePrivate *>(this)->_q_udpSocketReadNotification();
1289 void QSocks5SocketEnginePrivate::_q_udpSocketReadNotification()
1291 QSOCKS5_D_DEBUG << "_q_udpSocketReadNotification()";
1293 // check some state stuff
1294 if (!udpData->udpSocket->hasPendingDatagrams()) {
1295 QSOCKS5_D_DEBUG << "false read ??";
1299 while (udpData->udpSocket->hasPendingDatagrams()) {
1300 QByteArray sealedBuf(udpData->udpSocket->pendingDatagramSize(), 0);
1301 QSOCKS5_D_DEBUG << "new datagram";
1302 udpData->udpSocket->readDatagram(sealedBuf.data(), sealedBuf.size());
1304 if (!data->authenticator->unSeal(sealedBuf, &inBuf)) {
1305 QSOCKS5_D_DEBUG << "failed unsealing datagram discarding";
1308 QSOCKS5_DEBUG << dump(inBuf);
1310 const char *buf = inBuf.constData();
1311 if (inBuf.size() < 4) {
1312 QSOCKS5_D_DEBUG << "bugus udp data, discarding";
1315 QSocks5RevivedDatagram datagram;
1316 if (buf[pos++] != 0 || buf[pos++] != 0) {
1317 QSOCKS5_D_DEBUG << "invalid datagram discarding";
1320 if (buf[pos++] != 0) { //### add fragmentation reading support
1321 QSOCKS5_D_DEBUG << "don't support fragmentation yet disgarding";
1324 if (qt_socks5_get_host_address_and_port(inBuf, &datagram.address, &datagram.port, &pos) != 1) {
1325 QSOCKS5_D_DEBUG << "failed to get address from datagram disgarding";
1328 datagram.data = QByteArray(&buf[pos], inBuf.size() - pos);
1329 udpData->pendingDatagrams.enqueue(datagram);
1331 emitReadNotification();
1333 #endif // QT_NO_UDPSOCKET
1335 bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
1337 Q_D(QSocks5SocketEngine);
1339 // when bind wee will block until the bind is finished as the info from the proxy server is needed
1341 QHostAddress address;
1342 if (addr.protocol() == QAbstractSocket::AnyIPProtocol)
1343 address = QHostAddress::AnyIPv4; //SOCKS5 doesn't support dual stack, and there isn't any implementation of udp on ipv6 yet
1348 if (socketType() == QAbstractSocket::TcpSocket) {
1349 d->initialize(QSocks5SocketEnginePrivate::BindMode);
1350 #ifndef QT_NO_UDPSOCKET
1351 } else if (socketType() == QAbstractSocket::UdpSocket) {
1352 d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1355 //### something invalid
1360 #ifndef QT_NO_UDPSOCKET
1361 if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1362 if (!d->udpData->udpSocket->bind(address, port)) {
1363 QSOCKS5_Q_DEBUG << "local udp bind failed";
1364 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1367 d->localAddress = d->udpData->udpSocket->localAddress();
1368 d->localPort = d->udpData->udpSocket->localPort();
1371 if (d->mode == QSocks5SocketEnginePrivate::BindMode) {
1372 d->localAddress = address;
1373 d->localPort = port;
1375 //### something invalid
1379 int msecs = SOCKS5_BLOCKING_BIND_TIMEOUT;
1380 QElapsedTimer stopWatch;
1382 d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
1383 if (!d->waitForConnected(msecs, 0) ||
1384 d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1385 // waitForConnected sets the error state and closes the socket
1386 QSOCKS5_Q_DEBUG << "waitForConnected to proxy server" << d->data->controlSocket->errorString();
1389 if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) {
1390 setState(QAbstractSocket::BoundState);
1392 #ifndef QT_NO_UDPSOCKET
1393 } else if (d->socks5State == QSocks5SocketEnginePrivate::UdpAssociateSuccess) {
1394 setState(QAbstractSocket::BoundState);
1395 d->udpData->associateAddress = d->localAddress;
1396 d->localAddress = QHostAddress();
1397 d->udpData->associatePort = d->localPort;
1400 #ifndef QT_NO_BEARERMANAGEMENT
1401 dummy.setProperty("_q_networksession", property("_q_networksession"));
1403 dummy.setProxy(QNetworkProxy::NoProxy);
1405 || writeDatagram(0,0, d->data->controlSocket->localAddress(), dummy.localPort()) != 0
1406 || !dummy.waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))
1407 || dummy.readDatagram(0,0, &d->localAddress, &d->localPort) != 0) {
1408 QSOCKS5_DEBUG << "udp actual address and port lookup failed";
1409 setState(QAbstractSocket::UnconnectedState);
1410 setError(dummy.error(), dummy.errorString());
1411 d->data->controlSocket->close();
1412 //### reset and error
1415 QSOCKS5_DEBUG << "udp actual address and port" << d->localAddress << ':' << d->localPort;
1417 #endif // QT_NO_UDPSOCKET
1420 // binding timed out
1421 setError(QAbstractSocket::SocketTimeoutError,
1422 QLatin1String(QT_TRANSLATE_NOOP("QSocks5SocketEngine", "Network operation timed out")));
1424 ///### delete d->udpSocket;
1425 ///### d->udpSocket = 0;
1430 bool QSocks5SocketEngine::listen()
1432 Q_D(QSocks5SocketEngine);
1434 QSOCKS5_Q_DEBUG << "listen()";
1436 // check that we are in bound and then go to listening.
1437 if (d->socketState == QAbstractSocket::BoundState) {
1438 d->socketState = QAbstractSocket::ListeningState;
1440 // check if we already have a connection
1441 if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
1442 d->emitReadNotification();
1449 int QSocks5SocketEngine::accept()
1451 Q_D(QSocks5SocketEngine);
1452 // check we are listing ---
1454 QSOCKS5_Q_DEBUG << "accept()";
1456 if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) {
1457 QSOCKS5_Q_DEBUG << "BindSuccess adding" << d->socketDescriptor << "to the bind store";
1458 d->data->controlSocket->disconnect();
1459 d->data->controlSocket->setParent(0);
1460 d->bindData->localAddress = d->localAddress;
1461 d->bindData->localPort = d->localPort;
1462 qintptr sd = d->socketDescriptor;
1463 socks5BindStore()->add(sd, d->bindData);
1466 d->socketDescriptor = 0;
1467 //### do something about this socket layer ... set it closed and an error about why ...
1468 // reset state and local port/address
1469 d->socks5State = QSocks5SocketEnginePrivate::Uninitialized; // ..??
1470 d->socketState = QAbstractSocket::UnconnectedState;
1476 void QSocks5SocketEngine::close()
1478 QSOCKS5_Q_DEBUG << "close()";
1479 Q_D(QSocks5SocketEngine);
1480 if (d->data && d->data->controlSocket) {
1481 if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) {
1483 QElapsedTimer stopWatch;
1485 while (!d->data->controlSocket->bytesToWrite()) {
1486 if (!d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed())))
1490 d->data->controlSocket->close();
1492 #ifndef QT_NO_UDPSOCKET
1493 if (d->udpData && d->udpData->udpSocket)
1494 d->udpData->udpSocket->close();
1498 qint64 QSocks5SocketEngine::bytesAvailable() const
1500 Q_D(const QSocks5SocketEngine);
1501 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
1502 return d->connectData->readBuffer.size();
1503 #ifndef QT_NO_UDPSOCKET
1504 else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode
1505 && !d->udpData->pendingDatagrams.isEmpty())
1506 return d->udpData->pendingDatagrams.first().data.size();
1511 qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
1513 Q_D(QSocks5SocketEngine);
1514 QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ')';
1515 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
1516 if (d->connectData->readBuffer.size() == 0) {
1517 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
1518 //imitate remote closed
1520 setError(QAbstractSocket::RemoteHostClosedError,
1521 QLatin1String("Remote host closed connection###"));
1522 setState(QAbstractSocket::UnconnectedState);
1525 return 0; // nothing to be read
1528 qint64 copy = qMin<qint64>(d->connectData->readBuffer.size(), maxlen);
1529 memcpy(data, d->connectData->readBuffer.constData(), copy);
1530 d->connectData->readBuffer.remove(0, copy);
1531 QSOCKS5_DEBUG << "read" << dump(QByteArray(data, copy));
1533 #ifndef QT_NO_UDPSOCKET
1534 } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1535 return readDatagram(data, maxlen);
1541 qint64 QSocks5SocketEngine::write(const char *data, qint64 len)
1543 Q_D(QSocks5SocketEngine);
1544 QSOCKS5_Q_DEBUG << "write" << dump(QByteArray(data, len));
1546 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
1547 // clamp down the amount of bytes to transfer at once
1548 len = qMin<qint64>(len, MaxWriteBufferSize) - d->data->controlSocket->bytesToWrite();
1552 QByteArray buf = QByteArray::fromRawData(data, len);
1553 QByteArray sealedBuf;
1554 if (!d->data->authenticator->seal(buf, &sealedBuf)) {
1555 // ### Handle this error.
1558 qint64 written = d->data->controlSocket->write(sealedBuf);
1560 QSOCKS5_Q_DEBUG << "native write returned" << written;
1563 d->data->controlSocket->waitForBytesWritten(0);
1564 //NB: returning len rather than written for the OK case, because the "sealing" may increase the length
1566 #ifndef QT_NO_UDPSOCKET
1567 } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
1568 // send to connected address
1569 return writeDatagram(data, len, d->peerAddress, d->peerPort);
1572 //### set an error ???
1576 #ifndef QT_NO_UDPSOCKET
1577 #ifndef QT_NO_NETWORKINTERFACE
1578 bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &,
1579 const QNetworkInterface &)
1581 setError(QAbstractSocket::UnsupportedSocketOperationError,
1582 QLatin1String("Operation on socket is not supported"));
1586 bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &,
1587 const QNetworkInterface &)
1589 setError(QAbstractSocket::UnsupportedSocketOperationError,
1590 QLatin1String("Operation on socket is not supported"));
1595 QNetworkInterface QSocks5SocketEngine::multicastInterface() const
1597 return QNetworkInterface();
1600 bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &)
1602 setError(QAbstractSocket::UnsupportedSocketOperationError,
1603 QLatin1String("Operation on socket is not supported"));
1606 #endif // QT_NO_NETWORKINTERFACE
1608 qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr,
1611 Q_D(QSocks5SocketEngine);
1613 d->checkForDatagrams();
1615 if (d->udpData->pendingDatagrams.isEmpty())
1618 QSocks5RevivedDatagram datagram = d->udpData->pendingDatagrams.dequeue();
1619 int copyLen = qMin<int>(maxlen, datagram.data.size());
1620 memcpy(data, datagram.data.constData(), copyLen);
1622 *addr = datagram.address;
1624 *port = datagram.port;
1628 qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QHostAddress &address,
1631 Q_D(QSocks5SocketEngine);
1633 // it is possible to send with out first binding with udp, but socks5 requires a bind.
1635 d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
1636 // all udp needs to be bound
1637 if (!bind(QHostAddress(QLatin1String("0.0.0.0")), 0)) {
1644 outBuf.reserve(270 + len);
1648 if (!qt_socks5_set_host_address_and_port(address, port, &outBuf)) {
1650 outBuf += QByteArray(data, len);
1651 QSOCKS5_DEBUG << "sending" << dump(outBuf);
1652 QByteArray sealedBuf;
1653 if (!d->data->authenticator->seal(outBuf, &sealedBuf)) {
1654 QSOCKS5_DEBUG << "sealing data failed";
1655 setError(QAbstractSocket::SocketAccessError, d->data->authenticator->errorString());
1658 if (d->udpData->udpSocket->writeDatagram(sealedBuf, d->udpData->associateAddress, d->udpData->associatePort) != sealedBuf.size()) {
1659 //### try frgamenting
1660 if (d->udpData->udpSocket->error() == QAbstractSocket::DatagramTooLargeError)
1661 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1662 //### else maybe more serious error
1669 bool QSocks5SocketEngine::hasPendingDatagrams() const
1671 Q_D(const QSocks5SocketEngine);
1672 Q_INIT_CHECK(false);
1674 d->checkForDatagrams();
1676 return !d->udpData->pendingDatagrams.isEmpty();
1679 qint64 QSocks5SocketEngine::pendingDatagramSize() const
1681 Q_D(const QSocks5SocketEngine);
1683 d->checkForDatagrams();
1685 if (!d->udpData->pendingDatagrams.isEmpty())
1686 return d->udpData->pendingDatagrams.head().data.size();
1689 #endif // QT_NO_UDPSOCKET
1691 qint64 QSocks5SocketEngine::bytesToWrite() const
1693 Q_D(const QSocks5SocketEngine);
1694 if (d->data && d->data->controlSocket) {
1695 return d->data->controlSocket->bytesToWrite();
1701 int QSocks5SocketEngine::option(SocketOption option) const
1703 Q_D(const QSocks5SocketEngine);
1704 if (d->data && d->data->controlSocket) {
1705 // convert the enum and call the real socket
1706 if (option == QAbstractSocketEngine::LowDelayOption)
1707 return d->data->controlSocket->socketOption(QAbstractSocket::LowDelayOption).toInt();
1708 if (option == QAbstractSocketEngine::KeepAliveOption)
1709 return d->data->controlSocket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
1714 bool QSocks5SocketEngine::setOption(SocketOption option, int value)
1716 Q_D(QSocks5SocketEngine);
1717 if (d->data && d->data->controlSocket) {
1718 // convert the enum and call the real socket
1719 if (option == QAbstractSocketEngine::LowDelayOption)
1720 d->data->controlSocket->setSocketOption(QAbstractSocket::LowDelayOption, value);
1721 if (option == QAbstractSocketEngine::KeepAliveOption)
1722 d->data->controlSocket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
1728 bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
1730 if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1733 const Socks5State wantedState =
1734 mode == ConnectMode ? Connected :
1735 mode == BindMode ? BindSuccess :
1736 UdpAssociateSuccess;
1738 QElapsedTimer stopWatch;
1741 while (socks5State != wantedState) {
1742 if (!data->controlSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
1743 if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1746 setErrorState(QSocks5SocketEnginePrivate::ControlSocketError);
1747 if (timedOut && data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1756 bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
1758 Q_D(QSocks5SocketEngine);
1759 QSOCKS5_DEBUG << "waitForRead" << msecs;
1761 d->readNotificationActivated = false;
1763 QElapsedTimer stopWatch;
1766 // are we connected yet?
1767 if (!d->waitForConnected(msecs, timedOut))
1769 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1773 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode ||
1774 d->mode == QSocks5SocketEnginePrivate::BindMode) {
1775 while (!d->readNotificationActivated) {
1776 if (!d->data->controlSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
1777 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1780 setError(d->data->controlSocket->error(), d->data->controlSocket->errorString());
1781 if (timedOut && d->data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
1786 #ifndef QT_NO_UDPSOCKET
1788 while (!d->readNotificationActivated) {
1789 if (!d->udpData->udpSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
1790 setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
1791 if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
1796 #endif // QT_NO_UDPSOCKET
1800 bool ret = d->readNotificationActivated;
1801 d->readNotificationActivated = false;
1803 QSOCKS5_DEBUG << "waitForRead returned" << ret;
1808 bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
1810 Q_D(QSocks5SocketEngine);
1811 QSOCKS5_DEBUG << "waitForWrite" << msecs;
1813 QElapsedTimer stopWatch;
1816 // are we connected yet?
1817 if (!d->waitForConnected(msecs, timedOut))
1819 if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
1824 // flush any bytes we may still have buffered in the time that we have left
1825 if (d->data->controlSocket->bytesToWrite())
1826 d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed()));
1827 while ((msecs == -1 || stopWatch.elapsed() < msecs)
1828 && d->data->controlSocket->state() == QAbstractSocket::ConnectedState
1829 && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize)
1830 d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed()));
1831 return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
1834 bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
1835 bool checkRead, bool checkWrite,
1836 int msecs, bool *timedOut)
1838 Q_UNUSED(checkRead);
1840 bool canRead = waitForRead(msecs, timedOut);
1842 *readyToRead = canRead;
1846 bool canWrite = waitForWrite(msecs, timedOut);
1848 *readyToWrite = canWrite;
1852 bool QSocks5SocketEngine::isReadNotificationEnabled() const
1854 Q_D(const QSocks5SocketEngine);
1855 return d->readNotificationEnabled;
1858 void QSocks5SocketEngine::setReadNotificationEnabled(bool enable)
1860 Q_D(QSocks5SocketEngine);
1862 QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ')';
1864 bool emitSignal = false;
1865 if (!d->readNotificationEnabled
1867 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
1868 emitSignal = !d->connectData->readBuffer.isEmpty();
1869 #ifndef QT_NO_UDPSOCKET
1870 else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode)
1871 emitSignal = !d->udpData->pendingDatagrams.isEmpty();
1873 else if (d->mode == QSocks5SocketEnginePrivate::BindMode
1874 && d->socketState == QAbstractSocket::ListeningState
1875 && d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
1879 d->readNotificationEnabled = enable;
1882 d->emitReadNotification();
1885 bool QSocks5SocketEngine::isWriteNotificationEnabled() const
1887 Q_D(const QSocks5SocketEngine);
1888 return d->writeNotificationEnabled;
1891 void QSocks5SocketEngine::setWriteNotificationEnabled(bool enable)
1893 Q_D(QSocks5SocketEngine);
1894 d->writeNotificationEnabled = enable;
1895 if (enable && d->socketState == QAbstractSocket::ConnectedState) {
1896 if (d->mode == QSocks5SocketEnginePrivate::ConnectMode && d->data->controlSocket->bytesToWrite())
1897 return; // will be emitted as a result of bytes written
1898 d->emitWriteNotification();
1899 d->writeNotificationActivated = false;
1903 bool QSocks5SocketEngine::isExceptionNotificationEnabled() const
1905 Q_D(const QSocks5SocketEngine);
1906 return d->exceptNotificationEnabled;
1909 void QSocks5SocketEngine::setExceptionNotificationEnabled(bool enable)
1911 Q_D(QSocks5SocketEngine);
1912 d->exceptNotificationEnabled = enable;
1915 QAbstractSocketEngine *
1916 QSocks5SocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
1917 const QNetworkProxy &proxy, QObject *parent)
1919 Q_UNUSED(socketType);
1921 // proxy type must have been resolved by now
1922 if (proxy.type() != QNetworkProxy::Socks5Proxy) {
1923 QSOCKS5_DEBUG << "not proxying";
1926 QScopedPointer<QSocks5SocketEngine> engine(new QSocks5SocketEngine(parent));
1927 engine->setProxy(proxy);
1928 return engine.take();
1931 QAbstractSocketEngine *QSocks5SocketEngineHandler::createSocketEngine(qintptr socketDescriptor, QObject *parent)
1933 QSOCKS5_DEBUG << "createSocketEngine" << socketDescriptor;
1934 if (socks5BindStore()->contains(socketDescriptor)) {
1935 QSOCKS5_DEBUG << "bind store contains" << socketDescriptor;
1936 return new QSocks5SocketEngine(parent);
1941 #endif // QT_NO_SOCKS5