ChannelSession.cs 15 KB

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