Improve error-handling and error-reporting

1. Log the full traceback only once for each error (when we bounce the
message).

2. Use 451 response code on processing failure.

3. Disable decoding message contents as we operate on raw data anyway.
This commit is contained in:
Piotr F. Mieszkowski 2024-03-06 09:08:30 +01:00
parent 0ef3012e33
commit 8955cf6c9d
Signed by: pfm
GPG key ID: BDE5BC1FA5DC53D5
2 changed files with 19 additions and 17 deletions

View file

@ -40,7 +40,7 @@ class MailEncryptionProxy:
message = email.message_from_bytes(envelope.original_content, policy=SMTPUTF8)
if message.defects:
LOG.warning("Issues found: %d; %s", len(message.defects), repr(message.defects))
LOG.warning("Issues found: %s", repr(message.defects))
send = xport.SendFrom(envelope.mail_from)
for operation in gate.delivery_plan(envelope.rcpt_tos, message, keys):
@ -48,14 +48,12 @@ class MailEncryptionProxy:
try:
new_message = operation.perform(message)
send(new_message, operation.recipients())
except (EncryptionException, MailSerialisationException, UnicodeEncodeError):
# If the message can't be encrypted, deliver cleartext.
LOG.exception('Unable to encrypt message, delivering in cleartext')
if not isinstance(operation, KeepIntact):
self._send_unencrypted(operation, envelope, send)
else:
LOG.exception('Cannot perform: %s', operation)
raise
except (EncryptionException, MailSerialisationException) as e:
# If the message can't be encrypted or serialised to a
# stream of bytes, deliver original payload in
# cleartext.
LOG.error('Unable to encrypt message, delivering in cleartext: %s', e)
self._send_unencrypted(operation, envelope, send)
except:
LOG.exception('Unexpected exception caught, bouncing message')
@ -63,7 +61,7 @@ class MailEncryptionProxy:
if conf.should_log_headers():
LOG.error('Erroneous message headers: %s', self._beginning(envelope))
return xport.RESULT_ERROR
return xport.RESULT_ABORT
return xport.RESULT_OK
@ -80,9 +78,6 @@ class MailEncryptionProxy:
end = min(limit, 2560)
return e.original_content[0:end]
def _seconds_between(self, start_ms, end_ms) -> float:
return (end_ms - start_ms) * 1000
def _init_controller(keys: kcache.KeyRing, max_body_bytes=None, tout: float = 5):
proxy = MailEncryptionProxy(keys)
@ -90,7 +85,10 @@ def _init_controller(keys: kcache.KeyRing, max_body_bytes=None, tout: float = 5)
LOG.info(f"Initialising a mail Controller at {host}:{port}")
return Controller(proxy, hostname=host, port=port,
ready_timeout=tout,
data_size_limit=max_body_bytes)
data_size_limit=max_body_bytes,
# Do not decode data into str as we only operate on raw
# data available via Envelope.original_content.
decode_data=False)
def _validate_config():

View file

@ -9,8 +9,9 @@ import lacre.config as conf
# Mail status constants.
#
# These are the only values that our mail handler is allowed to return.
RESULT_OK = '250 OK'
RESULT_ERROR = '500 Could not process your message'
RESULT_OK = '250 OK' # delivered
RESULT_ABORT = '451 Aborted: error in processing' # aborted, retry later
RESULT_FAILED = '554 Transaction failed' # failed
LOG = logging.getLogger(__name__)
@ -68,4 +69,7 @@ class SendFrom:
if conf.flag_enabled('relay', 'starttls'):
smtp.starttls()
smtp.sendmail(self._from_addr, recipients, message)
try:
smtp.sendmail(self._from_addr, recipients, message)
except smtplib.SMTPException:
LOG.exception('Failed to deliver message')