[pyftpdlib-users] Problem on "active" connection

1,477 views
Skip to first unread message

Michele Petrazzo

unread,
May 25, 2010, 8:04:53 AM5/25/10
to Python FTP server library - Discussion group
Hi all,
I have a server behind a linux firewall, behind an adsl router and on
that server I have pyftpdlib configured and active.
On all the appliances in front of the server I dnat the ftp port, so
the connection from outside are correctly natted to the server.
Now I'm trying to make pyftpdlib work and act as ftp server, but I'm
seeing that yes, it can authenticate the user, but when I try to send
a command a receive a response (ls command), it wait a bit and the
client gone to timeout.
The session (1.1.1.1 is the public ip of the client):

Serving FTP on 0.0.0.0:21
[]1.1.1.1:48134 Connected.
1.1.1.1:48134 ==> 220 pyftpdlib 0.5.2 ready.
1.1.1.1:48134 <== USER MYUSERNAME
1.1.1.1:48134 ==> 331 Username ok, send password.
1.1.1.1:48134 <== PASS ******
1.1.1.1:48134 ==> 230 Login successful.
[MYUSERNAME]@1.1.1.1:48134 User MYUSERNAME logged in.
1.1.1.1:48134 <== SYST
1.1.1.1:48134 ==> 215 UNIX Type: L8
1.1.1.1:48134 <== PORT 1,1,1,1,130,243
1.1.1.1:48134 ==> 421 Active data channel timed out.
[MYUSERNAME]@1.1.1.1:48134 Disconnected

On the client:
michele:~$ ftp ftpserver
Connected to ftpserver.
220 pyftpdlib 0.5.2 ready.
Name (ftpserver:michele): MYUSERNAME
331 Username ok, send password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
421 Active data channel timed out.
ftp>

For a simple test, I install ProFTPD on the server where I try
pyftpdlib and it work like a charm!

What can I do?

Thanks,
Michele

--
You received this message because you are subscribed to the "Python FTP server library" project group:
http://code.google.com/p/pyftpdlib/
To post to this group, send email to pyft...@googlegroups.com
To unsubscribe from this group, send email to pyftpdlib-...@googlegroups.com
For more options, visit this group at http://groups.google.com/group/pyftpdlib

Giampaolo Rodolà

unread,
May 26, 2010, 4:44:06 AM5/26/10
to pyft...@googlegroups.com, michele....@gmail.com
2010/5/25 Michele Petrazzo <michele....@gmail.com>:

I suspect it might be related with this:
http://code.google.com/p/pyftpdlib/issues/detail?id=123
Could you please try to use the current code from trunk and tell me if
the problem persists?

Michele Petrazzo

unread,
May 26, 2010, 7:57:09 AM5/26/10
to Python FTP server library - Discussion group
On 26 Mag, 10:44, Giampaolo Rodolà <g.rod...@gmail.com> wrote:
> 2010/5/25 Michele Petrazzo <michele.petra...@gmail.com>:

> I suspect it might be related with this:http://code.google.com/p/pyftpdlib/issues/detail?id=123
> Could you please try to use the current code from trunk and tell me if
> the problem persists?

Forgot to say that I'm already using the last trunk... (r695)

Thanks,
Michele

Giampaolo Rodolà

unread,
May 26, 2010, 10:02:29 AM5/26/10
to pyft...@googlegroups.com, michele....@gmail.com
2010/5/26 Michele Petrazzo <michele....@gmail.com>:

Then I'm not sure.
Just to be clear: what's going on there is that the client sent a PORT
request to server which is supposed (the server) to connect to IP
1.1.1.1, port ((130 * 256) + 243) which is the address the client is
listening on.
From the server point of view this is nothing but a simple
socket.connect() call (ActiveDTP().__init__() method) .

If connect() doesn't succeed (times out) it could be up to a lot of
things, almost always related with routing problems (e.g. client is
behind a firewall which does not properly forward the incoming PORT
connection, etc...).

I really have no idea why this works with proftpd but not with pyftpdlib.
Are you sure both servers are running using the same configurations?
Are you sure you're using PORT (and not PASV instead) against proftpd as well?

