Problème de connexion implicite TLS Python FTP

j'ai besoin de me connecter au serveur FTPS auquel je peux me connecter avec succès en utilisant lftp. Cependant, quand J'essaie avec Python ftplib.FTP_TLS, it times out, la trace de la pile montre qu'elle attend que le serveur envoie un message de bienvenue ou autre. Est-ce que quelqu'un sait quel est le problème et comment le surmonter? Je me demande si quelque chose doit être fait du côté du serveur, mais pourquoi le client lftp fonctionne bien. Toute aide est grandement appréciée.

Voici la pile trace:

    ftp = ftplib.FTP_TLS()  
    ftp.connect(cfg.HOST, cfg.PORT, timeout=60)
  File "C:UsersusernameSoftwaresPython27libftplib.py", line 135, in connect  
    self.welcome = self.getresp()  
  File "C:UsersusernameSoftwaresPython27libftplib.py", line 210, in getresp  
    resp = self.getmultiline()  
  File "C:UsersusernameSoftwaresPython27libftplib.py", line 196, in getmultiline  
    line = self.getline()  
  File "C:UsersusernameSoftwaresPython27libftplib.py", line 183, in getline  
    line = self.file.readline()  
  File "C:UsersusernameSoftwaresPython27libsocket.py", line 447, in readline  
    data = self._sock.recv(self._rbufsize)  
socket.timeout: timed out  

une connexion réussie en utilisant lftp vers le même serveur ftps:

$ lftp
lftp :~> open ftps://ip_address:990
lftp ip_address:~> set ftps:initial-prot P
lftp ip_address:~> login ftps_user_id  ftps_user_passwd
lftp sftp_user_id@ip_address:~> ls
ls: Fatal error: SSL_connect: self signed certificate
lftp ftps_user_id@ip_address:~> set ssl:verif-certificate off
lftp ftps_user_id@ip_address:~> ls
lftp ftps_user_id@ip_address:/>

BTW, j'utilise Python 2.7.3. J'ai fait pas mal de recherche en utilisant Google, mais je n'ai rien trouvé d'utile.

j'ai toujours ce problème, j'apprécie si quelqu'un peut aider. En regardant de près le FTP.connect() la connexion au serveur n'est pas un problème mais obtenir un accusé de réception (ou le message de bienvenue) du serveur est un problème. lftp n'a pas ce problème et FileZilla n'a pas de problème non plus comme dans le log ici -

Status: Connecting to xx.xx.xx.xxx:990...  
Status: Connection established, initializing TLS...  
Status: Verifying certificate...  
Status: TLS/SSL connection established, waiting for welcome message...  
Response:   220-      Vous allez vous connecter sur un serveur prive  
Response:   220-     Seules les personnes habilitees y sont autorisees  
Response:   220 Les contrevenants s'exposent aux poursuites prevues par la loi.  
Command:    USER xxxxxxxxxxxxx  
Response:   331 Password required for xxxxxxxxxxxxx.  
Command:    PASS **********  
Response:   230 Login OK. Proceed.  
Command:    PBSZ 0  
Response:   200 PBSZ Command OK. Protection buffer size set to 0.  
Command:    PROT P  
Response:   200 PROT Command OK. Using Private data connection  
Status: Connected  
Status: Retrieving directory listing...  
Command:    PWD  
Response:   257 "/" is current folder.  
Command:    TYPE I  
Response:   200 Type set to I.  
Command:    PASV  
Response:   227 Entering Passive Mode (81,93,20,199,4,206).  
Command:    MLSD  
Response:   150 Opening BINARY mode data connection for MLSD /.  
Response:   226 Transfer complete. 0 bytes transferred. 0 bps.  
Status: Directory listing successful  
10
demandé sur Martin Prikryl 2012-08-28 21:30:02

5 réponses

j'ai travaillé sur le même problème pour une demi-journée et finalement pensé à elle.

pour le TLS/SSL FTP implicite (defualt port 990), notre programme client doit construire une connexion TLS/SSL immédiatement après la création de la socket. Mais la classe de python FTP_TLS ne recharge pas la se connecter fonction à partir de la classe FTP. Nous avons besoin pour résoudre le problème:

