mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-11 21:49:06 +00:00
Refactor and complete ChannelRequestMessage for different channel request;
Add support for version 1.99; Add shell/terminal like capabilities;
This commit is contained in:
@@ -167,7 +167,7 @@ namespace Renci.SshClient.Channels
|
||||
this._channelClosedWaitHandle.Set();
|
||||
}
|
||||
|
||||
protected virtual void OnRequest(ChannelRequestNames requestName, bool wantReply, string command, string subsystemName, uint exitStatus)
|
||||
protected virtual void OnRequest(ChannelRequestMessage message)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ namespace Renci.SshClient.Channels
|
||||
{
|
||||
if (e.Message.LocalChannelNumber == this.LocalChannelNumber)
|
||||
{
|
||||
this.OnRequest(e.Message.RequestName, e.Message.WantReply, e.Message.Command, e.Message.SubsystemName, e.Message.ExitStatus);
|
||||
this.OnRequest(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,15 @@ namespace Renci.SshClient.Channels
|
||||
/// </summary>
|
||||
private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(false);
|
||||
|
||||
private EventWaitHandle _channelRequestResponse = new ManualResetEvent(false);
|
||||
|
||||
private bool _channelRequestSucces;
|
||||
|
||||
public override ChannelTypes ChannelType
|
||||
{
|
||||
get { return ChannelTypes.Session; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the channel
|
||||
/// </summary>
|
||||
@@ -53,6 +62,23 @@ namespace Renci.SshClient.Channels
|
||||
this._channelOpenResponseWaitHandle.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel failed to open
|
||||
/// </summary>
|
||||
/// <param name="reasonCode">The reason code.</param>
|
||||
/// <param name="description">The description.</param>
|
||||
/// <param name="language">The language.</param>
|
||||
protected override void OnOpenFailure(uint reasonCode, string description, string language)
|
||||
{
|
||||
this._failedOpenAttempts++;
|
||||
|
||||
Debug.WriteLine(string.Format("Local channel: {0} attempts: {1}.", this.LocalChannelNumber, this._failedOpenAttempts));
|
||||
|
||||
this.SessionSemaphore.Release();
|
||||
|
||||
this._channelOpenResponseWaitHandle.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel is closed
|
||||
/// </summary>
|
||||
@@ -70,21 +96,168 @@ namespace Renci.SshClient.Channels
|
||||
this.SessionSemaphore.Release();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel failed to open
|
||||
/// </summary>
|
||||
/// <param name="reasonCode">The reason code.</param>
|
||||
/// <param name="description">The description.</param>
|
||||
/// <param name="language">The language.</param>
|
||||
protected override void OnOpenFailure(uint reasonCode, string description, string language)
|
||||
public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, string terminalMode)
|
||||
{
|
||||
this._failedOpenAttempts++;
|
||||
this._channelRequestResponse.Reset();
|
||||
|
||||
Debug.WriteLine(string.Format("Local channel: {0} attempts: {1}.", this.LocalChannelNumber, this._failedOpenAttempts));
|
||||
this.SendMessage(new ChannelRequestPseudoTerminalMessage
|
||||
{
|
||||
LocalChannelNumber = this.RemoteChannelNumber,
|
||||
//RequestName = ChannelRequestNames.PseudoTerminal,
|
||||
WantReply = true,
|
||||
EnvironmentVariable = environmentVariable,
|
||||
Columns = columns,
|
||||
Rows = rows,
|
||||
PixelWidth = width,
|
||||
PixelHeight = height,
|
||||
TerminalMode = terminalMode,
|
||||
});
|
||||
|
||||
this.SessionSemaphore.Release();
|
||||
this._channelRequestResponse.WaitOne();
|
||||
|
||||
this._channelOpenResponseWaitHandle.Set();
|
||||
return this._channelRequestSucces;
|
||||
}
|
||||
|
||||
public bool SendX11ForwardingRequest()
|
||||
{
|
||||
//byte SSH_MSG_CHANNEL_REQUEST
|
||||
//uint32 recipient channel
|
||||
//string "x11-req"
|
||||
//boolean want reply
|
||||
//boolean single connection
|
||||
//string x11 authentication protocol
|
||||
//string x11 authentication cookie
|
||||
//uint32 x11 screen number
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SendEnvironmentVariableRequest()
|
||||
{
|
||||
//byte SSH_MSG_CHANNEL_REQUEST
|
||||
//uint32 recipient channel
|
||||
//string "env"
|
||||
//boolean want reply
|
||||
//string variable name
|
||||
//string variable value
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SendShellRequest()
|
||||
{
|
||||
this._channelRequestResponse.Reset();
|
||||
|
||||
this.SendMessage(new ChannelRequestShellMessage
|
||||
{
|
||||
LocalChannelNumber = this.RemoteChannelNumber,
|
||||
//RequestName = ChannelRequestNames.Shell,
|
||||
WantReply = true,
|
||||
});
|
||||
|
||||
this._channelRequestResponse.WaitOne();
|
||||
|
||||
return this._channelRequestSucces;
|
||||
}
|
||||
|
||||
public bool SendExecRequest(string command)
|
||||
{
|
||||
this._channelRequestResponse.Reset();
|
||||
|
||||
this.SendMessage(new ChannelRequestExecMessage
|
||||
{
|
||||
LocalChannelNumber = this.RemoteChannelNumber,
|
||||
WantReply = true,
|
||||
Command = command,
|
||||
});
|
||||
|
||||
this._channelRequestResponse.WaitOne();
|
||||
|
||||
return this._channelRequestSucces;
|
||||
}
|
||||
|
||||
public bool SendSubsystemRequest(string subsystem)
|
||||
{
|
||||
this._channelRequestResponse.Reset();
|
||||
|
||||
this.SendMessage(new ChannelRequestSubsystemMessage
|
||||
{
|
||||
LocalChannelNumber = this.RemoteChannelNumber,
|
||||
WantReply = true,
|
||||
SubsystemName = subsystem,
|
||||
});
|
||||
|
||||
this._channelRequestResponse.WaitOne();
|
||||
|
||||
return this._channelRequestSucces;
|
||||
}
|
||||
|
||||
public bool SendWindowChangeRequest()
|
||||
{
|
||||
//byte SSH_MSG_CHANNEL_REQUEST
|
||||
//uint32 recipient channel
|
||||
//string "window-change"
|
||||
//boolean FALSE
|
||||
//uint32 terminal width, columns
|
||||
//uint32 terminal height, rows
|
||||
//uint32 terminal width, pixels
|
||||
//uint32 terminal height, pixels
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SendLocalFlowRequest()
|
||||
{
|
||||
//byte SSH_MSG_CHANNEL_REQUEST
|
||||
//uint32 recipient channel
|
||||
//string "xon-xoff"
|
||||
//boolean FALSE
|
||||
//boolean client can do
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SendSignalRequest()
|
||||
{
|
||||
//byte SSH_MSG_CHANNEL_REQUEST
|
||||
//uint32 recipient channel
|
||||
//string "signal"
|
||||
//boolean FALSE
|
||||
//string signal name (without the "SIG" prefix)
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SendExitStatusRequest()
|
||||
{
|
||||
//byte SSH_MSG_CHANNEL_REQUEST
|
||||
//uint32 recipient channel
|
||||
//string "exit-status"
|
||||
//boolean FALSE
|
||||
//uint32 exit_status
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SendExitSignalRequest()
|
||||
{
|
||||
//byte SSH_MSG_CHANNEL_REQUEST
|
||||
//uint32 recipient channel
|
||||
//string "exit-signal"
|
||||
//boolean FALSE
|
||||
//string signal name (without the "SIG" prefix)
|
||||
//boolean core dumped
|
||||
//string error message in ISO-10646 UTF-8 encoding
|
||||
//string language tag [RFC3066]
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnSuccess()
|
||||
{
|
||||
base.OnSuccess();
|
||||
this._channelRequestSucces = true;
|
||||
this._channelRequestResponse.Set();
|
||||
}
|
||||
|
||||
protected override void OnFailure()
|
||||
{
|
||||
base.OnFailure();
|
||||
this._channelRequestSucces = false;
|
||||
this._channelRequestResponse.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,11 +23,6 @@ namespace Renci.SshClient.Channels
|
||||
|
||||
private AsyncCallback _callback;
|
||||
|
||||
public override ChannelTypes ChannelType
|
||||
{
|
||||
get { return ChannelTypes.Session; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this channel has error.
|
||||
/// </summary>
|
||||
@@ -81,13 +76,7 @@ namespace Renci.SshClient.Channels
|
||||
this.Open();
|
||||
|
||||
// Send channel command request
|
||||
this.SendMessage(new ChannelRequestMessage
|
||||
{
|
||||
LocalChannelNumber = this.RemoteChannelNumber,
|
||||
RequestName = ChannelRequestNames.Exec,
|
||||
WantReply = false,
|
||||
Command = command,
|
||||
});
|
||||
this.SendExecRequest(command);
|
||||
|
||||
return _asyncResult;
|
||||
}
|
||||
@@ -193,34 +182,28 @@ namespace Renci.SshClient.Channels
|
||||
/// <param name="command">The command.</param>
|
||||
/// <param name="subsystemName">Name of the subsystem.</param>
|
||||
/// <param name="exitStatus">The exit status.</param>
|
||||
protected override void OnRequest(ChannelRequestNames requestName, bool wantReply, string command, string subsystemName, uint exitStatus)
|
||||
protected override void OnRequest(ChannelRequestMessage message)
|
||||
{
|
||||
base.OnRequest(requestName, wantReply, command, subsystemName, exitStatus);
|
||||
base.OnRequest(message);
|
||||
|
||||
Message replyMessage = new ChannelFailureMessage()
|
||||
{
|
||||
LocalChannelNumber = this.LocalChannelNumber,
|
||||
};
|
||||
|
||||
if (requestName == ChannelRequestNames.ExitStatus)
|
||||
if (message.RequestName == ChannelRequestExitStatusMessage.REQUEST_NAME)
|
||||
{
|
||||
this.ExitStatus = exitStatus;
|
||||
ChannelRequestExitStatusMessage exitStatusMessage = message.OfType<ChannelRequestExitStatusMessage>();
|
||||
|
||||
this.ExitStatus = exitStatusMessage.ExitStatus;
|
||||
|
||||
replyMessage = new ChannelSuccessMessage()
|
||||
{
|
||||
LocalChannelNumber = this.LocalChannelNumber,
|
||||
};
|
||||
}
|
||||
else if (requestName == ChannelRequestNames.PseudoTerminal)
|
||||
{
|
||||
// TODO: Check if when this request is received what to do, I suspect we receive this request when no more channel sessions are available
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(string.Format("Request name {0} is not implemented.", requestName));
|
||||
}
|
||||
|
||||
if (wantReply)
|
||||
if (message.WantReply)
|
||||
{
|
||||
this.SendMessage(replyMessage);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Renci.SshClient.Common;
|
||||
using Renci.SshClient.Messages.Connection;
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Channels
|
||||
@@ -30,11 +29,6 @@ namespace Renci.SshClient.Channels
|
||||
|
||||
private ChannelAsyncResult _asyncResult;
|
||||
|
||||
public override ChannelTypes ChannelType
|
||||
{
|
||||
get { return ChannelTypes.Session; }
|
||||
}
|
||||
|
||||
public ChannelSessionSftp()
|
||||
: base()
|
||||
{
|
||||
@@ -46,13 +40,7 @@ namespace Renci.SshClient.Channels
|
||||
base.Open();
|
||||
|
||||
// Send channel command request
|
||||
this.SendMessage(new ChannelRequestMessage
|
||||
{
|
||||
LocalChannelNumber = this.RemoteChannelNumber,
|
||||
RequestName = ChannelRequestNames.Subsystem,
|
||||
WantReply = true,
|
||||
SubsystemName = "sftp",
|
||||
});
|
||||
this.SendSubsystemRequest("sftp");
|
||||
|
||||
this.WaitHandle(this._channelRequestSuccessWaitHandle);
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Renci.SshClient.Messages.Connection;
|
||||
|
||||
namespace Renci.SshClient.Channels
|
||||
{
|
||||
internal class ChannelSessionShell : ChannelSession
|
||||
{
|
||||
|
||||
private EventWaitHandle _success = new AutoResetEvent(false);
|
||||
|
||||
/// <summary>
|
||||
/// Holds channel data stream
|
||||
/// </summary>
|
||||
private Stream _channelData;
|
||||
|
||||
/// <summary>
|
||||
/// Holds channel extended data stream
|
||||
/// </summary>
|
||||
private Stream _channelExtendedData;
|
||||
|
||||
public void Start(Stream output, Stream extendedOutput)
|
||||
{
|
||||
this.Open();
|
||||
|
||||
this._channelData = output;
|
||||
this._channelExtendedData = extendedOutput;
|
||||
|
||||
this.SendPseudoTerminalRequest("xterm", 80, 24, 640, 240, "");
|
||||
|
||||
_success.WaitOne();
|
||||
|
||||
this.SendShellRequest();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
// Close channel
|
||||
this.Close();
|
||||
}
|
||||
|
||||
protected override void OnSuccess()
|
||||
{
|
||||
base.OnSuccess();
|
||||
|
||||
_success.Set();
|
||||
}
|
||||
|
||||
protected override void OnFailure()
|
||||
{
|
||||
base.OnFailure();
|
||||
}
|
||||
|
||||
protected override void OnData(string data)
|
||||
{
|
||||
base.OnData(data);
|
||||
|
||||
this._channelData.Write(data.GetSshBytes().ToArray(), 0, data.Length);
|
||||
this._channelData.Flush();
|
||||
}
|
||||
|
||||
protected override void OnExtendedData(string data, uint dataTypeCode)
|
||||
{
|
||||
base.OnExtendedData(data, dataTypeCode);
|
||||
|
||||
// TODO: dataTypeCode is not handled
|
||||
this._channelExtendedData.Write(data.GetSshBytes().ToArray(), 0, data.Length);
|
||||
this._channelExtendedData.Flush();
|
||||
}
|
||||
|
||||
public void Send(string data)
|
||||
{
|
||||
this.SendMessage(new ChannelDataMessage
|
||||
{
|
||||
LocalChannelNumber = this.RemoteChannelNumber,
|
||||
Data = data,
|
||||
});
|
||||
}
|
||||
|
||||
public class ShellStream : MemoryStream
|
||||
{
|
||||
public ShellStream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
base.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
base.WriteByte(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ namespace Renci.SshClient.Common
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<byte> _loadedData;
|
||||
|
||||
public virtual IEnumerable<byte> GetBytes()
|
||||
{
|
||||
this._data = new List<byte>();
|
||||
@@ -32,6 +34,14 @@ namespace Renci.SshClient.Common
|
||||
return this._data;
|
||||
}
|
||||
|
||||
internal T OfType<T>() where T : SshData, new()
|
||||
{
|
||||
var result = new T();
|
||||
result.LoadBytes(this._loadedData);
|
||||
result.LoadData();
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract void LoadData();
|
||||
|
||||
protected abstract void SaveData();
|
||||
@@ -39,6 +49,7 @@ namespace Renci.SshClient.Common
|
||||
protected void LoadBytes(IEnumerable<byte> bytes)
|
||||
{
|
||||
this.ResetReader();
|
||||
this._loadedData = bytes.ToArray();
|
||||
this._data = new List<byte>(bytes);
|
||||
}
|
||||
|
||||
@@ -47,6 +58,17 @@ namespace Renci.SshClient.Common
|
||||
this._readerIndex = 1; // Set to 1 to skip first byte which specifies message type
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read all data
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected IEnumerable<byte> ReadBytes()
|
||||
{
|
||||
var result = this._data.Skip(this._readerIndex);
|
||||
this._readerIndex = this._data.Count;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected IEnumerable<byte> ReadBytes(int length)
|
||||
{
|
||||
var result = this._data.Skip(this._readerIndex).Take(length);
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestEnvironmentVariableMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "env";
|
||||
|
||||
public string VariableName { get; set; }
|
||||
|
||||
public string VariableValue { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
this.VariableName = this.ReadString();
|
||||
this.VariableValue = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.VariableName);
|
||||
this.Write(this.VariableValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestExecMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "exec";
|
||||
|
||||
public string Command { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
this.Command = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.Command);
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestExitSignalMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "exit-signal";
|
||||
|
||||
public string SignalName { get; set; }
|
||||
|
||||
public bool CoreDumped { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
this.SignalName = this.ReadString();
|
||||
this.CoreDumped = this.ReadBoolean();
|
||||
this.ErrorMessage = this.ReadString();
|
||||
this.Language = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.SignalName);
|
||||
this.Write(this.CoreDumped);
|
||||
this.Write(this.ErrorMessage);
|
||||
this.Write(this.Language);
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestExitStatusMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "exit-status";
|
||||
|
||||
public uint ExitStatus { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.ExitStatus = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
base.SaveData();
|
||||
this.Write(this.ExitStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelRequest; }
|
||||
}
|
||||
|
||||
public string RequestName { get; protected set; }
|
||||
|
||||
public bool WantReply { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
this.RequestName = this.ReadString();
|
||||
this.WantReply = this.ReadBoolean();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.RequestName);
|
||||
this.Write(this.WantReply);
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestPseudoTerminalMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "pty-req";
|
||||
|
||||
public string EnvironmentVariable { get; set; }
|
||||
|
||||
public uint Columns { get; set; }
|
||||
|
||||
public uint Rows { get; set; }
|
||||
|
||||
public uint PixelWidth { get; set; }
|
||||
|
||||
public uint PixelHeight { get; set; }
|
||||
|
||||
public string TerminalMode { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.EnvironmentVariable = this.ReadString();
|
||||
this.Columns = this.ReadUInt32();
|
||||
this.Rows = this.ReadUInt32();
|
||||
this.PixelWidth = this.ReadUInt32();
|
||||
this.PixelHeight = this.ReadUInt32();
|
||||
this.TerminalMode = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
base.SaveData();
|
||||
this.Write(this.EnvironmentVariable);
|
||||
this.Write(this.Columns);
|
||||
this.Write(this.Rows);
|
||||
this.Write(this.Rows);
|
||||
this.Write(this.PixelHeight);
|
||||
this.Write(this.TerminalMode);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestShellMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "shell";
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
base.SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestSignalMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "signal";
|
||||
|
||||
public string SignalName { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
this.SignalName = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.SignalName);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestSubsystemMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "subsystem";
|
||||
|
||||
public string SubsystemName { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
this.SubsystemName = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.SubsystemName);
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestWindowChangeMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "window-change";
|
||||
|
||||
public uint Columns { get; set; }
|
||||
|
||||
public uint Rows { get; set; }
|
||||
|
||||
public uint Width { get; set; }
|
||||
|
||||
public uint Height { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
this.Columns = this.ReadUInt32();
|
||||
this.Rows = this.ReadUInt32();
|
||||
this.Width = this.ReadUInt32();
|
||||
this.Height = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.Columns);
|
||||
this.Write(this.Rows);
|
||||
this.Write(this.Width);
|
||||
this.Write(this.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestX11ForwardingMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "x11-req";
|
||||
|
||||
public bool IsSingleConnection { get; set; }
|
||||
|
||||
public string AuthenticationProtocol { get; set; }
|
||||
|
||||
public string AuthenticationCookie { get; set; }
|
||||
|
||||
public uint ScreenNumber { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
this.IsSingleConnection = this.ReadBoolean();
|
||||
this.AuthenticationProtocol = this.ReadString();
|
||||
this.AuthenticationCookie = this.ReadString();
|
||||
this.ScreenNumber = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.IsSingleConnection);
|
||||
this.Write(this.AuthenticationProtocol);
|
||||
this.Write(this.AuthenticationCookie);
|
||||
this.Write(this.ScreenNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestXonXoffMessage : ChannelRequestMessage
|
||||
{
|
||||
public const string REQUEST_NAME = "xon-xoff";
|
||||
|
||||
public bool ClientCanDo { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
this.ClientCanDo = this.ReadBoolean();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.RequestName = REQUEST_NAME;
|
||||
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.ClientCanDo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelRequest; }
|
||||
}
|
||||
|
||||
public ChannelRequestNames RequestName { get; set; }
|
||||
|
||||
public bool WantReply { get; set; }
|
||||
|
||||
public string Command { get; set; }
|
||||
|
||||
public string SubsystemName { get; set; }
|
||||
|
||||
public uint ExitStatus { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
var requestName = this.ReadString();
|
||||
switch (requestName)
|
||||
{
|
||||
case "pty-req":
|
||||
break;
|
||||
case "x11-req":
|
||||
break;
|
||||
case "env":
|
||||
break;
|
||||
case "shell":
|
||||
break;
|
||||
case "exec":
|
||||
this.RequestName = ChannelRequestNames.Exec;
|
||||
this.WantReply = this.ReadBoolean();
|
||||
this.Command = this.ReadString();
|
||||
break;
|
||||
case "subsystem":
|
||||
this.RequestName = ChannelRequestNames.Subsystem;
|
||||
this.WantReply = this.ReadBoolean();
|
||||
this.SubsystemName = this.ReadString();
|
||||
break;
|
||||
case "window-change":
|
||||
break;
|
||||
case "xon-xoff":
|
||||
break;
|
||||
case "signal":
|
||||
break;
|
||||
case "exit-status":
|
||||
this.RequestName = ChannelRequestNames.ExitStatus;
|
||||
this.WantReply = this.ReadBoolean();
|
||||
this.ExitStatus = this.ReadUInt32();
|
||||
break;
|
||||
case "exit-signal":
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
|
||||
switch (this.RequestName)
|
||||
{
|
||||
case ChannelRequestNames.PseudoTerminal:
|
||||
break;
|
||||
case ChannelRequestNames.X11Forwarding:
|
||||
break;
|
||||
case ChannelRequestNames.EnvironmentVariable:
|
||||
break;
|
||||
case ChannelRequestNames.Shell:
|
||||
break;
|
||||
case ChannelRequestNames.Exec:
|
||||
this.Write("exec");
|
||||
this.Write(this.WantReply);
|
||||
this.Write(this.Command);
|
||||
break;
|
||||
case ChannelRequestNames.Subsystem:
|
||||
this.Write("subsystem");
|
||||
this.Write(this.WantReply);
|
||||
this.Write(this.SubsystemName);
|
||||
break;
|
||||
case ChannelRequestNames.WindowChange:
|
||||
break;
|
||||
case ChannelRequestNames.XonXoff:
|
||||
break;
|
||||
case ChannelRequestNames.Signal:
|
||||
break;
|
||||
case ChannelRequestNames.ExitStatus:
|
||||
this.Write("exit-status");
|
||||
this.Write(this.WantReply);
|
||||
this.Write(this.ExitStatus);
|
||||
break;
|
||||
case ChannelRequestNames.ExitSignal:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal enum ChannelRequestNames
|
||||
{
|
||||
/// <summary>
|
||||
/// pty-req
|
||||
/// </summary>
|
||||
PseudoTerminal,
|
||||
/// <summary>
|
||||
/// x11-req
|
||||
/// </summary>
|
||||
X11Forwarding,
|
||||
/// <summary>
|
||||
/// env
|
||||
/// </summary>
|
||||
EnvironmentVariable,
|
||||
/// <summary>
|
||||
/// shell
|
||||
/// </summary>
|
||||
Shell,
|
||||
/// <summary>
|
||||
/// exec
|
||||
/// </summary>
|
||||
Exec,
|
||||
/// <summary>
|
||||
/// subsystem
|
||||
/// </summary>
|
||||
Subsystem,
|
||||
/// <summary>
|
||||
/// window-change
|
||||
/// </summary>
|
||||
WindowChange,
|
||||
/// <summary>
|
||||
/// xon-xoff
|
||||
/// </summary>
|
||||
XonXoff,
|
||||
/// <summary>
|
||||
/// signal
|
||||
/// </summary>
|
||||
Signal,
|
||||
/// <summary>
|
||||
/// exit-status
|
||||
/// </summary>
|
||||
ExitStatus,
|
||||
/// <summary>
|
||||
/// exit-signal
|
||||
/// </summary>
|
||||
ExitSignal
|
||||
}
|
||||
}
|
||||
@@ -60,10 +60,21 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Channels\ChannelSessionShell.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestEnvironmentVariableMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestExecMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestExitSignalMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestExitStatusMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestPseudoTerminalMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestShellMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestSignalMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestSubsystemMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestWindowChangeMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestX11ForwardingMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestXonXoffMessage.cs" />
|
||||
<Compile Include="SshCommand.cs" />
|
||||
<Compile Include="MessageEventArgs.cs" />
|
||||
<Compile Include="Channels\ChannelAsyncResult.cs" />
|
||||
<Compile Include="Channels\ChannelEventArgs.cs" />
|
||||
<Compile Include="Channels\ChannelDirectTcpip.cs" />
|
||||
<Compile Include="Channels\ChannelForwardedTcpip.cs" />
|
||||
<Compile Include="Channels\ChannelSession.cs" />
|
||||
@@ -136,7 +147,6 @@
|
||||
<Compile Include="Security\KeyExchangeSendMessageEventArgs.cs" />
|
||||
<Compile Include="PrivateKeyFile.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequestNames.cs" />
|
||||
<Compile Include="Security\UserAuthentication.cs" />
|
||||
<Compile Include="Security\UserAuthenticationHost.cs" />
|
||||
<Compile Include="Security\UserAuthenticationNone.cs" />
|
||||
@@ -157,7 +167,7 @@
|
||||
<Compile Include="Messages\Connection\ChannelOpenFailureMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelOpenFailureReasons.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelOpenMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequestMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\ChannelRequestMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelSuccessMessage.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelWindowAdjustMessage.cs" />
|
||||
<Compile Include="Messages\Connection\GlobalRequestMessage.cs" />
|
||||
|
||||
@@ -442,12 +442,19 @@ namespace Renci.SshClient
|
||||
// Get server SSH version
|
||||
var version = versionMatch.Result("${protoversion}");
|
||||
|
||||
if (!version.Equals("2.0"))
|
||||
if (!(version.Equals("2.0") || version.Equals("1.99")))
|
||||
{
|
||||
throw new SshException(string.Format("Server version '{0}' is not supported.", version), DisconnectReasons.ProtocolVersionNotSupported);
|
||||
}
|
||||
|
||||
this.Write(Encoding.ASCII.GetBytes(string.Format("{0}\n", this.ClientVersion)));
|
||||
if (version.Equals("1.99"))
|
||||
{
|
||||
this.Write(Encoding.ASCII.GetBytes(string.Format("{0}", this.ClientVersion)));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Write(Encoding.ASCII.GetBytes(string.Format("{0}\n", this.ClientVersion)));
|
||||
}
|
||||
|
||||
// Register Transport response messages
|
||||
this.RegisterMessageType<DisconnectMessage>(MessageTypes.Disconnect);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Renci.SshClient.Channels;
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
@@ -9,48 +7,27 @@ namespace Renci.SshClient
|
||||
{
|
||||
private readonly Session _session;
|
||||
|
||||
private ChannelSessionShell _channel;
|
||||
|
||||
internal Shell(Session session)
|
||||
{
|
||||
this._session = session;
|
||||
}
|
||||
|
||||
public string Execute(string command)
|
||||
public void Connect(Stream output, Stream extendedOutput)
|
||||
{
|
||||
return this.Execute(command, null);
|
||||
this._channel = this._session.CreateChannel<ChannelSessionShell>();
|
||||
this._channel.Start(output, extendedOutput);
|
||||
}
|
||||
|
||||
public string Execute(string command, Stream extended)
|
||||
public void Send(string data)
|
||||
{
|
||||
using (MemoryStream resultStream = new MemoryStream())
|
||||
{
|
||||
this.Execute(command, resultStream, extended);
|
||||
|
||||
return Encoding.ASCII.GetString(resultStream.ToArray());
|
||||
}
|
||||
this._channel.Send(data);
|
||||
}
|
||||
|
||||
public void Execute(string command, Stream output, Stream extended)
|
||||
public void Disconnect()
|
||||
{
|
||||
this.EndExecute(this.BeginExecute(command, output, extended, null, null));
|
||||
}
|
||||
|
||||
public IAsyncResult BeginExecute(string command, Stream output, AsyncCallback callback, object state)
|
||||
{
|
||||
return this.BeginExecute(command, output, null, callback, state);
|
||||
}
|
||||
|
||||
public IAsyncResult BeginExecute(string command, Stream output, Stream extendedOutput, AsyncCallback callback, object state)
|
||||
{
|
||||
var channel = this._session.CreateChannel<ChannelSessionExec>();
|
||||
|
||||
return channel.BeginExecute(command, output, extendedOutput, callback, state);
|
||||
}
|
||||
|
||||
public void EndExecute(IAsyncResult asynchResult)
|
||||
{
|
||||
ChannelAsyncResult channelAsyncResult = asynchResult as ChannelAsyncResult;
|
||||
|
||||
channelAsyncResult.Channel.EndExecute(asynchResult);
|
||||
this._channel.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,11 @@ namespace Renci.SshClient
|
||||
return cmd;
|
||||
}
|
||||
|
||||
public Shell CreateShell()
|
||||
{
|
||||
return new Shell(this._session);
|
||||
}
|
||||
|
||||
public void RemoveForwardedPort(ForwardedPort port)
|
||||
{
|
||||
this._forwardedPorts.Remove(port);
|
||||
|
||||
Reference in New Issue
Block a user