Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/net/ssl/SNIHostName.java
38918 views
1
/*
2
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package javax.net.ssl;
27
28
import java.net.IDN;
29
import java.nio.ByteBuffer;
30
import java.nio.charset.CodingErrorAction;
31
import java.nio.charset.StandardCharsets;
32
import java.nio.charset.CharsetDecoder;
33
import java.nio.charset.CharacterCodingException;
34
import java.util.Locale;
35
import java.util.Objects;
36
import java.util.regex.Pattern;
37
38
/**
39
* Instances of this class represent a server name of type
40
* {@link StandardConstants#SNI_HOST_NAME host_name} in a Server Name
41
* Indication (SNI) extension.
42
* <P>
43
* As described in section 3, "Server Name Indication", of
44
* <A HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>,
45
* "HostName" contains the fully qualified DNS hostname of the server, as
46
* understood by the client. The encoded server name value of a hostname is
47
* represented as a byte string using ASCII encoding without a trailing dot.
48
* This allows the support of Internationalized Domain Names (IDN) through
49
* the use of A-labels (the ASCII-Compatible Encoding (ACE) form of a valid
50
* string of Internationalized Domain Names for Applications (IDNA)) defined
51
* in <A HREF="http://www.ietf.org/rfc/rfc5890.txt">RFC 5890</A>.
52
* <P>
53
* Note that {@code SNIHostName} objects are immutable.
54
*
55
* @see SNIServerName
56
* @see StandardConstants#SNI_HOST_NAME
57
*
58
* @since 1.8
59
*/
60
public final class SNIHostName extends SNIServerName {
61
62
// the decoded string value of the server name
63
private final String hostname;
64
65
/**
66
* Creates an {@code SNIHostName} using the specified hostname.
67
* <P>
68
* Note that per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
69
* the encoded server name value of a hostname is
70
* {@link StandardCharsets#US_ASCII}-compliant. In this method,
71
* {@code hostname} can be a user-friendly Internationalized Domain Name
72
* (IDN). {@link IDN#toASCII(String, int)} is used to enforce the
73
* restrictions on ASCII characters in hostnames (see
74
* <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
75
* <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
76
* <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>) and
77
* translate the {@code hostname} into ASCII Compatible Encoding (ACE), as:
78
* <pre>
79
* IDN.toASCII(hostname, IDN.USE_STD3_ASCII_RULES);
80
* </pre>
81
* <P>
82
* The {@code hostname} argument is illegal if it:
83
* <ul>
84
* <li> {@code hostname} is empty,</li>
85
* <li> {@code hostname} ends with a trailing dot,</li>
86
* <li> {@code hostname} is not a valid Internationalized
87
* Domain Name (IDN) compliant with the RFC 3490 specification.</li>
88
* </ul>
89
* @param hostname
90
* the hostname of this server name
91
*
92
* @throws NullPointerException if {@code hostname} is {@code null}
93
* @throws IllegalArgumentException if {@code hostname} is illegal
94
*/
95
public SNIHostName(String hostname) {
96
// IllegalArgumentException will be thrown if {@code hostname} is
97
// not a valid IDN.
98
super(StandardConstants.SNI_HOST_NAME,
99
(hostname = IDN.toASCII(
100
Objects.requireNonNull(hostname,
101
"Server name value of host_name cannot be null"),
102
IDN.USE_STD3_ASCII_RULES))
103
.getBytes(StandardCharsets.US_ASCII));
104
105
this.hostname = hostname;
106
107
// check the validity of the string hostname
108
checkHostName();
109
}
110
111
/**
112
* Creates an {@code SNIHostName} using the specified encoded value.
113
* <P>
114
* This method is normally used to parse the encoded name value in a
115
* requested SNI extension.
116
* <P>
117
* Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
118
* the encoded name value of a hostname is
119
* {@link StandardCharsets#US_ASCII}-compliant. However, in the previous
120
* version of the SNI extension (
121
* <A HREF="http://www.ietf.org/rfc/rfc4366.txt">RFC 4366</A>),
122
* the encoded hostname is represented as a byte string using UTF-8
123
* encoding. For the purpose of version tolerance, this method allows
124
* that the charset of {@code encoded} argument can be
125
* {@link StandardCharsets#UTF_8}, as well as
126
* {@link StandardCharsets#US_ASCII}. {@link IDN#toASCII(String)} is used
127
* to translate the {@code encoded} argument into ASCII Compatible
128
* Encoding (ACE) hostname.
129
* <P>
130
* It is strongly recommended that this constructor is only used to parse
131
* the encoded name value in a requested SNI extension. Otherwise, to
132
* comply with <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
133
* please always use {@link StandardCharsets#US_ASCII}-compliant charset
134
* and enforce the restrictions on ASCII characters in hostnames (see
135
* <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
136
* <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
137
* <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>)
138
* for {@code encoded} argument, or use
139
* {@link SNIHostName#SNIHostName(String)} instead.
140
* <P>
141
* The {@code encoded} argument is illegal if it:
142
* <ul>
143
* <li> {@code encoded} is empty,</li>
144
* <li> {@code encoded} ends with a trailing dot,</li>
145
* <li> {@code encoded} is not encoded in
146
* {@link StandardCharsets#US_ASCII} or
147
* {@link StandardCharsets#UTF_8}-compliant charset,</li>
148
* <li> {@code encoded} is not a valid Internationalized
149
* Domain Name (IDN) compliant with the RFC 3490 specification.</li>
150
* </ul>
151
*
152
* <P>
153
* Note that the {@code encoded} byte array is cloned
154
* to protect against subsequent modification.
155
*
156
* @param encoded
157
* the encoded hostname of this server name
158
*
159
* @throws NullPointerException if {@code encoded} is {@code null}
160
* @throws IllegalArgumentException if {@code encoded} is illegal
161
*/
162
public SNIHostName(byte[] encoded) {
163
// NullPointerException will be thrown if {@code encoded} is null
164
super(StandardConstants.SNI_HOST_NAME, encoded);
165
166
// Compliance: RFC 4366 requires that the hostname is represented
167
// as a byte string using UTF_8 encoding [UTF8]
168
try {
169
// Please don't use {@link String} constructors because they
170
// do not report coding errors.
171
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
172
.onMalformedInput(CodingErrorAction.REPORT)
173
.onUnmappableCharacter(CodingErrorAction.REPORT);
174
175
this.hostname = IDN.toASCII(
176
decoder.decode(ByteBuffer.wrap(encoded)).toString());
177
} catch (RuntimeException | CharacterCodingException e) {
178
throw new IllegalArgumentException(
179
"The encoded server name value is invalid", e);
180
}
181
182
// check the validity of the string hostname
183
checkHostName();
184
}
185
186
/**
187
* Returns the {@link StandardCharsets#US_ASCII}-compliant hostname of
188
* this {@code SNIHostName} object.
189
* <P>
190
* Note that, per
191
* <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>, the
192
* returned hostname may be an internationalized domain name that
193
* contains A-labels. See
194
* <A HREF="http://www.ietf.org/rfc/rfc5890.txt">RFC 5890</A>
195
* for more information about the detailed A-label specification.
196
*
197
* @return the {@link StandardCharsets#US_ASCII}-compliant hostname
198
* of this {@code SNIHostName} object
199
*/
200
public String getAsciiName() {
201
return hostname;
202
}
203
204
/**
205
* Compares this server name to the specified object.
206
* <P>
207
* Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>, DNS
208
* hostnames are case-insensitive. Two server hostnames are equal if,
209
* and only if, they have the same name type, and the hostnames are
210
* equal in a case-independent comparison.
211
*
212
* @param other
213
* the other server name object to compare with.
214
* @return true if, and only if, the {@code other} is considered
215
* equal to this instance
216
*/
217
@Override
218
public boolean equals(Object other) {
219
if (this == other) {
220
return true;
221
}
222
223
if (other instanceof SNIHostName) {
224
return hostname.equalsIgnoreCase(((SNIHostName)other).hostname);
225
}
226
227
return false;
228
}
229
230
/**
231
* Returns a hash code value for this {@code SNIHostName}.
232
* <P>
233
* The hash code value is generated using the case-insensitive hostname
234
* of this {@code SNIHostName}.
235
*
236
* @return a hash code value for this {@code SNIHostName}.
237
*/
238
@Override
239
public int hashCode() {
240
int result = 17; // 17/31: prime number to decrease collisions
241
result = 31 * result + hostname.toUpperCase(Locale.ENGLISH).hashCode();
242
243
return result;
244
}
245
246
/**
247
* Returns a string representation of the object, including the DNS
248
* hostname in this {@code SNIHostName} object.
249
* <P>
250
* The exact details of the representation are unspecified and subject
251
* to change, but the following may be regarded as typical:
252
* <pre>
253
* "type=host_name (0), value={@literal <hostname>}"
254
* </pre>
255
* The "{@literal <hostname>}" is an ASCII representation of the hostname,
256
* which may contains A-labels. For example, a returned value of an pseudo
257
* hostname may look like:
258
* <pre>
259
* "type=host_name (0), value=www.example.com"
260
* </pre>
261
* or
262
* <pre>
263
* "type=host_name (0), value=xn--fsqu00a.xn--0zwm56d"
264
* </pre>
265
* <P>
266
* Please NOTE that the exact details of the representation are unspecified
267
* and subject to change.
268
*
269
* @return a string representation of the object.
270
*/
271
@Override
272
public String toString() {
273
return "type=host_name (0), value=" + hostname;
274
}
275
276
/**
277
* Creates an {@link SNIMatcher} object for {@code SNIHostName}s.
278
* <P>
279
* This method can be used by a server to verify the acceptable
280
* {@code SNIHostName}s. For example,
281
* <pre>
282
* SNIMatcher matcher =
283
* SNIHostName.createSNIMatcher("www\\.example\\.com");
284
* </pre>
285
* will accept the hostname "www.example.com".
286
* <pre>
287
* SNIMatcher matcher =
288
* SNIHostName.createSNIMatcher("www\\.example\\.(com|org)");
289
* </pre>
290
* will accept hostnames "www.example.com" and "www.example.org".
291
*
292
* @param regex
293
* the <a href="{@docRoot}/java/util/regex/Pattern.html#sum">
294
* regular expression pattern</a>
295
* representing the hostname(s) to match
296
* @return a {@code SNIMatcher} object for {@code SNIHostName}s
297
* @throws NullPointerException if {@code regex} is
298
* {@code null}
299
* @throws java.util.regex.PatternSyntaxException if the regular expression's
300
* syntax is invalid
301
*/
302
public static SNIMatcher createSNIMatcher(String regex) {
303
if (regex == null) {
304
throw new NullPointerException(
305
"The regular expression cannot be null");
306
}
307
308
return new SNIHostNameMatcher(regex);
309
}
310
311
// check the validity of the string hostname
312
private void checkHostName() {
313
if (hostname.isEmpty()) {
314
throw new IllegalArgumentException(
315
"Server name value of host_name cannot be empty");
316
}
317
318
if (hostname.endsWith(".")) {
319
throw new IllegalArgumentException(
320
"Server name value of host_name cannot have the trailing dot");
321
}
322
}
323
324
private final static class SNIHostNameMatcher extends SNIMatcher {
325
326
// the compiled representation of a regular expression.
327
private final Pattern pattern;
328
329
/**
330
* Creates an SNIHostNameMatcher object.
331
*
332
* @param regex
333
* the <a href="{@docRoot}/java/util/regex/Pattern.html#sum">
334
* regular expression pattern</a>
335
* representing the hostname(s) to match
336
* @throws NullPointerException if {@code regex} is
337
* {@code null}
338
* @throws PatternSyntaxException if the regular expression's syntax
339
* is invalid
340
*/
341
SNIHostNameMatcher(String regex) {
342
super(StandardConstants.SNI_HOST_NAME);
343
pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
344
}
345
346
/**
347
* Attempts to match the given {@link SNIServerName}.
348
*
349
* @param serverName
350
* the {@link SNIServerName} instance on which this matcher
351
* performs match operations
352
*
353
* @return {@code true} if, and only if, the matcher matches the
354
* given {@code serverName}
355
*
356
* @throws NullPointerException if {@code serverName} is {@code null}
357
* @throws IllegalArgumentException if {@code serverName} is
358
* not of {@code StandardConstants#SNI_HOST_NAME} type
359
*
360
* @see SNIServerName
361
*/
362
@Override
363
public boolean matches(SNIServerName serverName) {
364
if (serverName == null) {
365
throw new NullPointerException(
366
"The SNIServerName argument cannot be null");
367
}
368
369
SNIHostName hostname;
370
if (!(serverName instanceof SNIHostName)) {
371
if (serverName.getType() != StandardConstants.SNI_HOST_NAME) {
372
throw new IllegalArgumentException(
373
"The server name type is not host_name");
374
}
375
376
try {
377
hostname = new SNIHostName(serverName.getEncoded());
378
} catch (NullPointerException | IllegalArgumentException e) {
379
return false;
380
}
381
} else {
382
hostname = (SNIHostName)serverName;
383
}
384
385
// Let's first try the ascii name matching
386
String asciiName = hostname.getAsciiName();
387
if (pattern.matcher(asciiName).matches()) {
388
return true;
389
}
390
391
// May be an internationalized domain name, check the Unicode
392
// representations.
393
return pattern.matcher(IDN.toUnicode(asciiName)).matches();
394
}
395
}
396
}
397
398