corrigiendo el zócalo de la biblioteca incorporada definitivamente no será una opción para todos, pero mi solución se parchear socket.create_connection()
utilizar un proxy HTTP cuando el nombre de host coincide con una lista blanca:
from base64 import b64encode
from functools import wraps
import socket
_real_create_connection = socket.create_connection
_proxied_hostnames = {} # hostname: (proxy_host, proxy_port, proxy_auth)
def register_proxy (host, proxy_host, proxy_port, proxy_username=None, proxy_password=None):
proxy_auth = None
if proxy_username is not None or proxy_password is not None:
proxy_auth = b64encode('{}:{}'.format(proxy_username or '', proxy_password or ''))
_proxied_hostnames[host] = (proxy_host, proxy_port, proxy_auth)
@wraps(_real_create_connection)
def create_connection (address, *args, **kwds):
host, port = address
if host not in _proxied_hostnames:
return _real_create_connection(address, *args, **kwds)
proxy_host, proxy_port, proxy_auth = _proxied_hostnames[host]
conn = _real_create_connection((proxy_host, proxy_port), *args, **kwds)
try:
conn.send('CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n{auth_header}\r\n'.format(
host=host, port=port,
auth_header=('Proxy-Authorization: basic {}\r\n'.format(proxy_auth) if proxy_auth else '')
))
response = ''
while not response.endswith('\r\n\r\n'):
response += conn.recv(4096)
if response.split()[1] != '200':
raise socket.error('CONNECT failed: {}'.format(response.strip()))
except socket.error:
conn.close()
raise
return conn
socket.create_connection = create_connection
también tuve que crear una subclase de ftplib.FTP que ignora la host
devuelto por PASV
y EPSV
comandos FTP. Ejemplo de uso:
from ftplib import FTP
import paramiko # For SFTP
from proxied_socket import register_proxy
class FTPIgnoreHost (FTP):
def makepasv (self):
# Ignore the host returned by PASV or EPSV commands (only use the port).
return self.host, FTP.makepasv(self)[1]
register_proxy('ftp.example.com', 'proxy.example.com', 3128, 'proxy_username', 'proxy_password')
ftp_connection = FTP('ftp.example.com', 'ftp_username', 'ftp_password')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # If you don't care about security.
ssh.connect('ftp.example.com', username='sftp_username', password='sftp_password')
sftp_connection = ssh.open_sftp()
el enlace en la respuesta anterior se 404. podría haber significado ésta: http : //mail.python.org/pipermail/python-list/2004-October/863602.html – AndrewR
La parte "anonymous at ftp.download.com" es pura ficción. Nada de eso ha sido mencionado en ningún RFC o implementado/respaldado por ningún servidor, hasta donde yo sé. Nativamente, el protocolo FTP no es compatible con el proxy. AFAIK, la única forma de proxy FTP es mediante el uso de SOCKS, en cuyo caso se supone que el cliente debe conectarse a SOCKS y el último debe ser instruido sobre cuál es el servidor FTP real. –
Esta respuesta me resuelve un gran dolor de cabeza. ¡¡¡Gracias!!! – dgg32