diff options
author | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2016-03-31 18:57:03 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2016-05-23 07:44:06 +0000 |
commit | 6b4963b5499269062e2cffe9e70df4f022c5bf8c (patch) | |
tree | 843803dfdfb23b2b238b515cd54cff5c52740053 | |
parent | e05fbf153d98a34ddda63b12402c15642994acdc (diff) |
Ssh: Implement tcp/ip forward tunneling
Change-Id: I529b3392ea7e4cbae2c736c9f55352ef6b19da98
Reviewed-by: Christian Kandeler <christian.kandeler@theqtcompany.com>
32 files changed, 1276 insertions, 147 deletions
diff --git a/src/libs/ssh/ssh.pro b/src/libs/ssh/ssh.pro index 8608d619dc..fff459a959 100644 --- a/src/libs/ssh/ssh.pro +++ b/src/libs/ssh/ssh.pro @@ -30,7 +30,10 @@ SOURCES = $$PWD/sshsendfacility.cpp \ $$PWD/sshinit.cpp \ $$PWD/sshdirecttcpiptunnel.cpp \ $$PWD/sshlogging.cpp \ - $$PWD/sshhostkeydatabase.cpp + $$PWD/sshhostkeydatabase.cpp \ + $$PWD/sshtcpipforwardserver.cpp \ + $$PWD/sshtcpiptunnel.cpp \ + $$PWD/sshforwardedtcpiptunnel.cpp HEADERS = $$PWD/sshsendfacility_p.h \ $$PWD/sshremoteprocess.h \ @@ -68,7 +71,12 @@ HEADERS = $$PWD/sshsendfacility_p.h \ $$PWD/sshinit_p.h \ $$PWD/sshdirecttcpiptunnel.h \ $$PWD/sshlogging_p.h \ - $$PWD/sshhostkeydatabase.h + $$PWD/sshhostkeydatabase.h \ + $$PWD/sshtcpipforwardserver.h \ + $$PWD/sshtcpipforwardserver_p.h \ + $$PWD/sshtcpiptunnel_p.h \ + $$PWD/sshforwardedtcpiptunnel.h \ + $$PWD/sshforwardedtcpiptunnel_p.h FORMS = $$PWD/sshkeycreationdialog.ui diff --git a/src/libs/ssh/ssh.qbs b/src/libs/ssh/ssh.qbs index dc317992f3..0ac3fefb51 100644 --- a/src/libs/ssh/ssh.qbs +++ b/src/libs/ssh/ssh.qbs @@ -29,6 +29,7 @@ QtcLibrary { "sshdirecttcpiptunnel.h", "sshdirecttcpiptunnel_p.h", "sshdirecttcpiptunnel.cpp", "ssherrors.h", "sshexception_p.h", + "sshforwardedtcpiptunnel.cpp", "sshforwardedtcpiptunnel.h", "sshforwardedtcpiptunnel_p.h", "sshhostkeydatabase.cpp", "sshhostkeydatabase.h", "sshincomingpacket_p.h", "sshincomingpacket.cpp", @@ -46,6 +47,8 @@ QtcLibrary { "sshremoteprocess.cpp", "sshremoteprocess.h", "sshremoteprocess_p.h", "sshremoteprocessrunner.cpp", "sshremoteprocessrunner.h", "sshsendfacility.cpp", "sshsendfacility_p.h", + "sshtcpipforwardserver.cpp", "sshtcpipforwardserver.h", "sshtcpipforwardserver_p.h", + "sshtcpiptunnel.cpp", "sshtcpiptunnel_p.h", ].concat(botanFiles) property var useSystemBotan: Environment.getEnv("USE_SYSTEM_BOTAN") === "1" diff --git a/src/libs/ssh/sshchannel.cpp b/src/libs/ssh/sshchannel.cpp index c23776153f..f4ae59a97d 100644 --- a/src/libs/ssh/sshchannel.cpp +++ b/src/libs/ssh/sshchannel.cpp @@ -172,7 +172,7 @@ void AbstractSshChannel::handleOpenFailure(const QString &reason) return; // Late server reply; we requested a channel close in the meantime. default: throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet."); + "Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet."); } m_timeoutTimer.stop(); diff --git a/src/libs/ssh/sshchannelmanager.cpp b/src/libs/ssh/sshchannelmanager.cpp index d6784580b9..291c6d9273 100644 --- a/src/libs/ssh/sshchannelmanager.cpp +++ b/src/libs/ssh/sshchannelmanager.cpp @@ -29,10 +29,15 @@ #include "sftpchannel_p.h" #include "sshdirecttcpiptunnel.h" #include "sshdirecttcpiptunnel_p.h" +#include "sshforwardedtcpiptunnel.h" +#include "sshforwardedtcpiptunnel_p.h" #include "sshincomingpacket_p.h" +#include "sshlogging_p.h" #include "sshremoteprocess.h" #include "sshremoteprocess_p.h" #include "sshsendfacility_p.h" +#include "sshtcpipforwardserver.h" +#include "sshtcpipforwardserver_p.h" #include <QList> @@ -51,10 +56,54 @@ void SshChannelManager::handleChannelRequest(const SshIncomingPacket &packet) ->handleChannelRequest(packet); } -void SshChannelManager::handleChannelOpen(const SshIncomingPacket &) +void SshChannelManager::handleChannelOpen(const SshIncomingPacket &packet) { - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Server tried to open channel on client."); + SshChannelOpen channelOpen = packet.extractChannelOpen(); + + SshTcpIpForwardServer::Ptr server; + + foreach (const SshTcpIpForwardServer::Ptr &candidate, m_listeningForwardServers) { + if (candidate->port() == channelOpen.remotePort + && candidate->bindAddress().toUtf8() == channelOpen.remoteAddress) { + server = candidate; + break; + } + }; + + + if (server.isNull()) { + // Apparently the server knows a remoteAddress we are not aware of. There are plenty of ways + // to make that happen: /etc/hosts on the server, different writings for localhost, + // different DNS servers, ... + // Rather than trying to figure that out, we just use the first listening forwarder with the + // same port. + foreach (const SshTcpIpForwardServer::Ptr &candidate, m_listeningForwardServers) { + if (candidate->port() == channelOpen.remotePort) { + server = candidate; + break; + } + }; + } + + if (server.isNull()) { + SshOpenFailureType reason = (channelOpen.remotePort == 0) ? + SSH_OPEN_UNKNOWN_CHANNEL_TYPE : SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; + try { + m_sendFacility.sendChannelOpenFailurePacket(channelOpen.remoteChannel, reason, + QByteArray()); + } catch (const Botan::Exception &e) { + qCWarning(sshLog, "Botan error: %s", e.what()); + } + return; + } + + SshForwardedTcpIpTunnel::Ptr tunnel(new SshForwardedTcpIpTunnel(m_nextLocalChannelId++, + m_sendFacility)); + tunnel->d->handleOpenSuccess(channelOpen.remoteChannel, channelOpen.remoteWindowSize, + channelOpen.remoteMaxPacketSize); + tunnel->open(QIODevice::ReadWrite); + server->setNewConnection(tunnel); + insertChannel(tunnel->d, tunnel); } void SshChannelManager::handleChannelOpenFailure(const SshIncomingPacket &packet) @@ -125,6 +174,39 @@ void SshChannelManager::handleChannelClose(const SshIncomingPacket &packet) } } +void SshChannelManager::handleRequestSuccess(const SshIncomingPacket &packet) +{ + if (m_waitingForwardServers.isEmpty()) { + throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, + "Unexpected request success packet.", + tr("Unexpected request success packet.")); + } + SshTcpIpForwardServer::Ptr server = m_waitingForwardServers.takeFirst(); + if (server->state() == SshTcpIpForwardServer::Closing) { + server->setClosed(); + } else if (server->state() == SshTcpIpForwardServer::Initializing) { + quint16 port = server->port(); + if (port == 0) + port = packet.extractRequestSuccess().bindPort; + server->setListening(port); + m_listeningForwardServers.append(server); + } else { + QSSH_ASSERT(false); + } +} + +void SshChannelManager::handleRequestFailure(const SshIncomingPacket &packet) +{ + Q_UNUSED(packet); + if (m_waitingForwardServers.isEmpty()) { + throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, + "Unexpected request failure packet.", + tr("Unexpected request failure packet.")); + } + SshTcpIpForwardServer::Ptr tunnel = m_waitingForwardServers.takeFirst(); + tunnel->setClosed(); +} + SshChannelManager::ChannelIterator SshChannelManager::lookupChannelAsIterator(quint32 channelId, bool allowNotFound) { @@ -165,7 +247,7 @@ QSsh::SftpChannel::Ptr SshChannelManager::createSftpChannel() return sftp; } -SshDirectTcpIpTunnel::Ptr SshChannelManager::createTunnel(const QString &originatingHost, +SshDirectTcpIpTunnel::Ptr SshChannelManager::createDirectTunnel(const QString &originatingHost, quint16 originatingPort, const QString &remoteHost, quint16 remotePort) { SshDirectTcpIpTunnel::Ptr tunnel(new SshDirectTcpIpTunnel(m_nextLocalChannelId++, @@ -174,6 +256,28 @@ SshDirectTcpIpTunnel::Ptr SshChannelManager::createTunnel(const QString &origina return tunnel; } +SshTcpIpForwardServer::Ptr SshChannelManager::createForwardServer(const QString &remoteHost, + quint16 remotePort) +{ + SshTcpIpForwardServer::Ptr server(new SshTcpIpForwardServer(remoteHost, remotePort, + m_sendFacility)); + connect(server.data(), &SshTcpIpForwardServer::stateChanged, + this, [this, server](SshTcpIpForwardServer::State state) { + switch (state) { + case SshTcpIpForwardServer::Closing: + m_listeningForwardServers.removeOne(server); + // fall through + case SshTcpIpForwardServer::Initializing: + m_waitingForwardServers.append(server); + break; + case SshTcpIpForwardServer::Listening: + case SshTcpIpForwardServer::Inactive: + break; + } + }); + return server; +} + void SshChannelManager::insertChannel(AbstractSshChannel *priv, const QSharedPointer<QObject> &pub) { diff --git a/src/libs/ssh/sshchannelmanager_p.h b/src/libs/ssh/sshchannelmanager_p.h index 9f2865e4f3..413ed12c8c 100644 --- a/src/libs/ssh/sshchannelmanager_p.h +++ b/src/libs/ssh/sshchannelmanager_p.h @@ -33,6 +33,7 @@ namespace QSsh { class SftpChannel; class SshDirectTcpIpTunnel; class SshRemoteProcess; +class SshTcpIpForwardServer; namespace Internal { @@ -49,8 +50,10 @@ public: QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); QSharedPointer<SshRemoteProcess> createRemoteShell(); QSharedPointer<SftpChannel> createSftpChannel(); - QSharedPointer<SshDirectTcpIpTunnel> createTunnel(const QString &originatingHost, + QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost, quint16 originatingPort, const QString &remoteHost, quint16 remotePort); + QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost, + quint16 remotePort); int channelCount() const; enum CloseAllMode { CloseAllRegular, CloseAllAndReset }; @@ -67,6 +70,8 @@ public: void handleChannelExtendedData(const SshIncomingPacket &packet); void handleChannelEof(const SshIncomingPacket &packet); void handleChannelClose(const SshIncomingPacket &packet); + void handleRequestSuccess(const SshIncomingPacket &packet); + void handleRequestFailure(const SshIncomingPacket &packet); signals: void timeout(); @@ -86,6 +91,8 @@ private: QHash<quint32, AbstractSshChannel *> m_channels; QHash<AbstractSshChannel *, QSharedPointer<QObject> > m_sessions; quint32 m_nextLocalChannelId; + QList<QSharedPointer<SshTcpIpForwardServer>> m_waitingForwardServers; + QList<QSharedPointer<SshTcpIpForwardServer>> m_listeningForwardServers; }; } // namespace Internal diff --git a/src/libs/ssh/sshconnection.cpp b/src/libs/ssh/sshconnection.cpp index 883f815ed8..557fb5e0c8 100644 --- a/src/libs/ssh/sshconnection.cpp +++ b/src/libs/ssh/sshconnection.cpp @@ -31,6 +31,7 @@ #include "sshchannelmanager_p.h" #include "sshcryptofacility_p.h" #include "sshdirecttcpiptunnel.h" +#include "sshtcpipforwardserver.h" #include "sshexception_p.h" #include "sshinit_p.h" #include "sshkeyexchange_p.h" @@ -180,11 +181,18 @@ QSharedPointer<SftpChannel> SshConnection::createSftpChannel() return d->createSftpChannel(); } -SshDirectTcpIpTunnel::Ptr SshConnection::createTunnel(const QString &originatingHost, +SshDirectTcpIpTunnel::Ptr SshConnection::createDirectTunnel(const QString &originatingHost, quint16 originatingPort, const QString &remoteHost, quint16 remotePort) { QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshDirectTcpIpTunnel::Ptr()); - return d->createTunnel(originatingHost, originatingPort, remoteHost, remotePort); + return d->createDirectTunnel(originatingHost, originatingPort, remoteHost, remotePort); +} + +QSharedPointer<SshTcpIpForwardServer> SshConnection::createForwardServer(const QString &remoteHost, + quint16 remotePort) +{ + QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshTcpIpForwardServer::Ptr()); + return d->createForwardServer(remoteHost, remotePort); } int SshConnection::closeAllChannels() @@ -296,6 +304,11 @@ void SshConnectionPrivate::setupPacketHandlers() setupPacketHandler(SSH_MSG_UNIMPLEMENTED, StateList() << ConnectionEstablished, &This::handleUnimplementedPacket); + + setupPacketHandler(SSH_MSG_REQUEST_SUCCESS, connectedList, + &This::handleRequestSuccess); + setupPacketHandler(SSH_MSG_REQUEST_FAILURE, connectedList, + &This::handleRequestFailure); } void SshConnectionPrivate::setupPacketHandler(SshPacketType type, @@ -680,7 +693,15 @@ void SshConnectionPrivate::handleDisconnect() "", tr("Server closed connection: %1").arg(msg.description)); } +void SshConnectionPrivate::handleRequestSuccess() +{ + m_channelManager->handleRequestSuccess(m_incomingPacket); +} +void SshConnectionPrivate::handleRequestFailure() +{ + m_channelManager->handleRequestFailure(m_incomingPacket); +} void SshConnectionPrivate::sendData(const QByteArray &data) { @@ -820,10 +841,17 @@ QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel() return m_channelManager->createSftpChannel(); } -SshDirectTcpIpTunnel::Ptr SshConnectionPrivate::createTunnel(const QString &originatingHost, +SshDirectTcpIpTunnel::Ptr SshConnectionPrivate::createDirectTunnel(const QString &originatingHost, quint16 originatingPort, const QString &remoteHost, quint16 remotePort) { - return m_channelManager->createTunnel(originatingHost, originatingPort, remoteHost, remotePort); + return m_channelManager->createDirectTunnel(originatingHost, originatingPort, remoteHost, + remotePort); +} + +SshTcpIpForwardServer::Ptr SshConnectionPrivate::createForwardServer(const QString &bindAddress, + quint16 bindPort) +{ + return m_channelManager->createForwardServer(bindAddress, bindPort); } const quint64 SshConnectionPrivate::InvalidSeqNr = static_cast<quint64>(-1); diff --git a/src/libs/ssh/sshconnection.h b/src/libs/ssh/sshconnection.h index 136c91c199..071c97c0c7 100644 --- a/src/libs/ssh/sshconnection.h +++ b/src/libs/ssh/sshconnection.h @@ -41,6 +41,7 @@ namespace QSsh { class SftpChannel; class SshDirectTcpIpTunnel; class SshRemoteProcess; +class SshTcpIpForwardServer; namespace Internal { class SshConnectionPrivate; } @@ -121,8 +122,10 @@ public: QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); QSharedPointer<SshRemoteProcess> createRemoteShell(); QSharedPointer<SftpChannel> createSftpChannel(); - QSharedPointer<SshDirectTcpIpTunnel> createTunnel(const QString &originatingHost, + QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost, quint16 originatingPort, const QString &remoteHost, quint16 remotePort); + QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost, + quint16 remotePort); // -1 if an error occurred, number of channels closed otherwise. int closeAllChannels(); diff --git a/src/libs/ssh/sshconnection_p.h b/src/libs/ssh/sshconnection_p.h index 06eb959294..ede1ceaa81 100644 --- a/src/libs/ssh/sshconnection_p.h +++ b/src/libs/ssh/sshconnection_p.h @@ -45,6 +45,7 @@ namespace QSsh { class SftpChannel; class SshRemoteProcess; class SshDirectTcpIpTunnel; +class SshTcpIpForwardServer; namespace Internal { class SshChannelManager; @@ -83,8 +84,10 @@ public: QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); QSharedPointer<SshRemoteProcess> createRemoteShell(); QSharedPointer<SftpChannel> createSftpChannel(); - QSharedPointer<SshDirectTcpIpTunnel> createTunnel(const QString &originatingHost, + QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost, quint16 originatingPort, const QString &remoteHost, quint16 remotePort); + QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost, + quint16 remotePort); SshStateInternal state() const { return m_state; } SshError error() const { return m_error; } @@ -132,6 +135,9 @@ private: void handleChannelEof(); void handleChannelClose(); void handleDisconnect(); + void handleRequestSuccess(); + void handleRequestFailure(); + bool canUseSocket() const; void createPrivateKey(); diff --git a/src/libs/ssh/sshdirecttcpiptunnel.cpp b/src/libs/ssh/sshdirecttcpiptunnel.cpp index 405f174bdb..e3b21531a2 100644 --- a/src/libs/ssh/sshdirecttcpiptunnel.cpp +++ b/src/libs/ssh/sshdirecttcpiptunnel.cpp @@ -38,25 +38,12 @@ namespace Internal { SshDirectTcpIpTunnelPrivate::SshDirectTcpIpTunnelPrivate(quint32 channelId, const QString &originatingHost, quint16 originatingPort, const QString &remoteHost, quint16 remotePort, SshSendFacility &sendFacility) - : AbstractSshChannel(channelId, sendFacility), + : SshTcpIpTunnelPrivate(channelId, sendFacility), m_originatingHost(originatingHost), m_originatingPort(originatingPort), m_remoteHost(remoteHost), m_remotePort(remotePort) { - connect(this, SIGNAL(eof()), SLOT(handleEof())); -} - -void SshDirectTcpIpTunnelPrivate::handleChannelSuccess() -{ - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_SUCCESS message."); -} - -void SshDirectTcpIpTunnelPrivate::handleChannelFailure() -{ - throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, - "Unexpected SSH_MSG_CHANNEL_FAILURE message."); } void SshDirectTcpIpTunnelPrivate::handleOpenSuccessInternal() @@ -64,50 +51,6 @@ void SshDirectTcpIpTunnelPrivate::handleOpenSuccessInternal() emit initialized(); } -void SshDirectTcpIpTunnelPrivate::handleOpenFailureInternal(const QString &reason) -{ - emit error(reason); - closeChannel(); -} - -void SshDirectTcpIpTunnelPrivate::handleChannelDataInternal(const QByteArray &data) -{ - m_data += data; - emit readyRead(); -} - -void SshDirectTcpIpTunnelPrivate::handleChannelExtendedDataInternal(quint32 type, - const QByteArray &data) -{ - qCWarning(sshLog, "%s: Unexpected extended channel data. Type is %u, content is '%s'.", - Q_FUNC_INFO, type, data.constData()); -} - -void SshDirectTcpIpTunnelPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus) -{ - qCWarning(sshLog, "%s: Unexpected exit status %d.", Q_FUNC_INFO, exitStatus.exitStatus); -} - -void SshDirectTcpIpTunnelPrivate::handleExitSignal(const SshChannelExitSignal &signal) -{ - qCWarning(sshLog, "%s: Unexpected exit signal %s.", Q_FUNC_INFO, signal.signal.constData()); -} - -void SshDirectTcpIpTunnelPrivate::closeHook() -{ - emit closed(); -} - -void SshDirectTcpIpTunnelPrivate::handleEof() -{ - /* - * For some reason, the OpenSSH server only sends EOF when the remote port goes away, - * but does not close the channel, even though it becomes useless in that case. - * So we close it ourselves. - */ - closeChannel(); -} - } // namespace Internal using namespace Internal; @@ -118,15 +61,13 @@ SshDirectTcpIpTunnel::SshDirectTcpIpTunnel(quint32 channelId, const QString &ori : d(new SshDirectTcpIpTunnelPrivate(channelId, originatingHost, originatingPort, remoteHost, remotePort, sendFacility)) { - connect(d, SIGNAL(initialized()), SIGNAL(initialized()), Qt::QueuedConnection); - connect(d, SIGNAL(readyRead()), SIGNAL(readyRead()), Qt::QueuedConnection); - connect(d, SIGNAL(closed()), SIGNAL(tunnelClosed()), Qt::QueuedConnection); - connect(d, SIGNAL(error(QString)), SLOT(handleError(QString)), Qt::QueuedConnection); + d->init(this); + connect(d, &SshDirectTcpIpTunnelPrivate::initialized, + this, &SshDirectTcpIpTunnel::initialized, Qt::QueuedConnection); } SshDirectTcpIpTunnel::~SshDirectTcpIpTunnel() { - d->closeChannel(); delete d; } @@ -170,24 +111,12 @@ void SshDirectTcpIpTunnel::initialize() qint64 SshDirectTcpIpTunnel::readData(char *data, qint64 maxlen) { - const qint64 bytesRead = qMin(qint64(d->m_data.count()), maxlen); - memcpy(data, d->m_data.constData(), bytesRead); - d->m_data.remove(0, bytesRead); - return bytesRead; + return d->readData(data, maxlen); } qint64 SshDirectTcpIpTunnel::writeData(const char *data, qint64 len) { - QSSH_ASSERT_AND_RETURN_VALUE(d->channelState() == AbstractSshChannel::SessionEstablished, 0); - - d->sendData(QByteArray(data, len)); - return len; -} - -void SshDirectTcpIpTunnel::handleError(const QString &reason) -{ - setErrorString(reason); - emit error(reason); + return d->writeData(data, len); } } // namespace QSsh diff --git a/src/libs/ssh/sshdirecttcpiptunnel.h b/src/libs/ssh/sshdirecttcpiptunnel.h index e242629d6c..5d23f1cfdc 100644 --- a/src/libs/ssh/sshdirecttcpiptunnel.h +++ b/src/libs/ssh/sshdirecttcpiptunnel.h @@ -36,6 +36,7 @@ namespace Internal { class SshChannelManager; class SshDirectTcpIpTunnelPrivate; class SshSendFacility; +class SshTcpIpTunnelPrivate; } // namespace Internal class QSSH_EXPORT SshDirectTcpIpTunnel : public QIODevice @@ -43,6 +44,7 @@ class QSSH_EXPORT SshDirectTcpIpTunnel : public QIODevice Q_OBJECT friend class Internal::SshChannelManager; + friend class Internal::SshTcpIpTunnelPrivate; public: typedef QSharedPointer<SshDirectTcpIpTunnel> Ptr; @@ -61,7 +63,6 @@ public: signals: void initialized(); void error(const QString &reason); - void tunnelClosed(); private: SshDirectTcpIpTunnel(quint32 channelId, const QString &originatingHost, @@ -72,8 +73,6 @@ private: qint64 readData(char *data, qint64 maxlen); qint64 writeData(const char *data, qint64 len); - Q_SLOT void handleError(const QString &reason); - Internal::SshDirectTcpIpTunnelPrivate * const d; }; diff --git a/src/libs/ssh/sshdirecttcpiptunnel_p.h b/src/libs/ssh/sshdirecttcpiptunnel_p.h index 3d665c0711..a2a4ac1cc5 100644 --- a/src/libs/ssh/sshdirecttcpiptunnel_p.h +++ b/src/libs/ssh/sshdirecttcpiptunnel_p.h @@ -25,14 +25,14 @@ #pragma once -#include "sshchannel_p.h" +#include "sshtcpiptunnel_p.h" namespace QSsh { class SshDirectTcpIpTunnel; namespace Internal { -class SshDirectTcpIpTunnelPrivate : public AbstractSshChannel +class SshDirectTcpIpTunnelPrivate : public SshTcpIpTunnelPrivate { Q_OBJECT @@ -45,31 +45,14 @@ public: signals: void initialized(); - void readyRead(); - void error(const QString &reason); - void closed(); - -private slots: - void handleEof(); private: - void handleChannelSuccess(); - void handleChannelFailure(); - void handleOpenSuccessInternal(); - void handleOpenFailureInternal(const QString &reason); - void handleChannelDataInternal(const QByteArray &data); - void handleChannelExtendedDataInternal(quint32 type, const QByteArray &data); - void handleExitStatus(const SshChannelExitStatus &exitStatus); - void handleExitSignal(const SshChannelExitSignal &signal); - - void closeHook(); const QString m_originatingHost; const quint16 m_originatingPort; const QString m_remoteHost; const quint16 m_remotePort; - QByteArray m_data; }; } // namespace Internal diff --git a/src/libs/ssh/sshforwardedtcpiptunnel.cpp b/src/libs/ssh/sshforwardedtcpiptunnel.cpp new file mode 100644 index 0000000000..0175dd0313 --- /dev/null +++ b/src/libs/ssh/sshforwardedtcpiptunnel.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "sshforwardedtcpiptunnel.h" +#include "sshforwardedtcpiptunnel_p.h" +#include "sshlogging_p.h" +#include "sshsendfacility_p.h" + +namespace QSsh { + +namespace Internal { +SshForwardedTcpIpTunnelPrivate::SshForwardedTcpIpTunnelPrivate(quint32 channelId, + SshSendFacility &sendFacility) : + SshTcpIpTunnelPrivate(channelId, sendFacility) +{ + setChannelState(SessionRequested); +} + +void SshForwardedTcpIpTunnelPrivate::handleOpenSuccessInternal() +{ + QSSH_ASSERT_AND_RETURN(channelState() == AbstractSshChannel::SessionEstablished); + + try { + m_sendFacility.sendChannelOpenConfirmationPacket(remoteChannel(), localChannelId(), + initialWindowSize(), maxPacketSize()); + } catch (const Botan::Exception &e) { // Won't happen, but let's play it safe. + qCWarning(sshLog, "Botan error: %s", e.what()); + closeChannel(); + } +} + +} // namespace Internal + +using namespace Internal; + +SshForwardedTcpIpTunnel::SshForwardedTcpIpTunnel(quint32 channelId, SshSendFacility &sendFacility) : + d(new SshForwardedTcpIpTunnelPrivate(channelId, sendFacility)) +{ + d->init(this); +} + +SshForwardedTcpIpTunnel::~SshForwardedTcpIpTunnel() +{ + delete d; +} + +bool SshForwardedTcpIpTunnel::atEnd() const +{ + return QIODevice::atEnd() && d->m_data.isEmpty(); +} + +qint64 SshForwardedTcpIpTunnel::bytesAvailable() const +{ + return QIODevice::bytesAvailable() + d->m_data.count(); +} + +bool SshForwardedTcpIpTunnel::canReadLine() const +{ + return QIODevice::canReadLine() || d->m_data.contains('\n'); +} + +void SshForwardedTcpIpTunnel::close() +{ + d->closeChannel(); + QIODevice::close(); +} + +qint64 SshForwardedTcpIpTunnel::readData(char *data, qint64 maxlen) +{ + return d->readData(data, maxlen); +} + +qint64 SshForwardedTcpIpTunnel::writeData(const char *data, qint64 len) +{ + return d->writeData(data, len); +} + +} // namespace QSsh diff --git a/src/libs/ssh/sshforwardedtcpiptunnel.h b/src/libs/ssh/sshforwardedtcpiptunnel.h new file mode 100644 index 0000000000..0675ba4787 --- /dev/null +++ b/src/libs/ssh/sshforwardedtcpiptunnel.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include "ssh_global.h" +#include <QIODevice> +#include <QSharedPointer> + +namespace QSsh { + +namespace Internal { +class SshChannelManager; +class SshForwardedTcpIpTunnelPrivate; +class SshSendFacility; +class SshTcpIpTunnelPrivate; +} // namespace Internal + +class QSSH_EXPORT SshForwardedTcpIpTunnel : public QIODevice +{ + Q_OBJECT + friend class Internal::SshChannelManager; + friend class Internal::SshTcpIpTunnelPrivate; + +public: + typedef QSharedPointer<SshForwardedTcpIpTunnel> Ptr; + ~SshForwardedTcpIpTunnel(); + + // QIODevice stuff + bool atEnd() const; + qint64 bytesAvailable() const; + bool canReadLine() const; + void close(); + bool isSequential() const { return true; } + +signals: + void error(const QString &reason); + +private: + SshForwardedTcpIpTunnel(quint32 channelId, Internal::SshSendFacility &sendFacility); + + // QIODevice stuff + qint64 readData(char *data, qint64 maxlen) override; + qint64 writeData(const char *data, qint64 len) override; + + Internal::SshForwardedTcpIpTunnelPrivate * const d; +}; + +} // namespace QSsh diff --git a/src/libs/ssh/sshforwardedtcpiptunnel_p.h b/src/libs/ssh/sshforwardedtcpiptunnel_p.h new file mode 100644 index 0000000000..7b5043dac8 --- /dev/null +++ b/src/libs/ssh/sshforwardedtcpiptunnel_p.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "sshforwardedtcpiptunnel.h" +#include "sshtcpiptunnel_p.h" + +namespace QSsh { +namespace Internal { + +class SshForwardedTcpIpTunnelPrivate : public SshTcpIpTunnelPrivate +{ + Q_OBJECT + friend class QSsh::SshForwardedTcpIpTunnel; +public: + SshForwardedTcpIpTunnelPrivate(quint32 channelId, SshSendFacility &sendFacility); + void handleOpenSuccessInternal() override; +}; + +} // namespace Internal +} // namespace QSsh diff --git a/src/libs/ssh/sshincomingpacket.cpp b/src/libs/ssh/sshincomingpacket.cpp index eedf294e50..5bb8391434 100644 --- a/src/libs/ssh/sshincomingpacket.cpp +++ b/src/libs/ssh/sshincomingpacket.cpp @@ -35,6 +35,7 @@ namespace Internal { const QByteArray SshIncomingPacket::ExitStatusType("exit-status"); const QByteArray SshIncomingPacket::ExitSignalType("exit-signal"); +const QByteArray SshIncomingPacket::ForwardedTcpIpType("forwarded-tcpip"); SshIncomingPacket::SshIncomingPacket() : m_serverSeqNr(0) { } @@ -313,6 +314,22 @@ SshDebug SshIncomingPacket::extractDebug() const } } +SshRequestSuccess SshIncomingPacket::extractRequestSuccess() const +{ + Q_ASSERT(isComplete()); + Q_ASSERT(type() == SSH_MSG_REQUEST_SUCCESS); + + try { + SshRequestSuccess msg; + quint32 offset = TypeOffset + 1; + msg.bindPort = SshPacketParser::asUint32(m_data, &offset); + return msg; + } catch (const SshPacketParseException &) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Invalid SSH_MSG_REQUEST_SUCCESS."); + } +} + SshUnimplemented SshIncomingPacket::extractUnimplemented() const { Q_ASSERT(isComplete()); @@ -329,6 +346,31 @@ SshUnimplemented SshIncomingPacket::extractUnimplemented() const } } +SshChannelOpen SshIncomingPacket::extractChannelOpen() const +{ + Q_ASSERT(isComplete()); + Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN); + + SshChannelOpen open; + try { + quint32 offset = TypeOffset + 1; + QByteArray type = SshPacketParser::asString(m_data, &offset); + open.remoteChannel = SshPacketParser::asUint32(m_data, &offset); + open.remoteWindowSize = SshPacketParser::asUint32(m_data, &offset); + open.remoteMaxPacketSize = SshPacketParser::asUint32(m_data, &offset); + if (type == ForwardedTcpIpType) { + open.remoteAddress = SshPacketParser::asString(m_data, &offset); + open.remotePort = SshPacketParser::asUint32(m_data, &offset); + } else { + open.remotePort = 0; + } + } catch (const SshPacketParseException &) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Server sent invalid SSH_MSG_CHANNEL_OPEN packet."); + } + return open; +} + SshChannelOpenFailure SshIncomingPacket::extractChannelOpenFailure() const { Q_ASSERT(isComplete()); diff --git a/src/libs/ssh/sshincomingpacket_p.h b/src/libs/ssh/sshincomingpacket_p.h index 5729108fea..bd7aea4d6e 100644 --- a/src/libs/ssh/sshincomingpacket_p.h +++ b/src/libs/ssh/sshincomingpacket_p.h @@ -97,6 +97,20 @@ struct SshUnimplemented quint32 invalidMsgSeqNr; }; +struct SshRequestSuccess +{ + quint32 bindPort; +}; + +struct SshChannelOpen +{ + quint32 remoteChannel; + quint32 remoteWindowSize; + quint32 remoteMaxPacketSize; + QByteArray remoteAddress; + quint32 remotePort; +}; + struct SshChannelOpenFailure { quint32 localChannel; @@ -147,7 +161,6 @@ struct SshChannelExitSignal QByteArray language; }; - class SshIncomingPacket : public AbstractSshPacket { public: @@ -164,8 +177,10 @@ public: SshUserAuthBanner extractUserAuthBanner() const; SshUserAuthInfoRequestPacket extractUserAuthInfoRequest() const; SshDebug extractDebug() const; + SshRequestSuccess extractRequestSuccess() const; SshUnimplemented extractUnimplemented() const; + SshChannelOpen extractChannelOpen() const; SshChannelOpenFailure extractChannelOpenFailure() const; SshChannelOpenConfirmation extractChannelOpenConfirmation() const; SshChannelWindowAdjust extractWindowAdjust() const; @@ -180,6 +195,7 @@ public: static const QByteArray ExitStatusType; static const QByteArray ExitSignalType; + static const QByteArray ForwardedTcpIpType; private: virtual quint32 cipherBlockSize() const; diff --git a/src/libs/ssh/sshoutgoingpacket.cpp b/src/libs/ssh/sshoutgoingpacket.cpp index ccd558ebb0..c74a8950f1 100644 --- a/src/libs/ssh/sshoutgoingpacket.cpp +++ b/src/libs/ssh/sshoutgoingpacket.cpp @@ -179,6 +179,19 @@ void SshOutgoingPacket::generateDirectTcpIpPacket(quint32 channelId, quint32 win .appendInt(remotePort).appendString(localIpAddress).appendInt(localPort).finalize(); } +void SshOutgoingPacket::generateTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort) +{ + init(SSH_MSG_GLOBAL_REQUEST).appendString("tcpip-forward").appendBool(true) + .appendString(bindAddress).appendInt(bindPort).finalize(); +} + +void SshOutgoingPacket::generateCancelTcpIpForwardPacket(const QByteArray &bindAddress, + quint32 bindPort) +{ + init(SSH_MSG_GLOBAL_REQUEST).appendString("cancel-tcpip-forward").appendBool(true) + .appendString(bindAddress).appendInt(bindPort).finalize(); +} + void SshOutgoingPacket::generateEnvPacket(quint32 remoteChannel, const QByteArray &var, const QByteArray &value) { @@ -255,6 +268,22 @@ void SshOutgoingPacket::generateChannelClosePacket(quint32 remoteChannel) init(SSH_MSG_CHANNEL_CLOSE).appendInt(remoteChannel).finalize(); } +void SshOutgoingPacket::generateChannelOpenConfirmationPacket(quint32 remoteChannel, + quint32 localChannel, + quint32 localWindowSize, + quint32 maxPacketSize) +{ + init(SSH_MSG_CHANNEL_OPEN_CONFIRMATION).appendInt(remoteChannel).appendInt(localChannel) + .appendInt(localWindowSize).appendInt(maxPacketSize).finalize(); +} + +void SshOutgoingPacket::generateChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason, + const QByteArray &reasonString) +{ + init(SSH_MSG_CHANNEL_OPEN_FAILURE).appendInt(remoteChannel).appendInt(reason) + .appendString(reasonString).appendString(QByteArray()).finalize(); +} + void SshOutgoingPacket::generateDisconnectPacket(SshErrorCode reason, const QByteArray &reasonString) { diff --git a/src/libs/ssh/sshoutgoingpacket_p.h b/src/libs/ssh/sshoutgoingpacket_p.h index e68b213260..d2d4f63f16 100644 --- a/src/libs/ssh/sshoutgoingpacket_p.h +++ b/src/libs/ssh/sshoutgoingpacket_p.h @@ -65,6 +65,8 @@ public: void generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort, const QByteArray &localIpAddress, quint32 localPort); + void generateTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort); + void generateCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort); void generateEnvPacket(quint32 remoteChannel, const QByteArray &var, const QByteArray &value); void generatePtyRequestPacket(quint32 remoteChannel, @@ -79,6 +81,10 @@ public: const QByteArray &signalName); void generateChannelEofPacket(quint32 remoteChannel); void generateChannelClosePacket(quint32 remoteChannel); + void generateChannelOpenConfirmationPacket(quint32 remoteChannel, quint32 localChannel, + quint32 localWindowSize, quint32 maxPackeSize); + void generateChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason, + const QByteArray &reasonString); private: virtual quint32 cipherBlockSize() const; diff --git a/src/libs/ssh/sshpacket_p.h b/src/libs/ssh/sshpacket_p.h index 64fb65f7ab..4a94a432d1 100644 --- a/src/libs/ssh/sshpacket_p.h +++ b/src/libs/ssh/sshpacket_p.h @@ -84,6 +84,13 @@ enum SshPacketType { SSH_MSG_INVALID = 128 }; +enum SshOpenFailureType { + SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1, + SSH_OPEN_CONNECT_FAILED = 2, + SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3, + SSH_OPEN_RESOURCE_SHORTAGE = 4 +}; + enum SshExtendedDataType { SSH_EXTENDED_DATA_STDERR = 1 }; class SshAbstractCryptoFacility; diff --git a/src/libs/ssh/sshsendfacility.cpp b/src/libs/ssh/sshsendfacility.cpp index 720c2b2941..491c6981b9 100644 --- a/src/libs/ssh/sshsendfacility.cpp +++ b/src/libs/ssh/sshsendfacility.cpp @@ -172,6 +172,18 @@ void SshSendFacility::sendDirectTcpIpPacket(quint32 channelId, quint32 windowSiz sendPacket(); } +void SshSendFacility::sendTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort) +{ + m_outgoingPacket.generateTcpIpForwardPacket(bindAddress, bindPort); + sendPacket(); +} + +void SshSendFacility::sendCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort) +{ + m_outgoingPacket.generateCancelTcpIpForwardPacket(bindAddress, bindPort); + sendPacket(); +} + void SshSendFacility::sendPtyRequestPacket(quint32 remoteChannel, const SshPseudoTerminal &terminal) { @@ -238,5 +250,20 @@ void SshSendFacility::sendChannelClosePacket(quint32 remoteChannel) sendPacket(); } +void SshSendFacility::sendChannelOpenConfirmationPacket(quint32 remoteChannel, quint32 localChannel, + quint32 localWindowSize, quint32 maxPacketSize) +{ + m_outgoingPacket.generateChannelOpenConfirmationPacket(remoteChannel, localChannel, + localWindowSize, maxPacketSize); + sendPacket(); +} + +void SshSendFacility::sendChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason, + const QByteArray &reasonString) +{ + m_outgoingPacket.generateChannelOpenFailurePacket(remoteChannel, reason, reasonString); + sendPacket(); +} + } // namespace Internal } // namespace QSsh diff --git a/src/libs/ssh/sshsendfacility_p.h b/src/libs/ssh/sshsendfacility_p.h index ba67464476..0eb92ae0b1 100644 --- a/src/libs/ssh/sshsendfacility_p.h +++ b/src/libs/ssh/sshsendfacility_p.h @@ -72,6 +72,8 @@ public: void sendDirectTcpIpPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort, const QByteArray &localIpAddress, quint32 localPort); + void sendTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort); + void sendCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort); void sendPtyRequestPacket(quint32 remoteChannel, const SshPseudoTerminal &terminal); void sendEnvPacket(quint32 remoteChannel, const QByteArray &var, @@ -85,6 +87,10 @@ public: const QByteArray &signalName); void sendChannelEofPacket(quint32 remoteChannel); void sendChannelClosePacket(quint32 remoteChannel); + void sendChannelOpenConfirmationPacket(quint32 remoteChannel, quint32 localChannel, + quint32 localWindowSize, quint32 maxPackeSize); + void sendChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason, + const QByteArray &reasonString); quint32 nextClientSeqNr() const { return m_clientSeqNr; } private: diff --git a/src/libs/ssh/sshtcpipforwardserver.cpp b/src/libs/ssh/sshtcpipforwardserver.cpp new file mode 100644 index 0000000000..89cc2f680c --- /dev/null +++ b/src/libs/ssh/sshtcpipforwardserver.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "sshtcpipforwardserver.h" +#include "sshtcpipforwardserver_p.h" + +#include "sshlogging_p.h" +#include "sshsendfacility_p.h" + +namespace QSsh { +namespace Internal { + +SshTcpIpForwardServerPrivate::SshTcpIpForwardServerPrivate(const QString &bindAddress, + quint16 bindPort, SshSendFacility &sendFacility) + : m_sendFacility(sendFacility), + m_bindAddress(bindAddress), + m_bindPort(bindPort), + m_state(SshTcpIpForwardServer::Inactive) +{ +} + +} // namespace Internal + +using namespace Internal; + +SshTcpIpForwardServer::SshTcpIpForwardServer(const QString &bindAddress, quint16 bindPort, + SshSendFacility &sendFacility) + : d(new SshTcpIpForwardServerPrivate(bindAddress, bindPort, sendFacility)) +{ + connect(&d->m_timeoutTimer, &QTimer::timeout, this, &SshTcpIpForwardServer::setClosed); +} + +void SshTcpIpForwardServer::setListening(quint16 port) +{ + QSSH_ASSERT_AND_RETURN(d->m_state != Listening); + d->m_bindPort = port; + d->m_state = Listening; + emit stateChanged(Listening); +} + +void SshTcpIpForwardServer::setClosed() +{ + QSSH_ASSERT_AND_RETURN(d->m_state != Inactive); + d->m_state = Inactive; + emit stateChanged(Inactive); +} + +void SshTcpIpForwardServer::setNewConnection(const SshForwardedTcpIpTunnel::Ptr &connection) +{ + d->m_pendingConnections.append(connection); + emit newConnection(); +} + +SshTcpIpForwardServer::~SshTcpIpForwardServer() +{ + delete d; +} + +void SshTcpIpForwardServer::initialize() +{ + if (d->m_state == Inactive || d->m_state == Closing) { + try { + d->m_state = Initializing; + emit stateChanged(Initializing); + d->m_sendFacility.sendTcpIpForwardPacket(d->m_bindAddress.toUtf8(), d->m_bindPort); + d->m_timeoutTimer.start(d->ReplyTimeout); + } catch (const Botan::Exception &e) { + qCWarning(sshLog, "Botan error: %s", e.what()); + d->m_timeoutTimer.stop(); + setClosed(); + } + } +} + +void SshTcpIpForwardServer::close() +{ + d->m_timeoutTimer.stop(); + + if (d->m_state == Initializing || d->m_state == Listening) { + try { + d->m_state = Closing; + emit stateChanged(Closing); + d->m_sendFacility.sendCancelTcpIpForwardPacket(d->m_bindAddress.toUtf8(), + d->m_bindPort); + d->m_timeoutTimer.start(d->ReplyTimeout); + } catch (const Botan::Exception &e) { + qCWarning(sshLog, "Botan error: %s", e.what()); + d->m_timeoutTimer.stop(); + setClosed(); + } + } +} + +const QString &SshTcpIpForwardServer::bindAddress() const +{ + return d->m_bindAddress; +} + +quint16 SshTcpIpForwardServer::port() const +{ + return d->m_bindPort; +} + +SshTcpIpForwardServer::State SshTcpIpForwardServer::state() const +{ + return d->m_state; +} + +SshForwardedTcpIpTunnel::Ptr SshTcpIpForwardServer::nextPendingConnection() +{ + return d->m_pendingConnections.takeFirst(); +} + +} // namespace QSsh diff --git a/src/libs/ssh/sshtcpipforwardserver.h b/src/libs/ssh/sshtcpipforwardserver.h new file mode 100644 index 0000000000..2a0ed7e32b --- /dev/null +++ b/src/libs/ssh/sshtcpipforwardserver.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "ssh_global.h" +#include "sshforwardedtcpiptunnel.h" +#include <QObject> + +namespace QSsh { + +namespace Internal { +class SshChannelManager; +class SshTcpIpForwardServerPrivate; +class SshSendFacility; +class SshConnectionPrivate; +} // namespace Internal + +class QSSH_EXPORT SshTcpIpForwardServer : public QObject +{ + Q_OBJECT + friend class Internal::SshChannelManager; + friend class Internal::SshConnectionPrivate; + +public: + enum State { + Inactive, + Initializing, + Listening, + Closing + }; + + typedef QSharedPointer<SshTcpIpForwardServer> Ptr; + ~SshTcpIpForwardServer(); + + const QString &bindAddress() const; + quint16 port() const; + State state() const; + void initialize(); + void close(); + + SshForwardedTcpIpTunnel::Ptr nextPendingConnection(); + +signals: + void error(const QString &reason); + void newConnection(); + void stateChanged(State state); + +private: + SshTcpIpForwardServer(const QString &bindAddress, quint16 bindPort, + Internal::SshSendFacility &sendFacility); + void setListening(quint16 port); + void setClosed(); + void setNewConnection(const SshForwardedTcpIpTunnel::Ptr &connection); + + Internal::SshTcpIpForwardServerPrivate * const d; +}; + +} // namespace QSsh diff --git a/src/libs/ssh/sshtcpipforwardserver_p.h b/src/libs/ssh/sshtcpipforwardserver_p.h new file mode 100644 index 0000000000..9f4775bee7 --- /dev/null +++ b/src/libs/ssh/sshtcpipforwardserver_p.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "sshtcpipforwardserver.h" +#include <QList> +#include <QTimer> + +namespace QSsh { +namespace Internal { + +class SshTcpIpForwardServerPrivate +{ +public: + static const int ReplyTimeout = 10000; // milli seconds + + SshTcpIpForwardServerPrivate(const QString &bindAddress, quint16 bindPort, + SshSendFacility &sendFacility); + + SshSendFacility &m_sendFacility; + QTimer m_timeoutTimer; + + const QString m_bindAddress; + quint16 m_bindPort; + SshTcpIpForwardServer::State m_state; + + QList<SshForwardedTcpIpTunnel::Ptr> m_pendingConnections; +}; + +} // namespace Internal +} // namespace QSsh diff --git a/src/libs/ssh/sshtcpiptunnel.cpp b/src/libs/ssh/sshtcpiptunnel.cpp new file mode 100644 index 0000000000..ac7bf5208b --- /dev/null +++ b/src/libs/ssh/sshtcpiptunnel.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "sshsendfacility_p.h" +#include "sshtcpiptunnel_p.h" + +#include "sshincomingpacket_p.h" +#include "sshexception_p.h" +#include "sshlogging_p.h" + +namespace QSsh { + +namespace Internal { +SshTcpIpTunnelPrivate::SshTcpIpTunnelPrivate(quint32 channelId, SshSendFacility &sendFacility) + : AbstractSshChannel(channelId, sendFacility) +{ + connect(this, &AbstractSshChannel::eof, this, &SshTcpIpTunnelPrivate::handleEof); +} + +SshTcpIpTunnelPrivate::~SshTcpIpTunnelPrivate() +{ + closeChannel(); +} + + + +void SshTcpIpTunnelPrivate::handleChannelSuccess() +{ + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Unexpected SSH_MSG_CHANNEL_SUCCESS message."); +} + +void SshTcpIpTunnelPrivate::handleChannelFailure() +{ + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Unexpected SSH_MSG_CHANNEL_FAILURE message."); +} + +void SshTcpIpTunnelPrivate::handleOpenFailureInternal(const QString &reason) +{ + emit error(reason); + closeChannel(); +} + +void SshTcpIpTunnelPrivate::handleChannelDataInternal(const QByteArray &data) +{ + m_data += data; + emit readyRead(); +} + +void SshTcpIpTunnelPrivate::handleChannelExtendedDataInternal(quint32 type, + const QByteArray &data) +{ + qCWarning(sshLog, "%s: Unexpected extended channel data. Type is %u, content is '%s'.", + Q_FUNC_INFO, type, data.constData()); +} + +void SshTcpIpTunnelPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus) +{ + qCWarning(sshLog, "%s: Unexpected exit status %d.", Q_FUNC_INFO, exitStatus.exitStatus); +} + +void SshTcpIpTunnelPrivate::handleExitSignal(const SshChannelExitSignal &signal) +{ + qCWarning(sshLog, "%s: Unexpected exit signal %s.", Q_FUNC_INFO, signal.signal.constData()); +} + +void SshTcpIpTunnelPrivate::closeHook() +{ + emit closed(); +} + +void SshTcpIpTunnelPrivate::handleEof() +{ + /* + * For some reason, the OpenSSH server only sends EOF when the remote port goes away, + * but does not close the channel, even though it becomes useless in that case. + * So we close it ourselves. + */ + closeChannel(); +} + +qint64 SshTcpIpTunnelPrivate::readData(char *data, qint64 maxlen) +{ + const qint64 bytesRead = qMin(qint64(m_data.count()), maxlen); + memcpy(data, m_data.constData(), bytesRead); + m_data.remove(0, bytesRead); + return bytesRead; +} + +qint64 SshTcpIpTunnelPrivate::writeData(const char *data, qint64 len) +{ + QSSH_ASSERT_AND_RETURN_VALUE(channelState() == AbstractSshChannel::SessionEstablished, 0); + + sendData(QByteArray(data, len)); + return len; +} + +} // namespace Internal + +} // namespace QSsh diff --git a/src/libs/ssh/sshtcpiptunnel_p.h b/src/libs/ssh/sshtcpiptunnel_p.h new file mode 100644 index 0000000000..f75541f118 --- /dev/null +++ b/src/libs/ssh/sshtcpiptunnel_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "sshchannel_p.h" +#include <QIODevice> +#include <QByteArray> + +namespace QSsh { +namespace Internal { + +class SshTcpIpTunnelPrivate : public AbstractSshChannel +{ + Q_OBJECT + +public: + SshTcpIpTunnelPrivate(quint32 channelId, SshSendFacility &sendFacility); + ~SshTcpIpTunnelPrivate(); + + template<class SshTcpIpTunnel> + void init(SshTcpIpTunnel *q) + { + connect(this, &SshTcpIpTunnelPrivate::closed, + q, &SshTcpIpTunnel::close, Qt::QueuedConnection); + connect(this, &SshTcpIpTunnelPrivate::readyRead, + q, &SshTcpIpTunnel::readyRead, Qt::QueuedConnection); + connect(this, &SshTcpIpTunnelPrivate::error, q, [q](const QString &reason) { + q->setErrorString(reason); + emit q->error(reason); + }, Qt::QueuedConnection); + } + + void handleChannelSuccess() override; + void handleChannelFailure() override; + + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + +signals: + void readyRead(); + void error(const QString &reason); + void closed(); + +private slots: + void handleEof(); + +protected: + void handleOpenFailureInternal(const QString &reason) override; + void handleChannelDataInternal(const QByteArray &data) override; + void handleChannelExtendedDataInternal(quint32 type, const QByteArray &data) override; + void handleExitStatus(const SshChannelExitStatus &exitStatus) override; + void handleExitSignal(const SshChannelExitSignal &signal) override; + void closeHook() override; + + QByteArray m_data; +}; + +} // namespace Internal +} // namespace QSsh diff --git a/tests/manual/ssh/tunnel/tunnel.cpp b/tests/manual/ssh/tunnel/directtunnel.cpp index bbced6635f..5c23669d71 100644 --- a/tests/manual/ssh/tunnel/tunnel.cpp +++ b/tests/manual/ssh/tunnel/directtunnel.cpp @@ -23,7 +23,7 @@ ** ****************************************************************************/ -#include "tunnel.h" +#include "directtunnel.h" #include <ssh/sshconnection.h> #include <ssh/sshdirecttcpiptunnel.h> @@ -41,7 +41,7 @@ const QByteArray TestData("Urgsblubb?"); using namespace QSsh; -Tunnel::Tunnel(const SshConnectionParameters ¶meters, QObject *parent) +DirectTunnel::DirectTunnel(const SshConnectionParameters ¶meters, QObject *parent) : QObject(parent), m_connection(new SshConnection(parameters, this)), m_targetServer(new QTcpServer(this)), @@ -51,46 +51,46 @@ Tunnel::Tunnel(const SshConnectionParameters ¶meters, QObject *parent) connect(m_connection, SIGNAL(error(QSsh::SshError)), SLOT(handleConnectionError())); } -Tunnel::~Tunnel() +DirectTunnel::~DirectTunnel() { } -void Tunnel::run() +void DirectTunnel::run() { std::cout << "Connecting to SSH server..." << std::endl; m_connection->connectToHost(); } -void Tunnel::handleConnectionError() +void DirectTunnel::handleConnectionError() { std::cerr << "SSH connection error: " << qPrintable(m_connection->errorString()) << std::endl; - qApp->exit(EXIT_FAILURE); + emit finished(EXIT_FAILURE); } -void Tunnel::handleConnected() +void DirectTunnel::handleConnected() { std::cout << "Opening server side..." << std::endl; if (!m_targetServer->listen(QHostAddress::LocalHost)) { std::cerr << "Error opening port: " << m_targetServer->errorString().toLocal8Bit().constData() << std::endl; - qApp->exit(EXIT_FAILURE); + emit finished(EXIT_FAILURE); return; } m_targetPort = m_targetServer->serverPort(); connect(m_targetServer, SIGNAL(newConnection()), SLOT(handleNewConnection())); - m_tunnel = m_connection->createTunnel(QLatin1String("localhost"), 1024, // made-up values - QLatin1String("localhost"), m_targetPort); + m_tunnel = m_connection->createDirectTunnel(QLatin1String("localhost"), 1024, // made-up values + QLatin1String("localhost"), m_targetPort); connect(m_tunnel.data(), SIGNAL(initialized()), SLOT(handleInitialized())); connect(m_tunnel.data(), SIGNAL(error(QString)), SLOT(handleTunnelError(QString))); connect(m_tunnel.data(), SIGNAL(readyRead()), SLOT(handleServerData())); - connect(m_tunnel.data(), SIGNAL(tunnelClosed()), SLOT(handleTunnelClosed())); + connect(m_tunnel.data(), SIGNAL(aboutToClose()), SLOT(handleTunnelClosed())); std::cout << "Initializing tunnel..." << std::endl; m_tunnel->initialize(); } -void Tunnel::handleInitialized() +void DirectTunnel::handleInitialized() { std::cout << "Writing data into the tunnel..." << std::endl; m_tunnel->write(TestData); @@ -99,7 +99,7 @@ void Tunnel::handleInitialized() timeoutTimer->start(10000); } -void Tunnel::handleServerData() +void DirectTunnel::handleServerData() { m_dataReceivedFromServer += m_tunnel->readAll(); if (m_dataReceivedFromServer == ServerDataPrefix + TestData) { @@ -109,25 +109,25 @@ void Tunnel::handleServerData() } } -void Tunnel::handleTunnelError(const QString &reason) +void DirectTunnel::handleTunnelError(const QString &reason) { std::cerr << "Tunnel error: " << reason.toLocal8Bit().constData() << std::endl; - qApp->exit(EXIT_FAILURE); + emit finished(EXIT_FAILURE); } -void Tunnel::handleTunnelClosed() +void DirectTunnel::handleTunnelClosed() { if (m_expectingChannelClose) { std::cout << "Successfully detected channel close." << std::endl; std::cout << "Test finished successfully." << std::endl; - qApp->quit(); + emit finished(EXIT_SUCCESS); } else { std::cerr << "Error: Remote host closed channel." << std::endl; - qApp->exit(EXIT_FAILURE); + emit finished(EXIT_FAILURE); } } -void Tunnel::handleNewConnection() +void DirectTunnel::handleNewConnection() { m_targetSocket = m_targetServer->nextPendingConnection(); m_targetServer->close(); @@ -136,14 +136,14 @@ void Tunnel::handleNewConnection() handleClientData(); } -void Tunnel::handleSocketError() +void DirectTunnel::handleSocketError() { std::cerr << "Socket error: " << m_targetSocket->errorString().toLocal8Bit().constData() << std::endl; - qApp->exit(EXIT_FAILURE); + emit finished(EXIT_FAILURE); } -void Tunnel::handleClientData() +void DirectTunnel::handleClientData() { m_dataReceivedFromClient += m_targetSocket->readAll(); if (m_dataReceivedFromClient == TestData) { @@ -153,8 +153,8 @@ void Tunnel::handleClientData() } } -void Tunnel::handleTimeout() +void DirectTunnel::handleTimeout() { std::cerr << "Error: Timeout waiting for test completion." << std::endl; - qApp->exit(EXIT_FAILURE); + emit finished(EXIT_FAILURE); } diff --git a/tests/manual/ssh/tunnel/tunnel.h b/tests/manual/ssh/tunnel/directtunnel.h index c01b478c40..26c1898b1c 100644 --- a/tests/manual/ssh/tunnel/tunnel.h +++ b/tests/manual/ssh/tunnel/directtunnel.h @@ -39,15 +39,18 @@ class SshConnectionParameters; class SshDirectTcpIpTunnel; } -class Tunnel : public QObject +class DirectTunnel : public QObject { Q_OBJECT public: - Tunnel(const QSsh::SshConnectionParameters ¶meters, QObject *parent = 0); - ~Tunnel(); + DirectTunnel(const QSsh::SshConnectionParameters ¶meters, QObject *parent = 0); + ~DirectTunnel(); void run(); +signals: + void finished(int errorCode); + private slots: void handleConnected(); void handleConnectionError(); diff --git a/tests/manual/ssh/tunnel/forwardtunnel.cpp b/tests/manual/ssh/tunnel/forwardtunnel.cpp new file mode 100644 index 0000000000..e80dbad30b --- /dev/null +++ b/tests/manual/ssh/tunnel/forwardtunnel.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "forwardtunnel.h" + +#include <ssh/sshconnection.h> +#include <ssh/sshtcpipforwardserver.h> + +#include <QCoreApplication> +#include <QTcpServer> +#include <QTcpSocket> + +#include <iostream> + +const QByteArray ClientDataPrefix("Received the following data: "); +const QByteArray TestData("Urgsblubb?"); + +ForwardTunnel::ForwardTunnel(const QSsh::SshConnectionParameters ¶meters, QObject *parent) + : QObject(parent), + m_connection(new QSsh::SshConnection(parameters, this)), + m_targetSocket(0), + m_targetPort(0) +{ + connect(m_connection, &QSsh::SshConnection::connected, this, &ForwardTunnel::handleConnected); + connect(m_connection, &QSsh::SshConnection::error, this, &ForwardTunnel::handleConnectionError); +} + +void ForwardTunnel::run() +{ + std::cout << "Connecting to SSH server..." << std::endl; + m_connection->connectToHost(); +} + +void ForwardTunnel::handleConnected() +{ + { + QTcpServer server; + if (server.listen(QHostAddress::LocalHost)) { + m_targetPort = server.serverPort(); + } else { + std::cerr << "Error while searching for free port: " + << server.errorString().toLocal8Bit().constData() << std::endl; + emit finished(EXIT_FAILURE); + } + } + + std::cout << "Initializing tunnel..." << std::endl; + m_server = m_connection->createForwardServer(QLatin1String("localhost"), m_targetPort); + connect(m_server.data(), &QSsh::SshTcpIpForwardServer::newConnection, + this, &ForwardTunnel::handleNewConnection); + connect(m_server.data(), &QSsh::SshTcpIpForwardServer::stateChanged, + this, [this](QSsh::SshTcpIpForwardServer::State state) { + if (state == QSsh::SshTcpIpForwardServer::Listening) + handleInitialized(); + else if (state == QSsh::SshTcpIpForwardServer::Inactive) + handleServerClosed(); + }); + connect(m_server.data(), &QSsh::SshTcpIpForwardServer::error, + this, &ForwardTunnel::handleServerError); + + m_server->initialize(); +} + +void ForwardTunnel::handleConnectionError(QSsh::SshError error) +{ + std::cout << "SSH connection error: " << error << " " << qPrintable(m_connection->errorString()) + << std::endl; + emit finished(EXIT_FAILURE); +} + +void ForwardTunnel::handleInitialized() +{ + std::cout << "Forward tunnel initialized, connecting ..." << std::endl; + m_targetSocket = new QTcpSocket(this); + connect(m_targetSocket, &QTcpSocket::connected, this, [this](){ + m_targetSocket->write(ClientDataPrefix + TestData); + }); + + connect(m_targetSocket, &QTcpSocket::readyRead, this, [this](){ + m_dataReceivedFromServer += m_targetSocket->readAll(); + if (m_dataReceivedFromServer == ClientDataPrefix + TestData) { + std::cout << "Data exchange successful. Closing client socket..." << std::endl; + m_targetSocket->close(); + } + }); + m_targetSocket->connectToHost(QLatin1String("localhost"), m_targetPort); +} + +void ForwardTunnel::handleServerError(const QString &reason) +{ + std::cerr << "Tunnel error:" << reason.toUtf8().constData() << std::endl; + emit finished(EXIT_FAILURE); +} + +void ForwardTunnel::handleServerClosed() +{ + std::cout << "Forward tunnel closed" << std::endl; + emit finished(EXIT_SUCCESS); +} + +void ForwardTunnel::handleNewConnection() +{ + std::cout << "Connection established" << std::endl; + QSsh::SshForwardedTcpIpTunnel::Ptr tunnel = m_server->nextPendingConnection(); + + connect(tunnel.data(), &QIODevice::readyRead, this, [this, tunnel](){ + m_dataReceivedFromClient += tunnel->readAll(); + if (m_dataReceivedFromClient == ClientDataPrefix + TestData) { + std::cout << "Data received successful. Sending it back..." << std::endl; + tunnel->write(m_dataReceivedFromClient); + } + }); + + connect(tunnel.data(), &QIODevice::aboutToClose, this, [this](){ + std::cout << "Server Connection closed, closing tunnel" << std::endl; + m_server->close(); + }); +} + +void ForwardTunnel::handleSocketError() +{ + std::cerr << "Socket error" << std::endl; + emit finished(EXIT_FAILURE); +} diff --git a/tests/manual/ssh/tunnel/forwardtunnel.h b/tests/manual/ssh/tunnel/forwardtunnel.h new file mode 100644 index 0000000000..d5cc23ff62 --- /dev/null +++ b/tests/manual/ssh/tunnel/forwardtunnel.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "ssh/ssherrors.h" + +#include <QObject> +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE +class QTcpSocket; +QT_END_NAMESPACE + +namespace QSsh { +class SshConnection; +class SshConnectionParameters; +class SshTcpIpForwardServer; +} + +class ForwardTunnel : public QObject +{ + Q_OBJECT +public: + ForwardTunnel(const QSsh::SshConnectionParameters ¶meters, + QObject *parent = 0); + void run(); + +signals: + void finished(int exitCode); + +private slots: + void handleConnected(); + void handleConnectionError(QSsh::SshError error); + void handleInitialized(); + void handleServerError(const QString &reason); + void handleServerClosed(); + void handleNewConnection(); + void handleSocketError(); + +private: + QSsh::SshConnection * const m_connection; + QSharedPointer<QSsh::SshTcpIpForwardServer> m_server; + QTcpSocket *m_targetSocket; + quint16 m_targetPort; + + QByteArray m_dataReceivedFromServer; + QByteArray m_dataReceivedFromClient; +}; diff --git a/tests/manual/ssh/tunnel/main.cpp b/tests/manual/ssh/tunnel/main.cpp index 467c9035d1..28136f1c89 100644 --- a/tests/manual/ssh/tunnel/main.cpp +++ b/tests/manual/ssh/tunnel/main.cpp @@ -24,7 +24,8 @@ ****************************************************************************/ #include "../remoteprocess/argumentscollector.h" -#include "tunnel.h" +#include "directtunnel.h" +#include "forwardtunnel.h" #include <ssh/sshconnection.h> @@ -44,7 +45,15 @@ int main(int argc, char *argv[]) parameters.host = QLatin1String("127.0.0.1"); if (!parseSuccess) return EXIT_FAILURE; - Tunnel tunnel(parameters); - tunnel.run(); + + DirectTunnel direct(parameters); + + parameters.host = QLatin1String("127.0.0.2"); + ForwardTunnel forward(parameters); + forward.run(); + + QObject::connect(&forward, &ForwardTunnel::finished, &direct, &DirectTunnel::run); + QObject::connect(&direct, &DirectTunnel::finished, &app, &QCoreApplication::exit); + return app.exec(); } diff --git a/tests/manual/ssh/tunnel/tunnel.pro b/tests/manual/ssh/tunnel/tunnel.pro index f2f960b98a..3d36634f7c 100644 --- a/tests/manual/ssh/tunnel/tunnel.pro +++ b/tests/manual/ssh/tunnel/tunnel.pro @@ -1,5 +1,12 @@ include(../ssh.pri) -TARGET=tunnel -SOURCES=main.cpp tunnel.cpp argumentscollector.cpp -HEADERS=tunnel.h argumentscollector.h +TARGET =tunnel +SOURCES = \ + main.cpp \ + argumentscollector.cpp \ + directtunnel.cpp \ + forwardtunnel.cpp +HEADERS = \ + argumentscollector.h \ + directtunnel.h \ + forwardtunnel.h |