1 __ssl = __import__('ssl')
3 from eventlet.patcher import slurp_properties
4 slurp_properties(__ssl, globals(), srckeys=dir(__ssl))
8 time = __import__('time')
10 from eventlet.support import get_errno, PY33, six
11 from eventlet.hubs import trampoline, IOClosed
12 from eventlet.greenio import (
13 set_nonblocking, GreenSocket, SOCKET_CLOSED, CONNECT_ERR, CONNECT_SUCCESS,
15 orig_socket = __import__('socket')
16 socket = orig_socket.socket
17 if sys.version_info >= (2, 7):
19 timeout_exc = SSLError
22 timeout_exc = orig_socket.timeout
24 __patched__ = ['SSLSocket', 'wrap_socket', 'sslwrap_simple']
26 _original_sslsocket = __ssl.SSLSocket
29 class GreenSSLSocket(_original_sslsocket):
30 """ This is a green version of the SSLSocket class from the ssl module added
31 in 2.6. For documentation on it, please see the Python standard
34 Python nonblocking ssl objects don't give errors when the other end
35 of the socket is closed (they do notice when the other end is shutdown,
36 though). Any write/read operations will simply hang if the socket is
37 closed from the other end. There is no obvious fix for this problem;
38 it appears to be a limitation of Python's ssl object implementation.
39 A workaround is to set a reasonable timeout on the socket using
40 settimeout(), and to close/reopen the connection when a timeout
41 occurs at an unexpected juncture in the code.
43 # we are inheriting from SSLSocket because its constructor calls
44 # do_handshake whose behavior we wish to override
46 def __init__(self, sock, keyfile=None, certfile=None,
47 server_side=False, cert_reqs=CERT_NONE,
48 ssl_version=PROTOCOL_SSLv23, ca_certs=None,
49 do_handshake_on_connect=True, *args, **kw):
50 if not isinstance(sock, GreenSocket):
51 sock = GreenSocket(sock)
53 self.act_non_blocking = sock.act_non_blocking
56 # On Python 2 SSLSocket constructor queries the timeout, it'd break without
58 self._timeout = sock.gettimeout()
60 # nonblocking socket handshaking on connect got disabled so let's pretend it's disabled
62 super(GreenSSLSocket, self).__init__(
63 sock.fd, keyfile, certfile, server_side, cert_reqs, ssl_version,
64 ca_certs, do_handshake_on_connect and six.PY2, *args, **kw)
66 # the superclass initializer trashes the methods so we remove
67 # the local-object versions of them and let the actual class
68 # methods shine through
69 # Note: This for Python 2
71 for fn in orig_socket._delegate_methods:
73 except AttributeError:
77 # Python 3 SSLSocket construction process overwrites the timeout so restore it
78 self._timeout = sock.gettimeout()
80 # it also sets timeout to None internally apparently (tested with 3.4.2)
81 _original_sslsocket.settimeout(self, 0.0)
82 assert _original_sslsocket.gettimeout(self) == 0.0
84 # see note above about handshaking
85 self.do_handshake_on_connect = do_handshake_on_connect
86 if do_handshake_on_connect and self._connected:
89 def settimeout(self, timeout):
90 self._timeout = timeout
95 def setblocking(self, flag):
97 self.act_non_blocking = False
100 self.act_non_blocking = True
103 def _call_trampolining(self, func, *a, **kw):
104 if self.act_non_blocking:
105 return func(*a, **kw)
109 return func(*a, **kw)
110 except SSLError as exc:
111 if get_errno(exc) == SSL_ERROR_WANT_READ:
114 timeout=self.gettimeout(),
115 timeout_exc=timeout_exc('timed out'))
116 elif get_errno(exc) == SSL_ERROR_WANT_WRITE:
119 timeout=self.gettimeout(),
120 timeout_exc=timeout_exc('timed out'))
124 def write(self, data):
125 """Write DATA to the underlying SSL channel. Returns
126 number of bytes of DATA actually transmitted."""
127 return self._call_trampolining(
128 super(GreenSSLSocket, self).write, data)
130 def read(self, *args, **kwargs):
131 """Read up to LEN bytes and return them.
132 Return zero-length string on EOF."""
134 return self._call_trampolining(
135 super(GreenSSLSocket, self).read, *args, **kwargs)
139 def send(self, data, flags=0):
141 return self._call_trampolining(
142 super(GreenSSLSocket, self).send, data, flags)
144 trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
145 return socket.send(self, data, flags)
147 def sendto(self, data, addr, flags=0):
148 # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
150 raise ValueError("sendto not allowed on instances of %s" %
153 trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
154 return socket.sendto(self, data, addr, flags)
156 def sendall(self, data, flags=0):
157 # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
161 "non-zero flags not allowed in calls to sendall() on %s" %
165 while (count < amount):
166 v = self.send(data[count:])
169 trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
174 return socket.sendall(self, data, flags)
175 except orig_socket.error as e:
176 if self.act_non_blocking:
178 if get_errno(e) == errno.EWOULDBLOCK:
179 trampoline(self, write=True,
180 timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
181 if get_errno(e) in SOCKET_CLOSED:
185 def recv(self, buflen=1024, flags=0):
186 # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
190 "non-zero flags not allowed in calls to recv() on %s" %
192 read = self.read(buflen)
197 return socket.recv(self, buflen, flags)
198 except orig_socket.error as e:
199 if self.act_non_blocking:
201 if get_errno(e) == errno.EWOULDBLOCK:
205 timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
208 if get_errno(e) in SOCKET_CLOSED:
212 def recv_into(self, buffer, nbytes=None, flags=0):
213 if not self.act_non_blocking:
214 trampoline(self, read=True, timeout=self.gettimeout(),
215 timeout_exc=timeout_exc('timed out'))
216 return super(GreenSSLSocket, self).recv_into(buffer, nbytes, flags)
218 def recvfrom(self, addr, buflen=1024, 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).recvfrom(addr, buflen, flags)
224 def recvfrom_into(self, buffer, nbytes=None, 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_into(buffer, nbytes, flags)
231 return GreenSocket(self._call_trampolining(
232 super(GreenSSLSocket, self).unwrap))
234 def do_handshake(self):
235 """Perform a TLS/SSL handshake."""
236 return self._call_trampolining(
237 super(GreenSSLSocket, self).do_handshake)
239 def _socket_connect(self, addr):
240 real_connect = socket.connect
241 if self.act_non_blocking:
242 return real_connect(self, addr)
244 # *NOTE: gross, copied code from greenio because it's not factored
245 # well enough to reuse
246 if self.gettimeout() is None:
249 return real_connect(self, addr)
250 except orig_socket.error as exc:
251 if get_errno(exc) in CONNECT_ERR:
252 trampoline(self, write=True)
253 elif get_errno(exc) in CONNECT_SUCCESS:
258 end = time.time() + self.gettimeout()
261 real_connect(self, addr)
262 except orig_socket.error as exc:
263 if get_errno(exc) in CONNECT_ERR:
266 timeout=end - time.time(), timeout_exc=timeout_exc('timed out'))
267 elif get_errno(exc) in CONNECT_SUCCESS:
271 if time.time() >= end:
272 raise timeout_exc('timed out')
274 def connect(self, addr):
275 """Connects to remote ADDR, and then wraps the connection in
277 # *NOTE: grrrrr copied this code from ssl.py because of the reference
278 # to socket.connect which we don't want to call directly
280 raise ValueError("attempt to connect already-connected SSLSocket!")
281 self._socket_connect(addr)
284 sslwrap = _ssl.sslwrap
285 except AttributeError:
286 # sslwrap was removed in 3.x and later in 2.7.9
288 sslobj = self._context._wrap_socket(self._sock, server_side, ssl_sock=self)
290 context = self.context if PY33 else self._context
291 sslobj = context._wrap_socket(self, server_side)
293 sslobj = sslwrap(self._sock, server_side, self.keyfile, self.certfile,
294 self.cert_reqs, self.ssl_version,
295 self.ca_certs, *([self.ciphers] if has_ciphers else []))
297 self._sslobj = sslobj
298 if self.do_handshake_on_connect:
302 """Accepts a new connection from a remote client, and returns
303 a tuple containing that new connection wrapped with a server-side
304 SSL channel, and the address of the remote client."""
305 # RDW grr duplication of code from greenio
306 if self.act_non_blocking:
307 newsock, addr = socket.accept(self)
311 newsock, addr = socket.accept(self)
312 set_nonblocking(newsock)
314 except orig_socket.error as e:
315 if get_errno(e) != errno.EWOULDBLOCK:
317 trampoline(self, read=True, timeout=self.gettimeout(),
318 timeout_exc=timeout_exc('timed out'))
320 new_ssl = type(self)(
322 keyfile=self.keyfile,
323 certfile=self.certfile,
325 cert_reqs=self.cert_reqs,
326 ssl_version=self.ssl_version,
327 ca_certs=self.ca_certs,
328 do_handshake_on_connect=self.do_handshake_on_connect,
329 suppress_ragged_eofs=self.suppress_ragged_eofs)
330 return (new_ssl, addr)
333 raise NotImplementedError("Can't dup an ssl object")
335 SSLSocket = GreenSSLSocket
338 def wrap_socket(sock, *a, **kw):
339 return GreenSSLSocket(sock, *a, **kw)
342 if hasattr(__ssl, 'sslwrap_simple'):
343 def sslwrap_simple(sock, keyfile=None, certfile=None):
344 """A replacement for the old socket.ssl function. Designed
345 for compability with Python 2.5 and earlier. Will disappear in
347 ssl_sock = GreenSSLSocket(sock, keyfile=keyfile, certfile=certfile,
350 ssl_version=PROTOCOL_SSLv23,