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 final 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 // Connect to the HTTP proxy server 126 String urlString = "http://" + destHost + ":" + destPort; 127 Socket httpSocket = privilegedDoTunnel(urlString, timeout); 128 129 // Success! 130 external_address = epoint; 131 132 // close the original socket impl and release its descriptor 133 close(); 134 135 // update the Sockets impl to the impl from the http Socket 136 SocketImpl si = httpSocket.impl; 137 getSocket().setImpl(si); 138 139 // best effort is made to try and reset options previously set 140 Set<Map.Entry<Integer,Object>> options = optionsMap.entrySet(); 141 try { 142 for(Map.Entry<Integer,Object> entry : options) { 143 si.setOption(entry.getKey(), entry.getValue()); 144 } 145 } catch (IOException x) { /* gulp! */ } 146 } 147 148 149 @Override 150 protected void listen(int backlog) { 151 throw new InternalError("should not get here"); 152 } 153 154 @Override 155 protected void accept(SocketImpl s) { 156 throw new InternalError("should not get here"); 157 } 158 159 @Override 160 void reset() { 161 throw new InternalError("should not get here"); 162 } 163 164 @Override 165 public void setOption(int opt, Object val) throws SocketException { 166 delegate.setOption(opt, val); 167 168 if (external_address != null) 169 return; // we're connected, just return 170 171 // store options so that they can be re-applied to the impl after connect 172 optionsMap.put(opt, val); 173 } 174 175 private Socket privilegedDoTunnel(final String urlString, 176 final int timeout) 177 throws IOException 178 { 179 try { 180 return java.security.AccessController.doPrivileged( 181 new java.security.PrivilegedExceptionAction<>() { 182 public Socket run() throws IOException { 183 return doTunnel(urlString, timeout); 184 } 185 }); 186 } catch (java.security.PrivilegedActionException pae) { 187 throw (IOException) pae.getException(); 188 } 189 } 190 191 private Socket doTunnel(String urlString, int connectTimeout) 192 throws IOException 193 { 194 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(server, port)); 195 URL destURL = new URL(urlString); 196 HttpURLConnection conn = (HttpURLConnection) destURL.openConnection(proxy); 197 conn.setConnectTimeout(connectTimeout); 198 int timeout = (int) getOption(SocketOptions.SO_TIMEOUT); 199 if (timeout > 0) { 200 conn.setReadTimeout(timeout); 201 } 202 conn.connect(); 203 doTunneling(conn); 204 try { 205 Object httpClient = httpField.get(conn); 206 return (Socket) serverSocketField.get(httpClient); 207 } catch (IllegalAccessException x) { 208 throw new InternalError("Should not reach here", x); 209 } 210 } 211 212 private void doTunneling(HttpURLConnection conn) throws IOException { 213 try { 214 doTunneling.invoke(conn); 215 } catch (ReflectiveOperationException x) { 216 Throwable cause = x.getCause(); 217 if (cause instanceof IOException) { 218 throw (IOException) cause; 219 } 220 throw new InternalError("Should not reach here", x); 221 } 222 } 223 224 @Override 225 protected InetAddress getInetAddress() { 226 if (external_address != null) 227 return external_address.getAddress(); 228 else 229 return delegate.getInetAddress(); 230 } 231 232 @Override 233 protected int getPort() { 234 if (external_address != null) 235 return external_address.getPort(); 236 else 237 return delegate.getPort(); 238 } 239 }