SocketAbstraction
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
using System;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Renci.SshNet.Abstractions
{
    internal static class SocketAbstraction
    {
        public static bool CanRead(Socket socket)
        {
            if (socket.Connected) {
                if (socket.Poll(-1, SelectMode.SelectRead))
                    return socket.Available > 0;
                return false;
            }
            return false;
        }
        public static bool CanWrite(Socket socket)
        {
            if (socket != null && socket.Connected)
                return socket.Poll(-1, SelectMode.SelectWrite);
            return false;
        }
        public static Socket Connect(IPEndPoint remoteEndpoint, TimeSpan connectTimeout)
        {
            Socket obj = new Socket(remoteEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) {
                NoDelay = true
            };
            ConnectCore(obj, remoteEndpoint, connectTimeout, true);
            return obj;
        }
        public static void Connect(Socket socket, IPEndPoint remoteEndpoint, TimeSpan connectTimeout)
        {
            ConnectCore(socket, remoteEndpoint, connectTimeout, false);
        }
        private static void ConnectCore(Socket socket, IPEndPoint remoteEndpoint, TimeSpan connectTimeout, bool ownsSocket)
        {
            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            SocketAsyncEventArgs socketAsyncEventArgs = new SocketAsyncEventArgs {
                UserToken = manualResetEvent,
                RemoteEndPoint = remoteEndpoint
            };
            socketAsyncEventArgs.Completed += ConnectCompleted;
            if (socket.ConnectAsync(socketAsyncEventArgs) && !manualResetEvent.WaitOne(connectTimeout)) {
                socketAsyncEventArgs.Completed -= ConnectCompleted;
                if (ownsSocket)
                    socket.Dispose();
                manualResetEvent.Dispose();
                socketAsyncEventArgs.Dispose();
                throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture, "Connection failed to establish within {0:F0} milliseconds.", connectTimeout.TotalMilliseconds));
            }
            manualResetEvent.Dispose();
            if (socketAsyncEventArgs.SocketError != 0) {
                SocketError socketError = socketAsyncEventArgs.SocketError;
                if (ownsSocket)
                    socket.Dispose();
                socketAsyncEventArgs.Dispose();
                throw new SocketException((int)socketError);
            }
            socketAsyncEventArgs.Dispose();
        }
        public static void ClearReadBuffer(Socket socket)
        {
            TimeSpan timeout = TimeSpan.FromMilliseconds(500);
            byte[] array = new byte[256];
            int num;
            do {
                num = ReadPartial(socket, array, 0, array.Length, timeout);
            } while (num > 0);
        }
        public static int ReadPartial(Socket socket, byte[] buffer, int offset, int size, TimeSpan timeout)
        {
            socket.ReceiveTimeout = (int)timeout.TotalMilliseconds;
            try {
                return socket.Receive(buffer, offset, size, SocketFlags.None);
            } catch (SocketException ex) {
                if (ex.SocketErrorCode == SocketError.TimedOut)
                    throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture, "Socket read operation has timed out after {0:F0} milliseconds.", timeout.TotalMilliseconds));
                throw;
            }
        }
        public static void ReadContinuous(Socket socket, byte[] buffer, int offset, int size, Action<byte[], int, int> processReceivedBytesAction)
        {
            socket.ReceiveTimeout = 0;
            while (socket.Connected) {
                try {
                    int num = socket.Receive(buffer, offset, size, SocketFlags.None);
                    if (num == 0)
                        return;
                    processReceivedBytesAction(buffer, offset, num);
                } catch (SocketException ex) {
                    if (!IsErrorResumable(ex.SocketErrorCode)) {
                        SocketError socketErrorCode = ex.SocketErrorCode;
                        if (socketErrorCode != SocketError.Interrupted && (uint)(socketErrorCode - 10053) > 1)
                            throw;
                        return;
                    }
                }
            }
        }
        public static int ReadByte(Socket socket, TimeSpan timeout)
        {
            byte[] array = new byte[1];
            if (Read(socket, array, 0, 1, timeout) == 0)
                return -1;
            return array[0];
        }
        public static void SendByte(Socket socket, byte value)
        {
            byte[] data = new byte[1] {
                value
            };
            Send(socket, data, 0, 1);
        }
        public static byte[] Read(Socket socket, int size, TimeSpan timeout)
        {
            byte[] array = new byte[size];
            Read(socket, array, 0, size, timeout);
            return array;
        }
        public static int Read(Socket socket, byte[] buffer, int offset, int size, TimeSpan readTimeout)
        {
            int num = 0;
            socket.ReceiveTimeout = (int)readTimeout.TotalMilliseconds;
            do {
                try {
                    int num2 = socket.Receive(buffer, offset + num, size - num, SocketFlags.None);
                    if (num2 == 0)
                        return 0;
                    num += num2;
                } catch (SocketException ex) {
                    if (!IsErrorResumable(ex.SocketErrorCode)) {
                        if (ex.SocketErrorCode == SocketError.TimedOut)
                            throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture, "Socket read operation has timed out after {0:F0} milliseconds.", readTimeout.TotalMilliseconds));
                        throw;
                    }
                    ThreadAbstraction.Sleep(30);
                }
            } while (num < size);
            return num;
        }
        public static void Send(Socket socket, byte[] data)
        {
            Send(socket, data, 0, data.Length);
        }
        public static void Send(Socket socket, byte[] data, int offset, int size)
        {
            int num = 0;
            do {
                try {
                    int num2 = socket.Send(data, offset + num, size - num, SocketFlags.None);
                    if (num2 == 0)
                        throw new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost);
                    num += num2;
                } catch (SocketException ex) {
                    if (!IsErrorResumable(ex.SocketErrorCode))
                        throw;
                    ThreadAbstraction.Sleep(30);
                }
            } while (num < size);
        }
        public static bool IsErrorResumable(SocketError socketError)
        {
            if (socketError == SocketError.IOPending || socketError == SocketError.WouldBlock || socketError == SocketError.NoBufferSpaceAvailable)
                return true;
            return false;
        }
        private static void ConnectCompleted(object sender, SocketAsyncEventArgs e)
        {
            ((ManualResetEvent)e.UserToken)?.Set();
        }
    }
}