|
@@ -5,25 +5,33 @@
|
|
|
*/
|
|
|
package org.elasticsearch.xpack.ml.utils;
|
|
|
|
|
|
+import org.apache.logging.log4j.LogManager;
|
|
|
import org.elasticsearch.common.io.Streams;
|
|
|
+import org.elasticsearch.common.logging.DeprecationLogger;
|
|
|
|
|
|
import java.io.InputStream;
|
|
|
+import java.security.AccessController;
|
|
|
+import java.security.PrivilegedAction;
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
+import java.util.Locale;
|
|
|
import java.util.Map;
|
|
|
+import java.util.StringJoiner;
|
|
|
|
|
|
public final class DomainSplitFunction {
|
|
|
|
|
|
- public static final String function;
|
|
|
- public static final Map<String, Object> params;
|
|
|
+ private static final DeprecationLogger DEPRECATION_LOGGER =
|
|
|
+ new DeprecationLogger(LogManager.getLogger(DomainSplitFunction.class));
|
|
|
|
|
|
- DomainSplitFunction() {}
|
|
|
+ private static final int MAX_DOMAIN_PART_LENGTH = 63;
|
|
|
|
|
|
+ private static final Map<String, String> exact;
|
|
|
+ private static final Map<String, String> under;
|
|
|
+ private static final Map<String, String> excluded;
|
|
|
static {
|
|
|
- Map<String, Object> paramsMap = new HashMap<>();
|
|
|
-
|
|
|
- Map<String, String> exact = new HashMap<>(2048);
|
|
|
+ Map<String, String> exactMap = new HashMap<>(2048);
|
|
|
|
|
|
String exactResourceName = "org/elasticsearch/xpack/ml/transforms/exact.properties";
|
|
|
|
|
@@ -31,253 +39,205 @@ public final class DomainSplitFunction {
|
|
|
List<String> lines = Streams.readAllLines(resource);
|
|
|
for (String line : lines) {
|
|
|
String[] split = line.split("=");
|
|
|
- exact.put(split[0].trim(), split[1].trim());
|
|
|
+ exactMap.put(split[0].trim(), split[1].trim());
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
throw new RuntimeException("Could not load DomainSplit resource", e);
|
|
|
}
|
|
|
- exact = Collections.unmodifiableMap(exact);
|
|
|
+ exact = Collections.unmodifiableMap(exactMap);
|
|
|
|
|
|
- Map<String, Object> under = new HashMap<>(30);
|
|
|
- under.put("bd", "i");
|
|
|
- under.put("np", "i");
|
|
|
- under.put("jm", "i");
|
|
|
- under.put("fj", "i");
|
|
|
- under.put("fk", "i");
|
|
|
- under.put("ye", "i");
|
|
|
- under.put("sch.uk", "i");
|
|
|
- under.put("bn", "i");
|
|
|
- under.put("kitakyushu.jp", "i");
|
|
|
- under.put("kobe.jp", "i");
|
|
|
- under.put("ke", "i");
|
|
|
- under.put("sapporo.jp", "i");
|
|
|
- under.put("kh", "i");
|
|
|
- under.put("mm", "i");
|
|
|
- under.put("il", "i");
|
|
|
- under.put("yokohama.jp", "i");
|
|
|
- under.put("ck", "i");
|
|
|
- under.put("nagoya.jp", "i");
|
|
|
- under.put("sendai.jp", "i");
|
|
|
- under.put("kw", "i");
|
|
|
- under.put("er", "i");
|
|
|
- under.put("mz", "i");
|
|
|
- under.put("platform.sh", "p");
|
|
|
- under.put("gu", "i");
|
|
|
- under.put("nom.br", "i");
|
|
|
- under.put("zm", "i");
|
|
|
- under.put("pg", "i");
|
|
|
- under.put("ni", "i");
|
|
|
- under.put("kawasaki.jp", "i");
|
|
|
- under.put("zw", "i");
|
|
|
- under = Collections.unmodifiableMap(under);
|
|
|
+ Map<String, String> underMap = new HashMap<>(30);
|
|
|
+ underMap.put("bd", "i");
|
|
|
+ underMap.put("np", "i");
|
|
|
+ underMap.put("jm", "i");
|
|
|
+ underMap.put("fj", "i");
|
|
|
+ underMap.put("fk", "i");
|
|
|
+ underMap.put("ye", "i");
|
|
|
+ underMap.put("sch.uk", "i");
|
|
|
+ underMap.put("bn", "i");
|
|
|
+ underMap.put("kitakyushu.jp", "i");
|
|
|
+ underMap.put("kobe.jp", "i");
|
|
|
+ underMap.put("ke", "i");
|
|
|
+ underMap.put("sapporo.jp", "i");
|
|
|
+ underMap.put("kh", "i");
|
|
|
+ underMap.put("mm", "i");
|
|
|
+ underMap.put("il", "i");
|
|
|
+ underMap.put("yokohama.jp", "i");
|
|
|
+ underMap.put("ck", "i");
|
|
|
+ underMap.put("nagoya.jp", "i");
|
|
|
+ underMap.put("sendai.jp", "i");
|
|
|
+ underMap.put("kw", "i");
|
|
|
+ underMap.put("er", "i");
|
|
|
+ underMap.put("mz", "i");
|
|
|
+ underMap.put("platform.sh", "p");
|
|
|
+ underMap.put("gu", "i");
|
|
|
+ underMap.put("nom.br", "i");
|
|
|
+ underMap.put("zm", "i");
|
|
|
+ underMap.put("pg", "i");
|
|
|
+ underMap.put("ni", "i");
|
|
|
+ underMap.put("kawasaki.jp", "i");
|
|
|
+ underMap.put("zw", "i");
|
|
|
+ under = Collections.unmodifiableMap(underMap);
|
|
|
|
|
|
- Map<String, String> excluded = new HashMap<>(9);
|
|
|
- excluded.put("city.yokohama.jp", "i");
|
|
|
- excluded.put("teledata.mz", "i");
|
|
|
- excluded.put("city.kobe.jp", "i");
|
|
|
- excluded.put("city.sapporo.jp", "i");
|
|
|
- excluded.put("city.kawasaki.jp", "i");
|
|
|
- excluded.put("city.nagoya.jp", "i");
|
|
|
- excluded.put("www.ck", "i");
|
|
|
- excluded.put("city.sendai.jp", "i");
|
|
|
- excluded.put("city.kitakyushu.jp", "i");
|
|
|
- excluded = Collections.unmodifiableMap(excluded);
|
|
|
+ Map<String, String> excludedMap = new HashMap<>(9);
|
|
|
+ excludedMap.put("city.yokohama.jp", "i");
|
|
|
+ excludedMap.put("teledata.mz", "i");
|
|
|
+ excludedMap.put("city.kobe.jp", "i");
|
|
|
+ excludedMap.put("city.sapporo.jp", "i");
|
|
|
+ excludedMap.put("city.kawasaki.jp", "i");
|
|
|
+ excludedMap.put("city.nagoya.jp", "i");
|
|
|
+ excludedMap.put("www.ck", "i");
|
|
|
+ excludedMap.put("city.sendai.jp", "i");
|
|
|
+ excludedMap.put("city.kitakyushu.jp", "i");
|
|
|
+ excluded = Collections.unmodifiableMap(excludedMap);
|
|
|
+ }
|
|
|
|
|
|
+ private DomainSplitFunction() {}
|
|
|
|
|
|
- paramsMap.put("excluded", excluded);
|
|
|
- paramsMap.put("under", under);
|
|
|
- paramsMap.put("exact", exact);
|
|
|
- params = Collections.unmodifiableMap(paramsMap);
|
|
|
+ private static String replaceDots(String input) {
|
|
|
+ String output = input;
|
|
|
+ if (output.indexOf('。') >= 0) {
|
|
|
+ output = output.replace('。', '.');
|
|
|
+ }
|
|
|
+ if (output.indexOf('.') >= 0) {
|
|
|
+ output = output.replace('.', '.');
|
|
|
+ }
|
|
|
+ if (output.indexOf('。') >= 0) {
|
|
|
+ output = output.replace('。', '.');
|
|
|
+ }
|
|
|
+ return output;
|
|
|
}
|
|
|
|
|
|
- static {
|
|
|
- String fn = "String replaceDots(String input) {\n" +
|
|
|
- " String output = input;\n" +
|
|
|
- " if (output.indexOf('。') >= 0) {\n" +
|
|
|
- " output = output.replace('。', '.');\n" +
|
|
|
- " }\n" +
|
|
|
- " if (output.indexOf('.') >= 0) {\n" +
|
|
|
- " output = output.replace('.', '.');\n" +
|
|
|
- " }\n" +
|
|
|
- " if (output.indexOf('。') >= 0) {\n" +
|
|
|
- " output = output.replace('。', '.');\n" +
|
|
|
- " }\n" +
|
|
|
- " return output;\n" +
|
|
|
- "}\n" +
|
|
|
- "List split(String value) {\n" +
|
|
|
- " int nextWord = 0;\n" +
|
|
|
- " List splits = [];\n" +
|
|
|
- " for(int i = 0; i < value.length(); i++) {\n" +
|
|
|
- " if(value.charAt(i) == (char)'.') {\n" +
|
|
|
- " splits.add(value.substring(nextWord, i));\n" +
|
|
|
- " nextWord = i+1;\n" +
|
|
|
- " }\n" +
|
|
|
- " }\n" +
|
|
|
- " if (nextWord != value.length()) {\n" +
|
|
|
- " splits.add(value.substring(nextWord, value.length()));\n" +
|
|
|
- " }\n" +
|
|
|
- " return splits;\n" +
|
|
|
- "}\n" +
|
|
|
- "List splitDomain(String domain) {\n" +
|
|
|
- " String dotDomain = replaceDots(domain);\n" +
|
|
|
- " return split(dotDomain);\n" +
|
|
|
- "}\n" +
|
|
|
- "boolean validateSyntax(List parts) {\n" +
|
|
|
- " int lastIndex = parts.length - 1;\n" +
|
|
|
- " /* Validate the last part specially, as it has different syntax rules. */\n" +
|
|
|
- " if (!validatePart(parts[lastIndex], true)) {\n" +
|
|
|
- " return false;\n" +
|
|
|
- " }\n" +
|
|
|
- " for (int i = 0; i < lastIndex; i++) {\n" +
|
|
|
- " String part = parts[i];\n" +
|
|
|
- " if (!validatePart(part, false)) {\n" +
|
|
|
- " return false;\n" +
|
|
|
- " }\n" +
|
|
|
- " }\n" +
|
|
|
- " return true;\n" +
|
|
|
- "}\n" +
|
|
|
- "boolean validatePart(String part, boolean isFinalPart) {\n" +
|
|
|
- " int MAX_DOMAIN_PART_LENGTH = 63;\n" +
|
|
|
- " if (part.length() < 1 || part.length() > MAX_DOMAIN_PART_LENGTH) {\n" +
|
|
|
- " return false;\n" +
|
|
|
- " }\n" +
|
|
|
- " int offset = 0;\n" +
|
|
|
- " int strLen = part.length();\n" +
|
|
|
- " while (offset < strLen) {\n" +
|
|
|
- " int curChar = part.charAt(offset);\n" +
|
|
|
- " offset += 1;\n" +
|
|
|
- " if (!(Character.isLetterOrDigit(curChar) || curChar == (char)'-' || curChar == (char)'_')) {\n" +
|
|
|
- " return false;\n" +
|
|
|
- " }\n" +
|
|
|
- " }\n" +
|
|
|
- " if (part.charAt(0) == (char)'-' || part.charAt(0) == (char)'_' ||\n" +
|
|
|
- " part.charAt(part.length() - 1) == (char)'-' || part.charAt(part.length() - 1) == (char)'_') {\n" +
|
|
|
- " return false;\n" +
|
|
|
- " }\n" +
|
|
|
- " if (isFinalPart && Character.isDigit(part.charAt(0))) {\n" +
|
|
|
- " return false;\n" +
|
|
|
- " }\n" +
|
|
|
- " return true;\n" +
|
|
|
- "}\n" +
|
|
|
- "int findPublicSuffix(Map params, List parts) {\n" +
|
|
|
- " int partsSize = parts.size();\n" +
|
|
|
- "\n" +
|
|
|
- " for (int i = 0; i < partsSize; i++) {\n" +
|
|
|
- " StringJoiner joiner = new StringJoiner('.');\n" +
|
|
|
- " for (String s : parts.subList(i, partsSize)) {\n" +
|
|
|
- " joiner.add(s);\n" +
|
|
|
- " }\n" +
|
|
|
- " /* parts.subList(i, partsSize).each(joiner::add); */\n" +
|
|
|
- " String ancestorName = joiner.toString();\n" +
|
|
|
- "\n" +
|
|
|
- " if (params['exact'].containsKey(ancestorName)) {\n" +
|
|
|
- " return i;\n" +
|
|
|
- " }\n" +
|
|
|
- "\n" +
|
|
|
- " /* Excluded domains (e.g. !nhs.uk) use the next highest\n" +
|
|
|
- " domain as the effective public suffix (e.g. uk). */\n" +
|
|
|
- "\n" +
|
|
|
- " if (params['excluded'].containsKey(ancestorName)) {\n" +
|
|
|
- " return i + 1;\n" +
|
|
|
- " }\n" +
|
|
|
- "\n" +
|
|
|
- " List pieces = split(ancestorName);\n" +
|
|
|
- " if (pieces.length >= 2 && params['under'].containsKey(pieces[1])) {\n" +
|
|
|
- " return i;\n" +
|
|
|
- " }\n" +
|
|
|
- " }\n" +
|
|
|
- "\n" +
|
|
|
- " return -1;\n" +
|
|
|
- "}\n" +
|
|
|
- "String ancestor(List parts, int levels) {\n" +
|
|
|
- " StringJoiner joiner = new StringJoiner('.');\n" +
|
|
|
- " for (String s : parts.subList(levels, parts.size())) {\n" +
|
|
|
- " joiner.add(s);\n" +
|
|
|
- " }\n" +
|
|
|
- " String name = joiner.toString();\n" +
|
|
|
- " if (name.endsWith('.')) {\n" +
|
|
|
- " name = name.substring(0, name.length() - 1);\n" +
|
|
|
- " }\n" +
|
|
|
- " return name;\n" +
|
|
|
- "}\n" +
|
|
|
- "String topPrivateDomain(String name, List parts, int publicSuffixIndex) {\n" +
|
|
|
- " if (publicSuffixIndex == 1) {\n" +
|
|
|
- " return name;\n" +
|
|
|
- " }\n" +
|
|
|
- " if (!(publicSuffixIndex > 0)) {\n" +
|
|
|
- " throw new IllegalArgumentException('Not under a public suffix: ' + name);\n" +
|
|
|
- " }\n" +
|
|
|
- " return ancestor(parts, publicSuffixIndex - 1);\n" +
|
|
|
- "}\n" +
|
|
|
- "List domainSplit(String host, Map params) {\n" +
|
|
|
- " int MAX_DNS_NAME_LENGTH = 253;\n" +
|
|
|
- " int MAX_LENGTH = 253;\n" +
|
|
|
- " int MAX_PARTS = 127;\n" +
|
|
|
- " if ('host'.isEmpty()) {\n" +
|
|
|
- " return ['',''];\n" +
|
|
|
- " }\n" +
|
|
|
- " host = host.trim();\n" +
|
|
|
- " if (host.contains(':')) {\n" +
|
|
|
- " return ['', host];\n" +
|
|
|
- " }\n" +
|
|
|
- " boolean tentativeIP = true;\n" +
|
|
|
- " for(int i = 0; i < host.length(); i++) {\n" +
|
|
|
- " if (!(Character.isDigit(host.charAt(i)) || host.charAt(i) == (char)'.')) {\n" +
|
|
|
- " tentativeIP = false;\n" +
|
|
|
- " break;\n" +
|
|
|
- " }\n" +
|
|
|
- " }\n" +
|
|
|
- " if (tentativeIP) {\n" +
|
|
|
- " /* special-snowflake rules now... */\n" +
|
|
|
- " if (host == '.') {\n" +
|
|
|
- " return ['',''];\n" +
|
|
|
- " }\n" +
|
|
|
- " return ['', host];\n" +
|
|
|
- " }\n" +
|
|
|
- " def normalizedHost = host;\n" +
|
|
|
- " normalizedHost = normalizedHost.toLowerCase();\n" +
|
|
|
- " List parts = splitDomain(normalizedHost);\n" +
|
|
|
- " int publicSuffixIndex = findPublicSuffix(params, parts);\n" +
|
|
|
- " if (publicSuffixIndex == 0) {\n" +
|
|
|
- " return ['', host];\n" +
|
|
|
- " }\n" +
|
|
|
- " String highestRegistered = '';\n" +
|
|
|
- " /* for the case where the host is internal like .local so is not a recognised public suffix */\n" +
|
|
|
- " if (publicSuffixIndex == -1) {\n" +
|
|
|
- " if (!parts.isEmpty()) {\n" +
|
|
|
- " if (parts.size() == 1) {\n" +
|
|
|
- " return ['', host];\n" +
|
|
|
- " }\n" +
|
|
|
- " if (parts.size() > 2) {\n" +
|
|
|
- " boolean allNumeric = true;\n" +
|
|
|
- " String value = parts.get(parts.size() - 1);\n" +
|
|
|
- " for (int i = 0; i < value.length(); i++) {\n" +
|
|
|
- " if (!Character.isDigit(value.charAt(i))) {\n" +
|
|
|
- " allNumeric = false;\n" +
|
|
|
- " break;\n" +
|
|
|
- " }\n" +
|
|
|
- " }\n" +
|
|
|
- " if (allNumeric) {\n" +
|
|
|
- " highestRegistered = parts.get(parts.size() - 2) + '.' + parts.get(parts.size() - 1);\n" +
|
|
|
- " } else {\n" +
|
|
|
- " highestRegistered = parts.get(parts.size() - 1);\n" +
|
|
|
- " }\n" +
|
|
|
- "\n" +
|
|
|
- " } else {\n" +
|
|
|
- " highestRegistered = parts.get(parts.size() - 1);\n" +
|
|
|
- " }\n" +
|
|
|
- " }\n" +
|
|
|
- " } else {\n" +
|
|
|
- " /* HRD is the top private domain */\n" +
|
|
|
- " highestRegistered = topPrivateDomain(normalizedHost, parts, publicSuffixIndex);\n" +
|
|
|
- " }\n" +
|
|
|
- " String subDomain = host.substring(0, host.length() - highestRegistered.length());\n" +
|
|
|
- " if (subDomain.endsWith('.')) {\n" +
|
|
|
- " subDomain = subDomain.substring(0, subDomain.length() - 1);\n" +
|
|
|
- " }\n" +
|
|
|
- " return [subDomain, highestRegistered];\n" +
|
|
|
- "}\n";
|
|
|
- fn = fn.replace("\n"," ");
|
|
|
- function = fn;
|
|
|
+ private static List<String> splitDomain(String domain) {
|
|
|
+ String dotDomain = replaceDots(domain);
|
|
|
+ return Arrays.asList(dotDomain.split("\\."));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int findPublicSuffix(List<String> parts) {
|
|
|
+ int partsSize = parts.size();
|
|
|
+ for (int i = 0; i < partsSize; i++) {
|
|
|
+ StringJoiner joiner = new StringJoiner(".");
|
|
|
+ for (String s : parts.subList(i, partsSize)) {
|
|
|
+ joiner.add(s);
|
|
|
+ }
|
|
|
+ /* parts.subList(i, partsSize).each(joiner::add); */
|
|
|
+ String ancestorName = joiner.toString();
|
|
|
+ if (exact.containsKey(ancestorName)) {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ /* Excluded domains (e.g. !nhs.uk) use the next highest
|
|
|
+ domain as the effective public suffix (e.g. uk). */
|
|
|
+ if (excluded.containsKey(ancestorName)) {
|
|
|
+ return i + 1;
|
|
|
+ }
|
|
|
+ String [] pieces = ancestorName.split("\\.");
|
|
|
+ if (pieces.length >= 2 && under.containsKey(pieces[1])) {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String ancestor(List<String> parts, int levels) {
|
|
|
+ StringJoiner joiner = new StringJoiner(".");
|
|
|
+ for (String s : parts.subList(levels, parts.size())) {
|
|
|
+ joiner.add(s);
|
|
|
+ }
|
|
|
+ String name = joiner.toString();
|
|
|
+ if (name.endsWith(".")) {
|
|
|
+ name = name.substring(0, name.length() - 1);
|
|
|
+ }
|
|
|
+ return name;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String topPrivateDomain(String name, List<String> parts, int publicSuffixIndex) {
|
|
|
+ if (publicSuffixIndex == 1) {
|
|
|
+ return name;
|
|
|
+ }
|
|
|
+ if (!(publicSuffixIndex > 0)) {
|
|
|
+ throw new IllegalArgumentException("Not under a public suffix: " + name);
|
|
|
+ }
|
|
|
+ return ancestor(parts, publicSuffixIndex - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static List<String> domainSplit(String host, Map<String, Object> params) {
|
|
|
+ // NOTE: we don't check SpecialPermission because this will be called (indirectly) from scripts
|
|
|
+ AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
|
|
+ DEPRECATION_LOGGER.deprecatedAndMaybeLog("domainSplit",
|
|
|
+ "Method [domainSplit] taking params is deprecated. Remove the params argument.");
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ return domainSplit(host);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Split {@code host} into sub domain and highest registered domain.
|
|
|
+ * The result is a list containing exactly 2 items the first is the sub domain
|
|
|
+ * and the second the highest registered domain.
|
|
|
+ *
|
|
|
+ * @param host The hostname to split
|
|
|
+ * @return The sub domain and highest registered domain
|
|
|
+ */
|
|
|
+ public static List<String> domainSplit(String host) {
|
|
|
+ host = host.trim();
|
|
|
+ if (host.contains(":")) {
|
|
|
+ return Arrays.asList("", host);
|
|
|
+ }
|
|
|
+ boolean tentativeIP = true;
|
|
|
+ for(int i = 0; i < host.length(); i++) {
|
|
|
+ if (!(Character.isDigit(host.charAt(i)) || host.charAt(i) == '.')) {
|
|
|
+ tentativeIP = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (tentativeIP) {
|
|
|
+ /* special-snowflake rules now... */
|
|
|
+ if (host.equals(".")) {
|
|
|
+ return Arrays.asList("","");
|
|
|
+ }
|
|
|
+ return Arrays.asList("", host);
|
|
|
+ }
|
|
|
+ String normalizedHost = host;
|
|
|
+ normalizedHost = normalizedHost.toLowerCase(Locale.ROOT);
|
|
|
+ List<String> parts = splitDomain(normalizedHost);
|
|
|
+ int publicSuffixIndex = findPublicSuffix(parts);
|
|
|
+ if (publicSuffixIndex == 0) {
|
|
|
+ return Arrays.asList("", host);
|
|
|
+ }
|
|
|
+ String highestRegistered = "";
|
|
|
+ /* for the case where the host is internal like .local so is not a recognised public suffix */
|
|
|
+ if (publicSuffixIndex == -1) {
|
|
|
+ if (!parts.isEmpty()) {
|
|
|
+ if (parts.size() == 1) {
|
|
|
+ return Arrays.asList("", host);
|
|
|
+ }
|
|
|
+ if (parts.size() > 2) {
|
|
|
+ boolean allNumeric = true;
|
|
|
+ String value = parts.get(parts.size() - 1);
|
|
|
+ for (int i = 0; i < value.length(); i++) {
|
|
|
+ if (!Character.isDigit(value.charAt(i))) {
|
|
|
+ allNumeric = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (allNumeric) {
|
|
|
+ highestRegistered = parts.get(parts.size() - 2) + '.' + parts.get(parts.size() - 1);
|
|
|
+ } else {
|
|
|
+ highestRegistered = parts.get(parts.size() - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ highestRegistered = parts.get(parts.size() - 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* HRD is the top private domain */
|
|
|
+ highestRegistered = topPrivateDomain(normalizedHost, parts, publicSuffixIndex);
|
|
|
+ }
|
|
|
+ String subDomain = host.substring(0, host.length() - highestRegistered.length());
|
|
|
+ if (subDomain.endsWith(".")) {
|
|
|
+ subDomain = subDomain.substring(0, subDomain.length() - 1);
|
|
|
+ }
|
|
|
+ return Arrays.asList(subDomain, highestRegistered);
|
|
|
}
|
|
|
}
|