> Forgot to say that I'm already using the last trunk... (r695)

In this specific case trunk differs from a pristine 0.5.2 version
installation in that bind() is called before connect() because of this
recent issue:
http://code.google.com/p/pyftpdlib/issues/detail?id=123

In my previous message I recommended to use the trunk version because
of this, now I'm going to recommend the contrary. =)
Try the "old" 0.5.2 version (which doesn't use bind()) and see if it
solves anything, although I doubt it.

Regards,

Giampaolo

Michele Petrazzo

unread,
May 26, 2010, 1:16:46 PM5/26/10
to Giampaolo Rodolà, pyft...@googlegroups.com
2010/5/26 Giampaolo Rodolà <g.ro...@gmail.com>:

> 2010/5/26 Michele Petrazzo <michele....@gmail.com>:
>> On 26 Mag, 10:44, Giampaolo Rodolà <g.rod...@gmail.com> wrote:
>>> 2010/5/25 Michele Petrazzo <michele.petra...@gmail.com>:
>>
>>> I suspect it might be related with this:http://code.google.com/p/pyftpdlib/issues/detail?id=123
>>> Could you please try to use the current code from trunk and tell me if
>>> the problem persists?
>>
>> Forgot to say that I'm already using the last trunk... (r695)
>>
>> Thanks,
>> Michele
>
> Then I'm not sure.
> If connect() doesn't succeed (times out) it could be up to a lot of
> things, almost always related with routing problems (e.g. client is
> behind a firewall which does not properly forward the incoming PORT
> connection, etc...).
>
> I really have no idea why this works with proftpd but not with pyftpdlib.
> Are you sure both servers are running using the same configurations?

What do you mean with "same configuration"? They cannot share anything
other than the "ftp port" since are both ftp servers... pyftpdlib have
a normal configuration with authentication done by
ftpserver.DummyAuthorizer and proftpd has the default one (no other
done except apt-get install proftpd)

> Are you sure you're using PORT (and not PASV instead) against proftpd as well?
>

I don't know.
I made tests only with the ftp command line installed into my debian
and the tests are done on two different customers (with the
environment above) and both has the same problem.
In all the tests, I tried also to use the "passive" command and the
same problem occurs.

>> Forgot to say that I'm already using the last trunk... (r695)
>
> In this specific case trunk differs from a pristine 0.5.2 version
> installation in that bind() is called before connect() because of this
> recent issue:
> http://code.google.com/p/pyftpdlib/issues/detail?id=123
>
> In my previous message I recommended to use the trunk version because
> of this, now I'm going to recommend the contrary. =)
> Try the "old" 0.5.2 version (which doesn't use bind()) and see if it
> solves anything, although I doubt it.
>

No. Same problem.

If can help you to debug, as said, both my customers have: adsl router
-> fw linux -> server with ftp server and it doesn't work (with
pyftpdlib). On another customer I have: ethernet connection (pppoe
with pubb ip address) -> fw linux -> server with ftp server and here
pyftpdlib works.
So, seeing this, you can tell me that it's the adsl router that create
the problem and I agree with you that it can be but... with proftpd it
works in both cases, so shall be something "inside" pyftpdlib...
Of course, if you need a tcpdump session (for example with both
servers) or something other, ask me without problem!

> Regards,
>
> Giampaolo
>

Thanks,
Michele

Giampaolo Rodolà

unread,
May 26, 2010, 2:59:34 PM5/26/10
to Michele Petrazzo, pyft...@googlegroups.com
2010/5/26 Michele Petrazzo <michele....@gmail.com>:

> What do you mean with "same configuration"? They cannot share anything
> other than the "ftp port" since are both ftp servers... pyftpdlib have
> a normal configuration with authentication done by
> ftpserver.DummyAuthorizer and proftpd has the default one (no other
> done except apt-get install proftpd)

I meant "same configuration in respect of active (PORT) connections".
Although pyftpdlib does not provide anything in that sense (ActiveDTP
class just uses bind() than connect() without providing any
"configurable option") maybe proftpd does and has it set by default.
I don't know... I'm just guessing.

> In all the tests, I tried also to use the "passive" command and the
> same problem occurs.

Ok, this is a different problem (PASV vs PORT).
In order to make passive data connections pass through you need to:

- set FTPHandler.masquerade_address to your public IP
- set FTPHandler.passive_ports, which expects a list of ports
(integers) pyftpdlib will use to listen on for passive data transfers
(example: FTPHandler.passive_ports = range(60000, 62000))
- configure the public gateway so that every incoming connection on
one of those ports gets redirected to the private address where the
server listens

There's a FAQ which explains this:
http://code.google.com/p/pyftpdlib/wiki/FAQ#I%27m_behind_a_NAT_/_gateway

In order to make passive connections work on proftpd a similar
configuration must be provided as well.
If PASV works on proftpd but not on pyftpdlib then I guess the first
one is properly configured in a similar manner, while the latter is
not.

> If can help you to debug, as said, both my customers have: adsl router
> -> fw linux -> server with ftp server and it doesn't work (with
> pyftpdlib). On another customer I have: ethernet connection (pppoe
> with pubb ip address) -> fw linux -> server with ftp server and here
> pyftpdlib works.

