forked from qt-creator/qt-creator
SSH: Introduce keep-alive mechanism.
Neither TCP nor the SSH protocol offer a built-in way to reliably notice connection loss, so we implement our own. Task-number: QTCREATORBUG-3783
This commit is contained in:
@@ -188,6 +188,8 @@ SshConnectionPrivate::SshConnectionPrivate(SshConnection *conn)
|
||||
{
|
||||
setupPacketHandlers();
|
||||
m_timeoutTimer.setSingleShot(true);
|
||||
m_keepAliveTimer.setSingleShot(true);
|
||||
m_keepAliveTimer.setInterval(10000);
|
||||
connect(m_channelManager, SIGNAL(timeout()), this, SLOT(handleTimeout()));
|
||||
}
|
||||
|
||||
@@ -255,6 +257,9 @@ void SshConnectionPrivate::setupPacketHandlers()
|
||||
<< KeyExchangeStarted << KeyExchangeSuccess
|
||||
<< UserAuthServiceRequested << UserAuthRequested
|
||||
<< ConnectionEstablished, &This::handleDisconnect);
|
||||
|
||||
setupPacketHandler(SSH_MSG_UNIMPLEMENTED,
|
||||
StateList() << ConnectionEstablished, &This::handleUnimplementedPacket);
|
||||
}
|
||||
|
||||
void SshConnectionPrivate::setupPacketHandler(SshPacketType type,
|
||||
@@ -436,6 +441,9 @@ void SshConnectionPrivate::handleUserAuthSuccessPacket()
|
||||
m_state = ConnectionEstablished;
|
||||
m_timeoutTimer.stop();
|
||||
emit connected();
|
||||
m_lastInvalidMsgSeqNr = InvalidSeqNr;
|
||||
connect(&m_keepAliveTimer, SIGNAL(timeout()), SLOT(sendKeepAlivePacket()));
|
||||
m_keepAliveTimer.start();
|
||||
}
|
||||
|
||||
void SshConnectionPrivate::handleUserAuthFailurePacket()
|
||||
@@ -452,6 +460,19 @@ void SshConnectionPrivate::handleDebugPacket()
|
||||
emit dataAvailable(msg.message);
|
||||
}
|
||||
|
||||
void SshConnectionPrivate::handleUnimplementedPacket()
|
||||
{
|
||||
const SshUnimplemented &msg = m_incomingPacket.extractUnimplemented();
|
||||
if (msg.invalidMsgSeqNr != m_lastInvalidMsgSeqNr) {
|
||||
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||
"Unexpected packet", tr("The server sent an unexpected SSH packet "
|
||||
"of type SSH_MSG_UNIMPLEMENTED."));
|
||||
}
|
||||
m_lastInvalidMsgSeqNr = InvalidSeqNr;
|
||||
m_timeoutTimer.stop();
|
||||
m_keepAliveTimer.start();
|
||||
}
|
||||
|
||||
void SshConnectionPrivate::handleChannelRequest()
|
||||
{
|
||||
m_channelManager->handleChannelRequest(m_incomingPacket);
|
||||
@@ -514,6 +535,8 @@ void SshConnectionPrivate::handleDisconnect()
|
||||
"", tr("Server closed connection: %1").arg(msg.description));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SshConnectionPrivate::sendData(const QByteArray &data)
|
||||
{
|
||||
if (canUseSocket())
|
||||
@@ -541,6 +564,14 @@ void SshConnectionPrivate::handleTimeout()
|
||||
tr("Timeout waiting for reply from server."));
|
||||
}
|
||||
|
||||
void SshConnectionPrivate::sendKeepAlivePacket()
|
||||
{
|
||||
Q_ASSERT(m_lastInvalidMsgSeqNr == InvalidSeqNr);
|
||||
m_lastInvalidMsgSeqNr = m_sendFacility.nextClientSeqNr();
|
||||
m_sendFacility.sendInvalidPacket();
|
||||
m_timeoutTimer.start(5000);
|
||||
}
|
||||
|
||||
void SshConnectionPrivate::connectToHost(const SshConnectionParameters &serverInfo)
|
||||
{
|
||||
m_incomingData.clear();
|
||||
@@ -577,6 +608,8 @@ void SshConnectionPrivate::closeConnection(SshErrorCode sshError,
|
||||
m_timeoutTimer.stop();
|
||||
disconnect(m_socket, 0, this, 0);
|
||||
disconnect(&m_timeoutTimer, 0, this, 0);
|
||||
m_keepAliveTimer.stop();
|
||||
disconnect(&m_keepAliveTimer, 0, this, 0);
|
||||
try {
|
||||
m_channelManager->closeAllChannels();
|
||||
m_sendFacility.sendDisconnectPacket(sshError, serverErrorString);
|
||||
@@ -606,5 +639,7 @@ QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel()
|
||||
return m_channelManager->createSftpChannel();
|
||||
}
|
||||
|
||||
const quint64 SshConnectionPrivate::InvalidSeqNr = static_cast<quint64>(-1);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Core
|
||||
|
||||
@@ -102,6 +102,7 @@ private:
|
||||
Q_SLOT void handleSocketError();
|
||||
Q_SLOT void handleSocketDisconnected();
|
||||
Q_SLOT void handleTimeout();
|
||||
Q_SLOT void sendKeepAlivePacket();
|
||||
|
||||
void handleServerId();
|
||||
void handlePackets();
|
||||
@@ -116,6 +117,7 @@ private:
|
||||
void handleUserAuthBannerPacket();
|
||||
void handleGlobalRequest();
|
||||
void handleDebugPacket();
|
||||
void handleUnimplementedPacket();
|
||||
void handleChannelRequest();
|
||||
void handleChannelOpen();
|
||||
void handleChannelOpenFailure();
|
||||
@@ -141,6 +143,8 @@ private:
|
||||
typedef QPair<StateList, PacketHandler> HandlerInStates;
|
||||
QHash<SshPacketType, HandlerInStates> m_packetHandlers;
|
||||
|
||||
static const quint64 InvalidSeqNr;
|
||||
|
||||
QTcpSocket *m_socket;
|
||||
SshStateInternal m_state;
|
||||
SshIncomingPacket m_incomingPacket;
|
||||
@@ -152,8 +156,10 @@ private:
|
||||
QString m_errorString;
|
||||
QScopedPointer<SshKeyExchange> m_keyExchange;
|
||||
QTimer m_timeoutTimer;
|
||||
QTimer m_keepAliveTimer;
|
||||
bool m_ignoreNextPacket;
|
||||
SshConnection *m_conn;
|
||||
quint64 m_lastInvalidMsgSeqNr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -257,7 +257,23 @@ SshDebug SshIncomingPacket::extractDebug() const
|
||||
return msg;
|
||||
} catch (SshPacketParseException &) {
|
||||
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||
"Invalid SSH_MSG_USERAUTH_BANNER.");
|
||||
"Invalid SSH_MSG_DEBUG.");
|
||||
}
|
||||
}
|
||||
|
||||
SshUnimplemented SshIncomingPacket::extractUnimplemented() const
|
||||
{
|
||||
Q_ASSERT(isComplete());
|
||||
Q_ASSERT(type() == SSH_MSG_UNIMPLEMENTED);
|
||||
|
||||
try {
|
||||
SshUnimplemented msg;
|
||||
quint32 offset = TypeOffset + 1;
|
||||
msg.invalidMsgSeqNr = SshPacketParser::asUint32(m_data, &offset);
|
||||
return msg;
|
||||
} catch (SshPacketParseException &) {
|
||||
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||
"Invalid SSH_MSG_UNIMPLEMENTED.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,11 @@ struct SshDebug
|
||||
QByteArray language;
|
||||
};
|
||||
|
||||
struct SshUnimplemented
|
||||
{
|
||||
quint32 invalidMsgSeqNr;
|
||||
};
|
||||
|
||||
struct SshChannelOpenFailure
|
||||
{
|
||||
quint32 localChannel;
|
||||
@@ -156,6 +161,7 @@ public:
|
||||
SshDisconnect extractDisconnect() const;
|
||||
SshUserAuthBanner extractUserAuthBanner() const;
|
||||
SshDebug extractDebug() const;
|
||||
SshUnimplemented extractUnimplemented() const;
|
||||
|
||||
SshChannelOpenFailure extractChannelOpenFailure() const;
|
||||
SshChannelOpenConfirmation extractChannelOpenConfirmation() const;
|
||||
|
||||
@@ -130,6 +130,16 @@ void SshOutgoingPacket::generateRequestFailurePacket()
|
||||
init(SSH_MSG_REQUEST_FAILURE).finalize();
|
||||
}
|
||||
|
||||
void SshOutgoingPacket::generateIgnorePacket()
|
||||
{
|
||||
init(SSH_MSG_IGNORE).finalize();
|
||||
}
|
||||
|
||||
void SshOutgoingPacket::generateInvalidMessagePacket()
|
||||
{
|
||||
init(SSH_MSG_INVALID).finalize();
|
||||
}
|
||||
|
||||
void SshOutgoingPacket::generateSessionPacket(quint32 channelId,
|
||||
quint32 windowSize, quint32 maxPacketSize)
|
||||
{
|
||||
|
||||
@@ -59,6 +59,8 @@ public:
|
||||
void generateUserAuthByKeyRequestPacket(const QByteArray &user,
|
||||
const QByteArray &service);
|
||||
void generateRequestFailurePacket();
|
||||
void generateIgnorePacket();
|
||||
void generateInvalidMessagePacket();
|
||||
void generateSessionPacket(quint32 channelId, quint32 windowSize,
|
||||
quint32 maxPacketSize);
|
||||
void generateEnvPacket(quint32 remoteChannel, const QByteArray &var,
|
||||
|
||||
@@ -79,7 +79,12 @@ enum SshPacketType {
|
||||
SSH_MSG_CHANNEL_CLOSE = 97,
|
||||
SSH_MSG_CHANNEL_REQUEST = 98,
|
||||
SSH_MSG_CHANNEL_SUCCESS = 99,
|
||||
SSH_MSG_CHANNEL_FAILURE = 100
|
||||
SSH_MSG_CHANNEL_FAILURE = 100,
|
||||
|
||||
// Not completely safe, since the server may actually understand this
|
||||
// message type as an extension. Switch to a different value in that case
|
||||
// (between 128 and 191).
|
||||
SSH_MSG_INVALID = 128
|
||||
};
|
||||
|
||||
enum SshExtendedDataType { SSH_EXTENDED_DATA_STDERR = 1 };
|
||||
|
||||
@@ -133,6 +133,18 @@ void SshSendFacility::sendRequestFailurePacket()
|
||||
sendPacket();
|
||||
}
|
||||
|
||||
void SshSendFacility::sendIgnorePacket()
|
||||
{
|
||||
m_outgoingPacket.generateIgnorePacket();
|
||||
sendPacket();
|
||||
}
|
||||
|
||||
void SshSendFacility::sendInvalidPacket()
|
||||
{
|
||||
m_outgoingPacket.generateInvalidMessagePacket();
|
||||
sendPacket();
|
||||
}
|
||||
|
||||
void SshSendFacility::sendSessionPacket(quint32 channelId, quint32 windowSize,
|
||||
quint32 maxPacketSize)
|
||||
{
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
void sendUserAuthByKeyRequestPacket(const QByteArray &user,
|
||||
const QByteArray &service);
|
||||
void sendRequestFailurePacket();
|
||||
void sendIgnorePacket();
|
||||
void sendInvalidPacket();
|
||||
void sendSessionPacket(quint32 channelId, quint32 windowSize,
|
||||
quint32 maxPacketSize);
|
||||
void sendEnvPacket(quint32 remoteChannel, const QByteArray &var,
|
||||
@@ -78,6 +80,7 @@ public:
|
||||
const QByteArray &signalName);
|
||||
void sendChannelEofPacket(quint32 remoteChannel);
|
||||
void sendChannelClosePacket(quint32 remoteChannel);
|
||||
quint32 nextClientSeqNr() const { return m_clientSeqNr; }
|
||||
|
||||
private:
|
||||
void sendPacket();
|
||||
|
||||
Reference in New Issue
Block a user