SocketAbstraction
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
using System;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
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);
}
[AsyncStateMachine(typeof(<ConnectAsync>d__4))]
public static Task ConnectAsync(Socket socket, IPEndPoint remoteEndpoint, CancellationToken cancellationToken)
{
<ConnectAsync>d__4 stateMachine = default(<ConnectAsync>d__4);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.socket = socket;
stateMachine.remoteEndpoint = remoteEndpoint;
stateMachine.cancellationToken = cancellationToken;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
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 Task<int> ReadAsync(Socket socket, byte[] buffer, int offset, int length, CancellationToken cancellationToken)
{
return socket.ReceiveAsync(buffer, offset, length, cancellationToken);
}
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();
}
}
}