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. 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 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 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 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 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. 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 152 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property, 153 ProxyConfig* config) { 154 if (!GetProxyRules(get_property, &config->proxy_rules())) 155 *config = ProxyConfig::CreateDirect(); 156 } 157 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 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: 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 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 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 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. 245 void AddObserver(Observer* observer) { 246 DCHECK(OnNetworkThread()); 247 observers_.AddObserver(observer); 248 } 249 250 void RemoveObserver(Observer* observer) { 251 DCHECK(OnNetworkThread()); 252 observers_.RemoveObserver(observer); 253 } 254 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. 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. 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 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: 302 explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {} 303 304 // ProxyConfigServiceAndroid::JNIDelegate overrides. 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 321 virtual void ProxySettingsChanged(JNIEnv* env, jobject self) OVERRIDE { 322 delegate_->ProxySettingsChanged(); 323 } 324 325 private: 326 Delegate* const delegate_; 327 }; 328 329 virtual ~Delegate() {} 330 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. 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 347 bool OnJNIThread() const { 348 return jni_task_runner_->RunsTasksOnCurrentThread(); 349 } 350 351 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 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 377 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() { 378 delegate_->Shutdown(); 379 } 380 381 // static 382 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) { 383 return RegisterNativesImpl(env); 384 } 385 386 void ProxyConfigServiceAndroid::set_exclude_pac_url(bool enabled) { 387 delegate_->set_exclude_pac_url(enabled); 388 } 389 390 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) { 391 delegate_->AddObserver(observer); 392 } 393 394 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) { 395 delegate_->RemoveObserver(observer); 396 } 397 398 ProxyConfigService::ConfigAvailability 399 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) { 400 return delegate_->GetLatestProxyConfig(config); 401 } 402 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 413 void ProxyConfigServiceAndroid::ProxySettingsChanged() { 414 delegate_->ProxySettingsChanged(); 415 } 416 417 } // namespace net 418