diff options
Diffstat (limited to 'ipapython')
-rw-r--r-- | ipapython/ipautil.py | 128 | ||||
-rw-r--r-- | ipapython/nsslib.py | 92 |
2 files changed, 109 insertions, 111 deletions
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py index e80434cf..22c8e293 100644 --- a/ipapython/ipautil.py +++ b/ipapython/ipautil.py @@ -48,6 +48,7 @@ from dns.exception import DNSException from ipapython.ipa_log_manager import * from ipapython import ipavalidate from ipapython import config + try: from subprocess import CalledProcessError except ImportError: @@ -672,72 +673,103 @@ def get_gsserror(e): def host_port_open(host, port, socket_type=socket.SOCK_STREAM, socket_timeout=None): - families = (socket.AF_INET, socket.AF_INET6) - success = False - - for family in families: + for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket_type): + af, socktype, proto, canonname, sa = res try: try: - s = socket.socket(family, socket_type) + s = socket.socket(af, socktype, proto) except socket.error: + s = None continue if socket_timeout is not None: s.settimeout(socket_timeout) - s.connect((host, port)) + s.connect(sa) if socket_type == socket.SOCK_DGRAM: s.send('') s.recv(512) - success = True + return True except socket.error, e: pass finally: - s.close() - - if success: - return True + if s: + s.close() return False def bind_port_responder(port, socket_type=socket.SOCK_STREAM, socket_timeout=None, responder_data=None): - families = (socket.AF_INET, socket.AF_INET6) + host = None # all available interfaces + last_socket_error = None - host = '' # all available interfaces + # At first try to create IPv6 socket as it is able to accept both IPv6 and + # IPv4 connections (when not turned off) + families = (socket.AF_INET6, socket.AF_INET) + s = None for family in families: try: - s = socket.socket(family, socket_type) + addr_infos = socket.getaddrinfo(host, port, family, socket_type, 0, + socket.AI_PASSIVE) except socket.error, e: - if family == families[-1]: # last available family - raise e + last_socket_error = e + continue + for res in addr_infos: + af, socktype, proto, canonname, sa = res + try: + s = socket.socket(af, socktype, proto) + except socket.error, e: + last_socket_error = e + s = None + continue - if socket_timeout is not None: - s.settimeout(socket_timeout) + if socket_timeout is not None: + s.settimeout(1) - if socket_type == socket.SOCK_STREAM: - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + if af == socket.AF_INET6: + try: + # Make sure IPv4 clients can connect to IPv6 socket + s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) + except socket.error: + pass - try: - s.bind((host, port)) + if socket_type == socket.SOCK_STREAM: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - if socket_type == socket.SOCK_STREAM: - s.listen(1) - connection, client_address = s.accept() try: - if responder_data: - connection.sendall(responder_data) #pylint: disable=E1101 + s.bind(sa) + + while True: + if socket_type == socket.SOCK_STREAM: + s.listen(1) + connection, client_address = s.accept() + try: + if responder_data: + connection.sendall(responder_data) #pylint: disable=E1101 + finally: + connection.close() + elif socket_type == socket.SOCK_DGRAM: + data, addr = s.recvfrom(1) + + if responder_data: + s.sendto(responder_data, addr) + except socket.timeout: + # Timeout is expectable as it was requested by caller, raise + # the exception back to him + raise + except socket.error, e: + last_socket_error = e + s.close() + s = None + continue finally: - connection.close() - elif socket_type == socket.SOCK_DGRAM: - data, addr = s.recvfrom(1) + if s: + s.close() - if responder_data: - s.sendto(responder_data, addr) - finally: - s.close() + if s is None and last_socket_error is not None: + raise last_socket_error # pylint: disable=E0702 def is_host_resolvable(fqdn): for rdtype in (rdatatype.A, rdatatype.AAAA): @@ -1015,34 +1047,24 @@ def utf8_encode_values(values): def wait_for_open_ports(host, ports, timeout=0): """ Wait until the specified port(s) on the remote host are open. Timeout - in seconds may be specified to limit the wait. + in seconds may be specified to limit the wait. If the timeout is + exceeded, socket.timeout exception is raised. """ if not isinstance(ports, (tuple, list)): ports = [ports] - root_logger.debug('wait_for_open_ports: %s %s timeout %d' % (host, ports, timeout)) + root_logger.debug('wait_for_open_ports: %s %s timeout %d', host, ports, timeout) op_timeout = time.time() + timeout - ipv6_failover = False for port in ports: while True: - try: - if ipv6_failover: - s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - else: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((host, port)) - s.close() + port_open = host_port_open(host, port) + + if port_open: break - except socket.error, e: - if e.errno == 111: # 111: Connection refused - if timeout and time.time() > op_timeout: # timeout exceeded - raise e - time.sleep(1) - elif not ipv6_failover: # fallback to IPv6 connection - ipv6_failover = True - else: - raise e + if timeout and time.time() > op_timeout: # timeout exceeded + raise socket.timeout() + time.sleep(1) def wait_for_open_socket(socket_name, timeout=0): """ diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py index aae24467..06bcba64 100644 --- a/ipapython/nsslib.py +++ b/ipapython/nsslib.py @@ -115,6 +115,12 @@ def client_auth_data_callback(ca_names, chosen_nickname, password, certdb): return False return False +_af_dict = { + socket.AF_INET: io.PR_AF_INET, + socket.AF_INET6: io.PR_AF_INET6, + socket.AF_UNSPEC: io.PR_AF_UNSPEC +} + class NSSAddressFamilyFallback(object): def __init__(self, family): self.sock_family = family @@ -124,67 +130,39 @@ class NSSAddressFamilyFallback(object): """ Translate a family from python socket module to nss family. """ - if sock_family in [ socket.AF_INET, socket.AF_UNSPEC ]: - return io.PR_AF_INET - elif sock_family == socket.AF_INET6: - return io.PR_AF_INET6 - else: + try: + return _af_dict[sock_family] + except KeyError: raise ValueError('Uknown socket family %d\n', sock_family) - def _get_next_family(self): - if self.sock_family == socket.AF_UNSPEC and \ - self.family == io.PR_AF_INET: - return io.PR_AF_INET6 - - return None - def _create_socket(self): self.sock = io.Socket(family=self.family) - def _connect_socket_family(self, host, port, family): - root_logger.debug("connect_socket_family: host=%s port=%s family=%s", - host, port, io.addr_family_name(family)) - try: - addr_info = [ ai for ai in io.AddrInfo(host) if ai.family == family ] - # No suitable families - if len(addr_info) == 0: - raise NSPRError(error.PR_ADDRESS_NOT_SUPPORTED_ERROR, - "Cannot resolve %s using family %s" % (host, io.addr_family_name(family))) - - # Try connecting to the NetworkAddresses - for net_addr in addr_info: - net_addr.port = port - root_logger.debug("connecting: %s", net_addr) - try: - self.sock.connect(net_addr) - except Exception, e: - root_logger.debug("Could not connect socket to %s, error: %s, retrying..", - net_addr, str(e)) - continue - else: - return - - # Could not connect with any of NetworkAddresses - raise NSPRError(error.PR_ADDRESS_NOT_SUPPORTED_ERROR, - "Could not connect to %s using any address" % host) - except ValueError, e: - raise NSPRError(error.PR_ADDRESS_NOT_SUPPORTED_ERROR, e.message) - def connect_socket(self, host, port): try: - self._connect_socket_family(host, port, self.family) - except NSPRError, e: - if e.errno == error.PR_ADDRESS_NOT_SUPPORTED_ERROR: - next_family = self._get_next_family() - if next_family: - self.family = next_family - self._create_socket() - self._connect_socket_family(host, port, self.family) - else: - root_logger.debug('No next family to try..') - raise e - else: - raise e + addr_info = io.AddrInfo(host, family=self.family) + except Exception: + raise NSPRError(error.PR_ADDRESS_NOT_SUPPORTED_ERROR, + "Cannot resolve %s using family %s" % (host, + io.addr_family_name(self.family))) + + for net_addr in addr_info: + root_logger.debug("Connecting: %s", net_addr) + net_addr.port = port + self.family = net_addr.family + try: + self._create_socket() + self.sock.connect(net_addr) + return + except Exception, e: + root_logger.debug("Could not connect socket to %s, error: %s", + net_addr, str(e)) + root_logger.debug("Try to continue with next family...") + continue + + raise NSPRError(error.PR_ADDRESS_NOT_SUPPORTED_ERROR, + "Could not connect to %s using any address" % host) + class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback): default_port = httplib.HTTPSConnection.default_port @@ -218,12 +196,10 @@ class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback): nss.nss_init(dbdir) ssl.set_domestic_policy() nss.set_password_callback(self.password_callback) - self._create_socket() def _create_socket(self): - - #TODO remove the try block once python-nss is guaranteed to - #contain these values + # TODO: remove the try block once python-nss is guaranteed to contain + # these values try : ssl_enable_renegotiation = SSL_ENABLE_RENEGOTIATION #pylint: disable=E0602 ssl_require_safe_negotiation = SSL_REQUIRE_SAFE_NEGOTIATION #pylint: disable=E0602 |