1 /* 2 * Copyright (c) 2010, 2019, 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 java.net; 27 28 import java.io.IOException; 29 import java.lang.reflect.Field; 30 import java.lang.reflect.Method; 31 import java.util.HashMap; 32 import java.util.Map; 33 import java.util.Set; 34 35 /** 36 * Basic SocketImpl that relies on the internal HTTP protocol handler 37 * implementation to perform the HTTP tunneling and authentication. Once 38 * connected, all socket operations delegate to a platform SocketImpl. 39 * 40 * @since 1.8 41 */ 42 43 /*package*/ class HttpConnectSocketImpl extends DelegatingSocketImpl { 44 45 private static final String httpURLClazzStr = 46 "sun.net.www.protocol.http.HttpURLConnection"; 47 private static final String netClientClazzStr = "sun.net.NetworkClient"; 48 private static final String doTunnelingStr = "doTunneling"; 49 private static final Field httpField; 50 private static final Field serverSocketField; 51 private static final Method doTunneling; 52 53 private final String server; 54 private InetSocketAddress external_address; 55 private HashMap<Integer, Object> optionsMap = new HashMap<>(); 56 57 static { 58 try { 59 Class<?> httpClazz = Class.forName(httpURLClazzStr, true, null); 60 httpField = httpClazz.getDeclaredField("http"); 61 doTunneling = httpClazz.getDeclaredMethod(doTunnelingStr); 62 Class<?> netClientClazz = Class.forName(netClientClazzStr, true, null); 63 serverSocketField = netClientClazz.getDeclaredField("serverSocket"); 64 65 java.security.AccessController.doPrivileged( 66 new java.security.PrivilegedAction<>() { 67 public Void run() { 68 httpField.setAccessible(true); 69 serverSocketField.setAccessible(true); 70 return null; 71 } 72 }); 73 } catch (ReflectiveOperationException x) { 74 throw new InternalError("Should not reach here", x); 75 } 76 } 77 78 HttpConnectSocketImpl(Proxy proxy, SocketImpl delegate) { 79 super(delegate); 80 SocketAddress a = proxy.address(); 81 if ( !(a instanceof InetSocketAddress) ) 82 throw new IllegalArgumentException("Unsupported address type"); 83 84 InetSocketAddress ad = (InetSocketAddress) a; 85 server = ad.getHostString(); 86 port = ad.getPort(); 87 } 88 89 @Override 90 protected void connect(String host, int port) throws IOException { 91 connect(new InetSocketAddress(host, port), 0); 92 } 93 94 @Override 95 protected void connect(InetAddress address, int port) throws IOException { 96 connect(new InetSocketAddress(address, port), 0); 97 } 98 99 @Override 100 void setSocket(Socket socket) { 101 delegate.socket = socket; 102 super.setSocket(socket); 103 } 104 105 @Override 106 void setServerSocket(ServerSocket socket) { 107 throw new InternalError("should not get here"); 108 } 109 110 @Override 111 protected void connect(SocketAddress endpoint, int timeout) 112 throws IOException 113 { 114 if (endpoint == null || !(endpoint instanceof InetSocketAddress)) 115 throw new IllegalArgumentException("Unsupported address type"); 116 final InetSocketAddress epoint = (InetSocketAddress)endpoint; 117 String destHost = epoint.isUnresolved() ? epoint.getHostName() 118 : epoint.getAddress().getHostAddress(); 119 final int destPort = epoint.getPort(); 120 121 SecurityManager security = System.getSecurityManager(); 122 if (security != null) 123 security.checkConnect(destHost, destPort); 124 125 if (destHost.contains(":")) 126 destHost = "[" + destHost + "]"; 127 128 // Connect to the HTTP proxy server 129 String urlString = "http://" + destHost + ":" + destPort; 130 Socket httpSocket = privilegedDoTunnel(urlString, timeout); 131 132 // Success! 133 external_address = epoint; 134 135 // close the original socket impl and release its descriptor 136 close(); 137 138 // update the Sockets impl to the impl from the http Socket 139 SocketImpl si = httpSocket.impl; 140 getSocket().setImpl(si); 141 142 // best effort is made to try and reset options previously set 143 Set<Map.Entry<Integer,Object>> options = optionsMap.entrySet(); 144 try { 145 for(Map.Entry<Integer,Object> entry : options) { 146 si.setOption(entry.getKey(), entry.getValue()); 147 } 148 } catch (IOException x) { /* gulp! */ } 149 } 150 151 152 @Override 153 protected void listen(int backlog) { 154 throw new InternalError("should not get here"); 155 } 156 157 @Override 158 protected void accept(SocketImpl s) { 159 throw new InternalError("should not get here"); 160 } 161 162 @Override 163 void reset() { 164 throw new InternalError("should not get here"); 165 } 166 167 @Override 168 public void setOption(int opt, Object val) throws SocketException { 169 delegate.setOption(opt, val); 170 171 if (external_address != null) 172 return; // we're connected, just return 173 174 // store options so that they can be re-applied to the impl after connect 175 optionsMap.put(opt, val); 176 } 177 178 private Socket privilegedDoTunnel(final String urlString, 179 final int timeout) 180 throws IOException 181 { 182 try { 183 return java.security.AccessController.doPrivileged( 184 new java.security.PrivilegedExceptionAction<>() { 185 public Socket run() throws IOException { 186 return doTunnel(urlString, timeout); 187 } 188 }); 189 } catch (java.security.PrivilegedActionException pae) { 190 throw (IOException) pae.getException(); 191 } 192 } 193 194 private Socket doTunnel(String urlString, int connectTimeout) 195 throws IOException 196 { 197 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(server, port)); 198 URL destURL = new URL(urlString); 199 HttpURLConnection conn = (HttpURLConnection) destURL.openConnection(proxy); 200 conn.setConnectTimeout(connectTimeout); 201 int timeout = (int) getOption(SocketOptions.SO_TIMEOUT); 202 if (timeout > 0) { 203 conn.setReadTimeout(timeout); 204 } 205 conn.connect(); 206 doTunneling(conn); 207 try { 208 Object httpClient = httpField.get(conn); 209 return (Socket) serverSocketField.get(httpClient); 210 } catch (IllegalAccessException x) { 211 throw new InternalError("Should not reach here", x); 212 } 213 } 214 215 private void doTunneling(HttpURLConnection conn) throws IOException { 216 try { 217 doTunneling.invoke(conn); 218 } catch (ReflectiveOperationException x) { 219 Throwable cause = x.getCause(); 220 if (cause instanceof IOException) { 221 throw (IOException) cause; 222 } 223 throw new InternalError("Should not reach here", x); 224 } 225 } 226 227 @Override 228 protected InetAddress getInetAddress() { 229 if (external_address != null) 230 return external_address.getAddress(); 231 else 232 return delegate.getInetAddress(); 233 } 234 235 @Override 236 protected int getPort() { 237 if (external_address != null) 238 return external_address.getPort(); 239 else 240 return delegate.getPort(); 241 } 242 }