/// <summary> /// Add context to the right listener /// share the same prefix /// the longer prefix wins /// </summary> /// <param name="context"></param> internal void AddContext(FtpListenerContext context) { // Decide which listen should this context added to int prefixLength = -1; FtpListener candidateListener = null; foreach (FtpListener listener in m_Listeners) { if (listener.IsListening) { int length = listener.CheckPrefix(context.Request.QueryString); if (length > prefixLength) { candidateListener = listener; prefixLength = length; } } } if (candidateListener != null) { candidateListener.AddContext(context); } else { if (m_DefaultListener == null) { m_DefaultListener = new FtpDefaultListener(); m_DefaultListener.Start(); } m_DefaultListener.AddContext(context); } }
/// <summary> /// Private method to remove a listener to the listener manager /// </summary> /// <param name="listener"></param> private void RemoveLis(FtpListener listener) { m_Listeners.Remove(listener); if (m_Listeners.Count == 0) { // not listeners, stop the listener manager this.Stop(); } }
/// <summary> /// Start FTP Proxy /// </summary> private void StartFtpService() { try { ftpListener = new FtpListener(settings.FtpProxyPort.Value); ftpListener.Start(); } catch (Exception ex) { Log.Write(MethodBase.GetCurrentMethod(), ex); } }
/// <summary> /// Private method to add a listener to the listener manager /// </summary> /// <param name="listener"></param> private void AddLis(FtpListener listener) { m_Listeners.Add(listener); }
/// <summary> /// Static method to remove listener /// </summary> /// <param name="listener"></param> internal static void RemoveListener(FtpListener listener) { FtpListenerManager manager = GetFtpManager(); manager.RemoveLis(listener); }
/// <summary> /// Static method to add listener /// </summary> /// <param name="listener"></param> internal static void AddListener(FtpListener listener) { FtpListenerManager manager = GetFtpManager(); manager.AddLis(listener); }
private static void ProcessClientRequest() { Debug.Print("Welcome to the FTP Service."); FtpListener.AuthenticationEvent += (object sender, UserAuthenticatorArgs e) => { if (e.User == "anonymous") { Debug.Print("hi anonymous"); e.Result = UserAuthenticationResult.Approved; } }; // a custom listener will be looking after the "special" directory in the ROOT volumes only FtpListener listener = new FtpListener(); listener.Prefixes.Add(virtualROOT + "special/"); listener.Start(); // all other directories will be handled by the standard listener, on both volumes FtpListener listener_root = new FtpFilesystemListener(virtualROOT, @"\ROOT\"); listener_root.Start(); FtpListener listener_winfs = new FtpFilesystemListener(virtualWINFS, @"\WINFS\"); listener_winfs.Start(); for (; ;) { FtpListenerContext context = listener.GetContext(); Stream stream = context.Response.OutputStream; switch (context.Request.Method) { case WebRequestMethodsEx.Ftp.ChangeDirectory: string path = context.Request.QueryString.Substring(virtualROOT.Length); int markerIdx = path.IndexOf('/'); if (markerIdx != -1) { path = path.Substring(0, markerIdx); string dir = @"\ROOT\" + path; if (Directory.Exists(dir) && monitoredDirectory == dir) { Debug.Print("Changed to the monitored directory"); context.Response.StatusCode = FtpStatusCode.FileActionOK; } else { context.Response.StatusCode = FtpStatusCode.ActionNotTakenFileUnavailable; } } else { Directory.SetCurrentDirectory(@"\ROOT"); context.Response.StatusCode = FtpStatusCode.FileActionOK; } stream.Close(); break; case WebRequestMethods.Ftp.ListDirectory: case WebRequestMethods.Ftp.ListDirectoryDetails: { DirectoryInfo cd = new DirectoryInfo(monitoredDirectory); foreach (FileInfo fi in cd.GetFiles()) { (stream as FtpResponseStream).Write(fi); } foreach (DirectoryInfo di in cd.GetDirectories()) { (stream as FtpResponseStream).Write(di); } context.Response.StatusCode = FtpStatusCode.ClosingData; stream.Close(); } break; case WebRequestMethods.Ftp.DownloadFile: DirectoryInfo info = new DirectoryInfo(monitoredDirectory); string prefix = virtualROOT + info.Name + "/"; string download = context.Request.QueryString.Substring(prefix.Length); string file = monitoredDirectory + @"\" + download; if (!File.Exists(file)) { throw new Exception("File does not exists!"); } using (FileStream fs = new FileStream(file, FileMode.Open)) { (stream as FtpResponseStream).Write(fs); context.Response.StatusCode = FtpStatusCode.ClosingData; stream.Close(); } break; default: context.Response.StatusCode = FtpStatusCode.CommandNotImplemented; stream.Close(); break; } } }
/// <summary> /// Control thread /// TODO: separate the functionalities into functions to reduce maintenance cost /// </summary> private void WorkerThread() { FtpCommand command; FtpState state = FtpState.WaitUser; string tempName = null; // store old name for "rename from" command string response; int timeout = 0; try { while (m_IsAlive) { if (m_IsDisposed) { break; } else if (!m_Welcomed) { response = "220 MyFTP " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + " Server [" + m_HostIp.ToString() + "] \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); m_Welcomed = true; } else if (m_CommandSocket == null) { Logging.Print("Session socket has been closed."); break; } else if (m_CommandSocket.Poll(1000, SelectMode.SelectRead)) { if (m_CommandSocket.Available == 0) { Logging.Print("REMOTE DISCONNECT " + m_CommandSocket.RemoteEndPoint.ToString()); m_IsAlive = false; break; } // read until find the end of a line command = FtpCommandCreator.Create(ReadCommand()); if (command == null) { break; } else { switch (command.Type) { case FtpCommandType.User: response = "331 " + command.Content + " login ok \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); state = FtpState.WaitPwd; tempName = command.Content; break; case FtpCommandType.Pass: if (state != FtpState.WaitPwd) { response = "332 Need Account for Login.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else { UserAuthenticatorArgs args = new UserAuthenticatorArgs(tempName, command.Content); FtpListener.RaiseEvent(this, args); if (args.Result == UserAuthenticationResult.Approved) { User.UserName = tempName; User.PassWord = command.Content; response = "230 access granted for " + User.UserName + ", restrictions apply. \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); state = FtpState.WaitCommand; break; } else { response = "530 Login incorrect. \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); state = FtpState.WaitCommand; break; } } case FtpCommandType.Cwd: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitUser) { response = "332 Need Account for Login.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { if (command.Content == null) { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else { FtpListenerRequest request = new FtpListenerRequest(WebRequestMethodsEx.Ftp.ChangeDirectory, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Quit: m_IsAlive = false; CloseDataChannel(); response = "221 Goodbye.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); m_CommandSocket.Close(); m_CommandSocket = null; break; case FtpCommandType.Pasv: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { CloseDataChannel(); m_DataThread = new Thread(PassiveThread); m_DataThread.Start(); m_DataReadyEvent.WaitOne(); // wait until port has been successfully assigned byte[] addrs = m_HostIp.GetAddressBytes(); int upper = m_HostPort / 256; int lower = m_HostPort % 256; if (addrs.Length == 4) //IPv4 { response = "227 Entering Passive Mode ("; foreach (int i in addrs) { response += i.ToString() + ","; } response += upper.ToString() + "," + lower.ToString() + ")\r\n"; } else // currently do not support IPv6 { throw new NotImplementedException("currently does not support IPv6"); } m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Type: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { response = "200 Command OK.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.List: if (!m_DataModeON) { response = "425 Use PORT or PASV first.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { response = "150 Opening UTF8 mode data connection for *** \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.ListDirectoryDetails, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.NList: if (!m_DataModeON) { response = "425 Use PORT or PASV first.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { response = "150 Opening UTF8 mode data connection for *** \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.ListDirectory, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Port: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand && command.Content != null) { CloseDataChannel(); string[] epad = command.Content.Split(new char[] { ',' }); if (epad.Length != 6) { response = "500 Invalid PORT command.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); } else { try { m_ClientIp = IPAddress.Parse(epad[0] + "." + epad[1] + "." + epad[2] + "." + epad[3]); m_ClientPort = Int32.Parse(epad[4]) * 256 + Int32.Parse(epad[5]); if (m_ClientPort <= 0 || m_ClientPort > 65535) { response = "500 Invalid PORT command.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); } else { m_ActThreadW = true; m_DataThread = new Thread(ActiveThread); m_DataThread.Start(); response = "200 PORT command successful.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); } } catch (Exception) { response = "500 Invalid PORT command.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); } } break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Sys: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { response = "215 System Type: EMIC\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Feature: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { response = "211-Features:\r\n SIZE\r\n211 End\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Pwd: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { response = "257 \"" + m_CurrentDirectory.GetNetPath() + "\" is the current directory. \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Retr: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { if (!m_DataModeON) { response = "425 Use PORT or PASV first.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } response = "150 Opening BINARY mode data connection for " + command.Content + ". \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.DownloadFile, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Opts: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { if (command.Content == "utf8") { response = "213 Always in utf8 mode \r\n"; } else { response = "550 Requested action not taken. Mode not support. \r\n"; } m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Size: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.GetFileSize, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Store: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { if (!m_DataModeON) { response = "425 Use PORT or PASV first.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } response = "150 Opening BINARY mode data connection for " + command.Content + ". \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.UploadFile, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Noop: response = "200 NOOP command successful. \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; case FtpCommandType.Delete: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.DeleteFile, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.MkDir: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.MakeDirectory, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Rmd: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.RemoveDirectory, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Rnfr: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { FtpListenerRequest request = new FtpListenerRequest(WebRequestMethodsEx.Ftp.RenameFrom, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } case FtpCommandType.Rnto: if (state == FtpState.WaitPwd) { response = "331 Need Password.\r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } else if (state == FtpState.WaitCommand) { FtpListenerRequest request = new FtpListenerRequest(WebRequestMethodsEx.Ftp.RenameTo, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this); FtpListenerContext context = new FtpListenerContext(this, request); m_SendContext.AddContext(context); break; } else { response = m_BadSequence; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } default: response = "502 Command not implemented. \r\n"; if (state == FtpState.WaitPwd) { state = FtpState.WaitUser; } m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); break; } } // reset time out timeout = 0; } else if (m_CommandSocket.Poll(1000, SelectMode.SelectError)) { Logging.Print("Disconnected unproperly."); break; } else { if (!m_Welcomed) { response = "220 MyFTP " + typeof(FtpListenerSession).Assembly.GetName().Version.ToString() + " Server [" + m_HostIp.ToString() + "] \r\n"; m_CommandSocket.Send(Encoding.UTF8.GetBytes(response)); m_Welcomed = true; } else { if (SessionTimeOut != -1) { if (timeout > SessionTimeOut) { m_IsAlive = false; Logging.Print("Connection time out."); break; } timeout += 50; } Thread.Sleep(50); } } } } catch (SocketException se) { Logging.Print("Control Socket Exception : " + se.ErrorCode.ToString()); } catch (IOException ioe) { Logging.Print("IOException: " + ioe.Message); } finally { m_IsAlive = false; if (m_CommandSocket != null) { m_CommandSocket.Close(); m_CommandSocket = null; } if (m_ListenSocket != null) { m_ListenSocket.Close(); m_CommandSocket = null; } Logging.Print("Client Disconnected."); } }