Repository navigation
Closed
Description
Hi,
I am reusing the same connection to download multiple files. I have NOOP enabled because I sometimes experience timeouts.
Look at this log
01-13 14:44:02.060 INFO 144 FtpLogger Response: 227 Entering Passive Mode (172,30,14,136,173,237)
01-13 14:44:02.062 INFO 144 FtpLogger Status: Connecting to 172.30.14.136:44525
01-13 14:44:02.067 INFO 144 FtpLogger Command: RETR 20211215_16.35.27-WD.log
01-13 14:44:02.124 INFO 144 FtpLogger Response: 125 Transfer starting BINARY mode for 20211215_16.35.27-WD.log.
01-13 14:44:03.580 DEBUG 144 FtpLogger Command: NOOP
01-13 14:44:03.585 DEBUG 144 FtpLogger Status: Disposing FtpSocketStream...
01-13 14:44:03.592 INFO 144 FtpLogger Response: 226 Transfer complete.
01-13 14:44:03.596 INFO 144 FtpLogger File Verification: PASS
01-13 14:44:03.598 DEBUG 144 FtpLogger > DownloadFile("C:\temp\logs\20210428_13.42.14-PO.log", "20210428_13.42.14-PO.log", Overwrite, OnlyChecksum)
01-13 14:44:03.607 DEBUG 144 FtpLogger > GetFileSize("20210428_13.42.14-PO.log")
01-13 14:44:03.613 DEBUG 144 FtpLogger > OpenRead("20210428_13.42.14-PO.log", Binary, 0, -1)
01-13 14:44:03.625 DEBUG 144 FtpLogger > OpenPassiveDataStream(PASV, "RETR 20210428_13.42.14-PO.log", 0)
01-13 14:44:03.636 INFO 144 FtpLogger Command: PASV
01-13 14:44:03.700 INFO 144 FtpLogger Response: 200 NOOP command successful.
01-13 14:44:03.899 DEBUG 144 FtpLogger > DownloadFile("C:\temp\logs\20211011_13.47.08-PO.log", "20211011_13.47.08-PO.log", Overwrite, OnlyChecksum)
01-13 14:44:03.914 DEBUG 144 FtpLogger > GetFileSize("20211011_13.47.08-PO.log")
01-13 14:44:03.931 DEBUG 144 FtpLogger > OpenRead("20211011_13.47.08-PO.log", Binary, 0, -1)
As you can see the NOOP response arrived very late and was probably treated as the response to the PASV command.
I see the following issues here.
- When NOOP is enabled we currently keep track of them by a boolean which means that if we send 3 of them, we lose that info. It's probably better to count the number of expected NOOP
- When looking for stale data we use the
AvailableDataon the socket, which does not wait, so like in this scenario no data was actually available even if we knew that a NOOP response should arrive. So I would not use the check stale data function here - I do not understand why the PASV listener is accepting the NOOP response. I mean, ok it's a success reply but then the PASV regex parsing should fail, but it doesn't.
Any thought?
Thanks
A
Activity
FanDjango commentedon Jan 16, 2022
@Adhara3
I am reusing the same connection to download multiple files. I have NOOP disabled because I have encountered the problems you have described (Stale Data problems). Since I do not need the NOOPs up to now I have not delved deeper in to the subject you describe, but I certainly agree with your analysis.
Adhara3 commentedon Jan 22, 2022
@FanDjango
i first tried not to use them but I experienced some disconnections, so in a way they helped me improve. I will try to provide a PR with some proposals.
Cheers
A
FanDjango commentedon Sep 23, 2022
Why even track the NOOP responses? Why not just send them out into the void. If any
200 NOOP command successfulshould arrive, just ignore them, they have served their purpose.Tracking them would tell us something is wrong if the responses don't come, but then we would at the same time have a lot of other indicators something is hanging. So...
I could imagine just discarding the
200 NOOP command successfulresponses, keeping the context focused on the actual commands being responded to. It's sort of like OUT-OF-BAND data, put it somewhere else, in this case: wastebasket.Your thoughts?
FanDjango commentedon Sep 24, 2022
So, I have been doing some fairly extensive testing on this and up to now I have not been able to reproduce the same problem as described above, where a NOOP response is wrongly considered a response to a different command.
Even with a major stress test,
NoopIntervalset to only100, it works. You just get massive amounts of stale data200 NOOP command successfulafter the226 Transfer complete. See the log number 1 below.But using NOOPs does have serious caveats because:
Once I use encryption (see log number 2, below), things go very wrong: When the download finishes, the stale data (which is very probably
200 NOOP command successful, many of them) can't be decrypted. And for some reason encryption (actually decryption) is no longer possible on this connection, for any commands after the download.Program:
Log #1 (no encryption active):
Log #2 (encryption active):
FanDjango commentedon Sep 24, 2022
Two things:
ReadStaleData: By not reading the available bytes directly off the socket, but instead picking the bytes off the stream, the ssl decryption is not thrown out of its context. Some other (new) errors then ensue out of that and I will do some more digging.FanDjango commentedon Sep 25, 2022
I have added some more debug logging in some places. See the listing further below.
If using encryption, NOOP will work if
FTP_Sess.Config.SslBuffering = FtpsBuffering.OffEven then, any encrypted data shown as stale data in
ReadStaleData(...)will show up as gobbledigook. Not a real problem.If buffering is turned on, all sorts of errors appear when you try to make NOOP handling better by consuming the NOOPs, either in
GetReply(...)or inReadStaleData(...).The most frustrating thing debugging this, is the huge
try {block inDownloadFileInternal(...)which catches an I/O Exception which actually occurs inFtpSocketStream.csin theWrite(...)method.There is a very big probability that NOOPs and their responses work differently on diverse servers. Thus trying to find a unified way to handle NOOPs is a big requirement. Otherwise
GetReply(...)andReadStaleData(...)need to be given a home in the ServerHandler universe.Currently I think the only way to make NOOPs work when the SSL stream is buffered, is to have two streams on the same socket. One for writing, one for reading.
Note: The following (log of encrypted transfer, no buffering) contains extensive debugging messages not included in the normal code, and I have implemented a new
GetReplyandReadStaleData:FanDjango commentedon Sep 26, 2022
As a first step I have created PR #966. It will force disable SSL Buffering if a user explicitely wants NOOPs to be sent out.
If and when NOOPs are made to work with SSL Buffering with no problems, this can be undone.
FanDjango commentedon Sep 26, 2022
We are not there yet by a long way... with my new versions of
ReadStaleData(...)andGetReply(...)that I have not released to as a PR yet.Server: proftpd
Current status:
.NET Framework 4.7
Encryption OFF, .NET Framework 4.7: works
NOOP Sequence Behavior: 200,200,200,200,200,200,200,200,200,200,200,200,226
(200 = 200 NOOP command successful)
Encryption ON, .NET Framework 4.7: works because SSL Buffering is turned off, but with current master: sometimes fails.
NOOP Sequence Behavior: 200,200,200,200,200,200,200,200,200,200,200,200,226
(200 = 200 NOOP: data transfer in progress)
Note the difference in the response text plain vs. encrypt
.NET 6
Encryption OFF, .NET 6: works
NOOP Sequence Behavior: 226,200,200,200,200,200,200,200,200,200,200,200,200
(200 = 200 NOOP command successful)
Encryption ON, .NET 6, does not work even though SSL Buffering is turned off, symptoms similar to buffering is on
NOOP Sequence Behavior: 200 and then failure because the server is not sending all NOOP responses right away.
(200 = 200 NOOP command successful)
Currently if you need to reliably use NOOPs, you need to either turn off encryption for best results or try .NET Framework, which will sometimes glitch with the current master. Also, your experience will vary depending on the server type and its configuration.
I will concentrate on the new
GetReply(...)andReadStaleData(...)for the moment, but this will take a while.FanDjango commentedon Sep 26, 2022
It is also worthwhile to read the following: Keep Alive Techniques
I have found FTP clients that disallow configuring NOOPs when the connection is encrypted as well as servers, that allow you to block keep-alives (such as NOOP). It seems that many are having probems concerning this and there is a bit of chaos in how it is being addressed.
So your mileage may vary when you try to keep your connection alive on a long running FTP.
robinrodricks commentedon Sep 27, 2022
Thanks @FanDjango for all the research. If you cannot make progress, I suggest trying to work on something else. As your current fixes and findings are already much better than we had so far.
robinrodricks commentedon Sep 27, 2022
@FanDjango See https://github.com/robinrodricks/FluentFTP/wiki/Noop
FanDjango commentedon Sep 29, 2022
Further development has taken place and is ongoing, I suggest checking the wiki regularly.
FanDjango commentedon Oct 7, 2022
The new version of
GetReplyis merged. This should resolve this topic unless totallyNOOPnon-conformant servers turn up.