Rabbit Remote Control 0.0.36
Loading...
Searching...
No Matches
ChannelSSHTunnel.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include "libssh/libssh.h"
4#include "libssh/callbacks.h"
5
6#include "ChannelSSHTunnel.h"
7#include <QLoggingCategory>
8#include <QThread>
9#include <QDateTime>
10#include <QAbstractEventDispatcher>
11#include <QScopedArrayPointer>
12#include <QtGlobal>
13#if defined(Q_OS_LINUX)
14 #include <sys/eventfd.h>
15#endif
16#if defined(Q_OS_WIN)
17 #include <WinSock2.h>
18 //#pragma comment(lib,"ws2_32.lib")
19#endif
20
21static Q_LOGGING_CATEGORY(log, "Channel.SSH.Tunnel")
22static Q_LOGGING_CATEGORY(logSSH, "Channel.SSH.log")
23
25 CParameterSSHTunnel* parameter,
26 CParameterNet *remote,
27 CConnect *pConnect,
28 bool bWakeUp,
29 QObject *parent)
30 : CChannel(parent),
31 m_Session(NULL),
32 m_Channel(NULL),
33 m_pConnect(pConnect),
34 m_pcapFile(NULL),
35 m_pParameter(parameter),
36 m_pRemoteNet(remote),
37/*
38 m_pSocketRead(nullptr),
39 m_pSocketWrite(nullptr),
40 m_pSocketException(nullptr),
41*/
42 m_pEvent(nullptr)
43{
44 qDebug(log) << "CChannelSSHTunnel::CChannelSSHTunnel()";
45 qDebug(log) << "libssh version:" << ssh_version(0);
46
47 if(bWakeUp)
48 m_pEvent = new Channel::CEvent(this);
49}
50
51CChannelSSHTunnel::~CChannelSSHTunnel()
52{
53 qDebug(log) << "CChannelSSHTunnel::~CChannelSSHTunnel()";
54 if(m_pEvent)
55 delete m_pEvent;
56}
57
59{
60 return tr("- libssh version: ") + ssh_version(0);
61}
62
63void CChannelSSHTunnel::cb_log(ssh_session session,
64 int priority,
65 const char *message,
66 void *userdata)
67{
68 switch (priority) {
69 case SSH_LOG_WARN:
70 qWarning(logSSH) << message;
71 break;
72 case SSH_LOG_INFO:
73 qInfo(logSSH) << message;
74 case SSH_LOG_DEBUG:
75 case SSH_LOG_TRACE:
76 qDebug(logSSH) << message;
77 default:
78 break;
79 }
80}
81
82int CChannelSSHTunnel::GetSocket()
83{
84 if(m_Session)
85 return ssh_get_fd(m_Session);
86 return SSH_INVALID_SOCKET;
87}
88
89int CChannelSSHTunnel::WakeUp()
90{
91 if(!m_pEvent) return 0;
92 return m_pEvent->WakeUp();
93}
94
95bool CChannelSSHTunnel::open(OpenMode mode)
96{
97 int nRet = 0;
98 QString szErr;
99
100 if(!m_pParameter) {
101 qCritical(log) << "The parameter is null";
102 }
103 if(!m_pRemoteNet)
104 qCritical(log) << "The remote net parameter is null";
105 Q_ASSERT(m_pParameter);
106 Q_ASSERT(m_pRemoteNet);
107
108 bool bRet = QIODevice::open(mode);
109 if(!bRet)
110 return bRet;
111
112 m_Session = ssh_new();
113 if(NULL == m_Session)
114 {
115 szErr = tr("SSH failed: ssh_new.");
116 qCritical(log) << szErr;
117 setErrorString(szErr);
118 return false;
119 }
120
121 do{
122 struct ssh_callbacks_struct cb;
123 memset(&cb, 0, sizeof(struct ssh_callbacks_struct));
124 cb.userdata = this,
125 cb.log_function = cb_log;;
126 ssh_callbacks_init(&cb);
127 ssh_set_callbacks(m_Session, &cb);
128
129 /*
130 int value = 1;
131 ssh_options_set(m_Session, SSH_OPTIONS_NODELAY, (const void*)&value);//*/
132
133 /*
134 See: [ssh_options_set()](https://api.libssh.org/stable/group__libssh__session.html#ga7a801b85800baa3f4e16f5b47db0a73d)
135 SSH_LOG_NOLOG: No logging
136 SSH_LOG_WARNING: Only warnings
137 SSH_LOG_PROTOCOL: High level protocol information
138 SSH_LOG_PACKET: Lower level protocol information, packet level
139 SSH_LOG_FUNCTIONS: Every function path The default is SSH_LOG_NOLOG.
140 */
141 int verbosity = SSH_LOG_NOLOG;
142 ssh_options_set(m_Session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
143
144 auto &net = m_pParameter->m_Net;
145 if(net.GetHost().isEmpty()) {
146 szErr = tr("SSH failed: the server is empty");
147 qCritical(log) << szErr;
148 setErrorString(szErr);
149 break;
150 } else {
151 nRet = ssh_options_set(m_Session, SSH_OPTIONS_HOST,
152 net.GetHost().toStdString().c_str());
153 if(nRet) {
154 szErr = tr("SSH failed: Set host fail. host:")
155 + net.GetHost() + ";"
156 + ssh_get_error(m_Session);
157 qCritical(log) << szErr;
158 setErrorString(szErr);
159 break;
160 }
161 }
162
163 uint nPort = net.GetPort();
164 nRet = ssh_options_set(m_Session, SSH_OPTIONS_PORT, &nPort);
165 if(nRet) {
166 szErr = tr("SSH failed: Set port fail. port:")
167 + QString::number(net.GetPort()) + ";"
168 + ssh_get_error(m_Session);
169 qCritical(log) << szErr;
170 setErrorString(szErr);
171 break;
172 }
173
174 if(!m_pParameter->GetPcapFile().isEmpty())
175 {
176 m_pcapFile = ssh_pcap_file_new();
177 if(m_pcapFile) {
178 if (ssh_pcap_file_open(
179 m_pcapFile, m_pParameter->GetPcapFile().toStdString().c_str())
180 == SSH_ERROR) {
181 qCritical(log) << "SSH failed: Error opening pcap file: "
182 << m_pParameter->GetPcapFile();
183 ssh_pcap_file_free(m_pcapFile);
184 m_pcapFile = nullptr;
185 }
186 if(m_pcapFile)
187 ssh_set_pcap_file(m_Session, m_pcapFile);
188 } else {
189 szErr = tr("SSH failed: ssh_pcap_file_new: ")
190 + ssh_get_error(m_Session);
191 qCritical(log) << szErr;
192 }
193 }
194
195 nRet = ssh_connect(m_Session);
196 if(nRet) {
197 szErr = tr("SSH failed: ssh connect ")
198 + net.GetHost()
199 + ":" + QString::number(net.GetPort())
200 + " ("
201 + ssh_get_error(m_Session);
202 szErr += ")";
203 qCritical(log) << szErr;
204 setErrorString(szErr);
205 break;
206 }
207
208 nRet = verifyKnownhost(m_Session);
209 if(nRet){
210 break;
211 }
212
213 auto &user = m_pParameter->m_Net.m_User;
214 if(((user.GetUsedType() == CParameterUser::TYPE::UserPassword)
215 && (user.GetPassword().isEmpty() || user.GetUser().isEmpty()))
216 || ((user.GetUsedType() == CParameterUser::TYPE::PublicKey)
217 && user.GetPassphrase().isEmpty())
218 && m_pConnect) {
219 emit m_pConnect->sigBlockShowWidget("CDlgUserPassword", nRet, &m_pParameter->m_Net);
220 if(QDialog::Accepted != nRet)
221 {
222 setErrorString(tr("User cancel"));
223 return false;
224 }
225 }
226
227 int nMeth = SSH_AUTH_METHOD_PUBLICKEY;
228 switch(user.GetUsedType()) {
229 case CParameterUser::TYPE::UserPassword:
230 nMeth = SSH_AUTH_METHOD_PASSWORD;
231 break;
232 case CParameterUser::TYPE::PublicKey:
233 nMeth = SSH_AUTH_METHOD_PUBLICKEY;
234 break;
235 }
236
237 nRet = authentication(m_Session,
238 user.GetUser(),
239 user.GetPassword(),
240 user.GetPassphrase(),
241 nMeth);
242 if(nRet) break;
243
244 nRet = forward(m_Session);
245 if(nRet) break;
246
247 emit sigConnected();
248
249 return bRet;
250 } while(0);
251
252 ssh_disconnect(m_Session);
253 ssh_free(m_Session);
254 m_Session = NULL;
255 return false;
256}
257
258void CChannelSSHTunnel::close()
259{
260 qDebug(log) << "CChannelSSHTunnel::close()";
261
262 if(!isOpen()) return;
263
264 WakeUp();
265
266 /*
267 QAbstractEventDispatcher* pDispatcher = QAbstractEventDispatcher::instance();
268 if(m_pSocketRead) {
269 pDispatcher->unregisterSocketNotifier(m_pSocketRead);
270 m_pSocketRead->deleteLater();
271 m_pSocketRead = nullptr;
272 }
273 if(m_pSocketWrite) {
274 pDispatcher->unregisterSocketNotifier(m_pSocketWrite);
275 m_pSocketWrite->deleteLater();
276 m_pSocketWrite = nullptr;
277 }
278 if(m_pSocketException) {
279 pDispatcher->unregisterSocketNotifier(m_pSocketException);
280 m_pSocketException->deleteLater();
281 m_pSocketException = nullptr;
282 }//*/
283
284 if(m_Channel) {
285 if(ssh_channel_is_open(m_Channel)) {
286 ssh_channel_close(m_Channel);
287 }
288 ssh_channel_free(m_Channel);
289 m_Channel = NULL;
290 }
291
292 if(m_Session) {
293 if(ssh_is_connected(m_Session))
294 ssh_disconnect(m_Session);
295 ssh_free(m_Session);
296 m_Session = NULL;
297 }
298
299 if(m_pcapFile)
300 {
301 ssh_pcap_file_close(m_pcapFile);
302 ssh_pcap_file_free(m_pcapFile);
303 m_pcapFile = nullptr;
304 }
305
306 QIODevice::close();
307}
308
309int CChannelSSHTunnel::verifyKnownhost(ssh_session session)
310{
311 int nRet = -1;
312 QString szErr;
313 ssh_key srv_pubkey = NULL;
314
315 auto &net = m_pParameter->m_Net;
316
317 nRet = ssh_get_server_publickey(session, &srv_pubkey);
318 if (nRet < 0) {
319 szErr = tr("SSH failed: Get server public key.") + " " + net.GetHost() + "; [";
320 szErr += ssh_get_error(session);
321 szErr += "]";
322 qCritical(log) << szErr;
323 setErrorString(szErr);
324 return -1;
325 }
326 unsigned char *hash = NULL;
327 size_t nLen = 0;
328 nRet = ssh_get_publickey_hash(srv_pubkey,
329 SSH_PUBLICKEY_HASH_SHA1,
330 &hash,
331 &nLen);
332 ssh_key_free(srv_pubkey);
333 if(nRet) {
334 szErr = tr("SSH failed: Get public key hash value fail.");
335 qCritical(log) << szErr;
336 setErrorString(szErr);
337 return -2;
338 }
339 QByteArray baHash((const char*)hash, nLen);
340 QString szHash = baHash.toHex(':').toStdString().c_str();
341 ssh_clean_pubkey_hash(&hash);
342
343 QMessageBox::StandardButton btRet = QMessageBox::Yes;
344 bool checkBox = false;
345 enum ssh_known_hosts_e state = ssh_session_is_known_server(session);
346 switch(state) {
347 case SSH_KNOWN_HOSTS_OK:
348 nRet = 0;
349 break;
350 case SSH_KNOWN_HOSTS_CHANGED:
351 nRet = -3;
352 szErr = net.GetHost() + " " + tr("the host key for server changed. it is now:") + "\n";
353 szErr += szHash + "\n";
354 szErr += tr("For security reasons, connection will be stopped.") + "\n";
355 szErr += tr("Please look at the OpenSSL documentation on "
356 "how to add a private CA to the store.");
357 qCritical(log) << szErr;
358 setErrorString(szErr);
359 break;
360 case SSH_KNOWN_HOSTS_OTHER:
361 nRet = -4;
362 szErr = net.GetHost() + " " + tr("the host key for this server was not found but an other type of key exists.") + "\n";
363 szErr += tr("An attacker might change the default server key to "
364 "confuse your client into thinking the key does not exist") + "\n";
365 szErr += tr("For security reasons, connection will be stopped.") + "\n";
366 szErr += tr("Please look at the OpenSSL documentation on "
367 "how to add a private CA to the store.");
368 qCritical(log) << szErr;
369 setErrorString(szErr);
370 break;
371 case SSH_KNOWN_HOSTS_NOT_FOUND:
372 nRet = -5;
373 szErr = net.GetHost() + " " + tr("is not find in known host file.") + "\n";
374 szErr += tr("If you accept the host key here, the file will be "
375 "automatically created.") + "\n";
376 szErr += tr("Host key hash:") + "\n" + szHash;
377 qDebug(log) << szErr;
378 if(!m_pConnect) break;
379 emit m_pConnect->sigBlockShowMessageBox(tr("Error"), szErr,
380#ifndef Q_OS_ANDROID
381 QMessageBox::Yes |
382#endif
383 QMessageBox::No | QMessageBox::Ignore,
384 btRet, checkBox);
385 if(QMessageBox::Yes == btRet) {
386 nRet = ssh_session_update_known_hosts(session);
387 if(nRet)
388 {
389 qCritical(log) << "ssh_session_update_known_hosts fail."
390 << ssh_get_error(session);
391 }
392 } if(QMessageBox::Ignore == btRet)
393 nRet = 0;
394 else
395 setErrorString(tr("Reject the host key"));
396 break;
397 case SSH_KNOWN_HOSTS_UNKNOWN:
398 nRet = -6;
399 szErr = net.GetHost() + " " + tr("is unknown. Do you trust the host key?") + "\n";
400 szErr += tr("Host key hash:") + "\n" + szHash;
401 qDebug(log) << szErr;
402 if(!m_pConnect) break;
403 emit m_pConnect->sigBlockShowMessageBox(tr("Error"), szErr,
404#ifndef Q_OS_ANDROID
405 QMessageBox::Yes |
406#endif
407 QMessageBox::No | QMessageBox::Ignore,
408 btRet, checkBox);
409 if(QMessageBox::Yes == btRet) {
410 nRet = ssh_session_update_known_hosts(session);
411 if (SSH_OK != nRet) {
412 qCritical(log) << "ssh_session_update_known_hosts fail."
413 << ssh_get_error(session);
414 }
415 } if(QMessageBox::Ignore == btRet)
416 nRet = 0;
417 else
418 setErrorString(tr("Reject the host key"));
419 break;
420 case SSH_KNOWN_HOSTS_ERROR:
421 nRet = -7;
422 szErr = tr("Error:") + ssh_get_error(session) + "\n";
423 szErr += net.GetHost() + " " + tr("the host key hash:") + "\n" + szHash + "\n";
424 szErr += tr("Will be stopped.");
425 qCritical(log) << szErr;
426 setErrorString(szErr);
427 emit sigError(nRet, szErr);
428 break;
429 }
430
431 return nRet;
432}
433
436 ssh_session session,
437 const QString szUser,
438 const QString szPassword,
439 const QString szPassphrase,
440 const int nMethod)
441{
442 int nRet = 0;
443 int nServerMethod = nMethod;
444
445 qDebug(log) << "Authentication method:" << nMethod;
446 //* Get authentication list from ssh server
447 nRet = ssh_userauth_none(session,
448 szUser.toStdString().c_str());
449 qDebug(log) << "ssh_userauth_none:" << nRet;
450 if(SSH_AUTH_SUCCESS == nRet)
451 return 0;
452
453 char *banner = nullptr;
454 banner = ssh_get_issue_banner(session);
455 if (banner)
456 {
457 qDebug(log) << "banner:" << banner;
458 free(banner);
459 }
460
461 nServerMethod = ssh_userauth_list(session,
462 szUser.toStdString().c_str());
463 qDebug(log) << "ssh_userauth_list:" << nServerMethod;
464 //*/
465
466 if(nServerMethod & nMethod & SSH_AUTH_METHOD_PUBLICKEY) {
467 auto &user = m_pParameter->m_Net.m_User;
468 if(user.GetUseSystemFile()) {
469 qDebug(log) << "User authentication with ssh_userauth_publickey_auto";
470 nRet = ssh_userauth_publickey_auto(session,
471 szUser.toStdString().c_str(),
472 szPassphrase.toStdString().c_str());
473 if(SSH_AUTH_SUCCESS == nRet)
474 return 0;
475 QString szErr = tr("SSH failed: Failed authenticating with publickey:")
476 + ssh_get_error(m_Session);
477 qCritical(log) << szErr;
478 setErrorString(szErr);
479 } else {
480 qDebug(log) << "User authentication with publickey";
481 nRet = authenticationPublicKey(
482 m_Session,
483 user.GetUser(),
484 user.GetPublicKeyFile(),
485 user.GetPrivateKeyFile(),
486 user.GetPassphrase());
487 if(SSH_AUTH_SUCCESS == nRet)
488 return 0;
489 }
490 }
491
492 if(nServerMethod & nMethod & SSH_AUTH_METHOD_PASSWORD) {
493 qDebug(log) << "User authentication with password";
494
495 nRet = ssh_userauth_password(session,
496 szUser.toStdString().c_str(),
497 szPassword.toStdString().c_str());
498 if(nRet) {
499 QString szErr = tr("Failed authenticating with password. User: ")
500 + szUser + "; "
501 + ssh_get_error(session);
502 qCritical(log) << szErr;
503 setErrorString(szErr);
504 return nRet;
505 }
506 }
507
508 return nRet;
509}
510
511int CChannelSSHTunnel::authenticationPublicKey(
512 ssh_session session,
513 const QString szUser,
514 const QString szPublicKeyFile,
515 const QString szPrivateKeyFile,
516 const QString szPassphrase)
517{
518 int nRet = 0;
519 QString szErr;
520 ssh_key publicKey = NULL;
521 ssh_key privateKey = NULL;
522
523 do {
524 if(szPublicKeyFile.isEmpty())
525 {
526 szErr = tr("SSH failed: There is not set public key file.");
527 qCritical(log) << szErr;
528 setErrorString(szErr);
529 break;
530 }
531 nRet = ssh_pki_import_pubkey_file(
532 szPublicKeyFile.toStdString().c_str(),
533 &publicKey);
534 if(SSH_OK != nRet) {
535 szErr = tr("SSH failed: Import public key fail.") + szPublicKeyFile;
536 if(SSH_EOF == nRet)
537 szErr += "\n" + tr("The file doesn't exist or permission denied:");
538 qCritical(log) << szErr;
539 setErrorString(szErr);
540 break;
541 }
542
543 nRet = ssh_userauth_try_publickey(
544 session,
545 szUser.toStdString().c_str(),
546 publicKey);
547 if(SSH_AUTH_SUCCESS != nRet)
548 {
549 szErr = tr("SSH failed: Authentication failed. User:") + szUser + "\n";
550 szErr += ssh_get_error(session);
551 qCritical(log) << szErr;
552 setErrorString(szErr);
553 break;
554 }
555
556 if(szPrivateKeyFile.isEmpty())
557 {
558 szErr = tr("SSH failed: There is not set private key file.");
559 qCritical(log) << szErr;
560 setErrorString(szErr);
561 break;
562 }
563 nRet = ssh_pki_import_privkey_file(
564 szPrivateKeyFile.toStdString().c_str(),
565 szPassphrase.toStdString().c_str(),
566 NULL, NULL, &privateKey);
567 if(SSH_OK != nRet) {
568
569 szErr = tr("SSH failed: Import private key fail.") + szPrivateKeyFile;
570 if(SSH_EOF == nRet)
571 szErr += "\n" + tr("The file doesn't exist or permission denied:");
572 qCritical(log) << szErr;
573 setErrorString(szErr);
574 break;
575 }
576
577 nRet = ssh_userauth_publickey(
578 session,
579 szUser.toStdString().c_str(),
580 privateKey);
581 if(SSH_AUTH_SUCCESS != nRet) {
582 szErr = tr("SSH failed: Authentication failed. User:") + szUser + "\n";
583 szErr += ssh_get_error(session);
584 qCritical(log) << szErr;
585 setErrorString(szErr);
586 }
587
588 } while(0);
589
590 if(publicKey)
591 ssh_key_free(publicKey);
592 if(privateKey)
593 ssh_key_free(privateKey);
594
595 return nRet;
596}
597
598int CChannelSSHTunnel::forward(ssh_session session)
599{
600 int nRet = 0;
601
602 Q_ASSERT(session);
603
604 m_Channel = ssh_channel_new(session);
605 if(NULL == m_Channel) {
606 qCritical(log) << "ssh_channel_new fail." << ssh_get_error(session);
607 return -1;
608 }
609
610 nRet = ssh_channel_open_forward(
611 m_Channel,
612 m_pRemoteNet->GetHost().toStdString().c_str(),
613 m_pRemoteNet->GetPort(),
614 m_pParameter->GetSourceHost().toStdString().c_str(),
615 m_pParameter->GetSourcePort());
616 if(SSH_OK != nRet) {
617 ssh_channel_free(m_Channel);
618 m_Channel = NULL;
619
620 QString szErr;
621 szErr = tr("SSH failed: open forward.") + ssh_get_error(session);
622 szErr += "(" + m_pRemoteNet->GetHost()
623 + ":" + QString::number(m_pRemoteNet->GetPort()) + ")";
624 qCritical(log) << szErr;
625 setErrorString(szErr);
626 return nRet;
627 }
628
629 qInfo(log) << "Connected:"
630 << m_pRemoteNet->GetHost()
631 + ":" + QString::number(m_pRemoteNet->GetPort())
632 << "with ssh turnnel:"
633 << m_pParameter->m_Net.GetHost()
634 + ":" + QString::number(m_pParameter->m_Net.GetPort());
635
636 //ssh_channel_set_blocking(m_Channel, 0);
637
638 return nRet;
639}
640
648{
649 int nRet = 0;
650
651 if(!m_Channel || !ssh_channel_is_open(m_Channel)
652 || ssh_channel_is_eof(m_Channel)) {
653 QString szErr = "The channel is not open";
654 qCritical(log) << szErr;
655 setErrorString(szErr);
656 return -1;
657 }
658
659 struct timeval timeout = {0, 5000000};
660 ssh_channel channels[2], channel_out[2];
661 channels[0] = m_Channel;
662 channels[1] = nullptr;
663
664 fd_set set;
665 FD_ZERO(&set);
666 socket_t fd = SSH_INVALID_SOCKET;
667 if(m_pEvent)
668 fd = m_pEvent->GetFd();
669 if(SSH_INVALID_SOCKET != fd)
670 FD_SET(fd, &set);
671
672 //qDebug(log) << "ssh_select:" << fd;
673 nRet = ssh_select(channels, channel_out, fd + 1, &set, &timeout);
674 //qDebug(log) << "ssh_select end:" << nRet;
675 if(EINTR == nRet)
676 return 0;
677
678 if(SSH_OK != nRet) {
679 QString szErr;
680 szErr = "ssh_channel_select failed: " + QString::number(nRet);
681 szErr += ssh_get_error(m_Session);
682 qCritical(log) << szErr;
683 setErrorString(szErr);
684 return -3;
685 }
686
687 if(SSH_INVALID_SOCKET != fd && FD_ISSET(fd, &set)) {
688 //qDebug(log) << "fires event";
689 if(m_pEvent) {
690 nRet = m_pEvent->Reset();
691 if(nRet) return -4;
692 }
693 }
694
695 if(!channel_out[0]) {
696 //qDebug(log) << "The channel is not select";
697 return 0;
698 }
699
700 if(ssh_channel_is_eof(m_Channel)) {
701 qWarning(log) << "Channel is eof";
702 setErrorString(tr("The channel is eof"));
703 // Stop
704 return -1;
705 }
706
707 // Get channel data length
708 nRet = ssh_channel_poll(m_Channel, 0);
709 //qDebug(log) << "Get channel data length:" << nRet;
710 if(SSH_ERROR == nRet)
711 {
712 QString szErr;
713 szErr = "ssh_channel_poll failed. nRet:";
714 szErr += QString::number(nRet);
715 szErr += ssh_get_error(m_Session);
716 setErrorString(szErr);
717 qCritical(log) << szErr;
718 return -6;
719 } else if(SSH_EOF == nRet) {
720 // Stop
721 return -1;
722 } else if(0 > nRet) {
723 QString szErr;
724 szErr = "ssh_channel_poll failed. nRet:";
725 szErr += QString::number(nRet);
726 szErr += ssh_get_error(m_Session);
727 setErrorString(szErr);
728 qCritical(log) << szErr;
729 // Error
730 return -7;
731 } else if(0 == nRet) {
732 //qDebug(log) << "The channel has not data";
733 return 0;
734 }
735
736 emit readyRead();
737
738 return 0;
739}
740
741// Because is same thread
742qint64 CChannelSSHTunnel::readData(char *data, qint64 maxlen)
743{
744 qint64 nRet = 0;
745
746 //qDebug(log) << Q_FUNC_INFO << maxlen;
747
748 Q_ASSERT(data && maxlen >= 0);
749 if(nullptr == data || 0 > maxlen) {
750 qCritical(log) << Q_FUNC_INFO << "The parameters is invalid" << maxlen;
751 return -1;
752 }
753
754 if(0 == maxlen) {
755 qCritical(log) << Q_FUNC_INFO << "maxlen:" << maxlen;
756 return 0;
757 }
758
759 if(!m_Channel || !ssh_channel_is_open(m_Channel))
760 {
761 QString szErr;
762 szErr = "The channel is not opened";
763 qCritical(log) << szErr;
764 setErrorString(szErr);
765 return -1;
766 }
767
768 nRet = ssh_channel_read_nonblocking(m_Channel, data, maxlen, 0);
769 if(SSH_AGAIN == nRet) {
770 qDebug(log) << Q_FUNC_INFO << "ssh again read";
771 return 0;
772 } else if(0 > nRet) {
773 QString szErr;
774 szErr = "Read data from channel failed. nRet:";
775 szErr += QString::number(nRet);
776 szErr += ssh_get_error(m_Session);
777 qCritical(log) << szErr;
778 return nRet;
779 }
780
781 return nRet;
782}
783
784qint64 CChannelSSHTunnel::writeData(const char *data, qint64 len)
785{
786 qint64 nRet = 0;
787
788 Q_ASSERT(data && len >= 0);
789 if(nullptr == data || 0 > len) {
790 qCritical(log) << Q_FUNC_INFO << "The parameters is invalid" << len;
791 return -1;
792 }
793
794 if(0 == len) {
795 qCritical(log) << Q_FUNC_INFO << "len:" << len;
796 return 0;
797 }
798
799 if(!m_Channel || !ssh_channel_is_open(m_Channel) || ssh_channel_is_eof(m_Channel))
800 {
801 QString szErr;
802 szErr = "The channel is not opened";
803 qCritical(log) << szErr;
804 setErrorString(szErr);
805 return -1;
806 }
807
808 nRet = ssh_channel_write(m_Channel, data, len);
809 if(SSH_AGAIN == nRet) {
810 qDebug(log) << Q_FUNC_INFO << "ssh again write";
811 return 0;
812 } else if(nRet < 0) {
813 QString szErr;
814 szErr = "Write data from channel failed:";
815 szErr += ssh_get_error(m_Session);
816 qCritical(log) << szErr;
817 setErrorString(szErr);
818 return -2;
819 }
820
821 return nRet;
822}
823
824int CChannelSSHTunnel::DoWait(bool bWrite, int timeout)
825{
826 int nRet = 0;
827 if(!m_Channel || !ssh_channel_is_open(m_Channel)
828 || ssh_channel_is_eof(m_Channel)) {
829 QString szErr = "The channel is not open";
830 qCritical(log) << szErr;
831 setErrorString(szErr);
832 return -1;
833 }
834
835 fd_set set;
836 FD_ZERO(&set);
837
838 struct timeval tm = {0, timeout};
839 ssh_channel channels[2], channel_out[2];
840 channels[0] = m_Channel;
841 channels[1] = nullptr;
842
843 if(bWrite) {
844 socket_t fd = SSH_INVALID_SOCKET;
845 if(m_pEvent)
846 fd = GetSocket();
847 if(SSH_INVALID_SOCKET != fd)
848 FD_SET(fd, &set);
849 nRet = select(fd + 1, nullptr, &set, nullptr, &tm);
850 if(0 > nRet) return nRet;
851 return 0;
852 }
853
854 //qDebug(log) << "ssh_select:" << fd;
855 nRet = ssh_select(channels, channel_out, 1, &set, &tm);
856 //qDebug(log) << "ssh_select end:" << nRet;
857 if(EINTR == nRet)
858 return 0;
859
860 if(SSH_OK != nRet) {
861 QString szErr;
862 szErr = "ssh_channel_select failed: " + QString::number(nRet);
863 szErr += ssh_get_error(m_Session);
864 qCritical(log) << szErr;
865 setErrorString(szErr);
866 return -3;
867 }
868
869 if(!channel_out[0]) {
870 //qDebug(log) << "The channel is not select";
871 return 0;
872 }
873
874 if(ssh_channel_is_eof(m_Channel)) {
875 qWarning(log) << "Channel is eof";
876 setErrorString(tr("The channel is eof"));
877 // Stop
878 return -1;
879 }
880
881 // Get channel data length
882 nRet = ssh_channel_poll(m_Channel, 0);
883 //qDebug(log) << "Get channel data length:" << nRet;
884 if(SSH_ERROR == nRet)
885 {
886 QString szErr;
887 szErr = "ssh_channel_poll failed. nRet:";
888 szErr += QString::number(nRet);
889 szErr += ssh_get_error(m_Session);
890 setErrorString(szErr);
891 qCritical(log) << szErr;
892 return -6;
893 } else if(SSH_EOF == nRet) {
894 // Stop
895 return -1;
896 } else if(0 > nRet) {
897 QString szErr;
898 szErr = "ssh_channel_poll failed. nRet:";
899 szErr += QString::number(nRet);
900 szErr += ssh_get_error(m_Session);
901 setErrorString(szErr);
902 qCritical(log) << szErr;
903 // Error
904 return -7;
905 } else if(0 == nRet) {
906 //qDebug(log) << "The channel has not data";
907 return 0;
908 }
909 return 0;
910}
911
912/*
913int CChannelSSHTunnel::ProcessSocket()
914{
915 int nRet = 0;
916 bool check = false;
917 socket_t fd = ssh_get_fd(m_Session);
918 m_pSocketRead = new QSocketNotifier(fd, QSocketNotifier::Read, this);
919 if(m_pSocketRead) {
920 check = connect(
921 m_pSocketRead, &QSocketNotifier::activated,
922 this, [&](int fd) {
923 qDebug(log) << "QSocketNotifier::activated: read";
924 Q_UNUSED(fd)
925 emit this->readyRead();
926 });
927 Q_ASSERT(check);
928 }
929
930 // m_pSocketWrite = new QSocketNotifier(fd, QSocketNotifier::Write, this);
931 // if(m_pSocketWrite) {
932 // check = connect(
933 // m_pSocketWrite, &QSocketNotifier::activated,
934 // this, [&](int fd){
935 // Q_UNUSED(fd)
936 // qDebug(log) << "QSocketNotifier::activated: write";
937 // });
938 // Q_ASSERT(check);
939 // }
940
941 m_pSocketException = new QSocketNotifier(fd, QSocketNotifier::Exception, this);
942 if(m_pSocketException) {
943 check = connect(
944 m_pSocketException, &QSocketNotifier::activated,
945 this, [&](int) {
946 qDebug(log) << "QSocketNotifier::activated: Exception";
947 QString szErr;
948 szErr = "Channel exception:";
949 szErr += ssh_get_error(m_Session);
950 qCritical(log) << szErr;
951 emit sigError(-1, szErr);
952 });
953 Q_ASSERT(check);
954 }
955
956 return nRet;
957}
958//*/
ssh tunnel class
virtual QString GetDetails() override
Depend on information.
virtual bool open(OpenMode mode) override
int authentication(ssh_session session, const QString szUser, const QString szPassword, const QString szPassphrase, const int nMethod=SSH_AUTH_METHOD_PASSWORD)
The channel interface class.
Definition Channel.h:25
void sigConnected()
emit when the channel is connected.
void sigError(int nErr, const QString &szErr)
emit when the channel is error
Connect interface.
Definition Connect.h:45
void sigBlockShowMessageBox(const QString &szTitle, const QString &szMessage, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton &nRet, bool &checkBox, QString checkBoxContext=QString())
Block background threads and display message dialogs in foreground threads (QMessageBox)
void sigBlockShowWidget(const QString &className, int &nRet, void *pContext)
Blocks the background thread and displays the window in the foreground thread.
Basic network parameters.
CParameterUser m_User
[Instance user]