Hello all,

I found a bug in the FtpCli - FtpSrv combination.
Steps to reproduce:

1) (Client) put FtpCli into passive mode
2) (Server) assign FtpSrv.FtpServerValidateGet
3) (Client) request something from the Server - e.g. with FtpClient.Get
4) (server) fail the resulting RETR command on the server-side in 
FtpServerValidateGet (either by setting Allowed to false or by raising an 

If the connection between Client and Server is slow enough(!) this will lead 
to wrong state in the client state-machine, so that subsequent RETR will 
fail! Below you can find a log that demonstrates the problem (logged on 
client side) - (please note that there are some custom-extended commands - 
namely STATUS and ACCESS - they all expect a one-line answer, just as 
regular ftp-commands).

The error occurs in line L6. L1 to L4 are as expected, L4 is the result from 
the failed FtpServerValidateGet.
Then the client issues the next command L5 and it expects a 1 line answer 
(which comes in L7), however L6 is received inbetween as the answer because 
the FtpSrv "unexpectedly" sends "226 File sent ok", although it never has 
transmitted anything. This unexpected answer shifts all subsequent 
server-answer and leads to an unexpected state at some time.

L1: > PASV
L2: < 227 Entering Passive Mode (81,189,215,215,78,33).
L3: > RETR _SYSTEMARCHIVE_5889c6e9f9fb40d68fa23d6d74011566
L4: < 501 Cannot RETR. IDENTICAL
L6: < 226 File sent ok
L8: < 200 Haupt-Bildschirm: Geteilter Schirm
L9: > PASV
L10: < 200 Ok. Access Type set!
L12: < 227 Entering Passive Mode (81,189,215,215,78,33).
L13: ErrorMessage: 227 Entering Passive Mode (81,189,215,215,78,33).

If you work on a fast network connection this problem doesn't occur, because 
L4 AND L6 are received "in one block" and effectively form L4 and L5, which 
doesn't shift anything.

Reason for the problem:
In passive mode FtpCli starts a passive data-connection to the server at the 
same time as it sends the RETR command (in TCustomFtpCli.DoGetAsync()), so 
it does NOT wait for a response from the FtpSrv if the RETR-command 
alltogether is valid (in active mode this doesn't happen because then the 
FtpSrv starts the data-connection - and this only happens if the 
FtpSrv.FtpServerValidateGet succeeds).
As soon as the client receives the "501 Cannot RETR." (triggered by 
TFtpServer.CommandRETR() and the call to FtpSrv.FtpServerValidateGet) it 
closes the data-connection again.
But this triggers TFtpServer.ClientRetrSessionClosed() which in turn sends 
the"226 File sent ok".

So the reason for the problem is clear, however I don't know how to correct 
a) the passive data-connection only be connected if / as soon as the 
"primary command" is approved by the server
b) the server simply ignore the ClientRetrSessionClosed and not send 
anything if nothing was transmitted (scheduled to transmit)?

btw: This led my to discover another fundamental problem with passive mode 
(which luckily obviously doesn't occur in reality):
On server side (in passive mode) DataSocket.OnSessionConnected, 
DataSocket.OnSessionClosed, DataSocket.OnDataAvailable and 
DataSocket.OnDataSent are assigned in 
TFtpServer.ClientPassiveSessionAvailable(), depending on the scheduled 
command (e.g. RETR).
Since in FtpCli both, passive-mode data-connection _and_ the desired command 
(e.g. "RETR"), are issued at the same time (although on different ports), it 
_could_ happen that the data-connection is established _before_ the 
control-connection has transmitted the command! This would lead to 
TFtpServer.ClientPassiveSessionAvailable() being called before the server 
knows for which command the passive-data-connection is connected - in return 
effectively failing the rest of the transfer...

best regards (and sorry for the lengthy post)
Peter Feldbaumer
p dot feldbaumer at utanet dot at 

To unsubscribe or change your settings for TWSocket mailing list
please goto http://www.elists.org/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be

Reply via email to