Rabbit Remote Control 0.0.37
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
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, "SSH.Tunnel")
22
24 CParameterSSHTunnel* parameter,
25 CParameterNet *remote,
26 CBackend *pBackend,
27 bool bWakeUp,
28 QObject *parent)
29 : CChannelSSH(pBackend, parameter, bWakeUp, parent)
30 , m_pRemoteNet(remote)
31/*
32 m_pSocketRead(nullptr),
33 m_pSocketWrite(nullptr),
34 m_pSocketException(nullptr),
35*/
36{
37 qDebug(log) << "CChannelSSHTunnel::CChannelSSHTunnel()";
38 qDebug(log) << "libssh version:" << ssh_version(0);
39}
40
41CChannelSSHTunnel::~CChannelSSHTunnel()
42{
43 qDebug(log) << "CChannelSSHTunnel::~CChannelSSHTunnel()";
44 if(m_pEvent)
45 delete m_pEvent;
46}
47
48int CChannelSSHTunnel::GetSocket()
49{
50 if(m_Session)
51 return ssh_get_fd(m_Session);
52 return SSH_INVALID_SOCKET;
53}
54
55void CChannelSSHTunnel::OnClose()
56{
57 qDebug(log) << "CChannelSSHTunnel::close()";
58
59 if(!isOpen()) return;
60
61 WakeUp();
62
63 /*
64 QAbstractEventDispatcher* pDispatcher = QAbstractEventDispatcher::instance();
65 if(m_pSocketRead) {
66 pDispatcher->unregisterSocketNotifier(m_pSocketRead);
67 m_pSocketRead->deleteLater();
68 m_pSocketRead = nullptr;
69 }
70 if(m_pSocketWrite) {
71 pDispatcher->unregisterSocketNotifier(m_pSocketWrite);
72 m_pSocketWrite->deleteLater();
73 m_pSocketWrite = nullptr;
74 }
75 if(m_pSocketException) {
76 pDispatcher->unregisterSocketNotifier(m_pSocketException);
77 m_pSocketException->deleteLater();
78 m_pSocketException = nullptr;
79 }//*/
80
81 if(m_Channel) {
82 if(ssh_channel_is_open(m_Channel)) {
83 ssh_channel_close(m_Channel);
84 }
85 ssh_channel_free(m_Channel);
86 m_Channel = NULL;
87 }
88}
89
90int CChannelSSHTunnel::OnOpen(ssh_session session)
91{
92 int nRet = 0;
93
94 Q_ASSERT(session);
95
96 m_Channel = ssh_channel_new(session);
97 if(NULL == m_Channel) {
98 qCritical(log) << "ssh_channel_new fail." << ssh_get_error(session);
99 return -1;
100 }
101
102 CParameterSSHTunnel* pPara = qobject_cast<CParameterSSHTunnel*>(m_pParameter);
103 Q_ASSERT(pPara);
104 nRet = ssh_channel_open_forward(
105 m_Channel,
106 m_pRemoteNet->GetHost().toStdString().c_str(),
107 m_pRemoteNet->GetPort(),
108 pPara->GetSourceHost().toStdString().c_str(),
109 pPara->GetSourcePort());
110 if(SSH_OK != nRet) {
111 ssh_channel_free(m_Channel);
112 m_Channel = NULL;
113
114 QString szErr;
115 szErr = tr("SSH failed: open forward.") + ssh_get_error(session);
116 szErr += "(" + m_pRemoteNet->GetHost()
117 + ":" + QString::number(m_pRemoteNet->GetPort()) + ")";
118 qCritical(log) << szErr;
119 setErrorString(szErr);
120 return nRet;
121 }
122
123 qInfo(log) << "Connected:"
124 << m_pRemoteNet->GetHost()
125 + ":" + QString::number(m_pRemoteNet->GetPort())
126 << "with ssh turnnel:"
127 << m_pParameter->m_Net.GetHost()
128 + ":" + QString::number(m_pParameter->m_Net.GetPort());
129
130 //ssh_channel_set_blocking(m_Channel, 0);
131
132 return nRet;
133}
134
142{
143 int nRet = 0;
144
145 if(!m_Channel || !ssh_channel_is_open(m_Channel)
146 || ssh_channel_is_eof(m_Channel)) {
147 QString szErr = "The channel is not open";
148 qCritical(log) << szErr;
149 setErrorString(szErr);
150 return -1;
151 }
152
153 struct timeval timeout = {0, DEFAULT_TIMEOUT};
154 ssh_channel channels[2], channel_out[2];
155 channels[0] = m_Channel;
156 channels[1] = nullptr;
157
158 fd_set set;
159 FD_ZERO(&set);
160 socket_t fd = SSH_INVALID_SOCKET;
161 if(m_pEvent)
162 fd = m_pEvent->GetFd();
163 if(SSH_INVALID_SOCKET != fd)
164 FD_SET(fd, &set);
165
166 //qDebug(log) << "ssh_select:" << fd;
167 nRet = ssh_select(channels, channel_out, fd + 1, &set, &timeout);
168 //qDebug(log) << "ssh_select end:" << nRet;
169 if(EINTR == nRet)
170 return 0;
171
172 if(SSH_OK != nRet) {
173 QString szErr;
174 szErr = "ssh_channel_select failed: " + QString::number(nRet);
175 szErr += ssh_get_error(m_Session);
176 qCritical(log) << szErr;
177 setErrorString(szErr);
178 return -3;
179 }
180
181 if(SSH_INVALID_SOCKET != fd && FD_ISSET(fd, &set)) {
182 //qDebug(log) << "fires event";
183 if(m_pEvent) {
184 nRet = m_pEvent->Reset();
185 if(nRet) return -4;
186 }
187 }
188
189 if(!channel_out[0]) {
190 //qDebug(log) << "The channel is not select";
191 return 0;
192 }
193
194 if(ssh_channel_is_eof(m_Channel)) {
195 qWarning(log) << "Channel is eof";
196 setErrorString(tr("The channel is eof"));
197 // Stop
198 return -1;
199 }
200
201 // Get channel data length
202 nRet = ssh_channel_poll(m_Channel, 0);
203 //qDebug(log) << "Get channel data length:" << nRet;
204 if(SSH_ERROR == nRet)
205 {
206 QString szErr;
207 szErr = "ssh_channel_poll failed. nRet:";
208 szErr += QString::number(nRet);
209 szErr += ssh_get_error(m_Session);
210 setErrorString(szErr);
211 qCritical(log) << szErr;
212 return -6;
213 } else if(SSH_EOF == nRet) {
214 // Stop
215 return -1;
216 } else if(0 > nRet) {
217 QString szErr;
218 szErr = "ssh_channel_poll failed. nRet:";
219 szErr += QString::number(nRet);
220 szErr += ssh_get_error(m_Session);
221 setErrorString(szErr);
222 qCritical(log) << szErr;
223 // Error
224 return -7;
225 } else if(0 == nRet) {
226 //qDebug(log) << "The channel has not data";
227 return 0;
228 }
229
230 emit readyRead();
231
232 return 0;
233}
234
235// Because is same thread
236qint64 CChannelSSHTunnel::readData(char *data, qint64 maxlen)
237{
238 qint64 nRet = 0;
239
240 //qDebug(log) << Q_FUNC_INFO << maxlen;
241
242 Q_ASSERT(data && maxlen >= 0);
243 if(nullptr == data || 0 > maxlen) {
244 qCritical(log) << Q_FUNC_INFO << "The parameters is invalid" << maxlen;
245 return -1;
246 }
247
248 if(0 == maxlen) {
249 qCritical(log) << Q_FUNC_INFO << "maxlen:" << maxlen;
250 return 0;
251 }
252
253 if(!m_Channel || !ssh_channel_is_open(m_Channel))
254 {
255 QString szErr;
256 szErr = "The channel is not opened";
257 qCritical(log) << szErr;
258 setErrorString(szErr);
259 return -1;
260 }
261
262 nRet = ssh_channel_read_nonblocking(m_Channel, data, maxlen, 0);
263 if(SSH_AGAIN == nRet) {
264 qDebug(log) << Q_FUNC_INFO << "ssh again read";
265 return 0;
266 } else if(0 > nRet) {
267 QString szErr;
268 szErr = "Read data from channel failed. nRet:";
269 szErr += QString::number(nRet);
270 szErr += ssh_get_error(m_Session);
271 qCritical(log) << szErr;
272 return nRet;
273 }
274
275 return nRet;
276}
277
278qint64 CChannelSSHTunnel::writeData(const char *data, qint64 len)
279{
280 qint64 nRet = 0;
281
282 Q_ASSERT(data && len >= 0);
283 if(nullptr == data || 0 > len) {
284 qCritical(log) << Q_FUNC_INFO << "The parameters is invalid" << len;
285 return -1;
286 }
287
288 if(0 == len) {
289 qCritical(log) << Q_FUNC_INFO << "len:" << len;
290 return 0;
291 }
292
293 if(!m_Channel || !ssh_channel_is_open(m_Channel) || ssh_channel_is_eof(m_Channel))
294 {
295 QString szErr;
296 szErr = "The channel is not opened";
297 qCritical(log) << szErr;
298 setErrorString(szErr);
299 return -1;
300 }
301
302 nRet = ssh_channel_write(m_Channel, data, len);
303 if(SSH_AGAIN == nRet) {
304 qDebug(log) << Q_FUNC_INFO << "ssh again write";
305 return 0;
306 } else if(nRet < 0) {
307 QString szErr;
308 szErr = "Write data from channel failed:";
309 szErr += ssh_get_error(m_Session);
310 qCritical(log) << szErr;
311 setErrorString(szErr);
312 return -2;
313 }
314
315 return nRet;
316}
317
318int CChannelSSHTunnel::DoWait(bool bWrite, int timeout)
319{
320 int nRet = 0;
321 if(!m_Channel || !ssh_channel_is_open(m_Channel)
322 || ssh_channel_is_eof(m_Channel)) {
323 QString szErr = "The channel is not open";
324 qCritical(log) << szErr;
325 setErrorString(szErr);
326 return -1;
327 }
328
329 fd_set set;
330 FD_ZERO(&set);
331
332 struct timeval tm = {0, timeout};
333 ssh_channel channels[2], channel_out[2];
334 channels[0] = m_Channel;
335 channels[1] = nullptr;
336
337 if(bWrite) {
338 socket_t fd = SSH_INVALID_SOCKET;
339 if(m_pEvent)
340 fd = GetSocket();
341 if(SSH_INVALID_SOCKET != fd)
342 FD_SET(fd, &set);
343 nRet = select(fd + 1, nullptr, &set, nullptr, &tm);
344 if(0 > nRet) return nRet;
345 return 0;
346 }
347
348 //qDebug(log) << "ssh_select:" << fd;
349 nRet = ssh_select(channels, channel_out, 1, &set, &tm);
350 //qDebug(log) << "ssh_select end:" << nRet;
351 if(EINTR == nRet)
352 return 0;
353
354 if(SSH_OK != nRet) {
355 QString szErr;
356 szErr = "ssh_channel_select failed: " + QString::number(nRet);
357 szErr += ssh_get_error(m_Session);
358 qCritical(log) << szErr;
359 setErrorString(szErr);
360 return -3;
361 }
362
363 if(!channel_out[0]) {
364 //qDebug(log) << "The channel is not select";
365 return 0;
366 }
367
368 if(ssh_channel_is_eof(m_Channel)) {
369 qWarning(log) << "Channel is eof";
370 setErrorString(tr("The channel is eof"));
371 // Stop
372 return -1;
373 }
374
375 // Get channel data length
376 nRet = ssh_channel_poll(m_Channel, 0);
377 //qDebug(log) << "Get channel data length:" << nRet;
378 if(SSH_ERROR == nRet)
379 {
380 QString szErr;
381 szErr = "ssh_channel_poll failed. nRet:";
382 szErr += QString::number(nRet);
383 szErr += ssh_get_error(m_Session);
384 setErrorString(szErr);
385 qCritical(log) << szErr;
386 return -6;
387 } else if(SSH_EOF == nRet) {
388 // Stop
389 return -1;
390 } else if(0 > nRet) {
391 QString szErr;
392 szErr = "ssh_channel_poll failed. nRet:";
393 szErr += QString::number(nRet);
394 szErr += ssh_get_error(m_Session);
395 setErrorString(szErr);
396 qCritical(log) << szErr;
397 // Error
398 return -7;
399 } else if(0 == nRet) {
400 //qDebug(log) << "The channel has not data";
401 return 0;
402 }
403 return 0;
404}
405
406/*
407int CChannelSSHTunnel::ProcessSocket()
408{
409 int nRet = 0;
410 bool check = false;
411 socket_t fd = ssh_get_fd(m_Session);
412 m_pSocketRead = new QSocketNotifier(fd, QSocketNotifier::Read, this);
413 if(m_pSocketRead) {
414 check = connect(
415 m_pSocketRead, &QSocketNotifier::activated,
416 this, [&](int fd) {
417 qDebug(log) << "QSocketNotifier::activated: read";
418 Q_UNUSED(fd)
419 emit this->readyRead();
420 });
421 Q_ASSERT(check);
422 }
423
424 // m_pSocketWrite = new QSocketNotifier(fd, QSocketNotifier::Write, this);
425 // if(m_pSocketWrite) {
426 // check = connect(
427 // m_pSocketWrite, &QSocketNotifier::activated,
428 // this, [&](int fd){
429 // Q_UNUSED(fd)
430 // qDebug(log) << "QSocketNotifier::activated: write";
431 // });
432 // Q_ASSERT(check);
433 // }
434
435 m_pSocketException = new QSocketNotifier(fd, QSocketNotifier::Exception, this);
436 if(m_pSocketException) {
437 check = connect(
438 m_pSocketException, &QSocketNotifier::activated,
439 this, [&](int) {
440 qDebug(log) << "QSocketNotifier::activated: Exception";
441 QString szErr;
442 szErr = "Channel exception:";
443 szErr += ssh_get_error(m_Session);
444 qCritical(log) << szErr;
445 emit sigError(-1, szErr);
446 });
447 Q_ASSERT(check);
448 }
449
450 return nRet;
451}
452//*/
Backend interface.
Definition Backend.h:42
ssh tunnel class
Basic network parameters.