class tyFTP(ftplib.FTP_TLS):
  def __init__(self,
               host='',
               user='',
               passwd='',
               acct='',
               keyfile=None,
               certfile=None,
               timeout=60):

    ftplib.FTP_TLS.__init__(self,
                            host=host,
                            user=user,
                            passwd=passwd,
                            acct=acct,
                            keyfile=keyfile,
                            certfile=certfile,
                            timeout=timeout)

  def connect(self, host='', port=0, timeout=-999):
    """Connect to host.  Arguments are:
    - host: hostname to connect to (string, default previous host)
    - port: port to connect to (integer, default previous port)
    """
    if host != '':
        self.host = host
    if port > 0:
        self.port = port
    if timeout != -999:
        self.timeout = timeout
    try:
        self.sock = socket.create_connection((self.host, self.port), self.timeout)
        self.af = self.sock.family
        # add this line!!!
        self.sock = ssl.wrap_socket(self.sock,
                                    self.keyfile,
                                    self.certfile,
                                    ssl_version=ssl.PROTOCOL_TLSv1)
        # add end
        self.file = self.sock.makefile('rb')
        self.welcome = self.getresp()
    except Exception as e:
        print(e)
    return self.welcome

cette classe dérivée recharge le se connecter fonction et construit un wrapper autour de la socket à TLS. Après vous connectez-vous et connectez-vous au serveur FTP, vous devez appeler: FTP_TLS.prot_p() avant d'exécuter une commande FTP!

Espérons que cela va aider ^_^

18
répondu tandztc 2016-03-20 01:20:15

extension de la réponse de NERV - ce qui m'a énormément aidé, voici comment j'ai pu résoudre mon problème avec une connexion TLS implicite sur le port 990 nécessitant une authentification.

from ftplib import FTP_TLS
import socket
import ssl

class tyFTP(FTP_TLS):
    def __init__(self, host='', user='', passwd='', acct='', keyfile=None, certfile=None, timeout=60):
        FTP_TLS.__init__(self, host, user, passwd, acct, keyfile, certfile, timeout)
    def connect(self, host='', port=0, timeout=-999):
        if host != '':
            self.host = host
        if port > 0:
            self.port = port
        if timeout != -999:
            self.timeout = timeout

        try: 
            self.sock = socket.create_connection((self.host, self.port), self.timeout)
            self.af = self.sock.family
            self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, ssl_version=ssl.PROTOCOL_TLSv1)
            self.file = self.sock.makefile('rb')
            self.welcome = self.getresp()
        except Exception as e:
            print e
        return self.welcome

puis à partir de mon application principale j'ai fait ceci:

from ImplicityTLS import tyFTP
server = tyFTP()
server.connect(host="xxxxx", port=990)
server.login(user="yyyy", passwd="fffff")
server.prot_p()

et c'est tout, j'ai pu télécharger des fichiers, etc. Les accessoires vont à NERV pour la réponse originale.

8
répondu Brad Decker 2013-12-14 15:07:11

pour étendre les solutions qui ont été proposées jusqu'à présent, le problème est que les connexions FTPS implicites ont besoin que la socket soit enveloppée ssl automatiquement, avant que nous ayons la chance d'appeler login(). Beaucoup de sous-classes que les gens proposent font cela dans le contexte de la méthode connect, nous pouvons plus généralement gérer cela en modifiant le get/set of self.chaussette avec une propriété auto-wrap sur le plateau:

import ftplib
import ssl

