1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2003, 2011, 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.util.StringTokenizer; 37 import java.io.IOException; 38 import sun.misc.RegexpPool; 39 import java.security.AccessController; 40 import java.security.PrivilegedAction; 41 import sun.net.NetProperties; 42 import sun.net.SocksProxy; 43 44 /** 45 * Supports proxy settings using system properties This proxy selector 46 * provides backward compatibility with the old http protocol handler 47 * as far as how proxy is set 48 * 49 * Most of the implementation copied from the old http protocol handler 50 * 51 * Supports http/https/ftp.proxyHost, http/https/ftp.proxyPort, 52 * proxyHost, proxyPort, and http/https/ftp.nonProxyHost, and socks. 53 * NOTE: need to do gopher as well 54 */ 55 public class DefaultProxySelector extends ProxySelector { 56 57 /** 58 * This is where we define all the valid System Properties we have to 59 * support for each given protocol. 60 * The format of this 2 dimensional array is : 61 * - 1 row per protocol (http, ftp, ...) 62 * - 1st element of each row is the protocol name 63 * - subsequent elements are prefixes for Host & Port properties 64 * listed in order of priority. 65 * Example: 66 * {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"}, 67 * means for FTP we try in that oder: 68 * + ftp.proxyHost & ftp.proxyPort 69 * + ftpProxyHost & ftpProxyPort 70 * + proxyHost & proxyPort 71 * + socksProxyHost & socksProxyPort 72 * 73 * Note that the socksProxy should *always* be the last on the list 74 */ 75 final static String[][] props = { 76 /* 77 * protocol, Property prefix 1, Property prefix 2, ... 78 */ 79 {"http", "http.proxy", "proxy", "socksProxy"}, 80 {"https", "https.proxy", "proxy", "socksProxy"}, 81 {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"}, 82 {"gopher", "gopherProxy", "socksProxy"}, 83 {"socket", "socksProxy"} 84 }; 85 86 private static final String SOCKS_PROXY_VERSION = "socksProxyVersion"; 87 88 private static boolean hasSystemProxies = false; 89 90 /** 91 * How to deal with "non proxy hosts": 92 * since we do have to generate a RegexpPool we don't want to do that if 93 * it's not necessary. Therefore we do cache the result, on a per-protocol 94 * basis, and change it only when the "source", i.e. the system property, 95 * did change. 96 */ 97 98 static class NonProxyInfo { 99 // Default value for nonProxyHosts, this provides backward compatibility 100 // by excluding localhost and its litteral notations. 101 static final String defStringVal = "localhost|127.*|[::1]|0.0.0.0|[::0]"; 102 103 String hostsSource; 104 RegexpPool hostsPool; 105 final String property; 106 final String defaultVal; 107 static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null, defStringVal); 108 static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null, defStringVal); 109 static NonProxyInfo httpsNonProxyInfo = new NonProxyInfo("https.nonProxyHosts", null, null, defStringVal); 110 NonProxyInfo(String p, String s, RegexpPool pool, String d)111 NonProxyInfo(String p, String s, RegexpPool pool, String d) { 112 property = p; 113 hostsSource = s; 114 hostsPool = pool; 115 defaultVal = d; 116 } 117 } 118 119 120 /** 121 * select() method. Where all the hard work is done. 122 * Build a list of proxies depending on URI. 123 * Since we're only providing compatibility with the system properties 124 * from previous releases (see list above), that list will always 125 * contain 1 single proxy, default being NO_PROXY. 126 */ select(URI uri)127 public java.util.List<Proxy> select(URI uri) { 128 if (uri == null) { 129 throw new IllegalArgumentException("URI can't be null."); 130 } 131 String protocol = uri.getScheme(); 132 String host = uri.getHost(); 133 134 if (host == null) { 135 // This is a hack to ensure backward compatibility in two 136 // cases: 1. hostnames contain non-ascii characters, 137 // internationalized domain names. in which case, URI will 138 // return null, see BugID 4957669; 2. Some hostnames can 139 // contain '_' chars even though it's not supposed to be 140 // legal, in which case URI will return null for getHost, 141 // but not for getAuthority() See BugID 4913253 142 String auth = uri.getAuthority(); 143 if (auth != null) { 144 int i; 145 i = auth.indexOf('@'); 146 if (i >= 0) { 147 auth = auth.substring(i+1); 148 } 149 i = auth.lastIndexOf(':'); 150 if (i >= 0) { 151 auth = auth.substring(0,i); 152 } 153 host = auth; 154 } 155 } 156 157 if (protocol == null || host == null) { 158 throw new IllegalArgumentException("protocol = "+protocol+" host = "+host); 159 } 160 List<Proxy> proxyl = new ArrayList<Proxy>(1); 161 162 NonProxyInfo pinfo = null; 163 164 if ("http".equalsIgnoreCase(protocol)) { 165 pinfo = NonProxyInfo.httpNonProxyInfo; 166 } else if ("https".equalsIgnoreCase(protocol)) { 167 // HTTPS uses the same property as HTTP, for backward 168 // compatibility 169 // 170 // Android changed : Allow a different set of flags for https hosts. 171 pinfo = NonProxyInfo.httpsNonProxyInfo; 172 } else if ("ftp".equalsIgnoreCase(protocol)) { 173 pinfo = NonProxyInfo.ftpNonProxyInfo; 174 } 175 176 /** 177 * Let's check the System properties for that protocol 178 */ 179 final String proto = protocol; 180 final NonProxyInfo nprop = pinfo; 181 final String urlhost = host.toLowerCase(); 182 183 /** 184 * This is one big doPrivileged call, but we're trying to optimize 185 * the code as much as possible. Since we're checking quite a few 186 * System properties it does help having only 1 call to doPrivileged. 187 * Be mindful what you do in here though! 188 */ 189 Proxy p = AccessController.doPrivileged( 190 new PrivilegedAction<Proxy>() { 191 public Proxy run() { 192 int i, j; 193 String phost = null; 194 int pport = 0; 195 String nphosts = null; 196 InetSocketAddress saddr = null; 197 198 // Then let's walk the list of protocols in our array 199 for (i=0; i<props.length; i++) { 200 if (props[i][0].equalsIgnoreCase(proto)) { 201 for (j = 1; j < props[i].length; j++) { 202 /* System.getProp() will give us an empty 203 * String, "" for a defined but "empty" 204 * property. 205 */ 206 phost = NetProperties.get(props[i][j]+"Host"); 207 if (phost != null && phost.length() != 0) 208 break; 209 } 210 if (phost == null || phost.length() == 0) { 211 /** 212 * No system property defined for that 213 * protocol. Let's check System Proxy 214 * settings (Gnome & Windows) if we were 215 * instructed to. 216 */ 217 // Android-changed, hasSystemProxies is always false 218 return Proxy.NO_PROXY; 219 } 220 // If a Proxy Host is defined for that protocol 221 // Let's get the NonProxyHosts property 222 if (nprop != null) { 223 nphosts = NetProperties.get(nprop.property); 224 synchronized (nprop) { 225 if (nphosts == null) { 226 if (nprop.defaultVal != null) { 227 nphosts = nprop.defaultVal; 228 } else { 229 nprop.hostsSource = null; 230 nprop.hostsPool = null; 231 } 232 } else if (nphosts.length() != 0) { 233 // add the required default patterns 234 // but only if property no set. If it 235 // is empty, leave empty. 236 nphosts += "|" + NonProxyInfo 237 .defStringVal; 238 } 239 if (nphosts != null) { 240 if (!nphosts.equals(nprop.hostsSource)) { 241 RegexpPool pool = new RegexpPool(); 242 StringTokenizer st = new StringTokenizer(nphosts, "|", false); 243 try { 244 while (st.hasMoreTokens()) { 245 pool.add(st.nextToken().toLowerCase(), Boolean.TRUE); 246 } 247 } catch (sun.misc.REException ex) { 248 } 249 nprop.hostsPool = pool; 250 nprop.hostsSource = nphosts; 251 } 252 } 253 if (nprop.hostsPool != null && 254 nprop.hostsPool.match(urlhost) != null) { 255 return Proxy.NO_PROXY; 256 } 257 } 258 } 259 // We got a host, let's check for port 260 261 pport = NetProperties.getInteger(props[i][j]+"Port", 0).intValue(); 262 if (pport == 0 && j < (props[i].length - 1)) { 263 // Can't find a port with same prefix as Host 264 // AND it's not a SOCKS proxy 265 // Let's try the other prefixes for that proto 266 for (int k = 1; k < (props[i].length - 1); k++) { 267 if ((k != j) && (pport == 0)) 268 pport = NetProperties.getInteger(props[i][k]+"Port", 0).intValue(); 269 } 270 } 271 272 // Still couldn't find a port, let's use default 273 if (pport == 0) { 274 if (j == (props[i].length - 1)) // SOCKS 275 pport = defaultPort("socket"); 276 else 277 pport = defaultPort(proto); 278 } 279 // We did find a proxy definition. 280 // Let's create the address, but don't resolve it 281 // as this will be done at connection time 282 saddr = InetSocketAddress.createUnresolved(phost, pport); 283 // Socks is *always* the last on the list. 284 if (j == (props[i].length - 1)) { 285 int version = NetProperties.getInteger(SOCKS_PROXY_VERSION, 5).intValue(); 286 return SocksProxy.create(saddr, version); 287 } else { 288 return new Proxy(Proxy.Type.HTTP, saddr); 289 } 290 } 291 } 292 return Proxy.NO_PROXY; 293 }}); 294 295 proxyl.add(p); 296 297 /* 298 * If no specific property was set for that URI, we should be 299 * returning an iterator to an empty List. 300 */ 301 return proxyl; 302 } 303 connectFailed(URI uri, SocketAddress sa, IOException ioe)304 public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { 305 if (uri == null || sa == null || ioe == null) { 306 throw new IllegalArgumentException("Arguments can't be null."); 307 } 308 // ignored 309 } 310 311 defaultPort(String protocol)312 private int defaultPort(String protocol) { 313 if ("http".equalsIgnoreCase(protocol)) { 314 return 80; 315 } else if ("https".equalsIgnoreCase(protocol)) { 316 return 443; 317 } else if ("ftp".equalsIgnoreCase(protocol)) { 318 return 80; 319 } else if ("socket".equalsIgnoreCase(protocol)) { 320 return 1080; 321 } else if ("gopher".equalsIgnoreCase(protocol)) { 322 return 80; 323 } else { 324 return -1; 325 } 326 } 327 } 328