Session.cs 100 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380
  1. using System;
  2. using System.Buffers.Binary;
  3. using System.Diagnostics;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Net.Sockets;
  7. using System.Security.Cryptography;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using Renci.SshNet.Abstractions;
  12. using Renci.SshNet.Channels;
  13. using Renci.SshNet.Common;
  14. using Renci.SshNet.Compression;
  15. using Renci.SshNet.Connection;
  16. using Renci.SshNet.Messages;
  17. using Renci.SshNet.Messages.Authentication;
  18. using Renci.SshNet.Messages.Connection;
  19. using Renci.SshNet.Messages.Transport;
  20. using Renci.SshNet.Security;
  21. using Renci.SshNet.Security.Cryptography;
  22. namespace Renci.SshNet
  23. {
  24. /// <summary>
  25. /// Provides functionality to connect and interact with SSH server.
  26. /// </summary>
  27. public class Session : ISession
  28. {
  29. internal const byte CarriageReturn = 0x0d;
  30. internal const byte LineFeed = 0x0a;
  31. /// <summary>
  32. /// Specifies maximum packet size defined by the protocol.
  33. /// </summary>
  34. /// <value>
  35. /// 68536 (64 KB + 3000 bytes).
  36. /// </value>
  37. internal const int MaximumSshPacketSize = LocalChannelDataPacketSize + 3000;
  38. /// <summary>
  39. /// Holds the initial local window size for the channels.
  40. /// </summary>
  41. /// <value>
  42. /// 2147483647 (2^31 - 1) bytes.
  43. /// </value>
  44. /// <remarks>
  45. /// We currently do not define a maximum (remote) window size.
  46. /// </remarks>
  47. private const int InitialLocalWindowSize = 0x7FFFFFFF;
  48. /// <summary>
  49. /// Holds the maximum size of channel data packets that we receive.
  50. /// </summary>
  51. /// <value>
  52. /// 64 KB.
  53. /// </value>
  54. /// <remarks>
  55. /// <para>
  56. /// This is the maximum size (in bytes) we support for the data (payload) of a
  57. /// <c>SSH_MSG_CHANNEL_DATA</c> message we receive.
  58. /// </para>
  59. /// <para>
  60. /// We currently do not enforce this limit.
  61. /// </para>
  62. /// </remarks>
  63. private const int LocalChannelDataPacketSize = 1024 * 64;
  64. /// <summary>
  65. /// Holds the factory to use for creating new services.
  66. /// </summary>
  67. private readonly IServiceFactory _serviceFactory;
  68. private readonly ISocketFactory _socketFactory;
  69. /// <summary>
  70. /// Holds an object that is used to ensure only a single thread can read from
  71. /// <see cref="_socket"/> at any given time.
  72. /// </summary>
  73. private readonly object _socketReadLock = new object();
  74. /// <summary>
  75. /// Holds an object that is used to ensure only a single thread can write to
  76. /// <see cref="_socket"/> at any given time.
  77. /// </summary>
  78. /// <remarks>
  79. /// This is also used to ensure that <see cref="_outboundPacketSequence"/> is
  80. /// incremented atomatically.
  81. /// </remarks>
  82. private readonly object _socketWriteLock = new object();
  83. /// <summary>
  84. /// Holds an object that is used to ensure only a single thread can dispose
  85. /// <see cref="_socket"/> at any given time.
  86. /// </summary>
  87. /// <remarks>
  88. /// This is also used to ensure that <see cref="_socket"/> will not be disposed
  89. /// while performing a given operation or set of operations on <see cref="_socket"/>.
  90. /// </remarks>
  91. private readonly SemaphoreSlim _socketDisposeLock = new SemaphoreSlim(1, 1);
  92. /// <summary>
  93. /// Holds an object that is used to ensure only a single thread can connect
  94. /// at any given time.
  95. /// </summary>
  96. private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(1, 1);
  97. /// <summary>
  98. /// Holds metadata about session messages.
  99. /// </summary>
  100. private SshMessageFactory _sshMessageFactory;
  101. /// <summary>
  102. /// Holds a <see cref="WaitHandle"/> that is signaled when the message listener loop has completed.
  103. /// </summary>
  104. private ManualResetEvent _messageListenerCompleted;
  105. /// <summary>
  106. /// Specifies outbound packet number.
  107. /// </summary>
  108. private volatile uint _outboundPacketSequence;
  109. /// <summary>
  110. /// Specifies incoming packet number.
  111. /// </summary>
  112. private uint _inboundPacketSequence;
  113. /// <summary>
  114. /// WaitHandle to signal that last service request was accepted.
  115. /// </summary>
  116. private EventWaitHandle _serviceAccepted = new AutoResetEvent(initialState: false);
  117. /// <summary>
  118. /// WaitHandle to signal that exception was thrown by another thread.
  119. /// </summary>
  120. private EventWaitHandle _exceptionWaitHandle = new ManualResetEvent(initialState: false);
  121. /// <summary>
  122. /// WaitHandle to signal that key exchange was completed.
  123. /// </summary>
  124. private ManualResetEventSlim _keyExchangeCompletedWaitHandle = new ManualResetEventSlim(initialState: false);
  125. /// <summary>
  126. /// Exception that need to be thrown by waiting thread.
  127. /// </summary>
  128. private Exception _exception;
  129. /// <summary>
  130. /// Specifies whether connection is authenticated.
  131. /// </summary>
  132. private bool _isAuthenticated;
  133. /// <summary>
  134. /// Specifies whether user issued Disconnect command or not.
  135. /// </summary>
  136. private bool _isDisconnecting;
  137. /// <summary>
  138. /// Indicates whether it is the init kex.
  139. /// </summary>
  140. private bool _isInitialKex;
  141. /// <summary>
  142. /// Indicates whether server supports strict key exchange.
  143. /// <see href="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL"/> 1.10.
  144. /// </summary>
  145. private bool _isStrictKex;
  146. private IKeyExchange _keyExchange;
  147. private HashAlgorithm _serverMac;
  148. private HashAlgorithm _clientMac;
  149. private bool _serverEtm;
  150. private bool _clientEtm;
  151. private Cipher _serverCipher;
  152. private Cipher _clientCipher;
  153. private bool _serverAead;
  154. private bool _clientAead;
  155. private Compressor _serverDecompression;
  156. private Compressor _clientCompression;
  157. private SemaphoreSlim _sessionSemaphore;
  158. private bool _isDisconnectMessageSent;
  159. private int _nextChannelNumber;
  160. /// <summary>
  161. /// Holds connection socket.
  162. /// </summary>
  163. private Socket _socket;
  164. /// <summary>
  165. /// Gets the session semaphore that controls session channels.
  166. /// </summary>
  167. /// <value>
  168. /// The session semaphore.
  169. /// </value>
  170. public SemaphoreSlim SessionSemaphore
  171. {
  172. get
  173. {
  174. if (_sessionSemaphore is SemaphoreSlim sessionSemaphore)
  175. {
  176. return sessionSemaphore;
  177. }
  178. sessionSemaphore = new SemaphoreSlim(ConnectionInfo.MaxSessions);
  179. if (Interlocked.CompareExchange(ref _sessionSemaphore, sessionSemaphore, comparand: null) is not null)
  180. {
  181. // Another thread has set _sessionSemaphore. Dispose our one.
  182. Debug.Assert(_sessionSemaphore != sessionSemaphore);
  183. sessionSemaphore.Dispose();
  184. }
  185. return _sessionSemaphore;
  186. }
  187. }
  188. /// <summary>
  189. /// Gets the next channel number.
  190. /// </summary>
  191. /// <value>
  192. /// The next channel number.
  193. /// </value>
  194. private uint NextChannelNumber
  195. {
  196. get
  197. {
  198. return (uint)Interlocked.Increment(ref _nextChannelNumber);
  199. }
  200. }
  201. /// <summary>
  202. /// Gets a value indicating whether the session is connected.
  203. /// </summary>
  204. /// <value>
  205. /// <see langword="true"/> if the session is connected; otherwise, <see langword="false"/>.
  206. /// </value>
  207. /// <remarks>
  208. /// This methods returns <see langword="true"/> in all but the following cases:
  209. /// <list type="bullet">
  210. /// <item>
  211. /// <description>The <see cref="Session"/> is disposed.</description>
  212. /// </item>
  213. /// <item>
  214. /// <description>The <c>SSH_MSG_DISCONNECT</c> message - which is used to disconnect from the server - has been sent.</description>
  215. /// </item>
  216. /// <item>
  217. /// <description>The client has not been authenticated successfully.</description>
  218. /// </item>
  219. /// <item>
  220. /// <description>The listener thread - which is used to receive messages from the server - has stopped.</description>
  221. /// </item>
  222. /// <item>
  223. /// <description>The socket used to communicate with the server is no longer connected.</description>
  224. /// </item>
  225. /// </list>
  226. /// </remarks>
  227. public bool IsConnected
  228. {
  229. get
  230. {
  231. if (_disposed || _isDisconnectMessageSent || !_isAuthenticated)
  232. {
  233. return false;
  234. }
  235. if (_messageListenerCompleted is null || _messageListenerCompleted.WaitOne(0))
  236. {
  237. return false;
  238. }
  239. return IsSocketConnected();
  240. }
  241. }
  242. /// <summary>
  243. /// Gets the session id.
  244. /// </summary>
  245. /// <value>
  246. /// The session id, or <see langword="null"/> if the client has not been authenticated.
  247. /// </value>
  248. public byte[] SessionId { get; private set; }
  249. /// <summary>
  250. /// Gets the client init message.
  251. /// </summary>
  252. /// <value>The client init message.</value>
  253. public Message ClientInitMessage { get; private set; }
  254. /// <summary>
  255. /// Gets the server version string.
  256. /// </summary>
  257. /// <value>
  258. /// The server version.
  259. /// </value>
  260. public string ServerVersion { get; private set; }
  261. /// <summary>
  262. /// Gets the client version string.
  263. /// </summary>
  264. /// <value>
  265. /// The client version.
  266. /// </value>
  267. public string ClientVersion { get; private set; }
  268. /// <summary>
  269. /// Gets the connection info.
  270. /// </summary>
  271. /// <value>
  272. /// The connection info.
  273. /// </value>
  274. public ConnectionInfo ConnectionInfo { get; private set; }
  275. /// <summary>
  276. /// Occurs when an error occurred.
  277. /// </summary>
  278. public event EventHandler<ExceptionEventArgs> ErrorOccured;
  279. /// <summary>
  280. /// Occurs when session has been disconnected from the server.
  281. /// </summary>
  282. public event EventHandler<EventArgs> Disconnected;
  283. /// <summary>
  284. /// Occurs when server identification received.
  285. /// </summary>
  286. public event EventHandler<SshIdentificationEventArgs> ServerIdentificationReceived;
  287. /// <summary>
  288. /// Occurs when host key received.
  289. /// </summary>
  290. public event EventHandler<HostKeyEventArgs> HostKeyReceived;
  291. /// <summary>
  292. /// Occurs when <see cref="BannerMessage"/> message is received from the server.
  293. /// </summary>
  294. public event EventHandler<MessageEventArgs<BannerMessage>> UserAuthenticationBannerReceived;
  295. /// <summary>
  296. /// Occurs when <see cref="InformationRequestMessage"/> message is received from the server.
  297. /// </summary>
  298. internal event EventHandler<MessageEventArgs<InformationRequestMessage>> UserAuthenticationInformationRequestReceived;
  299. /// <summary>
  300. /// Occurs when <see cref="PasswordChangeRequiredMessage"/> message is received from the server.
  301. /// </summary>
  302. internal event EventHandler<MessageEventArgs<PasswordChangeRequiredMessage>> UserAuthenticationPasswordChangeRequiredReceived;
  303. /// <summary>
  304. /// Occurs when <see cref="PublicKeyMessage"/> message is received from the server.
  305. /// </summary>
  306. internal event EventHandler<MessageEventArgs<PublicKeyMessage>> UserAuthenticationPublicKeyReceived;
  307. /// <summary>
  308. /// Occurs when <see cref="KeyExchangeDhGroupExchangeGroup"/> message is received from the server.
  309. /// </summary>
  310. internal event EventHandler<MessageEventArgs<KeyExchangeDhGroupExchangeGroup>> KeyExchangeDhGroupExchangeGroupReceived;
  311. /// <summary>
  312. /// Occurs when <see cref="KeyExchangeDhGroupExchangeReply"/> message is received from the server.
  313. /// </summary>
  314. internal event EventHandler<MessageEventArgs<KeyExchangeDhGroupExchangeReply>> KeyExchangeDhGroupExchangeReplyReceived;
  315. /// <summary>
  316. /// Occurs when <see cref="DisconnectMessage"/> message received
  317. /// </summary>
  318. internal event EventHandler<MessageEventArgs<DisconnectMessage>> DisconnectReceived;
  319. /// <summary>
  320. /// Occurs when <see cref="IgnoreMessage"/> message received
  321. /// </summary>
  322. internal event EventHandler<MessageEventArgs<IgnoreMessage>> IgnoreReceived;
  323. /// <summary>
  324. /// Occurs when <see cref="UnimplementedMessage"/> message received
  325. /// </summary>
  326. internal event EventHandler<MessageEventArgs<UnimplementedMessage>> UnimplementedReceived;
  327. /// <summary>
  328. /// Occurs when <see cref="DebugMessage"/> message received
  329. /// </summary>
  330. internal event EventHandler<MessageEventArgs<DebugMessage>> DebugReceived;
  331. /// <summary>
  332. /// Occurs when <see cref="ServiceRequestMessage"/> message received
  333. /// </summary>
  334. internal event EventHandler<MessageEventArgs<ServiceRequestMessage>> ServiceRequestReceived;
  335. /// <summary>
  336. /// Occurs when <see cref="ServiceAcceptMessage"/> message received
  337. /// </summary>
  338. internal event EventHandler<MessageEventArgs<ServiceAcceptMessage>> ServiceAcceptReceived;
  339. /// <summary>
  340. /// Occurs when <see cref="KeyExchangeInitMessage"/> message received
  341. /// </summary>
  342. internal event EventHandler<MessageEventArgs<KeyExchangeInitMessage>> KeyExchangeInitReceived;
  343. /// <summary>
  344. /// Occurs when a <see cref="KeyExchangeDhReplyMessage"/> message is received from the SSH server.
  345. /// </summary>
  346. internal event EventHandler<MessageEventArgs<KeyExchangeDhReplyMessage>> KeyExchangeDhReplyMessageReceived;
  347. /// <summary>
  348. /// Occurs when a <see cref="KeyExchangeEcdhReplyMessage"/> message is received from the SSH server.
  349. /// </summary>
  350. internal event EventHandler<MessageEventArgs<KeyExchangeEcdhReplyMessage>> KeyExchangeEcdhReplyMessageReceived;
  351. /// <summary>
  352. /// Occurs when <see cref="NewKeysMessage"/> message received
  353. /// </summary>
  354. internal event EventHandler<MessageEventArgs<NewKeysMessage>> NewKeysReceived;
  355. /// <summary>
  356. /// Occurs when <see cref="RequestMessage"/> message received
  357. /// </summary>
  358. internal event EventHandler<MessageEventArgs<RequestMessage>> UserAuthenticationRequestReceived;
  359. /// <summary>
  360. /// Occurs when <see cref="FailureMessage"/> message received
  361. /// </summary>
  362. internal event EventHandler<MessageEventArgs<FailureMessage>> UserAuthenticationFailureReceived;
  363. /// <summary>
  364. /// Occurs when <see cref="SuccessMessage"/> message received
  365. /// </summary>
  366. internal event EventHandler<MessageEventArgs<SuccessMessage>> UserAuthenticationSuccessReceived;
  367. /// <summary>
  368. /// Occurs when <see cref="GlobalRequestMessage"/> message received
  369. /// </summary>
  370. internal event EventHandler<MessageEventArgs<GlobalRequestMessage>> GlobalRequestReceived;
  371. /// <summary>
  372. /// Occurs when <see cref="RequestSuccessMessage"/> message received
  373. /// </summary>
  374. public event EventHandler<MessageEventArgs<RequestSuccessMessage>> RequestSuccessReceived;
  375. /// <summary>
  376. /// Occurs when <see cref="RequestFailureMessage"/> message received
  377. /// </summary>
  378. public event EventHandler<MessageEventArgs<RequestFailureMessage>> RequestFailureReceived;
  379. /// <summary>
  380. /// Occurs when <see cref="ChannelOpenMessage"/> message received
  381. /// </summary>
  382. public event EventHandler<MessageEventArgs<ChannelOpenMessage>> ChannelOpenReceived;
  383. /// <summary>
  384. /// Occurs when <see cref="ChannelOpenConfirmationMessage"/> message received
  385. /// </summary>
  386. public event EventHandler<MessageEventArgs<ChannelOpenConfirmationMessage>> ChannelOpenConfirmationReceived;
  387. /// <summary>
  388. /// Occurs when <see cref="ChannelOpenFailureMessage"/> message received
  389. /// </summary>
  390. public event EventHandler<MessageEventArgs<ChannelOpenFailureMessage>> ChannelOpenFailureReceived;
  391. /// <summary>
  392. /// Occurs when <see cref="ChannelWindowAdjustMessage"/> message received
  393. /// </summary>
  394. public event EventHandler<MessageEventArgs<ChannelWindowAdjustMessage>> ChannelWindowAdjustReceived;
  395. /// <summary>
  396. /// Occurs when <see cref="ChannelDataMessage"/> message received
  397. /// </summary>
  398. public event EventHandler<MessageEventArgs<ChannelDataMessage>> ChannelDataReceived;
  399. /// <summary>
  400. /// Occurs when <see cref="ChannelExtendedDataMessage"/> message received
  401. /// </summary>
  402. public event EventHandler<MessageEventArgs<ChannelExtendedDataMessage>> ChannelExtendedDataReceived;
  403. /// <summary>
  404. /// Occurs when <see cref="ChannelEofMessage"/> message received
  405. /// </summary>
  406. public event EventHandler<MessageEventArgs<ChannelEofMessage>> ChannelEofReceived;
  407. /// <summary>
  408. /// Occurs when <see cref="ChannelCloseMessage"/> message received
  409. /// </summary>
  410. public event EventHandler<MessageEventArgs<ChannelCloseMessage>> ChannelCloseReceived;
  411. /// <summary>
  412. /// Occurs when <see cref="ChannelRequestMessage"/> message received
  413. /// </summary>
  414. public event EventHandler<MessageEventArgs<ChannelRequestMessage>> ChannelRequestReceived;
  415. /// <summary>
  416. /// Occurs when <see cref="ChannelSuccessMessage"/> message received
  417. /// </summary>
  418. public event EventHandler<MessageEventArgs<ChannelSuccessMessage>> ChannelSuccessReceived;
  419. /// <summary>
  420. /// Occurs when <see cref="ChannelFailureMessage"/> message received
  421. /// </summary>
  422. public event EventHandler<MessageEventArgs<ChannelFailureMessage>> ChannelFailureReceived;
  423. /// <summary>
  424. /// Initializes a new instance of the <see cref="Session"/> class.
  425. /// </summary>
  426. /// <param name="connectionInfo">The connection info.</param>
  427. /// <param name="serviceFactory">The factory to use for creating new services.</param>
  428. /// <param name="socketFactory">A factory to create <see cref="Socket"/> instances.</param>
  429. /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is <see langword="null"/>.</exception>
  430. /// <exception cref="ArgumentNullException"><paramref name="serviceFactory"/> is <see langword="null"/>.</exception>
  431. /// <exception cref="ArgumentNullException"><paramref name="socketFactory"/> is <see langword="null"/>.</exception>
  432. internal Session(ConnectionInfo connectionInfo, IServiceFactory serviceFactory, ISocketFactory socketFactory)
  433. {
  434. if (connectionInfo is null)
  435. {
  436. throw new ArgumentNullException(nameof(connectionInfo));
  437. }
  438. if (serviceFactory is null)
  439. {
  440. throw new ArgumentNullException(nameof(serviceFactory));
  441. }
  442. if (socketFactory is null)
  443. {
  444. throw new ArgumentNullException(nameof(socketFactory));
  445. }
  446. ClientVersion = "SSH-2.0-Renci.SshNet.SshClient.0.0.1";
  447. ConnectionInfo = connectionInfo;
  448. _serviceFactory = serviceFactory;
  449. _socketFactory = socketFactory;
  450. _messageListenerCompleted = new ManualResetEvent(initialState: true);
  451. }
  452. /// <summary>
  453. /// Connects to the server.
  454. /// </summary>
  455. /// <exception cref="SocketException">Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname.</exception>
  456. /// <exception cref="SshConnectionException">SSH session could not be established.</exception>
  457. /// <exception cref="SshAuthenticationException">Authentication of SSH session failed.</exception>
  458. /// <exception cref="ProxyException">Failed to establish proxy connection.</exception>
  459. public void Connect()
  460. {
  461. if (IsConnected)
  462. {
  463. return;
  464. }
  465. _connectLock.Wait();
  466. try
  467. {
  468. if (IsConnected)
  469. {
  470. return;
  471. }
  472. // Reset connection specific information
  473. Reset();
  474. // Build list of available messages while connecting
  475. _sshMessageFactory = new SshMessageFactory();
  476. _socket = _serviceFactory.CreateConnector(ConnectionInfo, _socketFactory)
  477. .Connect(ConnectionInfo);
  478. var serverIdentification = _serviceFactory.CreateProtocolVersionExchange()
  479. .Start(ClientVersion, _socket, ConnectionInfo.Timeout);
  480. // Set connection versions
  481. ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();
  482. ConnectionInfo.ClientVersion = ClientVersion;
  483. DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification));
  484. if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.99")))
  485. {
  486. throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not supported.", serverIdentification.ProtocolVersion),
  487. DisconnectReason.ProtocolVersionNotSupported);
  488. }
  489. ServerIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification));
  490. // Register Transport response messages
  491. RegisterMessage("SSH_MSG_DISCONNECT");
  492. RegisterMessage("SSH_MSG_IGNORE");
  493. RegisterMessage("SSH_MSG_UNIMPLEMENTED");
  494. RegisterMessage("SSH_MSG_DEBUG");
  495. RegisterMessage("SSH_MSG_SERVICE_ACCEPT");
  496. RegisterMessage("SSH_MSG_KEXINIT");
  497. RegisterMessage("SSH_MSG_NEWKEYS");
  498. // Some server implementations might sent this message first, prior to establishing encryption algorithm
  499. RegisterMessage("SSH_MSG_USERAUTH_BANNER");
  500. // Send our key exchange init.
  501. // We need to do this before starting the message listener to avoid the case where we receive the server
  502. // key exchange init and we continue the key exchange before having sent our own init.
  503. _isInitialKex = true;
  504. ClientInitMessage = BuildClientInitMessage(includeStrictKexPseudoAlgorithm: true);
  505. SendMessage(ClientInitMessage);
  506. // Mark the message listener threads as started
  507. _ = _messageListenerCompleted.Reset();
  508. // Start incoming request listener
  509. // ToDo: Make message pump async, to not consume a thread for every session
  510. _ = ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
  511. // Wait for key exchange to be completed
  512. WaitOnHandle(_keyExchangeCompletedWaitHandle.WaitHandle);
  513. // If sessionId is not set then its not connected
  514. if (SessionId is null)
  515. {
  516. Disconnect();
  517. return;
  518. }
  519. // Request user authorization service
  520. SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication));
  521. // Wait for service to be accepted
  522. WaitOnHandle(_serviceAccepted);
  523. if (string.IsNullOrEmpty(ConnectionInfo.Username))
  524. {
  525. throw new SshException("Username is not specified.");
  526. }
  527. // Some servers send a global request immediately after successful authentication
  528. // Avoid race condition by already enabling SSH_MSG_GLOBAL_REQUEST before authentication
  529. RegisterMessage("SSH_MSG_GLOBAL_REQUEST");
  530. ConnectionInfo.Authenticate(this, _serviceFactory);
  531. _isAuthenticated = true;
  532. // Register Connection messages
  533. RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
  534. RegisterMessage("SSH_MSG_REQUEST_FAILURE");
  535. RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
  536. RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
  537. RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
  538. RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
  539. RegisterMessage("SSH_MSG_CHANNEL_REQUEST");
  540. RegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
  541. RegisterMessage("SSH_MSG_CHANNEL_FAILURE");
  542. RegisterMessage("SSH_MSG_CHANNEL_DATA");
  543. RegisterMessage("SSH_MSG_CHANNEL_EOF");
  544. RegisterMessage("SSH_MSG_CHANNEL_CLOSE");
  545. }
  546. finally
  547. {
  548. _ = _connectLock.Release();
  549. }
  550. }
  551. /// <summary>
  552. /// Asynchronously connects to the server.
  553. /// </summary>
  554. /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
  555. /// <returns>A <see cref="Task"/> that represents the asynchronous connect operation.</returns>
  556. /// <exception cref="SocketException">Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname.</exception>
  557. /// <exception cref="SshConnectionException">SSH session could not be established.</exception>
  558. /// <exception cref="SshAuthenticationException">Authentication of SSH session failed.</exception>
  559. /// <exception cref="ProxyException">Failed to establish proxy connection.</exception>
  560. public async Task ConnectAsync(CancellationToken cancellationToken)
  561. {
  562. // If connected don't connect again
  563. if (IsConnected)
  564. {
  565. return;
  566. }
  567. await _connectLock.WaitAsync(cancellationToken).ConfigureAwait(false);
  568. try
  569. {
  570. if (IsConnected)
  571. {
  572. return;
  573. }
  574. // Reset connection specific information
  575. Reset();
  576. // Build list of available messages while connecting
  577. _sshMessageFactory = new SshMessageFactory();
  578. _socket = await _serviceFactory.CreateConnector(ConnectionInfo, _socketFactory)
  579. .ConnectAsync(ConnectionInfo, cancellationToken).ConfigureAwait(false);
  580. var serverIdentification = await _serviceFactory.CreateProtocolVersionExchange()
  581. .StartAsync(ClientVersion, _socket, cancellationToken).ConfigureAwait(false);
  582. // Set connection versions
  583. ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();
  584. ConnectionInfo.ClientVersion = ClientVersion;
  585. DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification));
  586. if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.99")))
  587. {
  588. throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not supported.", serverIdentification.ProtocolVersion),
  589. DisconnectReason.ProtocolVersionNotSupported);
  590. }
  591. ServerIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification));
  592. // Register Transport response messages
  593. RegisterMessage("SSH_MSG_DISCONNECT");
  594. RegisterMessage("SSH_MSG_IGNORE");
  595. RegisterMessage("SSH_MSG_UNIMPLEMENTED");
  596. RegisterMessage("SSH_MSG_DEBUG");
  597. RegisterMessage("SSH_MSG_SERVICE_ACCEPT");
  598. RegisterMessage("SSH_MSG_KEXINIT");
  599. RegisterMessage("SSH_MSG_NEWKEYS");
  600. // Some server implementations might sent this message first, prior to establishing encryption algorithm
  601. RegisterMessage("SSH_MSG_USERAUTH_BANNER");
  602. // Send our key exchange init.
  603. // We need to do this before starting the message listener to avoid the case where we receive the server
  604. // key exchange init and we continue the key exchange before having sent our own init.
  605. _isInitialKex = true;
  606. ClientInitMessage = BuildClientInitMessage(includeStrictKexPseudoAlgorithm: true);
  607. SendMessage(ClientInitMessage);
  608. // Mark the message listener threads as started
  609. _ = _messageListenerCompleted.Reset();
  610. // Start incoming request listener
  611. // ToDo: Make message pump async, to not consume a thread for every session
  612. _ = ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
  613. // Wait for key exchange to be completed
  614. WaitOnHandle(_keyExchangeCompletedWaitHandle.WaitHandle);
  615. // If sessionId is not set then its not connected
  616. if (SessionId is null)
  617. {
  618. Disconnect();
  619. return;
  620. }
  621. // Request user authorization service
  622. SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication));
  623. // Wait for service to be accepted
  624. WaitOnHandle(_serviceAccepted);
  625. if (string.IsNullOrEmpty(ConnectionInfo.Username))
  626. {
  627. throw new SshException("Username is not specified.");
  628. }
  629. // Some servers send a global request immediately after successful authentication
  630. // Avoid race condition by already enabling SSH_MSG_GLOBAL_REQUEST before authentication
  631. RegisterMessage("SSH_MSG_GLOBAL_REQUEST");
  632. ConnectionInfo.Authenticate(this, _serviceFactory);
  633. _isAuthenticated = true;
  634. // Register Connection messages
  635. RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
  636. RegisterMessage("SSH_MSG_REQUEST_FAILURE");
  637. RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
  638. RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
  639. RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
  640. RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
  641. RegisterMessage("SSH_MSG_CHANNEL_REQUEST");
  642. RegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
  643. RegisterMessage("SSH_MSG_CHANNEL_FAILURE");
  644. RegisterMessage("SSH_MSG_CHANNEL_DATA");
  645. RegisterMessage("SSH_MSG_CHANNEL_EOF");
  646. RegisterMessage("SSH_MSG_CHANNEL_CLOSE");
  647. }
  648. finally
  649. {
  650. _ = _connectLock.Release();
  651. }
  652. }
  653. /// <summary>
  654. /// Disconnects from the server.
  655. /// </summary>
  656. /// <remarks>
  657. /// This sends a <b>SSH_MSG_DISCONNECT</b> message to the server, waits for the
  658. /// server to close the socket on its end and subsequently closes the client socket.
  659. /// </remarks>
  660. public void Disconnect()
  661. {
  662. DiagnosticAbstraction.Log(string.Format("[{0}] Disconnecting session.", ToHex(SessionId)));
  663. // send SSH_MSG_DISCONNECT message, clear socket read buffer and dispose it
  664. Disconnect(DisconnectReason.ByApplication, "Connection terminated by the client.");
  665. // at this point, we are sure that the listener thread will stop as we've
  666. // disconnected the socket, so lets wait until the message listener thread
  667. // has completed
  668. if (_messageListenerCompleted != null)
  669. {
  670. _ = _messageListenerCompleted.WaitOne();
  671. }
  672. }
  673. private void Disconnect(DisconnectReason reason, string message)
  674. {
  675. // transition to disconnecting state to avoid throwing exceptions while cleaning up, and to
  676. // ensure any exceptions that are raised do not overwrite the exception that is set
  677. _isDisconnecting = true;
  678. // send disconnect message to the server if the connection is still open
  679. // and the disconnect message has not yet been sent
  680. //
  681. // note that this should also cause the listener loop to be interrupted as
  682. // the server should respond by closing the socket
  683. if (IsConnected)
  684. {
  685. TrySendDisconnect(reason, message);
  686. }
  687. // disconnect socket, and dispose it
  688. SocketDisconnectAndDispose();
  689. }
  690. /// <summary>
  691. /// Waits for the specified handle or the exception handle for the receive thread
  692. /// to signal within the connection timeout.
  693. /// </summary>
  694. /// <param name="waitHandle">The wait handle.</param>
  695. /// <exception cref="SshConnectionException">A received package was invalid or failed the message integrity check.</exception>
  696. /// <exception cref="SshOperationTimeoutException">None of the handles are signaled in time and the session is not disconnecting.</exception>
  697. /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exception>
  698. /// <remarks>
  699. /// When neither handles are signaled in time and the session is not closing, then the
  700. /// session is disconnected.
  701. /// </remarks>
  702. void ISession.WaitOnHandle(WaitHandle waitHandle)
  703. {
  704. WaitOnHandle(waitHandle, ConnectionInfo.Timeout);
  705. }
  706. /// <summary>
  707. /// Waits for the specified handle or the exception handle for the receive thread
  708. /// to signal within the specified timeout.
  709. /// </summary>
  710. /// <param name="waitHandle">The wait handle.</param>
  711. /// <param name="timeout">The time to wait for any of the handles to become signaled.</param>
  712. /// <exception cref="SshConnectionException">A received package was invalid or failed the message integrity check.</exception>
  713. /// <exception cref="SshOperationTimeoutException">None of the handles are signaled in time and the session is not disconnecting.</exception>
  714. /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exception>
  715. /// <remarks>
  716. /// When neither handles are signaled in time and the session is not closing, then the
  717. /// session is disconnected.
  718. /// </remarks>
  719. void ISession.WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout)
  720. {
  721. WaitOnHandle(waitHandle, timeout);
  722. }
  723. /// <summary>
  724. /// Waits for the specified <seec ref="WaitHandle"/> to receive a signal, using a <see cref="TimeSpan"/>
  725. /// to specify the time interval.
  726. /// </summary>
  727. /// <param name="waitHandle">The <see cref="WaitHandle"/> that should be signaled.</param>
  728. /// <param name="timeout">A <see cref="TimeSpan"/> that represents the number of milliseconds to wait, or a <see cref="TimeSpan"/> that represents <c>-1</c> milliseconds to wait indefinitely.</param>
  729. /// <returns>
  730. /// A <see cref="WaitResult"/>.
  731. /// </returns>
  732. WaitResult ISession.TryWait(WaitHandle waitHandle, TimeSpan timeout)
  733. {
  734. return TryWait(waitHandle, timeout, out _);
  735. }
  736. /// <summary>
  737. /// Waits for the specified <seec ref="WaitHandle"/> to receive a signal, using a <see cref="TimeSpan"/>
  738. /// to specify the time interval.
  739. /// </summary>
  740. /// <param name="waitHandle">The <see cref="WaitHandle"/> that should be signaled.</param>
  741. /// <param name="timeout">A <see cref="TimeSpan"/> that represents the number of milliseconds to wait, or a <see cref="TimeSpan"/> that represents <c>-1</c> milliseconds to wait indefinitely.</param>
  742. /// <param name="exception">When this method returns <see cref="WaitResult.Failed"/>, contains the <see cref="Exception"/>.</param>
  743. /// <returns>
  744. /// A <see cref="WaitResult"/>.
  745. /// </returns>
  746. WaitResult ISession.TryWait(WaitHandle waitHandle, TimeSpan timeout, out Exception exception)
  747. {
  748. return TryWait(waitHandle, timeout, out exception);
  749. }
  750. /// <summary>
  751. /// Waits for the specified <seec ref="WaitHandle"/> to receive a signal, using a <see cref="TimeSpan"/>
  752. /// to specify the time interval.
  753. /// </summary>
  754. /// <param name="waitHandle">The <see cref="WaitHandle"/> that should be signaled.</param>
  755. /// <param name="timeout">A <see cref="TimeSpan"/> that represents the number of milliseconds to wait, or a <see cref="TimeSpan"/> that represents <c>-1</c> milliseconds to wait indefinitely.</param>
  756. /// <param name="exception">When this method returns <see cref="WaitResult.Failed"/>, contains the <see cref="Exception"/>.</param>
  757. /// <returns>
  758. /// A <see cref="WaitResult"/>.
  759. /// </returns>
  760. private WaitResult TryWait(WaitHandle waitHandle, TimeSpan timeout, out Exception exception)
  761. {
  762. if (waitHandle is null)
  763. {
  764. throw new ArgumentNullException(nameof(waitHandle));
  765. }
  766. var waitHandles = new[]
  767. {
  768. _exceptionWaitHandle,
  769. _messageListenerCompleted,
  770. waitHandle
  771. };
  772. switch (WaitHandle.WaitAny(waitHandles, timeout))
  773. {
  774. case 0:
  775. if (_exception is SshConnectionException)
  776. {
  777. exception = null;
  778. return WaitResult.Disconnected;
  779. }
  780. exception = _exception;
  781. return WaitResult.Failed;
  782. case 1:
  783. exception = null;
  784. return WaitResult.Disconnected;
  785. case 2:
  786. exception = null;
  787. return WaitResult.Success;
  788. case WaitHandle.WaitTimeout:
  789. exception = null;
  790. return WaitResult.TimedOut;
  791. default:
  792. throw new InvalidOperationException("Unexpected result.");
  793. }
  794. }
  795. /// <summary>
  796. /// Waits for the specified handle or the exception handle for the receive thread
  797. /// to signal within the connection timeout.
  798. /// </summary>
  799. /// <param name="waitHandle">The wait handle.</param>
  800. /// <exception cref="SshConnectionException">A received package was invalid or failed the message integrity check.</exception>
  801. /// <exception cref="SshOperationTimeoutException">None of the handles are signaled in time and the session is not disconnecting.</exception>
  802. /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exception>
  803. /// <remarks>
  804. /// When neither handles are signaled in time and the session is not closing, then the
  805. /// session is disconnected.
  806. /// </remarks>
  807. internal void WaitOnHandle(WaitHandle waitHandle)
  808. {
  809. WaitOnHandle(waitHandle, ConnectionInfo.Timeout);
  810. }
  811. /// <summary>
  812. /// Waits for the specified handle or the exception handle for the receive thread
  813. /// to signal within the specified timeout.
  814. /// </summary>
  815. /// <param name="waitHandle">The wait handle.</param>
  816. /// <param name="timeout">The time to wait for any of the handles to become signaled.</param>
  817. /// <exception cref="SshConnectionException">A received package was invalid or failed the message integrity check.</exception>
  818. /// <exception cref="SshOperationTimeoutException">None of the handles are signaled in time and the session is not disconnecting.</exception>
  819. /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exception>
  820. internal void WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout)
  821. {
  822. if (waitHandle is null)
  823. {
  824. throw new ArgumentNullException(nameof(waitHandle));
  825. }
  826. var waitHandles = new[]
  827. {
  828. _exceptionWaitHandle,
  829. _messageListenerCompleted,
  830. waitHandle
  831. };
  832. var signaledElement = WaitHandle.WaitAny(waitHandles, timeout);
  833. switch (signaledElement)
  834. {
  835. case 0:
  836. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(_exception).Throw();
  837. break;
  838. case 1:
  839. throw new SshConnectionException("Client not connected.");
  840. case 2:
  841. // Specified waithandle was signaled
  842. break;
  843. case WaitHandle.WaitTimeout:
  844. // when the session is disconnecting, a timeout is likely when no
  845. // network connectivity is available; depending on the configured
  846. // timeout either the WaitAny times out first or a SocketException
  847. // detailing a timeout thrown hereby completing the listener thread
  848. // (which makes us end up in case 1). Either way, we do not want to
  849. // report an exception to the client when we're disconnecting anyway
  850. if (!_isDisconnecting)
  851. {
  852. throw new SshOperationTimeoutException("Session operation has timed out");
  853. }
  854. break;
  855. default:
  856. throw new SshException($"Unexpected element '{signaledElement.ToString(CultureInfo.InvariantCulture)}' signaled.");
  857. }
  858. }
  859. /// <summary>
  860. /// Sends a message to the server.
  861. /// </summary>
  862. /// <param name="message">The message to send.</param>
  863. /// <exception cref="SshConnectionException">The client is not connected.</exception>
  864. /// <exception cref="SshOperationTimeoutException">The operation timed out.</exception>
  865. /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
  866. internal void SendMessage(Message message)
  867. {
  868. if (!_socket.CanWrite())
  869. {
  870. throw new SshConnectionException("Client not connected.");
  871. }
  872. if (!_keyExchangeCompletedWaitHandle.IsSet && message is not IKeyExchangedAllowed)
  873. {
  874. // Wait for key exchange to be completed
  875. WaitOnHandle(_keyExchangeCompletedWaitHandle.WaitHandle);
  876. }
  877. DiagnosticAbstraction.Log(string.Format("[{0}] Sending message '{1}' to server: '{2}'.", ToHex(SessionId), message.GetType().Name, message));
  878. var paddingMultiplier = _clientCipher is null ? (byte)8 : Math.Max((byte)8, _clientCipher.MinimumSize);
  879. var packetData = message.GetPacket(paddingMultiplier, _clientCompression, _clientEtm || _clientAead);
  880. // take a write lock to ensure the outbound packet sequence number is incremented
  881. // atomically, and only after the packet has actually been sent
  882. lock (_socketWriteLock)
  883. {
  884. byte[] hash = null;
  885. var packetDataOffset = 4; // first four bytes are reserved for outbound packet sequence
  886. // write outbound packet sequence to start of packet data
  887. BinaryPrimitives.WriteUInt32BigEndian(packetData, _outboundPacketSequence);
  888. if (_clientMac != null && !_clientEtm)
  889. {
  890. // calculate packet hash
  891. hash = _clientMac.ComputeHash(packetData);
  892. }
  893. // Encrypt packet data
  894. if (_clientCipher != null)
  895. {
  896. _clientCipher.SetSequenceNumber(_outboundPacketSequence);
  897. if (_clientEtm)
  898. {
  899. // The length of the "packet length" field in bytes
  900. const int packetLengthFieldLength = 4;
  901. var encryptedData = _clientCipher.Encrypt(packetData, packetDataOffset + packetLengthFieldLength, packetData.Length - packetDataOffset - packetLengthFieldLength);
  902. Array.Resize(ref packetData, packetDataOffset + packetLengthFieldLength + encryptedData.Length);
  903. // write encrypted data
  904. Buffer.BlockCopy(encryptedData, 0, packetData, packetDataOffset + packetLengthFieldLength, encryptedData.Length);
  905. // calculate packet hash
  906. hash = _clientMac.ComputeHash(packetData);
  907. }
  908. else
  909. {
  910. packetData = _clientCipher.Encrypt(packetData, packetDataOffset, packetData.Length - packetDataOffset);
  911. packetDataOffset = 0;
  912. }
  913. }
  914. if (packetData.Length > MaximumSshPacketSize)
  915. {
  916. throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Packet is too big. Maximum packet size is {0} bytes.", MaximumSshPacketSize));
  917. }
  918. var packetLength = packetData.Length - packetDataOffset;
  919. if (hash is null)
  920. {
  921. SendPacket(packetData, packetDataOffset, packetLength);
  922. }
  923. else
  924. {
  925. var data = new byte[packetLength + hash.Length];
  926. Buffer.BlockCopy(packetData, packetDataOffset, data, 0, packetLength);
  927. Buffer.BlockCopy(hash, 0, data, packetLength, hash.Length);
  928. SendPacket(data, 0, data.Length);
  929. }
  930. if (_isStrictKex && message is NewKeysMessage)
  931. {
  932. _outboundPacketSequence = 0;
  933. }
  934. else
  935. {
  936. // increment the packet sequence number only after we're sure the packet has
  937. // been sent; even though it's only used for the MAC, it needs to be incremented
  938. // for each package sent.
  939. //
  940. // the server will use it to verify the data integrity, and as such the order in
  941. // which messages are sent must follow the outbound packet sequence number
  942. _outboundPacketSequence++;
  943. }
  944. }
  945. }
  946. /// <summary>
  947. /// Sends an SSH packet to the server.
  948. /// </summary>
  949. /// <param name="packet">A byte array containing the packet to send.</param>
  950. /// <param name="offset">The offset of the packet.</param>
  951. /// <param name="length">The length of the packet.</param>
  952. /// <exception cref="SshConnectionException">Client is not connected to the server.</exception>
  953. /// <remarks>
  954. /// <para>
  955. /// The send is performed in a dispose lock to avoid <see cref="NullReferenceException"/>
  956. /// and/or <see cref="ObjectDisposedException"/> when sending the packet.
  957. /// </para>
  958. /// <para>
  959. /// This method is only to be used when the connection is established, as the locking
  960. /// overhead is not required while establishing the connection.
  961. /// </para>
  962. /// </remarks>
  963. private void SendPacket(byte[] packet, int offset, int length)
  964. {
  965. _socketDisposeLock.Wait();
  966. try
  967. {
  968. if (!_socket.IsConnected())
  969. {
  970. throw new SshConnectionException("Client not connected.");
  971. }
  972. SocketAbstraction.Send(_socket, packet, offset, length);
  973. }
  974. finally
  975. {
  976. _ = _socketDisposeLock.Release();
  977. }
  978. }
  979. /// <summary>
  980. /// Sends a message to the server.
  981. /// </summary>
  982. /// <param name="message">The message to send.</param>
  983. /// <returns>
  984. /// <see langword="true"/> if the message was sent to the server; otherwise, <see langword="false"/>.
  985. /// </returns>
  986. /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
  987. /// <remarks>
  988. /// This methods returns <see langword="false"/> when the attempt to send the message results in a
  989. /// <see cref="SocketException"/> or a <see cref="SshException"/>.
  990. /// </remarks>
  991. private bool TrySendMessage(Message message)
  992. {
  993. try
  994. {
  995. SendMessage(message);
  996. return true;
  997. }
  998. catch (SshException ex)
  999. {
  1000. DiagnosticAbstraction.Log(string.Format("Failure sending message '{0}' to server: '{1}' => {2}", message.GetType().Name, message, ex));
  1001. return false;
  1002. }
  1003. catch (SocketException ex)
  1004. {
  1005. DiagnosticAbstraction.Log(string.Format("Failure sending message '{0}' to server: '{1}' => {2}", message.GetType().Name, message, ex));
  1006. return false;
  1007. }
  1008. }
  1009. /// <summary>
  1010. /// Receives the message from the server.
  1011. /// </summary>
  1012. /// <returns>
  1013. /// The incoming SSH message, or <see langword="null"/> if the connection with the SSH server was closed.
  1014. /// </returns>
  1015. /// <remarks>
  1016. /// We need no locking here since all messages are read by a single thread.
  1017. /// </remarks>
  1018. private Message ReceiveMessage(Socket socket)
  1019. {
  1020. // the length of the packet sequence field in bytes
  1021. const int inboundPacketSequenceLength = 4;
  1022. // The length of the "packet length" field in bytes
  1023. const int packetLengthFieldLength = 4;
  1024. // The length of the "padding length" field in bytes
  1025. const int paddingLengthFieldLength = 1;
  1026. int blockSize;
  1027. // Determine the size of the first block which is 8 or cipher block size (whichever is larger) bytes, or 4 if "packet length" field is handled separately.
  1028. if (_serverEtm || _serverAead)
  1029. {
  1030. blockSize = (byte)4;
  1031. }
  1032. else if (_serverCipher != null)
  1033. {
  1034. blockSize = Math.Max((byte)8, _serverCipher.MinimumSize);
  1035. }
  1036. else
  1037. {
  1038. blockSize = (byte)8;
  1039. }
  1040. var serverMacLength = 0;
  1041. if (_serverAead)
  1042. {
  1043. serverMacLength = _serverCipher.TagSize;
  1044. }
  1045. else if (_serverMac != null)
  1046. {
  1047. serverMacLength = _serverMac.HashSize / 8;
  1048. }
  1049. byte[] data;
  1050. uint packetLength;
  1051. // avoid reading from socket while IsSocketConnected is attempting to determine whether the
  1052. // socket is still connected by invoking Socket.Poll(...) and subsequently verifying value of
  1053. // Socket.Available
  1054. lock (_socketReadLock)
  1055. {
  1056. // Read first block - which starts with the packet length
  1057. var firstBlock = new byte[blockSize];
  1058. if (TrySocketRead(socket, firstBlock, 0, blockSize) == 0)
  1059. {
  1060. // connection with SSH server was closed
  1061. return null;
  1062. }
  1063. var plainFirstBlock = firstBlock;
  1064. // First block is not encrypted in AES GCM mode.
  1065. if (_serverCipher is not null
  1066. #if NET6_0_OR_GREATER
  1067. and not Security.Cryptography.Ciphers.AesGcmCipher
  1068. #endif
  1069. )
  1070. {
  1071. _serverCipher.SetSequenceNumber(_inboundPacketSequence);
  1072. // First block is not encrypted in ETM mode.
  1073. if (_serverMac == null || !_serverEtm)
  1074. {
  1075. plainFirstBlock = _serverCipher.Decrypt(firstBlock);
  1076. }
  1077. }
  1078. packetLength = BinaryPrimitives.ReadUInt32BigEndian(plainFirstBlock);
  1079. // Test packet minimum and maximum boundaries
  1080. if (packetLength < Math.Max((byte)8, blockSize) - 4 || packetLength > MaximumSshPacketSize - 4)
  1081. {
  1082. throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length: {0}.", packetLength),
  1083. DisconnectReason.ProtocolError);
  1084. }
  1085. // Determine the number of bytes left to read; We've already read "blockSize" bytes, but the
  1086. // "packet length" field itself - which is 4 bytes - is not included in the length of the packet
  1087. var bytesToRead = (int)(packetLength - (blockSize - packetLengthFieldLength)) + serverMacLength;
  1088. // Construct buffer for holding the payload and the inbound packet sequence as we need both in order
  1089. // to generate the hash.
  1090. //
  1091. // The total length of the "data" buffer is an addition of:
  1092. // - inboundPacketSequenceLength (4 bytes)
  1093. // - packetLength
  1094. // - serverMacLength
  1095. //
  1096. // We include the inbound packet sequence to allow us to have the the full SSH packet in a single
  1097. // byte[] for the purpose of calculating the client hash. Room for the server MAC is foreseen
  1098. // to read the packet including server MAC in a single pass (except for the initial block).
  1099. data = new byte[bytesToRead + blockSize + inboundPacketSequenceLength];
  1100. BinaryPrimitives.WriteUInt32BigEndian(data, _inboundPacketSequence);
  1101. // Use raw packet length field to calculate the mac in AEAD mode.
  1102. if (_serverAead)
  1103. {
  1104. Buffer.BlockCopy(firstBlock, 0, data, inboundPacketSequenceLength, blockSize);
  1105. }
  1106. else
  1107. {
  1108. Buffer.BlockCopy(plainFirstBlock, 0, data, inboundPacketSequenceLength, blockSize);
  1109. }
  1110. if (bytesToRead > 0)
  1111. {
  1112. if (TrySocketRead(socket, data, blockSize + inboundPacketSequenceLength, bytesToRead) == 0)
  1113. {
  1114. return null;
  1115. }
  1116. }
  1117. }
  1118. // validate encrypted message against MAC
  1119. if (_serverMac != null && _serverEtm)
  1120. {
  1121. var clientHash = _serverMac.ComputeHash(data, 0, data.Length - serverMacLength);
  1122. #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
  1123. if (!CryptographicOperations.FixedTimeEquals(clientHash, new ReadOnlySpan<byte>(data, data.Length - serverMacLength, serverMacLength)))
  1124. #else
  1125. if (!Org.BouncyCastle.Utilities.Arrays.FixedTimeEquals(serverMacLength, clientHash, 0, data, data.Length - serverMacLength))
  1126. #endif
  1127. {
  1128. throw new SshConnectionException("MAC error", DisconnectReason.MacError);
  1129. }
  1130. }
  1131. if (_serverCipher != null)
  1132. {
  1133. var numberOfBytesToDecrypt = data.Length - (blockSize + inboundPacketSequenceLength + serverMacLength);
  1134. if (numberOfBytesToDecrypt > 0)
  1135. {
  1136. var decryptedData = _serverCipher.Decrypt(data, blockSize + inboundPacketSequenceLength, numberOfBytesToDecrypt);
  1137. Buffer.BlockCopy(decryptedData, 0, data, blockSize + inboundPacketSequenceLength, decryptedData.Length);
  1138. }
  1139. }
  1140. var paddingLength = data[inboundPacketSequenceLength + packetLengthFieldLength];
  1141. var messagePayloadLength = (int)packetLength - paddingLength - paddingLengthFieldLength;
  1142. var messagePayloadOffset = inboundPacketSequenceLength + packetLengthFieldLength + paddingLengthFieldLength;
  1143. // validate decrypted message against MAC
  1144. if (_serverMac != null && !_serverEtm)
  1145. {
  1146. var clientHash = _serverMac.ComputeHash(data, 0, data.Length - serverMacLength);
  1147. #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
  1148. if (!CryptographicOperations.FixedTimeEquals(clientHash, new ReadOnlySpan<byte>(data, data.Length - serverMacLength, serverMacLength)))
  1149. #else
  1150. if (!Org.BouncyCastle.Utilities.Arrays.FixedTimeEquals(serverMacLength, clientHash, 0, data, data.Length - serverMacLength))
  1151. #endif
  1152. {
  1153. throw new SshConnectionException("MAC error", DisconnectReason.MacError);
  1154. }
  1155. }
  1156. if (_serverDecompression != null)
  1157. {
  1158. data = _serverDecompression.Decompress(data, messagePayloadOffset, messagePayloadLength);
  1159. // Data now only contains the decompressed payload, and as such the offset is reset to zero
  1160. messagePayloadOffset = 0;
  1161. // The length of the payload is now the complete decompressed content
  1162. messagePayloadLength = data.Length;
  1163. }
  1164. _inboundPacketSequence++;
  1165. // The below code mirrors from https://github.com/openssh/openssh-portable/commit/1edb00c58f8a6875fad6a497aa2bacf37f9e6cd5
  1166. // It ensures the integrity of key exchange process.
  1167. if (_inboundPacketSequence == uint.MaxValue && _isInitialKex)
  1168. {
  1169. throw new SshConnectionException("Inbound packet sequence number is about to wrap during initial key exchange.", DisconnectReason.KeyExchangeFailed);
  1170. }
  1171. return LoadMessage(data, messagePayloadOffset, messagePayloadLength);
  1172. }
  1173. private void TrySendDisconnect(DisconnectReason reasonCode, string message)
  1174. {
  1175. var disconnectMessage = new DisconnectMessage(reasonCode, message);
  1176. // Send the disconnect message, but ignore the outcome
  1177. _ = TrySendMessage(disconnectMessage);
  1178. // Mark disconnect message sent regardless of whether the send sctually succeeded
  1179. _isDisconnectMessageSent = true;
  1180. }
  1181. /// <summary>
  1182. /// Called when <see cref="DisconnectMessage"/> received.
  1183. /// </summary>
  1184. /// <param name="message"><see cref="DisconnectMessage"/> message.</param>
  1185. internal void OnDisconnectReceived(DisconnectMessage message)
  1186. {
  1187. DiagnosticAbstraction.Log(string.Format("[{0}] Disconnect received: {1} {2}.", ToHex(SessionId), message.ReasonCode, message.Description));
  1188. // transition to disconnecting state to avoid throwing exceptions while cleaning up, and to
  1189. // ensure any exceptions that are raised do not overwrite the SshConnectionException that we
  1190. // set below
  1191. _isDisconnecting = true;
  1192. _exception = new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The connection was closed by the server: {0} ({1}).", message.Description, message.ReasonCode), message.ReasonCode);
  1193. _ = _exceptionWaitHandle.Set();
  1194. DisconnectReceived?.Invoke(this, new MessageEventArgs<DisconnectMessage>(message));
  1195. Disconnected?.Invoke(this, EventArgs.Empty);
  1196. // disconnect socket, and dispose it
  1197. SocketDisconnectAndDispose();
  1198. }
  1199. /// <summary>
  1200. /// Called when <see cref="IgnoreMessage"/> received.
  1201. /// </summary>
  1202. /// <param name="message"><see cref="IgnoreMessage"/> message.</param>
  1203. internal void OnIgnoreReceived(IgnoreMessage message)
  1204. {
  1205. IgnoreReceived?.Invoke(this, new MessageEventArgs<IgnoreMessage>(message));
  1206. }
  1207. /// <summary>
  1208. /// Called when <see cref="UnimplementedMessage"/> message received.
  1209. /// </summary>
  1210. /// <param name="message"><see cref="UnimplementedMessage"/> message.</param>
  1211. internal void OnUnimplementedReceived(UnimplementedMessage message)
  1212. {
  1213. UnimplementedReceived?.Invoke(this, new MessageEventArgs<UnimplementedMessage>(message));
  1214. }
  1215. /// <summary>
  1216. /// Called when <see cref="DebugMessage"/> message received.
  1217. /// </summary>
  1218. /// <param name="message"><see cref="DebugMessage"/> message.</param>
  1219. internal void OnDebugReceived(DebugMessage message)
  1220. {
  1221. DebugReceived?.Invoke(this, new MessageEventArgs<DebugMessage>(message));
  1222. }
  1223. /// <summary>
  1224. /// Called when <see cref="ServiceRequestMessage"/> message received.
  1225. /// </summary>
  1226. /// <param name="message"><see cref="ServiceRequestMessage"/> message.</param>
  1227. internal void OnServiceRequestReceived(ServiceRequestMessage message)
  1228. {
  1229. ServiceRequestReceived?.Invoke(this, new MessageEventArgs<ServiceRequestMessage>(message));
  1230. }
  1231. /// <summary>
  1232. /// Called when <see cref="ServiceAcceptMessage"/> message received.
  1233. /// </summary>
  1234. /// <param name="message"><see cref="ServiceAcceptMessage"/> message.</param>
  1235. internal void OnServiceAcceptReceived(ServiceAcceptMessage message)
  1236. {
  1237. ServiceAcceptReceived?.Invoke(this, new MessageEventArgs<ServiceAcceptMessage>(message));
  1238. _ = _serviceAccepted.Set();
  1239. }
  1240. internal void OnKeyExchangeDhGroupExchangeGroupReceived(KeyExchangeDhGroupExchangeGroup message)
  1241. {
  1242. KeyExchangeDhGroupExchangeGroupReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhGroupExchangeGroup>(message));
  1243. }
  1244. internal void OnKeyExchangeDhGroupExchangeReplyReceived(KeyExchangeDhGroupExchangeReply message)
  1245. {
  1246. KeyExchangeDhGroupExchangeReplyReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhGroupExchangeReply>(message));
  1247. }
  1248. /// <summary>
  1249. /// Called when <see cref="KeyExchangeInitMessage"/> message received.
  1250. /// </summary>
  1251. /// <param name="message"><see cref="KeyExchangeInitMessage"/> message.</param>
  1252. internal void OnKeyExchangeInitReceived(KeyExchangeInitMessage message)
  1253. {
  1254. // If _keyExchangeCompletedWaitHandle is already set, then this is a key
  1255. // re-exchange initiated by the server, and we need to send our own init
  1256. // message.
  1257. // Otherwise, the wait handle is not set and this received init is part of the
  1258. // initial connection for which we have already sent our init, so we shouldn't
  1259. // send another one.
  1260. var sendClientInitMessage = _keyExchangeCompletedWaitHandle.IsSet;
  1261. _keyExchangeCompletedWaitHandle.Reset();
  1262. if (_isInitialKex && message.KeyExchangeAlgorithms.Contains("kex-strict-s-v00@openssh.com"))
  1263. {
  1264. _isStrictKex = true;
  1265. DiagnosticAbstraction.Log(string.Format("[{0}] Enabling strict key exchange extension.", ToHex(SessionId)));
  1266. if (_inboundPacketSequence != 1)
  1267. {
  1268. throw new SshConnectionException("KEXINIT was not the first packet during strict key exchange.", DisconnectReason.KeyExchangeFailed);
  1269. }
  1270. }
  1271. // Disable messages that are not key exchange related
  1272. _sshMessageFactory.DisableNonKeyExchangeMessages(_isStrictKex);
  1273. _keyExchange = _serviceFactory.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms,
  1274. message.KeyExchangeAlgorithms);
  1275. ConnectionInfo.CurrentKeyExchangeAlgorithm = _keyExchange.Name;
  1276. DiagnosticAbstraction.Log(string.Format("[{0}] Performing {1} key exchange.", ToHex(SessionId), ConnectionInfo.CurrentKeyExchangeAlgorithm));
  1277. _keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived;
  1278. // Start the algorithm implementation
  1279. _keyExchange.Start(this, message, sendClientInitMessage);
  1280. KeyExchangeInitReceived?.Invoke(this, new MessageEventArgs<KeyExchangeInitMessage>(message));
  1281. }
  1282. internal void OnKeyExchangeDhReplyMessageReceived(KeyExchangeDhReplyMessage message)
  1283. {
  1284. KeyExchangeDhReplyMessageReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhReplyMessage>(message));
  1285. }
  1286. internal void OnKeyExchangeEcdhReplyMessageReceived(KeyExchangeEcdhReplyMessage message)
  1287. {
  1288. KeyExchangeEcdhReplyMessageReceived?.Invoke(this, new MessageEventArgs<KeyExchangeEcdhReplyMessage>(message));
  1289. }
  1290. /// <summary>
  1291. /// Called when <see cref="NewKeysMessage"/> message received.
  1292. /// </summary>
  1293. /// <param name="message"><see cref="NewKeysMessage"/> message.</param>
  1294. internal void OnNewKeysReceived(NewKeysMessage message)
  1295. {
  1296. // Update sessionId
  1297. SessionId ??= _keyExchange.ExchangeHash;
  1298. // Dispose of old ciphers and hash algorithms
  1299. if (_serverCipher is IDisposable disposableServerCipher)
  1300. {
  1301. disposableServerCipher.Dispose();
  1302. }
  1303. if (_clientCipher is IDisposable disposableClientCipher)
  1304. {
  1305. disposableClientCipher.Dispose();
  1306. }
  1307. if (_serverMac != null)
  1308. {
  1309. _serverMac.Dispose();
  1310. _serverMac = null;
  1311. }
  1312. if (_clientMac != null)
  1313. {
  1314. _clientMac.Dispose();
  1315. _clientMac = null;
  1316. }
  1317. // Update negotiated algorithms
  1318. _serverCipher = _keyExchange.CreateServerCipher(out _serverAead);
  1319. _clientCipher = _keyExchange.CreateClientCipher(out _clientAead);
  1320. _serverMac = _keyExchange.CreateServerHash(out _serverEtm);
  1321. _clientMac = _keyExchange.CreateClientHash(out _clientEtm);
  1322. _clientCompression = _keyExchange.CreateCompressor();
  1323. _serverDecompression = _keyExchange.CreateDecompressor();
  1324. // Dispose of old KeyExchange object as it is no longer needed.
  1325. _keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
  1326. _keyExchange.Dispose();
  1327. _keyExchange = null;
  1328. // Enable activated messages that are not key exchange related
  1329. _sshMessageFactory.EnableActivatedMessages();
  1330. if (_isInitialKex)
  1331. {
  1332. _isInitialKex = false;
  1333. ClientInitMessage = BuildClientInitMessage(includeStrictKexPseudoAlgorithm: false);
  1334. }
  1335. if (_isStrictKex)
  1336. {
  1337. _inboundPacketSequence = 0;
  1338. }
  1339. NewKeysReceived?.Invoke(this, new MessageEventArgs<NewKeysMessage>(message));
  1340. // Signal that key exchange completed
  1341. _keyExchangeCompletedWaitHandle.Set();
  1342. }
  1343. /// <summary>
  1344. /// Called when client is disconnecting from the server.
  1345. /// </summary>
  1346. void ISession.OnDisconnecting()
  1347. {
  1348. _isDisconnecting = true;
  1349. }
  1350. /// <summary>
  1351. /// Called when <see cref="RequestMessage"/> message received.
  1352. /// </summary>
  1353. /// <param name="message"><see cref="RequestMessage"/> message.</param>
  1354. internal void OnUserAuthenticationRequestReceived(RequestMessage message)
  1355. {
  1356. UserAuthenticationRequestReceived?.Invoke(this, new MessageEventArgs<RequestMessage>(message));
  1357. }
  1358. /// <summary>
  1359. /// Called when <see cref="FailureMessage"/> message received.
  1360. /// </summary>
  1361. /// <param name="message"><see cref="FailureMessage"/> message.</param>
  1362. internal void OnUserAuthenticationFailureReceived(FailureMessage message)
  1363. {
  1364. UserAuthenticationFailureReceived?.Invoke(this, new MessageEventArgs<FailureMessage>(message));
  1365. }
  1366. /// <summary>
  1367. /// Called when <see cref="SuccessMessage"/> message received.
  1368. /// </summary>
  1369. /// <param name="message"><see cref="SuccessMessage"/> message.</param>
  1370. internal void OnUserAuthenticationSuccessReceived(SuccessMessage message)
  1371. {
  1372. UserAuthenticationSuccessReceived?.Invoke(this, new MessageEventArgs<SuccessMessage>(message));
  1373. }
  1374. /// <summary>
  1375. /// Called when <see cref="BannerMessage"/> message received.
  1376. /// </summary>
  1377. /// <param name="message"><see cref="BannerMessage"/> message.</param>
  1378. internal void OnUserAuthenticationBannerReceived(BannerMessage message)
  1379. {
  1380. UserAuthenticationBannerReceived?.Invoke(this, new MessageEventArgs<BannerMessage>(message));
  1381. }
  1382. /// <summary>
  1383. /// Called when <see cref="InformationRequestMessage"/> message received.
  1384. /// </summary>
  1385. /// <param name="message"><see cref="InformationRequestMessage"/> message.</param>
  1386. internal void OnUserAuthenticationInformationRequestReceived(InformationRequestMessage message)
  1387. {
  1388. UserAuthenticationInformationRequestReceived?.Invoke(this, new MessageEventArgs<InformationRequestMessage>(message));
  1389. }
  1390. internal void OnUserAuthenticationPasswordChangeRequiredReceived(PasswordChangeRequiredMessage message)
  1391. {
  1392. UserAuthenticationPasswordChangeRequiredReceived?.Invoke(this, new MessageEventArgs<PasswordChangeRequiredMessage>(message));
  1393. }
  1394. internal void OnUserAuthenticationPublicKeyReceived(PublicKeyMessage message)
  1395. {
  1396. UserAuthenticationPublicKeyReceived?.Invoke(this, new MessageEventArgs<PublicKeyMessage>(message));
  1397. }
  1398. /// <summary>
  1399. /// Called when <see cref="GlobalRequestMessage"/> message received.
  1400. /// </summary>
  1401. /// <param name="message"><see cref="GlobalRequestMessage"/> message.</param>
  1402. internal void OnGlobalRequestReceived(GlobalRequestMessage message)
  1403. {
  1404. GlobalRequestReceived?.Invoke(this, new MessageEventArgs<GlobalRequestMessage>(message));
  1405. }
  1406. /// <summary>
  1407. /// Called when <see cref="RequestSuccessMessage"/> message received.
  1408. /// </summary>
  1409. /// <param name="message"><see cref="RequestSuccessMessage"/> message.</param>
  1410. internal void OnRequestSuccessReceived(RequestSuccessMessage message)
  1411. {
  1412. RequestSuccessReceived?.Invoke(this, new MessageEventArgs<RequestSuccessMessage>(message));
  1413. }
  1414. /// <summary>
  1415. /// Called when <see cref="RequestFailureMessage"/> message received.
  1416. /// </summary>
  1417. /// <param name="message"><see cref="RequestFailureMessage"/> message.</param>
  1418. internal void OnRequestFailureReceived(RequestFailureMessage message)
  1419. {
  1420. RequestFailureReceived?.Invoke(this, new MessageEventArgs<RequestFailureMessage>(message));
  1421. }
  1422. /// <summary>
  1423. /// Called when <see cref="ChannelOpenMessage"/> message received.
  1424. /// </summary>
  1425. /// <param name="message"><see cref="ChannelOpenMessage"/> message.</param>
  1426. internal void OnChannelOpenReceived(ChannelOpenMessage message)
  1427. {
  1428. ChannelOpenReceived?.Invoke(this, new MessageEventArgs<ChannelOpenMessage>(message));
  1429. }
  1430. /// <summary>
  1431. /// Called when <see cref="ChannelOpenConfirmationMessage"/> message received.
  1432. /// </summary>
  1433. /// <param name="message"><see cref="ChannelOpenConfirmationMessage"/> message.</param>
  1434. internal void OnChannelOpenConfirmationReceived(ChannelOpenConfirmationMessage message)
  1435. {
  1436. ChannelOpenConfirmationReceived?.Invoke(this, new MessageEventArgs<ChannelOpenConfirmationMessage>(message));
  1437. }
  1438. /// <summary>
  1439. /// Called when <see cref="ChannelOpenFailureMessage"/> message received.
  1440. /// </summary>
  1441. /// <param name="message"><see cref="ChannelOpenFailureMessage"/> message.</param>
  1442. internal void OnChannelOpenFailureReceived(ChannelOpenFailureMessage message)
  1443. {
  1444. ChannelOpenFailureReceived?.Invoke(this, new MessageEventArgs<ChannelOpenFailureMessage>(message));
  1445. }
  1446. /// <summary>
  1447. /// Called when <see cref="ChannelWindowAdjustMessage"/> message received.
  1448. /// </summary>
  1449. /// <param name="message"><see cref="ChannelWindowAdjustMessage"/> message.</param>
  1450. internal void OnChannelWindowAdjustReceived(ChannelWindowAdjustMessage message)
  1451. {
  1452. ChannelWindowAdjustReceived?.Invoke(this, new MessageEventArgs<ChannelWindowAdjustMessage>(message));
  1453. }
  1454. /// <summary>
  1455. /// Called when <see cref="ChannelDataMessage"/> message received.
  1456. /// </summary>
  1457. /// <param name="message"><see cref="ChannelDataMessage"/> message.</param>
  1458. internal void OnChannelDataReceived(ChannelDataMessage message)
  1459. {
  1460. ChannelDataReceived?.Invoke(this, new MessageEventArgs<ChannelDataMessage>(message));
  1461. }
  1462. /// <summary>
  1463. /// Called when <see cref="ChannelExtendedDataMessage"/> message received.
  1464. /// </summary>
  1465. /// <param name="message"><see cref="ChannelExtendedDataMessage"/> message.</param>
  1466. internal void OnChannelExtendedDataReceived(ChannelExtendedDataMessage message)
  1467. {
  1468. ChannelExtendedDataReceived?.Invoke(this, new MessageEventArgs<ChannelExtendedDataMessage>(message));
  1469. }
  1470. /// <summary>
  1471. /// Called when <see cref="ChannelCloseMessage"/> message received.
  1472. /// </summary>
  1473. /// <param name="message"><see cref="ChannelCloseMessage"/> message.</param>
  1474. internal void OnChannelEofReceived(ChannelEofMessage message)
  1475. {
  1476. ChannelEofReceived?.Invoke(this, new MessageEventArgs<ChannelEofMessage>(message));
  1477. }
  1478. /// <summary>
  1479. /// Called when <see cref="ChannelCloseMessage"/> message received.
  1480. /// </summary>
  1481. /// <param name="message"><see cref="ChannelCloseMessage"/> message.</param>
  1482. internal void OnChannelCloseReceived(ChannelCloseMessage message)
  1483. {
  1484. ChannelCloseReceived?.Invoke(this, new MessageEventArgs<ChannelCloseMessage>(message));
  1485. }
  1486. /// <summary>
  1487. /// Called when <see cref="ChannelRequestMessage"/> message received.
  1488. /// </summary>
  1489. /// <param name="message"><see cref="ChannelRequestMessage"/> message.</param>
  1490. internal void OnChannelRequestReceived(ChannelRequestMessage message)
  1491. {
  1492. ChannelRequestReceived?.Invoke(this, new MessageEventArgs<ChannelRequestMessage>(message));
  1493. }
  1494. /// <summary>
  1495. /// Called when <see cref="ChannelSuccessMessage"/> message received.
  1496. /// </summary>
  1497. /// <param name="message"><see cref="ChannelSuccessMessage"/> message.</param>
  1498. internal void OnChannelSuccessReceived(ChannelSuccessMessage message)
  1499. {
  1500. ChannelSuccessReceived?.Invoke(this, new MessageEventArgs<ChannelSuccessMessage>(message));
  1501. }
  1502. /// <summary>
  1503. /// Called when <see cref="ChannelFailureMessage"/> message received.
  1504. /// </summary>
  1505. /// <param name="message"><see cref="ChannelFailureMessage"/> message.</param>
  1506. internal void OnChannelFailureReceived(ChannelFailureMessage message)
  1507. {
  1508. ChannelFailureReceived?.Invoke(this, new MessageEventArgs<ChannelFailureMessage>(message));
  1509. }
  1510. private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e)
  1511. {
  1512. HostKeyReceived?.Invoke(this, e);
  1513. }
  1514. /// <summary>
  1515. /// Registers SSH message with the session.
  1516. /// </summary>
  1517. /// <param name="messageName">The name of the message to register with the session.</param>
  1518. public void RegisterMessage(string messageName)
  1519. {
  1520. _sshMessageFactory.EnableAndActivateMessage(messageName);
  1521. }
  1522. /// <summary>
  1523. /// Unregister SSH message from the session.
  1524. /// </summary>
  1525. /// <param name="messageName">The name of the message to unregister with the session.</param>
  1526. public void UnRegisterMessage(string messageName)
  1527. {
  1528. _sshMessageFactory.DisableAndDeactivateMessage(messageName);
  1529. }
  1530. /// <summary>
  1531. /// Loads a message from a given buffer.
  1532. /// </summary>
  1533. /// <param name="data">An array of bytes from which to construct the message.</param>
  1534. /// <param name="offset">The zero-based byte offset in <paramref name="data"/> at which to begin reading.</param>
  1535. /// <param name="count">The number of bytes to load.</param>
  1536. /// <returns>
  1537. /// A message constructed from <paramref name="data"/>.
  1538. /// </returns>
  1539. /// <exception cref="SshException">The type of the message is not supported.</exception>
  1540. private Message LoadMessage(byte[] data, int offset, int count)
  1541. {
  1542. var messageType = data[offset];
  1543. var message = _sshMessageFactory.Create(messageType);
  1544. message.Load(data, offset + 1, count - 1);
  1545. DiagnosticAbstraction.Log(string.Format("[{0}] Received message '{1}' from server: '{2}'.", ToHex(SessionId), message.GetType().Name, message));
  1546. return message;
  1547. }
  1548. private static string ToHex(byte[] bytes, int offset)
  1549. {
  1550. var byteCount = bytes.Length - offset;
  1551. var builder = new StringBuilder(bytes.Length * 2);
  1552. for (var i = offset; i < byteCount; i++)
  1553. {
  1554. var b = bytes[i];
  1555. _ = builder.Append(b.ToString("X2"));
  1556. }
  1557. return builder.ToString();
  1558. }
  1559. internal static string ToHex(byte[] bytes)
  1560. {
  1561. if (bytes is null)
  1562. {
  1563. return null;
  1564. }
  1565. return ToHex(bytes, 0);
  1566. }
  1567. /// <summary>
  1568. /// Gets a value indicating whether the socket is connected.
  1569. /// </summary>
  1570. /// <returns>
  1571. /// <see langword="true"/> if the socket is connected; otherwise, <see langword="false"/>.
  1572. /// </returns>
  1573. /// <remarks>
  1574. /// <para>
  1575. /// As a first check we verify whether <see cref="Socket.Connected"/> is
  1576. /// <see langword="true"/>. However, this only returns the state of the socket as of
  1577. /// the last I/O operation.
  1578. /// </para>
  1579. /// <para>
  1580. /// Therefore we use the combination of <see cref="Socket.Poll(int, SelectMode)"/> with mode <see cref="SelectMode.SelectRead"/>
  1581. /// and <see cref="Socket.Available"/> to verify if the socket is still connected.
  1582. /// </para>
  1583. /// <para>
  1584. /// The MSDN doc mention the following on the return value of <see cref="Socket.Poll(int, SelectMode)"/>
  1585. /// with mode <see cref="SelectMode.SelectRead"/>:
  1586. /// <list type="bullet">
  1587. /// <item>
  1588. /// <description><see langword="true"/> if data is available for reading;</description>
  1589. /// </item>
  1590. /// <item>
  1591. /// <description><see langword="true"/> if the connection has been closed, reset, or terminated; otherwise, returns <see langword="false"/>.</description>
  1592. /// </item>
  1593. /// </list>
  1594. /// </para>
  1595. /// <para>
  1596. /// <c>Conclusion:</c> when the return value is <see langword="true"/> - but no data is available for reading - then
  1597. /// the socket is no longer connected.
  1598. /// </para>
  1599. /// <para>
  1600. /// When a <see cref="Socket"/> is used from multiple threads, there's a race condition
  1601. /// between the invocation of <see cref="Socket.Poll(int, SelectMode)"/> and the moment
  1602. /// when the value of <see cref="Socket.Available"/> is obtained. To workaround this issue
  1603. /// we synchronize reads from the <see cref="Socket"/>.
  1604. /// </para>
  1605. /// <para>
  1606. /// We assume the socket is still connected if the read lock cannot be acquired immediately.
  1607. /// In this case, we just return <see langword="true"/> without actually waiting to acquire
  1608. /// the lock. We don't want to wait for the read lock if another thread already has it because
  1609. /// there are cases where the other thread holding the lock can be waiting indefinitely for
  1610. /// a socket read operation to complete.
  1611. /// </para>
  1612. /// </remarks>
  1613. private bool IsSocketConnected()
  1614. {
  1615. _socketDisposeLock.Wait();
  1616. try
  1617. {
  1618. if (!_socket.IsConnected())
  1619. {
  1620. return false;
  1621. }
  1622. if (!Monitor.TryEnter(_socketReadLock))
  1623. {
  1624. return true;
  1625. }
  1626. try
  1627. {
  1628. var connectionClosedOrDataAvailable = _socket.Poll(0, SelectMode.SelectRead);
  1629. return !(connectionClosedOrDataAvailable && _socket.Available == 0);
  1630. }
  1631. finally
  1632. {
  1633. Monitor.Exit(_socketReadLock);
  1634. }
  1635. }
  1636. finally
  1637. {
  1638. _ = _socketDisposeLock.Release();
  1639. }
  1640. }
  1641. /// <summary>
  1642. /// Performs a blocking read on the socket until <paramref name="length"/> bytes are received.
  1643. /// </summary>
  1644. /// <param name="socket">The <see cref="Socket"/> to read from.</param>
  1645. /// <param name="buffer">An array of type <see cref="byte"/> that is the storage location for the received data.</param>
  1646. /// <param name="offset">The position in <paramref name="buffer"/> parameter to store the received data.</param>
  1647. /// <param name="length">The number of bytes to read.</param>
  1648. /// <returns>
  1649. /// The number of bytes read.
  1650. /// </returns>
  1651. /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception>
  1652. /// <exception cref="SocketException">The read failed.</exception>
  1653. private static int TrySocketRead(Socket socket, byte[] buffer, int offset, int length)
  1654. {
  1655. return SocketAbstraction.Read(socket, buffer, offset, length, Timeout.InfiniteTimeSpan);
  1656. }
  1657. /// <summary>
  1658. /// Shuts down and disposes the socket.
  1659. /// </summary>
  1660. private void SocketDisconnectAndDispose()
  1661. {
  1662. if (_socket != null)
  1663. {
  1664. _socketDisposeLock.Wait();
  1665. try
  1666. {
  1667. #pragma warning disable CA1508 // Avoid dead conditional code; Value could have been changed by another thread.
  1668. if (_socket != null)
  1669. #pragma warning restore CA1508 // Avoid dead conditional code
  1670. {
  1671. if (_socket.Connected)
  1672. {
  1673. try
  1674. {
  1675. DiagnosticAbstraction.Log(string.Format("[{0}] Shutting down socket.", ToHex(SessionId)));
  1676. // Interrupt any pending reads; should be done outside of socket read lock as we
  1677. // actually want shutdown the socket to make sure blocking reads are interrupted.
  1678. //
  1679. // This may result in a SocketException (eg. An existing connection was forcibly
  1680. // closed by the remote host) which we'll log and ignore as it means the socket
  1681. // was already shut down.
  1682. _socket.Shutdown(SocketShutdown.Send);
  1683. }
  1684. catch (SocketException ex)
  1685. {
  1686. // TODO: log as warning
  1687. DiagnosticAbstraction.Log("Failure shutting down socket: " + ex);
  1688. }
  1689. }
  1690. DiagnosticAbstraction.Log(string.Format("[{0}] Disposing socket.", ToHex(SessionId)));
  1691. _socket.Dispose();
  1692. DiagnosticAbstraction.Log(string.Format("[{0}] Disposed socket.", ToHex(SessionId)));
  1693. _socket = null;
  1694. }
  1695. }
  1696. finally
  1697. {
  1698. _ = _socketDisposeLock.Release();
  1699. }
  1700. }
  1701. }
  1702. /// <summary>
  1703. /// Listens for incoming message from the server and handles them. This method run as a task on separate thread.
  1704. /// </summary>
  1705. private void MessageListener()
  1706. {
  1707. try
  1708. {
  1709. // remain in message loop until socket is shut down or until we're disconnecting
  1710. while (true)
  1711. {
  1712. var socket = _socket;
  1713. if (socket is null || !socket.Connected)
  1714. {
  1715. break;
  1716. }
  1717. try
  1718. {
  1719. // Block until either data is available or the socket is closed
  1720. var connectionClosedOrDataAvailable = socket.Poll(-1, SelectMode.SelectRead);
  1721. if (connectionClosedOrDataAvailable && socket.Available == 0)
  1722. {
  1723. // connection with SSH server was closed or connection was reset
  1724. break;
  1725. }
  1726. }
  1727. catch (ObjectDisposedException)
  1728. {
  1729. // The socket was disposed by either:
  1730. // * a call to Disconnect()
  1731. // * a call to Dispose()
  1732. // * a SSH_MSG_DISCONNECT received from server
  1733. break;
  1734. }
  1735. var message = ReceiveMessage(socket);
  1736. if (message is null)
  1737. {
  1738. // Connection with SSH server was closed, so break out of the message loop
  1739. break;
  1740. }
  1741. // process message
  1742. message.Process(this);
  1743. }
  1744. // connection with SSH server was closed or socket was disposed
  1745. RaiseError(CreateConnectionAbortedByServerException());
  1746. }
  1747. catch (SocketException ex)
  1748. {
  1749. RaiseError(new SshConnectionException(ex.Message, DisconnectReason.ConnectionLost, ex));
  1750. }
  1751. catch (Exception exp)
  1752. {
  1753. RaiseError(exp);
  1754. }
  1755. finally
  1756. {
  1757. // signal that the message listener thread has stopped
  1758. _ = _messageListenerCompleted.Set();
  1759. }
  1760. }
  1761. /// <summary>
  1762. /// Raises the <see cref="ErrorOccured"/> event.
  1763. /// </summary>
  1764. /// <param name="exp">The <see cref="Exception"/>.</param>
  1765. private void RaiseError(Exception exp)
  1766. {
  1767. var connectionException = exp as SshConnectionException;
  1768. DiagnosticAbstraction.Log(string.Format("[{0}] Raised exception: {1}", ToHex(SessionId), exp));
  1769. if (_isDisconnecting)
  1770. {
  1771. // a connection exception which is raised while isDisconnecting is normal and
  1772. // should be ignored
  1773. if (connectionException != null)
  1774. {
  1775. return;
  1776. }
  1777. // any timeout while disconnecting can be caused by loss of connectivity
  1778. // altogether and should be ignored
  1779. if (exp is SocketException socketException && socketException.SocketErrorCode == SocketError.TimedOut)
  1780. {
  1781. return;
  1782. }
  1783. }
  1784. // "save" exception and set exception wait handle to ensure any waits are interrupted
  1785. _exception = exp;
  1786. _ = _exceptionWaitHandle.Set();
  1787. ErrorOccured?.Invoke(this, new ExceptionEventArgs(exp));
  1788. if (connectionException != null)
  1789. {
  1790. DiagnosticAbstraction.Log(string.Format("[{0}] Disconnecting after exception: {1}", ToHex(SessionId), exp));
  1791. Disconnect(connectionException.DisconnectReason, exp.ToString());
  1792. }
  1793. }
  1794. /// <summary>
  1795. /// Resets connection-specific information to ensure state of a previous connection
  1796. /// does not affect new connections.
  1797. /// </summary>
  1798. private void Reset()
  1799. {
  1800. _ = _exceptionWaitHandle?.Reset();
  1801. _keyExchangeCompletedWaitHandle?.Reset();
  1802. _ = _messageListenerCompleted?.Set();
  1803. SessionId = null;
  1804. _isDisconnectMessageSent = false;
  1805. _isDisconnecting = false;
  1806. _isAuthenticated = false;
  1807. _exception = null;
  1808. }
  1809. private static SshConnectionException CreateConnectionAbortedByServerException()
  1810. {
  1811. return new SshConnectionException("An established connection was aborted by the server.",
  1812. DisconnectReason.ConnectionLost);
  1813. }
  1814. private KeyExchangeInitMessage BuildClientInitMessage(bool includeStrictKexPseudoAlgorithm)
  1815. {
  1816. return new KeyExchangeInitMessage
  1817. {
  1818. KeyExchangeAlgorithms = includeStrictKexPseudoAlgorithm ?
  1819. ConnectionInfo.KeyExchangeAlgorithms.Keys.Concat(["kex-strict-c-v00@openssh.com"]).ToArray() :
  1820. ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(),
  1821. ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(),
  1822. EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(),
  1823. EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(),
  1824. MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(),
  1825. MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(),
  1826. CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(),
  1827. CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(),
  1828. LanguagesClientToServer = new[] { string.Empty },
  1829. LanguagesServerToClient = new[] { string.Empty },
  1830. FirstKexPacketFollows = false,
  1831. Reserved = 0,
  1832. };
  1833. }
  1834. private bool _disposed;
  1835. /// <summary>
  1836. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  1837. /// </summary>
  1838. public void Dispose()
  1839. {
  1840. Dispose(disposing: true);
  1841. GC.SuppressFinalize(this);
  1842. }
  1843. /// <summary>
  1844. /// Releases unmanaged and - optionally - managed resources.
  1845. /// </summary>
  1846. /// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.</param>
  1847. protected virtual void Dispose(bool disposing)
  1848. {
  1849. if (_disposed)
  1850. {
  1851. return;
  1852. }
  1853. if (disposing)
  1854. {
  1855. DiagnosticAbstraction.Log(string.Format("[{0}] Disposing session.", ToHex(SessionId)));
  1856. Disconnect();
  1857. var serviceAccepted = _serviceAccepted;
  1858. if (serviceAccepted != null)
  1859. {
  1860. serviceAccepted.Dispose();
  1861. _serviceAccepted = null;
  1862. }
  1863. var exceptionWaitHandle = _exceptionWaitHandle;
  1864. if (exceptionWaitHandle != null)
  1865. {
  1866. exceptionWaitHandle.Dispose();
  1867. _exceptionWaitHandle = null;
  1868. }
  1869. var keyExchangeCompletedWaitHandle = _keyExchangeCompletedWaitHandle;
  1870. if (keyExchangeCompletedWaitHandle != null)
  1871. {
  1872. keyExchangeCompletedWaitHandle.Dispose();
  1873. _keyExchangeCompletedWaitHandle = null;
  1874. }
  1875. if (_serverCipher is IDisposable disposableServerCipher)
  1876. {
  1877. disposableServerCipher.Dispose();
  1878. }
  1879. if (_clientCipher is IDisposable disposableClientCipher)
  1880. {
  1881. disposableClientCipher.Dispose();
  1882. }
  1883. var serverMac = _serverMac;
  1884. if (serverMac != null)
  1885. {
  1886. serverMac.Dispose();
  1887. _serverMac = null;
  1888. }
  1889. var clientMac = _clientMac;
  1890. if (clientMac != null)
  1891. {
  1892. clientMac.Dispose();
  1893. _clientMac = null;
  1894. }
  1895. var serverDecompression = _serverDecompression;
  1896. if (serverDecompression != null)
  1897. {
  1898. serverDecompression.Dispose();
  1899. _serverDecompression = null;
  1900. }
  1901. var clientCompression = _clientCompression;
  1902. if (clientCompression != null)
  1903. {
  1904. clientCompression.Dispose();
  1905. _clientCompression = null;
  1906. }
  1907. var keyExchange = _keyExchange;
  1908. if (keyExchange != null)
  1909. {
  1910. keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
  1911. keyExchange.Dispose();
  1912. _keyExchange = null;
  1913. }
  1914. var messageListenerCompleted = _messageListenerCompleted;
  1915. if (messageListenerCompleted != null)
  1916. {
  1917. messageListenerCompleted.Dispose();
  1918. _messageListenerCompleted = null;
  1919. }
  1920. _disposed = true;
  1921. }
  1922. }
  1923. /// <summary>
  1924. /// Finalizes an instance of the <see cref="Session"/> class.
  1925. /// </summary>
  1926. ~Session()
  1927. {
  1928. Dispose(disposing: false);
  1929. }
  1930. /// <summary>
  1931. /// Gets the connection info.
  1932. /// </summary>
  1933. /// <value>The connection info.</value>
  1934. IConnectionInfo ISession.ConnectionInfo
  1935. {
  1936. get { return ConnectionInfo; }
  1937. }
  1938. /// <summary>
  1939. /// Gets a <see cref="WaitHandle"/> that can be used to wait for the message listener loop to complete.
  1940. /// </summary>
  1941. /// <value>
  1942. /// A <see cref="WaitHandle"/> that can be used to wait for the message listener loop to complete, or
  1943. /// <see langword="null"/> when the session has not been connected.
  1944. /// </value>
  1945. WaitHandle ISession.MessageListenerCompleted
  1946. {
  1947. get { return _messageListenerCompleted; }
  1948. }
  1949. /// <summary>
  1950. /// Create a new SSH session channel.
  1951. /// </summary>
  1952. /// <returns>
  1953. /// A new SSH session channel.
  1954. /// </returns>
  1955. IChannelSession ISession.CreateChannelSession()
  1956. {
  1957. return new ChannelSession(this, NextChannelNumber, InitialLocalWindowSize, LocalChannelDataPacketSize);
  1958. }
  1959. /// <summary>
  1960. /// Create a new channel for a locally forwarded TCP/IP port.
  1961. /// </summary>
  1962. /// <returns>
  1963. /// A new channel for a locally forwarded TCP/IP port.
  1964. /// </returns>
  1965. IChannelDirectTcpip ISession.CreateChannelDirectTcpip()
  1966. {
  1967. return new ChannelDirectTcpip(this, NextChannelNumber, InitialLocalWindowSize, LocalChannelDataPacketSize);
  1968. }
  1969. /// <summary>
  1970. /// Creates a "forwarded-tcpip" SSH channel.
  1971. /// </summary>
  1972. /// <param name="remoteChannelNumber">The number of the remote channel.</param>
  1973. /// <param name="remoteWindowSize">The window size of the remote channel.</param>
  1974. /// <param name="remoteChannelDataPacketSize">The data packet size of the remote channel.</param>
  1975. /// <returns>
  1976. /// A new "forwarded-tcpip" SSH channel.
  1977. /// </returns>
  1978. IChannelForwardedTcpip ISession.CreateChannelForwardedTcpip(uint remoteChannelNumber,
  1979. uint remoteWindowSize,
  1980. uint remoteChannelDataPacketSize)
  1981. {
  1982. return new ChannelForwardedTcpip(this,
  1983. NextChannelNumber,
  1984. InitialLocalWindowSize,
  1985. LocalChannelDataPacketSize,
  1986. remoteChannelNumber,
  1987. remoteWindowSize,
  1988. remoteChannelDataPacketSize);
  1989. }
  1990. /// <summary>
  1991. /// Sends a message to the server.
  1992. /// </summary>
  1993. /// <param name="message">The message to send.</param>
  1994. /// <exception cref="SshConnectionException">The client is not connected.</exception>
  1995. /// <exception cref="SshOperationTimeoutException">The operation timed out.</exception>
  1996. /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
  1997. void ISession.SendMessage(Message message)
  1998. {
  1999. SendMessage(message);
  2000. }
  2001. /// <summary>
  2002. /// Sends a message to the server.
  2003. /// </summary>
  2004. /// <param name="message">The message to send.</param>
  2005. /// <returns>
  2006. /// <see langword="true"/> if the message was sent to the server; otherwise, <see langword="false"/>.
  2007. /// </returns>
  2008. /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
  2009. /// <remarks>
  2010. /// This methods returns <see langword="false"/> when the attempt to send the message results in a
  2011. /// <see cref="SocketException"/> or a <see cref="SshException"/>.
  2012. /// </remarks>
  2013. bool ISession.TrySendMessage(Message message)
  2014. {
  2015. return TrySendMessage(message);
  2016. }
  2017. }
  2018. /// <summary>
  2019. /// Represents the result of a wait operations.
  2020. /// </summary>
  2021. internal enum WaitResult
  2022. {
  2023. /// <summary>
  2024. /// The <see cref="WaitHandle"/> was signaled within the specified interval.
  2025. /// </summary>
  2026. Success = 1,
  2027. /// <summary>
  2028. /// The <see cref="WaitHandle"/> was not signaled within the specified interval.
  2029. /// </summary>
  2030. TimedOut = 2,
  2031. /// <summary>
  2032. /// The session is in a disconnected state.
  2033. /// </summary>
  2034. Disconnected = 3,
  2035. /// <summary>
  2036. /// The session is in a failed state.
  2037. /// </summary>
  2038. Failed = 4
  2039. }
  2040. }