Commit dbaaff66 authored by Vincent Pelletier's avatar Vincent Pelletier

ERP5Type.ConnectionPlugin.SFTPConnection: Add bind address support.

Requires creating socket outside of paramiko.Transport .
parent 4781d9c8
...@@ -29,10 +29,11 @@ ...@@ -29,10 +29,11 @@
import os, socket import os, socket
from urlparse import urlparse from urlparse import urlparse
from socket import error from socket import error, socket, getaddrinfo, AF_UNSPEC, SOCK_STREAM
from xmlrpclib import Binary from xmlrpclib import Binary
from cStringIO import StringIO from cStringIO import StringIO
from paramiko import Transport, RSAKey, SFTPClient from paramiko import Transport, RSAKey, SFTPClient
from paramiko.util import retry_on_signal
class SFTPError(Exception): class SFTPError(Exception):
""" """
...@@ -45,20 +46,40 @@ class SFTPConnection: ...@@ -45,20 +46,40 @@ class SFTPConnection:
Handle a SFTP (SSH over FTP) Connection Handle a SFTP (SSH over FTP) Connection
""" """
def __init__(self, url, user_name, password=None, private_key=None): def __init__(self, url, user_name, password=None, private_key=None,
bind_address=None):
self.url = url self.url = url
self.user_name = user_name self.user_name = user_name
if password and private_key: if password and private_key:
raise SFTPError("Password and private_key cannot be defined simultaneously") raise SFTPError("Password and private_key cannot be defined simultaneously")
self.password = password self.password = password
self.private_key = private_key self.private_key = private_key
self.bind_address = bind_address
def connect(self): def connect(self):
""" Get a handle to a remote connection """ """ Get a handle to a remote connection """
# Check URL # Check URL
schema = urlparse(self.url) schema = urlparse(self.url)
if schema.scheme == 'sftp': if schema.scheme == 'sftp':
self.transport = Transport((schema.hostname, int(schema.port))) hostname = schema.hostname
port = int(schema.port)
# Socket creation code inspired from paramiko.Transport.__init__
# with added bind support.
for family, socktype, _, _, _ in getaddrinfo(
hostname, port, AF_UNSPEC, SOCK_STREAM,
):
if socktype == SOCK_STREAM:
sock = socket(family, SOCK_STREAM)
if self.bind_address:
# XXX: Expects bind address to be of same family as hostname.
# May not be easy if name resolution is involved.
# Try to reconciliate them ?
sock.bind((self.bind_addres, 0))
retry_on_signal(lambda: sock.connect((hostname, port)))
break
else:
raise SFTPError('No suitable socket family found')
self.transport = Transport(sock)
else: else:
raise SFTPError('Not a valid sftp url %s, type is %s' %(self.url, schema.scheme)) raise SFTPError('Not a valid sftp url %s, type is %s' %(self.url, schema.scheme))
# Add authentication to transport # Add authentication to transport
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment