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

ShellStream

public sealed class ShellStream : Stream
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.IO; using System.Net; 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 sealed class ShellStream : Stream { private const int DefaultBufferSize = 1024; private readonly ISession _session; private readonly Encoding _encoding; private readonly IChannelSession _channel; private readonly byte[] _carriageReturnBytes; private readonly byte[] _lineFeedBytes; private readonly bool _noTerminal; private readonly object _sync = new object(); private System.Net.ArrayBuffer _readBuffer; private System.Net.ArrayBuffer _writeBuffer; private bool _disposed; public bool DataAvailable { get { lock (_sync) { return _readBuffer.ActiveLength > 0; } } } public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => !_disposed; public override long Length { get { lock (_sync) { return _readBuffer.ActiveLength; } } } 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; internal ShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues, int bufferSize) : this(session, bufferSize, false) { 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; } } internal ShellStream(ISession session, int bufferSize) : this(session, bufferSize, true) { try { _channel.Open(); 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; } } private ShellStream(ISession session, int bufferSize, bool noTerminal) { if (bufferSize == -1) bufferSize = 1024; 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_ErrorOccurred; _readBuffer = new System.Net.ArrayBuffer(bufferSize, false); _writeBuffer = new System.Net.ArrayBuffer(bufferSize, false); _noTerminal = noTerminal; } public override void Flush() { ThrowHelper.ThrowObjectDisposedIf(_disposed, this); if (_writeBuffer.ActiveLength > 0) { _channel.SendData(_writeBuffer.DangerousGetUnderlyingBuffer(), _writeBuffer.ActiveStartOffset, _writeBuffer.ActiveLength); _writeBuffer.Discard(_writeBuffer.ActiveLength); } } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public void ChangeWindowSize(uint columns, uint rows, uint width, uint height) { ThrowHelper.ThrowObjectDisposedIf(_disposed, this); _channel.SendWindowChangeRequest(columns, rows, width, height); } 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) ? Math.Max(0, _readBuffer.ActiveLength - lookback) : 0; int num2 = _readBuffer.ActiveReadOnlySpan.Slice(num).IndexOf(bytes); if (num2 >= 0) { int num3 = num + num2 + bytes.Length; string string = GetString(num3); _readBuffer.Discard(num3); 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 = GetString(_readBuffer.ActiveLength); 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); _readBuffer.Discard(_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 System.Threading.Tasks.TaskToAsyncResult.Begin(Task.Run(() => ExpectRegex(timeout, lookback, expectActions)), callback, state); } [return: System.Runtime.CompilerServices.Nullable(2)] public string EndExpect(IAsyncResult asyncResult) { return System.Threading.Tasks.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.ActiveReadOnlySpan.IndexOf(_carriageReturnBytes); if (num >= 0) { ReadOnlySpan<byte> activeReadOnlySpan; int num2; if (num + _carriageReturnBytes.Length + _lineFeedBytes.Length > _readBuffer.ActiveLength) { activeReadOnlySpan = _readBuffer.ActiveReadOnlySpan; num2 = activeReadOnlySpan.Slice(0, num).IndexOf(_lineFeedBytes); } else { activeReadOnlySpan = _readBuffer.ActiveReadOnlySpan; num2 = activeReadOnlySpan.Slice(0, num + _carriageReturnBytes.Length + _lineFeedBytes.Length).IndexOf(_lineFeedBytes); } int num3 = num2; if (num3 >= 0 && num3 < num) { string string = GetString(num3); _readBuffer.Discard(num3 + _lineFeedBytes.Length); return string; } if (num3 != num + _carriageReturnBytes.Length) { string string2 = GetString(num); _readBuffer.Discard(num + _carriageReturnBytes.Length); return string2; } string string3 = GetString(num); _readBuffer.Discard(num + _carriageReturnBytes.Length + _lineFeedBytes.Length); return string3; } int num4 = _readBuffer.ActiveReadOnlySpan.IndexOf(_lineFeedBytes); if (num4 >= 0) { string string4 = GetString(num4); _readBuffer.Discard(num4 + _lineFeedBytes.Length); return string4; } if (_disposed) { string result = (_readBuffer.ActiveLength == 0) ? null : GetString(_readBuffer.ActiveLength); _readBuffer.Discard(_readBuffer.ActiveLength); 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)"); } public string Read() { lock (_sync) { string string = GetString(_readBuffer.ActiveLength); _readBuffer.Discard(_readBuffer.ActiveLength); return string; } } public override int Read(byte[] buffer, int offset, int count) { ThrowHelper.ValidateBufferArguments(buffer, offset, count); return Read(buffer.AsSpan(offset, count)); } [System.Runtime.CompilerServices.NullableContext(0)] private new int Read(Span<byte> buffer) { lock (_sync) { while (_readBuffer.ActiveLength == 0 && !_disposed) { Monitor.Wait(_sync); } int num = Math.Min(buffer.Length, _readBuffer.ActiveLength); ReadOnlySpan<byte> readOnlySpan = _readBuffer.ActiveReadOnlySpan; readOnlySpan = readOnlySpan.Slice(0, num); readOnlySpan.CopyTo(buffer); _readBuffer.Discard(num); return num; } } private string GetString(int length) { return _encoding.GetString(_readBuffer.DangerousGetUnderlyingBuffer(), _readBuffer.ActiveStartOffset, length); } [System.Runtime.CompilerServices.NullableContext(2)] public void Write(string text) { if (text != null) { Write(_encoding.GetBytes(text)); Flush(); } } public override void Write(byte[] buffer, int offset, int count) { ThrowHelper.ValidateBufferArguments(buffer, offset, count); Write(buffer.AsSpan(offset, count)); } [System.Runtime.CompilerServices.NullableContext(0)] private new void Write(ReadOnlySpan<byte> buffer) { ThrowHelper.ThrowObjectDisposedIf(_disposed, this); while (!buffer.IsEmpty) { if (_writeBuffer.AvailableLength == 0) Flush(); int num = Math.Min(buffer.Length, _writeBuffer.AvailableLength); buffer.Slice(0, num).CopyTo(_writeBuffer.AvailableSpan); _writeBuffer.Commit(num); buffer = buffer.Slice(num); } } public override void WriteByte(byte value) { Write(new ReadOnlySpan<byte>(new byte[1] { value })); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { ThrowHelper.ValidateBufferArguments(buffer, offset, count); return WriteAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask(); } [System.Runtime.CompilerServices.NullableContext(0)] [AsyncStateMachine(typeof(<WriteAsync>d__67))] private new ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken) { <WriteAsync>d__67 stateMachine = default(<WriteAsync>d__67); stateMachine.<>t__builder = AsyncValueTaskMethodBuilder.Create(); stateMachine.<>4__this = this; stateMachine.buffer = buffer; stateMachine.cancellationToken = cancellationToken; stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, [System.Runtime.CompilerServices.Nullable(2)] AsyncCallback callback, [System.Runtime.CompilerServices.Nullable(2)] object state) { return System.Threading.Tasks.TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count), callback, state); } public override void EndWrite(IAsyncResult asyncResult) { System.Threading.Tasks.TaskToAsyncResult.End(asyncResult); } public void WriteLine(string line) { Write(line + (_noTerminal ? "\n" : "\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_ErrorOccurred; _channel.DataReceived -= Channel_DataReceived; _channel.Closed -= Channel_Closed; _channel.Dispose(); Monitor.PulseAll(_sync); } base.Dispose(disposing); } } private void Session_ErrorOccurred([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) { ref System.Net.ArrayBuffer readBuffer = ref _readBuffer; ArraySegment<byte> data = e.Data; readBuffer.EnsureAvailableSpace(data.Count); e.Data.AsSpan().CopyTo(_readBuffer.AvailableSpan); ref System.Net.ArrayBuffer readBuffer2 = ref _readBuffer; data = e.Data; readBuffer2.Commit(data.Count); Monitor.PulseAll(_sync); } this.DataReceived?.Invoke(this, new ShellDataEventArgs(Extensions.ToArray(e.Data))); } } }