diff --git a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs
index 811f6f5d..006c04c2 100644
--- a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs
+++ b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs
@@ -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);
}
}
diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelSession.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelSession.cs
index c882a4db..0eb18841 100644
--- a/Renci.SshClient/Renci.SshClient/Channels/ChannelSession.cs
+++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelSession.cs
@@ -17,6 +17,15 @@ namespace Renci.SshClient.Channels
///
private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(false);
+ private EventWaitHandle _channelRequestResponse = new ManualResetEvent(false);
+
+ private bool _channelRequestSucces;
+
+ public override ChannelTypes ChannelType
+ {
+ get { return ChannelTypes.Session; }
+ }
+
///
/// Opens the channel
///
@@ -53,6 +62,23 @@ namespace Renci.SshClient.Channels
this._channelOpenResponseWaitHandle.Set();
}
+ ///
+ /// Called when channel failed to open
+ ///
+ /// The reason code.
+ /// The description.
+ /// The language.
+ 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();
+ }
+
///
/// Called when channel is closed
///
@@ -70,21 +96,168 @@ namespace Renci.SshClient.Channels
this.SessionSemaphore.Release();
}
- ///
- /// Called when channel failed to open
- ///
- /// The reason code.
- /// The description.
- /// The language.
- 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();
}
///
diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionExec.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionExec.cs
index 49018dc3..e0d468c2 100644
--- a/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionExec.cs
+++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionExec.cs
@@ -23,11 +23,6 @@ namespace Renci.SshClient.Channels
private AsyncCallback _callback;
- public override ChannelTypes ChannelType
- {
- get { return ChannelTypes.Session; }
- }
-
///
/// Gets or sets a value indicating whether this channel has error.
///
@@ -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
/// The command.
/// Name of the subsystem.
/// The exit status.
- 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();
+
+ 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);
}
diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionSftp.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionSftp.cs
index 2dcbe925..f9252e4e 100644
--- a/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionSftp.cs
+++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionSftp.cs
@@ -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);
diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionShell.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionShell.cs
new file mode 100644
index 00000000..4bcb9037
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionShell.cs
@@ -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);
+
+ ///
+ /// Holds channel data stream
+ ///
+ private Stream _channelData;
+
+ ///
+ /// Holds channel extended data stream
+ ///
+ 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);
+ }
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Common/SshData.cs b/Renci.SshClient/Renci.SshClient/Common/SshData.cs
index 10cc0e49..bf46e142 100644
--- a/Renci.SshClient/Renci.SshClient/Common/SshData.cs
+++ b/Renci.SshClient/Renci.SshClient/Common/SshData.cs
@@ -23,6 +23,8 @@ namespace Renci.SshClient.Common
}
}
+ private IEnumerable _loadedData;
+
public virtual IEnumerable GetBytes()
{
this._data = new List();
@@ -32,6 +34,14 @@ namespace Renci.SshClient.Common
return this._data;
}
+ internal T OfType() 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 bytes)
{
this.ResetReader();
+ this._loadedData = bytes.ToArray();
this._data = new List(bytes);
}
@@ -47,6 +58,17 @@ namespace Renci.SshClient.Common
this._readerIndex = 1; // Set to 1 to skip first byte which specifies message type
}
+ ///
+ /// Read all data
+ ///
+ ///
+ protected IEnumerable ReadBytes()
+ {
+ var result = this._data.Skip(this._readerIndex);
+ this._readerIndex = this._data.Count;
+ return result;
+ }
+
protected IEnumerable ReadBytes(int length)
{
var result = this._data.Skip(this._readerIndex).Take(length);
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestEnvironmentVariableMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestEnvironmentVariableMessage.cs
new file mode 100644
index 00000000..ceb10fc6
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestEnvironmentVariableMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestExecMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestExecMessage.cs
new file mode 100644
index 00000000..51fe229b
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestExecMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestExitSignalMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestExitSignalMessage.cs
new file mode 100644
index 00000000..af1ce2e9
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestExitSignalMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestExitStatusMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestExitStatusMessage.cs
new file mode 100644
index 00000000..a84f840e
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestExitStatusMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs
new file mode 100644
index 00000000..118923e7
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestPseudoTerminalMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestPseudoTerminalMessage.cs
new file mode 100644
index 00000000..587d1bc5
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestPseudoTerminalMessage.cs
@@ -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);
+
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestShellMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestShellMessage.cs
new file mode 100644
index 00000000..2eeb4a72
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestShellMessage.cs
@@ -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();
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestSignalMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestSignalMessage.cs
new file mode 100644
index 00000000..dcee7d7e
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestSignalMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestSubsystemMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestSubsystemMessage.cs
new file mode 100644
index 00000000..34ddabe4
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestSubsystemMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestWindowChangeMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestWindowChangeMessage.cs
new file mode 100644
index 00000000..06aac41b
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestWindowChangeMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestX11ForwardingMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestX11ForwardingMessage.cs
new file mode 100644
index 00000000..7e34813e
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestX11ForwardingMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestXonXoffMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestXonXoffMessage.cs
new file mode 100644
index 00000000..ad1a1c04
--- /dev/null
+++ b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequest/ChannelRequestXonXoffMessage.cs
@@ -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);
+ }
+ }
+}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequestMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequestMessage.cs
deleted file mode 100644
index eeac5587..00000000
--- a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequestMessage.cs
+++ /dev/null
@@ -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;
- }
- }
- }
-}
diff --git a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequestNames.cs b/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequestNames.cs
deleted file mode 100644
index 8ad83d0d..00000000
--- a/Renci.SshClient/Renci.SshClient/Messages/Connection/ChannelRequestNames.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-namespace Renci.SshClient.Messages.Connection
-{
- internal enum ChannelRequestNames
- {
- ///
- /// pty-req
- ///
- PseudoTerminal,
- ///
- /// x11-req
- ///
- X11Forwarding,
- ///
- /// env
- ///
- EnvironmentVariable,
- ///
- /// shell
- ///
- Shell,
- ///
- /// exec
- ///
- Exec,
- ///
- /// subsystem
- ///
- Subsystem,
- ///
- /// window-change
- ///
- WindowChange,
- ///
- /// xon-xoff
- ///
- XonXoff,
- ///
- /// signal
- ///
- Signal,
- ///
- /// exit-status
- ///
- ExitStatus,
- ///
- /// exit-signal
- ///
- ExitSignal
- }
-}
diff --git a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj
index 87119f09..b89bc333 100644
--- a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj
+++ b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj
@@ -60,10 +60,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
@@ -136,7 +147,6 @@
-
@@ -157,7 +167,7 @@
-
+
diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs
index 8c610ab8..f06442e0 100644
--- a/Renci.SshClient/Renci.SshClient/Session.cs
+++ b/Renci.SshClient/Renci.SshClient/Session.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(MessageTypes.Disconnect);
diff --git a/Renci.SshClient/Renci.SshClient/Shell.cs b/Renci.SshClient/Renci.SshClient/Shell.cs
index 268f98dd..d562402d 100644
--- a/Renci.SshClient/Renci.SshClient/Shell.cs
+++ b/Renci.SshClient/Renci.SshClient/Shell.cs
@@ -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();
+ 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();
-
- 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();
}
}
}
diff --git a/Renci.SshClient/Renci.SshClient/SshClient.cs b/Renci.SshClient/Renci.SshClient/SshClient.cs
index 4a0aabe7..58bd77b8 100644
--- a/Renci.SshClient/Renci.SshClient/SshClient.cs
+++ b/Renci.SshClient/Renci.SshClient/SshClient.cs
@@ -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);