Session.cs 99 KB

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