Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PORT commands fail to bind to command channel ip address #123

Closed
giampaolo opened this issue May 28, 2014 · 12 comments
Closed

PORT commands fail to bind to command channel ip address #123

giampaolo opened this issue May 28, 2014 · 12 comments
Assignees
Labels
bug Component-Library imported imported from old googlecode site and very likely outdated

Comments

@giampaolo
Copy link
Owner

From atkin...@gmail.com on December 17, 2009 22:27:03

What steps will reproduce the problem?  
1. create an ip alias on a avaliable network interface
2. use this alias in the connection tuple for init in creating an instance
of the server
3. make a PORT request 

What is the expected output?  


What do you see instead?  
Notice that the server will use the ip address of the interface and not the
alias used in bind() for the command channel. What version of pyftpdlib are you 
using? On what operating system? Which Python version? This issue exists in the 
repository version / Linux and FreeBSD / python 2.5.

quick and dirty fix below, however the same bind, port and exception
handling should be used
from the passive and command channel sections of the code.

--- /usr/lib/python2.5/site-packages/pyftpdlib/ftpserver.py.org 2009-12-17
13:11:22.000000000 -0800
+++ /usr/lib/python2.5/site-packages/pyftpdlib/ftpserver.py     2009-12-17
13:15:32.000000000 -0800
@@ -686,8 +686,10 @@
             self.idler = CallLater(self.timeout, self.handle_timeout)
         else:
             self.idler = None
+        ip = self.cmd_channel.getsockname()[0]
         self.create_socket(self.cmd_channel.af, socket.SOCK_STREAM)
         try:
