2929import com .sun .javafx .logging .PlatformLogger .Level ;
3030
3131import java .io .BufferedReader ;
32+ import java .io .File ;
33+ import java .io .FileInputStream ;
34+ import java .io .FileNotFoundException ;
3235import java .io .IOException ;
3336import java .io .InputStream ;
3437import java .io .InputStreamReader ;
3538import java .net .IDN ;
36- import java .util .Collections ;
37- import java .util .LinkedHashMap ;
39+ import java .security .AccessController ;
40+ import java .security .PrivilegedAction ;
41+ import java .util .HashMap ;
3842import java .util .Map ;
43+ import java .util .concurrent .ConcurrentHashMap ;
44+ import java .util .zip .ZipEntry ;
45+ import java .util .zip .ZipInputStream ;
3946
4047/**
4148 * A collection of static utility methods dealing with "public suffixes".
@@ -57,10 +64,30 @@ private enum Rule {
5764
5865
5966 /**
60- * The mapping from domain names to public suffix list rules.
67+ * The mapping from top-level domain names to public suffix list rules.
6168 */
62- private static final Map <String ,Rule > RULES =
63- loadRules ("effective_tld_names.dat" );
69+ private static final Map <String , Rules > rulesCache = new ConcurrentHashMap <>();
70+
71+
72+ /**
73+ * The public suffix list file.
74+ */
75+ private static final File pslFile = AccessController .doPrivileged ((PrivilegedAction <File >)
76+ () -> new File (System .getProperty ("java.home" ), "lib/security/public_suffix_list.dat" ));
77+
78+
79+ /*
80+ * Determines whether the public suffix list file is available.
81+ */
82+ private static final boolean pslFileExists = AccessController .doPrivileged (
83+ (PrivilegedAction <Boolean >) () -> {
84+ if (!pslFile .exists ()) {
85+ logger .warning ("Resource not found: " +
86+ "lib/security/public_suffix_list.dat" );
87+ return false ;
88+ }
89+ return true ;
90+ });
6491
6592
6693 /**
@@ -71,101 +98,132 @@ private PublicSuffixes() {
7198 }
7299
73100
101+ /**
102+ * Returns whether the public suffix list file is available.
103+ */
104+ static boolean pslFileExists () {
105+ return pslFileExists ;
106+ }
107+
108+
74109 /**
75110 * Determines if a domain is a public suffix.
76111 */
77112 static boolean isPublicSuffix (String domain ) {
78113 if (domain .length () == 0 ) {
79114 return false ;
80115 }
81- Rule rule = RULES . get ( domain );
82- if (rule == Rule . EXCEPTION_RULE ) {
116+
117+ if (! pslFileExists () ) {
83118 return false ;
84- } else if (rule == Rule .SIMPLE_RULE || rule == Rule .WILDCARD_RULE ) {
85- return true ;
86- } else {
87- int pos = domain .indexOf ('.' ) + 1 ;
88- if (pos == 0 ) {
89- pos = domain .length ();
90- }
91- String parent = domain .substring (pos );
92- return RULES .get (parent ) == Rule .WILDCARD_RULE ;
93119 }
120+
121+ Rules rules = Rules .getRules (domain );
122+ return rules == null ? false : rules .match (domain );
94123 }
95124
96- /**
97- * Loads the public suffix list from a given resource.
98- */
99- private static Map <String ,Rule > loadRules (String resourceName ) {
100- logger .finest ("resourceName: [{0}]" , resourceName );
101- Map <String ,Rule > result = null ;
102-
103- InputStream is = PublicSuffixes .class .getResourceAsStream (resourceName );
104- if (is != null ) {
105- BufferedReader reader = null ;
106- try {
107- reader = new BufferedReader (new InputStreamReader (is , "UTF-8" ));
108- result = loadRules (reader );
109- } catch (IOException ex ) {
110- logger .warning ("Unexpected error" , ex );
111- } finally {
125+ private static class Rules {
126+
127+ private final Map <String , Rule > rules = new HashMap <>();
128+
129+ private Rules (InputStream is ) throws IOException {
130+ InputStreamReader isr = new InputStreamReader (is , "UTF-8" );
131+ BufferedReader reader = new BufferedReader (isr );
132+
133+ String line ;
134+ int type = reader .read ();
135+ while (type != -1 && (line = reader .readLine ()) != null ) {
136+ Rule rule ;
137+ if (line .startsWith ("!" )) {
138+ line = line .substring (1 );
139+ rule = Rule .EXCEPTION_RULE ;
140+ } else if (line .startsWith ("*." )) {
141+ line = line .substring (2 );
142+ rule = Rule .WILDCARD_RULE ;
143+ } else {
144+ rule = Rule .SIMPLE_RULE ;
145+ }
112146 try {
113- if (reader != null ) {
114- reader .close ();
115- }
116- } catch (IOException ex ) {
117- logger .warning ("Unexpected error" , ex );
147+ line = IDN .toASCII (line , IDN .ALLOW_UNASSIGNED );
148+ } catch (Exception ex ) {
149+ logger .warning (String .format ("Error parsing rule: [%s]" , line ), ex );
150+ continue ;
118151 }
152+ rules .put (line , rule );
153+ type = reader .read ();
154+ }
155+ if (logger .isLoggable (Level .FINEST )) {
156+ logger .finest ("rules: {0}" , toLogString (rules ));
119157 }
120- } else {
121- logger .warning ("Resource not found: [{0}]" ,
122- resourceName );
123158 }
124159
125- result = result != null
126- ? Collections .unmodifiableMap (result )
127- : Collections .<String ,Rule >emptyMap ();
128- if (logger .isLoggable (Level .FINEST )) {
129- logger .finest ("result: {0}" , toLogString (result ));
160+ static Rules getRules (String domain ) {
161+ String tld = getTopLevelDomain (domain );
162+ if (tld .isEmpty ()) {
163+ return null ;
164+ }
165+ return rulesCache .computeIfAbsent (tld , k -> createRules (tld ));
130166 }
131- return result ;
132- }
133167
134- /**
135- * Loads the public suffix list from a given reader.
136- */
137- private static Map <String ,Rule > loadRules (BufferedReader reader )
138- throws IOException
139- {
140- Map <String ,Rule > result = new LinkedHashMap <String , Rule >();
141- String line ;
142- while ((line = reader .readLine ()) != null ) {
143- line = line .split ("\\ s+" , 2 )[0 ];
144- if (line .length () == 0 ) {
145- continue ;
168+ private static String getTopLevelDomain (String domain ) {
169+ domain = IDN .toUnicode (domain , IDN .ALLOW_UNASSIGNED );
170+ int n = domain .lastIndexOf ('.' );
171+ if (n == -1 ) {
172+ return domain ;
146173 }
147- if (line .startsWith ("//" )) {
148- continue ;
174+ return domain .substring (n + 1 );
175+ }
176+
177+ private static Rules createRules (String tld ) {
178+ try (InputStream pubSuffixStream = getPubSuffixStream ()) {
179+ if (pubSuffixStream == null ) {
180+ return null ;
181+ }
182+ ZipInputStream zis = new ZipInputStream (pubSuffixStream );
183+ ZipEntry ze = zis .getNextEntry ();
184+ while (ze != null ) {
185+ if (ze .getName ().equals (tld )) {
186+ return new Rules (zis );
187+ } else {
188+ ze = zis .getNextEntry ();
189+ }
190+ }
191+ } catch (IOException ex ) {
192+ logger .warning ("Unexpected error" , ex );
149193 }
150- Rule rule ;
151- if (line .startsWith ("!" )) {
152- line = line .substring (1 );
153- rule = Rule .EXCEPTION_RULE ;
154- } else if (line .startsWith ("*." )) {
155- line = line .substring (2 );
156- rule = Rule .WILDCARD_RULE ;
194+ return null ;
195+ }
196+
197+ private static InputStream getPubSuffixStream () {
198+ InputStream is = AccessController .doPrivileged (
199+ (PrivilegedAction <InputStream >) () -> {
200+ try {
201+ return new FileInputStream (pslFile );
202+ } catch (FileNotFoundException ex ) {
203+ logger .warning ("Resource not found: " +
204+ "lib/security/public_suffix_list.dat" );
205+ return null ;
206+ }
207+ }
208+ );
209+ return is ;
210+ }
211+
212+ boolean match (String domain ) {
213+ Rule rule = rules .get (domain );
214+ if (rule == Rule .EXCEPTION_RULE ) {
215+ return false ;
216+ } else if (rule == Rule .SIMPLE_RULE || rule == Rule .WILDCARD_RULE ) {
217+ return true ;
157218 } else {
158- rule = Rule .SIMPLE_RULE ;
159- }
160- try {
161- line = IDN .toASCII (line , IDN .ALLOW_UNASSIGNED );
162- } catch (Exception ex ) {
163- logger .warning (String .format ("Error parsing rule: [%s]" , line ), ex );
164- continue ;
219+ int pos = domain .indexOf ('.' ) + 1 ;
220+ if (pos == 0 ) {
221+ pos = domain .length ();
222+ }
223+ String parent = domain .substring (pos );
224+ return rules .get (parent ) == Rule .WILDCARD_RULE ;
165225 }
166- result .put (line , rule );
167226 }
168- return result ;
169227 }
170228
171229 /**
0 commit comments