[Tfug] python ftplib ports

Chris Niswander MOST.SENDERS.ARE._FILTERED.OUT_--FOR.MY.REAL.EMAIL.ADDRESS.check.my.website..tfug.rcvr.x6a3 at bitboost.com
Tue Sep 30 06:43:25 MST 2008


Hi, Jim!

Jim Secan wrote:
> Back to computers.  Has anyone figured out a way to constrain the range of
> ports used by the python ftplib.py library in data transfer?  I'm running
> into a problem wherein port 1025 is selected by whatever process sets the
> port to be used in the data transfer (I'm running in active mode) and the
> server rejects it.  I want to constrain the transfer to the upper reaches
> of port space.  I don't see that there's any interface on the ftplib
> library to accomplish this, and mucking about in the ftplib.py code tells
> me that I don't understand what it's doing well enough to modify it.  (I'm
> also trying to sort out why the server rejected port 1025, but I suspect
> it's going something else assigned to that port.)

Just a sec.  Your phrasing is a bit confusing here.
I understand that there's a port you want to specify
(to make it *not* 1025), but *which* do you want to specify:
   - the port on the client (where ftplib is running)
   - the port on the ftp server

I think I see how to specify either (or both).
I am using Python 2.5.0 for the following examples.

*-----------------------------------------------------------------
FIRST let's look at how to specify the port used on the client box,
because that is harder, and you seem to figure you have a hard question,
so I guess this is more likely to be your question? :-)

When the Python interpreter runs ftplib.py, normally
it runs the source code file that comes in its Lib directory.
So we can easily read and even fiddle with this code.

Reading this code, we may find that *for data transfer*, the socket
on the client machine seems to be created and have its
port *on the client machine* specified
in method ntransfercmd of class FTP.

If you're in *active mode*, ntransfercmd calls makeport
to create the data transfer socket and bind that socket
to a port on the client machine.

It appears to me that the specific code that chooses the port on the
client machine is:
         for res in socket.getaddrinfo(None, 0, self.af, \
                             socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
             af, socktype, proto, canonname, sa = res
             try:
                 sock = socket.socket(af, socktype, proto)
                 sock.bind(sa)
             except socket.error, msg:
                 if sock:
                     sock.close()
                 sock = None
                 continue
             break
It's that sock.bind(sa) call that binds the socket to a specific
port (specified within sa) on the client machine.

But hey, in the arguments we passed to getaddrinfo,
the port number is the second of those arguments, and we passed zero!
So sa is the tuple ('0.0.0.0', 0), specifying port 0!  Crazy! :-)

In reality, it seems that if we tell socket.bind to bind to port 0,
it just picks an available port,
instead of really using port zero.  Sane! :-)

Fortunately, we can specify the port number that we really want
instead of zero.  For example, we might call
   >>> sock = socket.socket(af, socktype, proto)
   >>> sock.bind( ('0.0.0.0', 3501) )
to bind a socket to port 3501 on the client machine.
   >>> sock2.getsockname()
   ('0.0.0.0', 3501)
   >>>

So, *IF* you are happy to yourself specify the exact port, you could
use your own copy of ftplib.py (put it in the same directory
as your own program, not in Python's Lib directory).
You could modify your ftplib.py so that when FTP.makeport
is called from ntransfercmd, you pass an extra optional port argument,
so in this one special case you
get the high port number that you want.

If you aren't happy to specify a specific high port number on
your client box, because you don't know any ports that are free,
then I am just too lazy to help with that right now. :-)

*-----------------------------------------------------------------
SECOND let's look at how to specify the port on the ftp server
that ftplib tries to connect to.

The shortish answer is, you call the FTP.ftplib() constructor
with no arguments.  Then you can specify the server port when you call
the socket's connect method.  (But you'll still have to call login
to actually use the ftp site.)

*-----------------------------------------------------------------
I hope you all will forgive my long-windedness, but I happened
to feel like reverse-engineering Python's ftplib.

Also, please forgive me AND LET ME KNOW if I got something kind of
wrong here.

I do hope that even if I made an error, the general principles
will be mostly correct and helpful.

--Chris Niswander







More information about the tfug mailing list