(domain_literal: str)
| 763 | |
| 764 | |
| 765 | def validate_email_domain_literal(domain_literal: str) -> DomainLiteralValidationResult: |
| 766 | # This is obscure domain-literal syntax. Parse it and return |
| 767 | # a compressed/normalized address. |
| 768 | # RFC 5321 4.1.3 and RFC 5322 3.4.1. |
| 769 | |
| 770 | addr: Union[ipaddress.IPv4Address, ipaddress.IPv6Address] |
| 771 | |
| 772 | # Try to parse the domain literal as an IPv4 address. |
| 773 | # There is no tag for IPv4 addresses, so we can never |
| 774 | # be sure if the user intends an IPv4 address. |
| 775 | if re.match(r"^[0-9\.]+$", domain_literal): |
| 776 | try: |
| 777 | addr = ipaddress.IPv4Address(domain_literal) |
| 778 | except ValueError as e: |
| 779 | raise EmailSyntaxError(f"The address in brackets after the @-sign is not valid: It is not an IPv4 address ({e}) or is missing an address literal tag.") from e |
| 780 | |
| 781 | # Return the IPv4Address object and the domain back unchanged. |
| 782 | return { |
| 783 | "domain_address": addr, |
| 784 | "domain": f"[{addr}]", |
| 785 | } |
| 786 | |
| 787 | # If it begins with "IPv6:" it's an IPv6 address. |
| 788 | if domain_literal.startswith("IPv6:"): |
| 789 | try: |
| 790 | addr = ipaddress.IPv6Address(domain_literal[5:]) |
| 791 | except ValueError as e: |
| 792 | raise EmailSyntaxError(f"The IPv6 address in brackets after the @-sign is not valid ({e}).") from e |
| 793 | |
| 794 | # Return the IPv6Address object and construct a normalized |
| 795 | # domain literal. |
| 796 | return { |
| 797 | "domain_address": addr, |
| 798 | "domain": f"[IPv6:{addr.compressed}]", |
| 799 | } |
| 800 | |
| 801 | # Nothing else is valid. |
| 802 | |
| 803 | if ":" not in domain_literal: |
| 804 | raise EmailSyntaxError("The part after the @-sign in brackets is not an IPv4 address and has no address literal tag.") |
| 805 | |
| 806 | # The tag (the part before the colon) has character restrictions, |
| 807 | # but since it must come from a registry of tags (in which only "IPv6" is defined), |
| 808 | # there's no need to check the syntax of the tag. See RFC 5321 4.1.2. |
| 809 | |
| 810 | # Check for permitted ASCII characters. This actually doesn't matter |
| 811 | # since there will be an exception after anyway. |
| 812 | bad_chars = { |
| 813 | safe_character_display(c) |
| 814 | for c in domain_literal |
| 815 | if not DOMAIN_LITERAL_CHARS.match(c) |
| 816 | } |
| 817 | if bad_chars: |
| 818 | raise EmailSyntaxError("The part after the @-sign contains invalid characters in brackets: " + ", ".join(sorted(bad_chars)) + ".") |
| 819 | |
| 820 | # There are no other domain literal tags. |
| 821 | # https://www.iana.org/assignments/address-literal-tags/address-literal-tags.xhtml |
| 822 | raise EmailSyntaxError("The part after the @-sign contains an invalid address literal tag in brackets.") |
no test coverage detected