FluentFTP icon indicating copy to clipboard operation
FluentFTP copied to clipboard

issue with Active Mode, cannot reuse the ActivePorts, then throw exception

Open DoraemonYu opened this issue 2 years ago • 8 comments

FluentFTP Version: reproduced on v40/v39/v38 Framework: reproduced on .NET4 and .NET6 FTP Server: reproduced on build-in FTP From Microsoft IIS and FileZilla Server FTP Client OS: reproduced on Microsoft Window 7 and Ubuntu 18.04 FTP Server OS: reproduced on Microsoft Windows Server 2016


Dear team,
Hello. Passive Mode is used in many situations, as far as we know. However, in the recent projects, we have to use the Acive Mode and limit the usage with ports rang.

Simple source code to reproduce this issue:

var ftpClient = new FluentFTP.FtpClient();
ftpClient.Host = "[[replace your ftp server ip here pls]]";
ftpClient.Port = 21;
ftpClient.Config.DataConnectionType = FluentFTP.FtpDataConnectionType.PORT;
ftpClient.Config.ActivePorts = new int[] { 8082 };

ftpClient.UploadFile(@"D:\0.jpg", "/0.jpg", FluentFTP.FtpRemoteExists.Overwrite, true);
ftpClient.UploadFile(@"D:\1.jpg", "/1.jpg", FluentFTP.FtpRemoteExists.Overwrite, true);
ftpClient.UploadFile(@"D:\2.jpg", "/2.jpg", FluentFTP.FtpRemoteExists.Overwrite, true);

Only the 0.jpg would be uploaded successfully. The subsequent files would throw the excpetion:

No valid active data port available!

This exception was originally thrown at this call stack:
    FluentFTP.FtpClient.StartListeningOnPort(FluentFTP.FtpDataStream)
    FluentFTP.FtpClient.OpenActiveDataStream(FluentFTP.FtpDataConnectionType, string, long)
    FluentFTP.FtpClient.OpenDataStream(string, long)
    FluentFTP.FtpClient.OpenWrite(string, FluentFTP.FtpDataType, long)

Logs from FluentFTP:

# Connect()
Status:   Connecting to ***:21
Response: 220-FileZilla Server 0.9.60 beta
Response: 220-written by Tim Kosse ([email protected])
Response: 220 Please visit https://filezilla-project.org/
Status:   Detected FTP server: FileZilla
Command:  USER ***
Response: 331 Password required for ***
Command:  PASS ***
Response: 230 Logged on
Command:  FEAT
Response: 211-Features:
Response: MDTM
Response: REST STREAM
Response: SIZE
Response: MLST type*;size*;modify*;
Response: MLSD
Response: UTF8
Response: CLNT
Response: MFMT
Response: EPSV
Response: EPRT
Response: 211 End
Status:   Text encoding: System.Text.UTF8Encoding+UTF8EncodingSealed
Command:  OPTS UTF8 ON
Response: 202 UTF8 mode is always enabled. No need to send this command.
Command:  SYST
Response: 215 UNIX emulated by FileZilla
Status:   Listing parser set to: Machine

# UploadFile("D:\0.jpg", "/0.jpg", Overwrite, True, None)

# FileExists("/0.jpg")
Command:  SIZE /0.jpg
Response: 550 File not found

# DirectoryExists("/")

# OpenWrite("/0.jpg", Binary)

# GetFileSize("/0.jpg")
Command:  SIZE /0.jpg
Response: 550 File not found
Command:  TYPE I
Response: 200 Type set to I

# OpenActiveDataStream(PORT, "STOR /0.jpg", 0)
Command:  PORT 127,0,0,1,31,146
Response: 200 Port command successful
Command:  STOR /0.jpg
Response: 150 Opening data channel for file upload to server of "/0.jpg"
Status:   Disposing FtpSocketStream...
Response: 226 Successfully transferred "/0.jpg"

# UploadFile("D:\1.jpg", "/1.jpg", Overwrite, True, None)