Ok, but we should first distinguish *what* doesn't work exactly,
whether passive (PASV, aka client connecting to server) or active
(PORT, aka server connecting to client) connections or both as they're
different problems with different solutions.

> So, seeing this, you can tell me that it's the adsl router that create
> the problem and I agree with you that it can be but... with proftpd it
> works in both cases, so shall be something "inside" pyftpdlib...
> Of course, if you need a tcpdump session (for example with both
> servers) or something other, ask me without problem!

Let's focus on the environment which has the problem.
What does not work exactly? PASV or PORT?
In case of PORT check what IP address arrives to the server.
You can do this with TCPDump or by just putting a debugging print in
ActiveDTP class, as such:

class ActiveDTP(asyncore.dispatcher):

def __init__(self, ip, port, cmd_channel):
print ip
...

It might be that the IP address you got in there is a private one
(e.g. 192.168.1.1), pyftpdlib tries to connect() to it and just fails.
The reason why proftpd succeeds might be that after it fails to
connect() to that address it determines the IP of the control
connection (which is supposed to be public, say 151.22.6.45) and tries
that one as a last resort.

But again, I'm just guessing. Such kind of problems are hard to debug
without putting hands on the environment.

Start to answer the questions I've raised, attach further logs if you
think they might help, then let's see where we can go from there.


Giampaolo

Michele Petrazzo

unread,
May 27, 2010, 4:48:51 AM5/27/10
to Giampaolo Rodolà, pyft...@googlegroups.com
2010/5/26 Giampaolo Rodolà <g.ro...@gmail.com>:

> 2010/5/26 Michele Petrazzo <michele....@gmail.com>:
>> What do you mean with "same configuration"? They cannot share anything
>> other than the "ftp port" since are both ftp servers... pyftpdlib have
>> a normal configuration with authentication done by
>> ftpserver.DummyAuthorizer and proftpd has the default one (no other
>> done except apt-get install proftpd)
>
> I meant "same configuration in respect of active (PORT) connections".
> Although pyftpdlib does not provide anything in that sense (ActiveDTP
> class just uses bind() than connect() without providing any
> "configurable option") maybe proftpd does and has it set by default.
> I don't know... I'm just guessing.
>

I don't know too

>> In all the tests, I tried also to use the "passive" command and the
>> same problem occurs.
>
> Ok, this is a different problem (PASV vs PORT).
> In order to make passive data connections pass through you need to:
>

<-cut->

Sorry, I'm introduced too many problems on the same time...I thought
it would help for understand the problem the tip to talk for the
passive connection, but I think I driven the conversation too many
away from the initial purpose, that was that I'm not able to use
pyftpdlib with active connection, not the passive one!


> So, seeing this, you can tell me that it's the adsl router that create
>> the problem and I agree with you that it can be but... with proftpd it
>> works in both cases, so shall be something "inside" pyftpdlib...
>> Of course, if you need a tcpdump session (for example with both
>> servers) or something other, ask me without problem!
>
> Let's focus on the environment which has the problem.
> What does not work exactly? PASV or PORT?

