ShellStream
Contains operation for working with SSH Shell.
using Renci.SshNet.Abstractions;
using Renci.SshNet.Channels;
using Renci.SshNet.Common;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace Renci.SshNet
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public class ShellStream : Stream
{
private readonly ISession _session;
private readonly Encoding _encoding;
private readonly IChannelSession _channel;
private readonly byte[] _carriageReturnBytes;
private readonly byte[] _lineFeedBytes;
private readonly object _sync = new object();
private readonly byte[] _writeBuffer;
private int _writeLength;
private byte[] _readBuffer;
private int _readHead;
private int _readTail;
private bool _disposed;
public bool DataAvailable {
get {
lock (_sync) {
return _readTail != _readHead;
}
}
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => !_disposed;
public override long Length {
get {
lock (_sync) {
return _readTail - _readHead;
}
}
}
public override long Position {
get {
return 0;
}
set {
throw new NotSupportedException();
}
}
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[method: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[field: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
public event EventHandler<ShellDataEventArgs> DataReceived;
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[method: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[field: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
public event EventHandler<ExceptionEventArgs> ErrorOccurred;
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[method: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[field: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
public event EventHandler<EventArgs> Closed;
[Conditional("DEBUG")]
private void AssertValid()
{
}
internal ShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues, int bufferSize)
{
if (bufferSize <= 0)
throw new ArgumentOutOfRangeException("bufferSize");
_encoding = session.ConnectionInfo.Encoding;
_session = session;
_carriageReturnBytes = _encoding.GetBytes("\r");
_lineFeedBytes = _encoding.GetBytes("\n");
_channel = _session.CreateChannelSession();
_channel.DataReceived += Channel_DataReceived;
_channel.Closed += Channel_Closed;
_session.Disconnected += Session_Disconnected;
_session.ErrorOccured += Session_ErrorOccured;
_readBuffer = new byte[bufferSize];
_writeBuffer = new byte[bufferSize];
try {
_channel.Open();
if (!_channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues))
throw new SshException("The pseudo-terminal request was not accepted by the server. Consult the server log for more information.");
if (!_channel.SendShellRequest())
throw new SshException("The request to start a shell was not accepted by the server. Consult the server log for more information.");
} catch {
Dispose();
throw;
}
}
public override void Flush()
{
ThrowIfDisposed();
if (_writeLength > 0) {
_channel.SendData(_writeBuffer, 0, _writeLength);
_writeLength = 0;
}
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public void Expect(params ExpectAction[] expectActions)
{
Expect(Timeout.InfiniteTimeSpan, expectActions);
}
public void Expect(TimeSpan timeout, params ExpectAction[] expectActions)
{
ExpectRegex(timeout, -1, expectActions);
}
public void Expect(TimeSpan timeout, int lookback, params ExpectAction[] expectActions)
{
ExpectRegex(timeout, lookback, expectActions);
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public string Expect(string text)
{
return Expect(text, Timeout.InfiniteTimeSpan, -1);
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public string Expect(string text, TimeSpan timeout, int lookback = -1)
{
ValidateTimeout(timeout);
ValidateLookback(lookback);
DateTime d = DateTime.Now.Add(timeout);
byte[] bytes = _encoding.GetBytes(text);
lock (_sync) {
while (true) {
int num = (lookback == -1) ? _readHead : Math.Max(_readTail - lookback, _readHead);
int num2 = _readBuffer.AsSpan(num, _readTail - num).IndexOf(bytes);
if (num2 >= 0) {
string string = _encoding.GetString(_readBuffer, _readHead, num - _readHead + num2 + bytes.Length);
_readHead = num + num2 + bytes.Length;
return string;
}
if (_disposed)
return null;
if (timeout == Timeout.InfiniteTimeSpan)
Monitor.Wait(_sync);
else {
TimeSpan timeSpan = d - DateTime.Now;
if (timeSpan < TimeSpan.Zero || !Monitor.Wait(_sync, timeSpan))
break;
}
}
return null;
}
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public string Expect(Regex regex)
{
return Expect(regex, Timeout.InfiniteTimeSpan, -1);
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public string Expect(Regex regex, TimeSpan timeout, int lookback = -1)
{
return ExpectRegex(timeout, lookback, new ExpectAction[1] {
new ExpectAction(regex, delegate {
})
});
}
[return: System.Runtime.CompilerServices.Nullable(2)]
private string ExpectRegex(TimeSpan timeout, int lookback, ExpectAction[] expectActions)
{
ValidateTimeout(timeout);
ValidateLookback(lookback);
DateTime d = DateTime.Now.Add(timeout);
lock (_sync) {
while (true) {
string string = _encoding.GetString(_readBuffer, _readHead, _readTail - _readHead);
int startat = (lookback != -1) ? Math.Max(string.Length - lookback, 0) : 0;
foreach (ExpectAction expectAction in expectActions) {
Match match = expectAction.Expect.Match(string, startat);
if (match.Success) {
string text = string.Substring(0, match.Index + match.Length);
_readHead += _encoding.GetByteCount(text);
expectAction.Action(text);
return text;
}
}
if (_disposed)
return null;
if (timeout == Timeout.InfiniteTimeSpan)
Monitor.Wait(_sync);
else {
TimeSpan timeSpan = d - DateTime.Now;
if (timeSpan < TimeSpan.Zero || !Monitor.Wait(_sync, timeSpan))
break;
}
}
return null;
}
}
public IAsyncResult BeginExpect(params ExpectAction[] expectActions)
{
return BeginExpect(Timeout.InfiniteTimeSpan, null, null, expectActions);
}
public IAsyncResult BeginExpect([System.Runtime.CompilerServices.Nullable(2)] AsyncCallback callback, params ExpectAction[] expectActions)
{
return BeginExpect(Timeout.InfiniteTimeSpan, callback, null, expectActions);
}
public IAsyncResult BeginExpect([System.Runtime.CompilerServices.Nullable(2)] AsyncCallback callback, [System.Runtime.CompilerServices.Nullable(2)] object state, params ExpectAction[] expectActions)
{
return BeginExpect(Timeout.InfiniteTimeSpan, callback, state, expectActions);
}
public IAsyncResult BeginExpect(TimeSpan timeout, [System.Runtime.CompilerServices.Nullable(2)] AsyncCallback callback, [System.Runtime.CompilerServices.Nullable(2)] object state, params ExpectAction[] expectActions)
{
return BeginExpect(timeout, -1, callback, state, expectActions);
}
public IAsyncResult BeginExpect(TimeSpan timeout, int lookback, [System.Runtime.CompilerServices.Nullable(2)] AsyncCallback callback, [System.Runtime.CompilerServices.Nullable(2)] object state, params ExpectAction[] expectActions)
{
return TaskToAsyncResult.Begin(Task.Run(() => ExpectRegex(timeout, lookback, expectActions)), callback, state);
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public string EndExpect(IAsyncResult asyncResult)
{
return TaskToAsyncResult.End<string>(asyncResult);
}
[System.Runtime.CompilerServices.NullableContext(2)]
public string ReadLine()
{
return ReadLine(Timeout.InfiniteTimeSpan);
}
[System.Runtime.CompilerServices.NullableContext(2)]
public string ReadLine(TimeSpan timeout)
{
ValidateTimeout(timeout);
DateTime d = DateTime.Now.Add(timeout);
lock (_sync) {
while (true) {
int num = _readBuffer.AsSpan(_readHead, _readTail - _readHead).IndexOf(_carriageReturnBytes);
if (num >= 0) {
int num2 = (num + _carriageReturnBytes.Length + _lineFeedBytes.Length <= _readTail - _readHead) ? _readBuffer.AsSpan(_readHead, num + _carriageReturnBytes.Length + _lineFeedBytes.Length).IndexOf(_lineFeedBytes) : _readBuffer.AsSpan(_readHead, num).IndexOf(_lineFeedBytes);
if (num2 >= 0 && num2 < num) {
string string = _encoding.GetString(_readBuffer, _readHead, num2);
_readHead += num2 + _lineFeedBytes.Length;
return string;
}
if (num2 != num + _carriageReturnBytes.Length) {
string string2 = _encoding.GetString(_readBuffer, _readHead, num);
_readHead += num + _carriageReturnBytes.Length;
return string2;
}
string string3 = _encoding.GetString(_readBuffer, _readHead, num);
_readHead += num + _carriageReturnBytes.Length + _lineFeedBytes.Length;
return string3;
}
int num3 = _readBuffer.AsSpan(_readHead, _readTail - _readHead).IndexOf(_lineFeedBytes);
if (num3 >= 0) {
string string4 = _encoding.GetString(_readBuffer, _readHead, num3);
_readHead += num3 + _lineFeedBytes.Length;
return string4;
}
if (_disposed) {
string result = (_readHead == _readTail) ? null : _encoding.GetString(_readBuffer, _readHead, _readTail - _readHead);
_readHead = (_readTail = 0);
return result;
}
if (timeout == Timeout.InfiniteTimeSpan)
Monitor.Wait(_sync);
else {
TimeSpan timeSpan = d - DateTime.Now;
if (timeSpan < TimeSpan.Zero || !Monitor.Wait(_sync, timeSpan))
break;
}
}
return null;
}
}
private static void ValidateTimeout(TimeSpan timeout)
{
if (timeout < TimeSpan.Zero && timeout != Timeout.InfiniteTimeSpan)
throw new ArgumentOutOfRangeException("timeout", "Value must be non-negative or equal to -1 millisecond (for infinite timeout)");
}
private static void ValidateLookback(int lookback)
{
if (lookback <= 0 && lookback != -1)
throw new ArgumentOutOfRangeException("lookback", "Value must be positive or equal to -1 (for no window)");
}
private void ThrowIfDisposed()
{
if (_disposed)
throw new ObjectDisposedException(GetType().FullName);
}
public string Read()
{
lock (_sync) {
string string = _encoding.GetString(_readBuffer, _readHead, _readTail - _readHead);
_readHead = (_readTail = 0);
return string;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
lock (_sync) {
while (_readHead == _readTail && !_disposed) {
Monitor.Wait(_sync);
}
int num = Math.Min(count, _readTail - _readHead);
Buffer.BlockCopy(_readBuffer, _readHead, buffer, offset, num);
_readHead += num;
return num;
}
}
[System.Runtime.CompilerServices.NullableContext(2)]
public void Write(string text)
{
if (text != null) {
byte[] bytes = _encoding.GetBytes(text);
Write(bytes, 0, bytes.Length);
Flush();
}
}
public override void Write(byte[] buffer, int offset, int count)
{
ThrowIfDisposed();
while (count > 0) {
if (_writeLength == _writeBuffer.Length)
Flush();
int num = Math.Min(count, _writeBuffer.Length - _writeLength);
Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeLength, num);
offset += num;
count -= num;
_writeLength += num;
}
}
public void WriteLine(string line)
{
Write(line + "\r");
}
protected override void Dispose(bool disposing)
{
if (!disposing)
base.Dispose(disposing);
else {
lock (_sync) {
if (_disposed)
return;
_disposed = true;
_session.Disconnected -= Session_Disconnected;
_session.ErrorOccured -= Session_ErrorOccured;
_channel.DataReceived -= Channel_DataReceived;
_channel.Closed -= Channel_Closed;
_channel.Dispose();
Monitor.PulseAll(_sync);
}
base.Dispose(disposing);
}
}
private void Session_ErrorOccured([System.Runtime.CompilerServices.Nullable(2)] object sender, ExceptionEventArgs e)
{
this.ErrorOccurred?.Invoke(this, e);
}
private void Session_Disconnected([System.Runtime.CompilerServices.Nullable(2)] object sender, EventArgs e)
{
Dispose();
}
private void Channel_Closed([System.Runtime.CompilerServices.Nullable(2)] object sender, ChannelEventArgs e)
{
Dispose();
if (this.Closed != null)
ThreadAbstraction.ExecuteThread(delegate {
this.Closed?.Invoke(this, EventArgs.Empty);
});
}
private void Channel_DataReceived([System.Runtime.CompilerServices.Nullable(2)] object sender, ChannelDataEventArgs e)
{
lock (_sync) {
if (_readBuffer.Length - _readTail >= e.Data.Length) {
Buffer.BlockCopy(e.Data, 0, _readBuffer, _readTail, e.Data.Length);
_readTail += e.Data.Length;
} else {
int num = _readTail - _readHead + e.Data.Length;
if (num <= _readBuffer.Length)
Buffer.BlockCopy(_readBuffer, _readHead, _readBuffer, 0, _readTail - _readHead);
else {
byte[] array = new byte[_readBuffer.Length * 2];
Buffer.BlockCopy(_readBuffer, _readHead, array, 0, _readTail - _readHead);
_readBuffer = array;
}
Buffer.BlockCopy(e.Data, 0, _readBuffer, _readTail - _readHead, e.Data.Length);
_readHead = 0;
_readTail = num;
}
Monitor.PulseAll(_sync);
}
this.DataReceived?.Invoke(this, new ShellDataEventArgs(e.Data));
}
}
}