3#include <QLoggingCategory>
4#include <QStandardPaths>
9 #if defined(HAVE_UNIX_DOMAIN_SOCKET)
21 #include <arpa/inet.h>
22 #include <sys/socket.h>
23 #include <netinet/tcp.h>
24 #if defined(HAVE_UNIX_DOMAIN_SOCKET)
29#include "ChannelSSHTunnelForward.h"
31static Q_LOGGING_CATEGORY(log,
"Channel.SSH.Tunnel.Forward")
36 m_Listen(SSH_INVALID_SOCKET),
37 m_Connector(SSH_INVALID_SOCKET),
40 qDebug(log) <<
"ChannelSSHTunnelForward::ChannelSSHTunnelForward()";
41 m_pBuffer =
new char[m_BufferLength];
44CChannelSSHTunnelForward::~CChannelSSHTunnelForward()
46 qDebug(log) <<
"ChannelSSHTunnelForward::~ChannelSSHTunnelForward()";
51int CChannelSSHTunnelForward::OpenSocket()
56 int type = SOCK_STREAM;
60 struct sockaddr_in listen_addr;
61 memset(&listen_addr, 0,
sizeof(listen_addr));
62 listen_addr.sin_family = AF_INET;
63 listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
64 listen_addr.sin_port = 0;
66 m_Listen = socket(family, type, 0);
67 if(SSH_INVALID_SOCKET == m_Listen) {
68 szErr =
"Create socket fail:" + QString::number(errno);
69 qCritical(log) << szErr;
70 setErrorString(szErr);
74 Channel::CEvent::SetSocketNonBlocking(m_Listen);
78 if (bind(m_Listen, (
struct sockaddr *) &listen_addr,
sizeof(listen_addr))
80 szErr =
"bind fail:" + QString(inet_ntoa(listen_addr.sin_addr))
81 + QString(
":") + QString::number(ntohs(listen_addr.sin_port))
82 +
" - " + QString::number(errno);
83 qCritical(log) << szErr;
84 setErrorString(szErr);
88 if (listen(m_Listen, 1) == -1) {
89 szErr =
"listen fail:" + QString(inet_ntoa(listen_addr.sin_addr))
90 + QString(
":") + QString::number(ntohs(listen_addr.sin_port))
91 +
" - " + QString::number(errno);
92 qCritical(log) << szErr;
93 setErrorString(szErr);
98 size =
sizeof(listen_addr);
99 if (getsockname(m_Listen, (
struct sockaddr *) &listen_addr, &size) == -1)
104 if (size !=
sizeof (listen_addr))
106 qDebug(log) <<
"listener in:"
107 << inet_ntoa(listen_addr.sin_addr)
108 + QString(
":") + QString::number(ntohs(listen_addr.sin_port));
109 emit sigServer(inet_ntoa(listen_addr.sin_addr), ntohs(listen_addr.sin_port));
114 CloseSocket(m_Listen);
115 m_Listen = SSH_INVALID_SOCKET;
121#if defined(HAVE_UNIX_DOMAIN_SOCKET)
122int CChannelSSHTunnelForward::OpenUnixSocket()
125 int family = AF_UNIX;
126 int type = SOCK_STREAM;
130 struct sockaddr_un listen_addr;
132 QString szUnixDomainSocket;
133 memset(&listen_addr, 0,
sizeof(listen_addr));
134 szPath = QStandardPaths::writableLocation(
135 QStandardPaths::TempLocation)
136 + QDir::separator() +
"RabbitRemoteControl";
137 auto now = std::chrono::system_clock::now();
138 auto tick = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
139 szUnixDomainSocket = szPath + QDir::separator() +
"RRC_" + QString::number(tick) +
"_"
140 + QString::number((
unsigned long)QThread::currentThreadId()) +
".socket";
144 QDir f(szUnixDomainSocket);
146 f.remove(szUnixDomainSocket);
147 size =
sizeof(sockaddr_un::sun_path);
148 if(szUnixDomainSocket.size() > size)
150 qCritical(log) <<
"The unix domain socket length greater then" << size;
153 memset(&listen_addr, 0,
sizeof(listen_addr));
154 listen_addr.sun_family = AF_UNIX;
155 strcpy(listen_addr.sun_path, szUnixDomainSocket.toStdString().c_str());
157 m_Listen = socket(family, type, 0);
158 if(SSH_INVALID_SOCKET == m_Listen) {
159 szErr =
"Create socket fail:" + QString::number(errno);
160 qCritical(log) << szErr;
161 setErrorString(szErr);
165 Channel::CEvent::SetSocketNonBlocking(m_Listen);
169 if (bind(m_Listen, (
struct sockaddr *) &listen_addr,
sizeof(listen_addr))
171 szErr =
"bind fail:" + QString(listen_addr.sun_path)
172 +
" - " + QString::number(errno);
173 qCritical(log) << szErr;
174 setErrorString(szErr);
178 if (listen(m_Listen, 1) == -1) {
179 szErr =
"listen fail:" + QString(listen_addr.sun_path)
180 +
" - " + QString::number(errno);
181 qCritical(log) << szErr;
182 setErrorString(szErr);
187 emit sigServer(szUnixDomainSocket);
188 qDebug(log) <<
"listener in:" << listen_addr.sun_path;
193 CloseSocket(m_Listen);
194 m_Listen = SSH_INVALID_SOCKET;
209 int nRet = OpenSocket();
210 if(nRet)
return false;
215void CChannelSSHTunnelForward::close()
217 qDebug(log) <<
"CChannelSSHTunnelForward::close()";
218 if(SSH_INVALID_SOCKET != m_Listen)
219 CloseSocket(m_Listen);
220 if(SSH_INVALID_SOCKET != m_Connector)
221 CloseSocket(m_Connector);
222 CChannelSSHTunnel::close();
225int CChannelSSHTunnelForward::CloseSocket(socket_t &s)
228 if(SSH_INVALID_SOCKET == s)
231 nRet = ::closesocket(s);
236 qCritical(log) <<
"Close socket fail" << errno <<
":" << strerror(errno);
237 s = SSH_INVALID_SOCKET;
241int CChannelSSHTunnelForward::AcceptConnect()
244 if(SSH_INVALID_SOCKET == m_Listen)
return 0;
245#if defined(HAVE_UNIX_DOMAIN_SOCKET)
246 struct sockaddr_un connect_addr;
248 struct sockaddr_in connect_addr;
250 memset(&connect_addr, 0,
sizeof(connect_addr));
251 socklen_t size =
sizeof(connect_addr);
252 m_Connector = accept(m_Listen, (
struct sockaddr *)&connect_addr, &size);
253 if(SSH_INVALID_SOCKET == m_Connector)
258 if(EAGAIN == errno || EWOULDBLOCK == errno)
261 QString szErr =
"The server accept is fail:";
262 szErr += QString::number(errno) +
" [" + strerror(errno) +
"]";
263 qCritical(log) << szErr;
264 setErrorString(szErr);
268 qDebug(log) <<
"accept connector fd:" << m_Connector;
269 Channel::CEvent::SetSocketNonBlocking(m_Connector);
271 CloseSocket(m_Listen);
272 m_Listen = SSH_INVALID_SOCKET;
276int CChannelSSHTunnelForward::ReadConnect()
280 if(SSH_INVALID_SOCKET == m_Connector) {
281 qDebug(log) <<
"The connector is invalid";
291 nRet = recv(m_Connector, m_pBuffer, m_BufferLength, 0);
297 if(EAGAIN == errno || EWOULDBLOCK == errno)
300 QString szErr =
"read data from socket fail:"
301 + QString::number(errno) +
" - " + strerror(errno)
302 +
" m_Server:" + QString::number(m_Listen);
303 qCritical(log) << szErr;
304 setErrorString(szErr);
309 int n = write(m_pBuffer, nRet);
311 QString szErr =
"write ssh tunnel fail:" + errorString();
312 qCritical(log) << szErr;
313 setErrorString(szErr);
317 }
while(m_BufferLength == nRet);
321int CChannelSSHTunnelForward::SSHReadyRead()
325 if(SSH_INVALID_SOCKET == m_Connector) {
335 nRet = read(m_pBuffer, m_BufferLength);
337 QString szErr =
"read data from ssh tunnel fail:" + errorString();
338 qCritical(log) << szErr;
339 setErrorString(szErr);
343 int n = send(m_Connector, m_pBuffer, nRet, 0);
346 if(EAGAIN == errno || EWOULDBLOCK == errno)
349 QString szErr =
"send to socket fail, connector fd:" + QString::number(m_Connector)
350 +
" - [" + QString::number(errno) +
"] - " + strerror(errno);
351 qCritical(log) << szErr;
352 setErrorString(szErr);
357 }
while(m_BufferLength == nRet);
361int CChannelSSHTunnelForward::Process()
365 if(!m_Session || !m_Channel || !ssh_channel_is_open(m_Channel)
366 || ssh_channel_is_eof(m_Channel)) {
367 QString szErr =
"The channel is not open";
368 qCritical(log) << szErr;
369 setErrorString(szErr);
373 struct timeval timeout = {0, 50000};
374 ssh_channel channels[2], channel_out[2];
375 channels[0] = m_Channel;
376 channels[1] =
nullptr;
380 socket_t fd = SSH_INVALID_SOCKET;
381 socket_t fdSSH = ssh_get_fd(m_Session);
383 if(SSH_INVALID_SOCKET != m_Listen) {
385 FD_SET(m_Listen, &set);
387 nRet = select(fd + 1, &set, NULL, NULL, &timeout);
388 }
else if(SSH_INVALID_SOCKET != m_Connector) {
390 FD_SET(m_Connector, &set);
392 if(SSH_INVALID_SOCKET != fdSSH)
395 fd = std::max(fd, fdSSH);
397 nRet = ssh_select(channels, channel_out, fd + 1, &set, &timeout);
403 szErr =
"ssh_channel_select failed: " + QString::number(nRet);
404 szErr += ssh_get_error(m_Session);
405 qCritical(log) << szErr;
406 setErrorString(szErr);
411 if(SSH_INVALID_SOCKET != m_Listen && FD_ISSET(m_Listen, &set)) {
412 nRet = AcceptConnect();
414 }
else if(SSH_INVALID_SOCKET != m_Connector && FD_ISSET(m_Connector, &set)) {
415 nRet = ReadConnect();
416 if(nRet)
return nRet;
420 qDebug(log) <<
"There is not channel";
424 if(ssh_channel_is_eof(m_Channel)) {
425 qWarning(log) <<
"Channel is eof";
426 setErrorString(tr(
"The channel is eof"));
430 nRet = ssh_channel_poll(m_Channel, 0);
434 szErr =
"ssh_channel_poll failed. nRet:";
435 szErr += QString::number(nRet);
436 szErr += ssh_get_error(m_Session);
437 qCritical(log) << szErr;
438 setErrorString(szErr);
446 return SSHReadyRead();
Includes SSH tunneling and a local socket service for forwarding data.
virtual bool open(OpenMode mode) override
virtual bool open(OpenMode mode) override