123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
- using System.Net;
- using System.Net.Sockets;
- using System.Threading;
- using System.Threading.Tasks;
- namespace FxSsh
- {
- public class SshServer : IDisposable
- {
- private readonly object _lock = new object();
- private readonly List<Session> _sessions = new List<Session>();
- private readonly Dictionary<string, string> _hostKey = new Dictionary<string, string>();
- private bool _isDisposed;
- private bool _started;
- private TcpListener _listenser = null;
- public SshServer()
- : this(new StartingInfo())
- { }
- public SshServer(StartingInfo info)
- {
- Contract.Requires(info != null);
- StartingInfo = info;
- }
- public StartingInfo StartingInfo { get; private set; }
- public event EventHandler<Session> ConnectionAccepted;
- public event EventHandler<Exception> ExceptionRasied;
- public void Start()
- {
- lock (_lock)
- {
- CheckDisposed();
- if (_started)
- throw new InvalidOperationException("The server is already started.");
- _listenser = StartingInfo.LocalAddress == IPAddress.IPv6Any
- ? TcpListener.Create(StartingInfo.Port) // dual stack
- : new TcpListener(StartingInfo.LocalAddress, StartingInfo.Port);
- _listenser.ExclusiveAddressUse = false;
- _listenser.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
- _listenser.Start();
- BeginAcceptSocket();
- _started = true;
- }
- }
- public void Stop()
- {
- lock (_lock)
- {
- CheckDisposed();
- if (!_started)
- throw new InvalidOperationException("The server is not started.");
- _listenser.Stop();
- _isDisposed = true;
- _started = false;
- foreach (var session in _sessions.ToArray())
- {
- try
- {
- session.Disconnect();
- }
- catch
- {
- }
- }
- }
- }
- public void AddHostKey(string type, string xml)
- {
- Contract.Requires(type != null);
- Contract.Requires(xml != null);
- if (!_hostKey.ContainsKey(type))
- _hostKey.Add(type, xml);
- }
- private void BeginAcceptSocket()
- {
- try
- {
- _listenser.BeginAcceptSocket(AcceptSocket, null);
- }
- catch (ObjectDisposedException)
- {
- return;
- }
- catch
- {
- if (_started)
- BeginAcceptSocket();
- }
- }
- private void AcceptSocket(IAsyncResult ar)
- {
- try
- {
- var socket = _listenser.EndAcceptSocket(ar);
- Task.Run(() =>
- {
- var session = new Session(socket, _hostKey, StartingInfo.ServerBanner);
- session.Disconnected += (ss, ee) =>
- {
- lock (_lock) _sessions.Remove(session);
- };
- lock (_lock)
- _sessions.Add(session);
- try
- {
- if (ConnectionAccepted != null)
- ConnectionAccepted(this, session);
- session.EstablishConnection();
- }
- catch (SshConnectionException ex)
- {
- session.Disconnect(ex.DisconnectReason, ex.Message);
- if (ExceptionRasied != null)
- ExceptionRasied(this, ex);
- }
- catch (Exception ex)
- {
- session.Disconnect();
- if (ExceptionRasied != null)
- ExceptionRasied(this, ex);
- }
- });
- }
- catch
- {
- }
- finally
- {
- BeginAcceptSocket();
- }
- }
- private void CheckDisposed()
- {
- if (_isDisposed)
- throw new ObjectDisposedException(GetType().FullName);
- }
- #region IDisposable
- public void Dispose()
- {
- lock (_lock)
- {
- if (_isDisposed)
- return;
- Stop();
- }
- }
- #endregion
- }
- }
|