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, 
"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);
 
  318                qWarning(log) << 
"The send data" << n << 
"<" << nRet;
 
  320    } 
while(m_BufferLength == nRet);
 
  324int CChannelSSHTunnelForward::SSHReadyRead()
 
  328    if(SSH_INVALID_SOCKET == m_Connector) {
 
  338        nRet = read(m_pBuffer, m_BufferLength);
 
  340            QString szErr = 
"read data from ssh tunnel fail:" + errorString();
 
  341            qCritical(log) << szErr;
 
  342            setErrorString(szErr);
 
  346            int n = send(m_Connector, m_pBuffer, nRet, 0);
 
  349                if(EAGAIN == errno || EWOULDBLOCK == errno) {
 
  354                    QString szErr = 
"send to socket fail, connector fd:" + QString::number(m_Connector)
 
  355                                    + 
" - [" + QString::number(errno) + 
"] - " + strerror(errno);
 
  356                    qCritical(log) << szErr;
 
  357                    setErrorString(szErr);
 
  362    } 
while(m_BufferLength == nRet);
 
  376    if(!m_Session || !m_Channel || !ssh_channel_is_open(m_Channel)
 
  377        || ssh_channel_is_eof(m_Channel)) {
 
  378        QString szErr = 
"The channel is not open";
 
  379        qCritical(log) << szErr;
 
  380        setErrorString(szErr);
 
  384    struct timeval timeout = {0, DEFAULT_TIMEOUT};
 
  385    ssh_channel channels[2], channel_out[2];
 
  386    channels[0] = m_Channel;
 
  387    channels[1] = 
nullptr;
 
  388    channel_out[0] = channel_out[1] = 
nullptr;
 
  392    socket_t fd = SSH_INVALID_SOCKET;
 
  393    socket_t fdSSH = ssh_get_fd(m_Session);
 
  395    if(SSH_INVALID_SOCKET != m_Listen) {
 
  397        FD_SET(m_Listen, &set);
 
  400        nRet = ssh_select(channels, channel_out, fd + 1, &set, &timeout);
 
  401    } 
else if(SSH_INVALID_SOCKET != m_Connector) {
 
  403        FD_SET(m_Connector, &set);
 
  405        if(SSH_INVALID_SOCKET != fdSSH)
 
  408            fd = std::max(fd, fdSSH);
 
  410        nRet = ssh_select(channels, channel_out, fd + 1, &set, &timeout);
 
  417        szErr = 
"ssh_channel_select failed: " + QString::number(nRet);
 
  418        szErr += ssh_get_error(m_Session);
 
  419        qCritical(log) << szErr;
 
  420        setErrorString(szErr);
 
  425    if(SSH_INVALID_SOCKET != m_Listen && FD_ISSET(m_Listen, &set)) {
 
  426        nRet = AcceptConnect();
 
  428    } 
else if(SSH_INVALID_SOCKET != m_Connector && FD_ISSET(m_Connector, &set)) {
 
  429        nRet = ReadConnect();
 
  430        if(nRet) 
return nRet;
 
  433    if(!channel_out[0]) {
 
  438    if(ssh_channel_is_eof(m_Channel)) {
 
  439        qWarning(log) << 
"Channel is eof";
 
  440        setErrorString(tr(
"The channel is eof"));
 
  446    nRet = ssh_channel_poll(m_Channel, 0);
 
  448    if(SSH_ERROR == nRet)
 
  451        szErr = 
"ssh_channel_poll failed. nRet:";
 
  452        szErr += QString::number(nRet);
 
  453        szErr += ssh_get_error(m_Session);
 
  454        setErrorString(szErr);
 
  455        qCritical(log) << szErr;
 
  457    } 
else if(SSH_EOF == nRet) {
 
  460    } 
else if(0 > nRet) {
 
  462        szErr = 
"ssh_channel_poll failed. nRet:";
 
  463        szErr += QString::number(nRet);
 
  464        szErr += ssh_get_error(m_Session);
 
  465        setErrorString(szErr);
 
  466        qCritical(log) << szErr;
 
  469    } 
else if(0 == nRet) {
 
  474    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
 
Basic network parameters.