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
>From 4b85251c303e8519939b702254ee0def932f8ed6 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Wed, 2 Feb 2011 13:57:16 +0100
Subject: [PATCH] Make nsslib IPv6 aware

---
 ipapython/nsslib.py |   89 +++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 73 insertions(+), 16 deletions(-)

diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py
index 129f1a0..7abbcf0 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,65 @@ 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 _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:
+           net_addr = io.NetworkAddress(host, port, family)
+        except ValueError, e:
+           raise NSPRError(error.PR_ADDRESS_NOT_SUPPORTED_ERROR, e.message)
+        logging.debug("connect: %s", net_addr)
+        self.sock.connect(net_addr, family)
+
+    def _create_socket(self):
+        self.sock = io.Socket(family=self.family)
+
+    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")
@@ -130,10 +186,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)
 
@@ -142,7 +200,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
@@ -156,11 +215,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):
         """
@@ -206,20 +261,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.3.5

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to