下面列出了java.net.IDN#toASCII ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
public static String encodeDomainName(String domainName) throws Exception {
if (domainName == null) {
return null;
} else {
return IDN.toASCII(domainName);
}
}
public SocksCmdRequest(SocksCmdType cmdType, SocksAddressType addressType, String host, int port) {
super(SocksRequestType.CMD);
if (cmdType == null) {
throw new NullPointerException("cmdType");
}
if (addressType == null) {
throw new NullPointerException("addressType");
}
if (host == null) {
throw new NullPointerException("host");
}
switch (addressType) {
case IPv4:
if (!NetUtil.isValidIpV4Address(host)) {
throw new IllegalArgumentException(host + " is not a valid IPv4 address");
}
break;
case DOMAIN:
if (IDN.toASCII(host).length() > 255) {
throw new IllegalArgumentException(host + " IDN: " + IDN.toASCII(host) + " exceeds 255 char limit");
}
break;
case IPv6:
if (!NetUtil.isValidIpV6Address(host)) {
throw new IllegalArgumentException(host + " is not a valid IPv6 address");
}
break;
case UNKNOWN:
break;
}
if (port <= 0 || port >= 65536) {
throw new IllegalArgumentException(port + " is not in bounds 0 < x < 65536");
}
this.cmdType = cmdType;
this.addressType = addressType;
this.host = IDN.toASCII(host);
this.port = port;
}
private static String validateAndConvertToAscii(String hostname) {
String ascii;
try {
ascii = IDN.toASCII(PercentDecoder.decodeUnreserved(hostname), IDN.ALLOW_UNASSIGNED);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid hostname: " + hostname);
}
if (ascii.isEmpty() || ".".equals(ascii)) {
throw new IllegalArgumentException("Invalid hostname: cannot be null or empty.");
}
return ascii;
}
/**
* 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 The validated domain name, possibly ACE-encoded
* @throws IllegalArgumentException The given domain name is not valid
*/
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 result;
}
public DefaultSocks5CommandRequest(
Socks5CommandType type, Socks5AddressType dstAddrType, String dstAddr, int dstPort) {
if (type == null) {
throw new NullPointerException("type");
}
if (dstAddrType == null) {
throw new NullPointerException("dstAddrType");
}
if (dstAddr == null) {
throw new NullPointerException("dstAddr");
}
if (dstAddrType == Socks5AddressType.IPv4) {
if (!NetUtil.isValidIpV4Address(dstAddr)) {
throw new IllegalArgumentException("dstAddr: " + dstAddr + " (expected: a valid IPv4 address)");
}
} else if (dstAddrType == Socks5AddressType.DOMAIN) {
dstAddr = IDN.toASCII(dstAddr);
if (dstAddr.length() > 255) {
throw new IllegalArgumentException("dstAddr: " + dstAddr + " (expected: less than 256 chars)");
}
} else if (dstAddrType == Socks5AddressType.IPv6) {
if (!NetUtil.isValidIpV6Address(dstAddr)) {
throw new IllegalArgumentException("dstAddr: " + dstAddr + " (expected: a valid IPv6 address");
}
}
if (dstPort < 0 || dstPort > 65535) {
throw new IllegalArgumentException("dstPort: " + dstPort + " (expected: 0~65535)");
}
this.type = type;
this.dstAddrType = dstAddrType;
this.dstAddr = dstAddr;
this.dstPort = dstPort;
}
public DefaultSocks5CommandResponse(
Socks5CommandStatus status, Socks5AddressType bndAddrType, String bndAddr, int bndPort) {
if (status == null) {
throw new NullPointerException("status");
}
if (bndAddrType == null) {
throw new NullPointerException("bndAddrType");
}
if (bndAddr != null) {
if (bndAddrType == Socks5AddressType.IPv4) {
if (!NetUtil.isValidIpV4Address(bndAddr)) {
throw new IllegalArgumentException("bndAddr: " + bndAddr + " (expected: a valid IPv4 address)");
}
} else if (bndAddrType == Socks5AddressType.DOMAIN) {
bndAddr = IDN.toASCII(bndAddr);
if (bndAddr.length() > 255) {
throw new IllegalArgumentException("bndAddr: " + bndAddr + " (expected: less than 256 chars)");
}
} else if (bndAddrType == Socks5AddressType.IPv6) {
if (!NetUtil.isValidIpV6Address(bndAddr)) {
throw new IllegalArgumentException("bndAddr: " + bndAddr + " (expected: a valid IPv6 address)");
}
}
}
if (bndPort < 0 || bndPort > 65535) {
throw new IllegalArgumentException("bndPort: " + bndPort + " (expected: 0~65535)");
}
this.status = status;
this.bndAddrType = bndAddrType;
this.bndAddr = bndAddr;
this.bndPort = bndPort;
}
@Override
public Set<String> generateCandidates(String originalString) {
Set<String> result = new HashSet<>();
String domain = originalString;
if(StringUtils.isEmpty(domain)) {
return result;
}
if(isAce(domain)) {
//this is an ace domain.
domain = IDN.toUnicode(domain);
}
for(int ws = 0;ws < domain.length();ws++) {
for(int i = 0;i < domain.length() - ws + 1;++i) {
String win = domain.substring(i, i+ws);
for(int j = 0;j < ws;j++) {
char c = win.charAt(j);
if( glyphs.containsKey(c)) {
for( String g : glyphs.get(c)) {
String winNew = win.replaceAll("" + c, g);
String d = domain.substring(0, i) + winNew + domain.substring(i + ws);
result.add(d);
if(!isAce(d)) {
try {
String dAscii = IDN.toASCII(d, IDN.ALLOW_UNASSIGNED);
if (!d.equals(dAscii)) {
result.add(dAscii);
}
}
catch(IllegalArgumentException iae) {
LOG.debug("Unable to parse " + d + ": " + iae.getMessage(), iae);
}
}
}
}
}
}
}
return result;
}
/**
* IDNA ASCII conversion, case normalization and validation.
*/
static String normalizeHostnamePattern(String hostnamePattern) {
requireNonNull(hostnamePattern, "hostnamePattern");
if (needsNormalization(hostnamePattern)) {
hostnamePattern = IDN.toASCII(hostnamePattern, IDN.ALLOW_UNASSIGNED);
}
if (!"*".equals(hostnamePattern) &&
!HOSTNAME_PATTERN.matcher(hostnamePattern.startsWith("*.") ? hostnamePattern.substring(2)
: hostnamePattern).matches()) {
throw new IllegalArgumentException("hostnamePattern: " + hostnamePattern);
}
return Ascii.toLowerCase(hostnamePattern);
}
/**
* IDNA ASCII conversion and case normalization IDNA ASCII转换和大小写规范化
*/
static String normalizeHostname(String hostname) {
if (needsNormalization(hostname)) {
hostname = IDN.toASCII(hostname, IDN.ALLOW_UNASSIGNED);
}
return hostname.toLowerCase(Locale.US);
}
@Nullable
private static String normalizeHostname(String line) {
final String hostname = IDN.toASCII(line.trim(), IDN.ALLOW_UNASSIGNED);
if (!HOSTNAME_PATTERN.matcher(hostname).matches()) {
return null;
}
return Ascii.toLowerCase(hostname);
}
public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean punycode, boolean tryTelegraph, boolean ask) {
if (fragment == null || fragment.getParentActivity() == null) {
return;
}
long inlineReturn = (fragment instanceof ChatActivity) ? ((ChatActivity) fragment).getInlineReturn() : 0;
if (Browser.isInternalUrl(url, null) || !ask) {
Browser.openUrl(fragment.getParentActivity(), url, inlineReturn == 0, tryTelegraph);
} else {
String urlFinal;
if (punycode) {
try {
Uri uri = Uri.parse(url);
String host = IDN.toASCII(uri.getHost(), IDN.ALLOW_UNASSIGNED);
urlFinal = uri.getScheme() + "://" + host + uri.getPath();
} catch (Exception e) {
FileLog.e(e);
urlFinal = url;
}
} else {
urlFinal = url;
}
AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getParentActivity());
builder.setTitle(LocaleController.getString("OpenUrlTitle", R.string.OpenUrlTitle));
String format = LocaleController.getString("OpenUrlAlert2", R.string.OpenUrlAlert2);
int index = format.indexOf("%");
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(String.format(format, urlFinal));
if (index >= 0) {
stringBuilder.setSpan(new URLSpan(urlFinal), index, index + urlFinal.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
builder.setMessage(stringBuilder);
builder.setMessageTextViewClickable(false);
builder.setPositiveButton(LocaleController.getString("Open", R.string.Open), (dialogInterface, i) -> Browser.openUrl(fragment.getParentActivity(), url, inlineReturn == 0, tryTelegraph));
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
fragment.showDialog(builder.create());
}
}
/**
* {@link java.net.IDN#toASCII(String)}
* @since 1.6
*/
public void test_ToASCII_LString() {
try {
IDN.toASCII(null);
fail("should throw NullPointerException");
} catch (NullPointerException e) {
// expected
}
assertEquals("www.xn--gwtq9nb2a.jp", IDN
.toASCII("www.\u65E5\u672C\u5E73.jp"));
assertEquals(
"www.xn--vckk7bxa0eza9ezc9d.com",
IDN
.toASCII("www.\u30CF\u30F3\u30C9\u30DC\u30FC\u30EB\u30B5\u30E0\u30BA.com"));
assertEquals("www.xn--frgbolaget-q5a.nu", IDN
.toASCII("www.f\u00E4rgbolaget.nu"));
assertEquals("www.xn--bcher-kva.de", IDN.toASCII("www.b\u00FCcher.de"));
assertEquals("www.xn--brndendekrlighed-vobh.com", IDN
.toASCII("www.br\u00E6ndendek\u00E6rlighed.com"));
assertEquals("www.xn--rksmrgs-5wao1o.se", IDN
.toASCII("www.r\u00E4ksm\u00F6rg\u00E5s.se"));
assertEquals("www.xn--9d0bm53a3xbzui.com", IDN
.toASCII("www.\uC608\uBE44\uAD50\uC0AC.com"));
assertEquals("xn--lck1c3crb1723bpq4a.com", IDN
.toASCII("\u7406\u5BB9\u30CA\u30AB\u30E0\u30E9.com"));
assertEquals("xn--l8je6s7a45b.org", IDN
.toASCII("\u3042\u30FC\u308B\u3044\u3093.org"));
assertEquals("www.xn--frjestadsbk-l8a.net", IDN
.toASCII("www.f\u00E4rjestadsbk.net"));
assertEquals("www.xn--mkitorppa-v2a.edu", IDN
.toASCII("www.m\u00E4kitorppa.edu"));
}
/**
* IDNA ASCII conversion, case normalization and validation.
*/
static String normalizeDefaultHostname(String defaultHostname) {
requireNonNull(defaultHostname, "defaultHostname");
if (needsNormalization(defaultHostname)) {
defaultHostname = IDN.toASCII(defaultHostname, IDN.ALLOW_UNASSIGNED);
}
if (!HOSTNAME_PATTERN.matcher(defaultHostname).matches()) {
throw new IllegalArgumentException("defaultHostname: " + defaultHostname);
}
return Ascii.toLowerCase(defaultHostname);
}
/**
* Attempts to convert a Unicode string to an ASCII string using IDN rules.
* As of May 2014, the underlying Java function IDNA2003.
* @param src String to convert.
* @return: String containing only ASCII characters on success, null on
* failure.
*/
@CalledByNative
private static String idnToASCII(String src) {
try {
return IDN.toASCII(src, IDN.USE_STD3_ASCII_RULES);
} catch (Exception e) {
return null;
}
}
/**
* Converts an internationalized domain name (IDN) in an URL to and from ASCII/Unicode.
* @param url the URL where the domain name should be converted
* @param toASCII if true converts from Unicode to ASCII, if false converts from ASCII to Unicode
* @return the URL containing the converted domain name
*/
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public static String convertIdn(String url, boolean toASCII) {
String urlNoDots = url;
String dots="";
while (urlNoDots.startsWith(".")) {
urlNoDots = url.substring(1);
dots = dots + ".";
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
// Find host name after '//' or '@'
int hostStart = 0;
if (urlNoDots.contains("//")) {
hostStart = url.indexOf("//") + "//".length();
} else if (url.contains("@")) {
hostStart = url.indexOf("@") + "@".length();
}
int hostEnd = url.substring(hostStart).indexOf("/");
// Handle URL which doesn't have a path (path is implicitly '/')
hostEnd = (hostEnd == -1 ? urlNoDots.length() : hostStart + hostEnd);
String host = urlNoDots.substring(hostStart, hostEnd);
host = (toASCII ? IDN.toASCII(host) : IDN.toUnicode(host));
return dots + urlNoDots.substring(0, hostStart) + host + urlNoDots.substring(hostEnd);
} else {
return dots + url;
}
}
private DnsQuestionWithoutTrailingDot(String name, DnsRecordType type) {
this.name = IDN.toASCII(requireNonNull(name, "name"));
this.type = requireNonNull(type, "type");
}
/**
* Creates an {@code SNIHostName} using the specified encoded value.
* <P>
* This method is normally used to parse the encoded name value in a
* requested SNI extension.
* <P>
* Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
* the encoded name value of a hostname is
* {@link StandardCharsets#US_ASCII}-compliant. However, in the previous
* version of the SNI extension (
* <A HREF="http://www.ietf.org/rfc/rfc4366.txt">RFC 4366</A>),
* the encoded hostname is represented as a byte string using UTF-8
* encoding. For the purpose of version tolerance, this method allows
* that the charset of {@code encoded} argument can be
* {@link StandardCharsets#UTF_8}, as well as
* {@link StandardCharsets#US_ASCII}. {@link IDN#toASCII(String)} is used
* to translate the {@code encoded} argument into ASCII Compatible
* Encoding (ACE) hostname.
* <P>
* It is strongly recommended that this constructor is only used to parse
* the encoded name value in a requested SNI extension. Otherwise, to
* comply with <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
* please always use {@link StandardCharsets#US_ASCII}-compliant charset
* and enforce the restrictions on ASCII characters in hostnames (see
* <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
* <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
* <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>)
* for {@code encoded} argument, or use
* {@link SNIHostName#SNIHostName(String)} instead.
* <P>
* The {@code encoded} argument is illegal if it:
* <ul>
* <li> {@code encoded} is empty,</li>
* <li> {@code encoded} ends with a trailing dot,</li>
* <li> {@code encoded} is not encoded in
* {@link StandardCharsets#US_ASCII} or
* {@link StandardCharsets#UTF_8}-compliant charset,</li>
* <li> {@code encoded} is not a valid Internationalized
* Domain Name (IDN) compliant with the RFC 3490 specification.</li>
* </ul>
*
* <P>
* Note that the {@code encoded} byte array is cloned
* to protect against subsequent modification.
*
* @param encoded
* the encoded hostname of this server name
*
* @throws NullPointerException if {@code encoded} is {@code null}
* @throws IllegalArgumentException if {@code encoded} is illegal
*/
public SNIHostName(byte[] encoded) {
// NullPointerException will be thrown if {@code encoded} is null
super(StandardConstants.SNI_HOST_NAME, encoded);
// Compliance: RFC 4366 requires that the hostname is represented
// as a byte string using UTF_8 encoding [UTF8]
try {
// Please don't use {@link String} constructors because they
// do not report coding errors.
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
this.hostname = IDN.toASCII(
decoder.decode(ByteBuffer.wrap(encoded)).toString());
} catch (RuntimeException | CharacterCodingException e) {
throw new IllegalArgumentException(
"The encoded server name value is invalid", e);
}
// check the validity of the string hostname
checkHostName();
}
/**
* Creates an {@code SNIHostName} using the specified encoded value.
* <P>
* This method is normally used to parse the encoded name value in a
* requested SNI extension.
* <P>
* Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
* the encoded name value of a hostname is
* {@link StandardCharsets#US_ASCII}-compliant. However, in the previous
* version of the SNI extension (
* <A HREF="http://www.ietf.org/rfc/rfc4366.txt">RFC 4366</A>),
* the encoded hostname is represented as a byte string using UTF-8
* encoding. For the purpose of version tolerance, this method allows
* that the charset of {@code encoded} argument can be
* {@link StandardCharsets#UTF_8}, as well as
* {@link StandardCharsets#US_ASCII}. {@link IDN#toASCII(String)} is used
* to translate the {@code encoded} argument into ASCII Compatible
* Encoding (ACE) hostname.
* <P>
* It is strongly recommended that this constructor is only used to parse
* the encoded name value in a requested SNI extension. Otherwise, to
* comply with <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
* please always use {@link StandardCharsets#US_ASCII}-compliant charset
* and enforce the restrictions on ASCII characters in hostnames (see
* <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
* <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
* <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>)
* for {@code encoded} argument, or use
* {@link SNIHostName#SNIHostName(String)} instead.
* <P>
* The {@code encoded} argument is illegal if it:
* <ul>
* <li> {@code encoded} is empty,</li>
* <li> {@code encoded} ends with a trailing dot,</li>
* <li> {@code encoded} is not encoded in
* {@link StandardCharsets#US_ASCII} or
* {@link StandardCharsets#UTF_8}-compliant charset,</li>
* <li> {@code encoded} is not a valid Internationalized
* Domain Name (IDN) compliant with the RFC 3490 specification.</li>
* </ul>
*
* <P>
* Note that the {@code encoded} byte array is cloned
* to protect against subsequent modification.
*
* @param encoded
* the encoded hostname of this server name
*
* @throws NullPointerException if {@code encoded} is {@code null}
* @throws IllegalArgumentException if {@code encoded} is illegal
*/
public SNIHostName(byte[] encoded) {
// NullPointerException will be thrown if {@code encoded} is null
super(StandardConstants.SNI_HOST_NAME, encoded);
// Compliance: RFC 4366 requires that the hostname is represented
// as a byte string using UTF_8 encoding [UTF8]
try {
// Please don't use {@link String} constructors because they
// do not report coding errors.
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
this.hostname = IDN.toASCII(
decoder.decode(ByteBuffer.wrap(encoded)).toString());
} catch (RuntimeException | CharacterCodingException e) {
throw new IllegalArgumentException(
"The encoded server name value is invalid", e);
}
// check the validity of the string hostname
checkHostName();
}
/**
* Creates an {@code SNIHostName} using the specified encoded value.
* <P>
* This method is normally used to parse the encoded name value in a
* requested SNI extension.
* <P>
* Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
* the encoded name value of a hostname is
* {@link StandardCharsets#US_ASCII}-compliant. However, in the previous
* version of the SNI extension (
* <A HREF="http://www.ietf.org/rfc/rfc4366.txt">RFC 4366</A>),
* the encoded hostname is represented as a byte string using UTF-8
* encoding. For the purpose of version tolerance, this method allows
* that the charset of {@code encoded} argument can be
* {@link StandardCharsets#UTF_8}, as well as
* {@link StandardCharsets#US_ASCII}. {@link IDN#toASCII(String)} is used
* to translate the {@code encoded} argument into ASCII Compatible
* Encoding (ACE) hostname.
* <P>
* It is strongly recommended that this constructor is only used to parse
* the encoded name value in a requested SNI extension. Otherwise, to
* comply with <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
* please always use {@link StandardCharsets#US_ASCII}-compliant charset
* and enforce the restrictions on ASCII characters in hostnames (see
* <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
* <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
* <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>)
* for {@code encoded} argument, or use
* {@link SNIHostName#SNIHostName(String)} instead.
* <P>
* The {@code encoded} argument is illegal if it:
* <ul>
* <li> {@code encoded} is empty,</li>
* <li> {@code encoded} ends with a trailing dot,</li>
* <li> {@code encoded} is not encoded in
* {@link StandardCharsets#US_ASCII} or
* {@link StandardCharsets#UTF_8}-compliant charset,</li>
* <li> {@code encoded} is not a valid Internationalized
* Domain Name (IDN) compliant with the RFC 3490 specification.</li>
* </ul>
*
* <P>
* Note that the {@code encoded} byte array is cloned
* to protect against subsequent modification.
*
* @param encoded
* the encoded hostname of this server name
*
* @throws NullPointerException if {@code encoded} is {@code null}
* @throws IllegalArgumentException if {@code encoded} is illegal
*/
public SNIHostName(byte[] encoded) {
// NullPointerException will be thrown if {@code encoded} is null
super(StandardConstants.SNI_HOST_NAME, encoded);
// Compliance: RFC 4366 requires that the hostname is represented
// as a byte string using UTF_8 encoding [UTF8]
try {
// Please don't use {@link String} constructors because they
// do not report coding errors.
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
this.hostname = IDN.toASCII(
decoder.decode(ByteBuffer.wrap(encoded)).toString());
} catch (RuntimeException | CharacterCodingException e) {
throw new IllegalArgumentException(
"The encoded server name value is invalid", e);
}
// check the validity of the string hostname
checkHostName();
}
/**
* Creates an {@code SNIHostName} using the specified encoded value.
* <P>
* This method is normally used to parse the encoded name value in a
* requested SNI extension.
* <P>
* Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
* the encoded name value of a hostname is
* {@link StandardCharsets#US_ASCII}-compliant. However, in the previous
* version of the SNI extension (
* <A HREF="http://www.ietf.org/rfc/rfc4366.txt">RFC 4366</A>),
* the encoded hostname is represented as a byte string using UTF-8
* encoding. For the purpose of version tolerance, this method allows
* that the charset of {@code encoded} argument can be
* {@link StandardCharsets#UTF_8}, as well as
* {@link StandardCharsets#US_ASCII}. {@link IDN#toASCII(String)} is used
* to translate the {@code encoded} argument into ASCII Compatible
* Encoding (ACE) hostname.
* <P>
* It is strongly recommended that this constructor is only used to parse
* the encoded name value in a requested SNI extension. Otherwise, to
* comply with <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
* please always use {@link StandardCharsets#US_ASCII}-compliant charset
* and enforce the restrictions on ASCII characters in hostnames (see
* <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
* <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
* <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>)
* for {@code encoded} argument, or use
* {@link SNIHostName#SNIHostName(String)} instead.
* <P>
* The {@code encoded} argument is illegal if it:
* <ul>
* <li> {@code encoded} is empty,</li>
* <li> {@code encoded} ends with a trailing dot,</li>
* <li> {@code encoded} is not encoded in
* {@link StandardCharsets#US_ASCII} or
* {@link StandardCharsets#UTF_8}-compliant charset,</li>
* <li> {@code encoded} is not a valid Internationalized
* Domain Name (IDN) compliant with the RFC 3490 specification.</li>
* </ul>
*
* <P>
* Note that the {@code encoded} byte array is cloned
* to protect against subsequent modification.
*
* @param encoded
* the encoded hostname of this server name
*
* @throws NullPointerException if {@code encoded} is {@code null}
* @throws IllegalArgumentException if {@code encoded} is illegal
*/
public SNIHostName(byte[] encoded) {
// NullPointerException will be thrown if {@code encoded} is null
super(StandardConstants.SNI_HOST_NAME, encoded);
// Compliance: RFC 4366 requires that the hostname is represented
// as a byte string using UTF_8 encoding [UTF8]
try {
// Please don't use {@link String} constructors because they
// do not report coding errors.
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
this.hostname = IDN.toASCII(
decoder.decode(ByteBuffer.wrap(encoded)).toString());
} catch (RuntimeException | CharacterCodingException e) {
throw new IllegalArgumentException(
"The encoded server name value is invalid", e);
}
// check the validity of the string hostname
checkHostName();
}