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();
|
setupPacketHandlers();
|
||||||
m_timeoutTimer.setSingleShot(true);
|
m_timeoutTimer.setSingleShot(true);
|
||||||
|
m_keepAliveTimer.setSingleShot(true);
|
||||||
|
m_keepAliveTimer.setInterval(10000);
|
||||||
connect(m_channelManager, SIGNAL(timeout()), this, SLOT(handleTimeout()));
|
connect(m_channelManager, SIGNAL(timeout()), this, SLOT(handleTimeout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,6 +257,9 @@ void SshConnectionPrivate::setupPacketHandlers()
|
|||||||
<< KeyExchangeStarted << KeyExchangeSuccess
|
<< KeyExchangeStarted << KeyExchangeSuccess
|
||||||
<< UserAuthServiceRequested << UserAuthRequested
|
<< UserAuthServiceRequested << UserAuthRequested
|
||||||
<< ConnectionEstablished, &This::handleDisconnect);
|
<< ConnectionEstablished, &This::handleDisconnect);
|
||||||
|
|
||||||
|
setupPacketHandler(SSH_MSG_UNIMPLEMENTED,
|
||||||
|
StateList() << ConnectionEstablished, &This::handleUnimplementedPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SshConnectionPrivate::setupPacketHandler(SshPacketType type,
|
void SshConnectionPrivate::setupPacketHandler(SshPacketType type,
|
||||||
@@ -436,6 +441,9 @@ void SshConnectionPrivate::handleUserAuthSuccessPacket()
|
|||||||
m_state = ConnectionEstablished;
|
m_state = ConnectionEstablished;
|
||||||
m_timeoutTimer.stop();
|
m_timeoutTimer.stop();
|
||||||
emit connected();
|
emit connected();
|
||||||
|
m_lastInvalidMsgSeqNr = InvalidSeqNr;
|
||||||
|
connect(&m_keepAliveTimer, SIGNAL(timeout()), SLOT(sendKeepAlivePacket()));
|
||||||
|
m_keepAliveTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SshConnectionPrivate::handleUserAuthFailurePacket()
|
void SshConnectionPrivate::handleUserAuthFailurePacket()
|
||||||
@@ -452,6 +460,19 @@ void SshConnectionPrivate::handleDebugPacket()
|
|||||||
emit dataAvailable(msg.message);
|
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()
|
void SshConnectionPrivate::handleChannelRequest()
|
||||||
{
|
{
|
||||||
m_channelManager->handleChannelRequest(m_incomingPacket);
|
m_channelManager->handleChannelRequest(m_incomingPacket);
|
||||||
@@ -514,6 +535,8 @@ void SshConnectionPrivate::handleDisconnect()
|
|||||||
"", tr("Server closed connection: %1").arg(msg.description));
|
"", tr("Server closed connection: %1").arg(msg.description));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SshConnectionPrivate::sendData(const QByteArray &data)
|
void SshConnectionPrivate::sendData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
if (canUseSocket())
|
if (canUseSocket())
|
||||||
@@ -541,6 +564,14 @@ void SshConnectionPrivate::handleTimeout()
|
|||||||
tr("Timeout waiting for reply from server."));
|
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)
|
void SshConnectionPrivate::connectToHost(const SshConnectionParameters &serverInfo)
|
||||||
{
|
{
|
||||||
m_incomingData.clear();
|
m_incomingData.clear();
|
||||||
@@ -577,6 +608,8 @@ void SshConnectionPrivate::closeConnection(SshErrorCode sshError,
|
|||||||
m_timeoutTimer.stop();
|
m_timeoutTimer.stop();
|
||||||
disconnect(m_socket, 0, this, 0);
|
disconnect(m_socket, 0, this, 0);
|
||||||
disconnect(&m_timeoutTimer, 0, this, 0);
|
disconnect(&m_timeoutTimer, 0, this, 0);
|
||||||
|
m_keepAliveTimer.stop();
|
||||||
|
disconnect(&m_keepAliveTimer, 0, this, 0);
|
||||||
try {
|
try {
|
||||||
m_channelManager->closeAllChannels();
|
m_channelManager->closeAllChannels();
|
||||||
m_sendFacility.sendDisconnectPacket(sshError, serverErrorString);
|
m_sendFacility.sendDisconnectPacket(sshError, serverErrorString);
|
||||||
@@ -606,5 +639,7 @@ QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel()
|
|||||||
return m_channelManager->createSftpChannel();
|
return m_channelManager->createSftpChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const quint64 SshConnectionPrivate::InvalidSeqNr = static_cast<quint64>(-1);
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ private:
|
|||||||
Q_SLOT void handleSocketError();
|
Q_SLOT void handleSocketError();
|
||||||
Q_SLOT void handleSocketDisconnected();
|
Q_SLOT void handleSocketDisconnected();
|
||||||
Q_SLOT void handleTimeout();
|
Q_SLOT void handleTimeout();
|
||||||
|
Q_SLOT void sendKeepAlivePacket();
|
||||||
|
|
||||||
void handleServerId();
|
void handleServerId();
|
||||||
void handlePackets();
|
void handlePackets();
|
||||||
@@ -116,6 +117,7 @@ private:
|
|||||||
void handleUserAuthBannerPacket();
|
void handleUserAuthBannerPacket();
|
||||||
void handleGlobalRequest();
|
void handleGlobalRequest();
|
||||||
void handleDebugPacket();
|
void handleDebugPacket();
|
||||||
|
void handleUnimplementedPacket();
|
||||||
void handleChannelRequest();
|
void handleChannelRequest();
|
||||||
void handleChannelOpen();
|
void handleChannelOpen();
|
||||||
void handleChannelOpenFailure();
|
void handleChannelOpenFailure();
|
||||||
@@ -141,6 +143,8 @@ private:
|
|||||||
typedef QPair<StateList, PacketHandler> HandlerInStates;
|
typedef QPair<StateList, PacketHandler> HandlerInStates;
|
||||||
QHash<SshPacketType, HandlerInStates> m_packetHandlers;
|
QHash<SshPacketType, HandlerInStates> m_packetHandlers;
|
||||||
|
|
||||||
|
static const quint64 InvalidSeqNr;
|
||||||
|
|
||||||
QTcpSocket *m_socket;
|
QTcpSocket *m_socket;
|
||||||
SshStateInternal m_state;
|
SshStateInternal m_state;
|
||||||
SshIncomingPacket m_incomingPacket;
|
SshIncomingPacket m_incomingPacket;
|
||||||
@@ -152,8 +156,10 @@ private:
|
|||||||
QString m_errorString;
|
QString m_errorString;
|
||||||
QScopedPointer<SshKeyExchange> m_keyExchange;
|
QScopedPointer<SshKeyExchange> m_keyExchange;
|
||||||
QTimer m_timeoutTimer;
|
QTimer m_timeoutTimer;
|
||||||
|
QTimer m_keepAliveTimer;
|
||||||
bool m_ignoreNextPacket;
|
bool m_ignoreNextPacket;
|
||||||
SshConnection *m_conn;
|
SshConnection *m_conn;
|
||||||
|
quint64 m_lastInvalidMsgSeqNr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -257,7 +257,23 @@ SshDebug SshIncomingPacket::extractDebug() const
|
|||||||
return msg;
|
return msg;
|
||||||
} catch (SshPacketParseException &) {
|
} catch (SshPacketParseException &) {
|
||||||
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
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;
|
QByteArray language;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SshUnimplemented
|
||||||
|
{
|
||||||
|
quint32 invalidMsgSeqNr;
|
||||||
|
};
|
||||||
|
|
||||||
struct SshChannelOpenFailure
|
struct SshChannelOpenFailure
|
||||||
{
|
{
|
||||||
quint32 localChannel;
|
quint32 localChannel;
|
||||||
@@ -156,6 +161,7 @@ public:
|
|||||||
SshDisconnect extractDisconnect() const;
|
SshDisconnect extractDisconnect() const;
|
||||||
SshUserAuthBanner extractUserAuthBanner() const;
|
SshUserAuthBanner extractUserAuthBanner() const;
|
||||||
SshDebug extractDebug() const;
|
SshDebug extractDebug() const;
|
||||||
|
SshUnimplemented extractUnimplemented() const;
|
||||||
|
|
||||||
SshChannelOpenFailure extractChannelOpenFailure() const;
|
SshChannelOpenFailure extractChannelOpenFailure() const;
|
||||||
SshChannelOpenConfirmation extractChannelOpenConfirmation() const;
|
SshChannelOpenConfirmation extractChannelOpenConfirmation() const;
|
||||||
|
|||||||
@@ -130,6 +130,16 @@ void SshOutgoingPacket::generateRequestFailurePacket()
|
|||||||
init(SSH_MSG_REQUEST_FAILURE).finalize();
|
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,
|
void SshOutgoingPacket::generateSessionPacket(quint32 channelId,
|
||||||
quint32 windowSize, quint32 maxPacketSize)
|
quint32 windowSize, quint32 maxPacketSize)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ public:
|
|||||||
void generateUserAuthByKeyRequestPacket(const QByteArray &user,
|
void generateUserAuthByKeyRequestPacket(const QByteArray &user,
|
||||||
const QByteArray &service);
|
const QByteArray &service);
|
||||||
void generateRequestFailurePacket();
|
void generateRequestFailurePacket();
|
||||||
|
void generateIgnorePacket();
|
||||||
|
void generateInvalidMessagePacket();
|
||||||
void generateSessionPacket(quint32 channelId, quint32 windowSize,
|
void generateSessionPacket(quint32 channelId, quint32 windowSize,
|
||||||
quint32 maxPacketSize);
|
quint32 maxPacketSize);
|
||||||
void generateEnvPacket(quint32 remoteChannel, const QByteArray &var,
|
void generateEnvPacket(quint32 remoteChannel, const QByteArray &var,
|
||||||
|
|||||||
@@ -79,7 +79,12 @@ enum SshPacketType {
|
|||||||
SSH_MSG_CHANNEL_CLOSE = 97,
|
SSH_MSG_CHANNEL_CLOSE = 97,
|
||||||
SSH_MSG_CHANNEL_REQUEST = 98,
|
SSH_MSG_CHANNEL_REQUEST = 98,
|
||||||
SSH_MSG_CHANNEL_SUCCESS = 99,
|
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 };
|
enum SshExtendedDataType { SSH_EXTENDED_DATA_STDERR = 1 };
|
||||||
|
|||||||
@@ -133,6 +133,18 @@ void SshSendFacility::sendRequestFailurePacket()
|
|||||||
sendPacket();
|
sendPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendIgnorePacket()
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateIgnorePacket();
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendInvalidPacket()
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateInvalidMessagePacket();
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
void SshSendFacility::sendSessionPacket(quint32 channelId, quint32 windowSize,
|
void SshSendFacility::sendSessionPacket(quint32 channelId, quint32 windowSize,
|
||||||
quint32 maxPacketSize)
|
quint32 maxPacketSize)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ public:
|
|||||||
void sendUserAuthByKeyRequestPacket(const QByteArray &user,
|
void sendUserAuthByKeyRequestPacket(const QByteArray &user,
|
||||||
const QByteArray &service);
|
const QByteArray &service);
|
||||||
void sendRequestFailurePacket();
|
void sendRequestFailurePacket();
|
||||||
|
void sendIgnorePacket();
|
||||||
|
void sendInvalidPacket();
|
||||||
void sendSessionPacket(quint32 channelId, quint32 windowSize,
|
void sendSessionPacket(quint32 channelId, quint32 windowSize,
|
||||||
quint32 maxPacketSize);
|
quint32 maxPacketSize);
|
||||||
void sendEnvPacket(quint32 remoteChannel, const QByteArray &var,
|
void sendEnvPacket(quint32 remoteChannel, const QByteArray &var,
|
||||||
@@ -78,6 +80,7 @@ public:
|
|||||||
const QByteArray &signalName);
|
const QByteArray &signalName);
|
||||||
void sendChannelEofPacket(quint32 remoteChannel);
|
void sendChannelEofPacket(quint32 remoteChannel);
|
||||||
void sendChannelClosePacket(quint32 remoteChannel);
|
void sendChannelClosePacket(quint32 remoteChannel);
|
||||||
|
quint32 nextClientSeqNr() const { return m_clientSeqNr; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sendPacket();
|
void sendPacket();
|
||||||
|
|||||||
Reference in New Issue
Block a user