Ssh: Implement tcp/ip forward tunneling

Change-Id: I529b3392ea7e4cbae2c736c9f55352ef6b19da98
Reviewed-by: Christian Kandeler <christian.kandeler@theqtcompany.com>
This commit is contained in:
Ulf Hermann
2016-03-31 18:57:03 +02:00
committed by Ulf Hermann
parent e05fbf153d
commit 6b4963b549
32 changed files with 1276 additions and 147 deletions

View File

@@ -30,7 +30,10 @@ SOURCES = $$PWD/sshsendfacility.cpp \
$$PWD/sshinit.cpp \ $$PWD/sshinit.cpp \
$$PWD/sshdirecttcpiptunnel.cpp \ $$PWD/sshdirecttcpiptunnel.cpp \
$$PWD/sshlogging.cpp \ $$PWD/sshlogging.cpp \
$$PWD/sshhostkeydatabase.cpp $$PWD/sshhostkeydatabase.cpp \
$$PWD/sshtcpipforwardserver.cpp \
$$PWD/sshtcpiptunnel.cpp \
$$PWD/sshforwardedtcpiptunnel.cpp
HEADERS = $$PWD/sshsendfacility_p.h \ HEADERS = $$PWD/sshsendfacility_p.h \
$$PWD/sshremoteprocess.h \ $$PWD/sshremoteprocess.h \
@@ -68,7 +71,12 @@ HEADERS = $$PWD/sshsendfacility_p.h \
$$PWD/sshinit_p.h \ $$PWD/sshinit_p.h \
$$PWD/sshdirecttcpiptunnel.h \ $$PWD/sshdirecttcpiptunnel.h \
$$PWD/sshlogging_p.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 FORMS = $$PWD/sshkeycreationdialog.ui

View File

@@ -29,6 +29,7 @@ QtcLibrary {
"sshdirecttcpiptunnel.h", "sshdirecttcpiptunnel_p.h", "sshdirecttcpiptunnel.cpp", "sshdirecttcpiptunnel.h", "sshdirecttcpiptunnel_p.h", "sshdirecttcpiptunnel.cpp",
"ssherrors.h", "ssherrors.h",
"sshexception_p.h", "sshexception_p.h",
"sshforwardedtcpiptunnel.cpp", "sshforwardedtcpiptunnel.h", "sshforwardedtcpiptunnel_p.h",
"sshhostkeydatabase.cpp", "sshhostkeydatabase.cpp",
"sshhostkeydatabase.h", "sshhostkeydatabase.h",
"sshincomingpacket_p.h", "sshincomingpacket.cpp", "sshincomingpacket_p.h", "sshincomingpacket.cpp",
@@ -46,6 +47,8 @@ QtcLibrary {
"sshremoteprocess.cpp", "sshremoteprocess.h", "sshremoteprocess_p.h", "sshremoteprocess.cpp", "sshremoteprocess.h", "sshremoteprocess_p.h",
"sshremoteprocessrunner.cpp", "sshremoteprocessrunner.h", "sshremoteprocessrunner.cpp", "sshremoteprocessrunner.h",
"sshsendfacility.cpp", "sshsendfacility_p.h", "sshsendfacility.cpp", "sshsendfacility_p.h",
"sshtcpipforwardserver.cpp", "sshtcpipforwardserver.h", "sshtcpipforwardserver_p.h",
"sshtcpiptunnel.cpp", "sshtcpiptunnel_p.h",
].concat(botanFiles) ].concat(botanFiles)
property var useSystemBotan: Environment.getEnv("USE_SYSTEM_BOTAN") === "1" property var useSystemBotan: Environment.getEnv("USE_SYSTEM_BOTAN") === "1"

View File

@@ -172,7 +172,7 @@ void AbstractSshChannel::handleOpenFailure(const QString &reason)
return; // Late server reply; we requested a channel close in the meantime. return; // Late server reply; we requested a channel close in the meantime.
default: default:
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, 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(); m_timeoutTimer.stop();

View File

