1==== 2FAQs 3==== 4 5.. contents:: Table of Contents 6 7Introduction 8============ 9 10What is pyftpdlib? 11------------------ 12 13pyftpdlib is a high-level library to easily write asynchronous portable FTP 14servers with `Python <http://www.python.org/>`__. 15 16What is Python? 17--------------- 18 19Python is an interpreted, interactive, object-oriented, easy-to-learn 20programming language. It is often compared to *Tcl, Perl, Scheme* or *Java*. 21 22I'm not a python programmer. Can I use it anyway? 23------------------------------------------------- 24 25Yes. pyftpdlib is a fully working FTP server implementation that can be run 26"as is". For example you could run an anonymous ftp server from cmd-line by 27running: 28 29.. code-block:: sh 30 31 $ sudo python -m pyftpdlib 32 [I 13-02-20 14:16:36] >>> starting FTP server on 0.0.0.0:8021 <<< 33 [I 13-02-20 14:16:36] poller: <class 'pyftpdlib.ioloop.Epoll'> 34 [I 13-02-20 14:16:36] masquerade (NAT) address: None 35 [I 13-02-20 14:16:36] passive ports: None 36 [I 13-02-20 14:16:36] use sendfile(2): True 37 38This is useful in case you want a quick and dirty way to share a directory 39without, say, installing and configuring samba. Starting from version 0.6.0 40options can be passed to the command line (see ``python -m pyftpdlib --help`` 41to see all available options). Examples: 42 43Anonymous FTP server with write access: 44 45.. code-block:: sh 46 47 $ sudo python -m pyftpdlib -w 48 ~pyftpdlib-1.3.1-py2.7.egg/pyftpdlib/authorizers.py:265: RuntimeWarning: write permissions assigned to anonymous user. 49 [I 13-02-20 14:16:36] >>> starting FTP server on 0.0.0.0:8021 <<< 50 [I 13-02-20 14:16:36] poller: <class 'pyftpdlib.ioloop.Epoll'> 51 [I 13-02-20 14:16:36] masquerade (NAT) address: None 52 [I 13-02-20 14:16:36] passive ports: None 53 [I 13-02-20 14:16:36] use sendfile(2): True 54 55Listen on a different ip/port: 56 57.. code-block:: sh 58 59 $ python -m pyftpdlib -i 127.0.0.1 -p 8021 60 [I 13-02-20 14:16:36] >>> starting FTP server on 0.0.0.0:8021 <<< 61 [I 13-02-20 14:16:36] poller: <class 'pyftpdlib.ioloop.Epoll'> 62 [I 13-02-20 14:16:36] masquerade (NAT) address: None 63 [I 13-02-20 14:16:36] passive ports: None 64 [I 13-02-20 14:16:36] use sendfile(2): True 65 66 67Customizing ftpd for basic tasks like adding users or deciding where log file 68should be placed is mostly simply editing variables. This is basically like 69learning how to edit a common unix ftpd.conf file and doesn't really require 70Python knowledge. Customizing ftpd more deeply requires a python script which 71imports pyftpdlib to be written separately. An example about how this could be 72done are the scripts contained in the 73`demo directory <https://github.com/giampaolo/pyftpdlib/tree/master/demo>`__. 74 75Getting help 76------------ 77 78There's a mailing list available at: 79http://groups.google.com/group/pyftpdlib/topics 80 81Installing and compatibility 82============================ 83 84How do I install pyftpdlib? 85--------------------------- 86 87If you are not new to Python you probably don't need that, otherwise follow the 88`install instructions <install.html>`__. 89 90Which Python versions are compatible? 91------------------------------------- 92 93From *2.6* to *3.X*. 94 95On which platforms can pyftpdlib be used? 96----------------------------------------- 97 98pyftpdlib should work on any platform where **select()**, **poll()**, 99**epoll()** or **kqueue()** system calls are available and on any Python 100implementation which refers to *cPython 2.6* or superior. 101The development team has mainly tested it under various *Linux*, *Windows*, 102*OSX* and *FreeBSD* systems. 103For FreeBSD is also available a 104`pre-compiled package <http://www.freshports.org/ftp/py-pyftpdlib/>`__ 105maintained by Li-Wen Hsu (lwhsu@freebsd.org). 106Other Python implementation like 107`PythonCE <http://pythonce.sourceforge.net/>`__ are known to work with 108pyftpdlib and every new version is usually tested against it. 109pyftpdlib currently does not work on `Jython <http://www.jython.org/>`__ 110since the latest Jython release refers to CPython 2.2.x serie. The best way 111to know whether pyftpdlib works on your platform is installing it and running 112its test suite. 113 114Usage 115===== 116 117How can I run long-running tasks without blocking the server? 118------------------------------------------------------------- 119 120pyftpdlib is an *asynchronous* FTP server. That means that if you need to run a 121time consuming task you have to use a separate Python process or thread for the 122actual processing work otherwise the entire asynchronous loop will be blocked. 123 124Let's suppose you want to implement a long-running task every time the server 125receives a file. The code snippet below shows the correct way to do it by using 126a thread. 127 128With ``self.del_channel()`` we temporarily "sleep" the connection handler which 129will be removed from the async IO poller loop and won't be able to send or 130receive any more data. It won't be closed (disconnected) as long as we don't 131invoke ``self.add_channel()``. This is fundamental when working with threads to 132avoid race conditions, dead locks etc. 133 134.. code-block:: python 135 136 class MyHandler(FTPHandler): 137 138 def on_file_received(self, file): 139 def blocking_task(): 140 time.sleep(5) 141 self.add_channel() 142 143 self.del_channel() 144 threading.Thread(target=blocking_task).start() 145 146Another possibility is to 147`change the default concurrency model <tutorial.html#changing-the-concurrency-model>`__. 148 149Why do I get socket.error "Permission denied" error on ftpd starting? 150--------------------------------------------------------------------- 151 152Probably because you're on a Unix system and you're trying to start ftpd as an 153unprivileged user. FTP servers bind on port 21 by default and only super-user 154account (e.g. root) can bind sockets on such ports. If you want to bind 155ftpd as non-privileged user you should set a port higher than 1024. 156 157How can I prevent the server version from being displayed? 158---------------------------------------------------------- 159 160Just modify `FTPHandler.banner <api.html#pyftpdlib.handlers.FTPHandler.banner>`__. 161 162Can control upload/download ratios? 163----------------------------------- 164 165Yes. Starting from version 0.5.2 pyftpdlib provides a new class called 166`ThrottledDTPHandler <api.html#pyftpdlib.handlers.ThrottledDTPHandler>`__. 167You can set speed limits by modifying 168`read_limit <api.html#pyftpdlib.handlers.ThrottledDTPHandler.read_limit>`__ 169and 170`write_limit <api.html#pyftpdlib.handlers.ThrottledDTPHandler.write_limit>`__ 171class attributes as it is shown in 172`throttled_ftpd.py <https://github.com/giampaolo/pyftpdlib/blob/master/demo/throttled_ftpd.py>`__ 173demo script. 174 175Are there ways to limit connections? 176------------------------------------ 177 178`FTPServer <api.html#pyftpdlib.servers.FTPServer>`__. class comes with two 179overridable attributes defaulting to zero 180(no limit): `max_cons <api.html#pyftpdlib.servers.FTPServer.max_cons>`__, 181which sets a limit for maximum simultaneous 182connection to handle by ftpd and 183`max_cons_per_ip <api.html#pyftpdlib.servers.FTPServer.max_cons_per_ip>`__ 184which set a limit for connections from the same IP address. Overriding these 185variables is always recommended to avoid DoS attacks. 186 187I'm behind a NAT / gateway 188-------------------------- 189 190When behind a NAT a ftp server needs to replace the IP local address displayed 191in PASV replies and instead use the public address of the NAT to allow client 192to connect. By overriding 193`masquerade_address <api.html#pyftpdlib.handlers.FTPHandler.masquerade_address>`__ 194attribute of `FTPHandler <api.html#pyftpdlib.handlers.FTPHandler.masquerade_address>`__ 195class you will force pyftpdlib to do such replacement. However, one problem 196still exists. The passive FTP connections will use ports from 1024 and up, 197which means that you must forward all ports 1024-65535 from the NAT to the FTP 198server! And you have to allow many (possibly) dangerous ports in your 199firewalling rules! To resolve this, simply override 200`passive_ports <api.html#pyftpdlib.handlers.FTPHandler.passive_ports>`__ 201attribute of `FTPHandler <api.html#pyftpdlib.handlers.FTPHandler.masquerade_address>`__ 202class to control what ports pyftpdlib will use for its passive data transfers. 203Value expected by `passive_ports <api.html#pyftpdlib.handlers.FTPHandler.passive_ports>`__ 204attribute is a list of integers (e.g. range(60000, 65535)) indicating which 205ports will be used for initializing the passive data channel. In case you run a 206FTP server with multiple private IP addresses behind a NAT firewall with 207multiple public IP addresses you can use 208`passive_ports <api.html#pyftpdlib.handlers.FTPHandler.passive_ports>`__ option 209which allows you to define multiple 1 to 1 mappings (**New in 0.6.0**). 210 211What is FXP? 212------------ 213 214FXP is part of the name of a popular Windows FTP client: 215`http://www.flashfxp.com <http://www.flashfxp.com>`__. This client has made the 216name "FXP" commonly used as a synonym for site-to-site FTP transfers, for 217transferring a file between two remote FTP servers without the transfer going 218through the client's host. Sometimes "FXP" is referred to as a protocol; in 219fact, it is not. The site-to-site transfer capability was deliberately designed 220into `RFC-959 <http://www.faqs.org/rfcs/rfc959.html>`__. More info can be found 221here: `http://www.proftpd.org/docs/howto/FXP.html 222<http://www.proftpd.org/docs/howto/FXP.html>`__. 223 224Does pyftpdlib support FXP? 225--------------------------- 226 227Yes. It is disabled by default for security reasons (see 228`RFC-2257 <http://tools.ietf.org/html/rfc2577>`__ and 229`FTP bounce attack description <http://www.cert.org/advisories/CA-1997-27.html>`__) 230but in case you want to enable it just set to True the 231`permit_foreign_addresses <api.html#pyftpdlib.handlers.FTPHandler.permit_foreign_addresses>`__ 232attribute of `FTPHandler <api.html#pyftpdlib.handlers.FTPHandler.masquerade_address>`__ class. 233 234Why timestamps shown by MDTM and ls commands (LIST, MLSD, MLST) are wrong? 235-------------------------------------------------------------------------- 236 237If by "wrong" you mean "different from the timestamp of that file on my client 238machine", then that is the expected behavior. 239Starting from version 0.6.0 pyftpdlib uses 240`GMT times <http://en.wikipedia.org/wiki/Greenwich*Mean*Time>`__ as recommended 241in `RFC-3659 <http://tools.ietf.org/html/rfc3659>`__. 242In case you want such commands to report local times instead just set the 243`use_gmt_times <api.html#pyftpdlib.handlers.FTPHandler.use_gmt_times>`__ attribute to ``False``. 244For further information you might want to take a look at 245`this <http://www.proftpd.org/docs/howto/Timestamps.html>`__ 246 247Implementation 248============== 249 250sendfile() 251---------- 252 253Starting from version 0.7.0 if 254`pysendfile <https://github.com/giampaolo/pysendfile/>`__ module is installed 255sendfile(2) system call be used when uploading files (from server to client) 256via RETR command. 257Using sendfile(2) usually results in transfer rates from 2x to 3x faster 258and less CPU usage. 259Note: use of sendfile() might introduce some unexpected issues with "non 260regular filesystems" such as NFS, SMBFS/Samba, CIFS and network mounts in 261general, see: http://www.proftpd.org/docs/howto/Sendfile.html. If you bump into 262one this problems the fix consists in disabling sendfile() usage via 263`FTPHandler.use_sendfile <api.html#pyftpdlib.handlers.FTPHandler.use_sendfile>`__ 264option: 265 266.. code-block:: python 267 268 from pyftpdlib.handlers import FTPHandler 269 handler = FTPHandler 270 handler.use_senfile = False 271 ... 272 273Globbing / STAT command implementation 274-------------------------------------- 275 276Globbing is a common Unix shell mechanism for expanding wildcard patterns to 277match multiple filenames. When an argument is provided to the *STAT* command, 278ftpd should return directory listing over the command channel. 279`RFC-959 <http://tools.ietf.org/html/rfc959>`__ does not explicitly mention 280globbing; this means that FTP servers are not required to support globbing in 281order to be compliant. However, many FTP servers do support globbing as a 282measure of convenience for FTP clients and users. In order to search for and 283match the given globbing expression, the code has to search (possibly) many 284directories, examine each contained filename, and build a list of matching 285files in memory. Since this operation can be quite intensive, both CPU- and 286memory-wise, pyftpdlib *does not* support globbing. 287 288ASCII transfers / SIZE command implementation 289--------------------------------------------- 290 291Properly handling the SIZE command when TYPE ASCII is used would require to 292scan the entire file to perform the ASCII translation logic 293(file.read().replace(os.linesep, '\r\n')) and then calculating the len of such 294data which may be different than the actual size of the file on the server. 295Considering that calculating such result could be very resource-intensive it 296could be easy for a malicious client to try a DoS attack, thus pyftpdlib 297rejects SIZE when the current TYPE is ASCII. However, clients in general should 298not be resuming downloads in ASCII mode. Resuming downloads in binary mode is 299the recommended way as specified in 300`RFC-3659 <http://tools.ietf.org/html/rfc3659>`__. 301 302IPv6 support 303------------ 304 305Starting from version 0.4.0 pyftpdlib *supports* IPv6 306(`RFC-2428 <http://tools.ietf.org/html/rfc2428>`__). If you use IPv6 and want 307your FTP daemon to do so just pass a valid IPv6 address to the 308`FTPServer <api.html#pyftpdlib.servers.FTPServer>`__ class constructor. 309Example: 310 311.. code-block:: python 312 313 >>> from pyftpdlib.servers import FTPServer 314 >>> address = ("::1", 21) # listen on localhost, port 21 315 >>> ftpd = FTPServer(address, FTPHandler) 316 >>> ftpd.serve_forever() 317 Serving FTP on ::1:21 318 319If your OS (for example: all recent UNIX systems) have an hybrid dual-stack 320IPv6/IPv4 implementation the code above will listen on both IPv4 and IPv6 by 321using a single IPv6 socket (**New in 0.6.0**). 322 323How do I install IPv6 support on my system? 324------------------------------------------- 325 326If you want to install IPv6 support on Linux run "modprobe ipv6", then 327"ifconfig". This should display the loopback adapter, with the address "::1". 328You should then be able to listen the server on that address, and connect to 329it. 330On Windows (XP SP2 and higher) run "netsh int ipv6 install". Again, you should 331be able to use IPv6 loopback afterwards. 332 333Can pyftpdlib be integrated with "real" users existing on the system? 334--------------------------------------------------------------------- 335 336Yes. Starting from version 0.6.0 pyftpdlib provides the new `UnixAuthorizer <api.html#pyftpdlib.authorizers.UnixAuthorizer>`__ 337and `WindowsAuthorizer <api.html#pyftpdlib.authorizers.WindowsAuthorizer>`__ classes. By using them pyftpdlib can look into the 338system account database to authenticate users. They also assume the id of real 339users every time the FTP server is going to access the filesystem (e.g. for 340creating or renaming a file) the authorizer will temporarily impersonate the 341currently logged on user, execute the filesystem call and then switch back to 342the user who originally started the server. Example UNIX and Windows FTP 343servers contained in the 344`demo directory <https://github.com/giampaolo/pyftpdlib/tree/master/demo>`__ 345shows how to use `UnixAuthorizer <api.html#pyftpdlib.authorizers.UnixAuthorizer>`__ and `WindowsAuthorizer <api.html#pyftpdlib.authorizers.WindowsAuthorizer>`__ classes. 346 347Does pyftpdlib support FTP over TLS/SSL (FTPS)? 348----------------------------------------------- 349 350Yes, starting from version 0.6.0, see: 351`Does pyftpdlib support FTP over TLS/SSL (FTPS)?`_ 352 353What about SITE commands? 354------------------------- 355 356The only supported SITE command is *SITE CHMOD* (change file mode). The user 357willing to add support for other specific SITE commands has to define a new 358``ftp_SITE_CMD`` method in the 359`FTPHandler <api.html#pyftpdlib.handlers.FTPHandler>`__ subclass and add a new 360entry in ``proto_cmds`` dictionary. Example: 361 362.. code-block:: python 363 364 from pyftpdlib.handlers import FTPHandler 365 366 proto_cmds = FTPHandler.proto_cmds.copy() 367 proto_cmds.update( 368 {'SITE RMTREE': dict(perm='R', auth=True, arg=True, 369 help='Syntax: SITE <SP> RMTREE <SP> path (remove directory tree).')} 370 ) 371 372 class CustomizedFTPHandler(FTPHandler): 373 proto_cmds = proto_cmds 374 375 def ftp_SITE_RMTREE(self, line): 376 """Recursively remove a directory tree.""" 377 # implementation here 378 # ... 379