1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/proxy/proxy_config_service_android.h"
6
7 #include <sys/system_properties.h>
8
9 #include "base/android/jni_array.h"
10 #include "base/android/jni_string.h"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/compiler_specific.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/observer_list.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/strings/string_tokenizer.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "jni/ProxyChangeListener_jni.h"
24 #include "net/base/host_port_pair.h"
25 #include "net/proxy/proxy_config.h"
26 #include "url/url_parse.h"
27
28 using base::android::AttachCurrentThread;
29 using base::android::ConvertUTF8ToJavaString;
30 using base::android::ConvertJavaStringToUTF8;
31 using base::android::CheckException;
32 using base::android::ClearException;
33 using base::android::ScopedJavaGlobalRef;
34
35 namespace net {
36
37 namespace {
38
39 typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback;
40
41 // Returns whether the provided string was successfully converted to a port.
ConvertStringToPort(const std::string & port,int * output)42 bool ConvertStringToPort(const std::string& port, int* output) {
43 url::Component component(0, port.size());
44 int result = url::ParsePort(port.c_str(), component);
45 if (result == url::PORT_INVALID || result == url::PORT_UNSPECIFIED)
46 return false;
47 *output = result;
48 return true;
49 }
50
ConstructProxyServer(ProxyServer::Scheme scheme,const std::string & proxy_host,const std::string & proxy_port)51 ProxyServer ConstructProxyServer(ProxyServer::Scheme scheme,
52 const std::string& proxy_host,
53 const std::string& proxy_port) {
54 DCHECK(!proxy_host.empty());
55 int port_as_int = 0;
56 if (proxy_port.empty())
57 port_as_int = ProxyServer::GetDefaultPortForScheme(scheme);
58 else if (!ConvertStringToPort(proxy_port, &port_as_int))
59 return ProxyServer();
60 DCHECK(port_as_int > 0);
61 return ProxyServer(
62 scheme,
63 HostPortPair(proxy_host, static_cast<uint16>(port_as_int)));
64 }
65
LookupProxy(const std::string & prefix,const GetPropertyCallback & get_property,ProxyServer::Scheme scheme)66 ProxyServer LookupProxy(const std::string& prefix,
67 const GetPropertyCallback& get_property,
68 ProxyServer::Scheme scheme) {
69 DCHECK(!prefix.empty());
70 std::string proxy_host = get_property.Run(prefix + ".proxyHost");
71 if (!proxy_host.empty()) {
72 std::string proxy_port = get_property.Run(prefix + ".proxyPort");
73 return ConstructProxyServer(scheme, proxy_host, proxy_port);
74 }
75 // Fall back to default proxy, if any.
76 proxy_host = get_property.Run("proxyHost");
77 if (!proxy_host.empty()) {
78 std::string proxy_port = get_property.Run("proxyPort");
79 return ConstructProxyServer(scheme, proxy_host, proxy_port);
80 }
81 return ProxyServer();
82 }
83
LookupSocksProxy(const GetPropertyCallback & get_property)84 ProxyServer LookupSocksProxy(const GetPropertyCallback& get_property) {
85 std::string proxy_host = get_property.Run("socksProxyHost");
86 if (!proxy_host.empty()) {
87 std::string proxy_port = get_property.Run("socksProxyPort");
88 return ConstructProxyServer(ProxyServer::SCHEME_SOCKS5, proxy_host,
89 proxy_port);
90 }
91 return ProxyServer();
92 }
93
AddBypassRules(const std::string & scheme,const GetPropertyCallback & get_property,ProxyBypassRules * bypass_rules)94 void AddBypassRules(const std::string& scheme,
95 const GetPropertyCallback& get_property,
96 ProxyBypassRules* bypass_rules) {
97 // The format of a hostname pattern is a list of hostnames that are separated
98 // by | and that use * as a wildcard. For example, setting the
99 // http.nonProxyHosts property to *.android.com|*.kernel.org will cause
100 // requests to http://developer.android.com to be made without a proxy.
101
102 // Force localhost to be on the proxy exclusion list;
103 // otherwise all localhost traffic is routed through
104 // the proxy which is not desired.
105 bypass_rules->AddRuleToBypassLocal();
106
107 std::string non_proxy_hosts =
108 get_property.Run(scheme + ".nonProxyHosts");
109 if (non_proxy_hosts.empty())
110 return;
111 base::StringTokenizer tokenizer(non_proxy_hosts, "|");
112 while (tokenizer.GetNext()) {
113 std::string token = tokenizer.token();
114 std::string pattern;
115 base::TrimWhitespaceASCII(token, base::TRIM_ALL, &pattern);
116 if (pattern.empty())
117 continue;
118 // '?' is not one of the specified pattern characters above.
119 DCHECK_EQ(std::string::npos, pattern.find('?'));
120 bypass_rules->AddRuleForHostname(scheme, pattern, -1);
121 }
122 }
123
124 // Returns true if a valid proxy was found.
GetProxyRules(const GetPropertyCallback & get_property,ProxyConfig::ProxyRules * rules)125 bool GetProxyRules(const GetPropertyCallback& get_property,
126 ProxyConfig::ProxyRules* rules) {
127 // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the
128 // mostly equivalent Android implementation. There is one intentional
129 // difference: by default Chromium uses the HTTP port (80) for HTTPS
130 // connections via proxy. This default is identical on other platforms.
131 // On the opposite, Java spec suggests to use HTTPS port (443) by default (the
132 // default value of https.proxyPort).
133 rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
134 rules->proxies_for_http.SetSingleProxyServer(
135 LookupProxy("http", get_property, ProxyServer::SCHEME_HTTP));
136 rules->proxies_for_https.SetSingleProxyServer(
137 LookupProxy("https", get_property, ProxyServer::SCHEME_HTTP));
138 rules->proxies_for_ftp.SetSingleProxyServer(
139 LookupProxy("ftp", get_property, ProxyServer::SCHEME_HTTP));
140 rules->fallback_proxies.SetSingleProxyServer(LookupSocksProxy(get_property));
141 rules->bypass_rules.Clear();
142 AddBypassRules("ftp", get_property, &rules->bypass_rules);
143 AddBypassRules("http", get_property, &rules->bypass_rules);
144 AddBypassRules("https", get_property, &rules->bypass_rules);
145 // We know a proxy was found if not all of the proxy lists are empty.
146 return !(rules->proxies_for_http.IsEmpty() &&
147 rules->proxies_for_https.IsEmpty() &&
148 rules->proxies_for_ftp.IsEmpty() &&
149 rules->fallback_proxies.IsEmpty());
150 };
151
GetLatestProxyConfigInternal(const GetPropertyCallback & get_property,ProxyConfig * config)152 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property,
153 ProxyConfig* config) {
154 if (!GetProxyRules(get_property, &config->proxy_rules()))
155 *config = ProxyConfig::CreateDirect();
156 }
157
GetJavaProperty(const std::string & property)158 std::string GetJavaProperty(const std::string& property) {
159 // Use Java System.getProperty to get configuration information.
160 // TODO(pliard): Conversion to/from UTF8 ok here?
161 JNIEnv* env = AttachCurrentThread();
162 ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property);
163 ScopedJavaLocalRef<jstring> result =
164 Java_ProxyChangeListener_getProperty(env, str.obj());
165 return result.is_null() ?
166 std::string() : ConvertJavaStringToUTF8(env, result.obj());
167 }
168
CreateStaticProxyConfig(const std::string & host,int port,const std::string & pac_url,const std::vector<std::string> & exclusion_list,ProxyConfig * config)169 void CreateStaticProxyConfig(const std::string& host,
170 int port,
171 const std::string& pac_url,
172 const std::vector<std::string>& exclusion_list,
173 ProxyConfig* config) {
174 if (!pac_url.empty()) {
175 config->set_pac_url(GURL(pac_url));
176 config->set_pac_mandatory(false);
177 } else if (port != 0) {
178 std::string rules = base::StringPrintf("%s:%d", host.c_str(), port);
179 config->proxy_rules().ParseFromString(rules);
180 config->proxy_rules().bypass_rules.Clear();
181
182 std::vector<std::string>::const_iterator it;
183 for (it = exclusion_list.begin(); it != exclusion_list.end(); ++it) {
184 std::string pattern;
185 base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &pattern);
186 if (pattern.empty())
187 continue;
188 config->proxy_rules().bypass_rules.AddRuleForHostname("", pattern, -1);
189 }
190 } else {
191 *config = ProxyConfig::CreateDirect();
192 }
193 }
194
195 } // namespace
196
197 class ProxyConfigServiceAndroid::Delegate
198 : public base::RefCountedThreadSafe<Delegate> {
199 public:
Delegate(const scoped_refptr<base::SequencedTaskRunner> & network_task_runner,const scoped_refptr<base::SequencedTaskRunner> & jni_task_runner,const GetPropertyCallback & get_property_callback)200 Delegate(const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
201 const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner,
202 const GetPropertyCallback& get_property_callback)
203 : jni_delegate_(this),
204 network_task_runner_(network_task_runner),
205 jni_task_runner_(jni_task_runner),
206 get_property_callback_(get_property_callback),
207 exclude_pac_url_(false) {
208 }
209
SetupJNI()210 void SetupJNI() {
211 DCHECK(OnJNIThread());
212 JNIEnv* env = AttachCurrentThread();
213 if (java_proxy_change_listener_.is_null()) {
214 java_proxy_change_listener_.Reset(
215 Java_ProxyChangeListener_create(
216 env, base::android::GetApplicationContext()));
217 CHECK(!java_proxy_change_listener_.is_null());
218 }
219 Java_ProxyChangeListener_start(
220 env,
221 java_proxy_change_listener_.obj(),
222 reinterpret_cast<intptr_t>(&jni_delegate_));
223 }
224
FetchInitialConfig()225 void FetchInitialConfig() {
226 DCHECK(OnJNIThread());
227 ProxyConfig proxy_config;
228 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
229 network_task_runner_->PostTask(
230 FROM_HERE,
231 base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
232 }
233
Shutdown()234 void Shutdown() {
235 if (OnJNIThread()) {
236 ShutdownOnJNIThread();
237 } else {
238 jni_task_runner_->PostTask(
239 FROM_HERE,
240 base::Bind(&Delegate::ShutdownOnJNIThread, this));
241 }
242 }
243
244 // Called only on the network thread.
AddObserver(Observer * observer)245 void AddObserver(Observer* observer) {
246 DCHECK(OnNetworkThread());
247 observers_.AddObserver(observer);
248 }
249
RemoveObserver(Observer * observer)250 void RemoveObserver(Observer* observer) {
251 DCHECK(OnNetworkThread());
252 observers_.RemoveObserver(observer);
253 }
254
GetLatestProxyConfig(ProxyConfig * config)255 ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) {
256 DCHECK(OnNetworkThread());
257 if (!config)
258 return ProxyConfigService::CONFIG_UNSET;
259 *config = proxy_config_;
260 return ProxyConfigService::CONFIG_VALID;
261 }
262
263 // Called on the JNI thread.
ProxySettingsChanged()264 void ProxySettingsChanged() {
265 DCHECK(OnJNIThread());
266 ProxyConfig proxy_config;
267 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
268 network_task_runner_->PostTask(
269 FROM_HERE,
270 base::Bind(
271 &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
272 }
273
274 // Called on the JNI thread.
ProxySettingsChangedTo(const std::string & host,int port,const std::string & pac_url,const std::vector<std::string> & exclusion_list)275 void ProxySettingsChangedTo(const std::string& host,
276 int port,
277 const std::string& pac_url,
278 const std::vector<std::string>& exclusion_list) {
279 DCHECK(OnJNIThread());
280 ProxyConfig proxy_config;
281 if (exclude_pac_url_) {
282 CreateStaticProxyConfig(host, port, "", exclusion_list, &proxy_config);
283 } else {
284 CreateStaticProxyConfig(host, port, pac_url, exclusion_list,
285 &proxy_config);
286 }
287 network_task_runner_->PostTask(
288 FROM_HERE,
289 base::Bind(
290 &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
291 }
292
set_exclude_pac_url(bool enabled)293 void set_exclude_pac_url(bool enabled) {
294 exclude_pac_url_ = enabled;
295 }
296
297 private:
298 friend class base::RefCountedThreadSafe<Delegate>;
299
300 class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate {
301 public:
JNIDelegateImpl(Delegate * delegate)302 explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {}
303
304 // ProxyConfigServiceAndroid::JNIDelegate overrides.
ProxySettingsChangedTo(JNIEnv * env,jobject jself,jstring jhost,jint jport,jstring jpac_url,jobjectArray jexclusion_list)305 virtual void ProxySettingsChangedTo(JNIEnv* env,
306 jobject jself,
307 jstring jhost,
308 jint jport,
309 jstring jpac_url,
310 jobjectArray jexclusion_list) OVERRIDE {
311 std::string host = ConvertJavaStringToUTF8(env, jhost);
312 std::string pac_url;
313 if (jpac_url)
314 ConvertJavaStringToUTF8(env, jpac_url, &pac_url);
315 std::vector<std::string> exclusion_list;
316 base::android::AppendJavaStringArrayToStringVector(
317 env, jexclusion_list, &exclusion_list);
318 delegate_->ProxySettingsChangedTo(host, jport, pac_url, exclusion_list);
319 }
320
ProxySettingsChanged(JNIEnv * env,jobject self)321 virtual void ProxySettingsChanged(JNIEnv* env, jobject self) OVERRIDE {
322 delegate_->ProxySettingsChanged();
323 }
324
325 private:
326 Delegate* const delegate_;
327 };
328
~Delegate()329 virtual ~Delegate() {}
330
ShutdownOnJNIThread()331 void ShutdownOnJNIThread() {
332 if (java_proxy_change_listener_.is_null())
333 return;
334 JNIEnv* env = AttachCurrentThread();
335 Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj());
336 }
337
338 // Called on the network thread.
SetNewConfigOnNetworkThread(const ProxyConfig & proxy_config)339 void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) {
340 DCHECK(OnNetworkThread());
341 proxy_config_ = proxy_config;
342 FOR_EACH_OBSERVER(Observer, observers_,
343 OnProxyConfigChanged(proxy_config,
344 ProxyConfigService::CONFIG_VALID));
345 }
346
OnJNIThread() const347 bool OnJNIThread() const {
348 return jni_task_runner_->RunsTasksOnCurrentThread();
349 }
350
OnNetworkThread() const351 bool OnNetworkThread() const {
352 return network_task_runner_->RunsTasksOnCurrentThread();
353 }
354
355 ScopedJavaGlobalRef<jobject> java_proxy_change_listener_;
356
357 JNIDelegateImpl jni_delegate_;
358 ObserverList<Observer> observers_;
359 scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
360 scoped_refptr<base::SequencedTaskRunner> jni_task_runner_;
361 GetPropertyCallback get_property_callback_;
362 ProxyConfig proxy_config_;
363 bool exclude_pac_url_;
364
365 DISALLOW_COPY_AND_ASSIGN(Delegate);
366 };
367
ProxyConfigServiceAndroid(const scoped_refptr<base::SequencedTaskRunner> & network_task_runner,const scoped_refptr<base::SequencedTaskRunner> & jni_task_runner)368 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
369 const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
370 const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner)
371 : delegate_(new Delegate(
372 network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) {
373 delegate_->SetupJNI();
374 delegate_->FetchInitialConfig();
375 }
376
~ProxyConfigServiceAndroid()377 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
378 delegate_->Shutdown();
379 }
380
381 // static
Register(JNIEnv * env)382 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
383 return RegisterNativesImpl(env);
384 }
385
set_exclude_pac_url(bool enabled)386 void ProxyConfigServiceAndroid::set_exclude_pac_url(bool enabled) {
387 delegate_->set_exclude_pac_url(enabled);
388 }
389
AddObserver(Observer * observer)390 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
391 delegate_->AddObserver(observer);
392 }
393
RemoveObserver(Observer * observer)394 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
395 delegate_->RemoveObserver(observer);
396 }
397
398 ProxyConfigService::ConfigAvailability
GetLatestProxyConfig(ProxyConfig * config)399 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
400 return delegate_->GetLatestProxyConfig(config);
401 }
402
ProxyConfigServiceAndroid(const scoped_refptr<base::SequencedTaskRunner> & network_task_runner,const scoped_refptr<base::SequencedTaskRunner> & jni_task_runner,GetPropertyCallback get_property_callback)403 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
404 const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
405 const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner,
406 GetPropertyCallback get_property_callback)
407 : delegate_(new Delegate(
408 network_task_runner, jni_task_runner, get_property_callback)) {
409 delegate_->SetupJNI();
410 delegate_->FetchInitialConfig();
411 }
412
ProxySettingsChanged()413 void ProxyConfigServiceAndroid::ProxySettingsChanged() {
414 delegate_->ProxySettingsChanged();
415 }
416
417 } // namespace net
418