ChannelSession.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Threading;
  5. using Renci.SshNet.Common;
  6. using Renci.SshNet.Messages.Connection;
  7. namespace Renci.SshNet.Channels
  8. {
  9. /// <summary>
  10. /// Implements Session SSH channel.
  11. /// </summary>
  12. internal sealed class ChannelSession : ClientChannel, IChannelSession
  13. {
  14. /// <summary>
  15. /// Counts failed channel open attempts
  16. /// </summary>
  17. private int _failedOpenAttempts;
  18. /// <summary>
  19. /// Holds a value indicating whether the session semaphore has been obtained by the current
  20. /// channel.
  21. /// </summary>
  22. /// <value>
  23. /// <c>0</c> when the session semaphore has not been obtained or has already been released,
  24. /// and <c>1</c> when the session has been obtained and still needs to be released.
  25. /// </value>
  26. private int _sessionSemaphoreObtained;
  27. /// <summary>
  28. /// Wait handle to signal when response was received to open the channel
  29. /// </summary>
  30. private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(false);
  31. private EventWaitHandle _channelRequestResponse = new ManualResetEvent(false);
  32. private bool _channelRequestSucces;
  33. /// <summary>
  34. /// Initializes a new <see cref="ChannelSession"/> instance.
  35. /// </summary>
  36. /// <param name="session">The session.</param>
  37. /// <param name="localChannelNumber">The local channel number.</param>
  38. /// <param name="localWindowSize">Size of the window.</param>
  39. /// <param name="localPacketSize">Size of the packet.</param>
  40. public ChannelSession(ISession session, uint localChannelNumber, uint localWindowSize, uint localPacketSize)
  41. : base(session, localChannelNumber, localWindowSize, localPacketSize)
  42. {
  43. }
  44. /// <summary>
  45. /// Gets the type of the channel.
  46. /// </summary>
  47. /// <value>
  48. /// The type of the channel.
  49. /// </value>
  50. public override ChannelTypes ChannelType
  51. {
  52. get { return ChannelTypes.Session; }
  53. }
  54. /// <summary>
  55. /// Opens the channel.
  56. /// </summary>
  57. public void Open()
  58. {
  59. // Try to open channel several times
  60. while (!IsOpen && _failedOpenAttempts < ConnectionInfo.RetryAttempts)
  61. {
  62. SendChannelOpenMessage();
  63. try
  64. {
  65. WaitOnHandle(_channelOpenResponseWaitHandle);
  66. }
  67. catch (Exception)
  68. {
  69. // avoid leaking session semaphore
  70. ReleaseSemaphore();
  71. throw;
  72. }
  73. }
  74. if (!IsOpen)
  75. throw new SshException(string.Format(CultureInfo.CurrentCulture, "Failed to open a channel after {0} attempts.", _failedOpenAttempts));
  76. }
  77. /// <summary>
  78. /// Called when channel is opened by the server.
  79. /// </summary>
  80. /// <param name="remoteChannelNumber">The remote channel number.</param>
  81. /// <param name="initialWindowSize">Initial size of the window.</param>
  82. /// <param name="maximumPacketSize">Maximum size of the packet.</param>
  83. protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize)
  84. {
  85. base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize);
  86. _channelOpenResponseWaitHandle.Set();
  87. }
  88. /// <summary>
  89. /// Called when channel failed to open.
  90. /// </summary>
  91. /// <param name="reasonCode">The reason code.</param>
  92. /// <param name="description">The description.</param>
  93. /// <param name="language">The language.</param>
  94. protected override void OnOpenFailure(uint reasonCode, string description, string language)
  95. {
  96. _failedOpenAttempts++;
  97. ReleaseSemaphore();
  98. _channelOpenResponseWaitHandle.Set();
  99. }
  100. protected override void Close()
  101. {
  102. base.Close();
  103. ReleaseSemaphore();
  104. }
  105. /// <summary>
  106. /// Sends the pseudo terminal request.
  107. /// </summary>
  108. /// <param name="environmentVariable">The environment variable.</param>
  109. /// <param name="columns">The columns.</param>
  110. /// <param name="rows">The rows.</param>
  111. /// <param name="width">The width.</param>
  112. /// <param name="height">The height.</param>
  113. /// <param name="terminalModeValues">The terminal mode values.</param>
  114. /// <returns>
  115. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  116. /// </returns>
  117. public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues)
  118. {
  119. _channelRequestResponse.Reset();
  120. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable, columns, rows, width, height, terminalModeValues)));
  121. WaitOnHandle(_channelRequestResponse);
  122. return _channelRequestSucces;
  123. }
  124. /// <summary>
  125. /// Sends the X11 forwarding request.
  126. /// </summary>
  127. /// <param name="isSingleConnection">if set to <c>true</c> the it is single connection.</param>
  128. /// <param name="protocol">The protocol.</param>
  129. /// <param name="cookie">The cookie.</param>
  130. /// <param name="screenNumber">The screen number.</param>
  131. /// <returns>
  132. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  133. /// </returns>
  134. public bool SendX11ForwardingRequest(bool isSingleConnection, string protocol, byte[] cookie, uint screenNumber)
  135. {
  136. _channelRequestResponse.Reset();
  137. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new X11ForwardingRequestInfo(isSingleConnection, protocol, cookie, screenNumber)));
  138. WaitOnHandle(_channelRequestResponse);
  139. return _channelRequestSucces;
  140. }
  141. /// <summary>
  142. /// Sends the environment variable request.
  143. /// </summary>
  144. /// <param name="variableName">Name of the variable.</param>
  145. /// <param name="variableValue">The variable value.</param>
  146. /// <returns>
  147. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  148. /// </returns>
  149. public bool SendEnvironmentVariableRequest(string variableName, string variableValue)
  150. {
  151. _channelRequestResponse.Reset();
  152. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new EnvironmentVariableRequestInfo(variableName, variableValue)));
  153. WaitOnHandle(_channelRequestResponse);
  154. return _channelRequestSucces;
  155. }
  156. /// <summary>
  157. /// Sends the shell request.
  158. /// </summary>
  159. /// <returns>
  160. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  161. /// </returns>
  162. public bool SendShellRequest()
  163. {
  164. _channelRequestResponse.Reset();
  165. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ShellRequestInfo()));
  166. WaitOnHandle(_channelRequestResponse);
  167. return _channelRequestSucces;
  168. }
  169. /// <summary>
  170. /// Sends the exec request.
  171. /// </summary>
  172. /// <param name="command">The command.</param>
  173. /// <returns>
  174. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  175. /// </returns>
  176. public bool SendExecRequest(string command)
  177. {
  178. _channelRequestResponse.Reset();
  179. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ExecRequestInfo(command, ConnectionInfo.Encoding)));
  180. WaitOnHandle(_channelRequestResponse);
  181. return _channelRequestSucces;
  182. }
  183. /// <summary>
  184. /// Sends the exec request.
  185. /// </summary>
  186. /// <param name="breakLength">Length of the break.</param>
  187. /// <returns>
  188. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  189. /// </returns>
  190. public bool SendBreakRequest(uint breakLength)
  191. {
  192. _channelRequestResponse.Reset();
  193. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new BreakRequestInfo(breakLength)));
  194. WaitOnHandle(_channelRequestResponse);
  195. return _channelRequestSucces;
  196. }
  197. /// <summary>
  198. /// Sends the subsystem request.
  199. /// </summary>
  200. /// <param name="subsystem">The subsystem.</param>
  201. /// <returns>
  202. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  203. /// </returns>
  204. public bool SendSubsystemRequest(string subsystem)
  205. {
  206. _channelRequestResponse.Reset();
  207. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new SubsystemRequestInfo(subsystem)));
  208. WaitOnHandle(_channelRequestResponse);
  209. return _channelRequestSucces;
  210. }
  211. /// <summary>
  212. /// Sends the window change request.
  213. /// </summary>
  214. /// <param name="columns">The columns.</param>
  215. /// <param name="rows">The rows.</param>
  216. /// <param name="width">The width.</param>
  217. /// <param name="height">The height.</param>
  218. /// <returns>
  219. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  220. /// </returns>
  221. public bool SendWindowChangeRequest(uint columns, uint rows, uint width, uint height)
  222. {
  223. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new WindowChangeRequestInfo(columns, rows, width, height)));
  224. return true;
  225. }
  226. /// <summary>
  227. /// Sends the local flow request.
  228. /// </summary>
  229. /// <param name="clientCanDo">if set to <c>true</c> [client can do].</param>
  230. /// <returns>
  231. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  232. /// </returns>
  233. public bool SendLocalFlowRequest(bool clientCanDo)
  234. {
  235. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new XonXoffRequestInfo(clientCanDo)));
  236. return true;
  237. }
  238. /// <summary>
  239. /// Sends the signal request.
  240. /// </summary>
  241. /// <param name="signalName">Name of the signal.</param>
  242. /// <returns>
  243. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  244. /// </returns>
  245. public bool SendSignalRequest(string signalName)
  246. {
  247. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new SignalRequestInfo(signalName)));
  248. return true;
  249. }
  250. /// <summary>
  251. /// Sends the exit status request.
  252. /// </summary>
  253. /// <param name="exitStatus">The exit status.</param>
  254. /// <returns>
  255. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  256. /// </returns>
  257. public bool SendExitStatusRequest(uint exitStatus)
  258. {
  259. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ExitStatusRequestInfo(exitStatus)));
  260. return true;
  261. }
  262. /// <summary>
  263. /// Sends the exit signal request.
  264. /// </summary>
  265. /// <param name="signalName">Name of the signal.</param>
  266. /// <param name="coreDumped">if set to <c>true</c> [core dumped].</param>
  267. /// <param name="errorMessage">The error message.</param>
  268. /// <param name="language">The language.</param>
  269. /// <returns>
  270. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  271. /// </returns>
  272. public bool SendExitSignalRequest(string signalName, bool coreDumped, string errorMessage, string language)
  273. {
  274. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ExitSignalRequestInfo(signalName, coreDumped, errorMessage, language)));
  275. return true;
  276. }
  277. /// <summary>
  278. /// Sends eow@openssh.com request.
  279. /// </summary>
  280. /// <returns>
  281. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  282. /// </returns>
  283. public bool SendEndOfWriteRequest()
  284. {
  285. _channelRequestResponse.Reset();
  286. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new EndOfWriteRequestInfo()));
  287. WaitOnHandle(_channelRequestResponse);
  288. return _channelRequestSucces;
  289. }
  290. /// <summary>
  291. /// Sends keepalive@openssh.com request.
  292. /// </summary>
  293. /// <returns>
  294. /// <c>true</c> if request was successful; otherwise <c>false</c>.
  295. /// </returns>
  296. public bool SendKeepAliveRequest()
  297. {
  298. _channelRequestResponse.Reset();
  299. SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new KeepAliveRequestInfo()));
  300. WaitOnHandle(_channelRequestResponse);
  301. return _channelRequestSucces;
  302. }
  303. /// <summary>
  304. /// Called when channel request was successful
  305. /// </summary>
  306. protected override void OnSuccess()
  307. {
  308. base.OnSuccess();
  309. _channelRequestSucces = true;
  310. var channelRequestResponse = _channelRequestResponse;
  311. if (channelRequestResponse != null)
  312. channelRequestResponse.Set();
  313. }
  314. /// <summary>
  315. /// Called when channel request failed.
  316. /// </summary>
  317. protected override void OnFailure()
  318. {
  319. base.OnFailure();
  320. _channelRequestSucces = false;
  321. var channelRequestResponse = _channelRequestResponse;
  322. if (channelRequestResponse != null)
  323. channelRequestResponse.Set();
  324. }
  325. /// <summary>
  326. /// Sends the channel open message.
  327. /// </summary>
  328. /// <exception cref="SshConnectionException">The client is not connected.</exception>
  329. /// <exception cref="SshOperationTimeoutException">The operation timed out.</exception>
  330. /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
  331. /// <remarks>
  332. /// <para>
  333. /// When a session semaphore for this instance has not yet been obtained by this or any other thread,
  334. /// the thread will block until such a semaphore is available and send a <see cref="ChannelOpenMessage"/>
  335. /// to the remote host.
  336. /// </para>
  337. /// <para>
  338. /// Note that the session semaphore is released in any of the following cases:
  339. /// <list type="bullet">
  340. /// <item>
  341. /// <description>A <see cref="ChannelOpenFailureMessage"/> is received for the channel being opened.</description>
  342. /// </item>
  343. /// <item>
  344. /// <description>The remote host does not respond to the <see cref="ChannelOpenMessage"/> within the configured <see cref="ConnectionInfo.Timeout"/>.</description>
  345. /// </item>
  346. /// <item>
  347. /// <description>The remote host closes the channel.</description>
  348. /// </item>
  349. /// <item>
  350. /// <description>The <see cref="ChannelSession"/> is disposed.</description>
  351. /// </item>
  352. /// <item>
  353. /// <description>A socket error occurs sending a message to the remote host.</description>
  354. /// </item>
  355. /// </list>
  356. /// </para>
  357. /// <para>
  358. /// If the session semaphore was already obtained for this instance (and not released), then this method
  359. /// immediately returns control to the caller. This should only happen when another thread has obtain the
  360. /// session semaphore and already sent the <see cref="ChannelOpenMessage"/>, but the remote host did not
  361. /// confirmed or rejected attempt to open the channel.
  362. /// </para>
  363. /// </remarks>
  364. private void SendChannelOpenMessage()
  365. {
  366. // do not allow open to be ChannelOpenMessage to be sent again until we've
  367. // had a response on the previous attempt for the current channel
  368. if (Interlocked.CompareExchange(ref _sessionSemaphoreObtained, 1, 0) == 0)
  369. {
  370. SessionSemaphore.Wait();
  371. SendMessage(new ChannelOpenMessage(LocalChannelNumber,
  372. LocalWindowSize,
  373. LocalPacketSize,
  374. new SessionChannelOpenInfo()));
  375. }
  376. }
  377. /// <summary>
  378. /// Releases unmanaged and - optionally - managed resources
  379. /// </summary>
  380. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  381. protected override void Dispose(bool disposing)
  382. {
  383. base.Dispose(disposing);
  384. if (disposing)
  385. {
  386. var channelOpenResponseWaitHandle = _channelOpenResponseWaitHandle;
  387. if (channelOpenResponseWaitHandle != null)
  388. {
  389. _channelOpenResponseWaitHandle = null;
  390. channelOpenResponseWaitHandle.Dispose();
  391. }
  392. var channelRequestResponse = _channelRequestResponse;
  393. if (channelRequestResponse != null)
  394. {
  395. _channelRequestResponse = null;
  396. channelRequestResponse.Dispose();
  397. }
  398. }
  399. }
  400. /// <summary>
  401. /// Releases the session semaphore.
  402. /// </summary>
  403. /// <remarks>
  404. /// When the session semaphore has already been released, or was never obtained by
  405. /// this instance, then this method does nothing.
  406. /// </remarks>
  407. private void ReleaseSemaphore()
  408. {
  409. if (Interlocked.CompareExchange(ref _sessionSemaphoreObtained, 0, 1) == 1)
  410. SessionSemaphore.Release();
  411. }
  412. }
  413. }