Our Products:   CompleteFTP  edtFTPnet/Free  edtFTPnet/PRO  edtFTPj/Free  edtFTPj/PRO
0 votes
11.5k views
in .NET FTP by (240 points)
Active FTP can fail during the file transfer when the active IP address has been specifically set.

This happens because the data socket is bound to the IP address of the control socket and not the IP address specified when the active IP address was set.

The problem is in the NewActiveDataSocket function of the FTPControlSocket class. It currently contains:

log.Debug("NewActiveDataSocket(" + port + ")");
// choose specified port
IPEndPoint endPoint = new IPEndPoint(((IPEndPoint)controlSock.LocalEndPoint).Address, port);
sock.Bind(endPoint);

Since there is no guarantee that the control socket was created with the same IP address specified when the active IP address set, the data transfer can fail because the PORT command tells the server the wrong IP address to connect to.

A much better implementation that fixes the problem is:

log.Debug("NewActiveDataSocket(" + port + ")");
// choose specified port
IPEndPoint endPoint;
if (activeIPAddress != null)
{
  endPoint = new IPEndPoint(activeIPAddress, port);
}
else
{
  endPoint = new IPEndPoint(((IPEndPoint)controlSock.LocalEndPoint).Address, port);
}
sock.Bind(endPoint);     

An even better implementation would force the control socket to bind to the IP specified for the active IP address instead of allowing the OS to decide.

And as we all know, letting Windows decide anything is just a bad idea :-)

12 Answers

0 votes
by (161k points)
There's a reason why this is the case and why we can't make the change you're suggesting.

Very often, the active IP address is actually on another machine - generally a firewall - and so it isn't possible to listen on the active IP address that is set. In fact this is why the active IP address property was introduced.

It sounds like you want to listen on the active IP address, but your control socket has connected out on another interface. Is that the case?

What we can do is to listen on the active IP address if it is listed as one of the interfaces for the localhost - do you think that will work for you?
0 votes
by (240 points)
You describe it correctly, the control socket is going out on one interface and we need the active data socket to be on another interface. If you cannot make the first change I am talking about (which works in most situations), then we need a way to control which interface the control socket is using (the second suggestion).

If you cannot use the active IP address property to select the local IP address for the control socket, then maybe there is the need for a new property so the local IP address can be set before the control socket is connected. Then the existing code would pick the correct IP address to bind when it created the active data socket.
0 votes
by (161k points)
If we check that the ActiveIPAddress is indeed a localhost IP (by matching against all the interface IP addresses) and listen on it in this case rather than the control socket IP, that should work for you?
0 votes
by (240 points)
NO!!! Just checking that an IP address chosen by Windows is on the local machine will not work.

We have several machines with multiple NIC's. In many cases the extra NIC's are used to talk to private frame relay and HDLC CS1's and CS3's that don't have routes out to the internet. In some cases these private nets are have routes to the internet on the other end, but we need to pass through firewalls that don't support the ALG to allow the incoming active data connection. In fact on a few machines Windows is choosing the loopback interface. In all cases, if I can control where the socket binds, we do have a NIC that connects to the internet through a firewall that does have proper support for the FTP ALG.

Without the ability to explicitly select the interface, it is not always possible to establish the active data connection. We have now gone to the point where we have modified the source so that we can get it to work. We tried my first suggestion of using the active IP address property to control where the data socket listens. That worked on the machines that unexpectedly choose the loopback interface. But that didn't work on all machines. Then we made the second change to add a new "ControlIPAddress" property to the FTPClient class. This appears to be working on ALL machines.

So the best solution is to add a new property on the FTPClient class to allow us to control which interface is used for the control socket. We can continue to use our bastard version of the EDT software but it would be better if this was incorporated into the standard releases of the software. If we are having the problem, so are other users.