+            self.bind((ip,0))
             self.connect((ip, port))
         except socket.gaierror:
             self.cmd_channel.respond("425 Can't connect to specified
address.") Please provide any additional information below.

Original issue: http://code.google.com/p/pyftpdlib/issues/detail?id=123

@giampaolo giampaolo self-assigned this May 28, 2014
@giampaolo
Copy link
Owner Author

From atkin...@gmail.com on December 17, 2009 14:39:47

whoops, of course use a different local variable for the
self.cmd_channel.getsockname()[0].   in that patch.  lol.
corrected:

--- /usr/lib/python2.5/site-packages/pyftpdlib/ftpserver.py.org 2009-12-17
13:11:22.000000000 -0800
+++ /usr/lib/python2.5/site-packages/pyftpdlib/ftpserver.py     2009-12-17
13:15:32.000000000 -0800
@@ -686,8 +686,10 @@
             self.idler = CallLater(self.timeout, self.handle_timeout)
         else:
             self.idler = None
+        s_ip = self.cmd_channel.getsockname()[0]
         self.create_socket(self.cmd_channel.af, socket.SOCK_STREAM)
         try:
+            self.bind((s_ip,0))
             self.connect((ip, port))
         except socket.gaierror:
             self.cmd_channel.respond("425 Can't connect to specified address.")

@giampaolo
Copy link
Owner Author

From billiej...@gmail.com on December 18, 2009 06:38:22

Mmmmm I'm not following you.
What problem are we talking about?
Why in the world do you want to use bind() in ActiveDTP() class which already uses
connect()?

@giampaolo
Copy link
Owner Author

From atkin...@gmail.com on December 18, 2009 06:58:06

OK here's an example:

Network Server A has several IP addresses bound to it's adapter eth0.  10.0.01 is the
IP address assigned by ifconfig, but 10.0.0.2 - 4 might be IP address aliases.  
pyftpdlib.ftpserver.FTPserver((10.0.0.3,21))

10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4

The network administrator decides he only needs to run the ftp server bound to IP
address 10.0.0.3 (maybe it's the only address allowed into the DMZ or the other side
of a secure tunnel, etc..)

FTP client C connects to 10.0.0.3 successfully via the Control channel, and issues a
PORT command

PORT 172,16,15,33,229,120

ActiveDTP creates a new socket:

self.create_socket(self.cmd_channel.af, socket.SOCK_STREAM)

then calls connect

self.connect((ip, port))

connect will go the OS and consult the routing table, using the source ip address
associated with the interface that is associated with the route, in this case 10.0.0.1.

The ftp client sees the connection from 10.0.0.1, to it's open port, but not from
10.0.0.3 - so it sends an immediate RST, resulting in a 426 on the control channel.

@giampaolo
Copy link
Owner Author

From billiej...@gmail.com on December 18, 2009 08:49:17

Ok, it is clear now, thanks for the explanation, but are you sure that bind() can
solve this situation?
Have you tried it yourself?
So far I've always seen bind() used before accept(), to put a socket in "listen"
mode, never before connect().

Aside from that are we actually sure that this is within the realm of problems that
should be dealt with by the server?
Shouldn't the administrator be supposed to properly configure the OS so that any low
level connect() socket call be redirected through the proper ip/interface?

Does the OS give the possibility to do that?

@giampaolo
Copy link
Owner Author

From atkin...@gmail.com on December 18, 2009 09:46:14

All bind does is bind the local protocol / address to the socket, nothing more.  It
can be used in both originating and receiving connections.  If nothing using bind
allows you to expand the passive_ports functionality to ActiveDTP, allowing the
administrator to control which source port ranges would be used for active a data
connections.

My server and client code that I'm working on here ran into this problem, 
because the
test harness we are using uses a range of ip addresses to simulate many different
clients and servers.  To fix it, I simply subbed ActiveDTP and used my own __init__()
to use bind() before connect -- works like a charm.

@giampaolo
Copy link
Owner Author

From billiej...@gmail.com on December 28, 2009 15:12:50

I've found a similar problem for httplib: http://bugs.python.org/issue3972 I 
think it makes sense to add a new bind_address attribute to ActiveDTP class 
defaulting to None.
Deliberately binding to command channel's IP address by default might not be 
universally desirable, plus I think it might not even work across all platforms.

@giampaolo
Copy link
Owner Author

From billiej...@gmail.com on April 08, 2010 14:46:31

Fixed as r672 which adds a new source_address attribute to ActiveDTP class.
This is the same solution adopted by Python stdlib's socket and httplib modules
( http://bugs.python.org/issue3972 ).

Status: FixedInSVN
Labels: Version-0.5.2 Milestone-0.6.0 Usability

@giampaolo
Copy link
Owner Author

From btimby@gmail.com on May 12, 2010 13:56:35

I am not sure I am happy with this solution (source_address). This assumes the server
is only listening on a single address. I would assume this is rarely the case, as the
daemon binds to 0.0.0.0 by default.

The httplib example is not relevant, as it is a client, and IS only using a single
address.

In our example, we want the active connection to come from the same IP address as the
command channel, I believe the provided patch in Comment 1 is the correct solution.

Proftpd for example implements this solution...

lines 338 & 339 of data.c: 
http://proftp.cvs.sourceforge.net/viewvc/proftp/proftpd/src/data.c?revision=1.124&view=markup --
  338   if (pr_netaddr_get_family(session.c->local_addr) ==
pr_netaddr_get_family(session.c->remote_addr)) {

  339     bind_addr = session.c->local_addr;
--

It is binding to the local address of the command channel (session.c).

@giampaolo
Copy link
Owner Author

From g.rodola on May 12, 2010 15:46:36

After all I think you're right.
Actually I didn't like the source_address solution in the first place as well.
I wasn't happy with the idea of adding a public attribute to the API of a class which
is not even documented just to solve a problem which is not so common.

My only concern was about less used platforms where this might not work but I guess
anyone who wants something other than Linux/OSX/Windows should be paying for it anyway.

@giampaolo
Copy link
Owner Author

From atkin...@gmail.com on May 12, 2010 18:18:34

Yes, by looking up the local serverside ip for the command channel connection you can
bind to :: or 0.0.0.0 for the server and still reply on from the expected source
address to the client.   I'm still using my local patched source successfully.

@giampaolo
Copy link
Owner Author

From g.rodola on May 12, 2010 18:25:58

Fixed in r688 .
Thank you.

@giampaolo
Copy link
Owner Author

From g.rodola on January 23, 2011 12:59:22

Implemented in version 0.6.0.

Status: Fixed
Owner: g.rodola

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Component-Library imported imported from old googlecode site and very likely outdated
Projects
None yet
Development

No branches or pull requests

1 participant