Session.cs 94 KB

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