using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; namespace mooftpserv { /// /// Main FTP server class. Manages the server socket, creates sessions. /// Can be used to configure the server. /// public class Server { // default buffer size for send/receive buffers private const int DEFAULT_BUFFER_SIZE = 64 * 1024; // default port for the server socket private const int DEFAULT_PORT = 21; private IPEndPoint endpoint; private int bufferSize = DEFAULT_BUFFER_SIZE; private IAuthHandler authHandler = null; private IFileSystemHandler fsHandler = null; private ILogHandler logHandler = null; private TcpListener socket = null; private List sessions; public Server() { this.endpoint = new IPEndPoint(GetDefaultAddress(), DEFAULT_PORT); this.sessions = new List(); } /// /// Gets or sets the local end point on which the server will listen. /// Has to be an IPv4 endpoint. /// The default value is IPAdress.Any and port 21, except on WinCE, /// where the first non-loopback IPv4 address will be used. /// public IPEndPoint LocalEndPoint { get { return endpoint; } set { endpoint = value; } } /// /// Gets or sets the local IP address on which the server will listen. /// Has to be an IPv4 address. /// If none is set, IPAddress.Any will be used, except on WinCE, /// where the first non-loopback IPv4 address will be used. /// public IPAddress LocalAddress { get { return endpoint.Address; } set { endpoint.Address = value; } } /// /// Gets or sets the local port on which the server will listen. /// The default value is 21. Note that on Linux, only root can open ports < 1024. /// public int LocalPort { get { return endpoint.Port; } set { endpoint.Port = value; } } /// /// Gets or sets the size of the send/receive buffer to be used by each session/connection. /// The default value is 64k. /// public int BufferSize { get { return bufferSize; } set { bufferSize = value; } } /// /// Gets or sets the auth handler that is used to check user credentials. /// If none is set, a DefaultAuthHandler will be created when the server starts. /// public IAuthHandler AuthHandler { get { return authHandler; } set { authHandler = value; } } /// /// Gets or sets the file system handler that implements file system access for FTP commands. /// If none is set, a DefaultFileSystemHandler is created when the server starts. /// public IFileSystemHandler FileSystemHandler { get { return fsHandler; } set { fsHandler = value; } } /// /// Gets or sets the log handler. Can be null to disable logging. /// The default value is null. /// public ILogHandler LogHandler { get { return logHandler; } set { logHandler = value; } } /// /// Run the server. The method will not return until Stop() is called. /// public void Run() { //if (authHandler == null) // authHandler = new DefaultAuthHandler(); //if (fsHandler == null) // fsHandler = new DefaultFileSystemHandler(); if (socket == null) socket = new TcpListener(endpoint); socket.Start(); // listen for new connections try { while (true) { Socket peer = socket.AcceptSocket(); IPEndPoint peerPort = (IPEndPoint) peer.RemoteEndPoint; Session session = new Session(peer, bufferSize, authHandler.Clone(peerPort), fsHandler.Clone(peerPort), logHandler.Clone(peerPort)); session.Start(); sessions.Add(session); // purge old sessions for (int i = sessions.Count - 1; i >= 0; --i) { if (!sessions[i].IsOpen) { sessions.RemoveAt(i); --i; } } } } catch (SocketException) { // ignore, Stop() will probably cause this exception } finally { // close all running connections foreach (Session s in sessions) { s.Stop(); } } } /// /// Stop the server. /// public void Stop() { if (socket == null) return; socket.Stop(); } /// /// Get the default address, which is IPAddress.Any everywhere except on WinCE, /// where all local addresses are enumerated and the first non-loopback IP is used. /// private IPAddress GetDefaultAddress() { // on WinCE, 0.0.0.0 does not work because for accepted sockets, // LocalEndPoint would also say 0.0.0.0 instead of the real IP #if WindowsCE IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName()); IPAddress bindIp = IPAddress.Loopback; foreach (IPAddress ip in host.AddressList) { if (ip.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(ip)) { return ip; } } return IPAddress.Loopback; #else return IPAddress.Any; #endif } } }