|
4 | 4 | __author__ = 'Simon Robinson'
|
5 | 5 | __copyright__ = 'Copyright (c) 2021 Simon Robinson'
|
6 | 6 | __license__ = 'Apache 2.0'
|
7 |
| -__version__ = '2022-01-27' # ISO 8601 |
| 7 | +__version__ = '2022-02-07' # ISO 8601 |
8 | 8 |
|
9 | 9 | import argparse
|
10 | 10 | import asyncore
|
@@ -614,7 +614,7 @@ def process_data(self, byte_data, censor_server_log=False):
|
614 | 614 | str_data = byte_data.decode('utf-8', 'replace').rstrip('\r\n')
|
615 | 615 | str_data_lower = str_data.lower()
|
616 | 616 |
|
617 |
| - # intercept EHLO so we can add STARTTLS (in parent class) |
| 617 | + # intercept EHLO so we can add STARTTLS (in server connection class) |
618 | 618 | if self.server_connection.ehlo is None and self.custom_configuration['starttls']:
|
619 | 619 | if str_data_lower.startswith('ehlo') or str_data_lower.startswith('helo'):
|
620 | 620 | self.server_connection.ehlo = str_data # save the command so we can replay later from the server side
|
@@ -692,7 +692,7 @@ def create_socket(self, socket_family=socket.AF_INET, socket_type=socket.SOCK_ST
|
692 | 692 | new_socket = socket.socket(socket_family, socket_type)
|
693 | 693 | new_socket.setblocking(True)
|
694 | 694 |
|
695 |
| - # connections can either be wrapped via the STARTTLS command, or SSL from the start |
| 695 | + # connections can either be upgraded (wrapped) after setup via the STARTTLS command, or secure from the start |
696 | 696 | if self.custom_configuration['starttls']:
|
697 | 697 | self.set_socket(new_socket)
|
698 | 698 | else:
|
@@ -880,8 +880,11 @@ def __init__(self, proxy_type, local_address, server_address, custom_configurati
|
880 | 880 | self.client_connections = []
|
881 | 881 |
|
882 | 882 | def info_string(self):
|
883 |
| - return '%s server at %s:%d proxying %s:%d' % (self.proxy_type, self.local_address[0], self.local_address[1], |
884 |
| - self.server_address[0], self.server_address[1]) |
| 883 | + secure = self.custom_configuration['local_certificate_path'] and self.custom_configuration['local_key_path'] |
| 884 | + return '%s server at %s:%d (%s) proxying %s:%d (%s)' % ( |
| 885 | + self.proxy_type, self.local_address[0], self.local_address[1], 'TLS' if secure else 'unsecured', |
| 886 | + self.server_address[0], self.server_address[1], |
| 887 | + 'STARTTLS' if self.custom_configuration['starttls'] else 'SSL/TLS') |
885 | 888 |
|
886 | 889 | def handle_accepted(self, connection, address):
|
887 | 890 | if MAX_CONNECTIONS <= 0 or len(self.client_connections) < MAX_CONNECTIONS:
|
@@ -927,6 +930,19 @@ def start(self):
|
927 | 930 | self.bind(self.local_address)
|
928 | 931 | self.listen(1)
|
929 | 932 |
|
| 933 | + def create_socket(self, socket_family=socket.AF_INET, socket_type=socket.SOCK_STREAM): |
| 934 | + if self.custom_configuration['local_certificate_path'] and self.custom_configuration['local_key_path']: |
| 935 | + new_socket = socket.socket(socket_family, socket_type) |
| 936 | + new_socket.setblocking(False) |
| 937 | + |
| 938 | + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) |
| 939 | + ssl_context.load_cert_chain( |
| 940 | + certfile=self.custom_configuration['local_certificate_path'], |
| 941 | + keyfile=self.custom_configuration['local_key_path']) |
| 942 | + self.set_socket(ssl_context.wrap_socket(new_socket, server_side=True)) |
| 943 | + else: |
| 944 | + super().create_socket(socket_family, socket_type) |
| 945 | + |
930 | 946 | def remove_client(self, client):
|
931 | 947 | if client in self.client_connections: # remove closed clients
|
932 | 948 | self.client_connections.remove(client)
|
@@ -1197,7 +1213,9 @@ def get_last_activity(account):
|
1197 | 1213 | @staticmethod
|
1198 | 1214 | def edit_config():
|
1199 | 1215 | if sys.platform == 'darwin':
|
1200 |
| - os.system('open %s' % CONFIG_FILE_PATH) |
| 1216 | + result = os.system('open %s' % CONFIG_FILE_PATH) |
| 1217 | + if result != 0: # no default editor found for this file type; open as a text file |
| 1218 | + os.system('open -t %s' % CONFIG_FILE_PATH) |
1201 | 1219 | elif sys.platform == 'win32':
|
1202 | 1220 | os.startfile(CONFIG_FILE_PATH)
|
1203 | 1221 | elif sys.platform.startswith('linux'):
|
@@ -1490,7 +1508,9 @@ def load_and_start_servers(self, icon=None):
|
1490 | 1508 | break
|
1491 | 1509 |
|
1492 | 1510 | custom_configuration = {
|
1493 |
| - 'starttls': config.getboolean(section, 'starttls', fallback=False) if server_type == 'SMTP' else False |
| 1511 | + 'starttls': config.getboolean(section, 'starttls', fallback=False) if server_type == 'SMTP' else False, |
| 1512 | + 'local_certificate_path': config.get(section, 'local_certificate_path', fallback=None), |
| 1513 | + 'local_key_path': config.get(section, 'local_key_path', fallback=None) |
1494 | 1514 | }
|
1495 | 1515 |
|
1496 | 1516 | if server_address: # all other values are checked, regex matched or have a fallback above
|
|
0 commit comments