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
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 ^_^
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.
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()
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')
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)
.
.
.