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 
17 package android.net;
18 
19 import android.os.ServiceManager;
20 import android.util.Log;
21 
22 import com.android.net.IProxyService;
23 
24 import com.google.android.collect.Lists;
25 
26 import java.io.IOException;
27 import java.net.InetSocketAddress;
28 import java.net.MalformedURLException;
29 import java.net.Proxy;
30 import java.net.Proxy.Type;
31 import java.net.ProxySelector;
32 import java.net.SocketAddress;
33 import java.net.URI;
34 import java.net.URISyntaxException;
35 import java.util.List;
36 
37 /**
38  * @hide
39  */
40 public class PacProxySelector extends ProxySelector {
41     private static final String TAG = "PacProxySelector";
42     public static final String PROXY_SERVICE = "com.android.net.IProxyService";
43     private static final String SOCKS = "SOCKS ";
44     private static final String PROXY = "PROXY ";
45 
46     private IProxyService mProxyService;
47     private final List<Proxy> mDefaultList;
48 
PacProxySelector()49     public PacProxySelector() {
50         mProxyService = IProxyService.Stub.asInterface(
51                 ServiceManager.getService(PROXY_SERVICE));
52         if (mProxyService == null) {
53             // Added because of b10267814 where mako is restarting.
54             Log.e(TAG, "PacProxyService: no proxy service");
55         }
56         mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
57     }
58 
59     @Override
select(URI uri)60     public List<Proxy> select(URI uri) {
61         if (mProxyService == null) {
62             mProxyService = IProxyService.Stub.asInterface(
63                     ServiceManager.getService(PROXY_SERVICE));
64         }
65         if (mProxyService == null) {
66             Log.e(TAG, "select: no proxy service return NO_PROXY");
67             return Lists.newArrayList(java.net.Proxy.NO_PROXY);
68         }
69         String response = null;
70         String urlString;
71         try {
72             // Strip path and username/password from URI so it's not visible to PAC script. The
73             // path often contains credentials the app does not want exposed to a potentially
74             // malicious PAC script.
75             if (!"http".equalsIgnoreCase(uri.getScheme())) {
76                 uri = new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), "/", null, null);
77             }
78             urlString = uri.toURL().toString();
79         } catch (URISyntaxException e) {
80             urlString = uri.getHost();
81         } catch (MalformedURLException e) {
82             urlString = uri.getHost();
83         }
84         try {
85             response = mProxyService.resolvePacFile(uri.getHost(), urlString);
86         } catch (Exception e) {
87             Log.e(TAG, "Error resolving PAC File", e);
88         }
89         if (response == null) {
90             return mDefaultList;
91         }
92 
93         return parseResponse(response);
94     }
95 
parseResponse(String response)96     private static List<Proxy> parseResponse(String response) {
97         String[] split = response.split(";");
98         List<Proxy> ret = Lists.newArrayList();
99         for (String s : split) {
100             String trimmed = s.trim();
101             if (trimmed.equals("DIRECT")) {
102                 ret.add(java.net.Proxy.NO_PROXY);
103             } else if (trimmed.startsWith(PROXY)) {
104                 Proxy proxy = proxyFromHostPort(Type.HTTP, trimmed.substring(PROXY.length()));
105                 if (proxy != null) {
106                     ret.add(proxy);
107                 }
108             } else if (trimmed.startsWith(SOCKS)) {
109                 Proxy proxy = proxyFromHostPort(Type.SOCKS, trimmed.substring(SOCKS.length()));
110                 if (proxy != null) {
111                     ret.add(proxy);
112                 }
113             }
114         }
115         if (ret.size() == 0) {
116             ret.add(java.net.Proxy.NO_PROXY);
117         }
118         return ret;
119     }
120 
proxyFromHostPort(Proxy.Type type, String hostPortString)121     private static Proxy proxyFromHostPort(Proxy.Type type, String hostPortString) {
122         try {
123             String[] hostPort = hostPortString.split(":");
124             String host = hostPort[0];
125             int port = Integer.parseInt(hostPort[1]);
126             return new Proxy(type, InetSocketAddress.createUnresolved(host, port));
127         } catch (NumberFormatException|ArrayIndexOutOfBoundsException e) {
128             Log.d(TAG, "Unable to parse proxy " + hostPortString + " " + e);
129             return null;
130         }
131     }
132 
133     @Override
connectFailed(URI uri, SocketAddress address, IOException failure)134     public void connectFailed(URI uri, SocketAddress address, IOException failure) {
135 
136     }
137 
138 }
139