1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** Contact: Qt Software Information ([email protected])
|
---|
5 | **
|
---|
6 | ** This file is part of the QtNetwork module of the Qt Toolkit.
|
---|
7 | **
|
---|
8 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
9 | ** Commercial Usage
|
---|
10 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
11 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
12 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
13 | ** a written agreement between you and Nokia.
|
---|
14 | **
|
---|
15 | ** GNU Lesser General Public License Usage
|
---|
16 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
17 | ** General Public License version 2.1 as published by the Free Software
|
---|
18 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
19 | ** packaging of this file. Please review the following information to
|
---|
20 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
21 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
22 | **
|
---|
23 | ** In addition, as a special exception, Nokia gives you certain
|
---|
24 | ** additional rights. These rights are described in the Nokia Qt LGPL
|
---|
25 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
|
---|
26 | ** package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you are unsure which license is appropriate for your use, please
|
---|
37 | ** contact the sales department at [email protected].
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include "qsocks5socketengine_p.h"
|
---|
43 |
|
---|
44 | #ifndef QT_NO_SOCKS5
|
---|
45 |
|
---|
46 | #include "qtcpsocket.h"
|
---|
47 | #include "qudpsocket.h"
|
---|
48 | #include "qtcpserver.h"
|
---|
49 | #include "qdebug.h"
|
---|
50 | #include "qhash.h"
|
---|
51 | #include "qqueue.h"
|
---|
52 | #include "qdatetime.h"
|
---|
53 | #include "qmutex.h"
|
---|
54 | #include "qthread.h"
|
---|
55 | #include "qcoreapplication.h"
|
---|
56 | #include "qurl.h"
|
---|
57 | #include "qauthenticator.h"
|
---|
58 | #include <qendian.h>
|
---|
59 |
|
---|
60 | QT_BEGIN_NAMESPACE
|
---|
61 |
|
---|
62 | static const int MaxWriteBufferSize = 128*1024;
|
---|
63 |
|
---|
64 | //#define QSOCKS5SOCKETLAYER_DEBUG
|
---|
65 |
|
---|
66 | #define MAX_DATA_DUMP 256
|
---|
67 | #if !defined(Q_OS_WINCE)
|
---|
68 | #define SOCKS5_BLOCKING_BIND_TIMEOUT 5000
|
---|
69 | #else
|
---|
70 | #define SOCKS5_BLOCKING_BIND_TIMEOUT 10000
|
---|
71 | #endif
|
---|
72 |
|
---|
73 | #define Q_INIT_CHECK(returnValue) do { \
|
---|
74 | if (!d->data) { \
|
---|
75 | return returnValue; \
|
---|
76 | } } while (0)
|
---|
77 |
|
---|
78 | #define S5_VERSION_5 0x05
|
---|
79 | #define S5_CONNECT 0x01
|
---|
80 | #define S5_BIND 0x02
|
---|
81 | #define S5_UDP_ASSOCIATE 0x03
|
---|
82 | #define S5_IP_V4 0x01
|
---|
83 | #define S5_DOMAINNAME 0x03
|
---|
84 | #define S5_IP_V6 0x04
|
---|
85 | #define S5_SUCCESS 0x00
|
---|
86 | #define S5_R_ERROR_SOCKS_FAILURE 0x01
|
---|
87 | #define S5_R_ERROR_CON_NOT_ALLOWED 0x02
|
---|
88 | #define S5_R_ERROR_NET_UNREACH 0x03
|
---|
89 | #define S5_R_ERROR_HOST_UNREACH 0x04
|
---|
90 | #define S5_R_ERROR_CONN_REFUSED 0x05
|
---|
91 | #define S5_R_ERROR_TTL 0x06
|
---|
92 | #define S5_R_ERROR_CMD_NOT_SUPPORTED 0x07
|
---|
93 | #define S5_R_ERROR_ADD_TYPE_NOT_SUPORTED 0x08
|
---|
94 |
|
---|
95 | #define S5_AUTHMETHOD_NONE 0x00
|
---|
96 | #define S5_AUTHMETHOD_PASSWORD 0x02
|
---|
97 | #define S5_AUTHMETHOD_NOTACCEPTABLE 0xFF
|
---|
98 |
|
---|
99 | #define S5_PASSWORDAUTH_VERSION 0x01
|
---|
100 |
|
---|
101 | #ifdef QSOCKS5SOCKETLAYER_DEBUG
|
---|
102 | # define QSOCKS5_Q_DEBUG qDebug() << this
|
---|
103 | # define QSOCKS5_D_DEBUG qDebug() << q_ptr
|
---|
104 | # define QSOCKS5_DEBUG qDebug() << "[QSocks5]"
|
---|
105 | static QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State s)
|
---|
106 | {
|
---|
107 | switch (s) {
|
---|
108 | case QSocks5SocketEnginePrivate::Uninitialized: return QLatin1String("Uninitialized");
|
---|
109 | case QSocks5SocketEnginePrivate::ConnectError: return QLatin1String("ConnectError");
|
---|
110 | case QSocks5SocketEnginePrivate::AuthenticationMethodsSent: return QLatin1String("AuthenticationMethodsSent");
|
---|
111 | case QSocks5SocketEnginePrivate::Authenticating: return QLatin1String("Authenticating");
|
---|
112 | case QSocks5SocketEnginePrivate::AuthenticatingError: return QLatin1String("AuthenticatingError");
|
---|
113 | case QSocks5SocketEnginePrivate::RequestMethodSent: return QLatin1String("RequestMethodSent");
|
---|
114 | case QSocks5SocketEnginePrivate::RequestError: return QLatin1String("RequestError");
|
---|
115 | case QSocks5SocketEnginePrivate::Connected: return QLatin1String("Connected");
|
---|
116 | case QSocks5SocketEnginePrivate::UdpAssociateSuccess: return QLatin1String("UdpAssociateSuccess");
|
---|
117 | case QSocks5SocketEnginePrivate::BindSuccess: return QLatin1String("BindSuccess");
|
---|
118 | case QSocks5SocketEnginePrivate::ControlSocketError: return QLatin1String("ControlSocketError");
|
---|
119 | case QSocks5SocketEnginePrivate::SocksError: return QLatin1String("SocksError");
|
---|
120 | case QSocks5SocketEnginePrivate::HostNameLookupError: return QLatin1String("HostNameLookupError");
|
---|
121 | default: break;
|
---|
122 | }
|
---|
123 | return QLatin1String("unknown state");
|
---|
124 | }
|
---|
125 |
|
---|
126 | static QString dump(const QByteArray &buf)
|
---|
127 | {
|
---|
128 | QString data;
|
---|
129 | for (int i = 0; i < qMin<int>(MAX_DATA_DUMP, buf.size()); ++i) {
|
---|
130 | if (i) data += QLatin1Char(' ');
|
---|
131 | uint val = (unsigned char)buf.at(i);
|
---|
132 | // data += QString("0x%1").arg(val, 3, 16, QLatin1Char('0'));
|
---|
133 | data += QString::number(val);
|
---|
134 | }
|
---|
135 | if (buf.size() > MAX_DATA_DUMP)
|
---|
136 | data += QLatin1String(" ...");
|
---|
137 |
|
---|
138 | return QString::fromLatin1("size: %1 data: { %2 }").arg(buf.size()).arg(data);
|
---|
139 | }
|
---|
140 |
|
---|
141 | #else
|
---|
142 | # define QSOCKS5_DEBUG if (0) qDebug()
|
---|
143 | # define QSOCKS5_Q_DEBUG if (0) qDebug()
|
---|
144 | # define QSOCKS5_D_DEBUG if (0) qDebug()
|
---|
145 |
|
---|
146 | static inline QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State) { return QString(); }
|
---|
147 | static inline QString dump(const QByteArray &) { return QString(); }
|
---|
148 | #endif
|
---|
149 |
|
---|
150 | /*
|
---|
151 | inserts the host address in buf at pos and updates pos.
|
---|
152 | if the func fails the data in buf and the vallue of pos is undefined
|
---|
153 | */
|
---|
154 | static bool qt_socks5_set_host_address_and_port(const QHostAddress &address, quint16 port, QByteArray *pBuf)
|
---|
155 | {
|
---|
156 | QSOCKS5_DEBUG << "setting [" << address << ":" << port << "]";
|
---|
157 |
|
---|
158 | union {
|
---|
159 | quint16 port;
|
---|
160 | quint32 ipv4;
|
---|
161 | QIPv6Address ipv6;
|
---|
162 | char ptr;
|
---|
163 | } data;
|
---|
164 |
|
---|
165 | // add address
|
---|
166 | if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
---|
167 | data.ipv4 = qToBigEndian<quint32>(address.toIPv4Address());
|
---|
168 | pBuf->append(S5_IP_V4);
|
---|
169 | pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv4));
|
---|
170 | } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
|
---|
171 | data.ipv6 = address.toIPv6Address();
|
---|
172 | pBuf->append(S5_IP_V6);
|
---|
173 | pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv6));
|
---|
174 | } else {
|
---|
175 | return false;
|
---|
176 | }
|
---|
177 |
|
---|
178 | // add port
|
---|
179 | data.port = qToBigEndian<quint16>(port);
|
---|
180 | pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
|
---|
181 | return true;
|
---|
182 | }
|
---|
183 |
|
---|
184 | /*
|
---|
185 | like above, but for a hostname
|
---|
186 | */
|
---|
187 | static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 port, QByteArray *pBuf)
|
---|
188 | {
|
---|
189 | QSOCKS5_DEBUG << "setting [" << hostname << ":" << port << "]";
|
---|
190 |
|
---|
191 | QByteArray encodedHostName = QUrl::toAce(hostname);
|
---|
192 | QByteArray &buf = *pBuf;
|
---|
193 |
|
---|
194 | if (encodedHostName.length() > 255)
|
---|
195 | return false;
|
---|
196 |
|
---|
197 | buf.append(S5_DOMAINNAME);
|
---|
198 | buf.append(uchar(encodedHostName.length()));
|
---|
199 | buf.append(encodedHostName);
|
---|
200 |
|
---|
201 | // add port
|
---|
202 | union {
|
---|
203 | quint16 port;
|
---|
204 | char ptr;
|
---|
205 | } data;
|
---|
206 | data.port = qToBigEndian<quint16>(port);
|
---|
207 | buf.append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
|
---|
208 |
|
---|
209 | return true;
|
---|
210 | }
|
---|
211 |
|
---|
212 |
|
---|
213 | /*
|
---|
214 | retrives the host address in buf at pos and updates pos.
|
---|
215 | if the func fails the value of the address and the pos is undefined
|
---|
216 | */
|
---|
217 | static bool qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddress *pAddress, quint16 *pPort, int *pPos)
|
---|
218 | {
|
---|
219 | bool ret = false;
|
---|
220 | int pos = *pPos;
|
---|
221 | const unsigned char *pBuf = reinterpret_cast<const unsigned char*>(buf.constData());
|
---|
222 | QHostAddress address;
|
---|
223 | quint16 port = 0;
|
---|
224 |
|
---|
225 | if (buf.size() - pos < 1) {
|
---|
226 | QSOCKS5_DEBUG << "need more data address/port";
|
---|
227 | return false;
|
---|
228 | }
|
---|
229 | if (pBuf[pos] == S5_IP_V4) {
|
---|
230 | pos++;
|
---|
231 | if (buf.size() - pos < 4) {
|
---|
232 | QSOCKS5_DEBUG << "need more data for ip4 address";
|
---|
233 | return false;
|
---|
234 | }
|
---|
235 | address.setAddress(qFromBigEndian<quint32>(&pBuf[pos]));
|
---|
236 | pos += 4;
|
---|
237 | ret = true;
|
---|
238 | } else if (pBuf[pos] == S5_IP_V6) {
|
---|
239 | pos++;
|
---|
240 | if (buf.size() - pos < 16) {
|
---|
241 | QSOCKS5_DEBUG << "need more data for ip6 address";
|
---|
242 | return false;
|
---|
243 | }
|
---|
244 | QIPv6Address add;
|
---|
245 | for (int i = 0; i < 16; ++i)
|
---|
246 | add[i] = buf[pos++];
|
---|
247 | ret = true;
|
---|
248 | } else if (pBuf[pos] == S5_DOMAINNAME){
|
---|
249 | // just skip it
|
---|
250 | pos++;
|
---|
251 | qDebug() << "skipping hostname of len" << uint(pBuf[pos]);
|
---|
252 | pos += uchar(pBuf[pos]);
|
---|
253 | } else {
|
---|
254 | QSOCKS5_DEBUG << "invalid address type" << (int)pBuf[pos];
|
---|
255 | ret = false;
|
---|
256 | }
|
---|
257 |
|
---|
258 | if (ret) {
|
---|
259 | if (buf.size() - pos < 2) {
|
---|
260 | QSOCKS5_DEBUG << "need more data for port";
|
---|
261 | return false;
|
---|
262 | }
|
---|
263 | port = qFromBigEndian<quint16>(&pBuf[pos]);
|
---|
264 | pos += 2;
|
---|
265 | }
|
---|
266 |
|
---|
267 | if (ret) {
|
---|
268 | QSOCKS5_DEBUG << "got [" << address << ":" << port << "]";
|
---|
269 | *pAddress = address;
|
---|
270 | *pPort = port;
|
---|
271 | *pPos = pos;
|
---|
272 | }
|
---|
273 |
|
---|
274 | return ret;
|
---|
275 | }
|
---|
276 |
|
---|
277 | /*
|
---|
278 | Returns the difference between msecs and elapsed. If msecs is -1,
|
---|
279 | however, -1 is returned.
|
---|
280 | */
|
---|
281 | static int qt_timeout_value(int msecs, int elapsed)
|
---|
282 | {
|
---|
283 | if (msecs == -1)
|
---|
284 | return -1;
|
---|
285 |
|
---|
286 | int timeout = msecs - elapsed;
|
---|
287 | return timeout < 0 ? 0 : timeout;
|
---|
288 | }
|
---|
289 |
|
---|
290 | struct QSocks5Data
|
---|
291 | {
|
---|
292 | QTcpSocket *controlSocket;
|
---|
293 | QSocks5Authenticator *authenticator;
|
---|
294 | };
|
---|
295 |
|
---|
296 | struct QSocks5ConnectData : public QSocks5Data
|
---|
297 | {
|
---|
298 | QByteArray readBuffer;
|
---|
299 | };
|
---|
300 |
|
---|
301 | struct QSocks5BindData : public QSocks5Data
|
---|
302 | {
|
---|
303 | QHostAddress localAddress;
|
---|
304 | quint16 localPort;
|
---|
305 | QHostAddress peerAddress;
|
---|
306 | quint16 peerPort;
|
---|
307 | QDateTime timeStamp;
|
---|
308 | };
|
---|
309 |
|
---|
310 | struct QSocks5RevivedDatagram
|
---|
311 | {
|
---|
312 | QByteArray data;
|
---|
313 | QHostAddress address;
|
---|
314 | quint16 port;
|
---|
315 | };
|
---|
316 |
|
---|
317 | #ifndef QT_NO_UDPSOCKET
|
---|
318 | struct QSocks5UdpAssociateData : public QSocks5Data
|
---|
319 | {
|
---|
320 | QUdpSocket *udpSocket;
|
---|
321 | QHostAddress associateAddress;
|
---|
322 | quint16 associatePort;
|
---|
323 | QQueue<QSocks5RevivedDatagram> pendingDatagrams;
|
---|
324 | };
|
---|
325 | #endif
|
---|
326 |
|
---|
327 | // needs to be thread safe
|
---|
328 | class QSocks5BindStore : public QObject
|
---|
329 | {
|
---|
330 | public:
|
---|
331 | QSocks5BindStore();
|
---|
332 | ~QSocks5BindStore();
|
---|
333 |
|
---|
334 | void add(int socketDescriptor, QSocks5BindData *bindData);
|
---|
335 | bool contains(int socketDescriptor);
|
---|
336 | QSocks5BindData *retrieve(int socketDescriptor);
|
---|
337 |
|
---|
338 | protected:
|
---|
339 | void timerEvent(QTimerEvent * event);
|
---|
340 |
|
---|
341 | QMutex mutex;
|
---|
342 | int sweepTimerId;
|
---|
343 | //socket descriptor, data, timestamp
|
---|
344 | QHash<int, QSocks5BindData *> store;
|
---|
345 | };
|
---|
346 |
|
---|
347 | Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore)
|
---|
348 |
|
---|
349 | QSocks5BindStore::QSocks5BindStore()
|
---|
350 | : mutex(QMutex::Recursive)
|
---|
351 | , sweepTimerId(-1)
|
---|
352 | {
|
---|
353 | QCoreApplication *app = QCoreApplication::instance();
|
---|
354 | if (app && app->thread() != thread())
|
---|
355 | moveToThread(app->thread());
|
---|
356 | }
|
---|
357 |
|
---|
358 | QSocks5BindStore::~QSocks5BindStore()
|
---|
359 | {
|
---|
360 | }
|
---|
361 |
|
---|
362 | void QSocks5BindStore::add(int socketDescriptor, QSocks5BindData *bindData)
|
---|
363 | {
|
---|
364 | QMutexLocker lock(&mutex);
|
---|
365 | if (store.contains(socketDescriptor)) {
|
---|
366 | // qDebug() << "delete it";
|
---|
367 | }
|
---|
368 | bindData->timeStamp = QDateTime::currentDateTime();
|
---|
369 | store.insert(socketDescriptor, bindData);
|
---|
370 | // start sweep timer if not started
|
---|
371 | if (sweepTimerId == -1)
|
---|
372 | sweepTimerId = startTimer(60000);
|
---|
373 | }
|
---|
374 |
|
---|
375 | bool QSocks5BindStore::contains(int socketDescriptor)
|
---|
376 | {
|
---|
377 | QMutexLocker lock(&mutex);
|
---|
378 | return store.contains(socketDescriptor);
|
---|
379 | }
|
---|
380 |
|
---|
381 | QSocks5BindData *QSocks5BindStore::retrieve(int socketDescriptor)
|
---|
382 | {
|
---|
383 | QMutexLocker lock(&mutex);
|
---|
384 | if (!store.contains(socketDescriptor))
|
---|
385 | return 0;
|
---|
386 | QSocks5BindData *bindData = store.take(socketDescriptor);
|
---|
387 | if (bindData) {
|
---|
388 | if (bindData->controlSocket->thread() != QThread::currentThread()) {
|
---|
389 | qWarning("Can not access socks5 bind data from different thread");
|
---|
390 | return 0;
|
---|
391 | }
|
---|
392 | } else {
|
---|
393 | QSOCKS5_DEBUG << "__ERROR__ binddata == 0";
|
---|
394 | }
|
---|
395 | // stop the sweep timer if not needed
|
---|
396 | if (store.isEmpty()) {
|
---|
397 | killTimer(sweepTimerId);
|
---|
398 | sweepTimerId = -1;
|
---|
399 | }
|
---|
400 | return bindData;
|
---|
401 | }
|
---|
402 |
|
---|
403 | void QSocks5BindStore::timerEvent(QTimerEvent * event)
|
---|
404 | {
|
---|
405 | QMutexLocker lock(&mutex);
|
---|
406 | if (event->timerId() == sweepTimerId) {
|
---|
407 | QSOCKS5_DEBUG << "QSocks5BindStore performing sweep";
|
---|
408 | QMutableHashIterator<int, QSocks5BindData *> it(store);
|
---|
409 | while (it.hasNext()) {
|
---|
410 | it.next();
|
---|
411 | if (it.value()->timeStamp.secsTo(QDateTime::currentDateTime()) > 350) {
|
---|
412 | QSOCKS5_DEBUG << "QSocks5BindStore removing JJJJ";
|
---|
413 | it.remove();
|
---|
414 | }
|
---|
415 | }
|
---|
416 | }
|
---|
417 | }
|
---|
418 |
|
---|
419 | QSocks5Authenticator::QSocks5Authenticator()
|
---|
420 | {
|
---|
421 | }
|
---|
422 |
|
---|
423 | QSocks5Authenticator::~QSocks5Authenticator()
|
---|
424 | {
|
---|
425 | }
|
---|
426 |
|
---|
427 | char QSocks5Authenticator::methodId()
|
---|
428 | {
|
---|
429 | return 0x00;
|
---|
430 | }
|
---|
431 |
|
---|
432 | bool QSocks5Authenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
|
---|
433 | {
|
---|
434 | Q_UNUSED(socket);
|
---|
435 | *completed = true;
|
---|
436 | return true;
|
---|
437 | }
|
---|
438 |
|
---|
439 | bool QSocks5Authenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
|
---|
440 | {
|
---|
441 | Q_UNUSED(socket);
|
---|
442 | *completed = true;
|
---|
443 | return true;
|
---|
444 | }
|
---|
445 |
|
---|
446 | bool QSocks5Authenticator::seal(const QByteArray buf, QByteArray *sealedBuf)
|
---|
447 | {
|
---|
448 | *sealedBuf = buf;
|
---|
449 | return true;
|
---|
450 | }
|
---|
451 |
|
---|
452 | bool QSocks5Authenticator::unSeal(const QByteArray sealedBuf, QByteArray *buf)
|
---|
453 | {
|
---|
454 | *buf = sealedBuf;
|
---|
455 | return true;
|
---|
456 | }
|
---|
457 |
|
---|
458 | bool QSocks5Authenticator::unSeal(QTcpSocket *sealedSocket, QByteArray *buf)
|
---|
459 | {
|
---|
460 | return unSeal(sealedSocket->readAll(), buf);
|
---|
461 | }
|
---|
462 |
|
---|
463 | QSocks5PasswordAuthenticator::QSocks5PasswordAuthenticator(const QString &userName, const QString &password)
|
---|
464 | {
|
---|
465 | this->userName = userName;
|
---|
466 | this->password = password;
|
---|
467 | }
|
---|
468 |
|
---|
469 | char QSocks5PasswordAuthenticator::methodId()
|
---|
470 | {
|
---|
471 | return 0x02;
|
---|
472 | }
|
---|
473 |
|
---|
474 | bool QSocks5PasswordAuthenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
|
---|
475 | {
|
---|
476 | *completed = false;
|
---|
477 | QByteArray uname = userName.toLatin1();
|
---|
478 | QByteArray passwd = password.toLatin1();
|
---|
479 | QByteArray dataBuf(3 + uname.size() + passwd.size(), 0);
|
---|
480 | char *buf = dataBuf.data();
|
---|
481 | int pos = 0;
|
---|
482 | buf[pos++] = S5_PASSWORDAUTH_VERSION;
|
---|
483 | buf[pos++] = uname.size();
|
---|
484 | memcpy(&buf[pos], uname.data(), uname.size());
|
---|
485 | pos += uname.size();
|
---|
486 | buf[pos++] = passwd.size();
|
---|
487 | memcpy(&buf[pos], passwd.data(), passwd.size());
|
---|
488 | return socket->write(dataBuf) == dataBuf.size();
|
---|
489 | }
|
---|
490 |
|
---|
491 | bool QSocks5PasswordAuthenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
|
---|
492 | {
|
---|
493 | *completed = false;
|
---|
494 |
|
---|
495 | if (socket->bytesAvailable() < 2)
|
---|
496 | return true;
|
---|
497 |
|
---|
498 | QByteArray buf = socket->read(2);
|
---|
499 | if (buf.at(0) == S5_PASSWORDAUTH_VERSION && buf.at(1) == 0x00) {
|
---|
500 | *completed = true;
|
---|
501 | return true;
|
---|
502 | }
|
---|
503 |
|
---|
504 | // must disconnect
|
---|
505 | socket->close();
|
---|
506 | return false;
|
---|
507 | }
|
---|
508 |
|
---|
509 | QString QSocks5PasswordAuthenticator::errorString()
|
---|
510 | {
|
---|
511 | return QLatin1String("Socks5 user name or password incorrect");
|
---|
512 | }
|
---|
513 |
|
---|
514 |
|
---|
515 |
|
---|
516 | QSocks5SocketEnginePrivate::QSocks5SocketEnginePrivate()
|
---|
517 | : socks5State(Uninitialized)
|
---|
518 | , readNotificationEnabled(false)
|
---|
519 | , writeNotificationEnabled(false)
|
---|
520 | , exceptNotificationEnabled(false)
|
---|
521 | , socketDescriptor(-1)
|
---|
522 | , data(0)
|
---|
523 | , connectData(0)
|
---|
524 | #ifndef QT_NO_UDPSOCKET
|
---|
525 | , udpData(0)
|
---|
526 | #endif
|
---|
527 | , bindData(0)
|
---|
528 | , readNotificationActivated(false)
|
---|
529 | , writeNotificationActivated(false)
|
---|
530 | , readNotificationPending(false)
|
---|
531 | , writeNotificationPending(false)
|
---|
532 | , connectionNotificationPending(false)
|
---|
533 | {
|
---|
534 | mode = NoMode;
|
---|
535 | }
|
---|
536 |
|
---|
537 | QSocks5SocketEnginePrivate::~QSocks5SocketEnginePrivate()
|
---|
538 | {
|
---|
539 | }
|
---|
540 |
|
---|
541 | void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode)
|
---|
542 | {
|
---|
543 | Q_Q(QSocks5SocketEngine);
|
---|
544 |
|
---|
545 | mode = socks5Mode;
|
---|
546 | if (mode == ConnectMode) {
|
---|
547 | connectData = new QSocks5ConnectData;
|
---|
548 | data = connectData;
|
---|
549 | #ifndef QT_NO_UDPSOCKET
|
---|
550 | } else if (mode == UdpAssociateMode) {
|
---|
551 | udpData = new QSocks5UdpAssociateData;
|
---|
552 | data = udpData;
|
---|
553 | udpData->udpSocket = new QUdpSocket(q);
|
---|
554 | udpData->udpSocket->setProxy(QNetworkProxy::NoProxy);
|
---|
555 | QObject::connect(udpData->udpSocket, SIGNAL(readyRead()),
|
---|
556 | q, SLOT(_q_udpSocketReadNotification()),
|
---|
557 | Qt::DirectConnection);
|
---|
558 | #endif // QT_NO_UDPSOCKET
|
---|
559 | } else if (mode == BindMode) {
|
---|
560 | bindData = new QSocks5BindData;
|
---|
561 | data = bindData;
|
---|
562 | }
|
---|
563 |
|
---|
564 | data->controlSocket = new QTcpSocket(q);
|
---|
565 | data->controlSocket->setProxy(QNetworkProxy::NoProxy);
|
---|
566 | QObject::connect(data->controlSocket, SIGNAL(connected()), q, SLOT(_q_controlSocketConnected()),
|
---|
567 | Qt::DirectConnection);
|
---|
568 | QObject::connect(data->controlSocket, SIGNAL(readyRead()), q, SLOT(_q_controlSocketReadNotification()),
|
---|
569 | Qt::DirectConnection);
|
---|
570 | QObject::connect(data->controlSocket, SIGNAL(bytesWritten(qint64)), q, SLOT(_q_controlSocketBytesWritten()),
|
---|
571 | Qt::DirectConnection);
|
---|
572 | QObject::connect(data->controlSocket, SIGNAL(error(QAbstractSocket::SocketError)),
|
---|
573 | q, SLOT(_q_controlSocketError(QAbstractSocket::SocketError)),
|
---|
574 | Qt::DirectConnection);
|
---|
575 | QObject::connect(data->controlSocket, SIGNAL(disconnected()), q, SLOT(_q_controlSocketDisconnected()),
|
---|
576 | Qt::DirectConnection);
|
---|
577 | QObject::connect(data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
|
---|
578 | q, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
|
---|
579 | Qt::DirectConnection);
|
---|
580 |
|
---|
581 | if (!proxyInfo.user().isEmpty() || !proxyInfo.password().isEmpty()) {
|
---|
582 | QSOCKS5_D_DEBUG << "using username/password authentication; user =" << proxyInfo.user();
|
---|
583 | data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
|
---|
584 | } else {
|
---|
585 | QSOCKS5_D_DEBUG << "not using authentication";
|
---|
586 | data->authenticator = new QSocks5Authenticator();
|
---|
587 | }
|
---|
588 | }
|
---|
589 |
|
---|
590 | void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, const QString &extraMessage)
|
---|
591 | {
|
---|
592 | Q_Q(QSocks5SocketEngine);
|
---|
593 |
|
---|
594 | switch (state) {
|
---|
595 | case Uninitialized:
|
---|
596 | case Authenticating:
|
---|
597 | case AuthenticationMethodsSent:
|
---|
598 | case RequestMethodSent:
|
---|
599 | case Connected:
|
---|
600 | case UdpAssociateSuccess:
|
---|
601 | case BindSuccess:
|
---|
602 | // these aren't error states
|
---|
603 | return;
|
---|
604 |
|
---|
605 | case ConnectError:
|
---|
606 | case ControlSocketError: {
|
---|
607 | QAbstractSocket::SocketError controlSocketError = data->controlSocket->error();
|
---|
608 | if (socks5State != Connected) {
|
---|
609 | switch (controlSocketError) {
|
---|
610 | case QAbstractSocket::ConnectionRefusedError:
|
---|
611 | q->setError(QAbstractSocket::ProxyConnectionRefusedError,
|
---|
612 | QSocks5SocketEngine::tr("Connection to proxy refused"));
|
---|
613 | break;
|
---|
614 | case QAbstractSocket::RemoteHostClosedError:
|
---|
615 | q->setError(QAbstractSocket::ProxyConnectionClosedError,
|
---|
616 | QSocks5SocketEngine::tr("Connection to proxy closed prematurely"));
|
---|
617 | break;
|
---|
618 | case QAbstractSocket::HostNotFoundError:
|
---|
619 | q->setError(QAbstractSocket::ProxyNotFoundError,
|
---|
620 | QSocks5SocketEngine::tr("Proxy host not found"));
|
---|
621 | break;
|
---|
622 | case QAbstractSocket::SocketTimeoutError:
|
---|
623 | if (state == ConnectError) {
|
---|
624 | q->setError(QAbstractSocket::ProxyConnectionTimeoutError,
|
---|
625 | QSocks5SocketEngine::tr("Connection to proxy timed out"));
|
---|
626 | break;
|
---|
627 | }
|
---|
628 | /* fall through */
|
---|
629 | default:
|
---|
630 | q->setError(controlSocketError, data->controlSocket->errorString());
|
---|
631 | break;
|
---|
632 | }
|
---|
633 | } else {
|
---|
634 | q->setError(controlSocketError, data->controlSocket->errorString());
|
---|
635 | }
|
---|
636 | break;
|
---|
637 | }
|
---|
638 |
|
---|
639 | case AuthenticatingError:
|
---|
640 | q->setError(QAbstractSocket::ProxyAuthenticationRequiredError,
|
---|
641 | extraMessage.isEmpty() ?
|
---|
642 | QSocks5SocketEngine::tr("Proxy authentication failed") :
|
---|
643 | QSocks5SocketEngine::tr("Proxy authentication failed: %1").arg(extraMessage));
|
---|
644 | break;
|
---|
645 |
|
---|
646 | case RequestError:
|
---|
647 | // error code set by caller (overload)
|
---|
648 | break;
|
---|
649 |
|
---|
650 | case SocksError:
|
---|
651 | q->setError(QAbstractSocket::ProxyProtocolError,
|
---|
652 | QSocks5SocketEngine::tr("SOCKS version 5 protocol error"));
|
---|
653 | break;
|
---|
654 |
|
---|
655 | case HostNameLookupError:
|
---|
656 | q->setError(QAbstractSocket::HostNotFoundError,
|
---|
657 | QAbstractSocket::tr("Host not found"));
|
---|
658 | break;
|
---|
659 | }
|
---|
660 |
|
---|
661 | q->setState(QAbstractSocket::UnconnectedState);
|
---|
662 | socks5State = state;
|
---|
663 | }
|
---|
664 |
|
---|
665 | void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, Socks5Error socks5error)
|
---|
666 | {
|
---|
667 | Q_Q(QSocks5SocketEngine);
|
---|
668 | switch (socks5error) {
|
---|
669 | case SocksFailure:
|
---|
670 | q->setError(QAbstractSocket::NetworkError,
|
---|
671 | QSocks5SocketEngine::tr("General SOCKSv5 server failure"));
|
---|
672 | break;
|
---|
673 | case ConnectionNotAllowed:
|
---|
674 | q->setError(QAbstractSocket::SocketAccessError,
|
---|
675 | QSocks5SocketEngine::tr("Connection not allowed by SOCKSv5 server"));
|
---|
676 | break;
|
---|
677 | case NetworkUnreachable:
|
---|
678 | q->setError(QAbstractSocket::NetworkError,
|
---|
679 | QAbstractSocket::tr("Network unreachable"));
|
---|
680 | break;
|
---|
681 | case HostUnreachable:
|
---|
682 | q->setError(QAbstractSocket::HostNotFoundError,
|
---|
683 | QAbstractSocket::tr("Host not found"));
|
---|
684 | break;
|
---|
685 | case ConnectionRefused:
|
---|
686 | q->setError(QAbstractSocket::ConnectionRefusedError,
|
---|
687 | QAbstractSocket::tr("Connection refused"));
|
---|
688 | break;
|
---|
689 | case TTLExpired:
|
---|
690 | q->setError(QAbstractSocket::NetworkError,
|
---|
691 | QSocks5SocketEngine::tr("TTL expired"));
|
---|
692 | break;
|
---|
693 | case CommandNotSupported:
|
---|
694 | q->setError(QAbstractSocket::UnsupportedSocketOperationError,
|
---|
695 | QSocks5SocketEngine::tr("SOCKSv5 command not supported"));
|
---|
696 | break;
|
---|
697 | case AddressTypeNotSupported:
|
---|
698 | q->setError(QAbstractSocket::UnsupportedSocketOperationError,
|
---|
699 | QSocks5SocketEngine::tr("Address type not supported"));
|
---|
700 | break;
|
---|
701 |
|
---|
702 | default:
|
---|
703 | q->setError(QAbstractSocket::UnknownSocketError,
|
---|
704 | QSocks5SocketEngine::tr("Unknown SOCKSv5 proxy error code 0x%1").arg(int(socks5error), 16));
|
---|
705 | break;
|
---|
706 | }
|
---|
707 |
|
---|
708 | setErrorState(state, QString());
|
---|
709 | }
|
---|
710 |
|
---|
711 | void QSocks5SocketEnginePrivate::reauthenticate()
|
---|
712 | {
|
---|
713 | Q_Q(QSocks5SocketEngine);
|
---|
714 |
|
---|
715 | // we require authentication
|
---|
716 | QAuthenticator auth;
|
---|
717 | emit q->proxyAuthenticationRequired(proxyInfo, &auth);
|
---|
718 |
|
---|
719 | if (!auth.user().isEmpty() || !auth.password().isEmpty()) {
|
---|
720 | // we have new credentials, let's try again
|
---|
721 | QSOCKS5_DEBUG << "authentication failure: retrying connection";
|
---|
722 | socks5State = QSocks5SocketEnginePrivate::Uninitialized;
|
---|
723 |
|
---|
724 | delete data->authenticator;
|
---|
725 | proxyInfo.setUser(auth.user());
|
---|
726 | proxyInfo.setPassword(auth.password());
|
---|
727 | data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
|
---|
728 |
|
---|
729 | data->controlSocket->blockSignals(true);
|
---|
730 | data->controlSocket->abort();
|
---|
731 | data->controlSocket->blockSignals(false);
|
---|
732 | data->controlSocket->connectToHost(proxyInfo.hostName(), proxyInfo.port());
|
---|
733 | } else {
|
---|
734 | // authentication failure
|
---|
735 |
|
---|
736 | setErrorState(AuthenticatingError);
|
---|
737 | data->controlSocket->close();
|
---|
738 | emitConnectionNotification();
|
---|
739 | }
|
---|
740 | }
|
---|
741 |
|
---|
742 | void QSocks5SocketEnginePrivate::parseAuthenticationMethodReply()
|
---|
743 | {
|
---|
744 | // not enough data to begin
|
---|
745 | if (data->controlSocket->bytesAvailable() < 2)
|
---|
746 | return;
|
---|
747 |
|
---|
748 | QByteArray buf = data->controlSocket->read(2);
|
---|
749 | if (buf.at(0) != S5_VERSION_5) {
|
---|
750 | QSOCKS5_D_DEBUG << "Socks5 version incorrect";
|
---|
751 | setErrorState(SocksError);
|
---|
752 | data->controlSocket->close();
|
---|
753 | emitConnectionNotification();
|
---|
754 | return;
|
---|
755 | }
|
---|
756 |
|
---|
757 | bool authComplete = false;
|
---|
758 | if (uchar(buf.at(1)) == S5_AUTHMETHOD_NONE) {
|
---|
759 | authComplete = true;
|
---|
760 | } else if (uchar(buf.at(1)) == S5_AUTHMETHOD_NOTACCEPTABLE) {
|
---|
761 | reauthenticate();
|
---|
762 | return;
|
---|
763 | } else if (buf.at(1) != data->authenticator->methodId()
|
---|
764 | || !data->authenticator->beginAuthenticate(data->controlSocket, &authComplete)) {
|
---|
765 | setErrorState(AuthenticatingError, QLatin1String("Socks5 host did not support authentication method."));
|
---|
766 | socketError = QAbstractSocket::SocketAccessError; // change the socket error
|
---|
767 | emitConnectionNotification();
|
---|
768 | return;
|
---|
769 | }
|
---|
770 |
|
---|
771 | if (authComplete)
|
---|
772 | sendRequestMethod();
|
---|
773 | else
|
---|
774 | socks5State = Authenticating;
|
---|
775 | }
|
---|
776 |
|
---|
777 | void QSocks5SocketEnginePrivate::parseAuthenticatingReply()
|
---|
778 | {
|
---|
779 | bool authComplete = false;
|
---|
780 | if (!data->authenticator->continueAuthenticate(data->controlSocket, &authComplete)) {
|
---|
781 | reauthenticate();
|
---|
782 | return;
|
---|
783 | }
|
---|
784 | if (authComplete)
|
---|
785 | sendRequestMethod();
|
---|
786 | }
|
---|
787 |
|
---|
788 | void QSocks5SocketEnginePrivate::sendRequestMethod()
|
---|
789 | {
|
---|
790 | QHostAddress address;
|
---|
791 | quint16 port = 0;
|
---|
792 | char command = 0;
|
---|
793 | if (mode == ConnectMode) {
|
---|
794 | command = S5_CONNECT;
|
---|
795 | address = peerAddress;
|
---|
796 | port = peerPort;
|
---|
797 | } else if (mode == BindMode) {
|
---|
798 | command = S5_BIND;
|
---|
799 | address = localAddress;
|
---|
800 | port = localPort;
|
---|
801 | } else {
|
---|
802 | #ifndef QT_NO_UDPSOCKET
|
---|
803 | command = S5_UDP_ASSOCIATE;
|
---|
804 | address = localAddress; //data->controlSocket->localAddress();
|
---|
805 | port = localPort;
|
---|
806 | #endif
|
---|
807 | }
|
---|
808 |
|
---|
809 | QByteArray buf;
|
---|
810 | buf.reserve(270); // big enough for domain name;
|
---|
811 | buf[0] = S5_VERSION_5;
|
---|
812 | buf[1] = command;
|
---|
813 | buf[2] = 0x00;
|
---|
814 | if (peerName.isEmpty() && !qt_socks5_set_host_address_and_port(address, port, &buf)) {
|
---|
815 | QSOCKS5_DEBUG << "error setting address" << address << " : " << port;
|
---|
816 | //### set error code ....
|
---|
817 | return;
|
---|
818 | } else if (!peerName.isEmpty() && !qt_socks5_set_host_name_and_port(peerName, port, &buf)) {
|
---|
819 | QSOCKS5_DEBUG << "error setting address" << address << " : " << port;
|
---|
820 | //### set error code ....
|
---|
821 | return;
|
---|
822 | }
|
---|
823 | QSOCKS5_DEBUG << "sending" << dump(buf);
|
---|
824 | QByteArray sealedBuf;
|
---|
825 | if (!data->authenticator->seal(buf, &sealedBuf)) {
|
---|
826 | // ### Handle this error.
|
---|
827 | }
|
---|
828 | data->controlSocket->write(sealedBuf);
|
---|
829 | data->controlSocket->flush();
|
---|
830 | socks5State = RequestMethodSent;
|
---|
831 | }
|
---|
832 |
|
---|
833 | void QSocks5SocketEnginePrivate::parseRequestMethodReply()
|
---|
834 | {
|
---|
835 | Q_Q(QSocks5SocketEngine);
|
---|
836 | QSOCKS5_DEBUG << "parseRequestMethodReply()";
|
---|
837 |
|
---|
838 | QByteArray inBuf;
|
---|
839 | if (!data->authenticator->unSeal(data->controlSocket, &inBuf)) {
|
---|
840 | // ### check error and not just not enough data
|
---|
841 | QSOCKS5_DEBUG << "unSeal failed, needs more data";
|
---|
842 | return;
|
---|
843 | }
|
---|
844 | QSOCKS5_DEBUG << dump(inBuf);
|
---|
845 | if (inBuf.size() < 2) {
|
---|
846 | QSOCKS5_DEBUG << "need more data for request reply header .. put this data somewhere";
|
---|
847 | return;
|
---|
848 | }
|
---|
849 |
|
---|
850 | QHostAddress address;
|
---|
851 | quint16 port = 0;
|
---|
852 |
|
---|
853 | if (inBuf.at(0) != S5_VERSION_5 || inBuf.length() < 3 || inBuf.at(2) != 0x00) {
|
---|
854 | QSOCKS5_DEBUG << "socks protocol error";
|
---|
855 | setErrorState(SocksError);
|
---|
856 | } else if (inBuf.at(1) != S5_SUCCESS) {
|
---|
857 | Socks5Error socks5Error = Socks5Error(inBuf.at(1));
|
---|
858 | QSOCKS5_DEBUG << "Request error :" << socks5Error;
|
---|
859 | if ((socks5Error == SocksFailure || socks5Error == ConnectionNotAllowed)
|
---|
860 | && !peerName.isEmpty()) {
|
---|
861 | // Dante seems to use this error code to indicate hostname resolution failure
|
---|
862 | setErrorState(HostNameLookupError);
|
---|
863 | } else {
|
---|
864 | setErrorState(RequestError, socks5Error);
|
---|
865 | }
|
---|
866 | } else {
|
---|
867 | // connection success, retrieve the remote addresses
|
---|
868 | int pos = 3;
|
---|
869 | if (!qt_socks5_get_host_address_and_port(inBuf, &address, &port, &pos)) {
|
---|
870 | QSOCKS5_DEBUG << "error getting address";
|
---|
871 | setErrorState(SocksError);
|
---|
872 | } else {
|
---|
873 | inBuf.remove(0, pos);
|
---|
874 | for (int i = inBuf.size() - 1; i >= 0 ; --i)
|
---|
875 | data->controlSocket->ungetChar(inBuf.at(i));
|
---|
876 | }
|
---|
877 | }
|
---|
878 |
|
---|
879 | if (socks5State == RequestMethodSent) {
|
---|
880 | // no error
|
---|
881 | localAddress = address;
|
---|
882 | localPort = port;
|
---|
883 |
|
---|
884 | if (mode == ConnectMode) {
|
---|
885 | socks5State = Connected;
|
---|
886 | // notify the upper layer that we're done
|
---|
887 | q->setState(QAbstractSocket::ConnectedState);
|
---|
888 | emitConnectionNotification();
|
---|
889 | } else if (mode == BindMode) {
|
---|
890 | socks5State = BindSuccess;
|
---|
891 | q->setState(QAbstractSocket::ListeningState);
|
---|
892 | } else {
|
---|
893 | socks5State = UdpAssociateSuccess;
|
---|
894 | }
|
---|
895 | } else if (socks5State == BindSuccess) {
|
---|
896 | // no error and we got a connection
|
---|
897 | bindData->peerAddress = address;
|
---|
898 | bindData->peerPort = port;
|
---|
899 |
|
---|
900 | emitReadNotification();
|
---|
901 | } else {
|
---|
902 | // got an error
|
---|
903 | data->controlSocket->close();
|
---|
904 | emitConnectionNotification();
|
---|
905 | }
|
---|
906 | }
|
---|
907 |
|
---|
908 | void QSocks5SocketEnginePrivate::_q_emitPendingReadNotification()
|
---|
909 | {
|
---|
910 | Q_Q(QSocks5SocketEngine);
|
---|
911 | readNotificationPending = false;
|
---|
912 | if (readNotificationEnabled) {
|
---|
913 | QSOCKS5_D_DEBUG << "emitting readNotification";
|
---|
914 | QPointer<QSocks5SocketEngine> qq = q;
|
---|
915 | emit q->readNotification();
|
---|
916 | if (!qq)
|
---|
917 | return;
|
---|
918 | // check if there needs to be a new zero read notification
|
---|
919 | if (data && data->controlSocket->state() == QAbstractSocket::UnconnectedState
|
---|
920 | && data->controlSocket->error() == QAbstractSocket::RemoteHostClosedError) {
|
---|
921 | connectData->readBuffer.clear();
|
---|
922 | emitReadNotification();
|
---|
923 | }
|
---|
924 | }
|
---|
925 | }
|
---|
926 |
|
---|
927 | void QSocks5SocketEnginePrivate::emitReadNotification()
|
---|
928 | {
|
---|
929 | Q_Q(QSocks5SocketEngine);
|
---|
930 | readNotificationActivated = true;
|
---|
931 | if (readNotificationEnabled && !readNotificationPending) {
|
---|
932 | QSOCKS5_D_DEBUG << "queueing readNotification";
|
---|
933 | readNotificationPending = true;
|
---|
934 | QMetaObject::invokeMethod(q, "_q_emitPendingReadNotification", Qt::QueuedConnection);
|
---|
935 | }
|
---|
936 | }
|
---|
937 |
|
---|
938 | void QSocks5SocketEnginePrivate::_q_emitPendingWriteNotification()
|
---|
939 | {
|
---|
940 | writeNotificationPending = false;
|
---|
941 | Q_Q(QSocks5SocketEngine);
|
---|
942 | if (writeNotificationEnabled) {
|
---|
943 | QSOCKS5_D_DEBUG << "emitting writeNotification";
|
---|
944 | emit q->writeNotification();
|
---|
945 | }
|
---|
946 | }
|
---|
947 |
|
---|
948 | void QSocks5SocketEnginePrivate::emitWriteNotification()
|
---|
949 | {
|
---|
950 | Q_Q(QSocks5SocketEngine);
|
---|
951 | writeNotificationActivated = true;
|
---|
952 | if (writeNotificationEnabled && !writeNotificationPending) {
|
---|
953 | QSOCKS5_D_DEBUG << "queueing writeNotification";
|
---|
954 | writeNotificationPending = true;
|
---|
955 | QMetaObject::invokeMethod(q, "_q_emitPendingWriteNotification", Qt::QueuedConnection);
|
---|
956 | }
|
---|
957 | }
|
---|
958 |
|
---|
959 | void QSocks5SocketEnginePrivate::_q_emitPendingConnectionNotification()
|
---|
960 | {
|
---|
961 | connectionNotificationPending = false;
|
---|
962 | Q_Q(QSocks5SocketEngine);
|
---|
963 | QSOCKS5_D_DEBUG << "emitting connectionNotification";
|
---|
964 | emit q->connectionNotification();
|
---|
965 | }
|
---|
966 |
|
---|
967 | void QSocks5SocketEnginePrivate::emitConnectionNotification()
|
---|
968 | {
|
---|
969 | Q_Q(QSocks5SocketEngine);
|
---|
970 | QSOCKS5_D_DEBUG << "queueing connectionNotification";
|
---|
971 | connectionNotificationPending = true;
|
---|
972 | QMetaObject::invokeMethod(q, "_q_emitPendingConnectionNotification", Qt::QueuedConnection);
|
---|
973 | }
|
---|
974 |
|
---|
975 | QSocks5SocketEngine::QSocks5SocketEngine(QObject *parent)
|
---|
976 | :QAbstractSocketEngine(*new QSocks5SocketEnginePrivate(), parent)
|
---|
977 | {
|
---|
978 | }
|
---|
979 |
|
---|
980 | QSocks5SocketEngine::~QSocks5SocketEngine()
|
---|
981 | {
|
---|
982 | Q_D(QSocks5SocketEngine);
|
---|
983 |
|
---|
984 | if (d->data) {
|
---|
985 | delete d->data->authenticator;
|
---|
986 | delete d->data->controlSocket;
|
---|
987 | }
|
---|
988 | if (d->connectData)
|
---|
989 | delete d->connectData;
|
---|
990 | #ifndef QT_NO_UDPSOCKET
|
---|
991 | if (d->udpData) {
|
---|
992 | delete d->udpData->udpSocket;
|
---|
993 | delete d->udpData;
|
---|
994 | }
|
---|
995 | #endif
|
---|
996 | if (d->bindData)
|
---|
997 | delete d->bindData;
|
---|
998 | }
|
---|
999 |
|
---|
1000 | static QBasicAtomicInt descriptorCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
|
---|
1001 |
|
---|
1002 | bool QSocks5SocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
|
---|
1003 | {
|
---|
1004 | Q_D(QSocks5SocketEngine);
|
---|
1005 |
|
---|
1006 | d->socketDescriptor = descriptorCounter.fetchAndAddRelaxed(1);
|
---|
1007 |
|
---|
1008 | d->socketType = type;
|
---|
1009 | d->socketProtocol = protocol;
|
---|
1010 |
|
---|
1011 | return true;
|
---|
1012 | }
|
---|
1013 |
|
---|
1014 | bool QSocks5SocketEngine::initialize(int socketDescriptor, QAbstractSocket::SocketState socketState)
|
---|
1015 | {
|
---|
1016 | Q_D(QSocks5SocketEngine);
|
---|
1017 |
|
---|
1018 | QSOCKS5_Q_DEBUG << "initialize" << socketDescriptor;
|
---|
1019 |
|
---|
1020 | // this is only valid for the other side of a bind, nothing else is supported
|
---|
1021 |
|
---|
1022 | if (socketState != QAbstractSocket::ConnectedState) {
|
---|
1023 | //### must be connected state ???
|
---|
1024 | return false;
|
---|
1025 | }
|
---|
1026 |
|
---|
1027 | QSocks5BindData *bindData = socks5BindStore()->retrieve(socketDescriptor);
|
---|
1028 | if (bindData) {
|
---|
1029 |
|
---|
1030 | d->socketState = QAbstractSocket::ConnectedState;
|
---|
1031 | d->socketType = QAbstractSocket::TcpSocket;
|
---|
1032 | d->connectData = new QSocks5ConnectData;
|
---|
1033 | d->data = d->connectData;
|
---|
1034 | d->mode = QSocks5SocketEnginePrivate::ConnectMode;
|
---|
1035 | d->data->controlSocket = bindData->controlSocket;
|
---|
1036 | bindData->controlSocket = 0;
|
---|
1037 | d->data->controlSocket->setParent(this);
|
---|
1038 | d->socketProtocol = d->data->controlSocket->localAddress().protocol();
|
---|
1039 | d->data->authenticator = bindData->authenticator;
|
---|
1040 | bindData->authenticator = 0;
|
---|
1041 | d->localPort = bindData->localPort;
|
---|
1042 | d->localAddress = bindData->localAddress;
|
---|
1043 | d->peerPort = bindData->peerPort;
|
---|
1044 | d->peerAddress = bindData->peerAddress;
|
---|
1045 | delete bindData;
|
---|
1046 |
|
---|
1047 | QObject::connect(d->data->controlSocket, SIGNAL(connected()), this, SLOT(_q_controlSocketConnected()),
|
---|
1048 | Qt::DirectConnection);
|
---|
1049 | QObject::connect(d->data->controlSocket, SIGNAL(readyRead()), this, SLOT(_q_controlSocketReadNotification()),
|
---|
1050 | Qt::DirectConnection);
|
---|
1051 | QObject::connect(d->data->controlSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_controlSocketBytesWritten()),
|
---|
1052 | Qt::DirectConnection);
|
---|
1053 | QObject::connect(d->data->controlSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketError(QAbstractSocket::SocketError)),
|
---|
1054 | Qt::DirectConnection);
|
---|
1055 | QObject::connect(d->data->controlSocket, SIGNAL(disconnected()), this, SLOT(_q_controlSocketDisconnected()),
|
---|
1056 | Qt::DirectConnection);
|
---|
1057 | QObject::connect(d->data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
|
---|
1058 | this, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
|
---|
1059 | Qt::DirectConnection);
|
---|
1060 |
|
---|
1061 | d->socks5State = QSocks5SocketEnginePrivate::Connected;
|
---|
1062 |
|
---|
1063 | if (d->data->controlSocket->bytesAvailable() != 0)
|
---|
1064 | d->_q_controlSocketReadNotification();
|
---|
1065 | return true;
|
---|
1066 | }
|
---|
1067 | return false;
|
---|
1068 | }
|
---|
1069 |
|
---|
1070 | void QSocks5SocketEngine::setProxy(const QNetworkProxy &networkProxy)
|
---|
1071 | {
|
---|
1072 | Q_D(QSocks5SocketEngine);
|
---|
1073 | d->proxyInfo = networkProxy;
|
---|
1074 | }
|
---|
1075 |
|
---|
1076 | int QSocks5SocketEngine::socketDescriptor() const
|
---|
1077 | {
|
---|
1078 | Q_D(const QSocks5SocketEngine);
|
---|
1079 | return d->socketDescriptor;
|
---|
1080 | }
|
---|
1081 |
|
---|
1082 | bool QSocks5SocketEngine::isValid() const
|
---|
1083 | {
|
---|
1084 | Q_D(const QSocks5SocketEngine);
|
---|
1085 | return d->socketType != QAbstractSocket::UnknownSocketType
|
---|
1086 | && d->socks5State != QSocks5SocketEnginePrivate::SocksError
|
---|
1087 | && (d->socketError == QAbstractSocket::UnknownSocketError
|
---|
1088 | || d->socketError == QAbstractSocket::SocketTimeoutError
|
---|
1089 | || d->socketError == QAbstractSocket::UnfinishedSocketOperationError);
|
---|
1090 | }
|
---|
1091 |
|
---|
1092 | bool QSocks5SocketEngine::connectInternal()
|
---|
1093 | {
|
---|
1094 | Q_D(QSocks5SocketEngine);
|
---|
1095 |
|
---|
1096 | if (!d->data) {
|
---|
1097 | if (socketType() == QAbstractSocket::TcpSocket) {
|
---|
1098 | d->initialize(QSocks5SocketEnginePrivate::ConnectMode);
|
---|
1099 | #ifndef QT_NO_UDPSOCKET
|
---|
1100 | } else if (socketType() == QAbstractSocket::UdpSocket) {
|
---|
1101 | d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
|
---|
1102 | // all udp needs to be bound
|
---|
1103 | if (!bind(QHostAddress(QLatin1String("0.0.0.0")), 0))
|
---|
1104 | return false;
|
---|
1105 |
|
---|
1106 | setState(QAbstractSocket::ConnectedState);
|
---|
1107 | return true;
|
---|
1108 | #endif
|
---|
1109 | } else {
|
---|
1110 | qFatal("QSocks5SocketEngine::connectToHost: in QTcpServer mode");
|
---|
1111 | return false;
|
---|
1112 | }
|
---|
1113 | }
|
---|
1114 |
|
---|
1115 | if (d->socks5State == QSocks5SocketEnginePrivate::Uninitialized
|
---|
1116 | && d->socketState != QAbstractSocket::ConnectingState) {
|
---|
1117 | setState(QAbstractSocket::ConnectingState);
|
---|
1118 | d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
|
---|
1119 | return false;
|
---|
1120 | }
|
---|
1121 | return false;
|
---|
1122 | }
|
---|
1123 |
|
---|
1124 | bool QSocks5SocketEngine::connectToHost(const QHostAddress &address, quint16 port)
|
---|
1125 | {
|
---|
1126 | Q_D(QSocks5SocketEngine);
|
---|
1127 | QSOCKS5_DEBUG << "connectToHost" << address << ":" << port;
|
---|
1128 |
|
---|
1129 | setPeerAddress(address);
|
---|
1130 | setPeerPort(port);
|
---|
1131 | d->peerName.clear();
|
---|
1132 |
|
---|
1133 | return connectInternal();
|
---|
1134 | }
|
---|
1135 |
|
---|
1136 | bool QSocks5SocketEngine::connectToHostByName(const QString &hostname, quint16 port)
|
---|
1137 | {
|
---|
1138 | Q_D(QSocks5SocketEngine);
|
---|
1139 |
|
---|
1140 | setPeerAddress(QHostAddress());
|
---|
1141 | setPeerPort(port);
|
---|
1142 | d->peerName = hostname;
|
---|
1143 |
|
---|
1144 | return connectInternal();
|
---|
1145 | }
|
---|
1146 |
|
---|
1147 | void QSocks5SocketEnginePrivate::_q_controlSocketConnected()
|
---|
1148 | {
|
---|
1149 | QSOCKS5_DEBUG << "_q_controlSocketConnected";
|
---|
1150 | QByteArray buf(3, 0);
|
---|
1151 | buf[0] = S5_VERSION_5;
|
---|
1152 | buf[1] = 0x01;
|
---|
1153 | buf[2] = data->authenticator->methodId();
|
---|
1154 | data->controlSocket->write(buf);
|
---|
1155 | socks5State = AuthenticationMethodsSent;
|
---|
1156 | }
|
---|
1157 |
|
---|
1158 | void QSocks5SocketEnginePrivate::_q_controlSocketReadNotification()
|
---|
1159 | {
|
---|
1160 | QSOCKS5_D_DEBUG << "_q_controlSocketReadNotification socks5state" << s5StateToString(socks5State)
|
---|
1161 | << "bytes available" << data->controlSocket->bytesAvailable();
|
---|
1162 |
|
---|
1163 | if (data->controlSocket->bytesAvailable() == 0) {
|
---|
1164 | QSOCKS5_D_DEBUG << "########## bogus read why do we get these ... on windows only";
|
---|
1165 | return;
|
---|
1166 | }
|
---|
1167 |
|
---|
1168 | switch (socks5State) {
|
---|
1169 | case AuthenticationMethodsSent:
|
---|
1170 | parseAuthenticationMethodReply();
|
---|
1171 | break;
|
---|
1172 | case Authenticating:
|
---|
1173 | parseAuthenticatingReply();
|
---|
1174 | break;
|
---|
1175 | case RequestMethodSent:
|
---|
1176 | parseRequestMethodReply();
|
---|
1177 | break;
|
---|
1178 | case Connected: {
|
---|
1179 | QByteArray buf;
|
---|
1180 | if (!data->authenticator->unSeal(data->controlSocket, &buf)) {
|
---|
1181 | // qDebug() << "unseal error maybe need to wait for more data";
|
---|
1182 | }
|
---|
1183 | if (buf.size()) {
|
---|
1184 | QSOCKS5_DEBUG << dump(buf);
|
---|
1185 | connectData->readBuffer += buf;
|
---|
1186 | emitReadNotification();
|
---|
1187 | }
|
---|
1188 | break;
|
---|
1189 | }
|
---|
1190 | case BindSuccess:
|
---|
1191 | // only get here if command is bind
|
---|
1192 | if (mode == BindMode) {
|
---|
1193 | parseRequestMethodReply();
|
---|
1194 | break;
|
---|
1195 | }
|
---|
1196 |
|
---|
1197 | // fall through
|
---|
1198 | default:
|
---|
1199 | qWarning("QSocks5SocketEnginePrivate::_q_controlSocketReadNotification: "
|
---|
1200 | "Unexpectedly received data while in state=%d and mode=%d",
|
---|
1201 | socks5State, mode);
|
---|
1202 | break;
|
---|
1203 | };
|
---|
1204 | }
|
---|
1205 |
|
---|
1206 | void QSocks5SocketEnginePrivate::_q_controlSocketBytesWritten()
|
---|
1207 | {
|
---|
1208 | QSOCKS5_DEBUG << "_q_controlSocketBytesWritten";
|
---|
1209 |
|
---|
1210 | if (socks5State != Connected
|
---|
1211 | || (mode == ConnectMode
|
---|
1212 | && data->controlSocket->bytesToWrite()))
|
---|
1213 | return;
|
---|
1214 | if (data->controlSocket->bytesToWrite() < MaxWriteBufferSize) {
|
---|
1215 | emitWriteNotification();
|
---|
1216 | writeNotificationActivated = false;
|
---|
1217 | }
|
---|
1218 | }
|
---|
1219 |
|
---|
1220 | void QSocks5SocketEnginePrivate::_q_controlSocketError(QAbstractSocket::SocketError error)
|
---|
1221 | {
|
---|
1222 | QSOCKS5_D_DEBUG << "controlSocketError" << error << data->controlSocket->errorString();
|
---|
1223 |
|
---|
1224 | if (error == QAbstractSocket::SocketTimeoutError)
|
---|
1225 | return; // ignore this error -- comes from the waitFor* functions
|
---|
1226 |
|
---|
1227 | if (error == QAbstractSocket::RemoteHostClosedError
|
---|
1228 | && socks5State == Connected) {
|
---|
1229 | // clear the read buffer in connect mode so that bytes available returns 0
|
---|
1230 | // if there already is a read notification pending then this will be processed first
|
---|
1231 | if (!readNotificationPending)
|
---|
1232 | connectData->readBuffer.clear();
|
---|
1233 | emitReadNotification();
|
---|
1234 | } else if (socks5State == Uninitialized
|
---|
1235 | || socks5State == AuthenticationMethodsSent
|
---|
1236 | || socks5State == Authenticating
|
---|
1237 | || socks5State == RequestMethodSent) {
|
---|
1238 | setErrorState(socks5State == Uninitialized ? ConnectError : ControlSocketError);
|
---|
1239 | data->controlSocket->close();
|
---|
1240 | emitConnectionNotification();
|
---|
1241 | } else {
|
---|
1242 | q_func()->setError(data->controlSocket->error(), data->controlSocket->errorString());
|
---|
1243 | emitReadNotification();
|
---|
1244 | }
|
---|
1245 | }
|
---|
1246 |
|
---|
1247 | void QSocks5SocketEnginePrivate::_q_controlSocketDisconnected()
|
---|
1248 | {
|
---|
1249 | QSOCKS5_D_DEBUG << "_q_controlSocketDisconnected";
|
---|
1250 | }
|
---|
1251 |
|
---|
1252 | void QSocks5SocketEnginePrivate::_q_controlSocketStateChanged(QAbstractSocket::SocketState state)
|
---|
1253 | {
|
---|
1254 | QSOCKS5_D_DEBUG << "_q_controlSocketStateChanged" << state;
|
---|
1255 | }
|
---|
1256 |
|
---|
1257 | #ifndef QT_NO_UDPSOCKET
|
---|
1258 | void QSocks5SocketEnginePrivate::checkForDatagrams() const
|
---|
1259 | {
|
---|
1260 | // udp should be unbuffered so we need to do some polling at certain points
|
---|
1261 | if (udpData->udpSocket->hasPendingDatagrams())
|
---|
1262 | const_cast<QSocks5SocketEnginePrivate *>(this)->_q_udpSocketReadNotification();
|
---|
1263 | }
|
---|
1264 |
|
---|
1265 | void QSocks5SocketEnginePrivate::_q_udpSocketReadNotification()
|
---|
1266 | {
|
---|
1267 | QSOCKS5_D_DEBUG << "_q_udpSocketReadNotification()";
|
---|
1268 |
|
---|
1269 | // check some state stuff
|
---|
1270 | if (!udpData->udpSocket->hasPendingDatagrams()) {
|
---|
1271 | QSOCKS5_D_DEBUG << "false read ??";
|
---|
1272 | return;
|
---|
1273 | }
|
---|
1274 |
|
---|
1275 | while (udpData->udpSocket->hasPendingDatagrams()) {
|
---|
1276 | QByteArray sealedBuf(udpData->udpSocket->pendingDatagramSize(), 0);
|
---|
1277 | QSOCKS5_D_DEBUG << "new datagram";
|
---|
1278 | udpData->udpSocket->readDatagram(sealedBuf.data(), sealedBuf.size());
|
---|
1279 | QByteArray inBuf;
|
---|
1280 | if (!data->authenticator->unSeal(sealedBuf, &inBuf)) {
|
---|
1281 | QSOCKS5_D_DEBUG << "failed unsealing datagram discarding";
|
---|
1282 | return;
|
---|
1283 | }
|
---|
1284 | QSOCKS5_DEBUG << dump(inBuf);
|
---|
1285 | int pos = 0;
|
---|
1286 | const char *buf = inBuf.constData();
|
---|
1287 | if (inBuf.size() < 4) {
|
---|
1288 | QSOCKS5_D_DEBUG << "bugus udp data, discarding";
|
---|
1289 | return;
|
---|
1290 | }
|
---|
1291 | QSocks5RevivedDatagram datagram;
|
---|
1292 | if (buf[pos++] != 0 || buf[pos++] != 0) {
|
---|
1293 | QSOCKS5_D_DEBUG << "invalid datagram discarding";
|
---|
1294 | return;
|
---|
1295 | }
|
---|
1296 | if (buf[pos++] != 0) { //### add fragmentation reading support
|
---|
1297 | QSOCKS5_D_DEBUG << "don't support fragmentation yet disgarding";
|
---|
1298 | return;
|
---|
1299 | }
|
---|
1300 | if (!qt_socks5_get_host_address_and_port(inBuf, &datagram.address, &datagram.port, &pos)) {
|
---|
1301 | QSOCKS5_D_DEBUG << "failed to get address from datagram disgarding";
|
---|
1302 | return;
|
---|
1303 | }
|
---|
1304 | datagram.data = QByteArray(&buf[pos], inBuf.size() - pos);
|
---|
1305 | udpData->pendingDatagrams.enqueue(datagram);
|
---|
1306 | }
|
---|
1307 | emitReadNotification();
|
---|
1308 | }
|
---|
1309 | #endif // QT_NO_UDPSOCKET
|
---|
1310 |
|
---|
1311 | bool QSocks5SocketEngine::bind(const QHostAddress &address, quint16 port)
|
---|
1312 | {
|
---|
1313 | Q_D(QSocks5SocketEngine);
|
---|
1314 |
|
---|
1315 | // when bind wee will block until the bind is finished as the info from the proxy server is needed
|
---|
1316 |
|
---|
1317 | if (!d->data) {
|
---|
1318 | if (socketType() == QAbstractSocket::TcpSocket) {
|
---|
1319 | d->initialize(QSocks5SocketEnginePrivate::BindMode);
|
---|
1320 | #ifndef QT_NO_UDPSOCKET
|
---|
1321 | } else if (socketType() == QAbstractSocket::UdpSocket) {
|
---|
1322 | d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
|
---|
1323 | #endif
|
---|
1324 | } else {
|
---|
1325 | //### something invalid
|
---|
1326 | return false;
|
---|
1327 | }
|
---|
1328 | }
|
---|
1329 |
|
---|
1330 | #ifndef QT_NO_UDPSOCKET
|
---|
1331 | if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
|
---|
1332 | if (!d->udpData->udpSocket->bind(address, port)) {
|
---|
1333 | QSOCKS5_Q_DEBUG << "local udp bind failed";
|
---|
1334 | setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
|
---|
1335 | return false;
|
---|
1336 | }
|
---|
1337 | d->localAddress = d->udpData->udpSocket->localAddress();
|
---|
1338 | d->localPort = d->udpData->udpSocket->localPort();
|
---|
1339 | } else
|
---|
1340 | #endif
|
---|
1341 | if (d->mode == QSocks5SocketEnginePrivate::BindMode) {
|
---|
1342 | d->localAddress = address;
|
---|
1343 | d->localPort = port;
|
---|
1344 | } else {
|
---|
1345 | //### something invalid
|
---|
1346 | return false;
|
---|
1347 | }
|
---|
1348 |
|
---|
1349 | int msecs = SOCKS5_BLOCKING_BIND_TIMEOUT;
|
---|
1350 | QTime stopWatch;
|
---|
1351 | stopWatch.start();
|
---|
1352 | d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
|
---|
1353 | if (!d->waitForConnected(msecs, 0) ||
|
---|
1354 | d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
|
---|
1355 | // waitForConnected sets the error state and closes the socket
|
---|
1356 | QSOCKS5_Q_DEBUG << "waitForConnected to proxy server" << d->data->controlSocket->errorString();
|
---|
1357 | return false;
|
---|
1358 | }
|
---|
1359 | if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) {
|
---|
1360 | setState(QAbstractSocket::BoundState);
|
---|
1361 | return true;
|
---|
1362 | #ifndef QT_NO_UDPSOCKET
|
---|
1363 | } else if (d->socks5State == QSocks5SocketEnginePrivate::UdpAssociateSuccess) {
|
---|
1364 | setState(QAbstractSocket::BoundState);
|
---|
1365 | d->udpData->associateAddress = d->localAddress;
|
---|
1366 | d->localAddress = QHostAddress();
|
---|
1367 | d->udpData->associatePort = d->localPort;
|
---|
1368 | d->localPort = 0;
|
---|
1369 | QUdpSocket dummy;
|
---|
1370 | dummy.setProxy(QNetworkProxy::NoProxy);
|
---|
1371 | if (!dummy.bind()
|
---|
1372 | || writeDatagram(0,0, d->data->controlSocket->localAddress(), dummy.localPort()) != 0
|
---|
1373 | || !dummy.waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))
|
---|
1374 | || dummy.readDatagram(0,0, &d->localAddress, &d->localPort) != 0) {
|
---|
1375 | QSOCKS5_DEBUG << "udp actual address and port lookup failed";
|
---|
1376 | setState(QAbstractSocket::UnconnectedState);
|
---|
1377 | setError(dummy.error(), dummy.errorString());
|
---|
1378 | d->data->controlSocket->close();
|
---|
1379 | //### reset and error
|
---|
1380 | return false;
|
---|
1381 | }
|
---|
1382 | QSOCKS5_DEBUG << "udp actual address and port" << d->localAddress << ":" << d->localPort;
|
---|
1383 | return true;
|
---|
1384 | #endif // QT_NO_UDPSOCKET
|
---|
1385 | }
|
---|
1386 |
|
---|
1387 | // binding timed out
|
---|
1388 | setError(QAbstractSocket::SocketTimeoutError,
|
---|
1389 | QLatin1String(QT_TRANSLATE_NOOP("QSocks5SocketEngine", "Network operation timed out")));
|
---|
1390 |
|
---|
1391 | ///### delete d->udpSocket;
|
---|
1392 | ///### d->udpSocket = 0;
|
---|
1393 | return false;
|
---|
1394 | }
|
---|
1395 |
|
---|
1396 |
|
---|
1397 | bool QSocks5SocketEngine::listen()
|
---|
1398 | {
|
---|
1399 | Q_D(QSocks5SocketEngine);
|
---|
1400 |
|
---|
1401 | QSOCKS5_Q_DEBUG << "listen()";
|
---|
1402 |
|
---|
1403 | // check that we are in bound and then go to listening.
|
---|
1404 | if (d->socketState == QAbstractSocket::BoundState) {
|
---|
1405 | d->socketState = QAbstractSocket::ListeningState;
|
---|
1406 |
|
---|
1407 | // check if we already have a connection
|
---|
1408 | if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
|
---|
1409 | d->emitReadNotification();
|
---|
1410 |
|
---|
1411 | return true;
|
---|
1412 | }
|
---|
1413 | return false;
|
---|
1414 | }
|
---|
1415 |
|
---|
1416 | int QSocks5SocketEngine::accept()
|
---|
1417 | {
|
---|
1418 | Q_D(QSocks5SocketEngine);
|
---|
1419 | // check we are listing ---
|
---|
1420 |
|
---|
1421 | QSOCKS5_Q_DEBUG << "accept()";
|
---|
1422 |
|
---|
1423 | if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) {
|
---|
1424 | QSOCKS5_Q_DEBUG << "BindSuccess adding" << d->socketDescriptor << "to the bind store";
|
---|
1425 | d->data->controlSocket->disconnect();
|
---|
1426 | d->data->controlSocket->setParent(0);
|
---|
1427 | d->bindData->localAddress = d->localAddress;
|
---|
1428 | d->bindData->localPort = d->localPort;
|
---|
1429 | int sd = d->socketDescriptor;
|
---|
1430 | socks5BindStore()->add(sd, d->bindData);
|
---|
1431 | d->data = 0;
|
---|
1432 | d->bindData = 0;
|
---|
1433 | d->socketDescriptor = 0;
|
---|
1434 | //### do something about this socket layer ... set it closed and an error about why ...
|
---|
1435 | // reset state and local port/address
|
---|
1436 | d->socks5State = QSocks5SocketEnginePrivate::Uninitialized; // ..??
|
---|
1437 | d->socketState = QAbstractSocket::UnconnectedState;
|
---|
1438 | return sd;
|
---|
1439 | }
|
---|
1440 | return -1;
|
---|
1441 | }
|
---|
1442 |
|
---|
1443 | void QSocks5SocketEngine::close()
|
---|
1444 | {
|
---|
1445 | QSOCKS5_Q_DEBUG << "close()";
|
---|
1446 | Q_D(QSocks5SocketEngine);
|
---|
1447 | if (d->data && d->data->controlSocket) {
|
---|
1448 | if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) {
|
---|
1449 | int msecs = 100;
|
---|
1450 | QTime stopWatch;
|
---|
1451 | stopWatch.start();
|
---|
1452 | while (!d->data->controlSocket->bytesToWrite()) {
|
---|
1453 | if (!d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed())))
|
---|
1454 | break;
|
---|
1455 | }
|
---|
1456 | }
|
---|
1457 | d->data->controlSocket->close();
|
---|
1458 | }
|
---|
1459 | #ifndef QT_NO_UDPSOCKET
|
---|
1460 | if (d->udpData && d->udpData->udpSocket)
|
---|
1461 | d->udpData->udpSocket->close();
|
---|
1462 | #endif
|
---|
1463 | }
|
---|
1464 |
|
---|
1465 | qint64 QSocks5SocketEngine::bytesAvailable() const
|
---|
1466 | {
|
---|
1467 | Q_D(const QSocks5SocketEngine);
|
---|
1468 | if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
|
---|
1469 | return d->connectData->readBuffer.size();
|
---|
1470 | #ifndef QT_NO_UDPSOCKET
|
---|
1471 | else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode
|
---|
1472 | && !d->udpData->pendingDatagrams.isEmpty())
|
---|
1473 | return d->udpData->pendingDatagrams.first().data.size();
|
---|
1474 | #endif
|
---|
1475 | return 0;
|
---|
1476 | }
|
---|
1477 |
|
---|
1478 | qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
|
---|
1479 | {
|
---|
1480 | Q_D(QSocks5SocketEngine);
|
---|
1481 | QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ")";
|
---|
1482 | if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
|
---|
1483 | if (d->connectData->readBuffer.size() == 0) {
|
---|
1484 | if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
|
---|
1485 | //imitate remote closed
|
---|
1486 | close();
|
---|
1487 | setError(QAbstractSocket::RemoteHostClosedError,
|
---|
1488 | QLatin1String("Remote host closed connection###"));
|
---|
1489 | setState(QAbstractSocket::UnconnectedState);
|
---|
1490 | return -1;
|
---|
1491 | } else {
|
---|
1492 | return 0; // nothing to be read
|
---|
1493 | }
|
---|
1494 | }
|
---|
1495 | qint64 copy = qMin<qint64>(d->connectData->readBuffer.size(), maxlen);
|
---|
1496 | memcpy(data, d->connectData->readBuffer.constData(), copy);
|
---|
1497 | d->connectData->readBuffer.remove(0, copy);
|
---|
1498 | QSOCKS5_DEBUG << "read" << dump(QByteArray(data, copy));
|
---|
1499 | return copy;
|
---|
1500 | #ifndef QT_NO_UDPSOCKET
|
---|
1501 | } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
|
---|
1502 | return readDatagram(data, maxlen);
|
---|
1503 | #endif
|
---|
1504 | }
|
---|
1505 | return 0;
|
---|
1506 | }
|
---|
1507 |
|
---|
1508 | qint64 QSocks5SocketEngine::write(const char *data, qint64 len)
|
---|
1509 | {
|
---|
1510 | Q_D(QSocks5SocketEngine);
|
---|
1511 | QSOCKS5_Q_DEBUG << "write" << dump(QByteArray(data, len));
|
---|
1512 |
|
---|
1513 | if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
|
---|
1514 | // clamp down the amount of bytes to transfer at once
|
---|
1515 | len = qMin<qint64>(len, MaxWriteBufferSize) - d->data->controlSocket->bytesToWrite();
|
---|
1516 | if (len <= 0)
|
---|
1517 | return 0;
|
---|
1518 |
|
---|
1519 | QByteArray buf = QByteArray::fromRawData(data, len);
|
---|
1520 | QByteArray sealedBuf;
|
---|
1521 | if (!d->data->authenticator->seal(buf, &sealedBuf)) {
|
---|
1522 | // ### Handle this error.
|
---|
1523 | }
|
---|
1524 |
|
---|
1525 | d->data->controlSocket->write(sealedBuf);
|
---|
1526 | d->data->controlSocket->waitForBytesWritten(0);
|
---|
1527 | return len;
|
---|
1528 | #ifndef QT_NO_UDPSOCKET
|
---|
1529 | } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
|
---|
1530 | // send to connected address
|
---|
1531 | return writeDatagram(data, len, d->peerAddress, d->peerPort);
|
---|
1532 | #endif
|
---|
1533 | }
|
---|
1534 | //### set an error ???
|
---|
1535 | return -1;
|
---|
1536 | }
|
---|
1537 |
|
---|
1538 | #ifndef QT_NO_UDPSOCKET
|
---|
1539 | qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr,
|
---|
1540 | quint16 *port)
|
---|
1541 | {
|
---|
1542 | Q_D(QSocks5SocketEngine);
|
---|
1543 |
|
---|
1544 | d->checkForDatagrams();
|
---|
1545 |
|
---|
1546 | if (d->udpData->pendingDatagrams.isEmpty())
|
---|
1547 | return 0;
|
---|
1548 |
|
---|
1549 | QSocks5RevivedDatagram datagram = d->udpData->pendingDatagrams.dequeue();
|
---|
1550 | int copyLen = qMin<int>(maxlen, datagram.data.size());
|
---|
1551 | memcpy(data, datagram.data.constData(), copyLen);
|
---|
1552 | if (addr)
|
---|
1553 | *addr = datagram.address;
|
---|
1554 | if (port)
|
---|
1555 | *port = datagram.port;
|
---|
1556 | return copyLen;
|
---|
1557 | }
|
---|
1558 |
|
---|
1559 | qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QHostAddress &address,
|
---|
1560 | quint16 port)
|
---|
1561 | {
|
---|
1562 | Q_D(QSocks5SocketEngine);
|
---|
1563 |
|
---|
1564 | // it is possible to send with out first binding with udp, but socks5 requires a bind.
|
---|
1565 | if (!d->data) {
|
---|
1566 | d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
|
---|
1567 | // all udp needs to be bound
|
---|
1568 | if (!bind(QHostAddress(QLatin1String("0.0.0.0")), 0)) {
|
---|
1569 | //### set error
|
---|
1570 | return -1;
|
---|
1571 | }
|
---|
1572 | }
|
---|
1573 |
|
---|
1574 | QByteArray outBuf;
|
---|
1575 | outBuf.reserve(270 + len);
|
---|
1576 | outBuf[0] = 0x00;
|
---|
1577 | outBuf[1] = 0x00;
|
---|
1578 | outBuf[2] = 0x00;
|
---|
1579 | if (!qt_socks5_set_host_address_and_port(address, port, &outBuf)) {
|
---|
1580 | }
|
---|
1581 | outBuf += QByteArray(data, len);
|
---|
1582 | QSOCKS5_DEBUG << "sending" << dump(outBuf);
|
---|
1583 | QByteArray sealedBuf;
|
---|
1584 | if (!d->data->authenticator->seal(outBuf, &sealedBuf)) {
|
---|
1585 | QSOCKS5_DEBUG << "sealing data failed";
|
---|
1586 | setError(QAbstractSocket::SocketAccessError, d->data->authenticator->errorString());
|
---|
1587 | return -1;
|
---|
1588 | }
|
---|
1589 | if (d->udpData->udpSocket->writeDatagram(sealedBuf, d->udpData->associateAddress, d->udpData->associatePort) != sealedBuf.size()) {
|
---|
1590 | //### try frgamenting
|
---|
1591 | if (d->udpData->udpSocket->error() == QAbstractSocket::DatagramTooLargeError)
|
---|
1592 | setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
|
---|
1593 | //### else maybe more serious error
|
---|
1594 | return -1;
|
---|
1595 | }
|
---|
1596 |
|
---|
1597 | return len;
|
---|
1598 | }
|
---|
1599 |
|
---|
1600 | bool QSocks5SocketEngine::hasPendingDatagrams() const
|
---|
1601 | {
|
---|
1602 | Q_D(const QSocks5SocketEngine);
|
---|
1603 | Q_INIT_CHECK(false);
|
---|
1604 |
|
---|
1605 | d->checkForDatagrams();
|
---|
1606 |
|
---|
1607 | return !d->udpData->pendingDatagrams.isEmpty();
|
---|
1608 | }
|
---|
1609 |
|
---|
1610 | qint64 QSocks5SocketEngine::pendingDatagramSize() const
|
---|
1611 | {
|
---|
1612 | Q_D(const QSocks5SocketEngine);
|
---|
1613 |
|
---|
1614 | d->checkForDatagrams();
|
---|
1615 |
|
---|
1616 | if (!d->udpData->pendingDatagrams.isEmpty())
|
---|
1617 | return d->udpData->pendingDatagrams.head().data.size();
|
---|
1618 | return 0;
|
---|
1619 | }
|
---|
1620 | #endif // QT_NO_UDPSOCKET
|
---|
1621 |
|
---|
1622 | int QSocks5SocketEngine::option(SocketOption option) const
|
---|
1623 | {
|
---|
1624 | Q_UNUSED(option);
|
---|
1625 | return -1;
|
---|
1626 | }
|
---|
1627 |
|
---|
1628 | bool QSocks5SocketEngine::setOption(SocketOption option, int value)
|
---|
1629 | {
|
---|
1630 | Q_UNUSED(option);
|
---|
1631 | Q_UNUSED(value);
|
---|
1632 | return false;
|
---|
1633 | }
|
---|
1634 |
|
---|
1635 | bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
|
---|
1636 | {
|
---|
1637 | if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
|
---|
1638 | return false;
|
---|
1639 |
|
---|
1640 | const Socks5State wantedState =
|
---|
1641 | mode == ConnectMode ? Connected :
|
---|
1642 | mode == BindMode ? BindSuccess :
|
---|
1643 | UdpAssociateSuccess;
|
---|
1644 |
|
---|
1645 | QTime stopWatch;
|
---|
1646 | stopWatch.start();
|
---|
1647 |
|
---|
1648 | while (socks5State != wantedState) {
|
---|
1649 | if (!data->controlSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
|
---|
1650 | if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
|
---|
1651 | return true;
|
---|
1652 |
|
---|
1653 | setErrorState(QSocks5SocketEnginePrivate::ControlSocketError);
|
---|
1654 | if (timedOut && data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
|
---|
1655 | *timedOut = true;
|
---|
1656 | return false;
|
---|
1657 | }
|
---|
1658 | }
|
---|
1659 |
|
---|
1660 | return true;
|
---|
1661 | }
|
---|
1662 |
|
---|
1663 | bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
|
---|
1664 | {
|
---|
1665 | Q_D(QSocks5SocketEngine);
|
---|
1666 | QSOCKS5_DEBUG << "waitForRead" << msecs;
|
---|
1667 |
|
---|
1668 | d->readNotificationActivated = false;
|
---|
1669 |
|
---|
1670 | QTime stopWatch;
|
---|
1671 | stopWatch.start();
|
---|
1672 |
|
---|
1673 | // are we connected yet?
|
---|
1674 | if (!d->waitForConnected(msecs, timedOut))
|
---|
1675 | return false;
|
---|
1676 | if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
|
---|
1677 | return true;
|
---|
1678 |
|
---|
1679 | // we're connected
|
---|
1680 | if (d->mode == QSocks5SocketEnginePrivate::ConnectMode ||
|
---|
1681 | d->mode == QSocks5SocketEnginePrivate::BindMode) {
|
---|
1682 | while (!d->readNotificationActivated) {
|
---|
1683 | if (!d->data->controlSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
|
---|
1684 | if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
|
---|
1685 | return true;
|
---|
1686 |
|
---|
1687 | setError(d->data->controlSocket->error(), d->data->controlSocket->errorString());
|
---|
1688 | if (timedOut && d->data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
|
---|
1689 | *timedOut = true;
|
---|
1690 | return false;
|
---|
1691 | }
|
---|
1692 | }
|
---|
1693 | #ifndef QT_NO_UDPSOCKET
|
---|
1694 | } else {
|
---|
1695 | while (!d->readNotificationActivated) {
|
---|
1696 | if (!d->udpData->udpSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
|
---|
1697 | setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
|
---|
1698 | if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
|
---|
1699 | *timedOut = true;
|
---|
1700 | return false;
|
---|
1701 | }
|
---|
1702 | }
|
---|
1703 | #endif // QT_NO_UDPSOCKET
|
---|
1704 | }
|
---|
1705 |
|
---|
1706 |
|
---|
1707 | bool ret = d->readNotificationActivated;
|
---|
1708 | d->readNotificationActivated = false;
|
---|
1709 |
|
---|
1710 | QSOCKS5_DEBUG << "waitForRead returned" << ret;
|
---|
1711 | return ret;
|
---|
1712 | }
|
---|
1713 |
|
---|
1714 |
|
---|
1715 | bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
|
---|
1716 | {
|
---|
1717 | Q_D(QSocks5SocketEngine);
|
---|
1718 | QSOCKS5_DEBUG << "waitForWrite" << msecs;
|
---|
1719 |
|
---|
1720 | QTime stopWatch;
|
---|
1721 | stopWatch.start();
|
---|
1722 |
|
---|
1723 | // are we connected yet?
|
---|
1724 | if (!d->waitForConnected(msecs, timedOut))
|
---|
1725 | return false;
|
---|
1726 | if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
|
---|
1727 | return true;
|
---|
1728 |
|
---|
1729 | // we're connected
|
---|
1730 |
|
---|
1731 | // flush any bytes we may still have buffered in the time that we have left
|
---|
1732 | if (d->data->controlSocket->bytesToWrite())
|
---|
1733 | d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed()));
|
---|
1734 | while ((msecs == -1 || stopWatch.elapsed() < msecs)
|
---|
1735 | && d->data->controlSocket->state() == QAbstractSocket::ConnectedState
|
---|
1736 | && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize)
|
---|
1737 | d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed()));
|
---|
1738 | return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
|
---|
1739 | }
|
---|
1740 |
|
---|
1741 | bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
|
---|
1742 | bool checkRead, bool checkWrite,
|
---|
1743 | int msecs, bool *timedOut)
|
---|
1744 | {
|
---|
1745 | Q_UNUSED(checkRead);
|
---|
1746 | if (!checkWrite) {
|
---|
1747 | bool canRead = waitForRead(msecs, timedOut);
|
---|
1748 | if (readyToRead)
|
---|
1749 | *readyToRead = canRead;
|
---|
1750 | return canRead;
|
---|
1751 | }
|
---|
1752 |
|
---|
1753 | bool canWrite = waitForWrite(msecs, timedOut);
|
---|
1754 | if (readyToWrite)
|
---|
1755 | *readyToWrite = canWrite;
|
---|
1756 | return canWrite;
|
---|
1757 | }
|
---|
1758 |
|
---|
1759 | bool QSocks5SocketEngine::isReadNotificationEnabled() const
|
---|
1760 | {
|
---|
1761 | Q_D(const QSocks5SocketEngine);
|
---|
1762 | return d->readNotificationEnabled;
|
---|
1763 | }
|
---|
1764 |
|
---|
1765 | void QSocks5SocketEngine::setReadNotificationEnabled(bool enable)
|
---|
1766 | {
|
---|
1767 | Q_D(QSocks5SocketEngine);
|
---|
1768 |
|
---|
1769 | QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ")";
|
---|
1770 |
|
---|
1771 | bool emitSignal = false;
|
---|
1772 | if (!d->readNotificationEnabled
|
---|
1773 | && enable) {
|
---|
1774 | if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
|
---|
1775 | emitSignal = !d->connectData->readBuffer.isEmpty();
|
---|
1776 | #ifndef QT_NO_UDPSOCKET
|
---|
1777 | else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode)
|
---|
1778 | emitSignal = !d->udpData->pendingDatagrams.isEmpty();
|
---|
1779 | #endif
|
---|
1780 | else if (d->mode == QSocks5SocketEnginePrivate::BindMode
|
---|
1781 | && d->socketState == QAbstractSocket::ListeningState
|
---|
1782 | && d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
|
---|
1783 | emitSignal = true;
|
---|
1784 | }
|
---|
1785 |
|
---|
1786 | d->readNotificationEnabled = enable;
|
---|
1787 |
|
---|
1788 | if (emitSignal)
|
---|
1789 | d->emitReadNotification();
|
---|
1790 | }
|
---|
1791 |
|
---|
1792 | bool QSocks5SocketEngine::isWriteNotificationEnabled() const
|
---|
1793 | {
|
---|
1794 | Q_D(const QSocks5SocketEngine);
|
---|
1795 | return d->writeNotificationEnabled;
|
---|
1796 | }
|
---|
1797 |
|
---|
1798 | void QSocks5SocketEngine::setWriteNotificationEnabled(bool enable)
|
---|
1799 | {
|
---|
1800 | Q_D(QSocks5SocketEngine);
|
---|
1801 | d->writeNotificationEnabled = enable;
|
---|
1802 | if (enable && d->socketState == QAbstractSocket::ConnectedState) {
|
---|
1803 | if (d->mode == QSocks5SocketEnginePrivate::ConnectMode && d->data->controlSocket->bytesToWrite())
|
---|
1804 | return; // will be emitted as a result of bytes written
|
---|
1805 | d->emitWriteNotification();
|
---|
1806 | d->writeNotificationActivated = false;
|
---|
1807 | }
|
---|
1808 | }
|
---|
1809 |
|
---|
1810 | bool QSocks5SocketEngine::isExceptionNotificationEnabled() const
|
---|
1811 | {
|
---|
1812 | Q_D(const QSocks5SocketEngine);
|
---|
1813 | return d->exceptNotificationEnabled;
|
---|
1814 | }
|
---|
1815 |
|
---|
1816 | void QSocks5SocketEngine::setExceptionNotificationEnabled(bool enable)
|
---|
1817 | {
|
---|
1818 | Q_D(QSocks5SocketEngine);
|
---|
1819 | d->exceptNotificationEnabled = enable;
|
---|
1820 | }
|
---|
1821 |
|
---|
1822 | QAbstractSocketEngine *
|
---|
1823 | QSocks5SocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
|
---|
1824 | const QNetworkProxy &proxy, QObject *parent)
|
---|
1825 | {
|
---|
1826 | Q_UNUSED(socketType);
|
---|
1827 |
|
---|
1828 | // proxy type must have been resolved by now
|
---|
1829 | if (proxy.type() != QNetworkProxy::Socks5Proxy) {
|
---|
1830 | QSOCKS5_DEBUG << "not proxying";
|
---|
1831 | return 0;
|
---|
1832 | }
|
---|
1833 | QSocks5SocketEngine *engine = new QSocks5SocketEngine(parent);
|
---|
1834 | engine->setProxy(proxy);
|
---|
1835 | return engine;
|
---|
1836 | }
|
---|
1837 |
|
---|
1838 | QAbstractSocketEngine *QSocks5SocketEngineHandler::createSocketEngine(int socketDescriptor, QObject *parent)
|
---|
1839 | {
|
---|
1840 | QSOCKS5_DEBUG << "createSocketEngine" << socketDescriptor;
|
---|
1841 | if (socks5BindStore()->contains(socketDescriptor)) {
|
---|
1842 | QSOCKS5_DEBUG << "bind store contains" << socketDescriptor;
|
---|
1843 | return new QSocks5SocketEngine(parent);
|
---|
1844 | }
|
---|
1845 | return 0;
|
---|
1846 | }
|
---|
1847 |
|
---|
1848 | #endif // QT_NO_SOCKS5
|
---|
1849 |
|
---|
1850 | QT_END_NAMESPACE
|
---|