Add python-eventlet package to MOS 9.0 repository
[packages/trusty/python-eventlet.git] / python-eventlet / eventlet / green / ssl.py
1 __ssl = __import__('ssl')
2
3 from eventlet.patcher import slurp_properties
4 slurp_properties(__ssl, globals(), srckeys=dir(__ssl))
5
6 import functools
7 import sys
8 import errno
9 time = __import__('time')
10
11 from eventlet.support import get_errno, PY33, six
12 from eventlet.hubs import trampoline, IOClosed
13 from eventlet.greenio import (
14     set_nonblocking, GreenSocket, SOCKET_CLOSED, CONNECT_ERR, CONNECT_SUCCESS,
15 )
16 orig_socket = __import__('socket')
17 socket = orig_socket.socket
18 if sys.version_info >= (2, 7):
19     has_ciphers = True
20     timeout_exc = SSLError
21 else:
22     has_ciphers = False
23     timeout_exc = orig_socket.timeout
24
25 __patched__ = [
26     'SSLSocket', 'SSLContext', 'wrap_socket', 'sslwrap_simple',
27     'create_default_context', '_create_default_https_context']
28
29 _original_sslsocket = __ssl.SSLSocket
30
31
32 class GreenSSLSocket(_original_sslsocket):
33     """ This is a green version of the SSLSocket class from the ssl module added
34     in 2.6.  For documentation on it, please see the Python standard
35     documentation.
36
37     Python nonblocking ssl objects don't give errors when the other end
38     of the socket is closed (they do notice when the other end is shutdown,
39     though).  Any write/read operations will simply hang if the socket is
40     closed from the other end.  There is no obvious fix for this problem;
41     it appears to be a limitation of Python's ssl object implementation.
42     A workaround is to set a reasonable timeout on the socket using
43     settimeout(), and to close/reopen the connection when a timeout
44     occurs at an unexpected juncture in the code.
45     """
46     # we are inheriting from SSLSocket because its constructor calls
47     # do_handshake whose behavior we wish to override
48
49     def __init__(self, sock, keyfile=None, certfile=None,
50                  server_side=False, cert_reqs=CERT_NONE,
51                  ssl_version=PROTOCOL_SSLv23, ca_certs=None,
52                  do_handshake_on_connect=True, *args, **kw):
53         if not isinstance(sock, GreenSocket):
54             sock = GreenSocket(sock)
55
56         self.act_non_blocking = sock.act_non_blocking
57
58         if six.PY2:
59             # On Python 2 SSLSocket constructor queries the timeout, it'd break without
60             # this assignment
61             self._timeout = sock.gettimeout()
62
63         # nonblocking socket handshaking on connect got disabled so let's pretend it's disabled
64         # even when it's on
65         super(GreenSSLSocket, self).__init__(
66             sock.fd, keyfile, certfile, server_side, cert_reqs, ssl_version,
67             ca_certs, do_handshake_on_connect and six.PY2, *args, **kw)
68
69         # the superclass initializer trashes the methods so we remove
70         # the local-object versions of them and let the actual class
71         # methods shine through
72         # Note: This for Python 2
73         try:
74             for fn in orig_socket._delegate_methods:
75                 delattr(self, fn)
76         except AttributeError:
77             pass
78
79         if six.PY3:
80             # Python 3 SSLSocket construction process overwrites the timeout so restore it
81             self._timeout = sock.gettimeout()
82
83             # it also sets timeout to None internally apparently (tested with 3.4.2)
84             _original_sslsocket.settimeout(self, 0.0)
85             assert _original_sslsocket.gettimeout(self) == 0.0
86
87             # see note above about handshaking
88             self.do_handshake_on_connect = do_handshake_on_connect
89             if do_handshake_on_connect and self._connected:
90                 self.do_handshake()
91
92     def settimeout(self, timeout):
93         self._timeout = timeout
94
95     def gettimeout(self):
96         return self._timeout
97
98     def setblocking(self, flag):
99         if flag:
100             self.act_non_blocking = False
101             self._timeout = None
102         else:
103             self.act_non_blocking = True
104             self._timeout = 0.0
105
106     def _call_trampolining(self, func, *a, **kw):
107         if self.act_non_blocking:
108             return func(*a, **kw)
109         else:
110             while True:
111                 try:
112                     return func(*a, **kw)
113                 except SSLError as exc:
114                     if get_errno(exc) == SSL_ERROR_WANT_READ:
115                         trampoline(self,
116                                    read=True,
117                                    timeout=self.gettimeout(),
118                                    timeout_exc=timeout_exc('timed out'))
119                     elif get_errno(exc) == SSL_ERROR_WANT_WRITE:
120                         trampoline(self,
121                                    write=True,
122                                    timeout=self.gettimeout(),
123                                    timeout_exc=timeout_exc('timed out'))
124                     else:
125                         raise
126
127     def write(self, data):
128         """Write DATA to the underlying SSL channel.  Returns
129         number of bytes of DATA actually transmitted."""
130         return self._call_trampolining(
131             super(GreenSSLSocket, self).write, data)
132
133     def read(self, *args, **kwargs):
134         """Read up to LEN bytes and return them.
135         Return zero-length string on EOF."""
136         try:
137             return self._call_trampolining(
138                 super(GreenSSLSocket, self).read, *args, **kwargs)
139         except IOClosed:
140             return b''
141
142     def send(self, data, flags=0):
143         if self._sslobj:
144             return self._call_trampolining(
145                 super(GreenSSLSocket, self).send, data, flags)
146         else:
147             trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
148             return socket.send(self, data, flags)
149
150     def sendto(self, data, addr, flags=0):
151         # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
152         if self._sslobj:
153             raise ValueError("sendto not allowed on instances of %s" %
154                              self.__class__)
155         else:
156             trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
157             return socket.sendto(self, data, addr, flags)
158
159     def sendall(self, data, flags=0):
160         # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
161         if self._sslobj:
162             if flags != 0:
163                 raise ValueError(
164                     "non-zero flags not allowed in calls to sendall() on %s" %
165                     self.__class__)
166             amount = len(data)
167             count = 0
168             data_to_send = data
169             while (count < amount):
170                 v = self.send(data_to_send)
171                 count += v
172                 if v == 0:
173                     trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
174                 else:
175                     data_to_send = data[count:]
176             return amount
177         else:
178             while True:
179                 try:
180                     return socket.sendall(self, data, flags)
181                 except orig_socket.error as e:
182                     if self.act_non_blocking:
183                         raise
184                     if get_errno(e) == errno.EWOULDBLOCK:
185                         trampoline(self, write=True,
186                                    timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
187                     if get_errno(e) in SOCKET_CLOSED:
188                         return ''
189                     raise
190
191     def recv(self, buflen=1024, flags=0):
192         # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
193         if self._sslobj:
194             if flags != 0:
195                 raise ValueError(
196                     "non-zero flags not allowed in calls to recv() on %s" %
197                     self.__class__)
198             read = self.read(buflen)
199             return read
200         else:
201             while True:
202                 try:
203                     return socket.recv(self, buflen, flags)
204                 except orig_socket.error as e:
205                     if self.act_non_blocking:
206                         raise
207                     if get_errno(e) == errno.EWOULDBLOCK:
208                         try:
209                             trampoline(
210                                 self, read=True,
211                                 timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
212                         except IOClosed:
213                             return b''
214                     if get_errno(e) in SOCKET_CLOSED:
215                         return b''
216                     raise
217
218     def recv_into(self, buffer, nbytes=None, flags=0):
219         if not self.act_non_blocking:
220             trampoline(self, read=True, timeout=self.gettimeout(),
221                        timeout_exc=timeout_exc('timed out'))
222         return super(GreenSSLSocket, self).recv_into(buffer, nbytes, flags)
223
224     def recvfrom(self, addr, buflen=1024, flags=0):
225         if not self.act_non_blocking:
226             trampoline(self, read=True, timeout=self.gettimeout(),
227                        timeout_exc=timeout_exc('timed out'))
228         return super(GreenSSLSocket, self).recvfrom(addr, buflen, flags)
229
230     def recvfrom_into(self, buffer, nbytes=None, flags=0):
231         if not self.act_non_blocking:
232             trampoline(self, read=True, timeout=self.gettimeout(),
233                        timeout_exc=timeout_exc('timed out'))
234         return super(GreenSSLSocket, self).recvfrom_into(buffer, nbytes, flags)
235
236     def unwrap(self):
237         return GreenSocket(self._call_trampolining(
238             super(GreenSSLSocket, self).unwrap))
239
240     def do_handshake(self):
241         """Perform a TLS/SSL handshake."""
242         return self._call_trampolining(
243             super(GreenSSLSocket, self).do_handshake)
244
245     def _socket_connect(self, addr):
246         real_connect = socket.connect
247         if self.act_non_blocking:
248             return real_connect(self, addr)
249         else:
250             # *NOTE: gross, copied code from greenio because it's not factored
251             # well enough to reuse
252             if self.gettimeout() is None:
253                 while True:
254                     try:
255                         return real_connect(self, addr)
256                     except orig_socket.error as exc:
257                         if get_errno(exc) in CONNECT_ERR:
258                             trampoline(self, write=True)
259                         elif get_errno(exc) in CONNECT_SUCCESS:
260                             return
261                         else:
262                             raise
263             else:
264                 end = time.time() + self.gettimeout()
265                 while True:
266                     try:
267                         real_connect(self, addr)
268                     except orig_socket.error as exc:
269                         if get_errno(exc) in CONNECT_ERR:
270                             trampoline(
271                                 self, write=True,
272                                 timeout=end - time.time(), timeout_exc=timeout_exc('timed out'))
273                         elif get_errno(exc) in CONNECT_SUCCESS:
274                             return
275                         else:
276                             raise
277                     if time.time() >= end:
278                         raise timeout_exc('timed out')
279
280     def connect(self, addr):
281         """Connects to remote ADDR, and then wraps the connection in
282         an SSL channel."""
283         # *NOTE: grrrrr copied this code from ssl.py because of the reference
284         # to socket.connect which we don't want to call directly
285         if self._sslobj:
286             raise ValueError("attempt to connect already-connected SSLSocket!")
287         self._socket_connect(addr)
288         server_side = False
289         try:
290             sslwrap = _ssl.sslwrap
291         except AttributeError:
292             # sslwrap was removed in 3.x and later in 2.7.9
293             if six.PY2:
294                 sslobj = self._context._wrap_socket(self._sock, server_side, ssl_sock=self)
295             else:
296                 context = self.context if PY33 else self._context
297                 sslobj = context._wrap_socket(self, server_side)
298         else:
299             sslobj = sslwrap(self._sock, server_side, self.keyfile, self.certfile,
300                              self.cert_reqs, self.ssl_version,
301                              self.ca_certs, *([self.ciphers] if has_ciphers else []))
302
303         self._sslobj = sslobj
304         if self.do_handshake_on_connect:
305             self.do_handshake()
306
307     def accept(self):
308         """Accepts a new connection from a remote client, and returns
309         a tuple containing that new connection wrapped with a server-side
310         SSL channel, and the address of the remote client."""
311         # RDW grr duplication of code from greenio
312         if self.act_non_blocking:
313             newsock, addr = socket.accept(self)
314         else:
315             while True:
316                 try:
317                     newsock, addr = socket.accept(self)
318                     set_nonblocking(newsock)
319                     break
320                 except orig_socket.error as e:
321                     if get_errno(e) != errno.EWOULDBLOCK:
322                         raise
323                     trampoline(self, read=True, timeout=self.gettimeout(),
324                                timeout_exc=timeout_exc('timed out'))
325
326         new_ssl = type(self)(
327             newsock,
328             keyfile=self.keyfile,
329             certfile=self.certfile,
330             server_side=True,
331             cert_reqs=self.cert_reqs,
332             ssl_version=self.ssl_version,
333             ca_certs=self.ca_certs,
334             do_handshake_on_connect=self.do_handshake_on_connect,
335             suppress_ragged_eofs=self.suppress_ragged_eofs)
336         return (new_ssl, addr)
337
338     def dup(self):
339         raise NotImplementedError("Can't dup an ssl object")
340
341 SSLSocket = GreenSSLSocket
342
343
344 def wrap_socket(sock, *a, **kw):
345     return GreenSSLSocket(sock, *a, **kw)
346
347
348 if hasattr(__ssl, 'sslwrap_simple'):
349     def sslwrap_simple(sock, keyfile=None, certfile=None):
350         """A replacement for the old socket.ssl function.  Designed
351         for compatibility with Python 2.5 and earlier.  Will disappear in
352         Python 3.0."""
353         ssl_sock = GreenSSLSocket(sock, keyfile=keyfile, certfile=certfile,
354                                   server_side=False,
355                                   cert_reqs=CERT_NONE,
356                                   ssl_version=PROTOCOL_SSLv23,
357                                   ca_certs=None)
358         return ssl_sock
359
360
361 if hasattr(__ssl, 'SSLContext'):
362     _original_sslcontext = __ssl.SSLContext
363
364     class GreenSSLContext(_original_sslcontext):
365         __slots__ = ()
366
367         def wrap_socket(self, sock, *a, **kw):
368             return GreenSSLSocket(sock, *a, _context=self, **kw)
369
370     SSLContext = GreenSSLContext
371
372     if hasattr(__ssl, 'create_default_context'):
373         _original_create_default_context = __ssl.create_default_context
374
375         def green_create_default_context(*a, **kw):
376             # We can't just monkey-patch on the green version of `wrap_socket`
377             # on to SSLContext instances, but SSLContext.create_default_context
378             # does a bunch of work. Rather than re-implementing it all, just
379             # switch out the __class__ to get our `wrap_socket` implementation
380             context = _original_create_default_context(*a, **kw)
381             context.__class__ = GreenSSLContext
382             return context
383
384         create_default_context = green_create_default_context
385         _create_default_https_context = green_create_default_context