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