giampaolo / pyftpdlib

Extremely fast and scalable Python FTP server library
MIT License
1.66k stars 266 forks source link

pyftpdlib server resets client connection in PASV/EPSV mode: curl: (13) Bad PASV/EPSV response: 200 #604

Closed Will-Fowler closed 11 months ago

Will-Fowler commented 1 year ago

Trying to download a file to a device over FTPS. I am using curl (7.88.1) as the client on the remote machine and pyftpdlib (1.5.7) to implement the FTP server on the local machine. While attempting to download my_file.txt, curl outputs: curl: (13) Bad PASV/EPSV response: 200.

In Wireshark I can see that when the client attempts to establish a TLS connection on the passive port, the pyftpdlib server responds to the "client hello" with a RST, ACK packet.

I have configured a Filezilla FTP server to 'Require explicit FTP over TLS' using the same certificate, key and passive port (1280), and using the same Curl command works without any issue.

The Curl command I am running on the remote is: curl --ssl-reqd ftp://xxx.xxx.4.233:2128/myfile.txt -o my_file.txt -k -v and the basic implementation of the server is below (I have also attached verbose logs for the pyftpdlib server and curl):

import os
import threading

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import TLS_FTPHandler
from pyftpdlib.servers import ThreadedFTPServer

CERTFILE = os.path.abspath(os.path.join(os.path.dirname(__file__),
                                        "certificate.pem"))
KEYFILE = os.path.abspath(os.path.join(os.path.dirname(__file__),
                                        "private_key.pem"))

class FtpSrv:
        def __init__(self):
                self.server = None
                self.server_thread = None

        def start_ftp_server(self, port=2121):
                server_address = ('0.0.0.0', port)
                authorizer = DummyAuthorizer()
                authorizer.add_anonymous(os.getcwd())

                handler = TLS_FTPHandler
                handler.authorizer = authorizer
                handler.banner = "pyftpdlib based ftpd ready."
                handler.passive_ports = [1280]
                handler.certfile = CERTFILE
                handler.keyfile = KEYFILE

                self.server = ThreadedFTPServer(server_address, handler)

                self.server.max_cons = 256
                self.server.max_cons_per_ip = 5

                self.server_thread = threading.Thread(target=self.server.serve_forever, daemon=True)
                self.server_thread.start()

server = FtpSrv()
server.start_ftp_server(port=2128)

curl_log.txt pyftpdlib_log.txt