@@ -29,10 +29,15 @@
#include "sftpchannel_p.h" #include "sftpchannel_p.h"
#include "sshdirecttcpiptunnel.h" #include "sshdirecttcpiptunnel.h"
#include "sshdirecttcpiptunnel_p.h" #include "sshdirecttcpiptunnel_p.h"
#include "sshforwardedtcpiptunnel.h"
#include "sshforwardedtcpiptunnel_p.h"
#include "sshincomingpacket_p.h" #include "sshincomingpacket_p.h"
#include "sshlogging_p.h"
#include "sshremoteprocess.h" #include "sshremoteprocess.h"
#include "sshremoteprocess_p.h" #include "sshremoteprocess_p.h"
#include "sshsendfacility_p.h" #include "sshsendfacility_p.h"
#include "sshtcpipforwardserver.h"
#include "sshtcpipforwardserver_p.h"
#include <QList> #include <QList>
@@ -51,10 +56,54 @@ void SshChannelManager::handleChannelRequest(const SshIncomingPacket &packet)
->handleChannelRequest(packet); ->handleChannelRequest(packet);
} }
void SshChannelManager::handleChannelOpen(const SshIncomingPacket &) void SshChannelManager::handleChannelOpen(const SshIncomingPacket &packet)
{ {
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, SshChannelOpen channelOpen = packet.extractChannelOpen();
"Server tried to open channel on client.");
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) 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, SshChannelManager::ChannelIterator SshChannelManager::lookupChannelAsIterator(quint32 channelId,
bool allowNotFound) bool allowNotFound)
{ {
@@ -165,7 +247,7 @@ QSsh::SftpChannel::Ptr SshChannelManager::createSftpChannel()
return sftp; return sftp;
} }
SshDirectTcpIpTunnel::Ptr SshChannelManager::createTunnel(const QString &originatingHost, SshDirectTcpIpTunnel::Ptr SshChannelManager::createDirectTunnel(const QString &originatingHost,
quint16 originatingPort, const QString &remoteHost, quint16 remotePort) quint16 originatingPort, const QString &remoteHost, quint16 remotePort)
{ {
SshDirectTcpIpTunnel::Ptr tunnel(new SshDirectTcpIpTunnel(m_nextLocalChannelId++, SshDirectTcpIpTunnel::Ptr tunnel(new SshDirectTcpIpTunnel(m_nextLocalChannelId++,
@@ -174,6 +256,28 @@ SshDirectTcpIpTunnel::Ptr SshChannelManager::createTunnel(const QString &origina
return tunnel; 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, void SshChannelManager::insertChannel(AbstractSshChannel *priv,
const QSharedPointer<QObject> &pub) const QSharedPointer<QObject> &pub)
{ {

View File

@@ -33,6 +33,7 @@ namespace QSsh {
class SftpChannel; class SftpChannel;
class SshDirectTcpIpTunnel; class SshDirectTcpIpTunnel;
class SshRemoteProcess; class SshRemoteProcess;
class SshTcpIpForwardServer;
namespace Internal { namespace Internal {
@@ -49,8 +50,10 @@ public:
QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command);
QSharedPointer<SshRemoteProcess> createRemoteShell(); QSharedPointer<SshRemoteProcess> createRemoteShell();
QSharedPointer<SftpChannel> createSftpChannel(); QSharedPointer<SftpChannel> createSftpChannel();
QSharedPointer<SshDirectTcpIpTunnel> createTunnel(const QString &originatingHost, QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost,
quint16 originatingPort, const QString &remoteHost, quint16 remotePort); quint16 originatingPort, const QString &remoteHost, quint16 remotePort);
QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost,
quint16 remotePort);
int channelCount() const; int channelCount() const;
enum CloseAllMode { CloseAllRegular, CloseAllAndReset }; enum CloseAllMode { CloseAllRegular, CloseAllAndReset };
@@ -67,6 +70,8 @@ public:
void handleChannelExtendedData(const SshIncomingPacket &packet); void handleChannelExtendedData(const SshIncomingPacket &packet);
void handleChannelEof(const SshIncomingPacket &packet); void handleChannelEof(const SshIncomingPacket &packet);
void handleChannelClose(const SshIncomingPacket &packet); void handleChannelClose(const SshIncomingPacket &packet);
void handleRequestSuccess(const SshIncomingPacket &packet);
void handleRequestFailure(const SshIncomingPacket &packet);
signals: signals:
void timeout(); void timeout();
@@ -86,6 +91,8 @@ private:
QHash<quint32, AbstractSshChannel *> m_channels; QHash<quint32, AbstractSshChannel *> m_channels;
QHash<AbstractSshChannel *, QSharedPointer<QObject> > m_sessions; QHash<AbstractSshChannel *, QSharedPointer<QObject> > m_sessions;
quint32 m_nextLocalChannelId; quint32 m_nextLocalChannelId;
QList<QSharedPointer<SshTcpIpForwardServer>> m_waitingForwardServers;
QList<QSharedPointer<SshTcpIpForwardServer>> m_listeningForwardServers;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -31,6 +31,7 @@
#include "sshchannelmanager_p.h" #include "sshchannelmanager_p.h"
#include "sshcryptofacility_p.h" #include "sshcryptofacility_p.h"
#include "sshdirecttcpiptunnel.h" #include "sshdirecttcpiptunnel.h"
#include "sshtcpipforwardserver.h"
#include "sshexception_p.h" #include "sshexception_p.h"
#include "sshinit_p.h" #include "sshinit_p.h"
#include "sshkeyexchange_p.h" #include "sshkeyexchange_p.h"
@@ -180,11 +181,18 @@ QSharedPointer<SftpChannel> SshConnection::createSftpChannel()
return d->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) quint16 originatingPort, const QString &remoteHost, quint16 remotePort)
{ {
QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshDirectTcpIpTunnel::Ptr()); 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() int SshConnection::closeAllChannels()
@@ -296,6 +304,11 @@ void SshConnectionPrivate::setupPacketHandlers()
setupPacketHandler(SSH_MSG_UNIMPLEMENTED, setupPacketHandler(SSH_MSG_UNIMPLEMENTED,
StateList() << ConnectionEstablished, &This::handleUnimplementedPacket); 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, void SshConnectionPrivate::setupPacketHandler(SshPacketType type,
@@ -680,7 +693,15 @@ void SshConnectionPrivate::handleDisconnect()
"", tr("Server closed connection: %1").arg(msg.description)); "", 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) void SshConnectionPrivate::sendData(const QByteArray &data)
{ {
@@ -820,10 +841,17 @@ QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel()
return m_channelManager->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) 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); const quint64 SshConnectionPrivate::InvalidSeqNr = static_cast<quint64>(-1);

View File

@@ -41,6 +41,7 @@ namespace QSsh {
class SftpChannel; class SftpChannel;
class SshDirectTcpIpTunnel; class SshDirectTcpIpTunnel;
class SshRemoteProcess; class SshRemoteProcess;
class SshTcpIpForwardServer;
namespace Internal { class SshConnectionPrivate; } namespace Internal { class SshConnectionPrivate; }
@@ -121,8 +122,10 @@ public:
QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command);
QSharedPointer<SshRemoteProcess> createRemoteShell(); QSharedPointer<SshRemoteProcess> createRemoteShell();
QSharedPointer<SftpChannel> createSftpChannel(); QSharedPointer<SftpChannel> createSftpChannel();
QSharedPointer<SshDirectTcpIpTunnel> createTunnel(const QString &originatingHost, QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost,
quint16 originatingPort, const QString &remoteHost, quint16 remotePort); 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. // -1 if an error occurred, number of channels closed otherwise.
int closeAllChannels(); int closeAllChannels();

View File

@@ -45,6 +45,7 @@ namespace QSsh {
class SftpChannel; class SftpChannel;
class SshRemoteProcess; class SshRemoteProcess;
class SshDirectTcpIpTunnel; class SshDirectTcpIpTunnel;
class SshTcpIpForwardServer;
namespace Internal { namespace Internal {
class SshChannelManager; class SshChannelManager;
@@ -83,8 +84,10 @@ public:
QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command);
QSharedPointer<SshRemoteProcess> createRemoteShell(); QSharedPointer<SshRemoteProcess> createRemoteShell();
QSharedPointer<SftpChannel> createSftpChannel(); QSharedPointer<SftpChannel> createSftpChannel();
QSharedPointer<SshDirectTcpIpTunnel> createTunnel(const QString &originatingHost, QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost,
quint16 originatingPort, const QString &remoteHost, quint16 remotePort); quint16 originatingPort, const QString &remoteHost, quint16 remotePort);
QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost,
quint16 remotePort);
SshStateInternal state() const { return m_state; } SshStateInternal state() const { return m_state; }
SshError error() const { return m_error; } SshError error() const { return m_error; }
@@ -132,6 +135,9 @@ private:
void handleChannelEof(); void handleChannelEof();
void handleChannelClose(); void handleChannelClose();
void handleDisconnect(); void handleDisconnect();
void handleRequestSuccess();
void handleRequestFailure();
bool canUseSocket() const; bool canUseSocket() const;
void createPrivateKey(); void createPrivateKey();

View File

@@ -38,25 +38,12 @@ namespace Internal {
SshDirectTcpIpTunnelPrivate::SshDirectTcpIpTunnelPrivate(quint32 channelId, SshDirectTcpIpTunnelPrivate::SshDirectTcpIpTunnelPrivate(quint32 channelId,
const QString &originatingHost, quint16 originatingPort, const QString &remoteHost, const QString &originatingHost, quint16 originatingPort, const QString &remoteHost,
quint16 remotePort, SshSendFacility &sendFacility) quint16 remotePort, SshSendFacility &sendFacility)
: AbstractSshChannel(channelId, sendFacility), : SshTcpIpTunnelPrivate(channelId, sendFacility),
m_originatingHost(originatingHost), m_originatingHost(originatingHost),
m_originatingPort(originatingPort), m_originatingPort(originatingPort),
m_remoteHost(remoteHost), m_remoteHost(remoteHost),
m_remotePort(remotePort) 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() void SshDirectTcpIpTunnelPrivate::handleOpenSuccessInternal()
@@ -64,50 +51,6 @@ void SshDirectTcpIpTunnelPrivate::handleOpenSuccessInternal()
emit initialized(); 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 } // namespace Internal
using namespace Internal; using namespace Internal;
@@ -118,15 +61,13 @@ SshDirectTcpIpTunnel::SshDirectTcpIpTunnel(quint32 channelId, const QString &ori
: d(new SshDirectTcpIpTunnelPrivate(channelId, originatingHost, originatingPort, remoteHost, : d(new SshDirectTcpIpTunnelPrivate(channelId, originatingHost, originatingPort, remoteHost,
remotePort, sendFacility)) remotePort, sendFacility))
{ {
connect(d, SIGNAL(initialized()), SIGNAL(initialized()), Qt::QueuedConnection); d->init(this);
connect(d, SIGNAL(readyRead()), SIGNAL(readyRead()), Qt::QueuedConnection); connect(d, &SshDirectTcpIpTunnelPrivate::initialized,
connect(d, SIGNAL(closed()), SIGNAL(tunnelClosed()), Qt::QueuedConnection); this, &SshDirectTcpIpTunnel::initialized, Qt::QueuedConnection);
connect(d, SIGNAL(error(QString)), SLOT(handleError(QString)), Qt::QueuedConnection);
} }
SshDirectTcpIpTunnel::~SshDirectTcpIpTunnel() SshDirectTcpIpTunnel::~SshDirectTcpIpTunnel()
{ {
d->closeChannel();
delete d; delete d;
} }
@@ -170,24 +111,12 @@ void SshDirectTcpIpTunnel::initialize()
qint64 SshDirectTcpIpTunnel::readData(char *data, qint64 maxlen) qint64 SshDirectTcpIpTunnel::readData(char *data, qint64 maxlen)
{ {
const qint64 bytesRead = qMin(qint64(d->m_data.count()), maxlen); return d->readData(data, maxlen);
memcpy(data, d->m_data.constData(), bytesRead);
d->m_data.remove(0, bytesRead);
return bytesRead;
} }
qint64 SshDirectTcpIpTunnel::writeData(const char *data, qint64 len) qint64 SshDirectTcpIpTunnel::writeData(const char *data, qint64 len)
{ {
QSSH_ASSERT_AND_RETURN_VALUE(d->channelState() == AbstractSshChannel::SessionEstablished, 0); return d->writeData(data, len);
d->sendData(QByteArray(data, len));
return len;
}
void SshDirectTcpIpTunnel::handleError(const QString &reason)
{
setErrorString(reason);
emit error(reason);
} }
} // namespace QSsh } // namespace QSsh

View File

@@ -36,6 +36,7 @@ namespace Internal {
class SshChannelManager; class SshChannelManager;
class SshDirectTcpIpTunnelPrivate; class SshDirectTcpIpTunnelPrivate;
class SshSendFacility; class SshSendFacility;
class SshTcpIpTunnelPrivate;
} // namespace Internal } // namespace Internal
class QSSH_EXPORT SshDirectTcpIpTunnel : public QIODevice class QSSH_EXPORT SshDirectTcpIpTunnel : public QIODevice
@@ -43,6 +44,7 @@ class QSSH_EXPORT SshDirectTcpIpTunnel : public QIODevice
Q_OBJECT Q_OBJECT
friend class Internal::SshChannelManager; friend class Internal::SshChannelManager;
friend class Internal::SshTcpIpTunnelPrivate;
public: public:
typedef QSharedPointer<SshDirectTcpIpTunnel> Ptr; typedef QSharedPointer<SshDirectTcpIpTunnel> Ptr;
@@ -61,7 +63,6 @@ public:
signals: signals:
void initialized(); void initialized();
void error(const QString &reason); void error(const QString &reason);
void tunnelClosed();
private: private:
SshDirectTcpIpTunnel(quint32 channelId, const QString &originatingHost, SshDirectTcpIpTunnel(quint32 channelId, const QString &originatingHost,
@@ -72,8 +73,6 @@ private:
qint64 readData(char *data, qint64 maxlen); qint64 readData(char *data, qint64 maxlen);
qint64 writeData(const char *data, qint64 len); qint64 writeData(const char *data, qint64 len);
Q_SLOT void handleError(const QString &reason);
Internal::SshDirectTcpIpTunnelPrivate * const d; Internal::SshDirectTcpIpTunnelPrivate * const d;
}; };

View File

@@ -25,14 +25,14 @@
#pragma once #pragma once
#include "sshchannel_p.h" #include "sshtcpiptunnel_p.h"
namespace QSsh { namespace QSsh {
class SshDirectTcpIpTunnel; class SshDirectTcpIpTunnel;
namespace Internal { namespace Internal {
class SshDirectTcpIpTunnelPrivate : public AbstractSshChannel class SshDirectTcpIpTunnelPrivate : public SshTcpIpTunnelPrivate
{ {
Q_OBJECT Q_OBJECT
@@ -45,31 +45,14 @@ public:
signals: signals:
void initialized(); void initialized();
void readyRead();
void error(const QString &reason);
void closed();
private slots:
void handleEof();
private: private:
void handleChannelSuccess();
void handleChannelFailure();
void handleOpenSuccessInternal(); 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 QString m_originatingHost;
const quint16 m_originatingPort; const quint16 m_originatingPort;
const QString m_remoteHost; const QString m_remoteHost;
const quint16 m_remotePort; const quint16 m_remotePort;
QByteArray m_data;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -35,6 +35,7 @@ namespace Internal {
const QByteArray SshIncomingPacket::ExitStatusType("exit-status"); const QByteArray SshIncomingPacket::ExitStatusType("exit-status");
const QByteArray SshIncomingPacket::ExitSignalType("exit-signal"); const QByteArray SshIncomingPacket::ExitSignalType("exit-signal");
const QByteArray SshIncomingPacket::ForwardedTcpIpType("forwarded-tcpip");
SshIncomingPacket::SshIncomingPacket() : m_serverSeqNr(0) { } 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 SshUnimplemented SshIncomingPacket::extractUnimplemented() const
{ {
Q_ASSERT(isComplete()); 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 SshChannelOpenFailure SshIncomingPacket::extractChannelOpenFailure() const
{ {
Q_ASSERT(isComplete()); Q_ASSERT(isComplete());

View File

@@ -97,6 +97,20 @@ struct SshUnimplemented
quint32 invalidMsgSeqNr; quint32 invalidMsgSeqNr;
}; };
struct SshRequestSuccess
{
quint32 bindPort;
};
struct SshChannelOpen
{
quint32 remoteChannel;
quint32 remoteWindowSize;
quint32 remoteMaxPacketSize;
QByteArray remoteAddress;
quint32 remotePort;
};
struct SshChannelOpenFailure struct SshChannelOpenFailure
{ {
quint32 localChannel; quint32 localChannel;
@@ -147,7 +161,6 @@ struct SshChannelExitSignal
QByteArray language; QByteArray language;
}; };
class SshIncomingPacket : public AbstractSshPacket class SshIncomingPacket : public AbstractSshPacket
{ {
public: public:
@@ -164,8 +177,10 @@ public:
SshUserAuthBanner extractUserAuthBanner() const; SshUserAuthBanner extractUserAuthBanner() const;
SshUserAuthInfoRequestPacket extractUserAuthInfoRequest() const; SshUserAuthInfoRequestPacket extractUserAuthInfoRequest() const;
SshDebug extractDebug() const; SshDebug extractDebug() const;
SshRequestSuccess extractRequestSuccess() const;
SshUnimplemented extractUnimplemented() const; SshUnimplemented extractUnimplemented() const;
SshChannelOpen extractChannelOpen() const;
SshChannelOpenFailure extractChannelOpenFailure() const; SshChannelOpenFailure extractChannelOpenFailure() const;
SshChannelOpenConfirmation extractChannelOpenConfirmation() const; SshChannelOpenConfirmation extractChannelOpenConfirmation() const;
SshChannelWindowAdjust extractWindowAdjust() const; SshChannelWindowAdjust extractWindowAdjust() const;
@@ -180,6 +195,7 @@ public:
static const QByteArray ExitStatusType; static const QByteArray ExitStatusType;
static const QByteArray ExitSignalType; static const QByteArray ExitSignalType;
static const QByteArray ForwardedTcpIpType;
private: private:
virtual quint32 cipherBlockSize() const; virtual quint32 cipherBlockSize() const;

View File

@@ -179,6 +179,19 @@ void SshOutgoingPacket::generateDirectTcpIpPacket(quint32 channelId, quint32 win
.appendInt(remotePort).appendString(localIpAddress).appendInt(localPort).finalize(); .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, void SshOutgoingPacket::generateEnvPacket(quint32 remoteChannel,
const QByteArray &var, const QByteArray &value) const QByteArray &var, const QByteArray &value)
{ {
@@ -255,6 +268,22 @@ void SshOutgoingPacket::generateChannelClosePacket(quint32 remoteChannel)
init(SSH_MSG_CHANNEL_CLOSE).appendInt(remoteChannel).finalize(); 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, void SshOutgoingPacket::generateDisconnectPacket(SshErrorCode reason,
const QByteArray &reasonString) const QByteArray &reasonString)
{ {

View File

@@ -65,6 +65,8 @@ public:
void generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize, void generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize,
quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort, quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort,
const QByteArray &localIpAddress, quint32 localPort); 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, void generateEnvPacket(quint32 remoteChannel, const QByteArray &var,
const QByteArray &value); const QByteArray &value);
void generatePtyRequestPacket(quint32 remoteChannel, void generatePtyRequestPacket(quint32 remoteChannel,
@@ -79,6 +81,10 @@ public:
const QByteArray &signalName); const QByteArray &signalName);
void generateChannelEofPacket(quint32 remoteChannel); void generateChannelEofPacket(quint32 remoteChannel);
void generateChannelClosePacket(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: private:
virtual quint32 cipherBlockSize() const; virtual quint32 cipherBlockSize() const;

View File

@@ -84,6 +84,13 @@ enum SshPacketType {
SSH_MSG_INVALID = 128 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 }; enum SshExtendedDataType { SSH_EXTENDED_DATA_STDERR = 1 };
class SshAbstractCryptoFacility; class SshAbstractCryptoFacility;

View File

@@ -172,6 +172,18 @@ void SshSendFacility::sendDirectTcpIpPacket(quint32 channelId, quint32 windowSiz
sendPacket(); 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, void SshSendFacility::sendPtyRequestPacket(quint32 remoteChannel,
const SshPseudoTerminal &terminal) const SshPseudoTerminal &terminal)
{ {
@@ -238,5 +250,20 @@ void SshSendFacility::sendChannelClosePacket(quint32 remoteChannel)
sendPacket(); 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 Internal
} // namespace QSsh } // namespace QSsh

View File

@@ -72,6 +72,8 @@ public:
void sendDirectTcpIpPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize, void sendDirectTcpIpPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize,
const QByteArray &remoteHost, quint32 remotePort, const QByteArray &localIpAddress, const QByteArray &remoteHost, quint32 remotePort, const QByteArray &localIpAddress,
quint32 localPort); quint32 localPort);
void sendTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort);
void sendCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort);
void sendPtyRequestPacket(quint32 remoteChannel, void sendPtyRequestPacket(quint32 remoteChannel,
const SshPseudoTerminal &terminal); const SshPseudoTerminal &terminal);
void sendEnvPacket(quint32 remoteChannel, const QByteArray &var, void sendEnvPacket(quint32 remoteChannel, const QByteArray &var,
@@ -85,6 +87,10 @@ public:
const QByteArray &signalName); const QByteArray &signalName);
void sendChannelEofPacket(quint32 remoteChannel); void sendChannelEofPacket(quint32 remoteChannel);
void sendChannelClosePacket(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; } quint32 nextClientSeqNr() const { return m_clientSeqNr; }
private: private:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -23,7 +23,7 @@
** **
****************************************************************************/ ****************************************************************************/
#include "tunnel.h" #include "directtunnel.h"
#include <ssh/sshconnection.h> #include <ssh/sshconnection.h>
#include <ssh/sshdirecttcpiptunnel.h> #include <ssh/sshdirecttcpiptunnel.h>
@@ -41,7 +41,7 @@ const QByteArray TestData("Urgsblubb?");
using namespace QSsh; using namespace QSsh;
Tunnel::Tunnel(const SshConnectionParameters &parameters, QObject *parent) DirectTunnel::DirectTunnel(const SshConnectionParameters &parameters, QObject *parent)
: QObject(parent), : QObject(parent),
m_connection(new SshConnection(parameters, this)), m_connection(new SshConnection(parameters, this)),
m_targetServer(new QTcpServer(this)), m_targetServer(new QTcpServer(this)),
@@ -51,46 +51,46 @@ Tunnel::Tunnel(const SshConnectionParameters &parameters, QObject *parent)
connect(m_connection, SIGNAL(error(QSsh::SshError)), SLOT(handleConnectionError())); 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; std::cout << "Connecting to SSH server..." << std::endl;
m_connection->connectToHost(); m_connection->connectToHost();
} }
void Tunnel::handleConnectionError() void DirectTunnel::handleConnectionError()
{ {
std::cerr << "SSH connection error: " << qPrintable(m_connection->errorString()) << std::endl; 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; std::cout << "Opening server side..." << std::endl;
if (!m_targetServer->listen(QHostAddress::LocalHost)) { if (!m_targetServer->listen(QHostAddress::LocalHost)) {
std::cerr << "Error opening port: " std::cerr << "Error opening port: "
<< m_targetServer->errorString().toLocal8Bit().constData() << std::endl; << m_targetServer->errorString().toLocal8Bit().constData() << std::endl;
qApp->exit(EXIT_FAILURE); emit finished(EXIT_FAILURE);
return; return;
} }
m_targetPort = m_targetServer->serverPort(); m_targetPort = m_targetServer->serverPort();
connect(m_targetServer, SIGNAL(newConnection()), SLOT(handleNewConnection())); connect(m_targetServer, SIGNAL(newConnection()), SLOT(handleNewConnection()));
m_tunnel = m_connection->createTunnel(QLatin1String("localhost"), 1024, // made-up values m_tunnel = m_connection->createDirectTunnel(QLatin1String("localhost"), 1024, // made-up values
QLatin1String("localhost"), m_targetPort); QLatin1String("localhost"), m_targetPort);
connect(m_tunnel.data(), SIGNAL(initialized()), SLOT(handleInitialized())); connect(m_tunnel.data(), SIGNAL(initialized()), SLOT(handleInitialized()));
connect(m_tunnel.data(), SIGNAL(error(QString)), SLOT(handleTunnelError(QString))); connect(m_tunnel.data(), SIGNAL(error(QString)), SLOT(handleTunnelError(QString)));
connect(m_tunnel.data(), SIGNAL(readyRead()), SLOT(handleServerData())); 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; std::cout << "Initializing tunnel..." << std::endl;
m_tunnel->initialize(); m_tunnel->initialize();
} }
void Tunnel::handleInitialized() void DirectTunnel::handleInitialized()
{ {
std::cout << "Writing data into the tunnel..." << std::endl; std::cout << "Writing data into the tunnel..." << std::endl;
m_tunnel->write(TestData); m_tunnel->write(TestData);
@@ -99,7 +99,7 @@ void Tunnel::handleInitialized()
timeoutTimer->start(10000); timeoutTimer->start(10000);
} }
void Tunnel::handleServerData() void DirectTunnel::handleServerData()
{ {
m_dataReceivedFromServer += m_tunnel->readAll(); m_dataReceivedFromServer += m_tunnel->readAll();
if (m_dataReceivedFromServer == ServerDataPrefix + TestData) { 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; 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) { if (m_expectingChannelClose) {
std::cout << "Successfully detected channel close." << std::endl; std::cout << "Successfully detected channel close." << std::endl;
std::cout << "Test finished successfully." << std::endl; std::cout << "Test finished successfully." << std::endl;
qApp->quit(); emit finished(EXIT_SUCCESS);
} else { } else {
std::cerr << "Error: Remote host closed channel." << std::endl; 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_targetSocket = m_targetServer->nextPendingConnection();
m_targetServer->close(); m_targetServer->close();
@@ -136,14 +136,14 @@ void Tunnel::handleNewConnection()
handleClientData(); handleClientData();
} }
void Tunnel::handleSocketError() void DirectTunnel::handleSocketError()
{ {
std::cerr << "Socket error: " << m_targetSocket->errorString().toLocal8Bit().constData() std::cerr << "Socket error: " << m_targetSocket->errorString().toLocal8Bit().constData()
<< std::endl; << std::endl;
qApp->exit(EXIT_FAILURE); emit finished(EXIT_FAILURE);
} }
void Tunnel::handleClientData() void DirectTunnel::handleClientData()
{ {
m_dataReceivedFromClient += m_targetSocket->readAll(); m_dataReceivedFromClient += m_targetSocket->readAll();
if (m_dataReceivedFromClient == TestData) { 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; std::cerr << "Error: Timeout waiting for test completion." << std::endl;
qApp->exit(EXIT_FAILURE); emit finished(EXIT_FAILURE);
} }

View File

@@ -39,15 +39,18 @@ class SshConnectionParameters;
class SshDirectTcpIpTunnel; class SshDirectTcpIpTunnel;
} }
class Tunnel : public QObject class DirectTunnel : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Tunnel(const QSsh::SshConnectionParameters &parameters, QObject *parent = 0); DirectTunnel(const QSsh::SshConnectionParameters &parameters, QObject *parent = 0);
~Tunnel(); ~DirectTunnel();
void run(); void run();
signals:
void finished(int errorCode);
private slots: private slots:
void handleConnected(); void handleConnected();
void handleConnectionError(); void handleConnectionError();

View File

@@ -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 &parameters, 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);
}

View File

@@ -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 &parameters,
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;
};

View File

@@ -24,7 +24,8 @@
****************************************************************************/ ****************************************************************************/
#include "../remoteprocess/argumentscollector.h" #include "../remoteprocess/argumentscollector.h"
#include "tunnel.h" #include "directtunnel.h"
#include "forwardtunnel.h"
#include <ssh/sshconnection.h> #include <ssh/sshconnection.h>
@@ -44,7 +45,15 @@ int main(int argc, char *argv[])
parameters.host = QLatin1String("127.0.0.1"); parameters.host = QLatin1String("127.0.0.1");
if (!parseSuccess) if (!parseSuccess)
return EXIT_FAILURE; 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(); return app.exec();
} }

View File

@@ -1,5 +1,12 @@
include(../ssh.pri) include(../ssh.pri)
TARGET=tunnel TARGET =tunnel
SOURCES=main.cpp tunnel.cpp argumentscollector.cpp SOURCES = \
HEADERS=tunnel.h argumentscollector.h main.cpp \
argumentscollector.cpp \
directtunnel.cpp \
forwardtunnel.cpp
HEADERS = \
argumentscollector.h \
directtunnel.h \
forwardtunnel.h