1 /* 2 * Copyright (c) 2011, 2014, 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 com.sun.webkit.network; 27 28 import java.io.BufferedReader; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.InputStreamReader; 32 import java.net.IDN; 33 import java.util.Collections; 34 import java.util.LinkedHashMap; 35 import java.util.Map; 36 import java.util.logging.Level; 37 import java.util.logging.Logger; 38 39 /** 40 * A collection of static utility methods dealing with "public suffixes". 41 */ 42 final class PublicSuffixes { 43 44 private static final Logger logger = 45 Logger.getLogger(PublicSuffixes.class.getName()); 46 47 48 /** 49 * Public suffix list rule types. 50 */ 51 private enum Rule { 52 SIMPLE_RULE, 53 WILDCARD_RULE, 54 EXCEPTION_RULE, 55 } 56 57 58 /** 59 * The mapping from domain names to public suffix list rules. 60 */ 61 private static final Map<String,Rule> RULES = 62 loadRules("effective_tld_names.dat"); 63 64 65 /** 66 * The private default constructor. Ensures non-instantiability. 67 */ 68 private PublicSuffixes() { 69 throw new AssertionError(); 70 } 71 72 73 /** 74 * Determines if a domain is a public suffix. 75 */ 76 static boolean isPublicSuffix(String domain) { 77 if (domain.length() == 0) { 78 return false; 79 } 80 Rule rule = RULES.get(domain); 81 if (rule == Rule.EXCEPTION_RULE) { 82 return false; 83 } else if (rule == Rule.SIMPLE_RULE || rule == Rule.WILDCARD_RULE) { 84 return true; 85 } else { 86 int pos = domain.indexOf('.') + 1; 87 if (pos == 0) { 88 pos = domain.length(); 89 } 90 String parent = domain.substring(pos); 91 return RULES.get(parent) == Rule.WILDCARD_RULE; 92 } 93 } 94 95 /** 96 * Loads the public suffix list from a given resource. 97 */ 98 private static Map<String,Rule> loadRules(String resourceName) { 99 logger.log(Level.FINEST, "resourceName: [{0}]", resourceName); 100 Map<String,Rule> result = null; 101 102 InputStream is = PublicSuffixes.class.getResourceAsStream(resourceName); 103 if (is != null) { 104 BufferedReader reader = null; 105 try { 106 reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); 107 result = loadRules(reader); 108 } catch (IOException ex) { 109 logger.log(Level.WARNING, "Unexpected error", ex); 110 } finally { 111 try { 112 if (reader != null) { 113 reader.close(); 114 } 115 } catch (IOException ex) { 116 logger.log(Level.WARNING, "Unexpected error", ex); 117 } 118 } 119 } else { 120 logger.log(Level.WARNING, "Resource not found: [{0}]", 121 resourceName); 122 } 123 124 result = result != null 125 ? Collections.unmodifiableMap(result) 126 : Collections.<String,Rule>emptyMap(); 127 if (logger.isLoggable(Level.FINEST)) { 128 logger.log(Level.FINEST, "result: {0}", toLogString(result)); 129 } 130 return result; 131 } 132 133 /** 134 * Loads the public suffix list from a given reader. 135 */ 136 private static Map<String,Rule> loadRules(BufferedReader reader) 137 throws IOException 138 { 139 Map<String,Rule> result = new LinkedHashMap<String, Rule>(); 140 String line; 141 while ((line = reader.readLine()) != null) { 142 line = line.split("\\s+", 2)[0]; 143 if (line.length() == 0) { 144 continue; 145 } 146 if (line.startsWith("//")) { 147 continue; 148 } 149 Rule rule; 150 if (line.startsWith("!")) { 151 line = line.substring(1); 152 rule = Rule.EXCEPTION_RULE; 153 } else if (line.startsWith("*.")) { 154 line = line.substring(2); 155 rule = Rule.WILDCARD_RULE; 156 } else { 157 rule = Rule.SIMPLE_RULE; 158 } 159 try { 160 line = IDN.toASCII(line, IDN.ALLOW_UNASSIGNED); 161 } catch (Exception ex) { 162 logger.log(Level.WARNING, String.format( 163 "Error parsing rule: [%s]", line), ex); 164 continue; 165 } 166 result.put(line, rule); 167 } 168 return result; 169 } 170 171 /** 172 * Converts a map of rules to a string suitable for displaying 173 * in the log. 174 */ 175 private static String toLogString(Map<String,Rule> rules) { 176 if (rules.isEmpty()) { 177 return "{}"; 178 } 179 StringBuilder sb = new StringBuilder(); 180 for (Map.Entry<String,Rule> entry : rules.entrySet()) { 181 sb.append(String.format("%n ")); 182 sb.append(entry.getKey()); 183 sb.append(": "); 184 sb.append(entry.getValue()); 185 } 186 return sb.toString(); 187 } 188 }