diff options
author | Keith Isdale <keith.isdale@nokia.com> | 2010-07-26 14:56:53 +1000 |
---|---|---|
committer | Keith Isdale <keith.isdale@nokia.com> | 2010-07-26 14:56:53 +1000 |
commit | 9f034793bcfc51c2b7c1dd14db806f7258f9a9eb (patch) | |
tree | 63bd0f50ce5b77828ad8205eafd7b9412810499e /coreplugin/ssh/sshchannel.cpp | |
parent | 619d92cfef29e653bfdf852e83888e50cfc4348f (diff) | |
parent | 65271649dbc90f3af1184ad1b23bdb64c0c07d07 (diff) |
Merge branch 'master' of git://git-nokia.trolltech.com.au/qtsoftware/research/qtuitest
Diffstat (limited to 'coreplugin/ssh/sshchannel.cpp')
-rw-r--r-- | coreplugin/ssh/sshchannel.cpp | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/coreplugin/ssh/sshchannel.cpp b/coreplugin/ssh/sshchannel.cpp new file mode 100644 index 0000000..6e1b9c4 --- /dev/null +++ b/coreplugin/ssh/sshchannel.cpp @@ -0,0 +1,244 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "sshchannel_p.h" + +#include "sshincomingpacket_p.h" +#include "sshsendfacility_p.h" + +#include <botan/exceptn.h> + +namespace Core { +namespace Internal { + +namespace { + const quint32 MinMaxPacketSize = 32768; + const quint32 MaxPacketSize = 16 * 1024 * 1024; + const quint32 InitialWindowSize = MaxPacketSize; + const quint32 NoChannel = 0xffffffffu; +} // anonymous namespace + +AbstractSshChannel::AbstractSshChannel(quint32 channelId, + SshSendFacility &sendFacility) + : m_sendFacility(sendFacility), m_localChannel(channelId), + m_remoteChannel(NoChannel), m_localWindowSize(InitialWindowSize), + m_remoteWindowSize(0), m_state(Inactive) +{ +} + +AbstractSshChannel::~AbstractSshChannel() +{ + +} + +void AbstractSshChannel::setChannelState(ChannelState state) +{ + m_state = state; + if (state == Closed) + closeHook(); +} + +void AbstractSshChannel::requestSessionStart() +{ + // Note: We are just being paranoid here about the Botan exceptions, + // which are extremely unlikely to happen, because if there was a problem + // with our cryptography stuff, it would have hit us before, on + // establishing the connection. + try { + m_sendFacility.sendSessionPacket(m_localChannel, InitialWindowSize, + MaxPacketSize); + setChannelState(SessionRequested); + } catch (Botan::Exception &e) { + m_errorString = QString::fromAscii(e.what()); + closeChannel(); + } +} + +void AbstractSshChannel::sendData(const QByteArray &data) +{ + try { + m_sendBuffer += data; + flushSendBuffer(); + } catch (Botan::Exception &e) { + m_errorString = QString::fromAscii(e.what()); + closeChannel(); + } +} + +void AbstractSshChannel::handleWindowAdjust(quint32 bytesToAdd) +{ + checkChannelActive(); + + const quint64 newValue = m_remoteWindowSize + bytesToAdd; + if (newValue > 0xffffffffu) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Illegal window size requested."); + } + + m_remoteWindowSize = newValue; + flushSendBuffer(); +} + +void AbstractSshChannel::flushSendBuffer() +{ + const quint32 bytesToSend + = qMin<quint32>(m_remoteWindowSize, m_sendBuffer.size()); + if (bytesToSend > 0) { + const QByteArray &data = m_sendBuffer.left(bytesToSend); + m_sendFacility.sendChannelDataPacket(m_remoteChannel, data); + m_sendBuffer.remove(0, bytesToSend); + m_remoteWindowSize -= bytesToSend; + } +} + +void AbstractSshChannel::handleOpenSuccess(quint32 remoteChannelId, + quint32 remoteWindowSize, quint32 remoteMaxPacketSize) +{ + if (m_state != SessionRequested) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Invalid SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet."); + } + + if (remoteMaxPacketSize < MinMaxPacketSize) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Maximum packet size too low."); + } + +#ifdef CREATOR_SSH_DEBUG + qDebug("Channel opened. remote channel id: %u, remote window size: %u, " + "remote max packet size: %u", + remoteChannelId, remoteWindowSize, remoteMaxPacketSize); +#endif + m_remoteChannel = remoteChannelId; + m_remoteWindowSize = remoteWindowSize; + m_remoteMaxPacketSize = remoteMaxPacketSize; + setChannelState(SessionEstablished); + handleOpenSuccessInternal(); +} + +void AbstractSshChannel::handleOpenFailure(const QString &reason) +{ + if (m_state != SessionRequested) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Invalid SSH_MSG_CHANNEL_OPEN_FAILURE packet."); + } + +#ifdef CREATOR_SSH_DEBUG + qDebug("Channel open request failed for channel %u", m_localChannel); +#endif + m_errorString = reason; + handleOpenFailureInternal(); +} + +void AbstractSshChannel::handleChannelEof() +{ + if (m_state == Inactive || m_state == Closed) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Unexpected SSH_MSG_CHANNEL_EOF message."); + } + m_localWindowSize = 0; +} + +void AbstractSshChannel::handleChannelClose() +{ +#ifdef CREATOR_SSH_DEBUG + qDebug("Receiving CLOSE for channel %u", m_localChannel); +#endif + if (channelState() == Inactive || channelState() == Closed) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Unexpected SSH_MSG_CHANNEL_CLOSE message."); + } + closeChannel(); + setChannelState(Closed); +} + +void AbstractSshChannel::handleChannelData(const QByteArray &data) +{ + const int bytesToDeliver = handleChannelOrExtendedChannelData(data); + handleChannelDataInternal(bytesToDeliver == data.size() + ? data : data.left(bytesToDeliver)); +} + +void AbstractSshChannel::handleChannelExtendedData(quint32 type, const QByteArray &data) +{ + const int bytesToDeliver = handleChannelOrExtendedChannelData(data); + handleChannelExtendedDataInternal(type, bytesToDeliver == data.size() + ? data : data.left(bytesToDeliver)); +} + +void AbstractSshChannel::handleChannelRequest(const SshIncomingPacket &packet) +{ + qWarning("Ignoring unknown request type '%s'", + packet.extractChannelRequestType().data()); +} + +int AbstractSshChannel::handleChannelOrExtendedChannelData(const QByteArray &data) +{ + checkChannelActive(); + + const int bytesToDeliver = qMin<quint32>(data.size(), maxDataSize()); + if (bytesToDeliver != data.size()) + 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); + } + return bytesToDeliver; +} + +void AbstractSshChannel::closeChannel() +{ + if (m_state != CloseRequested && m_state != Closed) { + if (m_state == Inactive) { + setChannelState(Closed); + } else { + setChannelState(CloseRequested); + m_sendFacility.sendChannelEofPacket(m_remoteChannel); + m_sendFacility.sendChannelClosePacket(m_remoteChannel); + } + } +} + +void AbstractSshChannel::checkChannelActive() +{ + if (channelState() == Inactive || channelState() == Closed) + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, + "Channel not open."); +} + +quint32 AbstractSshChannel::maxDataSize() const +{ + return qMin(m_localWindowSize, MaxPacketSize); +} + +} // namespace Internal +} // namespace Core |