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