ChannelForwardedTcpip.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. using System;
  2. using System.Linq;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Threading;
  6. using Renci.SshNet.Common;
  7. using Renci.SshNet.Messages.Connection;
  8. namespace Renci.SshNet.Channels
  9. {
  10. /// <summary>
  11. /// Implements "forwarded-tcpip" SSH channel.
  12. /// </summary>
  13. internal partial class ChannelForwardedTcpip : ServerChannel, IChannelForwardedTcpip
  14. {
  15. private readonly object _socketShutdownAndCloseLock = new object();
  16. private Socket _socket;
  17. private IForwardedPort _forwardedPort;
  18. /// <summary>
  19. /// Initializes a new <see cref="ChannelForwardedTcpip"/> instance.
  20. /// </summary>
  21. /// <param name="session">The session.</param>
  22. /// <param name="localChannelNumber">The local channel number.</param>
  23. /// <param name="localWindowSize">Size of the window.</param>
  24. /// <param name="localPacketSize">Size of the packet.</param>
  25. /// <param name="remoteChannelNumber">The remote channel number.</param>
  26. /// <param name="remoteWindowSize">The window size of the remote party.</param>
  27. /// <param name="remotePacketSize">The maximum size of a data packet that we can send to the remote party.</param>
  28. internal ChannelForwardedTcpip(ISession session, uint localChannelNumber, uint localWindowSize, uint localPacketSize, uint remoteChannelNumber, uint remoteWindowSize, uint remotePacketSize)
  29. : base(session, localChannelNumber, localWindowSize, localPacketSize, remoteChannelNumber, remoteWindowSize, remotePacketSize)
  30. {
  31. }
  32. /// <summary>
  33. /// Gets the type of the channel.
  34. /// </summary>
  35. /// <value>
  36. /// The type of the channel.
  37. /// </value>
  38. public override ChannelTypes ChannelType
  39. {
  40. get { return ChannelTypes.ForwardedTcpip; }
  41. }
  42. /// <summary>
  43. /// Binds the channel to the specified endpoint.
  44. /// </summary>
  45. /// <param name="remoteEndpoint">The endpoint to connect to.</param>
  46. /// <param name="forwardedPort">The forwarded port for which the channel is opened.</param>
  47. public void Bind(IPEndPoint remoteEndpoint, IForwardedPort forwardedPort)
  48. {
  49. byte[] buffer;
  50. if (!IsConnected)
  51. {
  52. throw new SshException("Session is not connected.");
  53. }
  54. _forwardedPort = forwardedPort;
  55. _forwardedPort.Closing += ForwardedPort_Closing;
  56. // Try to connect to the socket
  57. try
  58. {
  59. // Get buffer in memory for data exchange
  60. buffer = new byte[RemotePacketSize];
  61. OpenSocket(remoteEndpoint);
  62. // send channel open confirmation message
  63. SendMessage(new ChannelOpenConfirmationMessage(RemoteChannelNumber, LocalWindowSize, LocalPacketSize, LocalChannelNumber));
  64. }
  65. catch (Exception exp)
  66. {
  67. // send channel open failure message
  68. SendMessage(new ChannelOpenFailureMessage(RemoteChannelNumber, exp.ToString(), ChannelOpenFailureMessage.ConnectFailed, "en"));
  69. throw;
  70. }
  71. // Start reading data from the port and send to channel
  72. while (_socket != null && _socket.Connected)
  73. {
  74. try
  75. {
  76. var read = 0;
  77. InternalSocketReceive(buffer, ref read);
  78. if (read > 0)
  79. {
  80. #if TUNING
  81. SendData(buffer, 0, read);
  82. #else
  83. SendMessage(new ChannelDataMessage(RemoteChannelNumber, buffer.Take(read).ToArray()));
  84. #endif
  85. }
  86. else
  87. {
  88. // server quit sending
  89. break;
  90. }
  91. }
  92. catch (SocketException exp)
  93. {
  94. if (exp.SocketErrorCode == SocketError.WouldBlock ||
  95. exp.SocketErrorCode == SocketError.IOPending ||
  96. exp.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
  97. {
  98. // socket buffer is probably empty, wait and try again
  99. Thread.Sleep(30);
  100. }
  101. else if (exp.SocketErrorCode == SocketError.ConnectionAborted || exp.SocketErrorCode == SocketError.Interrupted)
  102. {
  103. break;
  104. }
  105. else
  106. throw; // throw any other error
  107. }
  108. }
  109. }
  110. protected override void OnErrorOccured(Exception exp)
  111. {
  112. base.OnErrorOccured(exp);
  113. // signal to the server that we will not send anything anymore; this will also interrupt the
  114. // blocking receive in Bind if the server sends FIN/ACK in time
  115. //
  116. // if the FIN/ACK is not sent in time, the socket will be closed in Close(bool)
  117. ShutdownSocket(SocketShutdown.Send);
  118. }
  119. /// <summary>
  120. /// Occurs as the forwarded port is being stopped.
  121. /// </summary>
  122. private void ForwardedPort_Closing(object sender, EventArgs eventArgs)
  123. {
  124. // signal to the server that we will not send anything anymore; this will also interrupt the
  125. // blocking receive in Bind if the server sends FIN/ACK in time
  126. //
  127. // if the FIN/ACK is not sent in time, the socket will be closed in Close(bool)
  128. ShutdownSocket(SocketShutdown.Send);
  129. }
  130. partial void OpenSocket(IPEndPoint remoteEndpoint);
  131. /// <summary>
  132. /// Shuts down the socket.
  133. /// </summary>
  134. /// <param name="how">One of the <see cref="SocketShutdown"/> values that specifies the operation that will no longer be allowed.</param>
  135. private void ShutdownSocket(SocketShutdown how)
  136. {
  137. if (_socket == null || !_socket.Connected)
  138. return;
  139. lock (_socketShutdownAndCloseLock)
  140. {
  141. if (_socket == null || !_socket.Connected)
  142. return;
  143. _socket.Shutdown(how);
  144. }
  145. }
  146. /// <summary>
  147. /// Closes the socket, hereby interrupting the blocking receive in <see cref="Bind(IPEndPoint,IForwardedPort)"/>.
  148. /// </summary>
  149. private void CloseSocket()
  150. {
  151. if (_socket == null)
  152. return;
  153. lock (_socketShutdownAndCloseLock)
  154. {
  155. if (_socket == null)
  156. return;
  157. // closing a socket actually disposes the socket, so we can safely dereference
  158. // the field to avoid entering the lock again later
  159. _socket.Close();
  160. _socket = null;
  161. }
  162. }
  163. /// <summary>
  164. /// Closes the channel, optionally waiting for the SSH_MSG_CHANNEL_CLOSE message to
  165. /// be received from the server.
  166. /// </summary>
  167. /// <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>
  168. protected override void Close(bool wait)
  169. {
  170. if (_forwardedPort != null)
  171. {
  172. _forwardedPort.Closing -= ForwardedPort_Closing;
  173. _forwardedPort = null;
  174. }
  175. // signal to the server that we will not send anything anymore; this will also interrupt the
  176. // blocking receive in Bind if the server sends FIN/ACK in time
  177. //
  178. // if the FIN/ACK is not sent in time, the socket will be closed after the channel is closed
  179. ShutdownSocket(SocketShutdown.Send);
  180. // close the SSH channel, and mark the channel closed
  181. base.Close(wait);
  182. // close the socket
  183. CloseSocket();
  184. }
  185. /// <summary>
  186. /// Called when channel data is received.
  187. /// </summary>
  188. /// <param name="data">The data.</param>
  189. protected override void OnData(byte[] data)
  190. {
  191. base.OnData(data);
  192. if (_socket != null && _socket.Connected)
  193. InternalSocketSend(data);
  194. }
  195. partial void InternalSocketSend(byte[] data);
  196. partial void InternalSocketReceive(byte[] buffer, ref int read);
  197. /// <summary>
  198. /// Releases unmanaged and - optionally - managed resources
  199. /// </summary>
  200. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  201. protected override void Dispose(bool disposing)
  202. {
  203. // make sure we've unsubscribed from all session events and closed the channel
  204. // before we starting disposing
  205. base.Dispose(disposing);
  206. if (disposing)
  207. {
  208. if (_socket != null)
  209. {
  210. _socket.Dispose();
  211. _socket = null;
  212. }
  213. }
  214. }
  215. }
  216. }