Session.cs 100 KB

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