Skip to content

Commit 79caa29

Browse files
authored
Improve reliability of secure local connections (#51)
Move SSL/TLS handshake to client connection handler
1 parent 99e25ec commit 79caa29

File tree

1 file changed

+31
-25
lines changed

1 file changed

+31
-25
lines changed

emailproxy.py

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
__author__ = 'Simon Robinson'
55
__copyright__ = 'Copyright (c) 2022 Simon Robinson'
66
__license__ = 'Apache 2.0'
7-
__version__ = '2022-08-17' # ISO 8601 (YYYY-MM-DD)
7+
__version__ = '2022-08-19' # ISO 8601 (YYYY-MM-DD)
88

99
import argparse
1010
import asyncore
@@ -597,6 +597,9 @@ def __init__(self, proxy_type, connection, socket_map, connection_info, server_c
597597
self.censor_next_log = False # try to avoid logging credentials
598598
self.authenticated = False
599599

600+
self.ssl_handshake_completed = not (
601+
custom_configuration['local_certificate_path'] and custom_configuration['local_key_path'])
602+
600603
def info_string(self):
601604
if Log.get_level() == logging.DEBUG:
602605
return '%s (%s:%d; %s:%d->%s:%d%s)' % (
@@ -607,20 +610,28 @@ def info_string(self):
607610
else:
608611
return '%s (%s:%d)' % (self.proxy_type, self.local_address[0], self.local_address[1])
609612

610-
def handle_connect(self):
611-
pass
612-
613-
def get_data(self):
613+
def ssl_handshake(self):
614614
try:
615-
byte_data = self.recv(RECEIVE_BUFFER_SIZE)
616-
return byte_data
617-
except (ssl.SSLWantReadError, ssl.SSLWantWriteError) as e: # only relevant when using local certificates
618-
Log.info(self.info_string(), 'Warning: caught client-side SSL recv error',
619-
'(see https://github.com/simonrob/email-oauth2-proxy/issues/9):', Log.error_string(e))
620-
return None
615+
# noinspection PyUnresolvedReferences
616+
self.socket.do_handshake()
617+
except (ssl.SSLWantReadError, ssl.SSLWantWriteError): # only relevant when using local certificates
618+
return
619+
else:
620+
Log.debug(self.info_string(), '[ SSL/TLS handshake complete ]')
621+
self.ssl_handshake_completed = True
622+
623+
def readable(self):
624+
return super().readable() and self.ssl_handshake_completed
625+
626+
def writable(self):
627+
return super().writable() and self.ssl_handshake_completed
621628

622629
def handle_read(self):
623-
byte_data = self.get_data()
630+
if not self.ssl_handshake_completed:
631+
self.ssl_handshake()
632+
return
633+
634+
byte_data = self.recv(RECEIVE_BUFFER_SIZE)
624635
if not byte_data:
625636
return
626637

@@ -675,18 +686,11 @@ def process_data(self, byte_data, censor_server_log=False):
675686
self.close()
676687

677688
def send(self, byte_data):
689+
while not self.ssl_handshake_completed:
690+
self.ssl_handshake()
691+
678692
Log.debug(self.info_string(), '<--', byte_data)
679-
try:
680-
super().send(byte_data)
681-
except (ssl.SSLWantReadError, ssl.SSLWantWriteError) as e: # only relevant when using local certificates
682-
Log.info(self.info_string(), 'Warning: caught client-side SSL send error',
683-
'(see https://github.com/simonrob/email-oauth2-proxy/issues/9):', Log.error_string(e))
684-
while True:
685-
try:
686-
super().send(byte_data)
687-
break
688-
except (ssl.SSLWantReadError, ssl.SSLWantWriteError):
689-
time.sleep(1)
693+
super().send(byte_data)
690694

691695
def log_info(self, message, message_type='info'):
692696
# override to redirect error messages to our own log
@@ -1355,7 +1359,7 @@ def start(self):
13551359
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
13561360
self.set_reuse_addr()
13571361
self.bind(self.local_address)
1358-
self.listen(1)
1362+
self.listen(5)
13591363

13601364
def create_socket(self, socket_family=socket.AF_INET, socket_type=socket.SOCK_STREAM):
13611365
if self.custom_configuration['local_certificate_path'] and self.custom_configuration['local_key_path']:
@@ -1366,7 +1370,9 @@ def create_socket(self, socket_family=socket.AF_INET, socket_type=socket.SOCK_ST
13661370
ssl_context.load_cert_chain(
13671371
certfile=self.custom_configuration['local_certificate_path'],
13681372
keyfile=self.custom_configuration['local_key_path'])
1369-
self.set_socket(ssl_context.wrap_socket(new_socket, server_side=True))
1373+
# suppress_ragged_eofs=True: see test_ssl.py documentation in https://github.com/python/cpython/pull/5266
1374+
self.set_socket(ssl_context.wrap_socket(new_socket, server_side=True, suppress_ragged_eofs=True,
1375+
do_handshake_on_connect=False))
13701376
else:
13711377
super().create_socket(socket_family, socket_type)
13721378

0 commit comments

Comments
 (0)