class ImplicitFTP_TLS(ftplib.FTP_TLS):
    """FTP_TLS subclass that automatically wraps sockets in SSL to support implicit FTPS."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._sock = None

    @property
    def sock(self):
        """Return the socket."""
        return self._sock

    @sock.setter
    def sock(self, value):
        """When modifying the socket, ensure that it is ssl wrapped."""
        if value is not None and not isinstance(value, ssl.SSLSocket):
            value = self.context.wrap_socket(value)
        self._sock = value

L'utilisation est essentiellement la même qu'avec la norme FTP_TLS classe:

ftp_client = ImplicitFTP_TLS()
ftp_client.connect(host='ftp.example.com', port=990)
ftp_client.login(user='USERNAME', passwd='PASSWORD')
ftp_client.prot_p()
8
répondu George Leslie-Waksman 2017-09-21 13:04:49

la réponse de NERV et L'échantillon de Brad Decker ont été très utiles. Bravo à eux. Ils m'a sauvé heures.

dans mon cas, la connexion a fonctionné une fois que j'ai enlevé le ssl_version paramètre de ssl.wrap_socket méthode. Aussi, pour envoyer une commande au serveur, j'ai dû remplacer le ntransfercmd méthode de l' FTP_TLS classe et de supprimer le ssl_version paramètre.

C'est le code qui a fonctionné pour moi:

from ftplib import FTP_TLS, FTP
import socket
import ssl

class IMPLICIT_FTP_TLS(FTP_TLS):
    def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
        certfile=None, timeout=60):
        FTP_TLS.__init__(self, host, user, passwd, acct, keyfile, certfile, timeout)

    def connect(self, host='', port=0, timeout=-999):
        '''Connect to host.  Arguments are:
        - host: hostname to connect to (string, default previous host)
        - port: port to connect to (integer, default previous port)
        '''
        if host != '':
            self.host = host
        if port > 0:
            self.port = port
        if timeout != -999:
            self.timeout = timeout
        try:
            self.sock = socket.create_connection((self.host, self.port), self.timeout)
            self.af = self.sock.family
            self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
            self.file = self.sock.makefile('rb')
            self.welcome = self.getresp()
        except Exception as e:
            print (e)
        return self.welcome

    def ntransfercmd(self, cmd, rest=None):
        conn, size = FTP.ntransfercmd(self, cmd, rest)
        if self._prot_p:
            conn = ssl.wrap_socket(conn, self.keyfile, self.certfile)
        return conn, size

et l'échantillon obligatoire:

>>> ftps = IMPLICIT_FTP_TLS()
>>> ftps.connect(host='your.ftp.host', port=990)
>>> ftps.login(user="your_user", passwd="your_passwd")
>>> ftps.prot_p()
>>> ftps.retrlines('LIST')
3
répondu Juan Moreno 2015-10-23 18:55:57

je sais que ce fil est assez ancien et ftp n'est pas aussi populaire qu'il l'était autrefois, mais de toute façon, au cas où il aide quelqu'un que je voudrais faire une contribution supplémentaire. J'ai rencontré une situation similaire en essayant de me connecter au serveur ftp en utilisant implicitement (Port 990) ftps en mode passif. Dans ce cas, le serveur, après négociation de la connexion initiale, fournit habituellement une nouvelle adresse IP hôte et un nouveau port, probablement différents de ceux qui ont été utilisés pour faire la connexion initiale, sur lesquels la réels transferts de données sont censés se produire. Aucun problème, les clients ftps, y compris python, peuvent gérer cela, seulement ce serveur particulier fournissait une adresse IP non-routable (probablement interne au pare-feu). J'ai remarqué que FileZilla s'est connecté sans problème, mais Python ftplib ne pouvait pas. Puis je suis tombé sur ce fil:

comment remplacer une adresse IP non routable par une adresse serveur sur ftplib

ce qui m'a mis dans le coup. Utilisation de la méthodologie Grzegorz Wierzowiecki J'ai étendu la méthode à laquelle fait allusion dans ce fil et est venu avec ceci, qui a résolu mon problème.

import ftplib, os, sys
import socket
import ssl
FTPS_OBJ = ftplib.FTP_TLS



def conn_i_ftps(FTP_Site, Login_Name, Login_Password):
    print "Starting IMPLICIT ftp_tls..."
    ftps = tyFTP()
    print ftps.connect(host=FTP_Site, port=990, timeout=120)
    ftps.prot_p()
    ftps.login(user=Login_Name, passwd=Login_Password)
    print "Logged In"
    ftps.retrlines('LIST')
    # return ftps


class tyFTP(FTPS_OBJ):
    def __init__(self, host='', user='', passwd='', acct='', keyfile=None, certfile=None, timeout=60):
        FTPS_OBJ.__init__(self, host, user, passwd, acct, keyfile, certfile, timeout)

    def connect(self, host='', port=0, timeout=-999):
        if host != '':
            self.host = host
        if port > 0:
            self.port = port
        if timeout != -999:
            self.timeout = timeout

        try:
            self.sock = socket.create_connection((self.host, self.port), self.timeout)
            self.af = self.sock.family
            self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
            self.file = self.sock.makefile('rb')
            self.welcome = self.getresp()
        except Exception as e:
            print e
        return self.welcome

    def makepasv(self):
        print port #<---Show passively assigned port
        print host #<---Show the non-routable, passively assigned IP
        host, port = FTPS_OBJ.makepasv(self)
        host = socket.gethostbyname(self.host) #<---- This changes the host back to the original IP that was used for the connection
        print 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
        print host #<----Showing the original IP
        return host, port

Puis le code est appelé comme ceci

FTP_Site       =  "ftp.someserver.com"
Login_Name     =  "some_name"
Login_Password =  "some_passwd"

conn_i_ftps(FTP_Site, Login_Name, Login_Password)

je suppose que l'on peut envelopper les lignes où l'hôte est changé de nouveau avec une instruction IF identifiant des adresses non-routables, comme ceci:

if host.split(".")[0] in (10, 192, 172):
     host = socket.gethostbyname(self.host)
     .
     .
     .
0
répondu andrew-3D 2017-05-23 12:02:31