Author: Amaury Forgeot d'Arc <[email protected]>
Branch: stdlib-2.7.10
Changeset: r78047:ae7a5e9438ee
Date: 2015-06-12 18:33 +0200
http://bitbucket.org/pypy/pypy/changeset/ae7a5e9438ee/

Log:    Add support for "alpn" in SSL.

        Not tested at all: my machine does not have the correct version.

diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -65,8 +65,8 @@
 constants["HAS_SNI"] = HAS_SNI
 constants["HAS_TLS_UNIQUE"] = HAVE_OPENSSL_FINISHED
 constants["HAS_ECDH"] = not OPENSSL_NO_ECDH
-constants["HAS_NPN"] = OPENSSL_NPN_NEGOTIATED
-constants["HAS_ALPN"] = HAVE_ALPN
+constants["HAS_NPN"] = HAS_NPN
+constants["HAS_ALPN"] = HAS_ALPN
 
 if not OPENSSL_NO_SSL2:
     constants["PROTOCOL_SSLv2"]  = PY_SSL_VERSION_SSL2
@@ -176,7 +176,45 @@
                                      client, client_len)
         return rffi.cast(rffi.INT, SSL_TLSEXT_ERR_OK)
 
+
+class SSLAlpnProtocols(object):
+
+    def __init__(self, ctx, protos):
+        self.protos = protos
+        self.buf, self.pinned, self.is_raw = rffi.get_nonmovingbuffer(protos)
+        ALPN_STORAGE.set(r_uint(rffi.cast(rffi.UINT, self.buf)), self)
+
+        with rffi.scoped_str2charp(protos) as protos_buf:
+            if libssl_SSL_CTX_set_alpn_protos(
+                    ctx, rffi.cast(rffi.UCHARP, protos_buf), len(protos)):
+                raise MemoryError
+        libssl_SSL_CTX_set_alpn_select_cb(
+            ctx, self.selectALPN_cb, self.buf)
+
+    def __del__(self):
+        rffi.free_nonmovingbuffer(
+            self.protos, self.buf, self.pinned, self.is_raw)
+
+    @staticmethod
+    def selectALPN_cb(s, out_ptr, outlen_ptr, client, client_len, args):
+        alpn = ALPN_STORAGE.get(r_uint(rffi.cast(rffi.UINT, args)))
+        if alpn and alpn.protos:
+            server = alpn.buf
+            server_len = len(alpn.protos)
+        else:
+            server = lltype.nullptr(rffi.CCHARP.TO)
+            server_len = 0
+
+        ret = libssl_SSL_select_next_proto(out_ptr, outlen_ptr,
+                                           server, server_len,
+                                           client, client_len)
+        if ret != OPENSSL_NPN_NEGOTIATED:
+            return rffi.cast(rffi.INT, SSL_TLSEXT_ERR_NOACK)
+        return rffi.cast(rffi.INT, SSL_TLSEXT_ERR_OK)
+
+
 NPN_STORAGE = RWeakValueDictionary(r_uint, SSLNpnProtocols)
+ALPN_STORAGE = RWeakValueDictionary(r_uint, SSLAlpnProtocols)
 
 SOCKET_STORAGE = RWeakValueDictionary(int, W_Root)
 
@@ -572,6 +610,18 @@
                     return space.wrap(
                         rffi.charpsize2str(out_ptr[0], intmask(len_ptr[0])))
 
+    def selected_alpn_protocol(self, space):
+        if not HAS_ALPN:
+            raise oefmt(space.w_NotImplementedError,
+                        "The ALPN extension requires OpenSSL 1.0.2 or later.")
+        with lltype.scoped_alloc(rffi.CCHARPP.TO, 1) as out_ptr:
+            with lltype.scoped_alloc(rffi.UINTP.TO, 1) as len_ptr:
+                libssl_SSL_get0_alpn_selected(self.ssl,
+                                              out_ptr, len_ptr)
+                if out_ptr[0]:
+                    return space.wrap(
+                        rffi.charpsize2str(out_ptr[0], intmask(len_ptr[0])))
+
     def compression_w(self, space):
         if not self.ssl:
             return space.w_None
@@ -635,6 +685,7 @@
     cipher=interp2app(_SSLSocket.cipher),
     shutdown=interp2app(_SSLSocket.shutdown),
     selected_npn_protocol = interp2app(_SSLSocket.selected_npn_protocol),
