< Summary

Information
Class: Renci.SshNet.Channels.ChannelDirectTcpip
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\Channels\ChannelDirectTcpip.cs
Line coverage
78%
Covered lines: 110
Uncovered lines: 30
Coverable lines: 140
Total lines: 317
Line coverage: 78.5%
Branch coverage
75%
Covered branches: 24
Total branches: 32
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%1100%
get_ChannelType()100%10%
Open(...)50%476.47%
ForwardedPort_Closing(...)100%1100%
Bind()50%271.42%
CloseSocket()100%4100%
ShutdownSocket(...)66.66%677.77%
Close()100%2100%
OnData(...)100%4100%
OnOpenConfirmation(...)100%1100%
OnOpenFailure(...)100%10%
OnEof()100%1100%
OnErrorOccured(...)100%1100%
OnDisconnected()100%10%
Dispose(...)70%1063.33%

File(s)

\home\appveyor\projects\ssh-net\src\Renci.SshNet\Channels\ChannelDirectTcpip.cs

#LineLine coverage
 1using System;
 2using System.Net;
 3using System.Net.Sockets;
 4using System.Threading;
 5using Renci.SshNet.Abstractions;
 6using Renci.SshNet.Common;
 7using Renci.SshNet.Messages.Connection;
 8
 9namespace Renci.SshNet.Channels
 10{
 11    /// <summary>
 12    /// Implements "direct-tcpip" SSH channel.
 13    /// </summary>
 14    internal sealed class ChannelDirectTcpip : ClientChannel, IChannelDirectTcpip
 15    {
 3116        private readonly object _socketLock = new object();
 17
 3118        private EventWaitHandle _channelOpen = new AutoResetEvent(initialState: false);
 3119        private EventWaitHandle _channelData = new AutoResetEvent(initialState: false);
 20        private IForwardedPort _forwardedPort;
 21        private Socket _socket;
 22
 23        /// <summary>
 24        /// Initializes a new instance of the <see cref="ChannelDirectTcpip"/> class.
 25        /// </summary>
 26        /// <param name="session">The session.</param>
 27        /// <param name="localChannelNumber">The local channel number.</param>
 28        /// <param name="localWindowSize">Size of the window.</param>
 29        /// <param name="localPacketSize">Size of the packet.</param>
 30        public ChannelDirectTcpip(ISession session, uint localChannelNumber, uint localWindowSize, uint localPacketSize)
 3131            : base(session, localChannelNumber, localWindowSize, localPacketSize)
 3132        {
 3133        }
 34
 35        /// <summary>
 36        /// Gets the type of the channel.
 37        /// </summary>
 38        /// <value>
 39        /// The type of the channel.
 40        /// </value>
 41        public override ChannelTypes ChannelType
 42        {
 043            get { return ChannelTypes.DirectTcpip; }
 44        }
 45
 46        public void Open(string remoteHost, uint port, IForwardedPort forwardedPort, Socket socket)
 3147        {
 3148            if (IsOpen)
 049            {
 050                throw new SshException("Channel is already open.");
 51            }
 52
 3153            if (!IsConnected)
 054            {
 055                throw new SshException("Session is not connected.");
 56            }
 57
 3158            _socket = socket;
 3159            _forwardedPort = forwardedPort;
 3160            _forwardedPort.Closing += ForwardedPort_Closing;
 61
 3162            var ep = (IPEndPoint) socket.RemoteEndPoint;
 63
 64            // Open channel
 3165            SendMessage(new ChannelOpenMessage(LocalChannelNumber,
 3166                                               LocalWindowSize,
 3167                                               LocalPacketSize,
 3168                                               new DirectTcpipChannelInfo(remoteHost, port, ep.Address.ToString(), (uint
 69
 70            // Wait for channel to open
 3171            WaitOnHandle(_channelOpen);
 3172        }
 73
 74        /// <summary>
 75        /// Occurs as the forwarded port is being stopped.
 76        /// </summary>
 77        private void ForwardedPort_Closing(object sender, EventArgs eventArgs)
 1078        {
 79            // signal to the client that we will not send anything anymore; this should also interrupt the
 80            // blocking receive in Bind if the client sends FIN/ACK in time
 1081            ShutdownSocket(SocketShutdown.Send);
 82
 83            // if the FIN/ACK is not sent in time by the remote client, then interrupt the blocking receive
 84            // by closing the socket
 1085            CloseSocket();
 1086        }
 87
 88        /// <summary>
 89        /// Binds channel to remote host.
 90        /// </summary>
 91        public void Bind()
 3192        {
 93            // Cannot bind if channel is not open
 3194            if (!IsOpen)
 095            {
 096                return;
 97            }
 98
 3199            var buffer = new byte[RemotePacketSize];
 100
 31101            SocketAbstraction.ReadContinuous(_socket, buffer, 0, buffer.Length, SendData);
 102
 103            // even though the client has disconnected, we still want to properly close the
 104            // channel
 105            //
 106            // we'll do this in in Close() - invoked through Dispose(bool) - that way we have
 107            // a single place from which we send an SSH_MSG_CHANNEL_EOF message and wait for
 108            // the SSH_MSG_CHANNEL_CLOSE message
 31109        }
 110
 111        /// <summary>
 112        /// Closes the socket, hereby interrupting the blocking receive in <see cref="Bind()"/>.
 113        /// </summary>
 114        private void CloseSocket()
 60115        {
 60116            if (_socket is null)
 29117            {
 29118                return;
 119            }
 120
 31121            lock (_socketLock)
 31122            {
 31123                if (_socket is null)
 3124                {
 3125                    return;
 126                }
 127
 128                // closing a socket actually disposes the socket, so we can safely dereference
 129                // the field to avoid entering the lock again later
 28130                _socket.Dispose();
 28131                _socket = null;
 28132            }
 60133        }
 134
 135        /// <summary>
 136        /// Shuts down the socket.
 137        /// </summary>
 138        /// <param name="how">One of the <see cref="SocketShutdown"/> values that specifies the operation that will no l
 139        private void ShutdownSocket(SocketShutdown how)
 66140        {
 66141            if (_socket is null)
 12142            {
 12143                return;
 144            }
 145
 54146            lock (_socketLock)
 54147            {
 54148                if (!_socket.IsConnected())
 23149                {
 23150                    return;
 151                }
 152
 153                try
 31154                {
 31155                    _socket.Shutdown(how);
 31156                }
 0157                catch (SocketException ex)
 0158                {
 159                    // TODO: log as warning
 0160                    DiagnosticAbstraction.Log("Failure shutting down socket: " + ex);
 0161                }
 31162            }
 66163        }
 164
 165        /// <summary>
 166        /// Closes the channel, waiting for the SSH_MSG_CHANNEL_CLOSE message to be received from the server.
 167        /// </summary>
 168        protected override void Close()
 50169        {
 50170            var forwardedPort = _forwardedPort;
 50171            if (forwardedPort != null)
 25172            {
 25173                forwardedPort.Closing -= ForwardedPort_Closing;
 25174                _forwardedPort = null;
 25175            }
 176
 177            // signal to the client that we will not send anything anymore; this will also interrupt the
 178            // blocking receive in Bind if the client sends FIN/ACK in time
 179            //
 180            // if the FIN/ACK is not sent in time, the socket will be closed after the channel is closed
 50181            ShutdownSocket(SocketShutdown.Send);
 182
 183            // close the SSH channel
 50184            base.Close();
 185
 186            // close the socket
 50187            CloseSocket();
 50188        }
 189
 190        /// <summary>
 191        /// Called when channel data is received.
 192        /// </summary>
 193        /// <param name="data">The data.</param>
 194        protected override void OnData(byte[] data)
 8195        {
 8196            base.OnData(data);
 197
 8198            if (_socket != null)
 8199            {
 8200                lock (_socketLock)
 8201                {
 8202                    if (_socket.IsConnected())
 8203                    {
 8204                        SocketAbstraction.Send(_socket, data, 0, data.Length);
 8205                    }
 8206                }
 8207            }
 8208        }
 209
 210        /// <summary>
 211        /// Called when channel is opened by the server.
 212        /// </summary>
 213        /// <param name="remoteChannelNumber">The remote channel number.</param>
 214        /// <param name="initialWindowSize">Initial size of the window.</param>
 215        /// <param name="maximumPacketSize">Maximum size of the packet.</param>
 216        protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketS
 31217        {
 31218            base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize);
 219
 31220            _ = _channelOpen.Set();
 31221        }
 222
 223        protected override void OnOpenFailure(uint reasonCode, string description, string language)
 0224        {
 0225            base.OnOpenFailure(reasonCode, description, language);
 226
 0227            _ = _channelOpen.Set();
 0228        }
 229
 230        /// <summary>
 231        /// Called when channel has no more data to receive.
 232        /// </summary>
 233        protected override void OnEof()
 3234        {
 3235            base.OnEof();
 236
 237            // the channel will send no more data, and hence it does not make sense to receive
 238            // any more data from the client to send to the remote party (and we surely won't
 239            // send anything anymore)
 240            //
 241            // this will also interrupt the blocking receive in Bind()
 3242            ShutdownSocket(SocketShutdown.Send);
 3243        }
 244
 245        /// <summary>
 246        /// Called whenever an unhandled <see cref="Exception"/> occurs in <see cref="Session"/> causing
 247        /// the message loop to be interrupted, or when an exception occurred processing a channel message.
 248        /// </summary>
 249        protected override void OnErrorOccured(Exception exp)
 3250        {
 3251            base.OnErrorOccured(exp);
 252
 253            // signal to the client that we will not send anything anymore; this will also interrupt the
 254            // blocking receive in Bind if the client sends FIN/ACK in time
 255            //
 256            // if the FIN/ACK is not sent in time, the socket will be closed in Close(bool)
 3257            ShutdownSocket(SocketShutdown.Send);
 3258        }
 259
 260        /// <summary>
 261        /// Called when the server wants to terminate the connection immmediately.
 262        /// </summary>
 263        /// <remarks>
 264        /// The sender MUST NOT send or receive any data after this message, and
 265        /// the recipient MUST NOT accept any data after receiving this message.
 266        /// </remarks>
 267        protected override void OnDisconnected()
 0268        {
 0269            base.OnDisconnected();
 270
 271            // the channel will accept or send no more data, and hence it does not make sense
 272            // to accept any more data from the client (and we surely won't send anything
 273            // anymore)
 274            //
 275            // so lets signal to the client that we will not send or receive anything anymore
 276            // this will also interrupt the blocking receive in Bind()
 0277            ShutdownSocket(SocketShutdown.Both);
 0278        }
 279
 280        protected override void Dispose(bool disposing)
 34281        {
 282            // make sure we've unsubscribed from all session events and closed the channel
 283            // before we starting disposing
 34284            base.Dispose(disposing);
 285
 34286            if (disposing)
 28287            {
 28288                if (_socket != null)
 0289                {
 0290                    lock (_socketLock)
 0291                    {
 0292                        var socket = _socket;
 0293                        if (socket != null)
 0294                        {
 0295                            _socket = null;
 0296                            socket.Dispose();
 0297                        }
 0298                    }
 0299                }
 300
 28301                var channelOpen = _channelOpen;
 28302                if (channelOpen != null)
 25303                {
 25304                    _channelOpen = null;
 25305                    channelOpen.Dispose();
 25306                }
 307
 28308                var channelData = _channelData;
 28309                if (channelData != null)
 25310                {
 25311                    _channelData = null;
 25312                    channelData.Dispose();
 25313                }
 28314            }
 34315        }
 316    }
 317}