Skip to content

Commit

Permalink
Relay TLS connection drop through FatalSSLAlert
Browse files Browse the repository at this point in the history
Specifically, this patch adds an exception interception code to the
place where the socket is being first wrapped. In case of the
`builtin` TLS adapter, this is also a place where a handshake attempt
happens.

This switches the method of relaying an unrecoverable connection error
from a sentinel of a tuple with two falsy values to raising a unified
exception consistently.
  • Loading branch information
webknjaz committed Jan 24, 2024
1 parent 6a82c1d commit eb92823
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 15 deletions.
9 changes: 7 additions & 2 deletions cheroot/connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,13 @@ def _from_server_socket(self, server_socket): # noqa: C901 # FIXME
if self.server.ssl_adapter is not None:
try:
s, ssl_env = self.server.ssl_adapter.wrap(s)
except errors.FatalSSLAlert as tls_connection_drop_error:
self.server.error_log(
f'Client {addr !s} lost — peer dropped the TLS '
'connection suddenly, during handshake: '
f'{tls_connection_drop_error !s}',
)
return
except errors.NoSSLError:
msg = (
'The client sent a plain HTTP request, but '
Expand All @@ -311,8 +318,6 @@ def _from_server_socket(self, server_socket): # noqa: C901 # FIXME
if ex.args[0] not in errors.socket_errors_to_ignore:
raise
return
if not s:
return
mf = self.server.ssl_adapter.makefile
# Re-apply our timeout since we may have a new socket object
if hasattr(s, 'settimeout'):
Expand Down
32 changes: 19 additions & 13 deletions cheroot/ssl/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
from ..makefile import StreamReader, StreamWriter
from ..server import HTTPServer

generic_socket_error = OSError


def _assert_ssl_exc_contains(exc, *msgs):
"""Check whether SSL exception contains either of messages provided."""
Expand Down Expand Up @@ -264,7 +262,6 @@ def bind(self, sock):

def wrap(self, sock):
"""Wrap and return the given socket, plus WSGI environ entries."""
EMPTY_RESULT = None, {}
try:
s = self.context.wrap_socket(
sock, do_handshake_on_connect=True, server_side=True,
Expand All @@ -276,16 +273,25 @@ def wrap(self, sock):
raise errors.FatalSSLAlert(
*tls_connection_drop_error.args,
) from tls_connection_drop_error
except ssl.SSLError as ex:
if ex.errno == ssl.SSL_ERROR_SSL:
if _assert_ssl_exc_contains(ex, 'http request'):
# The client is speaking HTTP to an HTTPS server.
raise errors.NoSSLError
except generic_socket_error:
pass
else:
return s, self.get_environ(s)
return EMPTY_RESULT
except ssl.SSLError as generic_tls_error:
peer_speaks_plain_http_over_https = (
generic_tls_error.errno == ssl.SSL_ERROR_SSL and
_assert_ssl_exc_contains(generic_tls_error, 'http request')
)
if peer_speaks_plain_http_over_https:
reraised_connection_drop_exc_cls = errors.NoSSLError
else:
reraised_connection_drop_exc_cls = errors.FatalSSLAlert

raise reraised_connection_drop_exc_cls(
*generic_tls_error.args,
) from generic_tls_error
except OSError as tcp_connection_drop_error:
raise errors.FatalSSLAlert(
*tcp_connection_drop_error.args,
) from tcp_connection_drop_error

return s, self.get_environ(s)

def get_environ(self, sock):
"""Create WSGI environ entries to be merged into each request."""
Expand Down

0 comments on commit eb92823

Please sign in to comment.