2397: Fix resolving alias addresses for postfix when target is a punycode domain r=mergify[bot] a=ghostwheel42

## What type of PR?

bug-fix

## What does this PR do?

- fix splitting of localpart in resolve_destination
- idna-enode domain-part of email addresses before returning to postfix

### Related issue(s)
- closes #2393


Co-authored-by: Alexander Graf <ghostwheel42@users.noreply.github.com>
master
bors[bot] 2 years ago committed by GitHub
commit 51945aa316
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -33,8 +33,9 @@ def postfix_alias_map(alias):
localpart, domain_name = models.Email.resolve_domain(alias) localpart, domain_name = models.Email.resolve_domain(alias)
if localpart is None: if localpart is None:
return flask.jsonify(domain_name) return flask.jsonify(domain_name)
destination = models.Email.resolve_destination(localpart, domain_name) if destinations := models.Email.resolve_destination(localpart, domain_name):
return flask.jsonify(",".join(destination)) if destination else flask.abort(404) return flask.jsonify(",".join(idna_encode(destinations)))
return flask.abort(404)
@internal.route("/postfix/transport/<path:email>") @internal.route("/postfix/transport/<path:email>")
def postfix_transport(email): def postfix_transport(email):
@ -142,9 +143,11 @@ def postfix_sender_login(sender):
if localpart is None: if localpart is None:
return flask.jsonify(",".join(wildcard_senders)) if wildcard_senders else flask.abort(404) return flask.jsonify(",".join(wildcard_senders)) if wildcard_senders else flask.abort(404)
localpart = localpart[:next((i for i, ch in enumerate(localpart) if ch in flask.current_app.config.get('RECIPIENT_DELIMITER')), None)] localpart = localpart[:next((i for i, ch in enumerate(localpart) if ch in flask.current_app.config.get('RECIPIENT_DELIMITER')), None)]
destination = models.Email.resolve_destination(localpart, domain_name, True) destinations = models.Email.resolve_destination(localpart, domain_name, True) or []
destination = [*destination, *wildcard_senders] if destination else [*wildcard_senders] destinations.extend(wildcard_senders)
return flask.jsonify(",".join(destination)) if destination else flask.abort(404) if destinations:
return flask.jsonify(",".join(idna_encode(destinations)))
return flask.abort(404)
@internal.route("/postfix/sender/rate/<path:sender>") @internal.route("/postfix/sender/rate/<path:sender>")
def postfix_sender_rate(sender): def postfix_sender_rate(sender):
@ -169,3 +172,11 @@ def postfix_sender_access(sender):
except sqlalchemy.exc.StatementError: except sqlalchemy.exc.StatementError:
pass pass
return flask.abort(404) return flask.abort(404)
# idna encode domain part of each address in list of addresses
def idna_encode(addresses):
return [
f"{localpart}@{idna.encode(domain).decode('ascii')}"
for (localpart, domain) in
(address.rsplit("@", 1) for address in addresses)
]

@ -439,10 +439,15 @@ class Email(object):
localpart_stripped = None localpart_stripped = None
stripped_alias = None stripped_alias = None
delim = os.environ.get('RECIPIENT_DELIMITER') if delims := os.environ.get('RECIPIENT_DELIMITER'):
if delim in localpart: try:
localpart_stripped = localpart.rsplit(delim, 1)[0] pos = next(i for i, c in enumerate(localpart) if c in delims)
except StopIteration:
pass
else:
localpart_stripped = localpart[:pos]
# is localpart@domain_name or localpart_stripped@domain_name an user?
user = User.query.get(f'{localpart}@{domain_name}') user = User.query.get(f'{localpart}@{domain_name}')
if not user and localpart_stripped: if not user and localpart_stripped:
user = User.query.get(f'{localpart_stripped}@{domain_name}') user = User.query.get(f'{localpart_stripped}@{domain_name}')
@ -450,19 +455,18 @@ class Email(object):
if user: if user:
email = f'{localpart}@{domain_name}' email = f'{localpart}@{domain_name}'
if user.forward_enabled: if not user.forward_enabled:
destination = user.forward_destination return [email]
if user.forward_keep or ignore_forward_keep:
destination.append(email)
else:
destination = [email]
destination = user.forward_destination
if user.forward_keep or ignore_forward_keep:
destination.append(email)
return destination return destination
pure_alias = Alias.resolve(localpart, domain_name) # is localpart, domain_name or localpart_stripped@domain_name an alias?
if pure_alias := Alias.resolve(localpart, domain_name):
if pure_alias and not pure_alias.wildcard: if not pure_alias.wildcard:
return pure_alias.destination return pure_alias.destination
if stripped_alias := Alias.resolve(localpart_stripped, domain_name): if stripped_alias := Alias.resolve(localpart_stripped, domain_name):
return stripped_alias.destination return stripped_alias.destination

Loading…
Cancel
Save