aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2016-03-31 18:57:03 +0200
committerUlf Hermann <ulf.hermann@qt.io>2016-05-23 07:44:06 +0000
commit6b4963b5499269062e2cffe9e70df4f022c5bf8c (patch)
tree843803dfdfb23b2b238b515cd54cff5c52740053
parente05fbf153d98a34ddda63b12402c15642994acdc (diff)
Ssh: Implement tcp/ip forward tunneling
Change-Id: I529b3392ea7e4cbae2c736c9f55352ef6b19da98 Reviewed-by: Christian Kandeler <christian.kandeler@theqtcompany.com>
-rw-r--r--src/libs/ssh/ssh.pro12
-rw-r--r--src/libs/ssh/ssh.qbs3
-rw-r--r--src/libs/ssh/sshchannel.cpp2
-rw-r--r--src/libs/ssh/sshchannelmanager.cpp112
-rw-r--r--src/libs/ssh/sshchannelmanager_p.h9
-rw-r--r--src/libs/ssh/sshconnection.cpp36
-rw-r--r--src/libs/ssh/sshconnection.h5
-rw-r--r--src/libs/ssh/sshconnection_p.h8
-rw-r--r--src/libs/ssh/sshdirecttcpiptunnel.cpp83
-rw-r--r--src/libs/ssh/sshdirecttcpiptunnel.h5
-rw-r--r--src/libs/ssh/sshdirecttcpiptunnel_p.h21
-rw-r--r--src/libs/ssh/sshforwardedtcpiptunnel.cpp100
-rw-r--r--src/libs/ssh/sshforwardedtcpiptunnel.h70
-rw-r--r--src/libs/ssh/sshforwardedtcpiptunnel_p.h44
-rw-r--r--src/libs/ssh/sshincomingpacket.cpp42
-rw-r--r--src/libs/ssh/sshincomingpacket_p.h18
-rw-r--r--src/libs/ssh/sshoutgoingpacket.cpp29
-rw-r--r--src/libs/ssh/sshoutgoingpacket_p.h6
-rw-r--r--src/libs/ssh/sshpacket_p.h7
-rw-r--r--src/libs/ssh/sshsendfacility.cpp27
-rw-r--r--src/libs/ssh/sshsendfacility_p.h6
-rw-r--r--src/libs/ssh/sshtcpipforwardserver.cpp136
-rw-r--r--src/libs/ssh/sshtcpipforwardserver.h81
-rw-r--r--src/libs/ssh/sshtcpipforwardserver_p.h54
-rw-r--r--src/libs/ssh/sshtcpiptunnel.cpp123
-rw-r--r--src/libs/ssh/sshtcpiptunnel_p.h82
-rw-r--r--tests/manual/ssh/tunnel/directtunnel.cpp (renamed from tests/manual/ssh/tunnel/tunnel.cpp)48
-rw-r--r--tests/manual/ssh/tunnel/directtunnel.h (renamed from tests/manual/ssh/tunnel/tunnel.h)9
-rw-r--r--tests/manual/ssh/tunnel/forwardtunnel.cpp146
-rw-r--r--tests/manual/ssh/tunnel/forwardtunnel.h71
-rw-r--r--tests/manual/ssh/tunnel/main.cpp15
-rw-r--r--tests/manual/ssh/tunnel/tunnel.pro13
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 &parameters, QObject *parent)
+DirectTunnel::DirectTunnel(const SshConnectionParameters &parameters, QObject *parent)
: QObject(parent),
m_connection(new SshConnection(parameters, 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()));
}
-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 &parameters, QObject *parent = 0);
- ~Tunnel();
+ DirectTunnel(const QSsh::SshConnectionParameters &parameters, 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 &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);
+}
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 &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;
+};
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