ForwardedPortRemote.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. using System;
  2. using System.Threading;
  3. using Renci.SshNet.Channels;
  4. using Renci.SshNet.Messages.Connection;
  5. using Renci.SshNet.Common;
  6. using System.Diagnostics;
  7. using System.Globalization;
  8. namespace Renci.SshNet
  9. {
  10. /// <summary>
  11. /// Provides functionality for remote port forwarding
  12. /// </summary>
  13. public partial class ForwardedPortRemote : ForwardedPort, IDisposable
  14. {
  15. private bool _requestStatus;
  16. private EventWaitHandle _globalRequestResponse = new AutoResetEvent(false);
  17. /// <summary>
  18. /// Gets the bound host.
  19. /// </summary>
  20. public string BoundHost { get; protected set; }
  21. /// <summary>
  22. /// Gets the bound port.
  23. /// </summary>
  24. public uint BoundPort { get; protected set; }
  25. /// <summary>
  26. /// Gets the forwarded host.
  27. /// </summary>
  28. public string Host { get; protected set; }
  29. /// <summary>
  30. /// Gets the forwarded port.
  31. /// </summary>
  32. public uint Port { get; protected set; }
  33. /// <summary>
  34. /// Initializes a new instance of the <see cref="ForwardedPortRemote"/> class.
  35. /// </summary>
  36. /// <param name="boundPort">The bound port.</param>
  37. /// <param name="host">The host.</param>
  38. /// <param name="port">The port.</param>
  39. public ForwardedPortRemote(uint boundPort, string host, uint port)
  40. : this(string.Empty, boundPort, host, port)
  41. {
  42. }
  43. /// <summary>
  44. /// Initializes a new instance of the <see cref="ForwardedPortRemote"/> class.
  45. /// </summary>
  46. /// <param name="boundHost">The bound host.</param>
  47. /// <param name="boundPort">The bound port.</param>
  48. /// <param name="host">The host.</param>
  49. /// <param name="port">The port.</param>
  50. public ForwardedPortRemote(string boundHost, uint boundPort, string host, uint port)
  51. {
  52. if (boundHost == null)
  53. throw new ArgumentNullException("boundHost");
  54. if (host == null)
  55. throw new ArgumentNullException("host");
  56. if (!boundHost.IsValidHost())
  57. throw new ArgumentException("boundHost");
  58. if (!boundPort.IsValidPort())
  59. throw new ArgumentOutOfRangeException("boundPort");
  60. if (!host.IsValidHost())
  61. throw new ArgumentException("host");
  62. if (!port.IsValidPort())
  63. throw new ArgumentOutOfRangeException("port");
  64. this.BoundHost = boundHost;
  65. this.BoundPort = boundPort;
  66. this.Host = host;
  67. this.Port = port;
  68. }
  69. /// <summary>
  70. /// Starts remote port forwarding.
  71. /// </summary>
  72. public override void Start()
  73. {
  74. base.Start();
  75. // If port already started don't start it again
  76. if (this.IsStarted)
  77. return;
  78. this.Session.RegisterMessage("SSH_MSG_REQUEST_FAILURE");
  79. this.Session.RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
  80. this.Session.RegisterMessage("SSH_MSG_CHANNEL_OPEN");
  81. this.Session.RequestSuccessReceived += Session_RequestSuccess;
  82. this.Session.RequestFailureReceived += Session_RequestFailure;
  83. this.Session.ChannelOpenReceived += Session_ChannelOpening;
  84. // Send global request to start direct tcpip
  85. this.Session.SendMessage(new GlobalRequestMessage(GlobalRequestName.TcpIpForward, true, this.BoundHost, this.BoundPort));
  86. this.Session.WaitHandle(this._globalRequestResponse);
  87. if (!this._requestStatus)
  88. {
  89. // If request failed don't handle channel opening for this request
  90. this.Session.ChannelOpenReceived -= Session_ChannelOpening;
  91. throw new SshException(string.Format(CultureInfo.CurrentCulture, "Port forwarding for '{0}' port '{1}' failed to start.", this.Host, this.Port));
  92. }
  93. else
  94. {
  95. this.IsStarted = true;
  96. }
  97. }
  98. /// <summary>
  99. /// Stops remote port forwarding.
  100. /// </summary>
  101. public override void Stop()
  102. {
  103. base.Stop();
  104. // If port not started you cant stop it
  105. if (!this.IsStarted)
  106. return;
  107. // Send global request to cancel direct tcpip
  108. this.Session.SendMessage(new GlobalRequestMessage(GlobalRequestName.CancelTcpIpForward, true, this.BoundHost, this.BoundPort));
  109. this.Session.WaitHandle(this._globalRequestResponse);
  110. this.Session.RequestSuccessReceived -= Session_RequestSuccess;
  111. this.Session.RequestFailureReceived -= Session_RequestFailure;
  112. this.Session.ChannelOpenReceived -= Session_ChannelOpening;
  113. this.IsStarted = false;
  114. }
  115. private void Session_ChannelOpening(object sender, MessageEventArgs<ChannelOpenMessage> e)
  116. {
  117. // Ensure that this is corresponding request
  118. var info = e.Message.Info as ForwardedTcpipChannelInfo;
  119. if (info != null)
  120. {
  121. if (info.ConnectedAddress == this.BoundHost && info.ConnectedPort == this.BoundPort)
  122. {
  123. this.ExecuteThread(() =>
  124. {
  125. try
  126. {
  127. this.RaiseRequestReceived(info.OriginatorAddress, info.OriginatorPort);
  128. var channel = this.Session.CreateChannel<ChannelForwardedTcpip>(e.Message.LocalChannelNumber, e.Message.InitialWindowSize, e.Message.MaximumPacketSize);
  129. channel.Bind(this.Host, this.Port);
  130. }
  131. catch (Exception exp)
  132. {
  133. this.RaiseExceptionEvent(exp);
  134. }
  135. });
  136. }
  137. }
  138. }
  139. private void Session_RequestFailure(object sender, System.EventArgs e)
  140. {
  141. this._requestStatus = false;
  142. this._globalRequestResponse.Set();
  143. }
  144. private void Session_RequestSuccess(object sender, MessageEventArgs<RequestSuccessMessage> e)
  145. {
  146. this._requestStatus = true;
  147. if (this.BoundPort == 0)
  148. {
  149. this.BoundPort = (e.Message.BoundPort == null) ? 0 : e.Message.BoundPort.Value;
  150. }
  151. this._globalRequestResponse.Set();
  152. }
  153. partial void ExecuteThread(Action action);
  154. #region IDisposable Members
  155. private bool _isDisposed = false;
  156. /// <summary>
  157. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  158. /// </summary>
  159. public void Dispose()
  160. {
  161. Dispose(true);
  162. GC.SuppressFinalize(this);
  163. }
  164. /// <summary>
  165. /// Releases unmanaged and - optionally - managed resources
  166. /// </summary>
  167. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  168. protected virtual void Dispose(bool disposing)
  169. {
  170. // Check to see if Dispose has already been called.
  171. if (!this._isDisposed)
  172. {
  173. // If disposing equals true, dispose all managed
  174. // and unmanaged resources.
  175. if (disposing)
  176. {
  177. // Dispose managed resources.
  178. if (this._globalRequestResponse != null)
  179. {
  180. this._globalRequestResponse.Dispose();
  181. this._globalRequestResponse = null;
  182. }
  183. }
  184. // Note disposing has been done.
  185. _isDisposed = true;
  186. }
  187. }
  188. /// <summary>
  189. /// Releases unmanaged resources and performs other cleanup operations before the
  190. /// <see cref="ForwardedPortRemote"/> is reclaimed by garbage collection.
  191. /// </summary>
  192. ~ForwardedPortRemote()
  193. {
  194. // Do not re-create Dispose clean-up code here.
  195. // Calling Dispose(false) is optimal in terms of
  196. // readability and maintainability.
  197. Dispose(false);
  198. }
  199. #endregion
  200. }
  201. }