• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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