From 8f603d037a3cf9ff944d459206fb6c053aeef118 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Sat, 8 Apr 2023 15:29:07 -0700 Subject: [PATCH] stop leaking sockets --- acme/acme/_internal/tests/standalone_test.py | 30 ++++++------- acme/acme/crypto_util.py | 44 +++++++++++--------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/acme/acme/_internal/tests/standalone_test.py b/acme/acme/_internal/tests/standalone_test.py index 85b05dbc8..ba58e6989 100644 --- a/acme/acme/_internal/tests/standalone_test.py +++ b/acme/acme/_internal/tests/standalone_test.py @@ -89,25 +89,25 @@ class HTTP01ServerTest(unittest.TestCase): def test_timely_shutdown(self): from acme.standalone import HTTP01Server - server = HTTP01Server(('', 0), resources=set(), timeout=0.05) - server_thread = threading.Thread(target=server.serve_forever) - server_thread.start() + with HTTP01Server(('', 0), resources=set(), timeout=0.05) as server: + server_thread = threading.Thread(target=server.serve_forever) + server_thread.start() - client = socket.socket() - client.connect(('localhost', server.socket.getsockname()[1])) + with socket.socket() as client: + client.connect(('localhost', server.socket.getsockname()[1])) - stop_thread = threading.Thread(target=server.shutdown) - stop_thread.start() - server_thread.join(5.) + stop_thread = threading.Thread(target=server.shutdown) + stop_thread.start() + server_thread.join(5.) - is_hung = server_thread.is_alive() - try: - client.shutdown(socket.SHUT_RDWR) - except: # pragma: no cover, pylint: disable=bare-except - # may raise error because socket could already be closed - pass + is_hung = server_thread.is_alive() + try: + client.shutdown(socket.SHUT_RDWR) + except: # pragma: no cover, pylint: disable=bare-except + # may raise error because socket could already be closed + pass - assert not is_hung, 'Server shutdown should not be hung' + assert not is_hung, 'Server shutdown should not be hung' @unittest.skipIf(not challenges.TLSALPN01.is_supported(), "pyOpenSSL too old") diff --git a/acme/acme/crypto_util.py b/acme/acme/crypto_util.py index 754f70e46..f35b634b5 100644 --- a/acme/acme/crypto_util.py +++ b/acme/acme/crypto_util.py @@ -136,27 +136,33 @@ class SSLSocket: # pylint: disable=too-few-public-methods def accept(self) -> Tuple[FakeConnection, Any]: # pylint: disable=missing-function-docstring sock, addr = self.sock.accept() - context = SSL.Context(self.method) - context.set_options(SSL.OP_NO_SSLv2) - context.set_options(SSL.OP_NO_SSLv3) - context.set_tlsext_servername_callback(self._pick_certificate_cb) - if self.alpn_selection is not None: - context.set_alpn_select_callback(self.alpn_selection) - - ssl_sock = self.FakeConnection(SSL.Connection(context, sock)) - ssl_sock.set_accept_state() - - # This log line is especially desirable because without it requests to - # our standalone TLSALPN server would not be logged. - logger.debug("Performing handshake with %s", addr) try: - ssl_sock.do_handshake() - except SSL.Error as error: - # _pick_certificate_cb might have returned without - # creating SSL context (wrong server name) - raise socket.error(error) + context = SSL.Context(self.method) + context.set_options(SSL.OP_NO_SSLv2) + context.set_options(SSL.OP_NO_SSLv3) + context.set_tlsext_servername_callback(self._pick_certificate_cb) + if self.alpn_selection is not None: + context.set_alpn_select_callback(self.alpn_selection) - return ssl_sock, addr + ssl_sock = self.FakeConnection(SSL.Connection(context, sock)) + ssl_sock.set_accept_state() + + # This log line is especially desirable because without it requests to + # our standalone TLSALPN server would not be logged. + logger.debug("Performing handshake with %s", addr) + try: + ssl_sock.do_handshake() + except SSL.Error as error: + # _pick_certificate_cb might have returned without + # creating SSL context (wrong server name) + raise socket.error(error) + + return ssl_sock, addr + except: + # If we encounter any error, close the new socket before reraising + # the exception. + sock.close() + raise def probe_sni(name: bytes, host: bytes, port: int = 443, timeout: int = 300, # pylint: disable=too-many-arguments