This project is read-only.

Not forcing EPSV when not featured

Developer
Sep 19, 2012 at 12:20 AM
Edited Sep 19, 2012 at 12:36 AM

Hi,

I was thinking about making a change that only tries EPSV if it is featured.

I thought about it, because I find a lot of servers that doesn't advertise it with the FEAT command, but doesn't return an error 500 which allow us to fallback to PASV.

I know we will lose EPSV on all of the servers that doesn't advertise it, but we will be able to work on server that have not expected behaviors.

EDIT: For these servers, the problems occurs in the FtpPassiveStream class, in the Open() method.
the line

reply = this.ControlConnection.Execute("EPSV");

returns an

FtpResponseType.PositiveCompletion

but it never succeed to connect, and the line

this.Socket.Connect(host, port);

fails.

One possibilities, but not clean, should be to catch exceptions from the Socket.Connect() method, and then tries PASV, but this is not something expected, and I think it might induce other behavior. That's why I think we should only uses EPSV if it is featured.

Coordinator
Sep 20, 2012 at 8:31 PM

There's already an enum type for each type of data stream so I think that maybe dumping the fallback to XYZ ideas might be the best path forward. It leaves the user of System.Net.FtpClient in control of the command used for the data channel and they can easily check ftpClient.Capabilities for the EPSV enum if they want to or just try to force it if they are sure the server supports EPSV anyway. That way System.Net.FtpClient is not doing anything the developer didn't expect.

Coordinator
Sep 20, 2012 at 8:32 PM

I'm gonna change it so that if you set it to use EPSV it only uses EPSV and the same for EPRT and make sure the EPSV & EPRT enums get set when the capabilities are loaded and they're detected.

Coordinator
Sep 20, 2012 at 8:34 PM

Later on we can add more FtpDataChannelTypes for the people who want automagic detection, for example we can do a FtpDataChannelType.AutoPassive that tries EPSV if it's listed in FEAT or PASV if it's not.

Coordinator
Sep 20, 2012 at 8:57 PM

Alright, I've pushed up a new revision 24a2279fc897 that I think should cover the bases. User's that want the old behavior can try { /* try EPSV */  } catch(FtpCommandException) { /* try PASV instead */ }

Sep 21, 2012 at 1:47 AM

Hi StrAbZ, I have recently encountered the same issue but I don't believe in my case anyhow that it was due to the remote server not supporting EPSV, but more likely there is a proxy or firewall in between that doesn't understand the EPSV command and so does not open the remote port to allow the client to connect back even though the FTP server behind said firewall/proxy has opened the port and is listening for connections.

In my case the client/server exchange went liek this:

< TYPE A
> 200 Type set to A; form set to N.
< EPSV
> 229 Entering Extended Passive Mode (|||33067|)  <- positive completion but then fails to open the remote socket.

Would be worth checking what the FtpResponseType.PositiveCompletion that you get is, if it is like mine above, then the FTP server almost certainly supports EPSV and your issue is with a firewall/proxy.

Developer
Sep 22, 2012 at 5:32 PM

That's what I thought first, but it worked on other FTP client that doesn't force EPSV, but stay on PASV.

Since PASV and EPSV are the same, except EPSV works with IPv6 and only sends the port, if it was a firewall/proxy problem it wouldn't have worked with PASV.

I have not used the last version of the library in my program yet, but I'm sure this gonna fix it.

Sep 26, 2012 at 4:07 AM

What I mean is that some firewalls will do statefull packet inspection and detect that the PASV command has been sent from inside the firewall and dynamically open the port specified in the PASV command on the firewall and foward this port to the server.  It could be that the firewall in your case does not understand the EPSV command so does not know that it needs to dynamically open a port and forward that to the server.

In this case, a client using PASV will work just fine, but one that attempts EPSV first and falls back to PASV only when it gets an error when using EPSV command will not work as no error is returned from the EPSV but a positive completion so the client never attempts to use PASV mode as a fall back.

Eamon

Developer
Sep 26, 2012 at 11:58 AM

Ok, I understand what you meant.

So there might be a firewall between, but not on my side (I deactivated all of them) and it doesn't work. And I like to think that if it was on my side it wouldn't work for every FTP server with EPSV.

But this prove it is better not to force EPSV :)

Oct 1, 2012 at 5:09 AM

The change to not assume EPSV support fails for me. I am using IPv6. When FtpPassiveStream.Open() tries PASV, the server returns:

    500 You are connected using IPv6. PASV is only for IPv4. You have to use the EPSV command instead.

This is from FileZilla. So FileZilla doesn't advertise EPSV in response to FEAT, and PASV doesn't work on IPv6.

So I'm thinking about doing this:

if (this.ControlConnection.HasCapability(FtpCapability.EPSV) || (this.Socket.AddressFamily == Sockets.AddressFamily.InterNetworkV6))                       

    type = FtpDataChannelType.ExtendedPassive;                   

else

    type = FtpDataChannelType.Passive;             

Which seems to be working in my case.

Developer
Oct 1, 2012 at 10:38 AM

Exact!

I'm working on a fix. It will force EPSV/EPRT if this is a IPv6 address.

I have also find that FtpClient (which uses, FtpChannel) connect only with IPv4 address, the socket created uses AddressFamily.InterNetwork, so it will also fix that.