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 }