PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Trunk/NSsh/NSsh.Server/TransportLayer/TransportLayerManager.cs

#
C# | 410 lines | 293 code | 80 blank | 37 comment | 36 complexity | 0c660a7d5aade66b45b692768a9336a5 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, GPL-2.0, GPL-3.0, LGPL-3.0
  1. /*
  2. * Copyright 2008 Luke Quinane
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * This work has been adapted from the Ganymed SSH-2 Java client.
  17. */
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Net;
  21. using System.Net.Sockets;
  22. using System.Text;
  23. using log4net;
  24. using NSsh.Common;
  25. using NSsh.Common.Packets;
  26. using NSsh.Server.TransportLayer.State;
  27. using System.IO;
  28. using NSsh.Common.Utility;
  29. using System.Threading;
  30. using NSsh.Server.Configuration;
  31. using NSsh.Common.Types;
  32. using System.Security.Cryptography;
  33. using System.Security.Principal;
  34. using Mono.Math;
  35. namespace NSsh.Server.TransportLayer
  36. {
  37. public class TransportLayerManager : ITransportLayerManager
  38. {
  39. private const int VERSION_STRING_MAX_LENGTH = 255;
  40. /// <summary>
  41. /// Logging support for this class.
  42. /// </summary>
  43. private static ILog log = LogManager.GetLogger(typeof(TransportLayerManager));
  44. private AbstractTransportState _state;
  45. public AbstractTransportState State
  46. {
  47. get { return _state; }
  48. }
  49. private Stream _stream;
  50. private Timer _idleTimeout;
  51. private EndPoint _clientEndPoint;
  52. private IPacketFactory _packetFactory;
  53. public TransportLayerManager()
  54. {
  55. CommunicationLock = new object();
  56. _packetFactory = Dependency.Resolve<IPacketFactory>();
  57. }
  58. ~TransportLayerManager()
  59. {
  60. Dispose(false);
  61. }
  62. private void IdleTimeoutCallback(object state)
  63. {
  64. if (Connected && OnIdleTimeout != null)
  65. {
  66. OnIdleTimeout(this, EventArgs.Empty);
  67. }
  68. }
  69. #region ITransportLayerManager Members
  70. public bool Connected { get; private set; }
  71. public event EventHandler OnIdleTimeout;
  72. public event EventHandler Disconnected;
  73. public void StartIdleTimeout(TimeSpan idleTimeout)
  74. {
  75. // create a new timer to timeout idle connections
  76. _idleTimeout = new Timer(
  77. new TimerCallback(IdleTimeoutCallback),
  78. null,
  79. idleTimeout,
  80. TimeSpan.FromMilliseconds(-1));
  81. }
  82. public void Process(Stream stream)
  83. {
  84. Connected = true;
  85. _stream = stream;
  86. _clientEndPoint = ClientSocket.RemoteEndPoint;
  87. ChangeState(TransportLayerState.Connected);
  88. while (Connected)
  89. {
  90. _state.Process(this);
  91. }
  92. }
  93. public void Reject(Stream stream)
  94. {
  95. Connected = true;
  96. _stream = stream;
  97. _clientEndPoint = ClientSocket.RemoteEndPoint;
  98. ChangeState(TransportLayerState.Connected);
  99. Disconnect(DisconnectReason.TooManyConnections);
  100. }
  101. public void Disconnect(DisconnectReason reason)
  102. {
  103. log.Debug("Disconnecting " + _clientEndPoint + " with " + reason + ".");
  104. DisconnectPacket disconnect = new DisconnectPacket();
  105. disconnect.DisconnectReason = reason;
  106. // TODO: Get proper description and language tag
  107. disconnect.Description = string.Empty;
  108. disconnect.LanguageTag = string.Empty;
  109. WritePacket(disconnect);
  110. Disconnect();
  111. }
  112. public void DisconnectReceived(DisconnectPacket packet)
  113. {
  114. log.Debug("Disconnect received from " + _clientEndPoint + ".");
  115. Disconnect();
  116. }
  117. private void Disconnect()
  118. {
  119. Connected = false;
  120. if (Disconnected != null)
  121. {
  122. Disconnected(this, new EventArgs());
  123. }
  124. if (_idleTimeout != null)
  125. {
  126. _idleTimeout.Dispose();
  127. _idleTimeout = null;
  128. }
  129. if (_stream != null)
  130. {
  131. _stream.Close();
  132. }
  133. try
  134. {
  135. ClientSocket.Shutdown(SocketShutdown.Both);
  136. ClientSocket.Close();
  137. }
  138. catch (Exception excp)
  139. {
  140. log.Debug("Exception closing client socket. " + excp.Message);
  141. }
  142. }
  143. /// <summary>
  144. /// Writes data directly to the network stream. This is only used to exchange the SSH version
  145. /// strings which are the first packets in the intialisation sequence.
  146. /// </summary>
  147. /// <param name="data">The data to write to the stream.</param>
  148. public void Write(byte[] data)
  149. {
  150. try
  151. {
  152. _stream.Write(data);
  153. _stream.Flush();
  154. }
  155. catch (Exception excp)
  156. {
  157. StreamExceptionHandler(excp);
  158. }
  159. }
  160. /// <summary>
  161. /// This method is used to only read from the network stream up until and including the location
  162. /// of the first CR LF sequence. The method is only used to read the version string from the remote
  163. /// end which will always be the first packet in the exchange. RFC 4253 chapter
  164. /// "4.2. Protocol Version Exchange" specifies that the version strings must be ASCII encoded.
  165. /// </summary>
  166. /// <returns>An ASCII representation of the bytes read NOT including the CR LF.</returns>
  167. public string ReadLine()
  168. {
  169. try
  170. {
  171. bool eol = false;
  172. byte[] buffer = new byte[VERSION_STRING_MAX_LENGTH];
  173. int index = 0;
  174. while (!eol && index < VERSION_STRING_MAX_LENGTH)
  175. {
  176. buffer[index] = (byte)_stream.ReadByte();
  177. // Detect the end of line.
  178. if (index > 0 && buffer[index - 1] == 0x0d && buffer[index] == 0x0a)
  179. {
  180. return Encoding.ASCII.GetString(buffer, 0, index - 1);
  181. }
  182. else
  183. {
  184. index++;
  185. }
  186. }
  187. throw new ApplicationException("The end of line was not detected on the version string.");
  188. }
  189. catch (Exception excp)
  190. {
  191. StreamExceptionHandler(excp);
  192. return null;
  193. }
  194. }
  195. public void WritePacket(Packet packet)
  196. {
  197. try
  198. {
  199. //log.Debug("WRITE PACKET: " + packet);
  200. _stream.Write(packet.ToByteArray(_transmitTransform, TransmitMac, TransmitSequenceNumber++));
  201. _stream.Flush();
  202. }
  203. catch (Exception excp)
  204. {
  205. StreamExceptionHandler(excp);
  206. }
  207. }
  208. public Packet ReadPacket()
  209. {
  210. try
  211. {
  212. Packet result;
  213. result = _packetFactory.ReadFrom(_stream, _receiveTransform, ReceiveMac, ReceiveSequenceNumber++);
  214. //log.Debug("READ PACKET: " + result);
  215. if (AuthenticatedIdentity != null && Connected)
  216. {
  217. _idleTimeout.Change(Dependency.Resolve<NSshServiceConfiguration>().IdleTimeout, TimeSpan.FromMilliseconds(-1));
  218. }
  219. return result;
  220. }
  221. catch (Exception excp)
  222. {
  223. StreamExceptionHandler(excp);
  224. return null;
  225. }
  226. }
  227. private void StreamExceptionHandler(Exception excp)
  228. {
  229. if (excp is ObjectDisposedException)
  230. {
  231. log.Debug("Stream was disconnected with ObjectDisposedException.");
  232. if (Connected)
  233. {
  234. Disconnect();
  235. }
  236. return;
  237. }
  238. else if (excp is EndOfStreamException)
  239. {
  240. log.Debug("Stream was disconnected by EndOfStreamException.");
  241. if (Connected)
  242. {
  243. Disconnect();
  244. }
  245. return;
  246. }
  247. else if (excp is IOException)
  248. {
  249. log.Debug("Stream was disconnected by SocketException.");
  250. if (Connected)
  251. {
  252. Disconnect();
  253. }
  254. return;
  255. }
  256. throw excp;
  257. }
  258. public Socket ClientSocket { get; set; }
  259. public string ClientVersion { get; set; }
  260. public string ServerVersion { get; set; }
  261. public void ChangeState(TransportLayerState newState)
  262. {
  263. log.Debug("Changing state to " + newState + ".");
  264. if (newState == TransportLayerState.VersionsExchanged)
  265. {
  266. TimeSpan idleTimeout = Dependency.Resolve<NSshServiceConfiguration>().AuthenticatedTimeout;
  267. _idleTimeout.Change(idleTimeout, TimeSpan.FromMilliseconds(-1));
  268. }
  269. else if (newState == TransportLayerState.Authenticated)
  270. {
  271. TimeSpan idleTimeout = Dependency.Resolve<NSshServiceConfiguration>().IdleTimeout;
  272. _idleTimeout.Change(idleTimeout, TimeSpan.FromMilliseconds(-1));
  273. }
  274. string stateKey = newState.ToString();
  275. _state = Dependency.Resolve<AbstractTransportState>(stateKey);
  276. }
  277. public TransportLayerParameters Parameters { get; set; }
  278. public KexInitPacket ClientKexInit { get; set; }
  279. public KexInitPacket ServerKexInit { get; set; }
  280. public BigInteger E { get; set; }
  281. public BigInteger X { get; set; }
  282. public BigInteger Key { get; set; }
  283. public byte[] Hash { get; set; }
  284. public byte[] SessionId { get; set; }
  285. public object CommunicationLock { get; set; }
  286. public uint TransmitSequenceNumber { get; set; }
  287. public uint ReceiveSequenceNumber { get; set; }
  288. private ICryptoTransform _transmitTransform;
  289. private SymmetricAlgorithm _transmitCipher;
  290. public SymmetricAlgorithm TransmitCipher
  291. {
  292. get { return _transmitCipher; }
  293. set
  294. {
  295. _transmitCipher = value;
  296. _transmitTransform = _transmitCipher.CreateEncryptor();
  297. }
  298. }
  299. private ICryptoTransform _receiveTransform;
  300. private SymmetricAlgorithm _receiveCipher;
  301. public SymmetricAlgorithm ReceiveCipher
  302. {
  303. get { return _receiveCipher; }
  304. set
  305. {
  306. _receiveCipher = value;
  307. _receiveTransform = _receiveCipher.CreateDecryptor();
  308. }
  309. }
  310. public HashAlgorithm TransmitMac { get; set; }
  311. public HashAlgorithm ReceiveMac { get; set; }
  312. public IIdentity AuthenticatedIdentity { get; set; }
  313. public string Password { get; set; }
  314. #endregion
  315. #region IDisposable Members
  316. public void Dispose()
  317. {
  318. GC.SuppressFinalize(this);
  319. Dispose(true);
  320. }
  321. private void Dispose(bool disposing)
  322. {
  323. if (disposing)
  324. {
  325. if (_state != null) _state.Dispose();
  326. }
  327. }
  328. #endregion
  329. }
  330. }