Refactor and complete ChannelRequestMessage for different channel request;

Add support for version 1.99;
Add shell/terminal like capabilities;
This commit is contained in:
olegkap_cp
2010-11-17 18:29:39 +00:00
parent c5bd6c203e
commit a8adef465c
24 changed files with 717 additions and 244 deletions
@@ -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);
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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();
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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" />
+9 -2
View File
@@ -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);
+9 -32
View File
@@ -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);