Channel.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1.  using System;
  2. using System.Threading;
  3. using Renci.SshNet.Common;
  4. using Renci.SshNet.Messages;
  5. using Renci.SshNet.Messages.Connection;
  6. using System.Globalization;
  7. namespace Renci.SshNet.Channels
  8. {
  9. /// <summary>
  10. /// Represents base class for SSH channel implementations.
  11. /// </summary>
  12. internal abstract class Channel : IChannel
  13. {
  14. private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(false);
  15. private EventWaitHandle _channelServerWindowAdjustWaitHandle = new ManualResetEvent(false);
  16. private EventWaitHandle _errorOccuredWaitHandle = new ManualResetEvent(false);
  17. private EventWaitHandle _disconnectedWaitHandle = new ManualResetEvent(false);
  18. private readonly object _serverWindowSizeLock = new object();
  19. private bool _closeMessageSent;
  20. private uint _initialWindowSize;
  21. private uint? _remoteWindowSize;
  22. private uint? _remoteChannelNumber;
  23. private uint? _remotePacketSize;
  24. private Session _session;
  25. /// <summary>
  26. /// Gets the session.
  27. /// </summary>
  28. /// <value>
  29. /// Thhe session.
  30. /// </value>
  31. protected Session Session
  32. {
  33. get { return _session; }
  34. }
  35. /// <summary>
  36. /// Gets the type of the channel.
  37. /// </summary>
  38. /// <value>
  39. /// The type of the channel.
  40. /// </value>
  41. public abstract ChannelTypes ChannelType { get; }
  42. /// <summary>
  43. /// Gets the local channel number.
  44. /// </summary>
  45. /// <value>
  46. /// The local channel number.
  47. /// </value>
  48. public uint LocalChannelNumber { get; private set; }
  49. /// <summary>
  50. /// Gets the maximum size of a packet.
  51. /// </summary>
  52. /// <value>
  53. /// The maximum size of a packet.
  54. /// </value>
  55. public uint LocalPacketSize { get; private set; }
  56. /// <summary>
  57. /// Gets the size of the local window.
  58. /// </summary>
  59. /// <value>
  60. /// The size of the local window.
  61. /// </value>
  62. public uint LocalWindowSize { get; private set; }
  63. /// <summary>
  64. /// Gets the remote channel number.
  65. /// </summary>
  66. /// <value>
  67. /// The remote channel number.
  68. /// </value>
  69. public uint RemoteChannelNumber
  70. {
  71. get
  72. {
  73. if (!_remoteChannelNumber.HasValue)
  74. throw CreateRemoteChannelInfoNotAvailableException();
  75. return _remoteChannelNumber.Value;
  76. }
  77. private set
  78. {
  79. _remoteChannelNumber = value;
  80. }
  81. }
  82. /// <summary>
  83. /// Gets the maximum size of a data packet that we can send using the channel.
  84. /// </summary>
  85. /// <value>
  86. /// The maximum size of data that can be sent using a <see cref="ChannelDataMessage"/>
  87. /// on the current channel.
  88. /// </value>
  89. /// <exception cref="InvalidOperationException">The channel has not been opened, or the open has not yet been confirmed.</exception>
  90. public uint RemotePacketSize
  91. {
  92. get
  93. {
  94. if (!_remotePacketSize.HasValue)
  95. throw CreateRemoteChannelInfoNotAvailableException();
  96. return _remotePacketSize.Value;
  97. }
  98. private set
  99. {
  100. _remotePacketSize = value;
  101. }
  102. }
  103. /// <summary>
  104. /// Gets the window size of the remote server.
  105. /// </summary>
  106. /// <value>
  107. /// The size of the server window.
  108. /// </value>
  109. public uint RemoteWindowSize
  110. {
  111. get
  112. {
  113. if (!_remoteWindowSize.HasValue)
  114. throw CreateRemoteChannelInfoNotAvailableException();
  115. return _remoteWindowSize.Value;
  116. }
  117. private set
  118. {
  119. _remoteWindowSize = value;
  120. }
  121. }
  122. /// <summary>
  123. /// Gets a value indicating whether this channel is open.
  124. /// </summary>
  125. /// <value>
  126. /// <c>true</c> if this channel is open; otherwise, <c>false</c>.
  127. /// </value>
  128. public bool IsOpen { get; protected set; }
  129. #region Message events
  130. /// <summary>
  131. /// Occurs when <see cref="ChannelDataMessage"/> message received
  132. /// </summary>
  133. public event EventHandler<ChannelDataEventArgs> DataReceived;
  134. /// <summary>
  135. /// Occurs when <see cref="ChannelExtendedDataMessage"/> message received
  136. /// </summary>
  137. public event EventHandler<ChannelDataEventArgs> ExtendedDataReceived;
  138. /// <summary>
  139. /// Occurs when <see cref="ChannelEofMessage"/> message received
  140. /// </summary>
  141. public event EventHandler<ChannelEventArgs> EndOfData;
  142. /// <summary>
  143. /// Occurs when <see cref="ChannelCloseMessage"/> message received
  144. /// </summary>
  145. public event EventHandler<ChannelEventArgs> Closed;
  146. /// <summary>
  147. /// Occurs when <see cref="ChannelRequestMessage"/> message received
  148. /// </summary>
  149. public event EventHandler<ChannelRequestEventArgs> RequestReceived;
  150. /// <summary>
  151. /// Occurs when <see cref="ChannelSuccessMessage"/> message received
  152. /// </summary>
  153. public event EventHandler<ChannelEventArgs> RequestSuccessed;
  154. /// <summary>
  155. /// Occurs when <see cref="ChannelFailureMessage"/> message received
  156. /// </summary>
  157. public event EventHandler<ChannelEventArgs> RequestFailed;
  158. #endregion
  159. /// <summary>
  160. /// Gets a value indicating whether the session is connected.
  161. /// </summary>
  162. /// <value>
  163. /// <c>true</c> if the session is connected; otherwise, <c>false</c>.
  164. /// </value>
  165. protected bool IsConnected
  166. {
  167. get { return this._session.IsConnected; }
  168. }
  169. /// <summary>
  170. /// Gets the connection info.
  171. /// </summary>
  172. /// <value>The connection info.</value>
  173. protected ConnectionInfo ConnectionInfo
  174. {
  175. get { return this._session.ConnectionInfo; }
  176. }
  177. /// <summary>
  178. /// Gets the session semaphore to control number of session channels
  179. /// </summary>
  180. /// <value>The session semaphore.</value>
  181. protected SemaphoreLight SessionSemaphore
  182. {
  183. get { return this._session.SessionSemaphore; }
  184. }
  185. /// <summary>
  186. /// Initializes the channel.
  187. /// </summary>
  188. /// <param name="session">The session.</param>
  189. /// <param name="localWindowSize">Size of the window.</param>
  190. /// <param name="localPacketSize">Size of the packet.</param>
  191. internal virtual void Initialize(Session session, uint localWindowSize, uint localPacketSize)
  192. {
  193. _session = session;
  194. _initialWindowSize = localWindowSize;
  195. LocalPacketSize = localPacketSize;
  196. LocalWindowSize = localWindowSize; // Initial window size
  197. LocalChannelNumber = session.NextChannelNumber;
  198. _session.ChannelWindowAdjustReceived += OnChannelWindowAdjust;
  199. _session.ChannelDataReceived += OnChannelData;
  200. _session.ChannelExtendedDataReceived += OnChannelExtendedData;
  201. _session.ChannelEofReceived += OnChannelEof;
  202. _session.ChannelCloseReceived += OnChannelClose;
  203. _session.ChannelRequestReceived += OnChannelRequest;
  204. _session.ChannelSuccessReceived += OnChannelSuccess;
  205. _session.ChannelFailureReceived += OnChannelFailure;
  206. _session.ErrorOccured += Session_ErrorOccured;
  207. _session.Disconnected += Session_Disconnected;
  208. }
  209. protected void InitializeRemoteInfo(uint remoteChannelNumber, uint remoteWindowSize, uint remotePacketSize)
  210. {
  211. RemoteChannelNumber = remoteChannelNumber;
  212. RemoteWindowSize = remoteWindowSize;
  213. RemotePacketSize = remotePacketSize;
  214. }
  215. /// <summary>
  216. /// Sends the SSH_MSG_CHANNEL_EOF message.
  217. /// </summary>
  218. public void SendEof()
  219. {
  220. // Send EOF message first when channel need to be closed
  221. this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber));
  222. }
  223. /// <summary>
  224. /// Sends a SSH_MSG_CHANNEL_DATA message with the specified payload.
  225. /// </summary>
  226. /// <param name="data">The payload to send.</param>
  227. public void SendData(byte[] data)
  228. {
  229. this.SendMessage(new ChannelDataMessage(this.RemoteChannelNumber, data));
  230. }
  231. /// <summary>
  232. /// Closes the channel.
  233. /// </summary>
  234. public virtual void Close()
  235. {
  236. this.Close(true);
  237. }
  238. #region Channel virtual methods
  239. /// <summary>
  240. /// Called when channel window need to be adjust.
  241. /// </summary>
  242. /// <param name="bytesToAdd">The bytes to add.</param>
  243. protected virtual void OnWindowAdjust(uint bytesToAdd)
  244. {
  245. lock (this._serverWindowSizeLock)
  246. {
  247. this.RemoteWindowSize += bytesToAdd;
  248. }
  249. this._channelServerWindowAdjustWaitHandle.Set();
  250. }
  251. /// <summary>
  252. /// Called when channel data is received.
  253. /// </summary>
  254. /// <param name="data">The data.</param>
  255. protected virtual void OnData(byte[] data)
  256. {
  257. this.AdjustDataWindow(data);
  258. var dataReceived = DataReceived;
  259. if (dataReceived != null)
  260. dataReceived(this, new ChannelDataEventArgs(LocalChannelNumber, data));
  261. }
  262. /// <summary>
  263. /// Called when channel extended data is received.
  264. /// </summary>
  265. /// <param name="data">The data.</param>
  266. /// <param name="dataTypeCode">The data type code.</param>
  267. protected virtual void OnExtendedData(byte[] data, uint dataTypeCode)
  268. {
  269. this.AdjustDataWindow(data);
  270. var extendedDataReceived = ExtendedDataReceived;
  271. if (extendedDataReceived != null)
  272. extendedDataReceived(this, new ChannelDataEventArgs(LocalChannelNumber, data, dataTypeCode));
  273. }
  274. /// <summary>
  275. /// Called when channel has no more data to receive.
  276. /// </summary>
  277. protected virtual void OnEof()
  278. {
  279. var endOfData = EndOfData;
  280. if (endOfData != null)
  281. endOfData(this, new ChannelEventArgs(LocalChannelNumber));
  282. }
  283. /// <summary>
  284. /// Called when channel is closed by the server.
  285. /// </summary>
  286. protected virtual void OnClose()
  287. {
  288. this.Close(false);
  289. var closed = Closed;
  290. if (closed != null)
  291. closed(this, new ChannelEventArgs(LocalChannelNumber));
  292. }
  293. /// <summary>
  294. /// Called when channel request received.
  295. /// </summary>
  296. /// <param name="info">Channel request information.</param>
  297. protected virtual void OnRequest(RequestInfo info)
  298. {
  299. var requestReceived = RequestReceived;
  300. if (requestReceived != null)
  301. requestReceived(this, new ChannelRequestEventArgs(info));
  302. }
  303. /// <summary>
  304. /// Called when channel request was successful
  305. /// </summary>
  306. protected virtual void OnSuccess()
  307. {
  308. var requestSuccessed = RequestSuccessed;
  309. if (requestSuccessed != null)
  310. requestSuccessed(this, new ChannelEventArgs(LocalChannelNumber));
  311. }
  312. /// <summary>
  313. /// Called when channel request failed.
  314. /// </summary>
  315. protected virtual void OnFailure()
  316. {
  317. var requestFailed = RequestFailed;
  318. if (requestFailed != null)
  319. requestFailed(this, new ChannelEventArgs(LocalChannelNumber));
  320. }
  321. #endregion
  322. /// <summary>
  323. /// Sends SSH message to the server.
  324. /// </summary>
  325. /// <param name="message">The message.</param>
  326. protected void SendMessage(Message message)
  327. {
  328. // send channel messages only while channel is open
  329. if (!this.IsOpen)
  330. return;
  331. this._session.SendMessage(message);
  332. }
  333. /// <summary>
  334. /// Sends close channel message to the server, and marks the channel closed.
  335. /// </summary>
  336. /// <param name="message">The message to send.</param>
  337. private void SendMessage(ChannelCloseMessage message)
  338. {
  339. // send channel messages only while channel is open
  340. if (!this.IsOpen)
  341. return;
  342. this._session.SendMessage(message);
  343. // when channel close message is sent channel considered to be closed
  344. this.IsOpen = false;
  345. }
  346. /// <summary>
  347. /// Sends channel data message to the servers.
  348. /// </summary>
  349. /// <param name="message">Channel data message.</param>
  350. /// <remarks>
  351. /// <para>
  352. /// When the data of the message exceeds the maximum packet size or the remote window
  353. /// size does not allow the full message to be sent, then this method will send the
  354. /// data in multiple chunks and will only wait for the remote window size to be adjusted
  355. /// when its zero.
  356. /// </para>
  357. /// <para>
  358. /// This is done to support SSH servers will a small window size that do not agressively
  359. /// increase their window size. We need to take into account that there may be SSH
  360. /// servers that only increase their window size when it has reached zero.
  361. /// </para>
  362. /// </remarks>
  363. protected void SendMessage(ChannelDataMessage message)
  364. {
  365. // send channel messages only while channel is open
  366. if (!this.IsOpen)
  367. return;
  368. var totalDataLength = message.Data.Length;
  369. var totalDataSent = 0;
  370. var totalBytesToSend = totalDataLength;
  371. while (totalBytesToSend > 0)
  372. {
  373. var dataThatCanBeSentInMessage = GetDataLengthThatCanBeSentInMessage(totalBytesToSend);
  374. if (dataThatCanBeSentInMessage == totalDataLength)
  375. {
  376. // we can send the message in one chunk
  377. this._session.SendMessage(message);
  378. }
  379. else
  380. {
  381. // we need to send the message in multiple chunks
  382. var dataToSend = new byte[dataThatCanBeSentInMessage];
  383. Array.Copy(message.Data, totalDataSent, dataToSend, 0, dataThatCanBeSentInMessage);
  384. this._session.SendMessage(new ChannelDataMessage(message.LocalChannelNumber, dataToSend));
  385. }
  386. totalDataSent += dataThatCanBeSentInMessage;
  387. totalBytesToSend -= dataThatCanBeSentInMessage;
  388. }
  389. }
  390. /// <summary>
  391. /// Sends channel extended data message to the servers.
  392. /// </summary>
  393. /// <param name="message">Channel data message.</param>
  394. /// <remarks>
  395. /// <para>
  396. /// When the data of the message exceeds the maximum packet size or the remote window
  397. /// size does not allow the full message to be sent, then this method will send the
  398. /// data in multiple chunks and will only wait for the remote window size to be adjusted
  399. /// when its zero.
  400. /// </para>
  401. /// <para>
  402. /// This is done to support SSH servers will a small window size that do not agressively
  403. /// increase their window size. We need to take into account that there may be SSH
  404. /// servers that only increase their window size when it has reached zero.
  405. /// </para>
  406. /// </remarks>
  407. protected void SendMessage(ChannelExtendedDataMessage message)
  408. {
  409. // end channel messages only while channel is open
  410. if (!this.IsOpen)
  411. return;
  412. var totalDataLength = message.Data.Length;
  413. var totalDataSent = 0;
  414. var totalBytesToSend = totalDataLength;
  415. while (totalBytesToSend > 0)
  416. {
  417. var dataThatCanBeSentInMessage = GetDataLengthThatCanBeSentInMessage(totalBytesToSend);
  418. if (dataThatCanBeSentInMessage == totalDataLength)
  419. {
  420. // we can send the message in one chunk
  421. this._session.SendMessage(message);
  422. }
  423. else
  424. {
  425. // we need to send the message in multiple chunks
  426. var dataToSend = new byte[dataThatCanBeSentInMessage];
  427. Array.Copy(message.Data, totalDataSent, dataToSend, 0, dataThatCanBeSentInMessage);
  428. this._session.SendMessage(new ChannelExtendedDataMessage(message.LocalChannelNumber,
  429. message.DataTypeCode, dataToSend));
  430. }
  431. totalDataSent += dataThatCanBeSentInMessage;
  432. totalBytesToSend -= dataThatCanBeSentInMessage;
  433. }
  434. }
  435. /// <summary>
  436. /// Waits for the handle to be signaled or for an error to occurs.
  437. /// </summary>
  438. /// <param name="waitHandle">The wait handle.</param>
  439. protected void WaitOnHandle(WaitHandle waitHandle)
  440. {
  441. this._session.WaitOnHandle(waitHandle);
  442. }
  443. protected virtual void Close(bool wait)
  444. {
  445. // send message to close the channel on the server
  446. // ignore sending close message when client not connected
  447. if (!_closeMessageSent && this.IsConnected)
  448. {
  449. lock (this)
  450. {
  451. if (!_closeMessageSent)
  452. {
  453. this.SendMessage(new ChannelCloseMessage(this.RemoteChannelNumber));
  454. this._closeMessageSent = true;
  455. }
  456. }
  457. }
  458. else
  459. {
  460. // also mark the channel closed if the session is no longer connected
  461. IsOpen = false;
  462. }
  463. // wait for channel to be closed
  464. if (wait)
  465. {
  466. WaitOnHandle(this._channelClosedWaitHandle);
  467. }
  468. }
  469. protected virtual void OnDisconnected()
  470. {
  471. }
  472. protected virtual void OnErrorOccured(Exception exp)
  473. {
  474. }
  475. private void Session_Disconnected(object sender, EventArgs e)
  476. {
  477. this.OnDisconnected();
  478. // If object is disposed or being disposed don't handle this event
  479. if (this._isDisposed)
  480. return;
  481. var disconnectedWaitHandle = this._disconnectedWaitHandle;
  482. if (disconnectedWaitHandle != null)
  483. disconnectedWaitHandle.Set();
  484. }
  485. private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
  486. {
  487. this.OnErrorOccured(e.Exception);
  488. // If object is disposed or being disposed don't handle this event
  489. if (this._isDisposed)
  490. return;
  491. var errorOccuredWaitHandle = this._errorOccuredWaitHandle;
  492. if (errorOccuredWaitHandle != null)
  493. errorOccuredWaitHandle.Set();
  494. }
  495. #region Channel message event handlers
  496. private void OnChannelWindowAdjust(object sender, MessageEventArgs<ChannelWindowAdjustMessage> e)
  497. {
  498. if (e.Message.LocalChannelNumber == this.LocalChannelNumber)
  499. {
  500. this.OnWindowAdjust(e.Message.BytesToAdd);
  501. }
  502. }
  503. private void OnChannelData(object sender, MessageEventArgs<ChannelDataMessage> e)
  504. {
  505. if (e.Message.LocalChannelNumber == this.LocalChannelNumber)
  506. {
  507. this.OnData(e.Message.Data);
  508. }
  509. }
  510. private void OnChannelExtendedData(object sender, MessageEventArgs<ChannelExtendedDataMessage> e)
  511. {
  512. if (e.Message.LocalChannelNumber == this.LocalChannelNumber)
  513. {
  514. this.OnExtendedData(e.Message.Data, e.Message.DataTypeCode);
  515. }
  516. }
  517. private void OnChannelEof(object sender, MessageEventArgs<ChannelEofMessage> e)
  518. {
  519. if (e.Message.LocalChannelNumber == this.LocalChannelNumber)
  520. {
  521. this.OnEof();
  522. }
  523. }
  524. private void OnChannelClose(object sender, MessageEventArgs<ChannelCloseMessage> e)
  525. {
  526. if (e.Message.LocalChannelNumber == this.LocalChannelNumber)
  527. {
  528. this.OnClose();
  529. var channelClosedWaitHandle = _channelClosedWaitHandle;
  530. if (channelClosedWaitHandle != null)
  531. channelClosedWaitHandle.Set();
  532. }
  533. }
  534. private void OnChannelRequest(object sender, MessageEventArgs<ChannelRequestMessage> e)
  535. {
  536. if (e.Message.LocalChannelNumber == this.LocalChannelNumber)
  537. {
  538. if (this._session.ConnectionInfo.ChannelRequests.ContainsKey(e.Message.RequestName))
  539. {
  540. // Get request specific class
  541. var requestInfo = this._session.ConnectionInfo.ChannelRequests[e.Message.RequestName];
  542. // Load request specific data
  543. requestInfo.Load(e.Message.RequestData);
  544. // Raise request specific event
  545. this.OnRequest(requestInfo);
  546. }
  547. else
  548. {
  549. throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Request '{0}' is not supported.", e.Message.RequestName));
  550. }
  551. }
  552. }
  553. private void OnChannelSuccess(object sender, MessageEventArgs<ChannelSuccessMessage> e)
  554. {
  555. if (e.Message.LocalChannelNumber == this.LocalChannelNumber)
  556. {
  557. this.OnSuccess();
  558. }
  559. }
  560. private void OnChannelFailure(object sender, MessageEventArgs<ChannelFailureMessage> e)
  561. {
  562. if (e.Message.LocalChannelNumber == this.LocalChannelNumber)
  563. {
  564. this.OnFailure();
  565. }
  566. }
  567. #endregion
  568. private void AdjustDataWindow(byte[] messageData)
  569. {
  570. this.LocalWindowSize -= (uint)messageData.Length;
  571. // Adjust window if window size is too low
  572. if (this.LocalWindowSize < this.LocalPacketSize)
  573. {
  574. this.SendMessage(new ChannelWindowAdjustMessage(this.RemoteChannelNumber, this._initialWindowSize - this.LocalWindowSize));
  575. this.LocalWindowSize = this._initialWindowSize;
  576. }
  577. }
  578. /// <summary>
  579. /// Determines the length of data that currently can be sent in a single message.
  580. /// </summary>
  581. /// <param name="messageLength">The length of the message that must be sent.</param>
  582. /// <returns>
  583. /// The actual data length that currently can be sent.
  584. /// </returns>
  585. private int GetDataLengthThatCanBeSentInMessage(int messageLength)
  586. {
  587. do
  588. {
  589. lock (this._serverWindowSizeLock)
  590. {
  591. var serverWindowSize = RemoteWindowSize;
  592. if (serverWindowSize == 0)
  593. {
  594. // allow us to be signal when remote window size is adjusted
  595. this._channelServerWindowAdjustWaitHandle.Reset();
  596. }
  597. else
  598. {
  599. var bytesThatCanBeSent = Math.Min(Math.Min(RemotePacketSize, (uint) messageLength),
  600. serverWindowSize);
  601. this.RemoteWindowSize -= bytesThatCanBeSent;
  602. return (int) bytesThatCanBeSent;
  603. }
  604. }
  605. // wait for remote window size to change
  606. this.WaitOnHandle(this._channelServerWindowAdjustWaitHandle);
  607. } while (true);
  608. }
  609. private InvalidOperationException CreateRemoteChannelInfoNotAvailableException()
  610. {
  611. throw new InvalidOperationException("The channel has not been opened, or the open has not yet been confirmed.");
  612. }
  613. #region IDisposable Members
  614. private bool _isDisposed;
  615. /// <summary>
  616. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  617. /// </summary>
  618. public void Dispose()
  619. {
  620. this.Dispose(true);
  621. GC.SuppressFinalize(this);
  622. }
  623. /// <summary>
  624. /// Releases unmanaged and - optionally - managed resources
  625. /// </summary>
  626. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  627. protected virtual void Dispose(bool disposing)
  628. {
  629. // Check to see if Dispose has already been called.
  630. if (!this._isDisposed)
  631. {
  632. // If disposing equals true, dispose all managed
  633. // and unmanaged resources.
  634. if (disposing)
  635. {
  636. this.Close(false);
  637. // Ensure that all events are detached from current instance
  638. this._session.ChannelWindowAdjustReceived -= OnChannelWindowAdjust;
  639. this._session.ChannelDataReceived -= OnChannelData;
  640. this._session.ChannelExtendedDataReceived -= OnChannelExtendedData;
  641. this._session.ChannelEofReceived -= OnChannelEof;
  642. this._session.ChannelCloseReceived -= OnChannelClose;
  643. this._session.ChannelRequestReceived -= OnChannelRequest;
  644. this._session.ChannelSuccessReceived -= OnChannelSuccess;
  645. this._session.ChannelFailureReceived -= OnChannelFailure;
  646. this._session.ErrorOccured -= Session_ErrorOccured;
  647. this._session.Disconnected -= Session_Disconnected;
  648. // Dispose managed resources.
  649. if (this._channelClosedWaitHandle != null)
  650. {
  651. this._channelClosedWaitHandle.Dispose();
  652. this._channelClosedWaitHandle = null;
  653. }
  654. if (this._channelServerWindowAdjustWaitHandle != null)
  655. {
  656. this._channelServerWindowAdjustWaitHandle.Dispose();
  657. this._channelServerWindowAdjustWaitHandle = null;
  658. }
  659. if (this._errorOccuredWaitHandle != null)
  660. {
  661. this._errorOccuredWaitHandle.Dispose();
  662. this._errorOccuredWaitHandle = null;
  663. }
  664. if (this._disconnectedWaitHandle != null)
  665. {
  666. this._disconnectedWaitHandle.Dispose();
  667. this._disconnectedWaitHandle = null;
  668. }
  669. }
  670. // Note disposing has been done.
  671. this._isDisposed = true;
  672. }
  673. }
  674. /// <summary>
  675. /// Releases unmanaged resources and performs other cleanup operations before the
  676. /// <see cref="Channel"/> is reclaimed by garbage collection.
  677. /// </summary>
  678. ~Channel()
  679. {
  680. // Do not re-create Dispose clean-up code here.
  681. // Calling Dispose(false) is optimal in terms of
  682. // readability and maintainability.
  683. this.Dispose(false);
  684. }
  685. #endregion
  686. }
  687. }