SshServer.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.Contracts;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. namespace FxSsh
  9. {
  10. public class SshServer : IDisposable
  11. {
  12. private readonly object _lock = new object();
  13. private readonly List<Session> _sessions = new List<Session>();
  14. private readonly Dictionary<string, string> _hostKey = new Dictionary<string, string>();
  15. private bool _isDisposed;
  16. private bool _started;
  17. private TcpListener _listenser = null;
  18. public SshServer()
  19. : this(new StartingInfo())
  20. { }
  21. public SshServer(StartingInfo info)
  22. {
  23. Contract.Requires(info != null);
  24. StartingInfo = info;
  25. }
  26. public StartingInfo StartingInfo { get; private set; }
  27. public event EventHandler<Session> ConnectionAccepted;
  28. public event EventHandler<Exception> ExceptionRasied;
  29. public void Start()
  30. {
  31. lock (_lock)
  32. {
  33. CheckDisposed();
  34. if (_started)
  35. throw new InvalidOperationException("The server is already started.");
  36. _listenser = StartingInfo.LocalAddress == IPAddress.IPv6Any
  37. ? TcpListener.Create(StartingInfo.Port) // dual stack
  38. : new TcpListener(StartingInfo.LocalAddress, StartingInfo.Port);
  39. _listenser.ExclusiveAddressUse = false;
  40. _listenser.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
  41. _listenser.Start();
  42. BeginAcceptSocket();
  43. _started = true;
  44. }
  45. }
  46. public void Stop()
  47. {
  48. lock (_lock)
  49. {
  50. CheckDisposed();
  51. if (!_started)
  52. throw new InvalidOperationException("The server is not started.");
  53. _listenser.Stop();
  54. _isDisposed = true;
  55. _started = false;
  56. foreach (var session in _sessions.ToArray())
  57. {
  58. try
  59. {
  60. session.Disconnect();
  61. }
  62. catch
  63. {
  64. }
  65. }
  66. }
  67. }
  68. public void AddHostKey(string type, string xml)
  69. {
  70. Contract.Requires(type != null);
  71. Contract.Requires(xml != null);
  72. if (!_hostKey.ContainsKey(type))
  73. _hostKey.Add(type, xml);
  74. }
  75. private void BeginAcceptSocket()
  76. {
  77. try
  78. {
  79. _listenser.BeginAcceptSocket(AcceptSocket, null);
  80. }
  81. catch (ObjectDisposedException)
  82. {
  83. return;
  84. }
  85. catch
  86. {
  87. if (_started)
  88. BeginAcceptSocket();
  89. }
  90. }
  91. private void AcceptSocket(IAsyncResult ar)
  92. {
  93. try
  94. {
  95. var socket = _listenser.EndAcceptSocket(ar);
  96. Task.Run(() =>
  97. {
  98. var session = new Session(socket, _hostKey, StartingInfo.ServerBanner);
  99. session.Disconnected += (ss, ee) =>
  100. {
  101. lock (_lock) _sessions.Remove(session);
  102. };
  103. lock (_lock)
  104. _sessions.Add(session);
  105. try
  106. {
  107. if (ConnectionAccepted != null)
  108. ConnectionAccepted(this, session);
  109. session.EstablishConnection();
  110. }
  111. catch (SshConnectionException ex)
  112. {
  113. session.Disconnect(ex.DisconnectReason, ex.Message);
  114. if (ExceptionRasied != null)
  115. ExceptionRasied(this, ex);
  116. }
  117. catch (Exception ex)
  118. {
  119. session.Disconnect();
  120. if (ExceptionRasied != null)
  121. ExceptionRasied(this, ex);
  122. }
  123. });
  124. }
  125. catch
  126. {
  127. }
  128. finally
  129. {
  130. BeginAcceptSocket();
  131. }
  132. }
  133. private void CheckDisposed()
  134. {
  135. if (_isDisposed)
  136. throw new ObjectDisposedException(GetType().FullName);
  137. }
  138. #region IDisposable
  139. public void Dispose()
  140. {
  141. lock (_lock)
  142. {
  143. if (_isDisposed)
  144. return;
  145. Stop();
  146. }
  147. }
  148. #endregion
  149. }
  150. }