diff options
author | Christian Kandeler <christian.kandeler@nokia.com> | 2012-06-19 13:03:48 +0200 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@nokia.com> | 2012-08-28 15:12:04 +0200 |
commit | edcf76613b82d4b04ad9d0797515dc42d983eb68 (patch) | |
tree | 5229324612e8489ebe70e304641ea0781893dfdd | |
parent | a57b4cf93efcb3fe30623566115b29e925367f7f (diff) | |
download | qt-creator-edcf76613b82d4b04ad9d0797515dc42d983eb68.tar.gz |
SSH: Implement tunneling.
This is the "direct-tcpip" port forwarding
specified in RFC 4254.
Change-Id: I1ffa2e923b4479c7211b1b4304e66895b565fb64
Reviewed-by: hjk <qthjk@ovi.com>
Reviewed-by: Christian Kandeler <christian.kandeler@nokia.com>
27 files changed, 1022 insertions, 43 deletions
diff --git a/src/libs/ssh/sftpchannel_p.h b/src/libs/ssh/sftpchannel_p.h index e3979312b2..9597e28e2d 100644 --- a/src/libs/ssh/sftpchannel_p.h +++ b/src/libs/ssh/sftpchannel_p.h @@ -49,14 +49,8 @@ class SftpChannelPrivate : public AbstractSshChannel Q_OBJECT friend class QSsh::SftpChannel; public: - enum SftpState { Inactive, SubsystemRequested, InitSent, Initialized }; - virtual void handleChannelSuccess(); - virtual void handleChannelFailure(); - - virtual void closeHook(); - signals: void initialized(); void initializationFailed(const QString &reason); @@ -72,6 +66,9 @@ private: SftpChannel *sftp); SftpJobId createJob(const AbstractSftpOperation::Ptr &job); + virtual void handleChannelSuccess(); + virtual void handleChannelFailure(); + virtual void handleOpenSuccessInternal(); virtual void handleOpenFailureInternal(const QString &reason); virtual void handleChannelDataInternal(const QByteArray &data); @@ -80,6 +77,8 @@ private: virtual void handleExitStatus(const SshChannelExitStatus &exitStatus); virtual void handleExitSignal(const SshChannelExitSignal &signal); + virtual void closeHook(); + void handleCurrentPacket(); void handleServerVersion(); void handleHandle(); diff --git a/src/libs/ssh/ssh.pro b/src/libs/ssh/ssh.pro index 2df5b0430b..3b4ce04962 100644 --- a/src/libs/ssh/ssh.pro +++ b/src/libs/ssh/ssh.pro @@ -29,7 +29,8 @@ SOURCES = $$PWD/sshsendfacility.cpp \ $$PWD/sshconnectionmanager.cpp \ $$PWD/sshkeypasswordretriever.cpp \ $$PWD/sftpfilesystemmodel.cpp \ - $$PWD/sshkeycreationdialog.cpp + $$PWD/sshkeycreationdialog.cpp \ + $$PWD/sshdirecttcpiptunnel.cpp HEADERS = $$PWD/sshsendfacility_p.h \ $$PWD/sshremoteprocess.h \ @@ -62,6 +63,8 @@ HEADERS = $$PWD/sshsendfacility_p.h \ $$PWD/sshkeypasswordretriever_p.h \ $$PWD/sftpfilesystemmodel.h \ $$PWD/sshkeycreationdialog.h \ - $$PWD/ssh_global.h + $$PWD/ssh_global.h \ + $$PWD/sshdirecttcpiptunnel_p.h \ + $$PWD/sshdirecttcpiptunnel.h FORMS = $$PWD/sshkeycreationdialog.ui diff --git a/src/libs/ssh/ssh.qbs b/src/libs/ssh/ssh.qbs index cb8603327c..42b84abcc5 100644 --- a/src/libs/ssh/ssh.qbs +++ b/src/libs/ssh/ssh.qbs @@ -35,6 +35,7 @@ QtcLibrary { "sshpacket.cpp", "sshpacket_p.h", "sshpacketparser.cpp", "sshpacketparser_p.h", "sshremoteprocess.cpp", "sshremoteprocess.h", "sshremoteprocess_p.h", + "sshdirecttcpiptunnel.h", "sshdirecttcpiptunnel_p.h", "sshdirecttcpiptunnel.cpp", "sshremoteprocessrunner.cpp", "sshremoteprocessrunner.h", "sshsendfacility.cpp", "sshsendfacility_p.h", "sshkeypasswordretriever.cpp", diff --git a/src/libs/ssh/sshchannel.cpp b/src/libs/ssh/sshchannel.cpp index f513f5121f..a2dfe78b5c 100644 --- a/src/libs/ssh/sshchannel.cpp +++ b/src/libs/ssh/sshchannel.cpp @@ -40,18 +40,14 @@ namespace QSsh { namespace Internal { -namespace { - const quint32 MinMaxPacketSize = 32768; - const quint32 MaxPacketSize = 16 * 1024 * 1024; - const quint32 InitialWindowSize = MaxPacketSize; - const quint32 NoChannel = 0xffffffffu; -} // anonymous namespace +const quint32 MinMaxPacketSize = 32768; +const quint32 NoChannel = 0xffffffffu; AbstractSshChannel::AbstractSshChannel(quint32 channelId, SshSendFacility &sendFacility) : m_sendFacility(sendFacility), m_timeoutTimer(new QTimer(this)), m_localChannel(channelId), m_remoteChannel(NoChannel), - m_localWindowSize(InitialWindowSize), m_remoteWindowSize(0), + m_localWindowSize(initialWindowSize()), m_remoteWindowSize(0), m_state(Inactive) { m_timeoutTimer->setSingleShot(true); @@ -77,8 +73,7 @@ void AbstractSshChannel::requestSessionStart() // with our cryptography stuff, it would have hit us before, on // establishing the connection. try { - m_sendFacility.sendSessionPacket(m_localChannel, InitialWindowSize, - MaxPacketSize); + m_sendFacility.sendSessionPacket(m_localChannel, initialWindowSize(), maxPacketSize()); setChannelState(SessionRequested); m_timeoutTimer->start(ReplyTimeout); } catch (Botan::Exception &e) { @@ -98,6 +93,16 @@ void AbstractSshChannel::sendData(const QByteArray &data) } } +quint32 AbstractSshChannel::initialWindowSize() +{ + return maxPacketSize(); +} + +quint32 AbstractSshChannel::maxPacketSize() +{ + return 16 * 1024 * 1024; +} + void AbstractSshChannel::handleWindowAdjust(quint32 bytesToAdd) { checkChannelActive(); @@ -174,6 +179,7 @@ void AbstractSshChannel::handleChannelEof() "Unexpected SSH_MSG_CHANNEL_EOF message."); } m_localWindowSize = 0; + emit eof(); } void AbstractSshChannel::handleChannelClose() @@ -224,10 +230,9 @@ int AbstractSshChannel::handleChannelOrExtendedChannelData(const QByteArray &dat qWarning("Misbehaving server does not respect local window, clipping."); m_localWindowSize -= bytesToDeliver; - if (m_localWindowSize < MaxPacketSize) { - m_localWindowSize += MaxPacketSize; - m_sendFacility.sendWindowAdjustPacket(m_remoteChannel, - MaxPacketSize); + if (m_localWindowSize < maxPacketSize()) { + m_localWindowSize += maxPacketSize(); + m_sendFacility.sendWindowAdjustPacket(m_remoteChannel, maxPacketSize()); } return bytesToDeliver; } @@ -256,7 +261,7 @@ void AbstractSshChannel::checkChannelActive() quint32 AbstractSshChannel::maxDataSize() const { - return qMin(m_localWindowSize, MaxPacketSize); + return qMin(m_localWindowSize, maxPacketSize()); } } // namespace Internal diff --git a/src/libs/ssh/sshchannel_p.h b/src/libs/ssh/sshchannel_p.h index d1a84e1563..5898bc44b1 100644 --- a/src/libs/ssh/sshchannel_p.h +++ b/src/libs/ssh/sshchannel_p.h @@ -53,17 +53,12 @@ public: Inactive, SessionRequested, SessionEstablished, CloseRequested, Closed }; - ChannelState channelState() const { return m_state; } - void setChannelState(ChannelState state); - quint32 localChannelId() const { return m_localChannel; } quint32 remoteChannel() const { return m_remoteChannel; } virtual void handleChannelSuccess() = 0; virtual void handleChannelFailure() = 0; - virtual void closeHook() = 0; - void handleOpenSuccess(quint32 remoteChannelId, quint32 remoteWindowSize, quint32 remoteMaxPacketSize); void handleOpenFailure(const QString &reason); @@ -74,8 +69,6 @@ public: void handleChannelExtendedData(quint32 type, const QByteArray &data); void handleChannelRequest(const SshIncomingPacket &packet); - void requestSessionStart(); - void sendData(const QByteArray &data); void closeChannel(); virtual ~AbstractSshChannel(); @@ -84,10 +77,20 @@ public: signals: void timeout(); + void eof(); protected: AbstractSshChannel(quint32 channelId, SshSendFacility &sendFacility); + void setChannelState(ChannelState state); + ChannelState channelState() const { return m_state; } + + void requestSessionStart(); + void sendData(const QByteArray &data); + + static quint32 initialWindowSize(); + static quint32 maxPacketSize(); + quint32 maxDataSize() const; void checkChannelActive(); @@ -103,7 +106,8 @@ private: virtual void handleExitStatus(const SshChannelExitStatus &exitStatus) = 0; virtual void handleExitSignal(const SshChannelExitSignal &signal) = 0; - void setState(ChannelState newState); + virtual void closeHook() = 0; + void flushSendBuffer(); int handleChannelOrExtendedChannelData(const QByteArray &data); diff --git a/src/libs/ssh/sshchannelmanager.cpp b/src/libs/ssh/sshchannelmanager.cpp index 792963702e..5d7f35f91d 100644 --- a/src/libs/ssh/sshchannelmanager.cpp +++ b/src/libs/ssh/sshchannelmanager.cpp @@ -32,6 +32,8 @@ #include "sftpchannel.h" #include "sftpchannel_p.h" +#include "sshdirecttcpiptunnel.h" +#include "sshdirecttcpiptunnel_p.h" #include "sshincomingpacket_p.h" #include "sshremoteprocess.h" #include "sshremoteprocess_p.h" @@ -168,6 +170,15 @@ QSsh::SftpChannel::Ptr SshChannelManager::createSftpChannel() return sftp; } +SshDirectTcpIpTunnel::Ptr SshChannelManager::createTunnel(quint16 remotePort, + const SshConnectionInfo &connectionInfo) +{ + SshDirectTcpIpTunnel::Ptr tunnel(new SshDirectTcpIpTunnel(m_nextLocalChannelId++, remotePort, + connectionInfo, m_sendFacility)); + insertChannel(tunnel->d, tunnel); + return tunnel; +} + 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 16626eddb1..28f7266deb 100644 --- a/src/libs/ssh/sshchannelmanager_p.h +++ b/src/libs/ssh/sshchannelmanager_p.h @@ -36,8 +36,9 @@ #include <QSharedPointer> namespace QSsh { - class SftpChannel; +class SshConnectionInfo; +class SshDirectTcpIpTunnel; class SshRemoteProcess; namespace Internal { @@ -55,8 +56,10 @@ public: QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); QSharedPointer<SshRemoteProcess> createRemoteShell(); QSharedPointer<SftpChannel> createSftpChannel(); - int channelCount() const; + QSharedPointer<SshDirectTcpIpTunnel> createTunnel(quint16 remotePort, + const SshConnectionInfo &connectionInfo); + int channelCount() const; enum CloseAllMode { CloseAllRegular, CloseAllAndReset }; int closeAllChannels(CloseAllMode mode); diff --git a/src/libs/ssh/sshconnection.cpp b/src/libs/ssh/sshconnection.cpp index ebf233d3b6..8dfd475dd5 100644 --- a/src/libs/ssh/sshconnection.cpp +++ b/src/libs/ssh/sshconnection.cpp @@ -35,8 +35,10 @@ #include "sshcapabilities_p.h" #include "sshchannelmanager_p.h" #include "sshcryptofacility_p.h" +#include "sshdirecttcpiptunnel.h" #include "sshexception_p.h" #include "sshkeyexchange_p.h" +#include "sshremoteprocess.h" #include <botan/botan.h> @@ -191,6 +193,12 @@ QSharedPointer<SftpChannel> SshConnection::createSftpChannel() return d->createSftpChannel(); } +SshDirectTcpIpTunnel::Ptr SshConnection::createTunnel(quint16 remotePort) +{ + QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshDirectTcpIpTunnel::Ptr()); + return d->createTunnel(remotePort); +} + int SshConnection::closeAllChannels() { try { @@ -730,6 +738,11 @@ QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel() return m_channelManager->createSftpChannel(); } +SshDirectTcpIpTunnel::Ptr SshConnectionPrivate::createTunnel(quint16 remotePort) +{ + return m_channelManager->createTunnel(remotePort, m_conn->connectionInfo()); +} + const quint64 SshConnectionPrivate::InvalidSeqNr = static_cast<quint64>(-1); } // namespace Internal diff --git a/src/libs/ssh/sshconnection.h b/src/libs/ssh/sshconnection.h index 66263c3077..3c982b197e 100644 --- a/src/libs/ssh/sshconnection.h +++ b/src/libs/ssh/sshconnection.h @@ -43,6 +43,7 @@ namespace QSsh { class SftpChannel; +class SshDirectTcpIpTunnel; class SshRemoteProcess; namespace Internal { @@ -103,6 +104,7 @@ public: QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); QSharedPointer<SshRemoteProcess> createRemoteShell(); QSharedPointer<SftpChannel> createSftpChannel(); + QSharedPointer<SshDirectTcpIpTunnel> createTunnel(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 bdf0c26c96..ed74322b4d 100644 --- a/src/libs/ssh/sshconnection_p.h +++ b/src/libs/ssh/sshconnection_p.h @@ -34,7 +34,6 @@ #include "sshconnection.h" #include "sshexception_p.h" #include "sshincomingpacket_p.h" -#include "sshremoteprocess.h" #include "sshsendfacility_p.h" #include <QHash> @@ -50,6 +49,8 @@ QT_END_NAMESPACE namespace QSsh { class SftpChannel; +class SshRemoteProcess; +class SshDirectTcpIpTunnel; namespace Internal { class SshChannelManager; @@ -88,6 +89,8 @@ public: QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command); QSharedPointer<SshRemoteProcess> createRemoteShell(); QSharedPointer<SftpChannel> createSftpChannel(); + QSharedPointer<SshDirectTcpIpTunnel> createTunnel(quint16 remotePort); + SshStateInternal state() const { return m_state; } SshError error() const { return m_error; } QString errorString() const { return m_errorString; } diff --git a/src/libs/ssh/sshdirecttcpiptunnel.cpp b/src/libs/ssh/sshdirecttcpiptunnel.cpp new file mode 100644 index 0000000000..ef0cada653 --- /dev/null +++ b/src/libs/ssh/sshdirecttcpiptunnel.cpp @@ -0,0 +1,194 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ +#include "sshdirecttcpiptunnel.h" +#include "sshdirecttcpiptunnel_p.h" + +#include "sshincomingpacket_p.h" +#include "sshsendfacility_p.h" + +#include <QTimer> + +namespace QSsh { +namespace Internal { + +SshDirectTcpIpTunnelPrivate::SshDirectTcpIpTunnelPrivate(quint32 channelId, quint16 remotePort, + const SshConnectionInfo &connectionInfo, SshSendFacility &sendFacility) + : AbstractSshChannel(channelId, sendFacility), + m_remotePort(remotePort), + m_connectionInfo(connectionInfo) +{ + 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() +{ + 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) +{ + qDebug("%s: Unexpected extended channel data. Type is %u, content is '%s'.", Q_FUNC_INFO, type, + data.constData()); +} + +void SshDirectTcpIpTunnelPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus) +{ + qDebug("%s: Unexpected exit status %d.", Q_FUNC_INFO, exitStatus.exitStatus); +} + +void SshDirectTcpIpTunnelPrivate::handleExitSignal(const SshChannelExitSignal &signal) +{ + qDebug("%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; + +SshDirectTcpIpTunnel::SshDirectTcpIpTunnel(quint32 channelId, quint16 remotePort, + const SshConnectionInfo &connectionInfo, SshSendFacility &sendFacility) + : d(new SshDirectTcpIpTunnelPrivate(channelId, remotePort, connectionInfo, 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); +} + +SshDirectTcpIpTunnel::~SshDirectTcpIpTunnel() +{ + d->closeChannel(); + delete d; +} + +bool SshDirectTcpIpTunnel::atEnd() const +{ + return QIODevice::atEnd() && d->m_data.isEmpty(); +} + +qint64 SshDirectTcpIpTunnel::bytesAvailable() const +{ + return QIODevice::bytesAvailable() + d->m_data.count(); +} + +bool SshDirectTcpIpTunnel::canReadLine() const +{ + return QIODevice::canReadLine() || d->m_data.contains('\n'); +} + +void SshDirectTcpIpTunnel::close() +{ + d->closeChannel(); + QIODevice::close(); +} + +void SshDirectTcpIpTunnel::initialize() +{ + QSSH_ASSERT_AND_RETURN(d->channelState() == AbstractSshChannel::Inactive); + + try { + QIODevice::open(QIODevice::ReadWrite); + d->m_sendFacility.sendDirectTcpIpPacket(d->localChannelId(), d->initialWindowSize(), + d->maxPacketSize(), d->m_connectionInfo.peerAddress.toString().toUtf8(), + d->m_remotePort, d->m_connectionInfo.localAddress.toString().toUtf8(), + d->m_connectionInfo.localPort); + d->setChannelState(AbstractSshChannel::SessionRequested); + d->m_timeoutTimer->start(d->ReplyTimeout); + } catch (Botan::Exception &e) { // Won't happen, but let's play it safe. + qDebug("Botan error: %s", e.what()); + d->closeChannel(); + } +} + +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; +} + +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); +} + +} // namespace QSsh diff --git a/src/libs/ssh/sshdirecttcpiptunnel.h b/src/libs/ssh/sshdirecttcpiptunnel.h new file mode 100644 index 0000000000..b451c96afc --- /dev/null +++ b/src/libs/ssh/sshdirecttcpiptunnel.h @@ -0,0 +1,90 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef SSHDIRECTTCPIPTUNNEL_H +#define SSHDIRECTTCPIPTUNNEL_H + +#include "ssh_global.h" + +#include <QIODevice> +#include <QSharedPointer> + +namespace QSsh { +class SshConnectionInfo; + +namespace Internal { +class SshChannelManager; +class SshDirectTcpIpTunnelPrivate; +class SshSendFacility; +} // namespace Internal + +class QSSH_EXPORT SshDirectTcpIpTunnel : public QIODevice +{ + Q_OBJECT + + friend class Internal::SshChannelManager; + +public: + typedef QSharedPointer<SshDirectTcpIpTunnel> Ptr; + + ~SshDirectTcpIpTunnel(); + + // QIODevice stuff + bool atEnd() const; + qint64 bytesAvailable() const; + bool canReadLine() const; + void close(); + bool isSequential() const { return true; } + + void initialize(); + +signals: + void initialized(); + void error(const QString &reason); + void tunnelClosed(); + +private: + SshDirectTcpIpTunnel(quint32 channelId, quint16 remotePort, + const SshConnectionInfo &connectionInfo, Internal::SshSendFacility &sendFacility); + + // QIODevice stuff + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + + Q_SLOT void handleError(const QString &reason); + + Internal::SshDirectTcpIpTunnelPrivate * const d; +}; + +} // namespace QSsh + +#endif // SSHDIRECTTCPIPTUNNEL_H diff --git a/src/libs/ssh/sshdirecttcpiptunnel_p.h b/src/libs/ssh/sshdirecttcpiptunnel_p.h new file mode 100644 index 0000000000..7122c4dc2a --- /dev/null +++ b/src/libs/ssh/sshdirecttcpiptunnel_p.h @@ -0,0 +1,84 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ +#ifndef DIRECTTCPIPCHANNEL_P_H +#define DIRECTTCPIPCHANNEL_P_H + +#include "sshchannel_p.h" + +#include "sshconnection.h" + +namespace QSsh { +class SshDirectTcpIpTunnel; + +namespace Internal { + +class SshDirectTcpIpTunnelPrivate : public AbstractSshChannel +{ + Q_OBJECT + + friend class QSsh::SshDirectTcpIpTunnel; + +public: + explicit SshDirectTcpIpTunnelPrivate(quint32 channelId, quint16 remotePort, + const SshConnectionInfo &connectionInfo, SshSendFacility &sendFacility); + +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 quint16 m_remotePort; + const SshConnectionInfo m_connectionInfo; + QByteArray m_data; +}; + +} // namespace Internal +} // namespace QSsh + +#endif // DIRECTTCPIPCHANNEL_P_H diff --git a/src/libs/ssh/sshoutgoingpacket.cpp b/src/libs/ssh/sshoutgoingpacket.cpp index 97b199beed..1be0aa4934 100644 --- a/src/libs/ssh/sshoutgoingpacket.cpp +++ b/src/libs/ssh/sshoutgoingpacket.cpp @@ -143,7 +143,16 @@ void SshOutgoingPacket::generateSessionPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize) { init(SSH_MSG_CHANNEL_OPEN).appendString("session").appendInt(channelId) - .appendInt(windowSize).appendInt(maxPacketSize).finalize(); + .appendInt(windowSize).appendInt(maxPacketSize).finalize(); +} + +void SshOutgoingPacket::generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize, + quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort, + const QByteArray &localIpAddress, quint32 localPort) +{ + init(SSH_MSG_CHANNEL_OPEN).appendString("direct-tcpip").appendInt(channelId) + .appendInt(windowSize).appendInt(maxPacketSize).appendString(remoteHost) + .appendInt(remotePort).appendString(localIpAddress).appendInt(localPort).finalize(); } void SshOutgoingPacket::generateEnvPacket(quint32 remoteChannel, diff --git a/src/libs/ssh/sshoutgoingpacket_p.h b/src/libs/ssh/sshoutgoingpacket_p.h index 60b2d3087a..7c77c749b3 100644 --- a/src/libs/ssh/sshoutgoingpacket_p.h +++ b/src/libs/ssh/sshoutgoingpacket_p.h @@ -62,6 +62,9 @@ public: void generateInvalidMessagePacket(); void generateSessionPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize); + void generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize, + quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort, + const QByteArray &localIpAddress, quint32 localPort); void generateEnvPacket(quint32 remoteChannel, const QByteArray &var, const QByteArray &value); void generatePtyRequestPacket(quint32 remoteChannel, diff --git a/src/libs/ssh/sshremoteprocess.cpp b/src/libs/ssh/sshremoteprocess.cpp index 36f71b5b80..a4f4ae76b5 100644 --- a/src/libs/ssh/sshremoteprocess.cpp +++ b/src/libs/ssh/sshremoteprocess.cpp @@ -167,6 +167,7 @@ void SshRemoteProcess::init() connect(d, SIGNAL(readyReadStandardError()), this, SIGNAL(readyReadStandardError()), Qt::QueuedConnection); connect(d, SIGNAL(closed(int)), this, SIGNAL(closed(int)), Qt::QueuedConnection); + connect(d, SIGNAL(eof()), SIGNAL(readChannelFinished()), Qt::QueuedConnection); } void SshRemoteProcess::addToEnvironment(const QByteArray &var, const QByteArray &value) diff --git a/src/libs/ssh/sshremoteprocess_p.h b/src/libs/ssh/sshremoteprocess_p.h index 57bf3f9ceb..6cecde117c 100644 --- a/src/libs/ssh/sshremoteprocess_p.h +++ b/src/libs/ssh/sshremoteprocess_p.h @@ -54,13 +54,6 @@ public: NotYetStarted, ExecRequested, StartFailed, Running, Exited }; - virtual void handleChannelSuccess(); - virtual void handleChannelFailure(); - - virtual void closeHook(); - - QByteArray &data(); - signals: void started(); void readyRead(); @@ -74,6 +67,9 @@ private: SshRemoteProcessPrivate(quint32 channelId, SshSendFacility &sendFacility, SshRemoteProcess *proc); + virtual void handleChannelSuccess(); + virtual void handleChannelFailure(); + virtual void handleOpenSuccessInternal(); virtual void handleOpenFailureInternal(const QString &reason); virtual void handleChannelDataInternal(const QByteArray &data); @@ -82,8 +78,11 @@ private: virtual void handleExitStatus(const SshChannelExitStatus &exitStatus); virtual void handleExitSignal(const SshChannelExitSignal &signal); + virtual void closeHook(); + void init(); void setProcState(ProcessState newState); + QByteArray &data(); QProcess::ProcessChannel m_readChannel; diff --git a/src/libs/ssh/sshsendfacility.cpp b/src/libs/ssh/sshsendfacility.cpp index 07f0a3755f..e90ba62246 100644 --- a/src/libs/ssh/sshsendfacility.cpp +++ b/src/libs/ssh/sshsendfacility.cpp @@ -150,6 +150,15 @@ void SshSendFacility::sendSessionPacket(quint32 channelId, quint32 windowSize, sendPacket(); } +void SshSendFacility::sendDirectTcpIpPacket(quint32 channelId, quint32 windowSize, + quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort, + const QByteArray &localIpAddress, quint32 localPort) +{ + m_outgoingPacket.generateDirectTcpIpPacket(channelId, windowSize, maxPacketSize, remoteHost, + remotePort, localIpAddress, localPort); + sendPacket(); +} + void SshSendFacility::sendPtyRequestPacket(quint32 remoteChannel, const SshPseudoTerminal &terminal) { diff --git a/src/libs/ssh/sshsendfacility_p.h b/src/libs/ssh/sshsendfacility_p.h index d7d20d901d..b17484e491 100644 --- a/src/libs/ssh/sshsendfacility_p.h +++ b/src/libs/ssh/sshsendfacility_p.h @@ -69,6 +69,9 @@ public: void sendInvalidPacket(); void sendSessionPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize); + void sendDirectTcpIpPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize, + const QByteArray &remoteHost, quint32 remotePort, const QByteArray &localIpAddress, + quint32 localPort); void sendPtyRequestPacket(quint32 remoteChannel, const SshPseudoTerminal &terminal); void sendEnvPacket(quint32 remoteChannel, const QByteArray &var, diff --git a/tests/manual/ssh/remoteprocess/remoteprocesstest.h b/tests/manual/ssh/remoteprocess/remoteprocesstest.h index a2d21e3e05..2cef8828dd 100644 --- a/tests/manual/ssh/remoteprocess/remoteprocesstest.h +++ b/tests/manual/ssh/remoteprocess/remoteprocesstest.h @@ -71,10 +71,10 @@ private: const QSsh::SshConnectionParameters m_sshParams; QTimer * const m_timeoutTimer; QTextStream *m_textStream; - QSsh::SshRemoteProcessRunner * const m_remoteRunner; QSsh::SshRemoteProcess::Ptr m_catProcess; QSsh::SshRemoteProcess::Ptr m_echoProcess; QSsh::SshConnection *m_sshConnection; + QSsh::SshRemoteProcessRunner * const m_remoteRunner; QByteArray m_remoteStdout; QByteArray m_remoteStderr; QByteArray m_remoteData; diff --git a/tests/manual/ssh/ssh.pro b/tests/manual/ssh/ssh.pro index 6284229802..fdfd768132 100644 --- a/tests/manual/ssh/ssh.pro +++ b/tests/manual/ssh/ssh.pro @@ -5,4 +5,4 @@ #------------------------------------------------- TEMPLATE = subdirs -SUBDIRS = errorhandling sftp remoteprocess shell sftpfsmodel +SUBDIRS = errorhandling sftp remoteprocess shell sftpfsmodel tunnel diff --git a/tests/manual/ssh/tunnel/argumentscollector.cpp b/tests/manual/ssh/tunnel/argumentscollector.cpp new file mode 100644 index 0000000000..a533ac966a --- /dev/null +++ b/tests/manual/ssh/tunnel/argumentscollector.cpp @@ -0,0 +1,174 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ +#include "argumentscollector.h" + +#include <QDir> +#include <QProcessEnvironment> + +#include <iostream> + +using namespace QSsh; + +using namespace std; + +ArgumentsCollector::ArgumentsCollector(const QStringList &args) + : m_arguments(args) +{ +} + +QSsh::SshConnectionParameters ArgumentsCollector::collect(bool &success) const +{ + SshConnectionParameters parameters; + parameters.host = QLatin1String("localhost"); + + try { + bool authTypeGiven = false; + bool portGiven = false; + bool timeoutGiven = false; + bool proxySettingGiven = false; + int pos; + int port; + + for (pos = 1; pos < m_arguments.count() - 1; ++pos) { + if (checkAndSetStringArg(pos, parameters.userName, "-u")) + continue; + if (checkAndSetIntArg(pos, port, portGiven, "-p") + || checkAndSetIntArg(pos, parameters.timeout, timeoutGiven, "-t")) + continue; + if (checkAndSetStringArg(pos, parameters.password, "-pwd")) { + if (!parameters.privateKeyFile.isEmpty()) + throw ArgumentErrorException(QLatin1String("-pwd and -k are mutually exclusive.")); + parameters.authenticationType + = SshConnectionParameters::AuthenticationByPassword; + authTypeGiven = true; + continue; + } + if (checkAndSetStringArg(pos, parameters.privateKeyFile, "-k")) { + if (!parameters.password.isEmpty()) + throw ArgumentErrorException(QLatin1String("-pwd and -k are mutually exclusive.")); + parameters.authenticationType + = SshConnectionParameters::AuthenticationByKey; + authTypeGiven = true; + continue; + } + if (!checkForNoProxy(pos, parameters.proxyType, proxySettingGiven)) + throw ArgumentErrorException(QLatin1String("unknown option ") + m_arguments.at(pos)); + } + + Q_ASSERT(pos <= m_arguments.count()); + if (pos == m_arguments.count() - 1) { + if (!checkForNoProxy(pos, parameters.proxyType, proxySettingGiven)) + throw ArgumentErrorException(QLatin1String("unknown option ") + m_arguments.at(pos)); + } + + if (!authTypeGiven) { + parameters.authenticationType = SshConnectionParameters::AuthenticationByKey; + parameters.privateKeyFile = QDir::homePath() + QLatin1String("/.ssh/id_rsa"); + } + + if (parameters.userName.isEmpty()) { + parameters.userName + = QProcessEnvironment::systemEnvironment().value(QLatin1String("USER")); + } + if (parameters.userName.isEmpty()) + throw ArgumentErrorException(QLatin1String("No user name given.")); + + if (parameters.host.isEmpty()) + throw ArgumentErrorException(QLatin1String("No host given.")); + + parameters.port = portGiven ? port : 22; + if (!timeoutGiven) + parameters.timeout = 30; + success = true; + } catch (ArgumentErrorException &ex) { + cerr << "Error: " << qPrintable(ex.error) << endl; + printUsage(); + success = false; + } + return parameters; +} + +void ArgumentsCollector::printUsage() const +{ + cerr << "Usage: " << qPrintable(m_arguments.first()) + << "[ -u <user> ] " + << "[ -pwd <password> | -k <private key file> ] [ -p <port> ] " + << "[ -t <timeout> ] [ -no-proxy ]" << endl; +} + +bool ArgumentsCollector::checkAndSetStringArg(int &pos, QString &arg, const char *opt) const +{ + if (m_arguments.at(pos) == QLatin1String(opt)) { + if (!arg.isEmpty()) { + throw ArgumentErrorException(QLatin1String("option ") + opt + + QLatin1String(" was given twice.")); + } + arg = m_arguments.at(++pos); + if (arg.isEmpty() && QLatin1String(opt) != QLatin1String("-pwd")) + throw ArgumentErrorException(QLatin1String("empty argument not allowed here.")); + return true; + } + return false; +} + +bool ArgumentsCollector::checkAndSetIntArg(int &pos, int &val, + bool &alreadyGiven, const char *opt) const +{ + if (m_arguments.at(pos) == QLatin1String(opt)) { + if (alreadyGiven) { + throw ArgumentErrorException(QLatin1String("option ") + opt + + QLatin1String(" was given twice.")); + } + bool isNumber; + val = m_arguments.at(++pos).toInt(&isNumber); + if (!isNumber) { + throw ArgumentErrorException(QLatin1String("option ") + opt + + QLatin1String(" needs integer argument")); + } + alreadyGiven = true; + return true; + } + return false; +} + +bool ArgumentsCollector::checkForNoProxy(int &pos, + SshConnectionParameters::ProxyType &type, bool &alreadyGiven) const +{ + if (m_arguments.at(pos) == QLatin1String("-no-proxy")) { + if (alreadyGiven) + throw ArgumentErrorException(QLatin1String("proxy setting given twice.")); + type = SshConnectionParameters::NoProxy; + alreadyGiven = true; + return true; + } + return false; +} diff --git a/tests/manual/ssh/tunnel/argumentscollector.h b/tests/manual/ssh/tunnel/argumentscollector.h new file mode 100644 index 0000000000..d7189597f1 --- /dev/null +++ b/tests/manual/ssh/tunnel/argumentscollector.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef ARGUMENTSCOLLECTOR_H +#define ARGUMENTSCOLLECTOR_H + +#include <ssh/sshconnection.h> + +#include <QStringList> + +class ArgumentsCollector +{ +public: + ArgumentsCollector(const QStringList &args); + QSsh::SshConnectionParameters collect(bool &success) const; +private: + struct ArgumentErrorException + { + ArgumentErrorException(const QString &error) : error(error) {} + const QString error; + }; + + void printUsage() const; + bool checkAndSetStringArg(int &pos, QString &arg, const char *opt) const; + bool checkAndSetIntArg(int &pos, int &val, bool &alreadyGiven, + const char *opt) const; + bool checkForNoProxy(int &pos, + QSsh::SshConnectionParameters::ProxyType &type, + bool &alreadyGiven) const; + + const QStringList m_arguments; +}; + +#endif // ARGUMENTSCOLLECTOR_H diff --git a/tests/manual/ssh/tunnel/main.cpp b/tests/manual/ssh/tunnel/main.cpp new file mode 100644 index 0000000000..b25beb46ff --- /dev/null +++ b/tests/manual/ssh/tunnel/main.cpp @@ -0,0 +1,55 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ +#include "../remoteprocess/argumentscollector.h" +#include "tunnel.h" + +#include <ssh/sshconnection.h> + +#include <QCoreApplication> +#include <QObject> +#include <QStringList> + +#include <cstdlib> +#include <iostream> + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + bool parseSuccess; + const QSsh::SshConnectionParameters ¶meters + = ArgumentsCollector(app.arguments()).collect(parseSuccess); + if (!parseSuccess) + return EXIT_FAILURE; + Tunnel tunnel(parameters); + tunnel.run(); + return app.exec(); +} diff --git a/tests/manual/ssh/tunnel/tunnel.cpp b/tests/manual/ssh/tunnel/tunnel.cpp new file mode 100644 index 0000000000..342102a881 --- /dev/null +++ b/tests/manual/ssh/tunnel/tunnel.cpp @@ -0,0 +1,165 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ +#include "tunnel.h" + +#include <ssh/sshconnection.h> +#include <ssh/sshdirecttcpiptunnel.h> + +#include <QCoreApplication> +#include <QTcpServer> +#include <QTcpSocket> +#include <QTimer> + +#include <cstdlib> +#include <iostream> + +const QByteArray ServerDataPrefix("Received the following data: "); +const QByteArray TestData("Urgsblubb?"); + +using namespace QSsh; + +Tunnel::Tunnel(const QSsh::SshConnectionParameters ¶meters, QObject *parent) + : QObject(parent), + m_connection(new SshConnection(parameters, this)), + m_tunnelServer(new QTcpServer(this)), + m_expectingChannelClose(false) +{ + connect(m_connection, SIGNAL(connected()), SLOT(handleConnected())); + connect(m_connection, SIGNAL(error(QSsh::SshError)), SLOT(handleConnectionError())); +} + +Tunnel::~Tunnel() +{ +} + +void Tunnel::run() +{ + std::cout << "Connecting to SSH server..." << std::endl; + m_connection->connectToHost(); +} + +void Tunnel::handleConnectionError() +{ + std::cerr << "SSH connection error: " << qPrintable(m_connection->errorString()) << std::endl; + qApp->exit(EXIT_FAILURE); +} + +void Tunnel::handleConnected() +{ + std::cout << "Opening server side..." << std::endl; + if (!m_tunnelServer->listen(QHostAddress::LocalHost)) { + std::cerr << "Error opening port: " + << m_tunnelServer->errorString().toLocal8Bit().constData() << std::endl; + qApp->exit(EXIT_FAILURE); + return; + } + m_forwardedPort = m_tunnelServer->serverPort(); + connect(m_tunnelServer, SIGNAL(newConnection()), SLOT(handleNewConnection())); + + m_tunnel = m_connection->createTunnel(m_forwardedPort); + 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())); + + std::cout << "Initializing tunnel..." << std::endl; + m_tunnel->initialize(); +} + +void Tunnel::handleInitialized() +{ + std::cout << "Writing data into the tunnel..." << std::endl; + m_tunnel->write(TestData); + QTimer * const timeoutTimer = new QTimer(this); + connect(timeoutTimer, SIGNAL(timeout()), SLOT(handleTimeout())); + timeoutTimer->start(10000); +} + +void Tunnel::handleServerData() +{ + m_dataReceivedFromServer += m_tunnel->readAll(); + if (m_dataReceivedFromServer == ServerDataPrefix + TestData) { + std::cout << "Data exchange successful. Closing server socket..." << std::endl; + m_expectingChannelClose = true; + m_tunnelSocket->close(); + } +} + +void Tunnel::handleTunnelError(const QString &reason) +{ + std::cerr << "Tunnel error: " << reason.toLocal8Bit().constData() << std::endl; + qApp->exit(EXIT_FAILURE); +} + +void Tunnel::handleTunnelClosed() +{ + if (m_expectingChannelClose) { + std::cout << "Successfully detected channel close." << std::endl; + std::cout << "Test finished successfully." << std::endl; + qApp->quit(); + } else { + std::cerr << "Error: Remote host closed channel." << std::endl; + qApp->exit(EXIT_FAILURE); + } +} + +void Tunnel::handleNewConnection() +{ + m_tunnelSocket = m_tunnelServer->nextPendingConnection(); + m_tunnelServer->close(); + connect(m_tunnelSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(handleSocketError())); + connect(m_tunnelSocket, SIGNAL(readyRead()), SLOT(handleClientData())); + handleClientData(); +} + +void Tunnel::handleSocketError() +{ + std::cerr << "Socket error: " << m_tunnelSocket->errorString().toLocal8Bit().constData() + << std::endl; + qApp->exit(EXIT_FAILURE); +} + +void Tunnel::handleClientData() +{ + m_dataReceivedFromClient += m_tunnelSocket->readAll(); + if (m_dataReceivedFromClient == TestData) { + std::cout << "Client data successfully received by server, now sending data to client..." + << std::endl; + m_tunnelSocket->write(ServerDataPrefix + m_dataReceivedFromClient); + } +} + +void Tunnel::handleTimeout() +{ + std::cerr << "Error: Timeout waiting for test completion." << std::endl; + qApp->exit(EXIT_FAILURE); +} diff --git a/tests/manual/ssh/tunnel/tunnel.h b/tests/manual/ssh/tunnel/tunnel.h new file mode 100644 index 0000000000..fd7ca14381 --- /dev/null +++ b/tests/manual/ssh/tunnel/tunnel.h @@ -0,0 +1,81 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ +#ifndef TUNNEL_H +#define TUNNEL_H + +#include <QObject> +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE +class QTcpServer; +class QTcpSocket; +QT_END_NAMESPACE + +namespace QSsh { +class SshConnection; +class SshConnectionParameters; +class SshDirectTcpIpTunnel; +} + +class Tunnel : public QObject +{ + Q_OBJECT +public: + Tunnel(const QSsh::SshConnectionParameters ¶meters, QObject *parent = 0); + ~Tunnel(); + + void run(); + +private slots: + void handleConnected(); + void handleConnectionError(); + void handleServerData(); + void handleInitialized(); + void handleTunnelError(const QString &reason); + void handleTunnelClosed(); + void handleNewConnection(); + void handleSocketError(); + void handleClientData(); + void handleTimeout(); + +private: + QSsh::SshConnection * const m_connection; + QSharedPointer<QSsh::SshDirectTcpIpTunnel> m_tunnel; + QTcpServer * const m_tunnelServer; + QTcpSocket *m_tunnelSocket; + quint16 m_forwardedPort; + QByteArray m_dataReceivedFromServer; + QByteArray m_dataReceivedFromClient; + bool m_expectingChannelClose; +}; + +#endif // TUNNEL_H diff --git a/tests/manual/ssh/tunnel/tunnel.pro b/tests/manual/ssh/tunnel/tunnel.pro new file mode 100644 index 0000000000..f2f960b98a --- /dev/null +++ b/tests/manual/ssh/tunnel/tunnel.pro @@ -0,0 +1,5 @@ +include(../ssh.pri) + +TARGET=tunnel +SOURCES=main.cpp tunnel.cpp argumentscollector.cpp +HEADERS=tunnel.h argumentscollector.h |