Increase consistency at handling domain literals #15
3 changed files with 30 additions and 15 deletions
|
@ -1,3 +1,5 @@
|
|||
from typing import Optional
|
||||
|
||||
from idna.core import IDNAError, encode
|
||||
|
||||
from .exceptions import AddressFormatError
|
||||
|
@ -22,7 +24,7 @@ class EmailAddress(object):
|
|||
raise AddressFormatError
|
||||
|
||||
# Convert internationalized domain name into the ACE encoding
|
||||
if self._domain.startswith('[') and self._domain.endswith(']'):
|
||||
if self.domain_literal_ip:
|
||||
self._ace_domain = self._domain
|
||||
else:
|
||||
try:
|
||||
|
@ -46,6 +48,16 @@ class EmailAddress(object):
|
|||
"""
|
||||
return self._domain
|
||||
|
||||
@property
|
||||
def domain_literal_ip(self) -> Optional[str]:
|
||||
"""
|
||||
If the domain part of the email address is a literal IP address
|
||||
enclosed in brackets, that IP address (without the brakcets) is
|
||||
returned. Otherwise, `None` is returned.
|
||||
"""
|
||||
if self._domain.startswith('[') and self._domain.endswith(']'):
|
||||
return self._domain[1:-1]
|
||||
|
||||
@property
|
||||
def ace(self) -> str:
|
||||
'The ASCII-compatible encoding for the email address.'
|
||||
|
|
|
@ -109,8 +109,11 @@ def mx_check(
|
|||
"""
|
||||
host = helo_host or gethostname()
|
||||
from_address = from_address or email_address
|
||||
mx_records = _get_mx_records(
|
||||
domain=email_address.domain, timeout=dns_timeout)
|
||||
if email_address.domain_literal_ip:
|
||||
mx_records = [email_address.domain_literal_ip]
|
||||
else:
|
||||
mx_records = _get_mx_records(
|
||||
domain=email_address.domain, timeout=dns_timeout)
|
||||
return _check_mx_records(
|
||||
mx_records=mx_records, smtp_timeout=smtp_timeout, helo_host=host,
|
||||
from_address=from_address, email_address=email_address)
|
||||
|
|
|
@ -35,16 +35,16 @@ def regex_check(address: EmailAddress) -> bool:
|
|||
if not USER_REGEX.match(address.user):
|
||||
raise AddressFormatError
|
||||
|
||||
# Validate domain part: a) hostname.
|
||||
if HOST_REGEX.match(address.ace_domain):
|
||||
return True
|
||||
# Validate domain part.
|
||||
if address.domain_literal_ip:
|
||||
literal_match = LITERAL_REGEX.match(address.ace_domain)
|
||||
if literal_match is None:
|
||||
raise AddressFormatError
|
||||
if not _validate_ipv46_address(literal_match[1]):
|
||||
raise AddressFormatError
|
||||
else:
|
||||
if HOST_REGEX.match(address.ace_domain) is None:
|
||||
raise AddressFormatError
|
||||
|
||||
# Validate domain part: b) literal IP address.
|
||||
literal_match = LITERAL_REGEX.match(address.ace_domain)
|
||||
if literal_match:
|
||||
ip_address = literal_match.group(1)
|
||||
if _validate_ipv46_address(ip_address):
|
||||
return True
|
||||
|
||||
# Domain part not successfully validated.
|
||||
raise AddressFormatError
|
||||
# All validations successful.
|
||||
return True
|
||||
|
|
Loading…
Reference in a new issue