ChannelForwardedTcpip
Implements "forwarded-tcpip" SSH channel.
            
                using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
using System;
using System.Net;
using System.Net.Sockets;
namespace Renci.SshNet.Channels
{
    internal class ChannelForwardedTcpip : ServerChannel, IChannelForwardedTcpip, IDisposable
    {
        private readonly object _socketShutdownAndCloseLock = new object();
        private Socket _socket;
        private IForwardedPort _forwardedPort;
        public override ChannelTypes ChannelType => ChannelTypes.ForwardedTcpip;
        internal ChannelForwardedTcpip(ISession session, uint localChannelNumber, uint localWindowSize, uint localPacketSize, uint remoteChannelNumber, uint remoteWindowSize, uint remotePacketSize)
            : base(session, localChannelNumber, localWindowSize, localPacketSize, remoteChannelNumber, remoteWindowSize, remotePacketSize)
        {
        }
        public void Bind(IPEndPoint remoteEndpoint, IForwardedPort forwardedPort)
        {
            if (!base.IsConnected)
                throw new SshException("Session is not connected.");
            _forwardedPort = forwardedPort;
            _forwardedPort.Closing += ForwardedPort_Closing;
            try {
                _socket = SocketAbstraction.Connect(remoteEndpoint, base.ConnectionInfo.Timeout);
                SendMessage(new ChannelOpenConfirmationMessage(base.RemoteChannelNumber, base.LocalWindowSize, base.LocalPacketSize, base.LocalChannelNumber));
            } catch (Exception ex) {
                SendMessage(new ChannelOpenFailureMessage(base.RemoteChannelNumber, ex.ToString(), 2, "en"));
                throw;
            }
            byte[] array = new byte[base.RemotePacketSize];
            SocketAbstraction.ReadContinuous(_socket, array, 0, array.Length, SendData);
        }
        protected override void OnErrorOccured(Exception exp)
        {
            base.OnErrorOccured(exp);
            ShutdownSocket(SocketShutdown.Send);
        }
        private void ForwardedPort_Closing(object sender, EventArgs eventArgs)
        {
            ShutdownSocket(SocketShutdown.Send);
        }
        private void ShutdownSocket(SocketShutdown how)
        {
            if (_socket != null) {
                lock (_socketShutdownAndCloseLock) {
                    Socket socket = _socket;
                    if (socket.IsConnected())
                        try {
                            socket.Shutdown(how);
                        } catch (SocketException) {
                        }
                }
            }
        }
        private void CloseSocket()
        {
            if (_socket != null) {
                lock (_socketShutdownAndCloseLock) {
                    Socket socket = _socket;
                    if (socket != null) {
                        _socket = null;
                        Extensions.Dispose(socket);
                    }
                }
            }
        }
        protected override void Close()
        {
            IForwardedPort forwardedPort = _forwardedPort;
            if (forwardedPort != null) {
                forwardedPort.Closing -= ForwardedPort_Closing;
                _forwardedPort = null;
            }
            ShutdownSocket(SocketShutdown.Send);
            base.Close();
            CloseSocket();
        }
        protected override void OnData(byte[] data)
        {
            base.OnData(data);
            Socket socket = _socket;
            if (socket.IsConnected())
                SocketAbstraction.Send(socket, data, 0, data.Length);
        }
    }
}