diff --git a/src/main/java/org/apache/commons/validator/routines/EmailValidator.java b/src/main/java/org/apache/commons/validator/routines/EmailValidator.java index 0eb58d1a9..460397456 100644 --- a/src/main/java/org/apache/commons/validator/routines/EmailValidator.java +++ b/src/main/java/org/apache/commons/validator/routines/EmailValidator.java @@ -42,7 +42,11 @@ public class EmailValidator implements Serializable { private static final String WORD = "((" + VALID_CHARS + "|')+|" + QUOTED_USER + ")"; private static final String EMAIL_REGEX = "^(.+)@(\\S+)$"; - private static final String IP_DOMAIN_REGEX = "^\\[(.*)\\]$"; + + /** + * RFC 5321 section 4.1.3: an IPv6 address literal carries the "IPv6:" tag (case-insensitive), an IPv4 literal is untagged. + */ + private static final String IP_DOMAIN_REGEX = "^\\[((?i)IPv6:)?(.*)\\]$"; private static final String USER_REGEX = "^" + WORD + "(\\." + WORD + ")*$"; private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX); @@ -195,9 +199,11 @@ protected boolean isValidDomain(final String domain) { final Matcher ipDomainMatcher = IP_DOMAIN_PATTERN.matcher(domain); if (ipDomainMatcher.matches()) { - final InetAddressValidator inetAddressValidator = - InetAddressValidator.getInstance(); - return inetAddressValidator.isValid(ipDomainMatcher.group(1)); + final InetAddressValidator inetAddressValidator = InetAddressValidator.getInstance(); + if (ipDomainMatcher.group(1) != null) { + return inetAddressValidator.isValidInet6Address(ipDomainMatcher.group(2)); + } + return inetAddressValidator.isValidInet4Address(ipDomainMatcher.group(2)); } // Domain is symbolic name if (allowTld) { diff --git a/src/test/java/org/apache/commons/validator/routines/EmailValidatorTest.java b/src/test/java/org/apache/commons/validator/routines/EmailValidatorTest.java index 87b3839a5..9ab3ddfd6 100644 --- a/src/test/java/org/apache/commons/validator/routines/EmailValidatorTest.java +++ b/src/test/java/org/apache/commons/validator/routines/EmailValidatorTest.java @@ -453,6 +453,23 @@ void testEmailWithNumericAddress() { assertTrue(validator.isValid("someone@yahoo.com")); } + /** + * Tests IPv6 address literals in the domain, which RFC 5321 section 4.1.3 tags with "IPv6:". + */ + @Test + void testEmailWithIpv6AddressLiteral() { + // Tagged IPv6 literals are accepted; the tag is ABNF-literal text so it is case insensitive. + assertTrue(validator.isValid("someone@[IPv6:2001:db8::1]")); + assertTrue(validator.isValid("someone@[IPv6:::1]")); + assertTrue(validator.isValid("someone@[ipv6:fe80::1]")); + // A bare IPv6 literal without the tag is not a valid address literal. + assertFalse(validator.isValid("someone@[2001:db8::1]")); + assertFalse(validator.isValid("someone@[::1]")); + // The tag is IPv6 only; an IPv4 literal stays untagged. + assertTrue(validator.isValid("someone@[216.109.118.76]")); + assertFalse(validator.isValid("someone@[IPv6:216.109.118.76]")); + } + /** * VALIDATOR-296 - A / or a ! is valid in the user part, but not in the domain part */