+    selected_alpn_protocol = interp2app(_SSLSocket.selected_alpn_protocol),
     compression = interp2app(_SSLSocket.compression_w),
     version = interp2app(_SSLSocket.version_w),
     tls_unique_cb = interp2app(_SSLSocket.tls_unique_cb_w),
@@ -1562,6 +1613,14 @@
 
         self.npn_protocols = SSLNpnProtocols(self.ctx, protos)
 
+    @unwrap_spec(protos='bufferstr')
+    def set_alpn_protocols_w(self, space, protos):
+        if not HAS_ALPN:
+            raise oefmt(space.w_NotImplementedError,
+                        "The ALPN extension requires OpenSSL 1.0.2 or later.")
+
+        self.alpn_protocols = SSLAlpnProtocols(self.ctx, protos)
+
     def get_ca_certs_w(self, space, w_binary_form=None):
         if w_binary_form and space.is_true(w_binary_form):
             binary_mode = True
@@ -1630,6 +1689,7 @@
     session_stats = interp2app(_SSLContext.session_stats_w),
     
set_default_verify_paths=interp2app(_SSLContext.descr_set_default_verify_paths),
     _set_npn_protocols=interp2app(_SSLContext.set_npn_protocols_w),
+    _set_alpn_protocols=interp2app(_SSLContext.set_alpn_protocols_w),
     get_ca_certs=interp2app(_SSLContext.get_ca_certs_w),
     set_ecdh_curve=interp2app(_SSLContext.set_ecdh_curve_w),
     set_servername_callback=interp2app(_SSLContext.set_servername_callback_w),
diff --git a/rpython/rlib/ropenssl.py b/rpython/rlib/ropenssl.py
--- a/rpython/rlib/ropenssl.py
+++ b/rpython/rlib/ropenssl.py
@@ -134,6 +134,7 @@
     SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER = 
rffi_platform.ConstantInteger("SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER")
     SSL_TLSEXT_ERR_OK = rffi_platform.ConstantInteger("SSL_TLSEXT_ERR_OK")
     SSL_TLSEXT_ERR_ALERT_FATAL = 
rffi_platform.ConstantInteger("SSL_TLSEXT_ERR_ALERT_FATAL")
+    SSL_TLSEXT_ERR_NOACK = 
rffi_platform.ConstantInteger("SSL_TLSEXT_ERR_NOACK")
 
     SSL_AD_INTERNAL_ERROR = 
rffi_platform.ConstantInteger("SSL_AD_INTERNAL_ERROR")
     SSL_AD_HANDSHAKE_FAILURE = 
rffi_platform.ConstantInteger("SSL_AD_HANDSHAKE_FAILURE")
@@ -260,7 +261,7 @@
                              OPENSSL_VERSION_NUMBER != 0x00909000
 if OPENSSL_VERSION_NUMBER < 0x0090800f and not OPENSSL_NO_ECDH:
     OPENSSL_NO_ECDH = True
-HAVE_ALPN = OPENSSL_VERSION_NUMBER >= 0x1000200fL and not OPENSSL_NO_TLSEXT
+HAS_ALPN = OPENSSL_VERSION_NUMBER >= 0x1000200fL and not OPENSSL_NO_TLSEXT
 
 
 def external(name, argtypes, restype, **kw):
@@ -513,6 +514,17 @@
     ssl_external(
         'SSL_get0_next_proto_negotiated', [
             SSL, rffi.CCHARPP, rffi.UINTP], lltype.Void)
+if HAS_ALPN:
+    ssl_external('SSL_CTX_set_alpn_protos',
+                 [SSL_CTX, rffi.UCHARP, rffi.UINT], rffi.INT)
+    SSL_ALPN_SEL_CB = lltype.Ptr(lltype.FuncType(
+        [SSL, rffi.CCHARPP, rffi.UCHARP, rffi.CCHARP, rffi.UINT, rffi.VOIDP],
+        rffi.INT))
+    ssl_external('SSL_CTX_set_alpn_select_cb',
+                 [SSL_CTX, SSL_ALPN_SEL_CB, rffi.VOIDP], lltype.Void)
+    ssl_external(
+        'SSL_get0_alpn_selected', [
+            SSL, rffi.CCHARPP, rffi.UINTP], lltype.Void)
 
 EVP_MD_CTX = rffi.COpaquePtr('EVP_MD_CTX', compilation_info=eci)
 EVP_MD     = lltype.Ptr(EVP_MD_st)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to