ProtocolVersionExchange
Handles the SSH protocol version exchange.
            
                using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
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 = new Regex("^SSH-(?<protoversion>[^-]+)-(?<softwareversion>.*?)([ ](?<comments>.+))?$", RegexOptions.ExplicitCapture | RegexOptions.Compiled);
        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__4))]
        public Task<SshIdentification> StartAsync(string clientVersion, Socket socket, CancellationToken cancellationToken)
        {
            <StartAsync>d__4 stateMachine = default(<StartAsync>d__4);
            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__7))]
        private static Task<string> SocketReadLineAsync(Socket socket, List<byte> buffer, CancellationToken cancellationToken)
        {
            <SocketReadLineAsync>d__7 stateMachine = default(<SocketReadLineAsync>d__7);
            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);
        }
    }
}