diff --git a/Renci.SshClient/Renci.SshNet/Channels/Channel.cs b/Renci.SshClient/Renci.SshNet/Channels/Channel.cs index e06f6a73..02a86a17 100644 --- a/Renci.SshClient/Renci.SshNet/Channels/Channel.cs +++ b/Renci.SshClient/Renci.SshNet/Channels/Channel.cs @@ -3,6 +3,7 @@ using System.Threading; using Renci.SshNet.Common; using Renci.SshNet.Messages; using Renci.SshNet.Messages.Connection; +using System.Globalization; namespace Renci.SshNet.Channels { @@ -540,7 +541,21 @@ namespace Renci.SshNet.Channels { if (e.Message.LocalChannelNumber == this.LocalChannelNumber) { - this.OnRequest(e.Message.Info); + if (this._session.ConnectionInfo.ChannelRequests.ContainsKey(e.Message.RequestName)) + { + // Get request specific class + RequestInfo requestInfo = this._session.ConnectionInfo.ChannelRequests[e.Message.RequestName]; + + // Load request specific data + requestInfo.Load(e.Message.RequestData); + + // Raise request specific event + this.OnRequest(requestInfo); + } + else + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Request '{0}' is not supported.", e.Message.RequestName)); + } } } diff --git a/Renci.SshClient/Renci.SshNet/Channels/ChannelSession.cs b/Renci.SshClient/Renci.SshNet/Channels/ChannelSession.cs index 79cf94c6..473e9af5 100644 --- a/Renci.SshClient/Renci.SshNet/Channels/ChannelSession.cs +++ b/Renci.SshClient/Renci.SshNet/Channels/ChannelSession.cs @@ -7,7 +7,7 @@ using System.Globalization; namespace Renci.SshNet.Channels { /// - /// Implements "forwarded-tcpip" SSH channel. + /// Implements Session SSH channel. /// internal class ChannelSession : Channel { @@ -271,6 +271,37 @@ namespace Renci.SshNet.Channels return true; } + /// + /// Sends eow@openssh.com request. + /// + /// true if request was successful; otherwise false. + public bool SendEndOfWriteRequest() + { + this._channelRequestResponse.Reset(); + + this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EndOfWriteRequestInfo())); + + this._channelRequestResponse.WaitOne(); + + return this._channelRequestSucces; + } + + /// + /// Sends keepalive@openssh.com request. + /// + /// true if request was successful; otherwise false. + public bool SendKeepAliveRequest() + { + this._channelRequestResponse.Reset(); + + this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new KeepAliveRequestInfo())); + + this._channelRequestResponse.WaitOne(); + + return this._channelRequestSucces; + } + + /// /// Called when channel request was successful /// diff --git a/Renci.SshClient/Renci.SshNet/ConnectionInfo.cs b/Renci.SshClient/Renci.SshNet/ConnectionInfo.cs index 6b8a48e4..61caefdb 100644 --- a/Renci.SshClient/Renci.SshNet/ConnectionInfo.cs +++ b/Renci.SshClient/Renci.SshNet/ConnectionInfo.cs @@ -8,6 +8,7 @@ using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Common; using System.Threading; using System.Net; +using Renci.SshNet.Messages.Connection; namespace Renci.SshNet { /// @@ -68,6 +69,11 @@ namespace Renci.SshNet /// public IDictionary CompressionAlgorithms { get; private set; } + /// + /// Gets supported channel requests for this connection. + /// + public IDictionary ChannelRequests { get; private set; } + /// /// Gets connection host. /// @@ -194,6 +200,22 @@ namespace Renci.SshNet {"zlib@openssh.com", typeof(ZlibOpenSsh)}, }; + this.ChannelRequests = new Dictionary() + { + {EnvironmentVariableRequestInfo.NAME, new EnvironmentVariableRequestInfo()}, + {ExecRequestInfo.NAME, new ExecRequestInfo()}, + {ExitSignalRequestInfo.NAME, new ExitSignalRequestInfo()}, + {ExitStatusRequestInfo.NAME, new ExitStatusRequestInfo()}, + {PseudoTerminalRequestInfo.NAME, new PseudoTerminalRequestInfo()}, + {ShellRequestInfo.NAME, new ShellRequestInfo()}, + {SignalRequestInfo.NAME, new SignalRequestInfo()}, + {SubsystemRequestInfo.NAME, new SubsystemRequestInfo()}, + {WindowChangeRequestInfo.NAME, new WindowChangeRequestInfo()}, + {X11ForwardingRequestInfo.NAME, new X11ForwardingRequestInfo()}, + {XonXoffRequestInfo.NAME, new XonXoffRequestInfo()}, + {EndOfWriteRequestInfo.NAME, new EndOfWriteRequestInfo()}, + {KeepAliveRequestInfo.NAME, new KeepAliveRequestInfo()}, + }; } /// diff --git a/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs b/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs index e1be096a..cf84643b 100644 --- a/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs +++ b/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs @@ -15,32 +15,12 @@ namespace Renci.SshNet.Messages.Connection /// /// The name of the request. /// - public string RequestName - { - get - { - return this.Info.RequestName; - } - } + public string RequestName { get; private set; } /// - /// Gets a value indicating whether the reply is needed. + /// Gets channel request data. /// - /// - /// true if reply is needed; otherwise, false. - /// - public bool WantReply - { - get - { - return this.Info.WantReply; - } - } - - /// - /// Gets channel request information. - /// - public RequestInfo Info { get; private set; } + public byte[] RequestData { get; private set; } /// /// Initializes a new instance of the class. @@ -58,7 +38,8 @@ namespace Renci.SshNet.Messages.Connection public ChannelRequestMessage(uint localChannelName, RequestInfo info) { this.LocalChannelNumber = localChannelName; - this.Info = info; + this.RequestName = info.RequestName; + this.RequestData = info.GetBytes(); } /// @@ -68,59 +49,8 @@ namespace Renci.SshNet.Messages.Connection { base.LoadData(); - var requestName = this.ReadString(); - var bytes = this.ReadBytes(); - - if (requestName == EnvironmentVariableRequestInfo.NAME) - { - this.Info = new EnvironmentVariableRequestInfo(); - } - else if (requestName == ExecRequestInfo.NAME) - { - this.Info = new ExecRequestInfo(); - } - else if (requestName == ExitSignalRequestInfo.NAME) - { - this.Info = new ExitSignalRequestInfo(); - } - else if (requestName == ExitStatusRequestInfo.NAME) - { - this.Info = new ExitStatusRequestInfo(); - } - else if (requestName == PseudoTerminalRequestInfo.NAME) - { - this.Info = new PseudoTerminalRequestInfo(); - } - else if (requestName == ShellRequestInfo.NAME) - { - this.Info = new ShellRequestInfo(); - } - else if (requestName == SignalRequestInfo.NAME) - { - this.Info = new SignalRequestInfo(); - } - else if (requestName == SubsystemRequestInfo.NAME) - { - this.Info = new SubsystemRequestInfo(); - } - else if (requestName == WindowChangeRequestInfo.NAME) - { - this.Info = new WindowChangeRequestInfo(); - } - else if (requestName == X11ForwardingRequestInfo.NAME) - { - this.Info = new X11ForwardingRequestInfo(); - } - else if (requestName == XonXoffRequestInfo.NAME) - { - this.Info = new XonXoffRequestInfo(); - } - else - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Request '{0}' is not supported.", requestName)); - } - - this.Info.Load(bytes); + this.RequestName = this.ReadString(); + this.RequestData = this.ReadBytes(); } /// @@ -131,7 +61,7 @@ namespace Renci.SshNet.Messages.Connection base.SaveData(); this.Write(this.RequestName); - this.Write(this.Info.GetBytes()); + this.Write(this.RequestData); } } } diff --git a/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs b/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs new file mode 100644 index 00000000..5ef73c00 --- /dev/null +++ b/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Messages.Connection +{ + public class EndOfWriteRequestInfo : RequestInfo + { + /// + /// Channel request name + /// + public const string NAME = "eow@openssh.com"; + + /// + /// Gets the name of the request. + /// + /// + /// The name of the request. + /// + public override string RequestName + { + get { return EndOfWriteRequestInfo.NAME; } + } + + /// + /// Initializes a new instance of the class. + /// + public EndOfWriteRequestInfo() + { + this.WantReply = false; + } + } +} diff --git a/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs b/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs new file mode 100644 index 00000000..048730ce --- /dev/null +++ b/Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Messages.Connection +{ + public class KeepAliveRequestInfo : RequestInfo + { + /// + /// Channel request name + /// + public const string NAME = "keepalive@openssh.com"; + + /// + /// Gets the name of the request. + /// + /// + /// The name of the request. + /// + public override string RequestName + { + get { return KeepAliveRequestInfo.NAME; } + } + + /// + /// Initializes a new instance of the class. + /// + public KeepAliveRequestInfo() + { + this.WantReply = false; + } + } +} diff --git a/Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj b/Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj index 19c5c237..ee69304e 100644 --- a/Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj +++ b/Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj @@ -103,6 +103,8 @@ Code + + Code