diff --git a/CHANGELOG.md b/CHANGELOG.md index 090cbfd..44ccb2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ In Development * Python 3.8 and 3.9 are no longer supported. * The `original` field of the returned validated email object incorrectly un-escaped the local part of the email address. Now it returns the original address, without any display name, as passed to validate_email. * add support python3.15 +* The IPv6 tag of a domain literal is now case-insensitive, as in the RFCs, so `me@[ipv6:::1]` is accepted (and normalized to `me@[IPv6:::1]`) when `allow_domain_literal=True`. 2.3.0 (August 26, 2025) ----------------------- diff --git a/email_validator/syntax.py b/email_validator/syntax.py index 9e00ce7..e88a3a4 100644 --- a/email_validator/syntax.py +++ b/email_validator/syntax.py @@ -784,8 +784,9 @@ def validate_email_domain_literal(domain_literal: str) -> DomainLiteralValidatio "domain": f"[{addr}]", } - # If it begins with "IPv6:" it's an IPv6 address. - if domain_literal.startswith("IPv6:"): + # If it begins with "IPv6:" it's an IPv6 address. The tag is case-insensitive + # like all ABNF literal strings (RFC 5234 2.3), so "ipv6:" is accepted too. + if domain_literal[:5].lower() == "ipv6:": try: addr = ipaddress.IPv6Address(domain_literal[5:]) except ValueError as e: diff --git a/tests/test_syntax.py b/tests/test_syntax.py index bac37a6..87db51a 100644 --- a/tests/test_syntax.py +++ b/tests/test_syntax.py @@ -407,6 +407,12 @@ def test_domain_literal() -> None: assert validated.domain == "[IPv6:::1]" assert repr(validated.domain_address) == "IPv6Address('::1')" + # Check that the IPv6 tag is case-insensitive and normalized. + for email_input in ("me@[ipv6:::1]", "me@[IPV6:::1]"): + validated = validate_email(email_input, allow_domain_literal=True) + assert validated.domain == "[IPv6:::1]" + assert repr(validated.domain_address) == "IPv6Address('::1')" + # Check that an IPv4 address in an IPv6 address is accepted. validated = validate_email("me@[IPv6:::ffff:1.2.3.4]", allow_domain_literal=True) assert validated.domain == "[IPv6:::ffff:1.2.3.4]"