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