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