|
|
@@ -49,6 +49,12 @@ namespace Renci.SshNet.Tests.Classes
|
|
|
internal SshIdentification ServerIdentification { get; set; }
|
|
|
protected bool CallSessionConnectWhenArrange { get; set; }
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Should the "server" wait for the client kexinit before sending its own.
|
|
|
+ /// A regression test simulating e.g. cisco devices.
|
|
|
+ /// </summary>
|
|
|
+ protected bool WaitForClientKeyExchangeInit { get; set; }
|
|
|
+
|
|
|
[TestInitialize]
|
|
|
public void Setup()
|
|
|
{
|
|
|
@@ -59,18 +65,18 @@ namespace Renci.SshNet.Tests.Classes
|
|
|
[TestCleanup]
|
|
|
public void TearDown()
|
|
|
{
|
|
|
- if (ServerSocket != null)
|
|
|
- {
|
|
|
- ServerSocket.Dispose();
|
|
|
- ServerSocket = null;
|
|
|
- }
|
|
|
-
|
|
|
if (ServerListener != null)
|
|
|
{
|
|
|
ServerListener.Dispose();
|
|
|
ServerListener = null;
|
|
|
}
|
|
|
|
|
|
+ if (ServerSocket != null)
|
|
|
+ {
|
|
|
+ ServerSocket.Dispose();
|
|
|
+ ServerSocket = null;
|
|
|
+ }
|
|
|
+
|
|
|
if (Session != null)
|
|
|
{
|
|
|
Session.Dispose();
|
|
|
@@ -115,6 +121,15 @@ namespace Renci.SshNet.Tests.Classes
|
|
|
var newKeysMessage = new NewKeysMessage();
|
|
|
var newKeys = newKeysMessage.GetPacket(8, null);
|
|
|
_ = ServerSocket.Send(newKeys, 4, newKeys.Length - 4, SocketFlags.None);
|
|
|
+
|
|
|
+ if (!_authenticationStarted)
|
|
|
+ {
|
|
|
+ var serviceAcceptMessage = ServiceAcceptMessageBuilder.Create(ServiceName.UserAuthentication)
|
|
|
+ .Build();
|
|
|
+ _ = ServerSocket.Send(serviceAcceptMessage, 0, serviceAcceptMessage.Length, SocketFlags.None);
|
|
|
+
|
|
|
+ _authenticationStarted = true;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
ServerListener = new AsyncSocketListener(_serverEndPoint)
|
|
|
@@ -125,36 +140,23 @@ namespace Renci.SshNet.Tests.Classes
|
|
|
{
|
|
|
ServerSocket = socket;
|
|
|
|
|
|
- // Since we're mocking the protocol version exchange, we'll immediately stat KEX upon
|
|
|
+ // Since we're mocking the protocol version exchange, we'll immediately start KEX upon
|
|
|
// having established the connection instead of when the client has been identified
|
|
|
|
|
|
- var keyExchangeInitMessage = new KeyExchangeInitMessage
|
|
|
- {
|
|
|
- CompressionAlgorithmsClientToServer = new string[0],
|
|
|
- CompressionAlgorithmsServerToClient = new string[0],
|
|
|
- EncryptionAlgorithmsClientToServer = new string[0],
|
|
|
- EncryptionAlgorithmsServerToClient = new string[0],
|
|
|
- KeyExchangeAlgorithms = new[] { _keyExchangeAlgorithm },
|
|
|
- LanguagesClientToServer = new string[0],
|
|
|
- LanguagesServerToClient = new string[0],
|
|
|
- MacAlgorithmsClientToServer = new string[0],
|
|
|
- MacAlgorithmsServerToClient = new string[0],
|
|
|
- ServerHostKeyAlgorithms = new string[0]
|
|
|
- };
|
|
|
- var keyExchangeInit = keyExchangeInitMessage.GetPacket(8, null);
|
|
|
- _ = ServerSocket.Send(keyExchangeInit, 4, keyExchangeInit.Length - 4, SocketFlags.None);
|
|
|
+ if (!WaitForClientKeyExchangeInit)
|
|
|
+ {
|
|
|
+ SendKeyExchangeInit();
|
|
|
+ }
|
|
|
};
|
|
|
ServerListener.BytesReceived += (received, socket) =>
|
|
|
{
|
|
|
ServerBytesReceivedRegister.Add(received);
|
|
|
|
|
|
- if (!_authenticationStarted)
|
|
|
+ if (WaitForClientKeyExchangeInit && received.Length > 5 && received[5] == 20)
|
|
|
{
|
|
|
- var serviceAcceptMessage = ServiceAcceptMessageBuilder.Create(ServiceName.UserAuthentication)
|
|
|
- .Build();
|
|
|
- _ = ServerSocket.Send(serviceAcceptMessage, 0, serviceAcceptMessage.Length, SocketFlags.None);
|
|
|
-
|
|
|
- _authenticationStarted = true;
|
|
|
+ // This is the KEXINIT. Send one back.
|
|
|
+ SendKeyExchangeInit();
|
|
|
+ WaitForClientKeyExchangeInit = false;
|
|
|
}
|
|
|
};
|
|
|
ServerListener.Start();
|
|
|
@@ -162,6 +164,25 @@ namespace Renci.SshNet.Tests.Classes
|
|
|
ClientSocket = new DirectConnector(_socketFactory).Connect(ConnectionInfo);
|
|
|
|
|
|
CallSessionConnectWhenArrange = true;
|
|
|
+
|
|
|
+ void SendKeyExchangeInit()
|
|
|
+ {
|
|
|
+ var keyExchangeInitMessage = new KeyExchangeInitMessage
|
|
|
+ {
|
|
|
+ CompressionAlgorithmsClientToServer = new string[0],
|
|
|
+ CompressionAlgorithmsServerToClient = new string[0],
|
|
|
+ EncryptionAlgorithmsClientToServer = new string[0],
|
|
|
+ EncryptionAlgorithmsServerToClient = new string[0],
|
|
|
+ KeyExchangeAlgorithms = new[] { _keyExchangeAlgorithm },
|
|
|
+ LanguagesClientToServer = new string[0],
|
|
|
+ LanguagesServerToClient = new string[0],
|
|
|
+ MacAlgorithmsClientToServer = new string[0],
|
|
|
+ MacAlgorithmsServerToClient = new string[0],
|
|
|
+ ServerHostKeyAlgorithms = new string[0]
|
|
|
+ };
|
|
|
+ var keyExchangeInit = keyExchangeInitMessage.GetPacket(8, null);
|
|
|
+ _ = ServerSocket.Send(keyExchangeInit, 4, keyExchangeInit.Length - 4, SocketFlags.None);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private void CreateMocks()
|
|
|
@@ -187,7 +208,7 @@ namespace Renci.SshNet.Tests.Classes
|
|
|
_ = ServiceFactoryMock.Setup(p => p.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, new[] { _keyExchangeAlgorithm })).Returns(_keyExchangeMock.Object);
|
|
|
_ = _keyExchangeMock.Setup(p => p.Name)
|
|
|
.Returns(_keyExchangeAlgorithm);
|
|
|
- _ = _keyExchangeMock.Setup(p => p.Start(Session, It.IsAny<KeyExchangeInitMessage>()));
|
|
|
+ _ = _keyExchangeMock.Setup(p => p.Start(Session, It.IsAny<KeyExchangeInitMessage>(), false));
|
|
|
_ = _keyExchangeMock.Setup(p => p.ExchangeHash)
|
|
|
.Returns(SessionId);
|
|
|
_ = _keyExchangeMock.Setup(p => p.CreateServerCipher())
|