Skip to content

Commit

Permalink
Support LF as line ending for banner and identification string
Browse files Browse the repository at this point in the history
Fixes #761
  • Loading branch information
drieseng committed Jan 12, 2021
1 parent a620835 commit 91b6458
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Common;
using Renci.SshNet.Connection;
using Renci.SshNet.Tests.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Renci.SshNet.Tests.Classes.Connection
{
[TestClass]
public class ProtocolVersionExchangeTest_ServerResponseValid_TerminatedByLineFeedWithoutCarriageReturn
{
private AsyncSocketListener _server;
private ProtocolVersionExchange _protocolVersionExchange;
private string _clientVersion;
private TimeSpan _timeout;
private IPEndPoint _serverEndPoint;
private List<byte> _dataReceivedByServer;
private byte[] _serverIdentification;
private bool _clientDisconnected;
private Socket _client;
private SshIdentification _actual;

[TestInitialize]
public void Setup()
{
Arrange();
Act();
}

[TestCleanup]
public void Cleanup()
{
if (_server != null)
{
_server.Dispose();
_server = null;
}

if (_client != null)
{
_client.Shutdown(SocketShutdown.Both);
_client.Close();
_client = null;
}
}

protected void Arrange()
{
_clientVersion = "SSH-2.0-Renci.SshNet.SshClient.0.0.1";
_timeout = TimeSpan.FromSeconds(5);
_serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122);
_dataReceivedByServer = new List<byte>();
_serverIdentification = Encoding.UTF8.GetBytes("Welcome stranger!\n\nSSH-Zero-OurSSHAppliance\n\0");

_server = new AsyncSocketListener(_serverEndPoint);
_server.Start();
_server.BytesReceived += (bytes, socket) =>
{
_dataReceivedByServer.AddRange(bytes);
socket.Send(_serverIdentification);
socket.Shutdown(SocketShutdown.Send);
};
_server.Disconnected += (socket) => _clientDisconnected = true;

_client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_client.Connect(_serverEndPoint);

_protocolVersionExchange = new ProtocolVersionExchange();
}

protected void Act()
{
_actual = _protocolVersionExchange.Start(_clientVersion, _client, _timeout);
}

[TestMethod]
public void StartShouldReturnIdentificationOfServer()
{
Assert.IsNotNull(_actual);
Assert.AreEqual("Zero", _actual.ProtocolVersion);
Assert.AreEqual("OurSSHAppliance", _actual.SoftwareVersion);
Assert.IsNull(_actual.Comments);
}

[TestMethod]
public void ClientIdentificationWasSentToServer()
{
var expected = Encoding.UTF8.GetBytes(_clientVersion);

Assert.AreEqual(expected.Length + 2, _dataReceivedByServer.Count);

Assert.IsTrue(expected.SequenceEqual(_dataReceivedByServer.Take(expected.Length)));
Assert.AreEqual(Session.CarriageReturn, _dataReceivedByServer[_dataReceivedByServer.Count - 2]);
Assert.AreEqual(Session.LineFeed, _dataReceivedByServer[_dataReceivedByServer.Count - 1]);
}

[TestMethod]
public void ClientRemainsConnected()
{
Assert.IsTrue(_client.Connected);
Assert.IsFalse(_clientDisconnected);
}

[TestMethod]
public void ClientDidNotReadPastIdentification()
{
var buffer = new byte[1];

var bytesReceived = _client.Receive(buffer);
Assert.AreEqual(1, bytesReceived);
Assert.AreEqual(0x00, buffer[0]);
}
}
}
17 changes: 14 additions & 3 deletions src/Renci.SshNet/Connection/ProtocolVersionExchange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,21 @@ private static string SocketReadLine(Socket socket, TimeSpan timeout, List<byte>
PacketDump.Create(buffer.ToArray(), 2)));
}

if (byteRead == Session.LineFeed && buffer.Count > startPosition + 1 && buffer[buffer.Count - 2] == Session.CarriageReturn)
if (byteRead == Session.LineFeed)
{
// Return current line without CRLF
return Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 2));
if (buffer.Count > startPosition + 1 && buffer[buffer.Count - 2] == Session.CarriageReturn)
{
// Return current line without CRLF
return Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 2));
}
else
{
// Even though RFC4253 clearly indicates that the identification string should be terminated
// by a CR LF we also support banners and identification strings that are terminated by a LF

// Return current line without LF
return Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 1));
}
}
}

Expand Down

0 comments on commit 91b6458

Please sign in to comment.