2010-07-12 09:33:22 +02:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
|
|
|
|
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
2010-04-26 11:43:25 +02:00
|
|
|
**
|
|
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** No Commercial Usage
|
2010-04-26 11:43:25 +02:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** This file contains pre-release code and may not be distributed.
|
|
|
|
|
** You may use this file in accordance with the terms and conditions
|
|
|
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
|
|
|
** this package.
|
2010-04-26 11:43:25 +02:00
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
2010-07-12 09:33:22 +02:00
|
|
|
**
|
2010-04-26 11:43:25 +02:00
|
|
|
** 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.
|
|
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** 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.
|
|
|
|
|
**
|
|
|
|
|
** If you have questions regarding the use of this file, please contact
|
|
|
|
|
** Nokia at qt-info@nokia.com.
|
2010-04-26 11:43:25 +02:00
|
|
|
**
|
2010-07-12 09:33:22 +02:00
|
|
|
**************************************************************************/
|
2010-04-26 11:43:25 +02:00
|
|
|
|
|
|
|
|
#include "sshconnection.h"
|
2010-07-12 09:33:22 +02:00
|
|
|
#include "sshconnection_p.h"
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
#include "sftpchannel.h"
|
|
|
|
|
#include "sshcapabilities_p.h"
|
|
|
|
|
#include "sshchannelmanager_p.h"
|
|
|
|
|
#include "sshcryptofacility_p.h"
|
|
|
|
|
#include "sshexception_p.h"
|
|
|
|
|
#include "sshkeyexchange_p.h"
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2011-01-03 12:04:33 +01:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
#include <botan/exceptn.h>
|
|
|
|
|
#include <botan/init.h>
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
#include <QtCore/QFile>
|
|
|
|
|
#include <QtCore/QMutex>
|
2010-09-27 17:28:38 +02:00
|
|
|
#include <QtNetwork/QNetworkProxy>
|
2010-07-12 09:33:22 +02:00
|
|
|
#include <QtNetwork/QTcpSocket>
|
2010-04-26 11:43:25 +02:00
|
|
|
|
|
|
|
|
namespace Core {
|
|
|
|
|
|
|
|
|
|
namespace {
|
2010-07-12 09:33:22 +02:00
|
|
|
const QByteArray ClientId("SSH-2.0-QtCreator\r\n");
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
bool staticInitializationsDone = false;
|
|
|
|
|
QMutex staticInitMutex;
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void doStaticInitializationsIfNecessary()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
if (!staticInitializationsDone) {
|
|
|
|
|
staticInitMutex.lock();
|
|
|
|
|
if (!staticInitializationsDone) {
|
|
|
|
|
Botan::LibraryInitializer::initialize("thread_safe=true");
|
2010-10-06 10:08:39 +02:00
|
|
|
qRegisterMetaType<Core::SshError>("Core::SshError");
|
2010-08-17 12:37:41 +02:00
|
|
|
qRegisterMetaType<Core::SftpJobId>("Core::SftpJobId");
|
2010-07-12 09:33:22 +02:00
|
|
|
staticInitializationsDone = true;
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
2010-07-12 09:33:22 +02:00
|
|
|
staticInitMutex.unlock();
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-08-24 10:51:31 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
|
2010-09-27 17:28:38 +02:00
|
|
|
SshConnectionParameters::SshConnectionParameters(ProxyType proxyType) :
|
|
|
|
|
timeout(0), authType(AuthByKey), port(0), proxyType(proxyType)
|
2010-08-24 10:51:31 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool equals(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
|
|
|
|
|
{
|
|
|
|
|
return p1.host == p2.host && p1.uname == p2.uname
|
|
|
|
|
&& p1.authType == p2.authType
|
|
|
|
|
&& (p1.authType == SshConnectionParameters::AuthByPwd ?
|
|
|
|
|
p1.pwd == p2.pwd : p1.privateKeyFile == p2.privateKeyFile)
|
|
|
|
|
&& p1.timeout == p2.timeout && p1.port == p2.port;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CORE_EXPORT bool operator==(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
|
|
|
|
|
{
|
|
|
|
|
return equals(p1, p2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CORE_EXPORT bool operator!=(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
|
|
|
|
|
{
|
|
|
|
|
return !equals(p1, p2);
|
2010-07-12 09:33:22 +02:00
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
// TODO: Mechanism for checking the host key. First connection to host: save, later: compare
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
SshConnection::Ptr SshConnection::create()
|
|
|
|
|
{
|
|
|
|
|
doStaticInitializationsIfNecessary();
|
|
|
|
|
return Ptr(new SshConnection);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
SshConnection::SshConnection() : d(new Internal::SshConnectionPrivate(this))
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
connect(d, SIGNAL(connected()), this, SIGNAL(connected()),
|
|
|
|
|
Qt::QueuedConnection);
|
|
|
|
|
connect(d, SIGNAL(dataAvailable(QString)), this,
|
|
|
|
|
SIGNAL(dataAvailable(QString)), Qt::QueuedConnection);
|
|
|
|
|
connect(d, SIGNAL(disconnected()), this, SIGNAL(disconnected()),
|
|
|
|
|
Qt::QueuedConnection);
|
2010-10-06 10:08:39 +02:00
|
|
|
connect(d, SIGNAL(error(Core::SshError)), this,
|
|
|
|
|
SIGNAL(error(Core::SshError)), Qt::QueuedConnection);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnection::connectToHost(const SshConnectionParameters &serverInfo)
|
|
|
|
|
{
|
|
|
|
|
d->connectToHost(serverInfo);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnection::disconnectFromHost()
|
|
|
|
|
{
|
|
|
|
|
d->closeConnection(Internal::SSH_DISCONNECT_BY_APPLICATION, SshNoError, "",
|
|
|
|
|
QString());
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
SshConnection::State SshConnection::state() const
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
switch (d->state()) {
|
|
|
|
|
case Internal::SocketUnconnected:
|
|
|
|
|
return Unconnected;
|
|
|
|
|
case Internal::ConnectionEstablished:
|
|
|
|
|
return Connected;
|
|
|
|
|
default:
|
|
|
|
|
return Connecting;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
SshError SshConnection::errorState() const
|
|
|
|
|
{
|
|
|
|
|
return d->error();
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
QString SshConnection::errorString() const
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
return d->errorString();
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
SshConnectionParameters SshConnection::connectionParameters() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_connParams;
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
SshConnection::~SshConnection()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
disconnect();
|
|
|
|
|
disconnectFromHost();
|
|
|
|
|
delete d;
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
QSharedPointer<SshRemoteProcess> SshConnection::createRemoteProcess(const QByteArray &command)
|
|
|
|
|
{
|
2011-01-03 12:04:33 +01:00
|
|
|
QTC_ASSERT(state() == Connected, return QSharedPointer<SshRemoteProcess>());
|
|
|
|
|
return d->createRemoteProcess(command);
|
2010-07-12 09:33:22 +02:00
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
QSharedPointer<SftpChannel> SshConnection::createSftpChannel()
|
|
|
|
|
{
|
2011-01-03 12:04:33 +01:00
|
|
|
QTC_ASSERT(state() == Connected, return QSharedPointer<SftpChannel>());
|
|
|
|
|
return d->createSftpChannel();
|
2010-07-12 09:33:22 +02:00
|
|
|
}
|
2010-05-05 10:06:31 +02:00
|
|
|
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
namespace Internal {
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
SshConnectionPrivate::SshConnectionPrivate(SshConnection *conn)
|
|
|
|
|
: m_socket(new QTcpSocket(this)), m_state(SocketUnconnected),
|
|
|
|
|
m_sendFacility(m_socket),
|
2010-08-16 22:10:34 +02:00
|
|
|
m_channelManager(new SshChannelManager(m_sendFacility, this)),
|
2010-09-27 17:28:38 +02:00
|
|
|
m_connParams(SshConnectionParameters::DefaultProxy),
|
2010-07-12 09:33:22 +02:00
|
|
|
m_error(SshNoError), m_ignoreNextPacket(false), m_conn(conn)
|
|
|
|
|
{
|
|
|
|
|
setupPacketHandlers();
|
2010-08-16 22:10:34 +02:00
|
|
|
m_timeoutTimer.setSingleShot(true);
|
|
|
|
|
connect(m_channelManager, SIGNAL(timeout()), this, SLOT(handleTimeout()));
|
2010-07-12 09:33:22 +02:00
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
SshConnectionPrivate::~SshConnectionPrivate()
|
|
|
|
|
{
|
|
|
|
|
disconnect();
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::setupPacketHandlers()
|
|
|
|
|
{
|
|
|
|
|
typedef SshConnectionPrivate This;
|
|
|
|
|
|
|
|
|
|
setupPacketHandler(SSH_MSG_KEXINIT, StateList() << SocketConnected,
|
|
|
|
|
&This::handleKeyExchangeInitPacket);
|
|
|
|
|
setupPacketHandler(SSH_MSG_KEXDH_REPLY, StateList() << KeyExchangeStarted,
|
|
|
|
|
&This::handleKeyExchangeReplyPacket);
|
|
|
|
|
|
|
|
|
|
setupPacketHandler(SSH_MSG_NEWKEYS, StateList() << KeyExchangeSuccess,
|
|
|
|
|
&This::handleNewKeysPacket);
|
|
|
|
|
setupPacketHandler(SSH_MSG_SERVICE_ACCEPT,
|
|
|
|
|
StateList() << UserAuthServiceRequested,
|
|
|
|
|
&This::handleServiceAcceptPacket);
|
|
|
|
|
setupPacketHandler(SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
|
|
|
|
|
StateList() << UserAuthRequested, &This::handlePasswordExpiredPacket);
|
|
|
|
|
setupPacketHandler(SSH_MSG_GLOBAL_REQUEST,
|
|
|
|
|
StateList() << ConnectionEstablished, &This::handleGlobalRequest);
|
|
|
|
|
|
|
|
|
|
const StateList authReqList = StateList() << UserAuthRequested;
|
|
|
|
|
setupPacketHandler(SSH_MSG_USERAUTH_BANNER, authReqList,
|
|
|
|
|
&This::handleUserAuthBannerPacket);
|
|
|
|
|
setupPacketHandler(SSH_MSG_USERAUTH_SUCCESS, authReqList,
|
|
|
|
|
&This::handleUserAuthSuccessPacket);
|
|
|
|
|
setupPacketHandler(SSH_MSG_USERAUTH_FAILURE, authReqList,
|
|
|
|
|
&This::handleUserAuthFailurePacket);
|
|
|
|
|
|
|
|
|
|
const StateList connectedList
|
|
|
|
|
= StateList() << ConnectionEstablished;
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_REQUEST, connectedList,
|
|
|
|
|
&This::handleChannelRequest);
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_OPEN, connectedList,
|
|
|
|
|
&This::handleChannelOpen);
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_OPEN_FAILURE, connectedList,
|
|
|
|
|
&This::handleChannelOpenFailure);
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, connectedList,
|
|
|
|
|
&This::handleChannelOpenConfirmation);
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_SUCCESS, connectedList,
|
|
|
|
|
&This::handleChannelSuccess);
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_FAILURE, connectedList,
|
|
|
|
|
&This::handleChannelFailure);
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_WINDOW_ADJUST, connectedList,
|
|
|
|
|
&This::handleChannelWindowAdjust);
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_DATA, connectedList,
|
|
|
|
|
&This::handleChannelData);
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_EXTENDED_DATA, connectedList,
|
|
|
|
|
&This::handleChannelExtendedData);
|
|
|
|
|
|
|
|
|
|
const StateList connectedOrClosedList
|
|
|
|
|
= StateList() << SocketUnconnected << ConnectionEstablished;
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_EOF, connectedOrClosedList,
|
|
|
|
|
&This::handleChannelEof);
|
|
|
|
|
setupPacketHandler(SSH_MSG_CHANNEL_CLOSE, connectedOrClosedList,
|
|
|
|
|
&This::handleChannelClose);
|
|
|
|
|
|
|
|
|
|
setupPacketHandler(SSH_MSG_DISCONNECT, StateList() << SocketConnected
|
|
|
|
|
<< KeyExchangeStarted << KeyExchangeSuccess
|
|
|
|
|
<< UserAuthServiceRequested << UserAuthRequested
|
|
|
|
|
<< ConnectionEstablished, &This::handleDisconnect);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::setupPacketHandler(SshPacketType type,
|
|
|
|
|
const SshConnectionPrivate::StateList &states,
|
|
|
|
|
SshConnectionPrivate::PacketHandler handler)
|
|
|
|
|
{
|
|
|
|
|
m_packetHandlers.insert(type, HandlerInStates(states, handler));
|
|
|
|
|
}
|
2010-05-05 10:06:31 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleSocketConnected()
|
2010-05-05 10:06:31 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_state = SocketConnected;
|
|
|
|
|
sendData(ClientId);
|
2010-05-05 10:06:31 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleIncomingData()
|
|
|
|
|
{
|
|
|
|
|
if (m_state == SocketUnconnected)
|
|
|
|
|
return; // For stuff queued in the event loop after we've called closeConnection();
|
|
|
|
|
|
|
|
|
|
try {
|
2010-08-16 12:40:55 +02:00
|
|
|
if (!canUseSocket())
|
|
|
|
|
return;
|
2010-07-12 09:33:22 +02:00
|
|
|
m_incomingData += m_socket->readAll();
|
|
|
|
|
#ifdef CREATOR_SSH_DEBUG
|
|
|
|
|
qDebug("state = %d, remote data size = %d", m_state,
|
|
|
|
|
m_incomingData.count());
|
|
|
|
|
#endif
|
|
|
|
|
if (m_state == SocketConnected)
|
|
|
|
|
handleServerId();
|
|
|
|
|
handlePackets();
|
|
|
|
|
} catch (SshServerException &e) {
|
|
|
|
|
closeConnection(e.error, SshProtocolError, e.errorStringServer,
|
|
|
|
|
tr("SSH Protocol error: %1").arg(e.errorStringUser));
|
|
|
|
|
} catch (SshClientException &e) {
|
|
|
|
|
closeConnection(SSH_DISCONNECT_BY_APPLICATION, e.error, "",
|
|
|
|
|
e.errorString);
|
|
|
|
|
} catch (Botan::Exception &e) {
|
|
|
|
|
closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshInternalError, "",
|
2010-09-15 15:16:13 +02:00
|
|
|
tr("Botan library exception: %1").arg(e.what()));
|
2010-07-12 09:33:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-05-05 10:06:31 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleServerId()
|
|
|
|
|
{
|
|
|
|
|
const int idOffset = m_incomingData.indexOf("SSH-");
|
|
|
|
|
if (idOffset == -1)
|
|
|
|
|
return;
|
|
|
|
|
m_incomingData.remove(0, idOffset);
|
|
|
|
|
if (m_incomingData.size() < 7)
|
|
|
|
|
return;
|
|
|
|
|
const QByteArray &version = m_incomingData.mid(4, 3);
|
|
|
|
|
if (version != "2.0") {
|
|
|
|
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
|
|
|
|
|
"Invalid protocol version.",
|
|
|
|
|
tr("Invalid protocol version: Expected '2.0', got '%1'.")
|
|
|
|
|
.arg(SshPacketParser::asUserString(version)));
|
|
|
|
|
}
|
|
|
|
|
const int endOffset = m_incomingData.indexOf("\r\n");
|
|
|
|
|
if (endOffset == -1)
|
|
|
|
|
return;
|
|
|
|
|
if (m_incomingData.at(7) != '-') {
|
|
|
|
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
|
|
|
|
"Invalid server id.", tr("Invalid server id '%1'.")
|
|
|
|
|
.arg(SshPacketParser::asUserString(m_incomingData)));
|
|
|
|
|
}
|
2010-05-05 10:06:31 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
m_keyExchange.reset(new SshKeyExchange(m_sendFacility));
|
|
|
|
|
m_keyExchange->sendKexInitPacket(m_incomingData.left(endOffset));
|
|
|
|
|
m_incomingData.remove(0, endOffset + 2);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handlePackets()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_incomingPacket.consumeData(m_incomingData);
|
|
|
|
|
while (m_incomingPacket.isComplete()) {
|
|
|
|
|
handleCurrentPacket();
|
|
|
|
|
m_incomingPacket.clear();
|
|
|
|
|
m_incomingPacket.consumeData(m_incomingData);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleCurrentPacket()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
Q_ASSERT(m_incomingPacket.isComplete());
|
|
|
|
|
Q_ASSERT(m_state == KeyExchangeStarted || !m_ignoreNextPacket);
|
2010-05-20 16:02:58 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
if (m_ignoreNextPacket) {
|
|
|
|
|
m_ignoreNextPacket = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
QHash<SshPacketType, HandlerInStates>::ConstIterator it
|
|
|
|
|
= m_packetHandlers.find(m_incomingPacket.type());
|
|
|
|
|
if (it == m_packetHandlers.end()) {
|
|
|
|
|
m_sendFacility.sendMsgUnimplementedPacket(m_incomingPacket.serverSeqNr());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!it.value().first.contains(m_state)) {
|
|
|
|
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
|
|
|
|
"Unexpected packet.", tr("Unexpected packet of type %1.")
|
|
|
|
|
.arg(m_incomingPacket.type()));
|
|
|
|
|
}
|
|
|
|
|
(this->*it.value().second)();
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleKeyExchangeInitPacket()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
// If the server sends a guessed packet, the guess must be wrong,
|
|
|
|
|
// because the algorithms we support requires us to initiate the
|
|
|
|
|
// key exchange.
|
|
|
|
|
if (m_keyExchange->sendDhInitPacket(m_incomingPacket))
|
|
|
|
|
m_ignoreNextPacket = true;
|
|
|
|
|
m_state = KeyExchangeStarted;
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleKeyExchangeReplyPacket()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_keyExchange->sendNewKeysPacket(m_incomingPacket,
|
|
|
|
|
ClientId.left(ClientId.size() - 2));
|
|
|
|
|
m_sendFacility.recreateKeys(*m_keyExchange);
|
|
|
|
|
m_state = KeyExchangeSuccess;
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleNewKeysPacket()
|
2010-05-10 11:01:56 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_incomingPacket.recreateKeys(*m_keyExchange);
|
|
|
|
|
m_keyExchange.reset();
|
|
|
|
|
m_sendFacility.sendUserAuthServiceRequestPacket();
|
|
|
|
|
m_state = UserAuthServiceRequested;
|
2010-05-10 11:01:56 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleServiceAcceptPacket()
|
|
|
|
|
{
|
|
|
|
|
if (m_connParams.authType == SshConnectionParameters::AuthByPwd) {
|
|
|
|
|
m_sendFacility.sendUserAuthByPwdRequestPacket(m_connParams.uname.toUtf8(),
|
|
|
|
|
SshCapabilities::SshConnectionService, m_connParams.pwd.toUtf8());
|
|
|
|
|
} else {
|
|
|
|
|
QFile privKeyFile(m_connParams.privateKeyFile);
|
|
|
|
|
bool couldOpen = privKeyFile.open(QIODevice::ReadOnly);
|
|
|
|
|
QByteArray contents;
|
|
|
|
|
if (couldOpen)
|
|
|
|
|
contents = privKeyFile.readAll();
|
|
|
|
|
if (!couldOpen || privKeyFile.error() != QFile::NoError) {
|
|
|
|
|
throw SshClientException(SshKeyFileError,
|
|
|
|
|
tr("Could not read private key file: %1")
|
|
|
|
|
.arg(privKeyFile.errorString()));
|
|
|
|
|
}
|
2010-05-10 11:01:56 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
m_sendFacility.createAuthenticationKey(contents);
|
|
|
|
|
m_sendFacility.sendUserAuthByKeyRequestPacket(m_connParams.uname.toUtf8(),
|
|
|
|
|
SshCapabilities::SshConnectionService);
|
|
|
|
|
}
|
|
|
|
|
m_state = UserAuthRequested;
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handlePasswordExpiredPacket()
|
2010-05-20 16:02:58 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
if (m_connParams.authType == SshConnectionParameters::AuthByKey) {
|
|
|
|
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
|
|
|
|
"Got SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, but did not use password.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw SshClientException(SshAuthenticationError, tr("Password expired."));
|
2010-05-20 16:02:58 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleUserAuthBannerPacket()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
emit dataAvailable(m_incomingPacket.extractUserAuthBanner().message);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleGlobalRequest()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_sendFacility.sendRequestFailurePacket();
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleUserAuthSuccessPacket()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_state = ConnectionEstablished;
|
|
|
|
|
m_timeoutTimer.stop();
|
|
|
|
|
emit connected();
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleUserAuthFailurePacket()
|
|
|
|
|
{
|
2010-08-17 10:47:36 +02:00
|
|
|
m_timeoutTimer.stop();
|
2010-07-12 09:33:22 +02:00
|
|
|
const QString errorMsg = m_connParams.authType == SshConnectionParameters::AuthByPwd
|
|
|
|
|
? tr("Server rejected password.") : tr("Server rejected key.");
|
|
|
|
|
throw SshClientException(SshAuthenticationError, errorMsg);
|
|
|
|
|
}
|
|
|
|
|
void SshConnectionPrivate::handleDebugPacket()
|
|
|
|
|
{
|
|
|
|
|
const SshDebug &msg = m_incomingPacket.extractDebug();
|
|
|
|
|
if (msg.display)
|
|
|
|
|
emit dataAvailable(msg.message);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelRequest()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_channelManager->handleChannelRequest(m_incomingPacket);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelOpen()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_channelManager->handleChannelOpen(m_incomingPacket);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelOpenFailure()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_channelManager->handleChannelOpenFailure(m_incomingPacket);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelOpenConfirmation()
|
|
|
|
|
{
|
|
|
|
|
m_channelManager->handleChannelOpenConfirmation(m_incomingPacket);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelSuccess()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_channelManager->handleChannelSuccess(m_incomingPacket);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelFailure()
|
|
|
|
|
{
|
|
|
|
|
m_channelManager->handleChannelFailure(m_incomingPacket);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelWindowAdjust()
|
|
|
|
|
{
|
|
|
|
|
m_channelManager->handleChannelWindowAdjust(m_incomingPacket);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelData()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_channelManager->handleChannelData(m_incomingPacket);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelExtendedData()
|
|
|
|
|
{
|
|
|
|
|
m_channelManager->handleChannelExtendedData(m_incomingPacket);
|
|
|
|
|
}
|
2010-04-26 11:43:25 +02:00
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelEof()
|
|
|
|
|
{
|
|
|
|
|
m_channelManager->handleChannelEof(m_incomingPacket);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleChannelClose()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
m_channelManager->handleChannelClose(m_incomingPacket);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleDisconnect()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
const SshDisconnect msg = m_incomingPacket.extractDisconnect();
|
|
|
|
|
throw SshServerException(SSH_DISCONNECT_CONNECTION_LOST,
|
|
|
|
|
"", tr("Server closed connection: %1").arg(msg.description));
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::sendData(const QByteArray &data)
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-08-16 12:40:55 +02:00
|
|
|
if (canUseSocket())
|
|
|
|
|
m_socket->write(data);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleSocketDisconnected()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshClosedByServerError,
|
|
|
|
|
"Connection closed unexpectedly.",
|
|
|
|
|
tr("Connection closed unexpectedly."));
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleSocketError()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
if (m_error == SshNoError) {
|
|
|
|
|
closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshSocketError,
|
|
|
|
|
"Network error", m_socket->errorString());
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::handleTimeout()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-08-16 22:10:34 +02:00
|
|
|
closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshTimeoutError, "",
|
|
|
|
|
tr("Timeout waiting for reply from server."));
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::connectToHost(const SshConnectionParameters &serverInfo)
|
|
|
|
|
{
|
|
|
|
|
m_incomingData.clear();
|
|
|
|
|
m_incomingPacket.reset();
|
|
|
|
|
m_sendFacility.reset();
|
|
|
|
|
m_error = SshNoError;
|
|
|
|
|
m_ignoreNextPacket = false;
|
|
|
|
|
m_errorString.clear();
|
|
|
|
|
connect(m_socket, SIGNAL(connected()), this, SLOT(handleSocketConnected()));
|
|
|
|
|
connect(m_socket, SIGNAL(readyRead()), this, SLOT(handleIncomingData()));
|
|
|
|
|
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this,
|
|
|
|
|
SLOT(handleSocketError()));
|
|
|
|
|
connect(m_socket, SIGNAL(disconnected()), this,
|
|
|
|
|
SLOT(handleSocketDisconnected()));
|
2010-08-16 22:10:34 +02:00
|
|
|
connect(&m_timeoutTimer, SIGNAL(timeout()), this, SLOT(handleTimeout()));
|
2010-07-12 09:33:22 +02:00
|
|
|
this->m_connParams = serverInfo;
|
|
|
|
|
m_state = SocketConnecting;
|
|
|
|
|
m_timeoutTimer.start(m_connParams.timeout * 1000);
|
2010-09-27 17:28:38 +02:00
|
|
|
m_socket->setProxy(m_connParams.proxyType == SshConnectionParameters::DefaultProxy
|
|
|
|
|
? QNetworkProxy::DefaultProxy : QNetworkProxy::NoProxy);
|
2010-07-12 09:33:22 +02:00
|
|
|
m_socket->connectToHost(serverInfo.host, serverInfo.port);
|
2010-05-20 16:02:58 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
void SshConnectionPrivate::closeConnection(SshErrorCode sshError,
|
|
|
|
|
SshError userError, const QByteArray &serverErrorString,
|
|
|
|
|
const QString &userErrorString)
|
|
|
|
|
{
|
|
|
|
|
// Prevent endless loops by recursive exceptions.
|
|
|
|
|
if (m_state == SocketUnconnected || m_error != SshNoError)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_error = userError;
|
|
|
|
|
m_errorString = userErrorString;
|
|
|
|
|
m_timeoutTimer.stop();
|
|
|
|
|
disconnect(m_socket, 0, this, 0);
|
2010-08-16 22:10:34 +02:00
|
|
|
disconnect(&m_timeoutTimer, 0, this, 0);
|
2010-07-12 09:33:22 +02:00
|
|
|
try {
|
|
|
|
|
m_channelManager->closeAllChannels();
|
|
|
|
|
m_sendFacility.sendDisconnectPacket(sshError, serverErrorString);
|
|
|
|
|
} catch (Botan::Exception &) {} // Nothing sensible to be done here.
|
|
|
|
|
if (m_error != SshNoError)
|
|
|
|
|
emit error(userError);
|
|
|
|
|
if (m_state == ConnectionEstablished)
|
|
|
|
|
emit disconnected();
|
2010-08-16 12:40:55 +02:00
|
|
|
if (canUseSocket())
|
|
|
|
|
m_socket->disconnectFromHost();
|
2010-07-12 09:33:22 +02:00
|
|
|
m_state = SocketUnconnected;
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-08-16 12:40:55 +02:00
|
|
|
bool SshConnectionPrivate::canUseSocket() const
|
|
|
|
|
{
|
|
|
|
|
return m_socket->isValid()
|
|
|
|
|
&& m_socket->state() == QAbstractSocket::ConnectedState;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
QSharedPointer<SshRemoteProcess> SshConnectionPrivate::createRemoteProcess(const QByteArray &command)
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
return m_channelManager->createRemoteProcess(command);
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel()
|
2010-04-26 11:43:25 +02:00
|
|
|
{
|
2010-07-12 09:33:22 +02:00
|
|
|
return m_channelManager->createSftpChannel();
|
2010-04-26 11:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 09:33:22 +02:00
|
|
|
} // namespace Internal
|
2010-04-26 11:43:25 +02:00
|
|
|
} // namespace Core
|