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