<PackageReference Include="SSH.NET" Version="2025.0.0" />

ProtocolVersionExchange

Handles the SSH protocol version exchange.
using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using System.Text.RegularExpressions.Generated; using System.Threading; using System.Threading.Tasks; namespace Renci.SshNet.Connection { internal sealed class ProtocolVersionExchange : IProtocolVersionExchange { private const byte Null = 0; private const string ServerVersionPattern = "^SSH-(?<protoversion>[^-]+)-(?<softwareversion>.*?)([ ](?<comments>.+))?$"; private static readonly Regex ServerVersionRegex = GetServerVersionRegex(); [GeneratedRegex("^SSH-(?<protoversion>[^-]+)-(?<softwareversion>.*?)([ ](?<comments>.+))?$", RegexOptions.ExplicitCapture)] [GeneratedCode("System.Text.RegularExpressions.Generator", "8.0.12.16413")] private static Regex GetServerVersionRegex() { return <RegexGenerator_g>FF02E8C37B99F41659B0026E2B1005AC8CE9F303D9F9E176C498C4EA009567923__GetServerVersionRegex_2.Instance; } public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout) { SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\r\n")); List<byte> list = new List<byte>(); Match match; do { string text = SocketReadLine(socket, timeout, list); if (text == null) { if (list.Count == 0) throw CreateConnectionLostException(); throw CreateServerResponseDoesNotContainIdentification(list); } match = ServerVersionRegex.Match(text); } while (!match.Success); return new SshIdentification(GetGroupValue(match, "protoversion"), GetGroupValue(match, "softwareversion"), GetGroupValue(match, "comments")); } [AsyncStateMachine(typeof(<StartAsync>d__5))] public Task<SshIdentification> StartAsync(string clientVersion, Socket socket, CancellationToken cancellationToken) { <StartAsync>d__5 stateMachine = default(<StartAsync>d__5); stateMachine.<>t__builder = AsyncTaskMethodBuilder<SshIdentification>.Create(); stateMachine.clientVersion = clientVersion; stateMachine.socket = socket; stateMachine.cancellationToken = cancellationToken; stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } private static string GetGroupValue(Match match, string groupName) { Group group = match.Groups[groupName]; if (group.Success) return group.Value; return null; } private static string SocketReadLine(Socket socket, TimeSpan timeout, List<byte> buffer) { byte[] array = new byte[1]; int count = buffer.Count; while (SocketAbstraction.Read(socket, array, 0, array.Length, timeout) != 0) { byte b = array[0]; buffer.Add(b); switch (b) { case 0: throw CreateServerResponseContainsNullCharacterException(buffer); case 10: if (buffer.Count > count + 1 && buffer[buffer.Count - 2] == 13) return Encoding.UTF8.GetString(buffer.ToArray(), count, buffer.Count - (count + 2)); return Encoding.UTF8.GetString(buffer.ToArray(), count, buffer.Count - (count + 1)); } } return null; } [AsyncStateMachine(typeof(<SocketReadLineAsync>d__8))] private static Task<string> SocketReadLineAsync(Socket socket, List<byte> buffer, CancellationToken cancellationToken) { <SocketReadLineAsync>d__8 stateMachine = default(<SocketReadLineAsync>d__8); stateMachine.<>t__builder = AsyncTaskMethodBuilder<string>.Create(); stateMachine.socket = socket; stateMachine.buffer = buffer; stateMachine.cancellationToken = cancellationToken; stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } private static SshConnectionException CreateConnectionLostException() { return new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The server response does not contain an SSH identification string.{0}The connection to the remote server was closed before any data was received.{0}{0}More information on the Protocol Version Exchange is available here:{0}https://tools.ietf.org/html/rfc4253#section-4.2", Environment.NewLine), DisconnectReason.ConnectionLost); } private static SshConnectionException CreateServerResponseContainsNullCharacterException(List<byte> buffer) { throw new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The server response contains a null character at position 0x{0:X8}:{1}{1}{2}{1}{1}A server must not send a null character before the Protocol Version Exchange is complete.{1}{1}More information is available here:{1}https://tools.ietf.org/html/rfc4253#section-4.2", buffer.Count, Environment.NewLine, PacketDump.Create(buffer.ToArray(), 2))); } private static SshConnectionException CreateServerResponseDoesNotContainIdentification(List<byte> bytesReceived) { throw new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The server response does not contain an SSH identification string:{0}{0}{1}{0}{0}More information on the Protocol Version Exchange is available here:{0}https://tools.ietf.org/html/rfc4253#section-4.2", Environment.NewLine, PacketDump.Create(bytesReceived, 2)), DisconnectReason.ProtocolError); } } }