1 /** 2 * Copyright (c) 2013, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.proxyhandler; 17 18 import android.os.RemoteException; 19 import android.util.Log; 20 21 import com.android.net.IProxyPortListener; 22 import com.google.android.collect.Lists; 23 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.io.OutputStream; 27 import java.net.InetSocketAddress; 28 import java.net.Proxy; 29 import java.net.ProxySelector; 30 import java.net.ServerSocket; 31 import java.net.Socket; 32 import java.net.SocketException; 33 import java.net.URI; 34 import java.net.URISyntaxException; 35 import java.util.List; 36 import java.util.concurrent.ExecutorService; 37 import java.util.concurrent.Executors; 38 39 /** 40 * @hide 41 */ 42 public class ProxyServer extends Thread { 43 44 private static final String CONNECT = "CONNECT"; 45 private static final String HTTP_OK = "HTTP/1.1 200 OK\n"; 46 47 private static final String TAG = "ProxyServer"; 48 49 private ExecutorService threadExecutor; 50 51 public boolean mIsRunning = false; 52 53 private ServerSocket serverSocket; 54 private int mPort; 55 private IProxyPortListener mCallback; 56 57 private class ProxyConnection implements Runnable { 58 private Socket connection; 59 ProxyConnection(Socket connection)60 private ProxyConnection(Socket connection) { 61 this.connection = connection; 62 } 63 64 @Override run()65 public void run() { 66 try { 67 String requestLine = getLine(connection.getInputStream()); 68 if (requestLine == null) { 69 connection.close(); 70 return; 71 } 72 String[] splitLine = requestLine.split(" "); 73 if (splitLine.length < 3) { 74 connection.close(); 75 return; 76 } 77 String requestType = splitLine[0]; 78 String urlString = splitLine[1]; 79 80 String host = ""; 81 int port = 80; 82 83 if (requestType.equals(CONNECT)) { 84 String[] hostPortSplit = urlString.split(":"); 85 host = hostPortSplit[0]; 86 try { 87 port = Integer.parseInt(hostPortSplit[1]); 88 } catch (NumberFormatException nfe) { 89 port = 443; 90 } 91 urlString = "Https://" + host + ":" + port; 92 } else { 93 try { 94 URI url = new URI(urlString); 95 host = url.getHost(); 96 port = url.getPort(); 97 if (port < 0) { 98 port = 80; 99 } 100 } catch (URISyntaxException e) { 101 connection.close(); 102 return; 103 } 104 } 105 106 List<Proxy> list = Lists.newArrayList(); 107 try { 108 list = ProxySelector.getDefault().select(new URI(urlString)); 109 } catch (URISyntaxException e) { 110 e.printStackTrace(); 111 } 112 Socket server = null; 113 for (Proxy proxy : list) { 114 try { 115 if (!proxy.equals(Proxy.NO_PROXY)) { 116 // Only Inets created by PacProxySelector. 117 InetSocketAddress inetSocketAddress = 118 (InetSocketAddress)proxy.address(); 119 server = new Socket(inetSocketAddress.getHostName(), 120 inetSocketAddress.getPort()); 121 sendLine(server, requestLine); 122 } else { 123 server = new Socket(host, port); 124 if (requestType.equals(CONNECT)) { 125 while (getLine(connection.getInputStream()).length() != 0); 126 // No proxy to respond so we must. 127 sendLine(connection, HTTP_OK); 128 } else { 129 sendLine(server, requestLine); 130 } 131 } 132 } catch (IOException ioe) { 133 134 } 135 if (server != null) { 136 break; 137 } 138 } 139 if (server == null) { 140 server = new Socket(host, port); 141 if (requestType.equals(CONNECT)) { 142 while (getLine(connection.getInputStream()).length() != 0); 143 // No proxy to respond so we must. 144 sendLine(connection, HTTP_OK); 145 } else { 146 sendLine(server, requestLine); 147 } 148 } 149 // Pass data back and forth until complete. 150 SocketConnect.connect(connection, server); 151 } catch (IOException e) { 152 Log.d(TAG, "Problem Proxying", e); 153 } 154 try { 155 connection.close(); 156 } catch (IOException ioe) { 157 158 } 159 } 160 getLine(InputStream inputStream)161 private String getLine(InputStream inputStream) throws IOException { 162 StringBuffer buffer = new StringBuffer(); 163 int byteBuffer = inputStream.read(); 164 if (byteBuffer < 0) return ""; 165 do { 166 if (byteBuffer != '\r') { 167 buffer.append((char)byteBuffer); 168 } 169 byteBuffer = inputStream.read(); 170 } while ((byteBuffer != '\n') && (byteBuffer >= 0)); 171 172 return buffer.toString(); 173 } 174 sendLine(Socket socket, String line)175 private void sendLine(Socket socket, String line) throws IOException { 176 OutputStream os = socket.getOutputStream(); 177 os.write(line.getBytes()); 178 os.write('\r'); 179 os.write('\n'); 180 os.flush(); 181 } 182 } 183 ProxyServer()184 public ProxyServer() { 185 threadExecutor = Executors.newCachedThreadPool(); 186 mPort = -1; 187 mCallback = null; 188 } 189 190 @Override run()191 public void run() { 192 try { 193 serverSocket = new ServerSocket(0); 194 195 if (serverSocket != null) { 196 setPort(serverSocket.getLocalPort()); 197 198 while (mIsRunning) { 199 try { 200 Socket socket = serverSocket.accept(); 201 // Only receive local connections. 202 if (socket.getInetAddress().isLoopbackAddress()) { 203 ProxyConnection parser = new ProxyConnection(socket); 204 205 threadExecutor.execute(parser); 206 } else { 207 socket.close(); 208 } 209 } catch (IOException e) { 210 e.printStackTrace(); 211 } 212 } 213 } 214 } catch (SocketException e) { 215 Log.e(TAG, "Failed to start proxy server", e); 216 } catch (IOException e1) { 217 Log.e(TAG, "Failed to start proxy server", e1); 218 } 219 220 mIsRunning = false; 221 } 222 setPort(int port)223 public synchronized void setPort(int port) { 224 if (mCallback != null) { 225 try { 226 mCallback.setProxyPort(port); 227 } catch (RemoteException e) { 228 Log.w(TAG, "Proxy failed to report port to PacManager", e); 229 } 230 } 231 mPort = port; 232 } 233 setCallback(IProxyPortListener callback)234 public synchronized void setCallback(IProxyPortListener callback) { 235 if (mPort != -1) { 236 try { 237 callback.setProxyPort(mPort); 238 } catch (RemoteException e) { 239 Log.w(TAG, "Proxy failed to report port to PacManager", e); 240 } 241 } 242 mCallback = callback; 243 } 244 startServer()245 public synchronized void startServer() { 246 mIsRunning = true; 247 start(); 248 } 249 stopServer()250 public synchronized void stopServer() { 251 mIsRunning = false; 252 if (serverSocket != null) { 253 try { 254 serverSocket.close(); 255 serverSocket = null; 256 } catch (IOException e) { 257 e.printStackTrace(); 258 } 259 } 260 } 261 isBound()262 public boolean isBound() { 263 return (mPort != -1); 264 } 265 getPort()266 public int getPort() { 267 return mPort; 268 } 269 } 270