Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/security/util/DomainName.java
41159 views
1
/*
2
* Copyright (c) 2017, 2021, 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 sun.security.util;
27
28
import java.io.BufferedReader;
29
import java.io.File;
30
import java.io.FileInputStream;
31
import java.io.FileNotFoundException;
32
import java.io.InputStream;
33
import java.io.InputStreamReader;
34
import java.io.IOException;
35
import java.security.AccessController;
36
import java.security.PrivilegedAction;
37
import java.util.Arrays;
38
import java.util.HashSet;
39
import java.util.Iterator;
40
import java.util.LinkedList;
41
import java.util.List;
42
import java.util.Map;
43
import java.util.Set;
44
import java.util.concurrent.ConcurrentHashMap;
45
import java.util.zip.ZipEntry;
46
import java.util.zip.ZipInputStream;
47
48
import static java.nio.charset.StandardCharsets.UTF_8;
49
50
import sun.security.ssl.SSLLogger;
51
52
/**
53
* Allows public suffixes and registered domains to be determined from a
54
* string that represents a target domain name. A database of known
55
* registered suffixes is used to perform the determination.
56
*
57
* A public suffix is defined as the rightmost part of a domain name
58
* that is not owned by an individual registrant. Examples of
59
* public suffixes are:
60
* com
61
* edu
62
* co.uk
63
* k12.ak.us
64
* com.tw
65
* \u7db2\u8def.tw
66
*
67
* Public suffixes effectively denote registration authorities.
68
*
69
* A registered domain is a public suffix preceded by one domain label
70
* and a ".". Examples are:
71
* oracle.com
72
* mit.edu
73
*
74
* The internal database is derived from the information maintained at
75
* http://publicsuffix.org. The information is fixed for a particular
76
* JDK installation, but may be updated in future releases or updates.
77
*
78
* Because of the large number of top-level domains (TLDs) and public
79
* suffix rules, we only load the rules on demand -- from a Zip file
80
* containing an entry for each TLD.
81
*
82
* As each entry is loaded, its data is stored permanently in a cache.
83
*
84
* The containment hierarchy for the data is shown below:
85
*
86
* Rules --> contains all the rules for a particular TLD
87
* RuleSet --> contains all the rules that match 1 label
88
* RuleSet --> contains all the rules that match 2 labels
89
* RuleSet --> contains all the rules that match 3 labels
90
* :
91
* RuleSet --> contains all the rules that match N labels
92
* HashSet of rules, where each rule is an exception rule, a "normal"
93
* rule, a wildcard rule (rules that contain a wildcard prefix only),
94
* or a LinkedList of "other" rules
95
*
96
* The general matching algorithm tries to find a longest match. So, the
97
* search begins at the RuleSet with the most labels, and works backwards.
98
*
99
* Exceptions take priority over all other rules, and if a Rule contains
100
* any exceptions, then even if we find a "normal" match, we search all
101
* other RuleSets for exceptions. It is assumed that all other rules don't
102
* intersect/overlap. If this happens, a match will be returned, but not
103
* necessarily the expected one. For a further explanation of the rules,
104
* see http://publicsuffix.org/list/.
105
*
106
* The "other" rules are for the (possible future) case where wildcards
107
* are located in a rule any place other than the beginning.
108
*/
109
110
class DomainName {
111
/**
112
* For efficiency, the set of rules for each TLD is kept
113
* in text files and only loaded if needed.
114
*/
115
private static final Map<String, Rules> cache = new ConcurrentHashMap<>();
116
117
private DomainName() {}
118
119
/**
120
* Returns the registered domain of the specified domain.
121
*
122
* @param domain the domain name
123
* @return the registered domain, or null if not known or not registerable
124
* @throws NullPointerException if domain is null
125
*/
126
public static RegisteredDomain registeredDomain(String domain) {
127
Match match = getMatch(domain);
128
return (match != null) ? match.registeredDomain() : null;
129
}
130
131
private static Match getMatch(String domain) {
132
if (domain == null) {
133
throw new NullPointerException();
134
}
135
Rules rules = Rules.getRules(domain);
136
return rules == null ? null : rules.match(domain);
137
}
138
139
/**
140
* A Rules object contains a list of rules for a particular TLD.
141
*
142
* Rules are stored in a linked list of RuleSet objects. The list is
143
* indexed according to the number of labels in the name (minus one)
144
* such that all rules with the same number of labels are stored
145
* in the same RuleSet.
146
*
147
* Doing this means we can find the longest match first, and also we
148
* can stop comparing as soon as we find a match.
149
*/
150
private static class Rules {
151
152
private final LinkedList<RuleSet> ruleSets = new LinkedList<>();
153
private final boolean hasExceptions;
154
155
private Rules(InputStream is) throws IOException {
156
InputStreamReader isr = new InputStreamReader(is, UTF_8);
157
BufferedReader reader = new BufferedReader(isr);
158
boolean hasExceptions = false;
159
160
String line;
161
int type = reader.read();
162
while (type != -1 && (line = reader.readLine()) != null) {
163
int numLabels = RuleSet.numLabels(line);
164
if (numLabels != 0) {
165
RuleSet ruleset = getRuleSet(numLabels - 1);
166
ruleset.addRule(type, line);
167
hasExceptions |= ruleset.hasExceptions;
168
}
169
type = reader.read();
170
}
171
this.hasExceptions = hasExceptions;
172
}
173
174
static Rules getRules(String domain) {
175
String tld = getTopLevelDomain(domain);
176
if (tld.isEmpty()) {
177
return null;
178
}
179
return cache.computeIfAbsent(tld, k -> createRules(tld));
180
}
181
182
private static String getTopLevelDomain(String domain) {
183
int n = domain.lastIndexOf('.');
184
if (n == -1) {
185
return domain;
186
}
187
return domain.substring(n + 1);
188
}
189
190
private static Rules createRules(String tld) {
191
try (InputStream pubSuffixStream = getPubSuffixStream()) {
192
if (pubSuffixStream == null) {
193
return null;
194
}
195
return getRules(tld, new ZipInputStream(pubSuffixStream));
196
} catch (IOException e) {
197
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
198
SSLLogger.fine(
199
"cannot parse public suffix data for " + tld +
200
": " + e.getMessage());
201
}
202
return null;
203
}
204
}
205
206
private static InputStream getPubSuffixStream() {
207
@SuppressWarnings("removal")
208
InputStream is = AccessController.doPrivileged(
209
new PrivilegedAction<>() {
210
@Override
211
public InputStream run() {
212
File f = new File(System.getProperty("java.home"),
213
"lib/security/public_suffix_list.dat");
214
try {
215
return new FileInputStream(f);
216
} catch (FileNotFoundException e) {
217
return null;
218
}
219
}
220
}
221
);
222
if (is == null) {
223
if (SSLLogger.isOn && SSLLogger.isOn("ssl") &&
224
SSLLogger.isOn("trustmanager")) {
225
SSLLogger.fine(
226
"lib/security/public_suffix_list.dat not found");
227
}
228
}
229
return is;
230
}
231
232
private static Rules getRules(String tld,
233
ZipInputStream zis) throws IOException {
234
boolean found = false;
235
ZipEntry ze = zis.getNextEntry();
236
while (ze != null && !found) {
237
if (ze.getName().equals(tld)) {
238
found = true;
239
} else {
240
ze = zis.getNextEntry();
241
}
242
}
243
if (!found) {
244
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
245
SSLLogger.fine("Domain " + tld + " not found");
246
}
247
return null;
248
}
249
return new Rules(zis);
250
}
251
252
/**
253
* Return the requested RuleSet. If it hasn't been created yet,
254
* create it and any RuleSets leading up to it.
255
*/
256
private RuleSet getRuleSet(int index) {
257
if (index < ruleSets.size()) {
258
return ruleSets.get(index);
259
}
260
RuleSet r = null;
261
for (int i = ruleSets.size(); i <= index; i++) {
262
r = new RuleSet(i + 1);
263
ruleSets.add(r);
264
}
265
return r;
266
}
267
268
/**
269
* Find a match for the target string.
270
*/
271
Match match(String domain) {
272
// Start at the end of the rules list, looking for longest match.
273
// After we find a normal match, we only look for exceptions.
274
Match possibleMatch = null;
275
276
Iterator<RuleSet> it = ruleSets.descendingIterator();
277
while (it.hasNext()) {
278
RuleSet ruleSet = it.next();
279
Match match = ruleSet.match(domain);
280
if (match != null) {
281
if (match.type() == Rule.Type.EXCEPTION || !hasExceptions) {
282
return match;
283
}
284
if (possibleMatch == null) {
285
possibleMatch = match;
286
}
287
}
288
}
289
return possibleMatch;
290
}
291
292
/**
293
* Represents a set of rules with the same number of labels
294
* and for a particular TLD.
295
*
296
* Examples:
297
* numLabels = 2
298
* names: co.uk, ac.uk
299
* wildcards *.de (only "de" stored in HashSet)
300
* exceptions: !foo.de (stored as "foo.de")
301
*/
302
private static class RuleSet {
303
// the number of labels in this ruleset
304
private final int numLabels;
305
private final Set<Rule> rules = new HashSet<>();
306
boolean hasExceptions = false;
307
private static final RegisteredDomain.Type[] AUTHS =
308
RegisteredDomain.Type.values();
309
310
RuleSet(int n) {
311
numLabels = n;
312
}
313
314
void addRule(int auth, String rule) {
315
if (rule.startsWith("!")) {
316
rules.add(new Rule(rule.substring(1), Rule.Type.EXCEPTION,
317
AUTHS[auth]));
318
hasExceptions = true;
319
} else if (rule.startsWith("*.") &&
320
rule.lastIndexOf('*') == 0) {
321
rules.add(new Rule(rule.substring(2), Rule.Type.WILDCARD,
322
AUTHS[auth]));
323
} else if (rule.indexOf('*') == -1) {
324
// a "normal" label
325
rules.add(new Rule(rule, Rule.Type.NORMAL, AUTHS[auth]));
326
} else {
327
// There is a wildcard in a non-leading label. This case
328
// doesn't currently exist, but we need to handle it anyway.
329
rules.add(new OtherRule(rule, AUTHS[auth], split(rule)));
330
}
331
}
332
333
Match match(String domain) {
334
Match match = null;
335
for (Rule rule : rules) {
336
switch (rule.type) {
337
case NORMAL:
338
if (match == null) {
339
match = matchNormal(domain, rule);
340
}
341
break;
342
case WILDCARD:
343
if (match == null) {
344
match = matchWildcard(domain, rule);
345
}
346
break;
347
case OTHER:
348
if (match == null) {
349
match = matchOther(domain, rule);
350
}
351
break;
352
case EXCEPTION:
353
Match excMatch = matchException(domain, rule);
354
if (excMatch != null) {
355
return excMatch;
356
}
357
break;
358
}
359
}
360
return match;
361
}
362
363
private static LinkedList<String> split(String rule) {
364
String[] labels = rule.split("\\.");
365
return new LinkedList<>(Arrays.asList(labels));
366
}
367
368
private static int numLabels(String rule) {
369
if (rule.isEmpty()) {
370
return 0;
371
}
372
int len = rule.length();
373
int count = 0;
374
int index = 0;
375
while (index < len) {
376
int pos;
377
if ((pos = rule.indexOf('.', index)) == -1) {
378
return count + 1;
379
}
380
index = pos + 1;
381
count++;
382
}
383
return count;
384
}
385
386
/**
387
* Check for a match with an explicit name rule or a wildcard rule
388
* (i.e., a non-exception rule).
389
*/
390
private Match matchNormal(String domain, Rule rule) {
391
int index = labels(domain, numLabels);
392
if (index == -1) {
393
return null;
394
}
395
396
// Check for explicit names.
397
String substring = domain.substring(index);
398
if (rule.domain.equals(substring)) {
399
return new CommonMatch(domain, rule, index);
400
}
401
402
return null;
403
}
404
405
private Match matchWildcard(String domain, Rule rule) {
406
// Now check for wildcards. In this case, there is one fewer
407
// label than numLabels.
408
int index = labels(domain, numLabels - 1);
409
if (index > 0) {
410
String substring = domain.substring(index);
411
412
if (rule.domain.equals(substring)) {
413
return new CommonMatch(domain, rule,
414
labels(domain, numLabels));
415
}
416
}
417
418
return null;
419
}
420
421
/**
422
* Check for a match with an exception rule.
423
*/
424
private Match matchException(String domain, Rule rule) {
425
int index = labels(domain, numLabels);
426
if (index == -1) {
427
return null;
428
}
429
String substring = domain.substring(index);
430
431
if (rule.domain.equals(substring)) {
432
return new CommonMatch(domain, rule,
433
labels(domain, numLabels - 1));
434
}
435
436
return null;
437
}
438
439
/**
440
* A left-to-right comparison of labels.
441
* The simplest approach to doing match() would be to
442
* use a descending iterator giving a right-to-left comparison.
443
* But, it's more efficient to do left-to-right compares
444
* because the left most labels are the ones most likely to be
445
* different. We just have to figure out which label to start at.
446
*/
447
private Match matchOther(String domain, Rule rule) {
448
OtherRule otherRule = (OtherRule)rule;
449
LinkedList<String> target = split(domain);
450
451
int diff = target.size() - numLabels;
452
if (diff < 0) {
453
return null;
454
}
455
456
boolean found = true;
457
for (int i = 0; i < numLabels; i++) {
458
String ruleLabel = otherRule.labels.get(i);
459
String targetLabel = target.get(i + diff);
460
461
if (ruleLabel.charAt(0) != '*' &&
462
!ruleLabel.equalsIgnoreCase(targetLabel)) {
463
found = false;
464
break;
465
}
466
}
467
if (found) {
468
return new OtherMatch(rule, numLabels, target);
469
}
470
return null;
471
}
472
473
/**
474
* Returns a substring (index) with the n right-most labels from s.
475
* Returns -1 if s does not have at least n labels, 0, if the
476
* substring is s.
477
*/
478
private static int labels(String s, int n) {
479
if (n < 1) {
480
return -1;
481
}
482
int index = s.length();
483
for (int i = 0; i < n; i++) {
484
int next = s.lastIndexOf('.', index);
485
if (next == -1) {
486
if (i == n - 1) {
487
return 0;
488
} else {
489
return -1;
490
}
491
}
492
index = next - 1;
493
}
494
return index + 2;
495
}
496
}
497
}
498
499
private static class Rule {
500
enum Type { EXCEPTION, NORMAL, OTHER, WILDCARD }
501
502
String domain;
503
Type type;
504
RegisteredDomain.Type auth;
505
Rule(String domain, Type type, RegisteredDomain.Type auth) {
506
this.domain = domain;
507
this.type = type;
508
this.auth = auth;
509
}
510
}
511
512
private static class OtherRule extends Rule {
513
List<String> labels;
514
OtherRule(String domain, RegisteredDomain.Type auth,
515
List<String> labels) {
516
super(domain, Type.OTHER, auth);
517
this.labels = labels;
518
}
519
}
520
521
/**
522
* Represents a string's match with a rule in the public suffix list.
523
*/
524
private interface Match {
525
RegisteredDomain registeredDomain();
526
Rule.Type type();
527
}
528
529
private static class RegisteredDomainImpl implements RegisteredDomain {
530
private final String name;
531
private final Type type;
532
private final String publicSuffix;
533
RegisteredDomainImpl(String name, Type type, String publicSuffix) {
534
this.name = name;
535
this.type = type;
536
this.publicSuffix = publicSuffix;
537
}
538
@Override
539
public String name() {
540
return name;
541
}
542
@Override
543
public Type type() {
544
return type;
545
}
546
@Override
547
public String publicSuffix() {
548
return publicSuffix;
549
}
550
}
551
552
/**
553
* Represents a match against a standard rule in the public suffix list.
554
* A standard rule is an explicit name, a wildcard rule with a wildcard
555
* only in the leading label, or an exception rule.
556
*/
557
private static class CommonMatch implements Match {
558
private String domain;
559
private int publicSuffix; // index to
560
private int registeredDomain; // index to
561
private final Rule rule;
562
563
CommonMatch(String domain, Rule rule, int publicSuffix) {
564
this.domain = domain;
565
this.publicSuffix = publicSuffix;
566
this.rule = rule;
567
// now locate the previous label
568
registeredDomain = domain.lastIndexOf('.', publicSuffix - 2);
569
if (registeredDomain == -1) {
570
registeredDomain = 0;
571
} else {
572
registeredDomain++;
573
}
574
}
575
576
@Override
577
public RegisteredDomain registeredDomain() {
578
if (publicSuffix == 0) {
579
return null;
580
}
581
return new RegisteredDomainImpl(domain.substring(registeredDomain),
582
rule.auth,
583
domain.substring(publicSuffix));
584
}
585
586
@Override
587
public Rule.Type type() {
588
return rule.type;
589
}
590
}
591
592
/**
593
* Represents a non-match with {@code NO_MATCH} or a match against
594
* a non-standard rule in the public suffix list. A non-standard rule
595
* is a wildcard rule that includes wildcards in a label other than
596
* the leading label. The public suffix list doesn't currently have
597
* such rules.
598
*/
599
private static class OtherMatch implements Match {
600
private final Rule rule;
601
private final int numLabels;
602
private final LinkedList<String> target;
603
604
OtherMatch(Rule rule, int numLabels, LinkedList<String> target) {
605
this.rule = rule;
606
this.numLabels = numLabels;
607
this.target = target;
608
}
609
610
@Override
611
public RegisteredDomain registeredDomain() {
612
int nlabels = numLabels + 1;
613
if (nlabels > target.size()) {
614
// special case when registered domain is same as pub suff
615
return null;
616
}
617
return new RegisteredDomainImpl(getSuffixes(nlabels),
618
rule.auth, getSuffixes(numLabels));
619
}
620
621
@Override
622
public Rule.Type type() {
623
return rule.type;
624
}
625
626
private String getSuffixes(int n) {
627
Iterator<String> targetIter = target.descendingIterator();
628
StringBuilder sb = new StringBuilder();
629
while (n > 0 && targetIter.hasNext()) {
630
String s = targetIter.next();
631
sb.insert(0, s);
632
if (n > 1) {
633
sb.insert(0, '.');
634
}
635
n--;
636
}
637
return sb.toString();
638
}
639
}
640
}
641
642