1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package sun.net.spi; 28 29 import java.net.InetSocketAddress; 30 import java.net.Proxy; 31 import java.net.ProxySelector; 32 import java.net.SocketAddress; 33 import java.net.URI; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.io.IOException; 37 import java.security.AccessController; 38 import java.security.PrivilegedAction; 39 import java.util.StringJoiner; 40 import java.util.regex.Pattern; 41 import sun.net.NetProperties; 42 import sun.net.SocksProxy; 43 import static java.util.regex.Pattern.quote; 44 45 /** 46 * Supports proxy settings using system properties This proxy selector 47 * provides backward compatibility with the old http protocol handler 48 * as far as how proxy is set 49 * 50 * Most of the implementation copied from the old http protocol handler 51 * 52 * Supports http/https/ftp.proxyHost, http/https/ftp.proxyPort, 53 * proxyHost, proxyPort, and http/https/ftp.nonProxyHost, and socks. 54 * NOTE: need to do gopher as well 55 */ 56 public class DefaultProxySelector extends ProxySelector { 57 58 /** 59 * This is where we define all the valid System Properties we have to 60 * support for each given protocol. 61 * The format of this 2 dimensional array is : 62 * - 1 row per protocol (http, ftp, ...) 63 * - 1st element of each row is the protocol name 64 * - subsequent elements are prefixes for Host & Port properties 65 * listed in order of priority. 66 * Example: 67 * {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"}, 68 * means for FTP we try in that oder: 69 * + ftp.proxyHost & ftp.proxyPort 70 * + ftpProxyHost & ftpProxyPort 71 * + proxyHost & proxyPort 72 * + socksProxyHost & socksProxyPort 73 * 74 * Note that the socksProxy should *always* be the last on the list 75 */ 76 final static String[][] props = { 77 /* 78 * protocol, Property prefix 1, Property prefix 2, ... 79 */ 80 {"http", "http.proxy", "proxy", "socksProxy"}, 81 {"https", "https.proxy", "proxy", "socksProxy"}, 82 {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"}, 83 {"gopher", "gopherProxy", "socksProxy"}, 84 {"socket", "socksProxy"} 85 }; 86 87 private static final String SOCKS_PROXY_VERSION = "socksProxyVersion"; 88 89 private static boolean hasSystemProxies = false; 90 91 // Android-removed: Nonfunctional init logic: "net" library does not exist on Android. 92 /* 93 static { 94 final String key = "java.net.useSystemProxies"; 95 Boolean b = AccessController.doPrivileged( 96 new PrivilegedAction<Boolean>() { 97 public Boolean run() { 98 return NetProperties.getBoolean(key); 99 }}); 100 if (b != null && b.booleanValue()) { 101 java.security.AccessController.doPrivileged( 102 new java.security.PrivilegedAction<Void>() { 103 public Void run() { 104 System.loadLibrary("net"); 105 return null; 106 } 107 }); 108 hasSystemProxies = init(); 109 } 110 } 111 */ 112 113 /** 114 * How to deal with "non proxy hosts": 115 * since we do have to generate a pattern we don't want to do that if 116 * it's not necessary. Therefore we do cache the result, on a per-protocol 117 * basis, and change it only when the "source", i.e. the system property, 118 * did change. 119 */ 120 // Android-note: Integrated some upstream changes from beyond OpenJDK8u121-b13. 121 // This includes NonProxyInfo.pattern -> hostsPool and associated changes. 122 // See http://b/62368386 123 static class NonProxyInfo { 124 // Default value for nonProxyHosts, this provides backward compatibility 125 // by excluding localhost and its litteral notations. 126 static final String defStringVal = "localhost|127.*|[::1]|0.0.0.0|[::0]"; 127 128 String hostsSource; 129 Pattern pattern; 130 final String property; 131 final String defaultVal; 132 static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null, defStringVal); 133 static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null, defStringVal); 134 static NonProxyInfo socksNonProxyInfo = new NonProxyInfo("socksNonProxyHosts", null, null, defStringVal); 135 // Android-changed: Different NonProxyInfo flags for https hosts vs. http. 136 static NonProxyInfo httpsNonProxyInfo = new NonProxyInfo("https.nonProxyHosts", null, null, defStringVal); 137 NonProxyInfo(String p, String s, Pattern pattern, String d)138 NonProxyInfo(String p, String s, Pattern pattern, String d) { 139 property = p; 140 hostsSource = s; 141 this.pattern = pattern; 142 defaultVal = d; 143 } 144 } 145 146 147 /** 148 * select() method. Where all the hard work is done. 149 * Build a list of proxies depending on URI. 150 * Since we're only providing compatibility with the system properties 151 * from previous releases (see list above), that list will always 152 * contain 1 single proxy, default being NO_PROXY. 153 */ select(URI uri)154 public java.util.List<Proxy> select(URI uri) { 155 if (uri == null) { 156 throw new IllegalArgumentException("URI can't be null."); 157 } 158 String protocol = uri.getScheme(); 159 String host = uri.getHost(); 160 161 if (host == null) { 162 // This is a hack to ensure backward compatibility in two 163 // cases: 1. hostnames contain non-ascii characters, 164 // internationalized domain names. in which case, URI will 165 // return null, see BugID 4957669; 2. Some hostnames can 166 // contain '_' chars even though it's not supposed to be 167 // legal, in which case URI will return null for getHost, 168 // but not for getAuthority() See BugID 4913253 169 String auth = uri.getAuthority(); 170 if (auth != null) { 171 int i; 172 i = auth.indexOf('@'); 173 if (i >= 0) { 174 auth = auth.substring(i+1); 175 } 176 i = auth.lastIndexOf(':'); 177 if (i >= 0) { 178 auth = auth.substring(0,i); 179 } 180 host = auth; 181 } 182 } 183 184 if (protocol == null || host == null) { 185 throw new IllegalArgumentException("protocol = "+protocol+" host = "+host); 186 } 187 List<Proxy> proxyl = new ArrayList<Proxy>(1); 188 189 NonProxyInfo pinfo = null; 190 191 if ("http".equalsIgnoreCase(protocol)) { 192 pinfo = NonProxyInfo.httpNonProxyInfo; 193 } else if ("https".equalsIgnoreCase(protocol)) { 194 // HTTPS uses the same property as HTTP, for backward 195 // compatibility 196 // Android-changed: Different NonProxyInfo flags for https hosts vs. http. 197 // pinfo = NonProxyInfo.httpNonProxyInfo; 198 pinfo = NonProxyInfo.httpsNonProxyInfo; 199 } else if ("ftp".equalsIgnoreCase(protocol)) { 200 pinfo = NonProxyInfo.ftpNonProxyInfo; 201 } else if ("socket".equalsIgnoreCase(protocol)) { 202 pinfo = NonProxyInfo.socksNonProxyInfo; 203 } 204 205 /** 206 * Let's check the System properties for that protocol 207 */ 208 final String proto = protocol; 209 final NonProxyInfo nprop = pinfo; 210 final String urlhost = host.toLowerCase(); 211 212 /** 213 * This is one big doPrivileged call, but we're trying to optimize 214 * the code as much as possible. Since we're checking quite a few 215 * System properties it does help having only 1 call to doPrivileged. 216 * Be mindful what you do in here though! 217 */ 218 Proxy p = AccessController.doPrivileged( 219 new PrivilegedAction<Proxy>() { 220 public Proxy run() { 221 int i, j; 222 String phost = null; 223 int pport = 0; 224 String nphosts = null; 225 InetSocketAddress saddr = null; 226 227 // Then let's walk the list of protocols in our array 228 for (i=0; i<props.length; i++) { 229 if (props[i][0].equalsIgnoreCase(proto)) { 230 for (j = 1; j < props[i].length; j++) { 231 /* System.getProp() will give us an empty 232 * String, "" for a defined but "empty" 233 * property. 234 */ 235 phost = NetProperties.get(props[i][j]+"Host"); 236 if (phost != null && phost.length() != 0) 237 break; 238 } 239 if (phost == null || phost.length() == 0) { 240 /** 241 * No system property defined for that 242 * protocol. Let's check System Proxy 243 * settings (Gnome & Windows) if we were 244 * instructed to. 245 */ 246 // Android-removed: Dead code, hasSystemProxies is always false. 247 /* 248 if (hasSystemProxies) { 249 String sproto; 250 if (proto.equalsIgnoreCase("socket")) 251 sproto = "socks"; 252 else 253 sproto = proto; 254 Proxy sproxy = getSystemProxy(sproto, urlhost); 255 if (sproxy != null) { 256 return sproxy; 257 } 258 } 259 */ 260 return Proxy.NO_PROXY; 261 } 262 // If a Proxy Host is defined for that protocol 263 // Let's get the NonProxyHosts property 264 if (nprop != null) { 265 nphosts = NetProperties.get(nprop.property); 266 synchronized (nprop) { 267 if (nphosts == null) { 268 if (nprop.defaultVal != null) { 269 nphosts = nprop.defaultVal; 270 } else { 271 nprop.hostsSource = null; 272 nprop.pattern = null; 273 } 274 } else if (nphosts.length() != 0) { 275 // add the required default patterns 276 // but only if property no set. If it 277 // is empty, leave empty. 278 nphosts += "|" + NonProxyInfo 279 .defStringVal; 280 } 281 if (nphosts != null) { 282 if (!nphosts.equals(nprop.hostsSource)) { 283 nprop.pattern = toPattern(nphosts); 284 nprop.hostsSource = nphosts; 285 } 286 } 287 if (shouldNotUseProxyFor(nprop.pattern, urlhost)) { 288 return Proxy.NO_PROXY; 289 } 290 } 291 } 292 // We got a host, let's check for port 293 294 pport = NetProperties.getInteger(props[i][j]+"Port", 0).intValue(); 295 if (pport == 0 && j < (props[i].length - 1)) { 296 // Can't find a port with same prefix as Host 297 // AND it's not a SOCKS proxy 298 // Let's try the other prefixes for that proto 299 for (int k = 1; k < (props[i].length - 1); k++) { 300 if ((k != j) && (pport == 0)) 301 pport = NetProperties.getInteger(props[i][k]+"Port", 0).intValue(); 302 } 303 } 304 305 // Still couldn't find a port, let's use default 306 if (pport == 0) { 307 if (j == (props[i].length - 1)) // SOCKS 308 pport = defaultPort("socket"); 309 else 310 pport = defaultPort(proto); 311 } 312 // We did find a proxy definition. 313 // Let's create the address, but don't resolve it 314 // as this will be done at connection time 315 saddr = InetSocketAddress.createUnresolved(phost, pport); 316 // Socks is *always* the last on the list. 317 if (j == (props[i].length - 1)) { 318 int version = NetProperties.getInteger(SOCKS_PROXY_VERSION, 5).intValue(); 319 return SocksProxy.create(saddr, version); 320 } else { 321 return new Proxy(Proxy.Type.HTTP, saddr); 322 } 323 } 324 } 325 return Proxy.NO_PROXY; 326 }}); 327 328 proxyl.add(p); 329 330 /* 331 * If no specific property was set for that URI, we should be 332 * returning an iterator to an empty List. 333 */ 334 return proxyl; 335 } 336 connectFailed(URI uri, SocketAddress sa, IOException ioe)337 public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { 338 if (uri == null || sa == null || ioe == null) { 339 throw new IllegalArgumentException("Arguments can't be null."); 340 } 341 // ignored 342 } 343 344 defaultPort(String protocol)345 private int defaultPort(String protocol) { 346 if ("http".equalsIgnoreCase(protocol)) { 347 return 80; 348 } else if ("https".equalsIgnoreCase(protocol)) { 349 return 443; 350 } else if ("ftp".equalsIgnoreCase(protocol)) { 351 return 80; 352 } else if ("socket".equalsIgnoreCase(protocol)) { 353 return 1080; 354 } else if ("gopher".equalsIgnoreCase(protocol)) { 355 return 80; 356 } else { 357 return -1; 358 } 359 } 360 361 // Android-removed: Native logic not available/used on Android. 362 /* 363 private native static boolean init(); 364 private synchronized native Proxy getSystemProxy(String protocol, String host); 365 */ 366 367 /** 368 * @return {@code true} if given this pattern for non-proxy hosts and this 369 * urlhost the proxy should NOT be used to access this urlhost 370 */ shouldNotUseProxyFor(Pattern pattern, String urlhost)371 static boolean shouldNotUseProxyFor(Pattern pattern, String urlhost) { 372 if (pattern == null || urlhost.isEmpty()) 373 return false; 374 boolean matches = pattern.matcher(urlhost).matches(); 375 return matches; 376 } 377 378 /** 379 * @param mask non-null mask 380 * @return {@link java.util.regex.Pattern} corresponding to this mask 381 * or {@code null} in case mask should not match anything 382 */ toPattern(String mask)383 static Pattern toPattern(String mask) { 384 boolean disjunctionEmpty = true; 385 StringJoiner joiner = new StringJoiner("|"); 386 for (String disjunct : mask.split("\\|")) { 387 if (disjunct.isEmpty()) 388 continue; 389 disjunctionEmpty = false; 390 String regex = disjunctToRegex(disjunct.toLowerCase()); 391 joiner.add(regex); 392 } 393 return disjunctionEmpty ? null : Pattern.compile(joiner.toString()); 394 } 395 396 /** 397 * @param disjunct non-null mask disjunct 398 * @return java regex string corresponding to this mask 399 */ disjunctToRegex(String disjunct)400 static String disjunctToRegex(String disjunct) { 401 String regex; 402 if (disjunct.startsWith("*")) { 403 regex = ".*" + quote(disjunct.substring(1)); 404 } else if (disjunct.endsWith("*")) { 405 regex = quote(disjunct.substring(0, disjunct.length() - 1)) + ".*"; 406 } else { 407 regex = quote(disjunct); 408 } 409 return regex; 410 } 411 } 412