<PackageReference Include="SSH.NET" Version="2016.0.0-beta2" />

ForwardedPortRemote

Provides functionality for remote port forwarding
using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; using System; using System.Globalization; using System.Net; using System.Threading; namespace Renci.SshNet { public class ForwardedPortRemote : ForwardedPort, IDisposable { private bool _requestStatus; private EventWaitHandle _globalRequestResponse = new AutoResetEvent(false); private int _pendingRequests; private bool _isStarted; private bool _isDisposed; public override bool IsStarted => _isStarted; public IPAddress BoundHostAddress { get; set; } public string BoundHost => BoundHostAddress.ToString(); public uint BoundPort { get; set; } public IPAddress HostAddress { get; set; } public string Host => HostAddress.ToString(); public uint Port { get; set; } public ForwardedPortRemote(IPAddress boundHostAddress, uint boundPort, IPAddress hostAddress, uint port) { if (boundHostAddress == null) throw new ArgumentNullException("boundHostAddress"); if (hostAddress == null) throw new ArgumentNullException("hostAddress"); boundPort.ValidatePort("boundPort"); port.ValidatePort("port"); BoundHostAddress = boundHostAddress; BoundPort = boundPort; HostAddress = hostAddress; Port = port; } public ForwardedPortRemote(uint boundPort, string host, uint port) : this(string.Empty, boundPort, host, port) { } public ForwardedPortRemote(string boundHost, uint boundPort, string host, uint port) : this(DnsAbstraction.GetHostAddresses(boundHost)[0], boundPort, DnsAbstraction.GetHostAddresses(host)[0], port) { } protected override void StartPort() { base.Session.RegisterMessage("SSH_MSG_REQUEST_FAILURE"); base.Session.RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); base.Session.RegisterMessage("SSH_MSG_CHANNEL_OPEN"); base.Session.RequestSuccessReceived += Session_RequestSuccess; base.Session.RequestFailureReceived += Session_RequestFailure; base.Session.ChannelOpenReceived += Session_ChannelOpening; base.Session.SendMessage(new GlobalRequestMessage(GlobalRequestName.TcpIpForward, true, BoundHost, BoundPort)); base.Session.WaitOnHandle(_globalRequestResponse); if (!_requestStatus) { base.Session.RequestSuccessReceived -= Session_RequestSuccess; base.Session.RequestFailureReceived -= Session_RequestFailure; base.Session.ChannelOpenReceived -= Session_ChannelOpening; throw new SshException(string.Format(CultureInfo.CurrentCulture, "Port forwarding for '{0}' port '{1}' failed to start.", new object[2] { Host, Port })); } _isStarted = true; } protected override void StopPort(TimeSpan timeout) { if (IsStarted) { _isStarted = false; base.StopPort(timeout); base.Session.SendMessage(new GlobalRequestMessage(GlobalRequestName.CancelTcpIpForward, true, BoundHost, BoundPort)); WaitHandle.WaitAny(new WaitHandle[2] { _globalRequestResponse, base.Session.MessageListenerCompleted }, timeout); base.Session.RequestSuccessReceived -= Session_RequestSuccess; base.Session.RequestFailureReceived -= Session_RequestFailure; base.Session.ChannelOpenReceived -= Session_ChannelOpening; DateTime now = DateTime.Now; while (Interlocked.CompareExchange(ref _pendingRequests, 0, 0) != 0 && (!(DateTime.Now - now >= timeout) || !(timeout != Renci.SshNet.Session.InfiniteTimeSpan))) { ThreadAbstraction.Sleep(50); } } } protected override void CheckDisposed() { if (_isDisposed) throw new ObjectDisposedException(GetType().FullName); } private void Session_ChannelOpening(object sender, MessageEventArgs<ChannelOpenMessage> e) { ChannelOpenMessage channelOpenMessage = e.Message; ForwardedTcpipChannelInfo info = channelOpenMessage.Info as ForwardedTcpipChannelInfo; if (info != null && info.ConnectedAddress == BoundHost && info.ConnectedPort == BoundPort) { if (!_isStarted) base.Session.SendMessage(new ChannelOpenFailureMessage(channelOpenMessage.LocalChannelNumber, "", 1)); else ThreadAbstraction.ExecuteThread(delegate { Interlocked.Increment(ref _pendingRequests); try { RaiseRequestReceived(info.OriginatorAddress, info.OriginatorPort); using (IChannelForwardedTcpip channelForwardedTcpip = base.Session.CreateChannelForwardedTcpip(channelOpenMessage.LocalChannelNumber, channelOpenMessage.InitialWindowSize, channelOpenMessage.MaximumPacketSize)) { channelForwardedTcpip.Exception += Channel_Exception; channelForwardedTcpip.Bind(new IPEndPoint(HostAddress, (int)Port), this); channelForwardedTcpip.Close(); } } catch (Exception exception) { RaiseExceptionEvent(exception); } finally { Interlocked.Decrement(ref _pendingRequests); } }); } } private void Channel_Exception(object sender, ExceptionEventArgs exceptionEventArgs) { RaiseExceptionEvent(exceptionEventArgs.Exception); } private void Session_RequestFailure(object sender, EventArgs e) { _requestStatus = false; _globalRequestResponse.Set(); } private void Session_RequestSuccess(object sender, MessageEventArgs<RequestSuccessMessage> e) { _requestStatus = true; if (BoundPort == 0) BoundPort = (e.Message.BoundPort.HasValue ? e.Message.BoundPort.Value : 0); _globalRequestResponse.Set(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected override void Dispose(bool disposing) { if (!_isDisposed) { base.Dispose(disposing); if (disposing) { ISession session = base.Session; if (session != null) { session.RequestSuccessReceived -= Session_RequestSuccess; session.RequestFailureReceived -= Session_RequestFailure; session.ChannelOpenReceived -= Session_ChannelOpening; base.Session = null; } EventWaitHandle globalRequestResponse = _globalRequestResponse; if (globalRequestResponse != null) { globalRequestResponse.Dispose(); _globalRequestResponse = null; } } _isDisposed = true; } } ~ForwardedPortRemote() { Dispose(false); } } }