8
8
import os
9
9
import ssl
10
10
import tempfile
11
+ import weakref
11
12
12
13
from . import (
13
14
Backend ,
17
18
NextProtocol ,
18
19
PrivateKey ,
19
20
ServerContext ,
21
+ TLSConfiguration ,
20
22
TLSError ,
21
23
TLSVersion ,
22
24
TLSWrappedBuffer ,
71
73
del ctx
72
74
73
75
76
+ # This is a mapping of concrete SSLObject objects to the TLSWrappedBuffers
77
+ # implementation used here. We use this to get the thing we need in the SNI
78
+ # callback. The weakrefs are used to avoid keeping TLSWrappedBuffers objects
79
+ # alive unnecessarily.
80
+ _object_to_buffer_map = weakref .WeakValueDictionary ()
81
+
82
+
83
+ def _sni_callback_builder (user_callback , original_config ):
84
+ """
85
+ This function returns a closure that includes the PEP 543 SNI callback we
86
+ want to call. The closure is the "wrapping" SNI callback, which we use to
87
+ translate from the stdlib's callback to the PEP 543 callback form.
88
+
89
+ Thanks to the immutability we can also close over the original context used
90
+ to add this SNI callback.
91
+ """
92
+ # This shortcut ensures that if there is no SNI callback to set, we unset
93
+ # it from the context, just to be sure.
94
+ if user_callback is None :
95
+ return None
96
+
97
+ def pep543_callback (ssl_obj , servername , stdlib_context ):
98
+ buffer = _object_to_buffer_map [ssl_obj ]
99
+
100
+ # TODO: Are exceptions sensibly propagated here?
101
+ new_config = user_callback (buffer , servername , original_config )
102
+
103
+ # Not returning a TLSConfiguration is an error.
104
+ if not isinstance (new_config , TLSConfiguration ):
105
+ return ssl .ALERT_DESCRIPTION_INTERNAL_ERROR
106
+
107
+ # Returning an identical configuration to the one that was passed in
108
+ # means do nothing. If it's different we need to create a new
109
+ # SSLContext and pass it in.
110
+ if new_config != original_config :
111
+ new_ctx = _init_context (new_config )
112
+ ssl_obj .context = new_ctx
113
+
114
+ # Returning None, perversely, is how one signals success from this
115
+ # function. Will wonders never cease?
116
+ return None
117
+
118
+ return pep543_callback
119
+
120
+
74
121
@contextmanager
75
122
def _error_converter (ignore_filter = ()):
76
123
"""
@@ -189,6 +236,17 @@ def _configure_context_for_negotiation(context, inner_protocols):
189
236
return context
190
237
191
238
239
+ def _configure_context_for_sni (context , sni_callback , original_config ):
240
+ """
241
+ Given a PEP 543 SNI callback, configures the SSLContext to actually call
242
+ it.
243
+ """
244
+ context .set_servername_callback (
245
+ _sni_callback_builder (sni_callback , original_config )
246
+ )
247
+ return context
248
+
249
+
192
250
def _init_context (config ):
193
251
"""
194
252
Initialize an ssl.SSLContext object with a given configuration.
@@ -212,7 +270,9 @@ def _init_context(config):
212
270
config .lowest_supported_version ,
213
271
config .highest_supported_version ,
214
272
)
215
- # TODO: Add ServerNameCallback
273
+ some_context = _configure_context_for_sni (
274
+ some_context , config .sni_callback , config
275
+ )
216
276
return some_context
217
277
218
278
@@ -239,6 +299,9 @@ def __init__(self, parent_context, ssl_context, server_hostname):
239
299
server_hostname = server_hostname
240
300
)
241
301
302
+ # Keep track of the fact that we are the owner of this object.
303
+ _object_to_buffer_map [self ._object ] = self
304
+
242
305
# We need to track whether the connection is established to properly
243
306
# report the TLS version. This is to work around a Python bug:
244
307
# https://bugs.python.org/issue29781
0 commit comments