ForwardedPortLocal.NET.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. using System;
  2. using System.Diagnostics;
  3. using System.Net.Sockets;
  4. using System.Net;
  5. using System.Threading;
  6. using Renci.SshNet.Common;
  7. namespace Renci.SshNet
  8. {
  9. /// <summary>
  10. /// Provides functionality for local port forwarding
  11. /// </summary>
  12. public partial class ForwardedPortLocal
  13. {
  14. private Socket _listener;
  15. private int _pendingRequests;
  16. partial void ExecuteThread(Action action);
  17. partial void InternalStart()
  18. {
  19. var addr = BoundHost.GetIPAddress();
  20. var ep = new IPEndPoint(addr, (int) BoundPort);
  21. _listener = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp) {Blocking = true};
  22. _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
  23. _listener.Bind(ep);
  24. _listener.Listen(1);
  25. // update bound port (in case original was passed as zero)
  26. BoundPort = (uint)((IPEndPoint)_listener.LocalEndPoint).Port;
  27. Session.ErrorOccured += Session_ErrorOccured;
  28. Session.Disconnected += Session_Disconnected;
  29. _listenerTaskCompleted = new ManualResetEvent(false);
  30. ExecuteThread(() =>
  31. {
  32. try
  33. {
  34. while (true)
  35. {
  36. // accept new inbound connection
  37. var asyncResult = _listener.BeginAccept(AcceptCallback, _listener);
  38. // wait for the connection to be established
  39. asyncResult.AsyncWaitHandle.WaitOne();
  40. }
  41. }
  42. catch (ObjectDisposedException)
  43. {
  44. // BeginAccept will throw an ObjectDisposedException when the
  45. // socket is closed
  46. }
  47. catch (Exception ex)
  48. {
  49. RaiseExceptionEvent(ex);
  50. }
  51. finally
  52. {
  53. // mark listener stopped
  54. _listenerTaskCompleted.Set();
  55. }
  56. });
  57. }
  58. private void AcceptCallback(IAsyncResult ar)
  59. {
  60. // Get the socket that handles the client request
  61. var serverSocket = (Socket)ar.AsyncState;
  62. Socket clientSocket;
  63. try
  64. {
  65. clientSocket = serverSocket.EndAccept(ar);
  66. }
  67. catch (ObjectDisposedException)
  68. {
  69. // when the socket is closed, an ObjectDisposedException is thrown
  70. // by Socket.EndAccept(IAsyncResult)
  71. return;
  72. }
  73. Interlocked.Increment(ref _pendingRequests);
  74. try
  75. {
  76. clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
  77. clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
  78. var originatorEndPoint = (IPEndPoint) clientSocket.RemoteEndPoint;
  79. RaiseRequestReceived(originatorEndPoint.Address.ToString(),
  80. (uint)originatorEndPoint.Port);
  81. using (var channel = Session.CreateChannelDirectTcpip())
  82. {
  83. channel.Exception += Channel_Exception;
  84. channel.Open(Host, Port, this, clientSocket);
  85. channel.Bind();
  86. channel.Close();
  87. }
  88. }
  89. catch (Exception exp)
  90. {
  91. RaiseExceptionEvent(exp);
  92. CloseSocket(clientSocket);
  93. }
  94. finally
  95. {
  96. Interlocked.Decrement(ref _pendingRequests);
  97. }
  98. }
  99. private static void CloseSocket(Socket socket)
  100. {
  101. if (socket.Connected)
  102. {
  103. socket.Shutdown(SocketShutdown.Both);
  104. socket.Close();
  105. }
  106. }
  107. partial void InternalStop(TimeSpan timeout)
  108. {
  109. if (timeout == TimeSpan.Zero)
  110. return;
  111. var stopWatch = new Stopwatch();
  112. stopWatch.Start();
  113. while (true)
  114. {
  115. // break out of loop when all pending requests have been processed
  116. if (Interlocked.CompareExchange(ref _pendingRequests, 0, 0) == 0)
  117. break;
  118. // break out of loop when specified timeout has elapsed
  119. if (stopWatch.Elapsed >= timeout && timeout != SshNet.Session.InfiniteTimeSpan)
  120. break;
  121. // give channels time to process pending requests
  122. Thread.Sleep(50);
  123. }
  124. stopWatch.Stop();
  125. }
  126. /// <summary>
  127. /// Interrupts the listener, and waits for the listener loop to finish.
  128. /// </summary>
  129. /// <remarks>
  130. /// When the forwarded port is stopped, then any further action is skipped.
  131. /// </remarks>
  132. partial void StopListener()
  133. {
  134. if (!IsStarted)
  135. return;
  136. Session.Disconnected -= Session_Disconnected;
  137. Session.ErrorOccured -= Session_ErrorOccured;
  138. // close listener socket
  139. _listener.Close();
  140. // wait for listener loop to finish
  141. _listenerTaskCompleted.WaitOne();
  142. }
  143. private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
  144. {
  145. StopListener();
  146. }
  147. private void Session_Disconnected(object sender, EventArgs e)
  148. {
  149. StopListener();
  150. }
  151. private void Channel_Exception(object sender, ExceptionEventArgs e)
  152. {
  153. RaiseExceptionEvent(e.Exception);
  154. }
  155. }
  156. }