ChannelSession.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. using System.Threading;
  2. using Renci.SshNet.Common;
  3. using Renci.SshNet.Messages.Connection;
  4. using System.Globalization;
  5. using System.Collections.Generic;
  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. this.SessionSemaphore.Release();
  73. this._channelOpenResponseWaitHandle.Set();
  74. }
  75. /// <summary>
  76. /// Called when channel is closed by the server.
  77. /// </summary>
  78. protected override void OnClose()
  79. {
  80. base.OnClose();
  81. // This timeout needed since when channel is closed it does not immediately becomes available
  82. // but it takes time for the server to clean up resource and allow new channels to be created.
  83. Thread.Sleep(100);
  84. this.SessionSemaphore.Release();
  85. }
  86. protected override void Close(bool wait)
  87. {
  88. base.Close(wait);
  89. if (!wait)
  90. {
  91. this.SessionSemaphore.Release();
  92. }
  93. }
  94. /// <summary>
  95. /// Sends the pseudo terminal request.
  96. /// </summary>
  97. /// <param name="environmentVariable">The environment variable.</param>
  98. /// <param name="columns">The columns.</param>
  99. /// <param name="rows">The rows.</param>
  100. /// <param name="width">The width.</param>
  101. /// <param name="height">The height.</param>
  102. /// <param name="terminalModeValues">The terminal mode values.</param>
  103. /// <returns>
  104. /// true if request was successful; otherwise false.
  105. /// </returns>
  106. public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues)
  107. {
  108. this._channelRequestResponse.Reset();
  109. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable, columns, rows, width, height, terminalModeValues)));
  110. this.WaitHandle(this._channelRequestResponse);
  111. return this._channelRequestSucces;
  112. }
  113. /// <summary>
  114. /// Sends the X11 forwarding request.
  115. /// </summary>
  116. /// <param name="isSingleConnection">if set to <c>true</c> the it is single connection.</param>
  117. /// <param name="protocol">The protocol.</param>
  118. /// <param name="cookie">The cookie.</param>
  119. /// <param name="screenNumber">The screen number.</param>
  120. /// <returns>true if request was successful; otherwise false.</returns>
  121. public bool SendX11ForwardingRequest(bool isSingleConnection, string protocol, byte[] cookie, uint screenNumber)
  122. {
  123. this._channelRequestResponse.Reset();
  124. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new X11ForwardingRequestInfo(isSingleConnection, protocol, cookie, screenNumber)));
  125. this.WaitHandle(this._channelRequestResponse);
  126. return this._channelRequestSucces;
  127. }
  128. /// <summary>
  129. /// Sends the environment variable request.
  130. /// </summary>
  131. /// <param name="variableName">Name of the variable.</param>
  132. /// <param name="variableValue">The variable value.</param>
  133. /// <returns>true if request was successful; otherwise false.</returns>
  134. public bool SendEnvironmentVariableRequest(string variableName, string variableValue)
  135. {
  136. this._channelRequestResponse.Reset();
  137. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EnvironmentVariableRequestInfo(variableName, variableValue)));
  138. this.WaitHandle(this._channelRequestResponse);
  139. return this._channelRequestSucces;
  140. }
  141. /// <summary>
  142. /// Sends the shell request.
  143. /// </summary>
  144. /// <returns>true if request was successful; otherwise false.</returns>
  145. public bool SendShellRequest()
  146. {
  147. this._channelRequestResponse.Reset();
  148. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ShellRequestInfo()));
  149. this.WaitHandle(this._channelRequestResponse);
  150. return this._channelRequestSucces;
  151. }
  152. /// <summary>
  153. /// Sends the exec request.
  154. /// </summary>
  155. /// <param name="command">The command.</param>
  156. /// <returns>true if request was successful; otherwise false.</returns>
  157. public bool SendExecRequest(string command)
  158. {
  159. this._channelRequestResponse.Reset();
  160. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExecRequestInfo(command, this.ConnectionInfo.Encoding)));
  161. this.WaitHandle(this._channelRequestResponse);
  162. return this._channelRequestSucces;
  163. }
  164. /// <summary>
  165. /// Sends the exec request.
  166. /// </summary>
  167. /// <param name="breakLength">Length of the break.</param>
  168. /// <returns>true if request was successful; otherwise false.</returns>
  169. public bool SendBreakRequest(uint breakLength)
  170. {
  171. this._channelRequestResponse.Reset();
  172. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new BreakRequestInfo(breakLength)));
  173. this.WaitHandle(this._channelRequestResponse);
  174. return this._channelRequestSucces;
  175. }
  176. /// <summary>
  177. /// Sends the subsystem request.
  178. /// </summary>
  179. /// <param name="subsystem">The subsystem.</param>
  180. /// <returns>true if request was successful; otherwise false.</returns>
  181. public bool SendSubsystemRequest(string subsystem)
  182. {
  183. this._channelRequestResponse.Reset();
  184. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SubsystemRequestInfo(subsystem)));
  185. this.WaitHandle(this._channelRequestResponse);
  186. return this._channelRequestSucces;
  187. }
  188. /// <summary>
  189. /// Sends the window change request.
  190. /// </summary>
  191. /// <param name="columns">The columns.</param>
  192. /// <param name="rows">The rows.</param>
  193. /// <param name="width">The width.</param>
  194. /// <param name="height">The height.</param>
  195. /// <returns>true if request was successful; otherwise false.</returns>
  196. public bool SendWindowChangeRequest(uint columns, uint rows, uint width, uint height)
  197. {
  198. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new WindowChangeRequestInfo(columns, rows, width, height)));
  199. return true;
  200. }
  201. /// <summary>
  202. /// Sends the local flow request.
  203. /// </summary>
  204. /// <param name="clientCanDo">if set to <c>true</c> [client can do].</param>
  205. /// <returns>true if request was successful; otherwise false.</returns>
  206. public bool SendLocalFlowRequest(bool clientCanDo)
  207. {
  208. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new XonXoffRequestInfo(clientCanDo)));
  209. return true;
  210. }
  211. /// <summary>
  212. /// Sends the signal request.
  213. /// </summary>
  214. /// <param name="signalName">Name of the signal.</param>
  215. /// <returns>true if request was successful; otherwise false.</returns>
  216. public bool SendSignalRequest(string signalName)
  217. {
  218. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SignalRequestInfo(signalName)));
  219. return true;
  220. }
  221. /// <summary>
  222. /// Sends the exit status request.
  223. /// </summary>
  224. /// <param name="exitStatus">The exit status.</param>
  225. /// <returns>true if request was successful; otherwise false.</returns>
  226. public bool SendExitStatusRequest(uint exitStatus)
  227. {
  228. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExitStatusRequestInfo(exitStatus)));
  229. return true;
  230. }
  231. /// <summary>
  232. /// Sends the exit signal request.
  233. /// </summary>
  234. /// <param name="signalName">Name of the signal.</param>
  235. /// <param name="coreDumped">if set to <c>true</c> [core dumped].</param>
  236. /// <param name="errorMessage">The error message.</param>
  237. /// <param name="language">The language.</param>
  238. /// <returns>true if request was successful; otherwise false.</returns>
  239. public bool SendExitSignalRequest(string signalName, bool coreDumped, string errorMessage, string language)
  240. {
  241. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExitSignalRequestInfo(signalName, coreDumped, errorMessage, language)));
  242. return true;
  243. }
  244. /// <summary>
  245. /// Sends eow@openssh.com request.
  246. /// </summary>
  247. /// <returns>true if request was successful; otherwise false.</returns>
  248. public bool SendEndOfWriteRequest()
  249. {
  250. this._channelRequestResponse.Reset();
  251. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EndOfWriteRequestInfo()));
  252. this.WaitHandle(this._channelRequestResponse);
  253. return this._channelRequestSucces;
  254. }
  255. /// <summary>
  256. /// Sends keepalive@openssh.com request.
  257. /// </summary>
  258. /// <returns>true if request was successful; otherwise false.</returns>
  259. public bool SendKeepAliveRequest()
  260. {
  261. this._channelRequestResponse.Reset();
  262. this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new KeepAliveRequestInfo()));
  263. this.WaitHandle(this._channelRequestResponse);
  264. return this._channelRequestSucces;
  265. }
  266. /// <summary>
  267. /// Called when channel request was successful
  268. /// </summary>
  269. protected override void OnSuccess()
  270. {
  271. base.OnSuccess();
  272. this._channelRequestSucces = true;
  273. this._channelRequestResponse.Set();
  274. }
  275. /// <summary>
  276. /// Called when channel request failed.
  277. /// </summary>
  278. protected override void OnFailure()
  279. {
  280. base.OnFailure();
  281. this._channelRequestSucces = false;
  282. this._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. }