Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/net/ssl/SNIHostName.java
38918 views
/*1* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.net.ssl;2627import java.net.IDN;28import java.nio.ByteBuffer;29import java.nio.charset.CodingErrorAction;30import java.nio.charset.StandardCharsets;31import java.nio.charset.CharsetDecoder;32import java.nio.charset.CharacterCodingException;33import java.util.Locale;34import java.util.Objects;35import java.util.regex.Pattern;3637/**38* Instances of this class represent a server name of type39* {@link StandardConstants#SNI_HOST_NAME host_name} in a Server Name40* Indication (SNI) extension.41* <P>42* As described in section 3, "Server Name Indication", of43* <A HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>,44* "HostName" contains the fully qualified DNS hostname of the server, as45* understood by the client. The encoded server name value of a hostname is46* represented as a byte string using ASCII encoding without a trailing dot.47* This allows the support of Internationalized Domain Names (IDN) through48* the use of A-labels (the ASCII-Compatible Encoding (ACE) form of a valid49* string of Internationalized Domain Names for Applications (IDNA)) defined50* in <A HREF="http://www.ietf.org/rfc/rfc5890.txt">RFC 5890</A>.51* <P>52* Note that {@code SNIHostName} objects are immutable.53*54* @see SNIServerName55* @see StandardConstants#SNI_HOST_NAME56*57* @since 1.858*/59public final class SNIHostName extends SNIServerName {6061// the decoded string value of the server name62private final String hostname;6364/**65* Creates an {@code SNIHostName} using the specified hostname.66* <P>67* Note that per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,68* the encoded server name value of a hostname is69* {@link StandardCharsets#US_ASCII}-compliant. In this method,70* {@code hostname} can be a user-friendly Internationalized Domain Name71* (IDN). {@link IDN#toASCII(String, int)} is used to enforce the72* restrictions on ASCII characters in hostnames (see73* <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,74* <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,75* <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>) and76* translate the {@code hostname} into ASCII Compatible Encoding (ACE), as:77* <pre>78* IDN.toASCII(hostname, IDN.USE_STD3_ASCII_RULES);79* </pre>80* <P>81* The {@code hostname} argument is illegal if it:82* <ul>83* <li> {@code hostname} is empty,</li>84* <li> {@code hostname} ends with a trailing dot,</li>85* <li> {@code hostname} is not a valid Internationalized86* Domain Name (IDN) compliant with the RFC 3490 specification.</li>87* </ul>88* @param hostname89* the hostname of this server name90*91* @throws NullPointerException if {@code hostname} is {@code null}92* @throws IllegalArgumentException if {@code hostname} is illegal93*/94public SNIHostName(String hostname) {95// IllegalArgumentException will be thrown if {@code hostname} is96// not a valid IDN.97super(StandardConstants.SNI_HOST_NAME,98(hostname = IDN.toASCII(99Objects.requireNonNull(hostname,100"Server name value of host_name cannot be null"),101IDN.USE_STD3_ASCII_RULES))102.getBytes(StandardCharsets.US_ASCII));103104this.hostname = hostname;105106// check the validity of the string hostname107checkHostName();108}109110/**111* Creates an {@code SNIHostName} using the specified encoded value.112* <P>113* This method is normally used to parse the encoded name value in a114* requested SNI extension.115* <P>116* Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,117* the encoded name value of a hostname is118* {@link StandardCharsets#US_ASCII}-compliant. However, in the previous119* version of the SNI extension (120* <A HREF="http://www.ietf.org/rfc/rfc4366.txt">RFC 4366</A>),121* the encoded hostname is represented as a byte string using UTF-8122* encoding. For the purpose of version tolerance, this method allows123* that the charset of {@code encoded} argument can be124* {@link StandardCharsets#UTF_8}, as well as125* {@link StandardCharsets#US_ASCII}. {@link IDN#toASCII(String)} is used126* to translate the {@code encoded} argument into ASCII Compatible127* Encoding (ACE) hostname.128* <P>129* It is strongly recommended that this constructor is only used to parse130* the encoded name value in a requested SNI extension. Otherwise, to131* comply with <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,132* please always use {@link StandardCharsets#US_ASCII}-compliant charset133* and enforce the restrictions on ASCII characters in hostnames (see134* <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,135* <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,136* <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>)137* for {@code encoded} argument, or use138* {@link SNIHostName#SNIHostName(String)} instead.139* <P>140* The {@code encoded} argument is illegal if it:141* <ul>142* <li> {@code encoded} is empty,</li>143* <li> {@code encoded} ends with a trailing dot,</li>144* <li> {@code encoded} is not encoded in145* {@link StandardCharsets#US_ASCII} or146* {@link StandardCharsets#UTF_8}-compliant charset,</li>147* <li> {@code encoded} is not a valid Internationalized148* Domain Name (IDN) compliant with the RFC 3490 specification.</li>149* </ul>150*151* <P>152* Note that the {@code encoded} byte array is cloned153* to protect against subsequent modification.154*155* @param encoded156* the encoded hostname of this server name157*158* @throws NullPointerException if {@code encoded} is {@code null}159* @throws IllegalArgumentException if {@code encoded} is illegal160*/161public SNIHostName(byte[] encoded) {162// NullPointerException will be thrown if {@code encoded} is null163super(StandardConstants.SNI_HOST_NAME, encoded);164165// Compliance: RFC 4366 requires that the hostname is represented166// as a byte string using UTF_8 encoding [UTF8]167try {168// Please don't use {@link String} constructors because they169// do not report coding errors.170CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()171.onMalformedInput(CodingErrorAction.REPORT)172.onUnmappableCharacter(CodingErrorAction.REPORT);173174this.hostname = IDN.toASCII(175decoder.decode(ByteBuffer.wrap(encoded)).toString());176} catch (RuntimeException | CharacterCodingException e) {177throw new IllegalArgumentException(178"The encoded server name value is invalid", e);179}180181// check the validity of the string hostname182checkHostName();183}184185/**186* Returns the {@link StandardCharsets#US_ASCII}-compliant hostname of187* this {@code SNIHostName} object.188* <P>189* Note that, per190* <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>, the191* returned hostname may be an internationalized domain name that192* contains A-labels. See193* <A HREF="http://www.ietf.org/rfc/rfc5890.txt">RFC 5890</A>194* for more information about the detailed A-label specification.195*196* @return the {@link StandardCharsets#US_ASCII}-compliant hostname197* of this {@code SNIHostName} object198*/199public String getAsciiName() {200return hostname;201}202203/**204* Compares this server name to the specified object.205* <P>206* Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>, DNS207* hostnames are case-insensitive. Two server hostnames are equal if,208* and only if, they have the same name type, and the hostnames are209* equal in a case-independent comparison.210*211* @param other212* the other server name object to compare with.213* @return true if, and only if, the {@code other} is considered214* equal to this instance215*/216@Override217public boolean equals(Object other) {218if (this == other) {219return true;220}221222if (other instanceof SNIHostName) {223return hostname.equalsIgnoreCase(((SNIHostName)other).hostname);224}225226return false;227}228229/**230* Returns a hash code value for this {@code SNIHostName}.231* <P>232* The hash code value is generated using the case-insensitive hostname233* of this {@code SNIHostName}.234*235* @return a hash code value for this {@code SNIHostName}.236*/237@Override238public int hashCode() {239int result = 17; // 17/31: prime number to decrease collisions240result = 31 * result + hostname.toUpperCase(Locale.ENGLISH).hashCode();241242return result;243}244245/**246* Returns a string representation of the object, including the DNS247* hostname in this {@code SNIHostName} object.248* <P>249* The exact details of the representation are unspecified and subject250* to change, but the following may be regarded as typical:251* <pre>252* "type=host_name (0), value={@literal <hostname>}"253* </pre>254* The "{@literal <hostname>}" is an ASCII representation of the hostname,255* which may contains A-labels. For example, a returned value of an pseudo256* hostname may look like:257* <pre>258* "type=host_name (0), value=www.example.com"259* </pre>260* or261* <pre>262* "type=host_name (0), value=xn--fsqu00a.xn--0zwm56d"263* </pre>264* <P>265* Please NOTE that the exact details of the representation are unspecified266* and subject to change.267*268* @return a string representation of the object.269*/270@Override271public String toString() {272return "type=host_name (0), value=" + hostname;273}274275/**276* Creates an {@link SNIMatcher} object for {@code SNIHostName}s.277* <P>278* This method can be used by a server to verify the acceptable279* {@code SNIHostName}s. For example,280* <pre>281* SNIMatcher matcher =282* SNIHostName.createSNIMatcher("www\\.example\\.com");283* </pre>284* will accept the hostname "www.example.com".285* <pre>286* SNIMatcher matcher =287* SNIHostName.createSNIMatcher("www\\.example\\.(com|org)");288* </pre>289* will accept hostnames "www.example.com" and "www.example.org".290*291* @param regex292* the <a href="{@docRoot}/java/util/regex/Pattern.html#sum">293* regular expression pattern</a>294* representing the hostname(s) to match295* @return a {@code SNIMatcher} object for {@code SNIHostName}s296* @throws NullPointerException if {@code regex} is297* {@code null}298* @throws java.util.regex.PatternSyntaxException if the regular expression's299* syntax is invalid300*/301public static SNIMatcher createSNIMatcher(String regex) {302if (regex == null) {303throw new NullPointerException(304"The regular expression cannot be null");305}306307return new SNIHostNameMatcher(regex);308}309310// check the validity of the string hostname311private void checkHostName() {312if (hostname.isEmpty()) {313throw new IllegalArgumentException(314"Server name value of host_name cannot be empty");315}316317if (hostname.endsWith(".")) {318throw new IllegalArgumentException(319"Server name value of host_name cannot have the trailing dot");320}321}322323private final static class SNIHostNameMatcher extends SNIMatcher {324325// the compiled representation of a regular expression.326private final Pattern pattern;327328/**329* Creates an SNIHostNameMatcher object.330*331* @param regex332* the <a href="{@docRoot}/java/util/regex/Pattern.html#sum">333* regular expression pattern</a>334* representing the hostname(s) to match335* @throws NullPointerException if {@code regex} is336* {@code null}337* @throws PatternSyntaxException if the regular expression's syntax338* is invalid339*/340SNIHostNameMatcher(String regex) {341super(StandardConstants.SNI_HOST_NAME);342pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);343}344345/**346* Attempts to match the given {@link SNIServerName}.347*348* @param serverName349* the {@link SNIServerName} instance on which this matcher350* performs match operations351*352* @return {@code true} if, and only if, the matcher matches the353* given {@code serverName}354*355* @throws NullPointerException if {@code serverName} is {@code null}356* @throws IllegalArgumentException if {@code serverName} is357* not of {@code StandardConstants#SNI_HOST_NAME} type358*359* @see SNIServerName360*/361@Override362public boolean matches(SNIServerName serverName) {363if (serverName == null) {364throw new NullPointerException(365"The SNIServerName argument cannot be null");366}367368SNIHostName hostname;369if (!(serverName instanceof SNIHostName)) {370if (serverName.getType() != StandardConstants.SNI_HOST_NAME) {371throw new IllegalArgumentException(372"The server name type is not host_name");373}374375try {376hostname = new SNIHostName(serverName.getEncoded());377} catch (NullPointerException | IllegalArgumentException e) {378return false;379}380} else {381hostname = (SNIHostName)serverName;382}383384// Let's first try the ascii name matching385String asciiName = hostname.getAsciiName();386if (pattern.matcher(asciiName).matches()) {387return true;388}389390// May be an internationalized domain name, check the Unicode391// representations.392return pattern.matcher(IDN.toUnicode(asciiName)).matches();393}394}395}396397398