ForwardedPortLocal.NET.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using System;
  2. using System.Diagnostics;
  3. using System.Net.Sockets;
  4. using System.Net;
  5. using System.Threading;
  6. using Renci.SshNet.Abstractions;
  7. using Renci.SshNet.Common;
  8. namespace Renci.SshNet
  9. {
  10. /// <summary>
  11. /// Provides functionality for local port forwarding
  12. /// </summary>
  13. public partial class ForwardedPortLocal
  14. {
  15. private Socket _listener;
  16. private int _pendingRequests;
  17. partial void InternalStart()
  18. {
  19. var addr = DnsAbstraction.GetHostAddresses(BoundHost)[0];
  20. var ep = new IPEndPoint(addr, (int) BoundPort);
  21. _listener = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  22. // TODO: decide if we want to have blocking socket
  23. #if FEATURE_SOCKET_SETSOCKETOPTION
  24. _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
  25. #endif // FEATURE_SOCKET_SETSOCKETOPTION
  26. _listener.Bind(ep);
  27. _listener.Listen(1);
  28. // update bound port (in case original was passed as zero)
  29. BoundPort = (uint)((IPEndPoint)_listener.LocalEndPoint).Port;
  30. Session.ErrorOccured += Session_ErrorOccured;
  31. Session.Disconnected += Session_Disconnected;
  32. _listenerTaskCompleted = new ManualResetEvent(false);
  33. ThreadAbstraction.ExecuteThread(() =>
  34. {
  35. try
  36. {
  37. #if FEATURE_SOCKET_EAP
  38. StartAccept();
  39. #elif FEATURE_SOCKET_APM
  40. _listener.BeginAccept(AcceptCallback, _listener);
  41. #elif FEATURE_SOCKET_TAP
  42. #error Accepting new socket connections is not implemented.
  43. #else
  44. #error Accepting new socket connections is not implemented.
  45. #endif
  46. // wait until listener is stopped
  47. _listenerTaskCompleted.WaitOne();
  48. }
  49. catch (ObjectDisposedException)
  50. {
  51. // BeginAccept / AcceptAsync will throw an ObjectDisposedException when the
  52. // server is closed before the listener has started accepting connections.
  53. //
  54. // As we start accepting connection on a separate thread, this is possible
  55. // when the listener is stopped right after it was started.
  56. // mark listener stopped
  57. _listenerTaskCompleted.Set();
  58. }
  59. catch (Exception ex)
  60. {
  61. RaiseExceptionEvent(ex);
  62. // mark listener stopped
  63. _listenerTaskCompleted.Set();
  64. }
  65. finally
  66. {
  67. if (Session != null)
  68. {
  69. Session.Disconnected -= Session_Disconnected;
  70. Session.ErrorOccured -= Session_ErrorOccured;
  71. }
  72. }
  73. });
  74. }
  75. #if FEATURE_SOCKET_EAP
  76. private void StartAccept()
  77. {
  78. var args = new SocketAsyncEventArgs();
  79. args.Completed += AcceptCompleted;
  80. if (!_listener.AcceptAsync(args))
  81. {
  82. AcceptCompleted(null, args);
  83. }
  84. }
  85. private void AcceptCompleted(object sender, SocketAsyncEventArgs acceptAsyncEventArgs)
  86. {
  87. if (acceptAsyncEventArgs.SocketError == SocketError.OperationAborted)
  88. {
  89. // server was stopped
  90. return;
  91. }
  92. if (acceptAsyncEventArgs.SocketError != SocketError.Success)
  93. {
  94. // accept new connection
  95. StartAccept();
  96. // dispose broken socket
  97. acceptAsyncEventArgs.AcceptSocket.Dispose();
  98. return;
  99. }
  100. // accept new connection
  101. StartAccept();
  102. // process connection
  103. ProcessAccept(acceptAsyncEventArgs.AcceptSocket);
  104. }
  105. #elif FEATURE_SOCKET_APM
  106. private void AcceptCallback(IAsyncResult ar)
  107. {
  108. // Get the socket that handles the client request
  109. var serverSocket = (Socket) ar.AsyncState;
  110. Socket clientSocket;
  111. try
  112. {
  113. clientSocket = serverSocket.EndAccept(ar);
  114. }
  115. catch (ObjectDisposedException)
  116. {
  117. // when the server socket is closed, an ObjectDisposedException is thrown
  118. // by Socket.EndAccept(IAsyncResult)
  119. return;
  120. }
  121. // accept new connection
  122. _listener.BeginAccept(AcceptCallback, _listener);
  123. // process connection
  124. ProcessAccept(clientSocket);
  125. }
  126. #endif
  127. private void ProcessAccept(Socket clientSocket)
  128. {
  129. Interlocked.Increment(ref _pendingRequests);
  130. try
  131. {
  132. #if FEATURE_SOCKET_SETSOCKETOPTION
  133. clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
  134. clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
  135. #endif //FEATURE_SOCKET_SETSOCKETOPTION
  136. var originatorEndPoint = (IPEndPoint) clientSocket.RemoteEndPoint;
  137. RaiseRequestReceived(originatorEndPoint.Address.ToString(),
  138. (uint)originatorEndPoint.Port);
  139. using (var channel = Session.CreateChannelDirectTcpip())
  140. {
  141. channel.Exception += Channel_Exception;
  142. channel.Open(Host, Port, this, clientSocket);
  143. channel.Bind();
  144. channel.Close();
  145. }
  146. }
  147. catch (Exception exp)
  148. {
  149. RaiseExceptionEvent(exp);
  150. CloseSocket(clientSocket);
  151. }
  152. finally
  153. {
  154. Interlocked.Decrement(ref _pendingRequests);
  155. }
  156. }
  157. private static void CloseSocket(Socket socket)
  158. {
  159. if (socket.Connected)
  160. {
  161. socket.Shutdown(SocketShutdown.Both);
  162. socket.Dispose();
  163. }
  164. }
  165. partial void InternalStop(TimeSpan timeout)
  166. {
  167. if (timeout == TimeSpan.Zero)
  168. return;
  169. var stopWatch = new Stopwatch();
  170. stopWatch.Start();
  171. while (true)
  172. {
  173. // break out of loop when all pending requests have been processed
  174. if (Interlocked.CompareExchange(ref _pendingRequests, 0, 0) == 0)
  175. break;
  176. // break out of loop when specified timeout has elapsed
  177. if (stopWatch.Elapsed >= timeout && timeout != SshNet.Session.InfiniteTimeSpan)
  178. break;
  179. // give channels time to process pending requests
  180. ThreadAbstraction.Sleep(50);
  181. }
  182. stopWatch.Stop();
  183. }
  184. /// <summary>
  185. /// Interrupts the listener, and waits for the listener loop to finish.
  186. /// </summary>
  187. /// <remarks>
  188. /// When the forwarded port is stopped, then any further action is skipped.
  189. /// </remarks>
  190. partial void StopListener()
  191. {
  192. if (!IsStarted)
  193. return;
  194. // close listener socket
  195. _listener.Dispose();
  196. // allow listener thread to stop
  197. _listenerTaskCompleted.Set();
  198. }
  199. partial void InternalDispose(bool disposing)
  200. {
  201. if (disposing)
  202. {
  203. var listener = _listener;
  204. if (listener != null)
  205. {
  206. _listener = null;
  207. listener.Dispose();
  208. }
  209. }
  210. }
  211. private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
  212. {
  213. StopListener();
  214. }
  215. private void Session_Disconnected(object sender, EventArgs e)
  216. {
  217. StopListener();
  218. }
  219. private void Channel_Exception(object sender, ExceptionEventArgs e)
  220. {
  221. RaiseExceptionEvent(e.Exception);
  222. }
  223. }
  224. }