ChannelSession.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. using System.Diagnostics;
  2. using System.Threading;
  3. using Renci.SshNet.Common;
  4. using Renci.SshNet.Messages.Connection;
  5. using System.Globalization;
  6. namespace Renci.SshNet.Channels
  7. {
  8. /// <summary>
  9. /// Implements Session SSH channel.
  10. /// </summary>
  11. internal class ChannelSession : Channel
  12. {
  13. /// <summary>
  14. /// Counts faile channel open attempts
  15. /// </summary>
  16. private int _failedOpenAttempts;
  17. /// <summary>
  18. /// Wait handle to signal when response was received to open the channel
  19. /// </summary>
  20. private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(false);
  21. private EventWaitHandle _channelRequestResponse = new ManualResetEvent(false);
  22. private bool _channelRequestSucces;
  23. /// <summary>
  24. /// Gets the type of the channel.
  25. /// </summary>
  26. /// <value>
  27. /// The type of the channel.
  28. /// </value>
  29. public override ChannelTypes ChannelType
  30. {
  31. get { return ChannelTypes.Session; }
  32. }
  33. /// <summary>
  34. /// Opens the channel.
  35. /// </summary>
  36. public virtual void Open()
  37. {
  38. if (!this.IsOpen)
  39. {
  40. // Try to open channel several times
  41. while (this._failedOpenAttempts < this.ConnectionInfo.RetryAttempts && !this.IsOpen)
  42. {
  43. this.SendChannelOpenMessage();
  44. this.WaitHandle(this._channelOpenResponseWaitHandle);
  45. }
  46. if (!this.IsOpen)
  47. {
  48. throw new SshException(string.Format(CultureInfo.CurrentCulture, "Failed to open a channel after {0} attempts.", this._failedOpenAttempts));
  49. }
  50. }
  51. }
  52. /// <summary>
  53. /// Called when channel is opened by the server.
  54. /// </summary>
  55. /// <param name="remoteChannelNumber">The remote channel number.</param>
  56. /// <param name="initialWindowSize">Initial size of the window.</param>
  57. /// <param name="maximumPacketSize">Maximum size of the packet.</param>
  58. protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize)
  59. {
  60. base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize);
  61. this._channelOpenResponseWaitHandle.Set();
  62. }
  63. /// <summary>
  64. /// Called when channel failed to open.
  65. /// </summary>
  66. /// <param name="reasonCode">The reason code.</param>
  67. /// <param name="description">The description.</param>
  68. /// <param name="language">The language.</param>
  69. protected override void OnOpenFailure(uint reasonCode, string description, string language)
  70. {
  71. this._failedOpenAttempts++;
  72. Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Local channel: {0} attempts: {1}.", this.LocalChannelNumber, this._failedOpenAttempts));
  73. this.SessionSemaphore.Release();
  74. this._channelOpenResponseWaitHandle.Set();
  75. }
  76. /// <summary>
  77. /// Called when channel is closed by the server.
  78. /// </summary>
  79. protected override void OnClose()
  80. {
  81. base.OnClose();
  82. // This timeout needed since when channel is closed it does not immediately becomes available
  83. // but it takes time for the server to clean up resource and allow new channels to be created.
  84. Thread.Sleep(100);
  85. this.SessionSemaphore.Release();
  86. }
  87. /// <summary>
  88. /// Sends the pseudo terminal request.
  89. /// </summary>
  90. /// <param name="environmentVariable">The environment variable.</param>
  91. /// <param name="columns">The columns.</param>
  92. /// <param name="rows">The rows.</param>
  93. /// <param name="width">The width.</param>
  94. /// <param name="height">The height.</param>
  95. /// <param name="terminalMode">The terminal mode.</param>
  96. /// <returns>true if request was successful; otherwise false.</returns>
  97. public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, string terminalMode)
  98. {
  99. this._channelRequestResponse.Reset();
  100. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable, columns, rows, width, height, terminalMode)));
  101. this._channelRequestResponse.WaitOne();
  102. return this._channelRequestSucces;
  103. }
  104. /// <summary>
  105. /// Sends the X11 forwarding request.
  106. /// </summary>
  107. /// <param name="isSingleConnection">if set to <c>true</c> the it is single connection.</param>
  108. /// <param name="protocol">The protocol.</param>
  109. /// <param name="cookie">The cookie.</param>
  110. /// <param name="screenNumber">The screen number.</param>
  111. /// <returns>true if request was successful; otherwise false.</returns>
  112. public bool SendX11ForwardingRequest(bool isSingleConnection, string protocol, byte[] cookie, uint screenNumber)
  113. {
  114. this._channelRequestResponse.Reset();
  115. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new X11ForwardingRequestInfo(isSingleConnection, protocol, cookie, screenNumber)));
  116. this._channelRequestResponse.WaitOne();
  117. return this._channelRequestSucces;
  118. }
  119. /// <summary>
  120. /// Sends the environment variable request.
  121. /// </summary>
  122. /// <param name="variableName">Name of the variable.</param>
  123. /// <param name="variableValue">The variable value.</param>
  124. /// <returns>true if request was successful; otherwise false.</returns>
  125. public bool SendEnvironmentVariableRequest(string variableName, string variableValue)
  126. {
  127. this._channelRequestResponse.Reset();
  128. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EnvironmentVariableRequestInfo(variableName, variableValue)));
  129. this._channelRequestResponse.WaitOne();
  130. return this._channelRequestSucces;
  131. }
  132. /// <summary>
  133. /// Sends the shell request.
  134. /// </summary>
  135. /// <returns>true if request was successful; otherwise false.</returns>
  136. public bool SendShellRequest()
  137. {
  138. this._channelRequestResponse.Reset();
  139. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ShellRequestInfo()));
  140. this._channelRequestResponse.WaitOne();
  141. return this._channelRequestSucces;
  142. }
  143. /// <summary>
  144. /// Sends the exec request.
  145. /// </summary>
  146. /// <param name="command">The command.</param>
  147. /// <returns>true if request was successful; otherwise false.</returns>
  148. public bool SendExecRequest(string command)
  149. {
  150. this._channelRequestResponse.Reset();
  151. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExecRequestInfo(command)));
  152. this._channelRequestResponse.WaitOne();
  153. return this._channelRequestSucces;
  154. }
  155. /// <summary>
  156. /// Sends the exec request.
  157. /// </summary>
  158. /// <param name="breakLength">Length of the break.</param>
  159. /// <returns>true if request was successful; otherwise false.</returns>
  160. public bool SendBreakRequest(uint breakLength)
  161. {
  162. this._channelRequestResponse.Reset();
  163. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new BreakRequestInfo(breakLength)));
  164. this._channelRequestResponse.WaitOne();
  165. return this._channelRequestSucces;
  166. }
  167. /// <summary>
  168. /// Sends the subsystem request.
  169. /// </summary>
  170. /// <param name="subsystem">The subsystem.</param>
  171. /// <returns>true if request was successful; otherwise false.</returns>
  172. public bool SendSubsystemRequest(string subsystem)
  173. {
  174. this._channelRequestResponse.Reset();
  175. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SubsystemRequestInfo(subsystem)));
  176. this._channelRequestResponse.WaitOne();
  177. return this._channelRequestSucces;
  178. }
  179. /// <summary>
  180. /// Sends the window change request.
  181. /// </summary>
  182. /// <param name="columns">The columns.</param>
  183. /// <param name="rows">The rows.</param>
  184. /// <param name="width">The width.</param>
  185. /// <param name="height">The height.</param>
  186. /// <returns>true if request was successful; otherwise false.</returns>
  187. public bool SendWindowChangeRequest(uint columns, uint rows, uint width, uint height)
  188. {
  189. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new WindowChangeRequestInfo(columns, rows, width, height)));
  190. return true;
  191. }
  192. /// <summary>
  193. /// Sends the local flow request.
  194. /// </summary>
  195. /// <param name="clientCanDo">if set to <c>true</c> [client can do].</param>
  196. /// <returns>true if request was successful; otherwise false.</returns>
  197. public bool SendLocalFlowRequest(bool clientCanDo)
  198. {
  199. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new XonXoffRequestInfo(clientCanDo)));
  200. return true;
  201. }
  202. /// <summary>
  203. /// Sends the signal request.
  204. /// </summary>
  205. /// <param name="signalName">Name of the signal.</param>
  206. /// <returns>true if request was successful; otherwise false.</returns>
  207. public bool SendSignalRequest(string signalName)
  208. {
  209. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SignalRequestInfo(signalName)));
  210. return true;
  211. }
  212. /// <summary>
  213. /// Sends the exit status request.
  214. /// </summary>
  215. /// <param name="exitStatus">The exit status.</param>
  216. /// <returns>true if request was successful; otherwise false.</returns>
  217. public bool SendExitStatusRequest(uint exitStatus)
  218. {
  219. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExitStatusRequestInfo(exitStatus)));
  220. return true;
  221. }
  222. /// <summary>
  223. /// Sends the exit signal request.
  224. /// </summary>
  225. /// <param name="signalName">Name of the signal.</param>
  226. /// <param name="coreDumped">if set to <c>true</c> [core dumped].</param>
  227. /// <param name="errorMessage">The error message.</param>
  228. /// <param name="language">The language.</param>
  229. /// <returns>true if request was successful; otherwise false.</returns>
  230. public bool SendExitSignalRequest(string signalName, bool coreDumped, string errorMessage, string language)
  231. {
  232. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExitSignalRequestInfo(signalName, coreDumped, errorMessage, language)));
  233. return true;
  234. }
  235. /// <summary>
  236. /// Sends eow@openssh.com request.
  237. /// </summary>
  238. /// <returns>true if request was successful; otherwise false.</returns>
  239. public bool SendEndOfWriteRequest()
  240. {
  241. this._channelRequestResponse.Reset();
  242. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EndOfWriteRequestInfo()));
  243. this._channelRequestResponse.WaitOne();
  244. return this._channelRequestSucces;
  245. }
  246. /// <summary>
  247. /// Sends keepalive@openssh.com request.
  248. /// </summary>
  249. /// <returns>true if request was successful; otherwise false.</returns>
  250. public bool SendKeepAliveRequest()
  251. {
  252. this._channelRequestResponse.Reset();
  253. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new KeepAliveRequestInfo()));
  254. this._channelRequestResponse.WaitOne();
  255. return this._channelRequestSucces;
  256. }
  257. /// <summary>
  258. /// Called when channel request was successful
  259. /// </summary>
  260. protected override void OnSuccess()
  261. {
  262. base.OnSuccess();
  263. this._channelRequestSucces = true;
  264. this._channelRequestResponse.Set();
  265. }
  266. /// <summary>
  267. /// Called when channel request failed.
  268. /// </summary>
  269. protected override void OnFailure()
  270. {
  271. base.OnFailure();
  272. this._channelRequestSucces = false;
  273. this._channelRequestResponse.Set();
  274. }
  275. /// <summary>
  276. /// Sends the channel open message.
  277. /// </summary>
  278. protected void SendChannelOpenMessage()
  279. {
  280. lock (this.SessionSemaphore)
  281. {
  282. // Ensure that channels are available
  283. this.SessionSemaphore.Wait();
  284. this.SendMessage(new ChannelOpenMessage(this.LocalChannelNumber, this.LocalWindowSize, this.PacketSize, new SessionChannelOpenInfo()));
  285. }
  286. }
  287. /// <summary>
  288. /// Releases unmanaged and - optionally - managed resources
  289. /// </summary>
  290. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  291. protected override void Dispose(bool disposing)
  292. {
  293. if (this._channelOpenResponseWaitHandle != null)
  294. {
  295. this._channelOpenResponseWaitHandle.Dispose();
  296. this._channelOpenResponseWaitHandle = null;
  297. }
  298. if (this._channelRequestResponse != null)
  299. {
  300. this._channelRequestResponse.Dispose();
  301. this._channelRequestResponse = null;
  302. }
  303. base.Dispose(disposing);
  304. }
  305. }
  306. }