ChannelForwardedTcpip.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using Renci.SshNet.Abstractions;
  5. using Renci.SshNet.Common;
  6. using Renci.SshNet.Messages.Connection;
  7. namespace Renci.SshNet.Channels
  8. {
  9. /// <summary>
  10. /// Implements "forwarded-tcpip" SSH channel.
  11. /// </summary>
  12. internal class ChannelForwardedTcpip : ServerChannel, IChannelForwardedTcpip
  13. {
  14. private readonly object _socketShutdownAndCloseLock = new object();
  15. private Socket _socket;
  16. private IForwardedPort _forwardedPort;
  17. /// <summary>
  18. /// Initializes a new <see cref="ChannelForwardedTcpip"/> instance.
  19. /// </summary>
  20. /// <param name="session">The session.</param>
  21. /// <param name="localChannelNumber">The local channel number.</param>
  22. /// <param name="localWindowSize">Size of the window.</param>
  23. /// <param name="localPacketSize">Size of the packet.</param>
  24. /// <param name="remoteChannelNumber">The remote channel number.</param>
  25. /// <param name="remoteWindowSize">The window size of the remote party.</param>
  26. /// <param name="remotePacketSize">The maximum size of a data packet that we can send to the remote party.</param>
  27. internal ChannelForwardedTcpip(ISession session, uint localChannelNumber, uint localWindowSize, uint localPacketSize, uint remoteChannelNumber, uint remoteWindowSize, uint remotePacketSize)
  28. : base(session, localChannelNumber, localWindowSize, localPacketSize, remoteChannelNumber, remoteWindowSize, remotePacketSize)
  29. {
  30. }
  31. /// <summary>
  32. /// Gets the type of the channel.
  33. /// </summary>
  34. /// <value>
  35. /// The type of the channel.
  36. /// </value>
  37. public override ChannelTypes ChannelType
  38. {
  39. get { return ChannelTypes.ForwardedTcpip; }
  40. }
  41. /// <summary>
  42. /// Binds the channel to the specified endpoint.
  43. /// </summary>
  44. /// <param name="remoteEndpoint">The endpoint to connect to.</param>
  45. /// <param name="forwardedPort">The forwarded port for which the channel is opened.</param>
  46. public void Bind(IPEndPoint remoteEndpoint, IForwardedPort forwardedPort)
  47. {
  48. if (!IsConnected)
  49. {
  50. throw new SshException("Session is not connected.");
  51. }
  52. _forwardedPort = forwardedPort;
  53. _forwardedPort.Closing += ForwardedPort_Closing;
  54. // Try to connect to the socket
  55. try
  56. {
  57. _socket = SocketAbstraction.Connect(remoteEndpoint, ConnectionInfo.Timeout);
  58. // send channel open confirmation message
  59. SendMessage(new ChannelOpenConfirmationMessage(RemoteChannelNumber, LocalWindowSize, LocalPacketSize, LocalChannelNumber));
  60. }
  61. catch (Exception exp)
  62. {
  63. // send channel open failure message
  64. SendMessage(new ChannelOpenFailureMessage(RemoteChannelNumber, exp.ToString(), ChannelOpenFailureMessage.ConnectFailed, "en"));
  65. throw;
  66. }
  67. var buffer = new byte[RemotePacketSize];
  68. SocketAbstraction.ReadContinuous(_socket, buffer, 0, buffer.Length, SendData);
  69. }
  70. protected override void OnErrorOccured(Exception exp)
  71. {
  72. base.OnErrorOccured(exp);
  73. // signal to the server that we will not send anything anymore; this will also interrupt the
  74. // blocking receive in Bind if the server sends FIN/ACK in time
  75. //
  76. // if the FIN/ACK is not sent in time, the socket will be closed in Close(bool)
  77. ShutdownSocket(SocketShutdown.Send);
  78. }
  79. /// <summary>
  80. /// Occurs as the forwarded port is being stopped.
  81. /// </summary>
  82. private void ForwardedPort_Closing(object sender, EventArgs eventArgs)
  83. {
  84. #if DEBUG_GERT
  85. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelForwardedTcpip.ForwardedPort_Closing");
  86. #endif // DEBUG_GERT
  87. // signal to the server that we will not send anything anymore; this will also interrupt the
  88. // blocking receive in Bind if the server sends FIN/ACK in time
  89. //
  90. // if the FIN/ACK is not sent in time, the socket will be closed in Close(bool)
  91. ShutdownSocket(SocketShutdown.Send);
  92. }
  93. /// <summary>
  94. /// Shuts down the socket.
  95. /// </summary>
  96. /// <param name="how">One of the <see cref="SocketShutdown"/> values that specifies the operation that will no longer be allowed.</param>
  97. private void ShutdownSocket(SocketShutdown how)
  98. {
  99. #if DEBUG_GERT
  100. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelForwardedTcpip.ShutdownSocket");
  101. #endif // DEBUG_GERT
  102. if (_socket == null || !_socket.Connected)
  103. return;
  104. lock (_socketShutdownAndCloseLock)
  105. {
  106. var socket = _socket;
  107. if (socket == null || !socket.Connected)
  108. return;
  109. socket.Shutdown(how);
  110. }
  111. }
  112. /// <summary>
  113. /// Closes the socket, hereby interrupting the blocking receive in <see cref="Bind(IPEndPoint,IForwardedPort)"/>.
  114. /// </summary>
  115. private void CloseSocket()
  116. {
  117. if (_socket == null)
  118. return;
  119. lock (_socketShutdownAndCloseLock)
  120. {
  121. #if DEBUG_GERT
  122. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelForwardedTcpip.CloseSocket");
  123. #endif // DEBUG_GERT
  124. var socket = _socket;
  125. if (socket != null)
  126. {
  127. // closing a socket actually disposes the socket, so we can safely dereference
  128. // the field to avoid entering the lock again later
  129. socket.Dispose();
  130. _socket = null;
  131. }
  132. }
  133. }
  134. /// <summary>
  135. /// Closes the channel, optionally waiting for the SSH_MSG_CHANNEL_CLOSE message to
  136. /// be received from the server.
  137. /// </summary>
  138. /// <param name="wait"><c>true</c> to wait for the SSH_MSG_CHANNEL_CLOSE message to be received from the server; otherwise, <c>false</c>.</param>
  139. protected override void Close(bool wait)
  140. {
  141. #if DEBUG_GERT
  142. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelForwardedTcpip.Close");
  143. #endif // DEBUG_GERT
  144. var forwardedPort = _forwardedPort;
  145. if (forwardedPort != null)
  146. {
  147. forwardedPort.Closing -= ForwardedPort_Closing;
  148. _forwardedPort = null;
  149. }
  150. // signal to the server that we will not send anything anymore; this will also interrupt the
  151. // blocking receive in Bind if the server sends FIN/ACK in time
  152. //
  153. // if the FIN/ACK is not sent in time, the socket will be closed after the channel is closed
  154. ShutdownSocket(SocketShutdown.Send);
  155. // close the SSH channel, and mark the channel closed
  156. base.Close(wait);
  157. // close the socket
  158. CloseSocket();
  159. }
  160. #if DEBUG_GERT
  161. protected override void OnClose()
  162. {
  163. base.OnClose();
  164. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | OnClose");
  165. }
  166. #endif // DEBUG_GERT
  167. /// <summary>
  168. /// Called when channel data is received.
  169. /// </summary>
  170. /// <param name="data">The data.</param>
  171. protected override void OnData(byte[] data)
  172. {
  173. base.OnData(data);
  174. var socket = _socket;
  175. if (socket != null && socket.Connected)
  176. {
  177. SocketAbstraction.Send(socket, data, 0, data.Length);
  178. }
  179. }
  180. }
  181. }