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 @@
package org.jivesoftware.util;
import java.io.UnsupportedEncodingException;
import java.net.IDN;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.BreakIterator;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
......@@ -33,7 +35,6 @@ import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
......@@ -54,8 +55,6 @@ public class StringUtils {
private static final char[] LT_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() {
// Not instantiable.
}
......@@ -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
* @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) {
if (domain == null) {
return false;
}
StringTokenizer parser = new StringTokenizer(domain, ".");
while (parser.hasMoreTokens()) {
if (!DOMAIN_NAME.matcher(parser.nextToken()).matches()) {
return false;
}
public static String validateDomainName(String domain) {
if (domain == null || domain.trim().length() == 0) {
throw new IllegalArgumentException("Domain name cannot be null or empty");
}
String result = IDN.toASCII(domain);
if (result.equals(domain)) {
// no conversion; validate again via USE_STD3_ASCII_RULES
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;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Test;
......@@ -10,30 +10,38 @@ public class StringUtilsTest {
@Test
public void testValidDomainNames() {
String domain = "www.mycompany.com";
assertTrue("Domain should be valid", StringUtils.isValidDomainName(domain));
assertValidDomainName("www.mycompany.com");
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
public void testInvalidDomainNames() {
String domain = "www.my_company.com";
assertFalse("Domain should not be valid", StringUtils.isValidDomainName(domain));
assertInvalidDomainName("www.my_company.com", "Contains non-LDH characters");
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";
assertFalse("Domain should not be valid", StringUtils.isValidDomainName(domain));
private void assertValidDomainName(String domain) {
assertValidDomainName(domain, domain);
}
domain = "abc.<test>.de";
assertFalse("Domain should not be valid", StringUtils.isValidDomainName(domain));
private void assertValidDomainName(String domain, String expected) {
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 @@
if (serverAllowed) {
int intRemotePort = 0;
// Validate params
if (domain == null || domain.trim().length() == 0 || !StringUtils.isValidDomainName(domain)) {
errors.put("domain","");
try {
StringUtils.validateDomainName(domain);
} catch (IllegalArgumentException iae) {
errors.put("domain", "");
}
if (remotePort == null || remotePort.trim().length() == 0 || "0".equals(remotePort)) {
errors.put("remotePort","");
......@@ -164,8 +166,10 @@
if (serverBlocked) {
// Validate params
if (domain == null || domain.trim().length() == 0 || !StringUtils.isValidDomainName(domain)) {
errors.put("domain","");
try {
StringUtils.validateDomainName(domain);
} catch (IllegalArgumentException iae) {
errors.put("domain", "");
}
// If no errors, continue:
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