25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

476 satır
19KB

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