# FileExists("/1.jpg")
Command:  SIZE /1.jpg
Response: 550 File not found

# DirectoryExists("/")

# OpenWrite("/1.jpg", Binary)

# GetFileSize("/1.jpg")
Command:  SIZE /1.jpg
Response: 550 File not found

# OpenActiveDataStream(PORT, "STOR /1.jpg", 0)
Unhandled exception. FluentFTP.FtpException: Error while uploading the file to the server. See InnerException for more info.
 ---> System.Exception: No valid active data port available!

Then, if we change the code (add one more port) From Config.ActivePorts = new int[] { 8082 }; To Config.ActivePorts = new int[] { 8082, 8081 }; and restart the application, the first file 0.jpg and second file 1.jpg would be uploaded successfully but subsequent files.

Therefore, we can call UploadFile times as count(Config.ActivePorts) only.

As you see, each time we call UploadFile method, FluentFTP would bind and listen to a new port. Due to cannot reuse old one , and then the ports set by the Config.ActivePorts will be used up soon.


What other things that we've tried already and have no luck:

  1. change Config.SocketKeepAlive
  2. change Config.DisconnectWithShutdown
  3. change Config.DisconnectWithQuit
  4. call Disconnect after UploadFile and Connect again
  5. call Disconnect and Dispose after UploadFile and recreate and reconnect again
  6. try to add code m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); to FtpSocketStream::Listen method.

Looking forward your reply and ideas. Thanks so much :)

DoraemonYu avatar Sep 16 '22 02:09 DoraemonYu

Hi, thanks for this excellent and detailed bug report. We will look into it.

robinrodricks avatar Sep 16 '22 14:09 robinrodricks

I have reproduced this and can confirm this behaviour.

It seems that the sockets in the selection loop are not being selected because they are still in a listening state, I need to do a netstat to confirm this... Yes, they remain in LISTEN.

FanDjango avatar Sep 18 '22 15:09 FanDjango

Looks to me like on a Listen - Accept sequence, the bound listening socket is overwritten by the acceptance socket, thereby "leaving it hanging", still listening. Subsequent close/dispose actions will get rid of the data connection (the "accepted socket"), but not the listening socket.

FanDjango avatar Sep 18 '22 19:09 FanDjango

I have put up a PR #957 that fixes this - perhaps @DoraemonYu you can find some way to test this, before it is released by @robinrodricks for everyone?

FanDjango avatar Sep 19 '22 12:09 FanDjango

Dear @FanDjango and @robinrodricks , Thanks for your contribution and reply.

I download the newest code from trunk, because I saw it was merged, then recompile and make retest.

As result,

  • [X] It is now fixed with .Net Framework, I confirm it under .net 4.6.2. Weee!!
  • [ ] However, it still does NOT work with .Net Core, I confirm it under .net 6.

More informations with under .Net Core which might be helpful for debugging.

  • First of all, I double check the codes that I downloaded, and confirm that #957 is included.
  • I found that FtpSocketStream::Accept() and FtpSocketStream::EndAccept(), that modified by #957 ,would never be called when running under .Net Core, thereby make no effect for this bug seemingly.
  • I set breakpoints for above methods, and them would not be hit. Then I double check via find all references feature from Visual Studio and show no result as well.
  • By the way, the code for reproduce is same as before.

Could you double check again pls, thanks so much.

DoraemonYu avatar Sep 20 '22 01:09 DoraemonYu

Thanks once again for the excellent diagnosis.

I found the place: private void CheckResult(SocketAsyncEventArgs args) {...}

Added the needed .Close() and tested it with .NET 6.

Once it is merged, you can test it too.

FanDjango avatar Sep 20 '22 09:09 FanDjango

It is merged.

FanDjango avatar Sep 20 '22 12:09 FanDjango

Dear @FanDjango and @robinrodricks , It now work under both .Net Framewrok and .Net Core runtime. Cheer!

Thanks again for you && Have a good day :)

DoraemonYu avatar Sep 21 '22 00:09 DoraemonYu