Adjust the package revision; no actual code changes
[packages/trusty/python-eventlet.git] / 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 sys
7 import errno
8 time = __import__('time')
9
10 from eventlet.support import get_errno
11 from eventlet.hubs import trampoline, IOClosed
12 from eventlet.greenio import set_nonblocking, GreenSocket, SOCKET_CLOSED, CONNECT_ERR, CONNECT_SUCCESS
13 orig_socket = __import__('socket')
14 socket = orig_socket.socket
15 if sys.version_info >= (2, 7):
16     has_ciphers = True
17     timeout_exc = SSLError
18 else:
19     has_ciphers = False
20     timeout_exc = orig_socket.timeout
21
22 __patched__ = ['SSLSocket', 'wrap_socket', 'sslwrap_simple']
23
24
25 class GreenSSLSocket(__ssl.SSLSocket):
26
27     """ This is a green version of the SSLSocket class from the ssl module added
28     in 2.6.  For documentation on it, please see the Python standard
29     documentation.
30
31     Python nonblocking ssl objects don't give errors when the other end
32     of the socket is closed (they do notice when the other end is shutdown,
33     though).  Any write/read operations will simply hang if the socket is
34     closed from the other end.  There is no obvious fix for this problem;
35     it appears to be a limitation of Python's ssl object implementation.
36     A workaround is to set a reasonable timeout on the socket using
37     settimeout(), and to close/reopen the connection when a timeout
38     occurs at an unexpected juncture in the code.
39     """
40     # we are inheriting from SSLSocket because its constructor calls
41     # do_handshake whose behavior we wish to override
42
43     def __init__(self, sock, *args, **kw):
44         if not isinstance(sock, GreenSocket):
45             sock = GreenSocket(sock)
46
47         self.act_non_blocking = sock.act_non_blocking
48         self._timeout = sock.gettimeout()
49         super(GreenSSLSocket, self).__init__(sock.fd, *args, **kw)
50
51         # the superclass initializer trashes the methods so we remove
52         # the local-object versions of them and let the actual class
53         # methods shine through
54         try:
55             for fn in orig_socket._delegate_methods:
56                 delattr(self, fn)
57         except AttributeError:
58             pass
59
60     def settimeout(self, timeout):
61         self._timeout = timeout
62
63     def gettimeout(self):
64         return self._timeout
65
66     def setblocking(self, flag):
67         if flag:
68             self.act_non_blocking = False
69             self._timeout = None
70         else:
71             self.act_non_blocking = True
72             self._timeout = 0.0
73
74     def _call_trampolining(self, func, *a, **kw):
75         if self.act_non_blocking:
76             return func(*a, **kw)
77         else:
78             while True:
79                 try:
80                     return func(*a, **kw)
81                 except SSLError as exc:
82                     if get_errno(exc) == SSL_ERROR_WANT_READ:
83                         trampoline(self,
84                                    read=True,
85                                    timeout=self.gettimeout(),
86                                    timeout_exc=timeout_exc('timed out'))
87                     elif get_errno(exc) == SSL_ERROR_WANT_WRITE:
88                         trampoline(self,
89                                    write=True,
90                                    timeout=self.gettimeout(),
91                                    timeout_exc=timeout_exc('timed out'))
92                     else:
93                         raise
94
95     def write(self, data):
96         """Write DATA to the underlying SSL channel.  Returns
97         number of bytes of DATA actually transmitted."""
98         return self._call_trampolining(
99             super(GreenSSLSocket, self).write, data)
100
101     def read(self, len=1024):
102         """Read up to LEN bytes and return them.
103         Return zero-length string on EOF."""
104         try:
105             return self._call_trampolining(
106                 super(GreenSSLSocket, self).read, len)
107         except IOClosed:
108             return ''
109
110     def send(self, data, flags=0):
111         if self._sslobj:
112             return self._call_trampolining(
113                 super(GreenSSLSocket, self).send, data, flags)
114         else:
115             trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
116             return socket.send(self, data, flags)
117
118     def sendto(self, data, addr, flags=0):
119         # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
120         if self._sslobj:
121             raise ValueError("sendto not allowed on instances of %s" %
122                              self.__class__)
123         else:
124             trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
125             return socket.sendto(self, data, addr, flags)
126
127     def sendall(self, data, flags=0):
128         # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
129         if self._sslobj:
130             if flags != 0:
131                 raise ValueError(
132                     "non-zero flags not allowed in calls to sendall() on %s" %
133                     self.__class__)
134             amount = len(data)
135             count = 0
136             while (count < amount):
137                 v = self.send(data[count:])
138                 count += v
139                 if v == 0:
140                     trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
141             return amount
142         else:
143             while True:
144                 try:
145                     return socket.sendall(self, data, flags)
146                 except orig_socket.error as e:
147                     if self.act_non_blocking:
148                         raise
149                     if get_errno(e) == errno.EWOULDBLOCK:
150                         trampoline(self, write=True,
151                                    timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
152                     if get_errno(e) in SOCKET_CLOSED:
153                         return ''
154                     raise
155
156     def recv(self, buflen=1024, flags=0):
157         # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
158         if self._sslobj:
159             if flags != 0:
160                 raise ValueError(
161                     "non-zero flags not allowed in calls to recv() on %s" %
162                     self.__class__)
163             read = self.read(buflen)
164             return read
165         else:
166             while True:
167                 try:
168                     return socket.recv(self, buflen, flags)
169                 except orig_socket.error as e:
170                     if self.act_non_blocking:
171                         raise
172                     if get_errno(e) == errno.EWOULDBLOCK:
173                         try:
174                             trampoline(self, read=True,
175                                        timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
176                         except IOClosed:
177                             return ''
178                     if get_errno(e) in SOCKET_CLOSED:
179                         return ''
180                     raise
181
182     def recv_into(self, buffer, nbytes=None, flags=0):
183         if not self.act_non_blocking:
184             trampoline(self, read=True, timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
185         return super(GreenSSLSocket, self).recv_into(buffer, nbytes, flags)
186
187     def recvfrom(self, addr, buflen=1024, flags=0):
188         if not self.act_non_blocking:
189             trampoline(self, read=True, timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
190         return super(GreenSSLSocket, self).recvfrom(addr, buflen, flags)
191
192     def recvfrom_into(self, buffer, nbytes=None, flags=0):
193         if not self.act_non_blocking:
194             trampoline(self, read=True, timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
195         return super(GreenSSLSocket, self).recvfrom_into(buffer, nbytes, flags)
196
197     def unwrap(self):
198         return GreenSocket(self._call_trampolining(
199             super(GreenSSLSocket, self).unwrap))
200
201     def do_handshake(self):
202         """Perform a TLS/SSL handshake."""
203         return self._call_trampolining(
204             super(GreenSSLSocket, self).do_handshake)
205
206     def _socket_connect(self, addr):
207         real_connect = socket.connect
208         if self.act_non_blocking:
209             return real_connect(self, addr)
210         else:
211             # *NOTE: gross, copied code from greenio because it's not factored
212             # well enough to reuse
213             if self.gettimeout() is None:
214                 while True:
215                     try:
216                         return real_connect(self, addr)
217                     except orig_socket.error as exc:
218                         if get_errno(exc) in CONNECT_ERR:
219                             trampoline(self, write=True)
220                         elif get_errno(exc) in CONNECT_SUCCESS:
221                             return
222                         else:
223                             raise
224             else:
225                 end = time.time() + self.gettimeout()
226                 while True:
227                     try:
228                         real_connect(self, addr)
229                     except orig_socket.error as exc:
230                         if get_errno(exc) in CONNECT_ERR:
231                             trampoline(self, write=True,
232                                        timeout=end - time.time(), timeout_exc=timeout_exc('timed out'))
233                         elif get_errno(exc) in CONNECT_SUCCESS:
234                             return
235                         else:
236                             raise
237                     if time.time() >= end:
238                         raise timeout_exc('timed out')
239
240     def connect(self, addr):
241         """Connects to remote ADDR, and then wraps the connection in
242         an SSL channel."""
243         # *NOTE: grrrrr copied this code from ssl.py because of the reference
244         # to socket.connect which we don't want to call directly
245         if self._sslobj:
246             raise ValueError("attempt to connect already-connected SSLSocket!")
247         self._socket_connect(addr)
248         if has_ciphers:
249             self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile,
250                                         self.cert_reqs, self.ssl_version,
251                                         self.ca_certs, self.ciphers)
252         else:
253             self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile,
254                                         self.cert_reqs, self.ssl_version,
255                                         self.ca_certs)
256         if self.do_handshake_on_connect:
257             self.do_handshake()
258
259     def accept(self):
260         """Accepts a new connection from a remote client, and returns
261         a tuple containing that new connection wrapped with a server-side
262         SSL channel, and the address of the remote client."""
263         # RDW grr duplication of code from greenio
264         if self.act_non_blocking:
265             newsock, addr = socket.accept(self)
266         else:
267             while True:
268                 try:
269                     newsock, addr = socket.accept(self)
270                     set_nonblocking(newsock)
271                     break
272                 except orig_socket.error as e:
273                     if get_errno(e) != errno.EWOULDBLOCK:
274                         raise
275                     trampoline(self, read=True, timeout=self.gettimeout(),
276                                timeout_exc=timeout_exc('timed out'))
277
278         new_ssl = type(self)(
279             newsock,
280             keyfile=self.keyfile,
281             certfile=self.certfile,
282             server_side=True,
283             cert_reqs=self.cert_reqs,
284             ssl_version=self.ssl_version,
285             ca_certs=self.ca_certs,
286             do_handshake_on_connect=self.do_handshake_on_connect,
287             suppress_ragged_eofs=self.suppress_ragged_eofs)
288         return (new_ssl, addr)
289
290     def dup(self):
291         raise NotImplementedError("Can't dup an ssl object")
292
293 SSLSocket = GreenSSLSocket
294
295
296 def wrap_socket(sock, *a, **kw):
297     return GreenSSLSocket(sock, *a, **kw)
298
299
300 if hasattr(__ssl, 'sslwrap_simple'):
301     def sslwrap_simple(sock, keyfile=None, certfile=None):
302         """A replacement for the old socket.ssl function.  Designed
303         for compability with Python 2.5 and earlier.  Will disappear in
304         Python 3.0."""
305         ssl_sock = GreenSSLSocket(sock, keyfile=keyfile, certfile=certfile,
306                                   server_side=False,
307                                   cert_reqs=CERT_NONE,
308                                   ssl_version=PROTOCOL_SSLv23,
309                                   ca_certs=None)
310         return ssl_sock