In any case, the reason the bind operation was included in the original Berkley Sockets (and later Windows Sockets) on outgoing sockets was at least partially to handle cases where there are multiple interfaces. It actually really surprised me that EDT does not already support this. It's just not feasible for an OS to always deduce the proper interface.
0 votes
by (51.2k points)
In fact, we have anticipated that someone would one day have the problem you are having, but in the five years since the first release noone else has ever reported it, so we have seen no reason to address it. We have a wish-list as long as a railway and are necessarily ruthless in deprioritising low-impact items.

We have considered adding a property similar to your ControlIPAddress but are concerned that it would needlessly confuse the 99.99% (literally) of users who don't need it.

Having said that, we may still do it, but I don't understand why the solution my colleague proposed won't solve your particular problem.

Bruce is not proposing "just checking that an IP address chosen by Windows is on the local machine", which would clearly be pointless. He is proposing checking if ActiveIPAddress (which you set yourself) is on the local machine, and if it is then binding to that address. This is what you proposed yourself initially, but with the additional safeguard that we are checking if ActiveIPAddress is actually on the local machine.

- Hans (EnterpriseDT)
0 votes
by (240 points)
Doing as suggested, and checking to see if the IP address is on the local machine would help for some situations. Especially when Windows chooses the loopback address to listen on. But this does not work when you have several NIC's. If the control socket is created on the wrong interface, you may not be able to initially connect to the FTP server. If you happen to be able to connect to the server, you may not be able to establish the active data connection. The simple property on FTPClient to set the control sockets IP address handles both cases without using logic to enumerate the interfaces on the local machine.

If you think the new property will confuse someone, call it something like "MultipleNicIP" and add a line in the docs describing what it does and when you need to use it.

All you got to do is add a new parameter to FTPControlSocket's constructor to pass the local address, optionally bind to the value before the call to Initialize. In FTPClient all you need is a private variable, and simple property, and pass the private variable to the new constructor. All in all just 10 minutes of work for a slow coder.
0 votes
by (161k points)
We now plan to add LocalBindingIPAddress, which can optionally be specified. This will also be used for the active data socket, although the ActiveIPAddress will be the IP address actually sent in the PORT command (if it is set).

Do you think this will solve your problem?

All in all just 10 minutes of work for a slow coder.


We have tens of thousands of users of this library out there, and every change (and there are a lot of requests) must be carefully considered to see what impact it might have on them. Please refrain from these kind of comments.
0 votes
by (220 points)
I realize that this thread is two years old but I recently ran across the same problem.

I tried using passive FTP to connect to the remote server, but that server was returning a port number greater than 42000. For some reason my Windows 2003 server could not connect out over that port. It was not a firewall issue, since I was able to connect using FileZilla from a Windows 7 workstation from behind the same firewall. It was something strange with Windows 2003. The following Microsoft KB article (http://support.microsoft.com/kb/196271) failed to correct the problem. So, I punted to active FTP mode.

Well, that had problems too and it wasn't the firewall either, since FileZilla from the same machine was able to connect using active mode without a problem. I dived into the source code and located the same issue reported by jschafer, i.e. the NewActiveDataSocket method creates an enpoint that's listening to 127.0.0.1, not the fixed 192.168.x.x. IP address on my network card that I specified with the PublicIPAddress property. This 192.168.x.x address is what the NAT on my firewall is expecting. I implemented the fix that jschafer suggested and everything worked perfectly.

So, was there ever any resolution to this issue?

Thanks in advance for any information.
0 votes
by (161k points)
So you added a new property to specify the interface that the control socket binds to?
0 votes
by (220 points)
No, I implemented the following, as noted by jschafer, in the NewActiveDataPort method:

if (activeIPAddress != null) 
{ 
  endPoint = new IPEndPoint(activeIPAddress, port); 
} 
else 
{ 
  endPoint = new IPEndPoint(((IPEndPoint)controlSock.LocalEndPoint).Address, port); 
}


...and it corrected the problem.

Is there a different, preferred way to do this?

Categories

...