FubarDevelopment / FtpServer

Portable FTP server written in .NET
http://fubardevelopment.github.io/FtpServer/
MIT License
473 stars 161 forks source link

Server claims not logged in with EnableAnonymous #101

Open martijnhoekstra opened 4 years ago

martijnhoekstra commented 4 years ago

I'm trying to set up (what I believe to be) a minimal ftp server in a test, and connecting to it like so:

[TestClass]
public class FTPTest {
    private const string localhost = "127.0.0.1";
    private const string ftpdir = "TestFTPServer";
    private string Basedir => Path.Combine(Path.GetTempPath(), ftpdir);

    private Task<ServiceProvider> sp;
    private Task<IFtpServerHost> host;

    private Task Initialized => Task.WhenAll(sp, host);

    [TestInitialize]
    public void StartInitialization() {
        var pair = InitializeFTP(Basedir);
        var tsp = new TaskCompletionSource<ServiceProvider>();
        var thost = new TaskCompletionSource<IFtpServerHost>();
        pair.ContinueWith(t => {
            var (csp, chost) = t.Result;
            tsp.TrySetResult(csp);
            thost.TrySetResult(chost);
        });
        sp = tsp.Task;
        host = thost.Task;
    }
    [TestCleanup]
    public void Cleanup() {
        var mysp = sp;
        var myhost = host;
        sp = null;
        host = null;
        try
        {
            Directory.Delete(Basedir, true);
        }
        catch (Exception) { }
        try
        {
            var t = myhost.Result.StopAsync();
            t.Wait();
        }
        catch (Exception) { }
        try {
            mysp.Result.Dispose();
        } catch (Exception) { }
    }
    private static async Task<(ServiceProvider, IFtpServerHost)> InitializeFTP(string basedir) {
        var serviceProvider = new ServiceCollection()
            .Configure<DotNetFileSystemOptions>(opt => opt.RootPath = basedir)
            .AddFtpServer(builder => builder
                .UseDotNetFileSystem() // Use the .NET file system functionality
                .EnableAnonymousAuthentication())
            .Configure<FtpServerOptions>(opt => opt.ServerAddress = localhost)
            .BuildServiceProvider();

        var ftpServerHost = serviceProvider.GetRequiredService<IFtpServerHost>();
        await ftpServerHost.StartAsync();
        return (serviceProvider, ftpServerHost);
    }

    [TestMethod]
    public async Task CheckFTPServer() {
        var ftpRequest = (FtpWebRequest)WebRequest.Create(new Uri("ftp://127.0.0.1/myfilename"));
       //both with and without the following line
        ftpRequest.Credentials = new NetworkCredential("anonymous", "anonymous");
        ftpRequest.Method = WebRequestMethods.Ftp.UploadFile;
        var requestStream = await ftpRequest.GetRequestStreamAsync();
    }

}

The following test throws an exception:

Test method CargoQualityCenter.CDMP.FTPTest.FTPTest.CheckFTPServer threw exception: 
System.Net.WebException: The remote server returned an error: (530) Not logged in.

STDOut is

Debug Trace:
NGettext: Creating a built-in plural rule for langcode "en" for locale "en".
NGettext: Translation not found for message id "FTP Server Ready".
NGettext: Translation not found for message id "User {0} logged in, needs password".
NGettext: Translation not found for message id "Username or password incorrect".

I'm not sure how I should be logging in to the server instead.

timvandenhof commented 4 years ago

Hi Martijn, I'm facing the exact same issue after upgrading from a very old version. Have you been able to resolve this issue?

Been able to reproduce this on 3.1.1 and 3.1.2-beta.5.

Edit: This not a bug, it's documented here: http://fubardevelopment.github.io/FtpServer/articles/authentication.html

You have to register an IAnonymousPasswordValidator.

services.AddSingleton<IAnonymousPasswordValidator>(new NoValidation()); 

As per RFC1635, the username should be 'anonymous' with any password. The code above specifies NoValidation for anonymous authentication. I think It would be better if the FubarFtp server uses this configuration as a default.