PORT, so active.

> In case of PORT check what IP address arrives to the server.
> You can do this with TCPDump or by just putting a debugging print in
> ActiveDTP class, as such:
>
> class ActiveDTP(asyncore.dispatcher):
>
>    def __init__(self, ip, port, cmd_channel):
>        print ip
>        ...
>
> It might be that the IP address you got in there is a private one
> (e.g. 192.168.1.1), pyftpdlib tries to connect() to it and just fails.
> The reason why proftpd succeeds might be that after it fails to
> connect() to that address it determines the IP of the control
> connection (which is supposed to be public, say 151.22.6.45) and tries
> that one as a last resort.
>
> But again, I'm just guessing. Such kind of problems are hard to debug
> without putting hands on the environment.
>

This is the server output (client ip 89.202.235.9):

89.202.235.9:49321 <== SYST
89.202.235.9:49321 ==> 215 UNIX Type: L8
89.202.235.9:49321 <== PORT 89,202,235,9,222,96
DEBUG DATA 89.202.235.9 56928 <socket._socketobject object at 0x931b304>
89.202.235.9:49321 ==> 421 Active data channel timed out.
[user]@89.202.235.9:49321 Disconnected.

On client firewall I see the communication thought the port 49321, but
nothing arrive at port 56928

(after about 30 minutes and a lot of tcpdump tests :) ...)

On the client firewall, I tcpdump and compare the two sessions with
proftpd and pyftpdlib and I see that on the proftpd one, after the
PORT command of the client, the server reply with a "200 PORT command
successful" and the client continue with an ack. After starts with the
LIST command and continue...
On the pyftpdlib session this doesn't happen: client ask for the PORT
and server reply with an ack, after all stall for about 30 seconds and
continue with the timeout.


> Start to answer the questions I've raised, attach further logs if you
> think they might help, then let's see where we can go from there.
>
>

Hope that this " simplified dump" can help.

Michele

Giampaolo Rodolà

unread,
May 27, 2010, 2:58:36 PM5/27/10
to Michele Petrazzo, pyft...@googlegroups.com
2010/5/27 Michele Petrazzo <michele....@gmail.com>:

> On client firewall I see the communication thought the port 49321, but
> nothing arrive at port 56928
> (after about 30 minutes and a lot of tcpdump tests :) ...)

Are you saying that server is trying to connect to port 49321instead of 56928?

> (after about 30 minutes and a lot of tcpdump tests :) ...)
>
> On the client firewall, I tcpdump and compare the two sessions with
> proftpd and pyftpdlib and I see that on the proftpd one, after the
> PORT command of the client, the server reply with a "200 PORT command
> successful" and the client continue with an ack. After starts with the
> LIST command and continue...
> On the pyftpdlib session this doesn't happen: client ask for the PORT
> and server reply with an ack, after all stall for about 30 seconds and
> continue with the timeout.

Unfortunately that doesn't help much.
proftpd responds with 200 because a connection took place *first*.
The problem with pyftpdlib is that doesn't happen.
connect() doesn't connect hence 200 isn't obviously sent.

Since we're talking about active (PORT) connections the problem is
probably the client firewall which doesn't properly forward port
56928.
As to why such connection passes through by using proftpd I really have no idea.
I'm not aware of any "magic" you can do against the server socket
which calls connect() aside from calling bind() first, which latest
revision of pyftpdlib already does.

Could you please try to run the following script against both proftpd
and pyftpdlib and paste the results?

import ftplib, socket

ftp = ftplib.FTP(timeout=3)
ftp.connect(host='localhost', port=21)
ftp.login()

# passive
ftp.set_pasv(True)
host, port = ftp.makepasv()
conn = socket.create_connection((host, port), timeout=3)
print "passive: local:%s remote:%s" %(conn.getsockname(), conn.getpeername())

# active
ftp.set_pasv(False)
listening_sock = ftp.makeport()
print "active: local:" + str(listening_sock.getsockname())
conn = listening_sock.accept()[0]
print "active: local:%s remote:%s" %(conn.getsockname(), conn.getpeername())

Reply all
Reply to author
Forward
0 new messages