using System; using System.Threading; namespace Renci.SshNet { /// /// Provides functionality for local port forwarding /// public partial class ForwardedPortLocal : ForwardedPort, IDisposable { private EventWaitHandle _listenerTaskCompleted; /// /// Gets the bound host. /// public string BoundHost { get; private set; } /// /// Gets the bound port. /// public uint BoundPort { get; private set; } /// /// Gets the forwarded host. /// public string Host { get; private set; } /// /// Gets the forwarded port. /// public uint Port { get; private set; } /// /// Gets or sets a value indicating whether port forwarding is started. /// /// /// true if port forwarding is started; otherwise, false. /// public override bool IsStarted { get { return _listenerTaskCompleted != null && !_listenerTaskCompleted.WaitOne(0); } } /// /// Initializes a new instance of the class. /// /// The bound port. /// The host. /// The port. /// is greater than . /// is null. /// is greater than . /// /// /// public ForwardedPortLocal(uint boundPort, string host, uint port) : this(string.Empty, boundPort, host, port) { } /// /// Initializes a new instance of the class. /// /// The bound host. /// The host. /// The port. /// is null. /// is null. /// is greater than . public ForwardedPortLocal(string boundHost, string host, uint port) : this(boundHost, 0, host, port) { } /// /// Initializes a new instance of the class. /// /// The bound host. /// The bound port. /// The host. /// The port. /// is null. /// is null. /// is greater than . /// is greater than . public ForwardedPortLocal(string boundHost, uint boundPort, string host, uint port) { if (boundHost == null) throw new ArgumentNullException("boundHost"); if (host == null) throw new ArgumentNullException("host"); boundPort.ValidatePort("boundPort"); port.ValidatePort("port"); this.BoundHost = boundHost; this.BoundPort = boundPort; this.Host = host; this.Port = port; } /// /// Starts local port forwarding. /// protected override void StartPort() { this.InternalStart(); } /// /// Stops local port forwarding, and waits for the specified timeout until all pending /// requests are processed. /// /// The maximum amount of time to wait for pending requests to finish processing. protected override void StopPort(TimeSpan timeout) { if (IsStarted) { // prevent new requests from getting processed before we signal existing // channels that the port is closing StopListener(); // signal existing channels that the port is closing base.StopPort(timeout); } // wait for open channels to close InternalStop(timeout); } /// /// Ensures the current instance is not disposed. /// /// The current instance is disposed. protected override void CheckDisposed() { if (_isDisposed) throw new ObjectDisposedException(GetType().FullName); } partial void InternalStart(); /// /// Interrupts the listener, and waits for the listener loop to finish. /// /// /// When the forwarded port is stopped, then any further action is skipped. /// partial void StopListener(); partial void InternalStop(TimeSpan timeout); #region IDisposable Members private bool _isDisposed; /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. protected override void Dispose(bool disposing) { if (!_isDisposed) { base.Dispose(disposing); if (disposing) { if (_listenerTaskCompleted != null) { _listenerTaskCompleted.Dispose(); _listenerTaskCompleted = null; } } _isDisposed = true; } } /// /// Releases unmanaged resources and performs other cleanup operations before the /// is reclaimed by garbage collection. /// ~ForwardedPortLocal() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } #endregion } }