[Freeipa-devel] [PATCH] 049 Make nsslib IPv6 aware

Jakub Hrozek jhrozek at redhat.com
Thu Feb 17 19:32:29 UTC 2011


On Thu, Feb 17, 2011 at 08:25:37PM +0100, Jakub Hrozek wrote:
> On Wed, Feb 09, 2011 at 10:23:27AM +0100, Jan Zelený wrote:
> > Jakub Hrozek <jhrozek at redhat.com> wrote:
> > > On Thu, Feb 03, 2011 at 02:23:11PM +0100, Jan Zelený wrote:
> > > > Jakub Hrozek <jhrozek at redhat.com> wrote:
> > > > > Hi,
> > > > > 
> > > > > attached is a patch to nsslib.py that changes its semantics so
> > > > > it is able to work with different address families. It is the last
> > > > > piece of IPv6 support.
> > > > > 
> > > > > Aside from the hunks in the patch, I still need to set Requires: in the
> > > > > patch (don't know the exact version yet). Also, the attached patch
> > > > > always tries IPv4 first and only falls back to IPv6. I think there
> > > > > should be a config option that tells IPA to prefer one of the address
> > > > > families or use it exclusively for performance reasons.
> > > > > 
> > > > > Please note that the patch requires the latest changes to python-nss
> > > > > in order to work correctly. Since John is still working on python-nss
> > > > > packages, this patch should be treated as a preview and not pushed even
> > > > > if it is deemed OK. At this stage, I'd like to get at least the general
> > > > > approach and code reviewed so I can fix it tomorrow.
> > > > > 
> > > > > Thank you,
> > > > > 
> > > > >     Jakub
> > > > 
> > > > The patch looks ok, all my questions answered off-list. Also tested with
> > > > IPv4 (latest python-nss installed) and IPv6, both work fine.
> > > > 
> > > > ACK
> > > > 
> > > > Jan
> > > 
> > > Thanks for the review. But attached is a new version of the patch that
> > > changes the semantics a little based on what's recommended by the new
> > > version of python-nss: don't construct the NetworkAddress object
> > > manually, but rather resolve the hostname using the AddrInfo object and
> > > then try connecting to the list of of NetworkAddress object manually.
> > 
> > Changes consulted off-list, the patch looks good. Will do some more testing on 
> > RHEL6. Unless I find some issues, this patch is ACKed.
> > 
> > Jan
> > 
> 
> One more change - bumped the minimum required version of python-nss to
> 0.11 which is in the nightly devel repo now.
> 

and now with the patch attached.
-------------- next part --------------
>From fd089113524c250c502eb2e4028affd29754dd77 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 2 Feb 2011 13:57:16 +0100
Subject: [PATCH] Make nsslib IPv6 aware

---
 freeipa.spec.in     |    5 ++-
 ipapython/nsslib.py |  108 +++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 96 insertions(+), 17 deletions(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index f301aa2..0e54caf 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -177,7 +177,7 @@ Requires: python-kerberos >= 1.1-3
 Requires: authconfig
 Requires: gnupg
 Requires: pyOpenSSL
-Requires: python-nss >= 0.9-8
+Requires: python-nss >= 0.11
 Requires: python-lxml
 Requires: python-netaddr
 
@@ -476,6 +476,9 @@ fi
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf
 
 %changelog
+* Thu Feb  17 2011 Jakub Hrozek <jhrozek at redhat.com> - 1.99-45
+- Set minimum version of python-nss to 0.11 to make sure IPv6 support is in
+
 * Wed Feb  9 2011 Rob Crittenden <rcritten at redhat.com> - 1.99-44
 - Set minimum version of sssd to 1.5.1
 
diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py
index fad65a3..8d77863 100644
--- a/ipapython/nsslib.py
+++ b/ipapython/nsslib.py
@@ -21,12 +21,14 @@
 import sys
 import httplib
 import getpass
+import socket
 import logging
 
 from nss.error import NSPRError
 import nss.io as io
 import nss.nss as nss
 import nss.ssl as ssl
+import nss.error as error
 
 def auth_certificate_callback(sock, check_sig, is_server, certdb):
     cert_is_valid = False
@@ -113,11 +115,84 @@ def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
                 return False
         return False
 
-class NSSConnection(httplib.HTTPConnection):
+class NSSAddressFamilyFallback(object):
+    def __init__(self, family):
+        self.sock_family = family
+        self.family = self._get_nss_family(self.sock_family)
+
+    def _get_nss_family(self, sock_family):
+        """
+        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:
+            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):
+        logging.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
+                logging.debug("connecting: %s", net_addr)
+                try:
+                    self.sock.connect(net_addr, family)
+                except Exception, e:
+                    logging.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:
+                    logging.debug('No next family to try..')
+                    raise e
+            else:
+                raise e
+
+class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback):
     default_port = httplib.HTTPSConnection.default_port
 
-    def __init__(self, host, port=None, strict=None, dbdir=None):
+    def __init__(self, host, port=None, strict=None,
+                 dbdir=None, family=socket.AF_UNSPEC):
         httplib.HTTPConnection.__init__(self, host, port, strict)
+        NSSAddressFamilyFallback.__init__(self, family)
 
         if not dbdir:
             raise RuntimeError("dbdir is required")
@@ -134,10 +209,12 @@ class NSSConnection(httplib.HTTPConnection):
         nss.nss_init(dbdir)
         ssl.set_domestic_policy()
         nss.set_password_callback(self.password_callback)
+        self._create_socket()
 
+    def _create_socket(self):
         # Create the socket here so we can do things like let the caller
         # override the NSS callbacks
-        self.sock = ssl.SSLSocket()
+        self.sock = ssl.SSLSocket(family=self.family)
         self.sock.set_ssl_option(ssl.SSL_SECURITY, True)
         self.sock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True)
 
@@ -146,7 +223,8 @@ class NSSConnection(httplib.HTTPConnection):
 
         # Provide a callback to verify the servers certificate
         self.sock.set_auth_certificate_callback(auth_certificate_callback,
-                                           nss.get_default_certdb())
+                                                nss.get_default_certdb())
+        self.sock.set_hostname(self.host)
 
     def password_callback(self, slot, retry, password):
         if not retry and password: return password
@@ -160,11 +238,7 @@ class NSSConnection(httplib.HTTPConnection):
         pass
 
     def connect(self):
-        logging.debug("connect: host=%s port=%s", self.host, self.port)
-        self.sock.set_hostname(self.host)
-        net_addr = io.NetworkAddress(self.host, self.port)
-        logging.debug("connect: %s", net_addr)
-        self.sock.connect(net_addr)
+        self.connect_socket(self.host, self.port)
 
     def endheaders(self, message=None):
         """
@@ -210,20 +284,22 @@ class NSSHTTPS(httplib.HTTP):
             port = None
         self._setup(self._connection_class(host, port, strict, dbdir=dbdir))
 
-class NSPRConnection(httplib.HTTPConnection):
+class NSPRConnection(httplib.HTTPConnection, NSSAddressFamilyFallback):
     default_port = httplib.HTTPConnection.default_port
 
-    def __init__(self, host, port=None, strict=None):
+    def __init__(self, host, port=None, strict=None, family=socket.AF_UNSPEC):
         httplib.HTTPConnection.__init__(self, host, port, strict)
+        NSSAddressFamilyFallback.__init__(self, family)
 
         logging.debug('%s init %s', self.__class__.__name__, host)
+        self._create_socket()
+
+    def _create_socket(self):
+        super(NSPRConnection, self)._create_socket()
+        self.sock.set_hostname(self.host)
 
-        self.sock = io.Socket()
     def connect(self):
-        logging.debug("connect: host=%s port=%s", self.host, self.port)
-        net_addr = io.NetworkAddress(self.host, self.port)
-        logging.debug("connect: %s", net_addr)
-        self.sock.connect(net_addr)
+        self.connect_socket(self.host, self.port)
 
 class NSPRHTTP(httplib.HTTP):
     _http_vsn = 11
-- 
1.7.4



More information about the Freeipa-devel mailing list