1 /*
2  * Copyright (C) 2007 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.annotation.SdkConstant;
20 import android.annotation.SdkConstant.SdkConstantType;
21 import android.content.Context;
22 import android.text.TextUtils;
23 import android.util.Log;
24 
25 import org.apache.http.HttpHost;
26 
27 import java.net.InetSocketAddress;
28 import java.net.ProxySelector;
29 import java.net.URI;
30 import java.util.List;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 
34 /**
35  * A convenience class for accessing the user and default proxy
36  * settings.
37  */
38 public final class Proxy {
39 
40     // Set to true to enable extra debugging.
41     private static final boolean DEBUG = false;
42     private static final String TAG = "Proxy";
43 
44     private static final ProxySelector sDefaultProxySelector;
45 
46     /**
47      * Used to notify an app that's caching the default connection proxy
48      * that either the default connection or its proxy has changed.
49      * The intent will have the following extra value:</p>
50      * <ul>
51      *   <li><em>EXTRA_PROXY_INFO</em> - The ProxyProperties for the proxy.  Non-null,
52      *                                   though if the proxy is undefined the host string
53      *                                   will be empty.
54      * </ul>
55      *
56      * <p class="note">This is a protected intent that can only be sent by the system
57      */
58     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
59     public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
60     /**
61      * Intent extra included with {@link #PROXY_CHANGE_ACTION} intents.
62      * It describes the new proxy being used (as a {@link ProxyInfo} object).
63      */
64     public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
65 
66     /** @hide */
67     public static final int PROXY_VALID             = 0;
68     /** @hide */
69     public static final int PROXY_HOSTNAME_EMPTY    = 1;
70     /** @hide */
71     public static final int PROXY_HOSTNAME_INVALID  = 2;
72     /** @hide */
73     public static final int PROXY_PORT_EMPTY        = 3;
74     /** @hide */
75     public static final int PROXY_PORT_INVALID      = 4;
76     /** @hide */
77     public static final int PROXY_EXCLLIST_INVALID  = 5;
78 
79     private static ConnectivityManager sConnectivityManager = null;
80 
81     // Hostname / IP REGEX validation
82     // Matches blank input, ips, and domain names
83     private static final String NAME_IP_REGEX =
84         "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
85 
86     private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
87 
88     private static final Pattern HOSTNAME_PATTERN;
89 
90     private static final String EXCL_REGEX =
91         "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*";
92 
93     private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$";
94 
95     private static final Pattern EXCLLIST_PATTERN;
96 
97     static {
98         HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
99         EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
100         sDefaultProxySelector = ProxySelector.getDefault();
101     }
102 
103     /**
104      * Return the proxy object to be used for the URL given as parameter.
105      * @param ctx A Context used to get the settings for the proxy host.
106      * @param url A URL to be accessed. Used to evaluate exclusion list.
107      * @return Proxy (java.net) object containing the host name. If the
108      *         user did not set a hostname it returns the default host.
109      *         A null value means that no host is to be used.
110      * {@hide}
111      */
getProxy(Context ctx, String url)112     public static final java.net.Proxy getProxy(Context ctx, String url) {
113         String host = "";
114         if ((url != null) && !isLocalHost(host)) {
115             URI uri = URI.create(url);
116             ProxySelector proxySelector = ProxySelector.getDefault();
117 
118             List<java.net.Proxy> proxyList = proxySelector.select(uri);
119 
120             if (proxyList.size() > 0) {
121                 return proxyList.get(0);
122             }
123         }
124         return java.net.Proxy.NO_PROXY;
125     }
126 
127 
128     /**
129      * Return the proxy host set by the user.
130      * @param ctx A Context used to get the settings for the proxy host.
131      * @return String containing the host name. If the user did not set a host
132      *         name it returns the default host. A null value means that no
133      *         host is to be used.
134      * @deprecated Use standard java vm proxy values to find the host, port
135      *         and exclusion list.  This call ignores the exclusion list.
136      */
getHost(Context ctx)137     public static final String getHost(Context ctx) {
138         java.net.Proxy proxy = getProxy(ctx, null);
139         if (proxy == java.net.Proxy.NO_PROXY) return null;
140         try {
141             return ((InetSocketAddress)(proxy.address())).getHostName();
142         } catch (Exception e) {
143             return null;
144         }
145     }
146 
147     /**
148      * Return the proxy port set by the user.
149      * @param ctx A Context used to get the settings for the proxy port.
150      * @return The port number to use or -1 if no proxy is to be used.
151      * @deprecated Use standard java vm proxy values to find the host, port
152      *         and exclusion list.  This call ignores the exclusion list.
153      */
getPort(Context ctx)154     public static final int getPort(Context ctx) {
155         java.net.Proxy proxy = getProxy(ctx, null);
156         if (proxy == java.net.Proxy.NO_PROXY) return -1;
157         try {
158             return ((InetSocketAddress)(proxy.address())).getPort();
159         } catch (Exception e) {
160             return -1;
161         }
162     }
163 
164     /**
165      * Return the default proxy host specified by the carrier.
166      * @return String containing the host name or null if there is no proxy for
167      * this carrier.
168      * @deprecated Use standard java vm proxy values to find the host, port and
169      *         exclusion list.  This call ignores the exclusion list and no
170      *         longer reports only mobile-data apn-based proxy values.
171      */
getDefaultHost()172     public static final String getDefaultHost() {
173         String host = System.getProperty("http.proxyHost");
174         if (TextUtils.isEmpty(host)) return null;
175         return host;
176     }
177 
178     /**
179      * Return the default proxy port specified by the carrier.
180      * @return The port number to be used with the proxy host or -1 if there is
181      * no proxy for this carrier.
182      * @deprecated Use standard java vm proxy values to find the host, port and
183      *         exclusion list.  This call ignores the exclusion list and no
184      *         longer reports only mobile-data apn-based proxy values.
185      */
getDefaultPort()186     public static final int getDefaultPort() {
187         if (getDefaultHost() == null) return -1;
188         try {
189             return Integer.parseInt(System.getProperty("http.proxyPort"));
190         } catch (NumberFormatException e) {
191             return -1;
192         }
193     }
194 
195     /**
196      * Returns the preferred proxy to be used by clients. This is a wrapper
197      * around {@link android.net.Proxy#getHost()}.
198      *
199      * @param context the context which will be passed to
200      * {@link android.net.Proxy#getHost()}
201      * @param url the target URL for the request
202      * @note Calling this method requires permission
203      * android.permission.ACCESS_NETWORK_STATE
204      * @return The preferred proxy to be used by clients, or null if there
205      * is no proxy.
206      * {@hide}
207      */
208     // TODO: Get rid of this method. It's used only in tests.
getPreferredHttpHost(Context context, String url)209     public static final HttpHost getPreferredHttpHost(Context context,
210             String url) {
211         java.net.Proxy prefProxy = getProxy(context, url);
212         if (prefProxy.equals(java.net.Proxy.NO_PROXY)) {
213             return null;
214         } else {
215             InetSocketAddress sa = (InetSocketAddress)prefProxy.address();
216             return new HttpHost(sa.getHostName(), sa.getPort(), "http");
217         }
218     }
219 
isLocalHost(String host)220     private static final boolean isLocalHost(String host) {
221         if (host == null) {
222             return false;
223         }
224         try {
225             if (host != null) {
226                 if (host.equalsIgnoreCase("localhost")) {
227                     return true;
228                 }
229                 if (NetworkUtils.numericToInetAddress(host).isLoopbackAddress()) {
230                     return true;
231                 }
232             }
233         } catch (IllegalArgumentException iex) {
234         }
235         return false;
236     }
237 
238     /**
239      * Validate syntax of hostname, port and exclusion list entries
240      * {@hide}
241      */
validate(String hostname, String port, String exclList)242     public static int validate(String hostname, String port, String exclList) {
243         Matcher match = HOSTNAME_PATTERN.matcher(hostname);
244         Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
245 
246         if (!match.matches()) return PROXY_HOSTNAME_INVALID;
247 
248         if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID;
249 
250         if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY;
251 
252         if (port.length() > 0) {
253             if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY;
254             int portVal = -1;
255             try {
256                 portVal = Integer.parseInt(port);
257             } catch (NumberFormatException ex) {
258                 return PROXY_PORT_INVALID;
259             }
260             if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID;
261         }
262         return PROXY_VALID;
263     }
264 
265     /** @hide */
setHttpProxySystemProperty(ProxyInfo p)266     public static final void setHttpProxySystemProperty(ProxyInfo p) {
267         String host = null;
268         String port = null;
269         String exclList = null;
270         Uri pacFileUrl = Uri.EMPTY;
271         if (p != null) {
272             host = p.getHost();
273             port = Integer.toString(p.getPort());
274             exclList = p.getExclusionListAsString();
275             pacFileUrl = p.getPacFileUrl();
276         }
277         setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
278     }
279 
280     /** @hide */
setHttpProxySystemProperty(String host, String port, String exclList, Uri pacFileUrl)281     public static final void setHttpProxySystemProperty(String host, String port, String exclList,
282             Uri pacFileUrl) {
283         if (exclList != null) exclList = exclList.replace(",", "|");
284         if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
285         if (host != null) {
286             System.setProperty("http.proxyHost", host);
287             System.setProperty("https.proxyHost", host);
288         } else {
289             System.clearProperty("http.proxyHost");
290             System.clearProperty("https.proxyHost");
291         }
292         if (port != null) {
293             System.setProperty("http.proxyPort", port);
294             System.setProperty("https.proxyPort", port);
295         } else {
296             System.clearProperty("http.proxyPort");
297             System.clearProperty("https.proxyPort");
298         }
299         if (exclList != null) {
300             System.setProperty("http.nonProxyHosts", exclList);
301             System.setProperty("https.nonProxyHosts", exclList);
302         } else {
303             System.clearProperty("http.nonProxyHosts");
304             System.clearProperty("https.nonProxyHosts");
305         }
306         if (!Uri.EMPTY.equals(pacFileUrl)) {
307             ProxySelector.setDefault(new PacProxySelector());
308         } else {
309             ProxySelector.setDefault(sDefaultProxySelector);
310         }
311     }
312 }
313