Commit cb5048dd authored by Tom Evans's avatar Tom Evans

OF-807: Use IDN rules for S2S server names

Allow international domain names (per RFC 3490).
parent 2df7460b
...@@ -20,9 +20,11 @@ ...@@ -20,9 +20,11 @@
package org.jivesoftware.util; package org.jivesoftware.util;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.IDN;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.text.BreakIterator; import java.text.BreakIterator;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
...@@ -33,7 +35,6 @@ import java.util.Map; ...@@ -33,7 +35,6 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import javax.mail.internet.AddressException; import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress; import javax.mail.internet.InternetAddress;
...@@ -54,8 +55,6 @@ public class StringUtils { ...@@ -54,8 +55,6 @@ public class StringUtils {
private static final char[] LT_ENCODE = "<".toCharArray(); private static final char[] LT_ENCODE = "<".toCharArray();
private static final char[] GT_ENCODE = ">".toCharArray(); private static final char[] GT_ENCODE = ">".toCharArray();
private static final Pattern DOMAIN_NAME = Pattern.compile("^(?:[A-Za-z0-9][A-Za-z0-9\\-]{0,61}[A-Za-z0-9]|[A-Za-z0-9])$");
private StringUtils() { private StringUtils() {
// Not instantiable. // Not instantiable.
} }
...@@ -1122,22 +1121,25 @@ public class StringUtils { ...@@ -1122,22 +1121,25 @@ public class StringUtils {
} }
/** /**
* Returns true if the string passed in is a valid domain name * Returns a valid domain name, possibly as an ACE-encoded IDN
* (per <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>).
* *
* @param domain Proposed domain name * @param domain Proposed domain name
* @return true if the string passed in is a valid domain name * @return The validated domain name, possibly ACE-encoded
* @throws IllegalArgumentException The given domain name is not valid
*/ */
public static boolean isValidDomainName(String domain) { public static String validateDomainName(String domain) {
if (domain == null) { if (domain == null || domain.trim().length() == 0) {
return false; throw new IllegalArgumentException("Domain name cannot be null or empty");
} }
StringTokenizer parser = new StringTokenizer(domain, "."); String result = IDN.toASCII(domain);
while (parser.hasMoreTokens()) { if (result.equals(domain)) {
if (!DOMAIN_NAME.matcher(parser.nextToken()).matches()) { // no conversion; validate again via USE_STD3_ASCII_RULES
return false; IDN.toASCII(domain, IDN.USE_STD3_ASCII_RULES);
} } else {
Log.info(MessageFormat.format("Converted domain name: from '{0}' to '{1}'", domain, result));
} }
return true; return result;
} }
/** /**
......
package org.jivesoftware.util; package org.jivesoftware.util;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail;
import org.junit.Test; import org.junit.Test;
...@@ -10,30 +10,38 @@ public class StringUtilsTest { ...@@ -10,30 +10,38 @@ public class StringUtilsTest {
@Test @Test
public void testValidDomainNames() { public void testValidDomainNames() {
String domain = "www.mycompany.com"; assertValidDomainName("www.mycompany.com");
assertTrue("Domain should be valid", StringUtils.isValidDomainName(domain)); assertValidDomainName("www.my-company.com");
assertValidDomainName("abc.de");
assertValidDomainName("tronon.be", "xn--tronon-zua.be");
assertValidDomainName("bb.at", "xn--bb-eka.at");
domain = "www.my-company.com";
assertTrue("Domain should be valid", StringUtils.isValidDomainName(domain));
domain = "abc.de";
assertTrue("Domain should be valid", StringUtils.isValidDomainName(domain));
} }
@Test @Test
public void testInvalidDomainNames() { public void testInvalidDomainNames() {
String domain = "www.my_company.com"; assertInvalidDomainName("www.my_company.com", "Contains non-LDH characters");
assertFalse("Domain should not be valid", StringUtils.isValidDomainName(domain)); assertInvalidDomainName("www.-dash.com", "Has leading or trailing hyphen");
assertInvalidDomainName("www.dash-.com", "Has leading or trailing hyphen");
assertInvalidDomainName("abc.<test>.de", "Contains non-LDH characters");
domain = "www.-dash.com"; }
assertFalse("Domain should not be valid", StringUtils.isValidDomainName(domain));
domain = "www.dash-.com"; private void assertValidDomainName(String domain) {
assertFalse("Domain should not be valid", StringUtils.isValidDomainName(domain)); assertValidDomainName(domain, domain);
}
domain = "abc.<test>.de"; private void assertValidDomainName(String domain, String expected) {
assertFalse("Domain should not be valid", StringUtils.isValidDomainName(domain)); assertEquals("Domain should be valid: " + domain, expected, StringUtils.validateDomainName(domain));
} }
private void assertInvalidDomainName(String domain, String expectedCause) {
try {
StringUtils.validateDomainName(domain);
fail("Domain should not be valid: " + domain);
} catch (IllegalArgumentException iae) {
assertEquals("Unexpected cause: " + iae.getMessage(), expectedCause, iae.getMessage());
}
}
} }
...@@ -136,8 +136,10 @@ ...@@ -136,8 +136,10 @@
if (serverAllowed) { if (serverAllowed) {
int intRemotePort = 0; int intRemotePort = 0;
// Validate params // Validate params
if (domain == null || domain.trim().length() == 0 || !StringUtils.isValidDomainName(domain)) { try {
errors.put("domain",""); StringUtils.validateDomainName(domain);
} catch (IllegalArgumentException iae) {
errors.put("domain", "");
} }
if (remotePort == null || remotePort.trim().length() == 0 || "0".equals(remotePort)) { if (remotePort == null || remotePort.trim().length() == 0 || "0".equals(remotePort)) {
errors.put("remotePort",""); errors.put("remotePort","");
...@@ -164,8 +166,10 @@ ...@@ -164,8 +166,10 @@
if (serverBlocked) { if (serverBlocked) {
// Validate params // Validate params
if (domain == null || domain.trim().length() == 0 || !StringUtils.isValidDomainName(domain)) { try {
errors.put("domain",""); StringUtils.validateDomainName(domain);
} catch (IllegalArgumentException iae) {
errors.put("domain", "");
} }
// If no errors, continue: // If no errors, continue:
if (errors.isEmpty()) { if (errors.isEmpty()) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment