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_resolution/proxy_config_service_linux.h"
6
7 #include <errno.h>
8 #include <limits.h>
9 #if !defined(OS_BSD)
10 #include <sys/inotify.h>
11 #endif
12 #include <unistd.h>
13
14 #include <map>
15 #include <utility>
16
17 #include "base/bind.h"
18 #include "base/files/file_descriptor_watcher_posix.h"
19 #include "base/files/file_path.h"
20 #include "base/files/file_util.h"
21 #include "base/files/scoped_file.h"
22 #include "base/logging.h"
23 #include "base/macros.h"
24 #include "base/memory/ptr_util.h"
25 #include "base/nix/xdg_util.h"
26 #include "base/sequenced_task_runner.h"
27 #include "base/single_thread_task_runner.h"
28 #include "base/strings/string_number_conversions.h"
29 #include "base/strings/string_tokenizer.h"
30 #include "base/strings/string_util.h"
31 #include "base/task/post_task.h"
32 #include "base/task/task_traits.h"
33 #include "base/threading/thread_restrictions.h"
34 #include "base/timer/timer.h"
35 #include "net/base/proxy_server.h"
36
37 #if defined(USE_GIO)
38 #include <gio/gio.h>
39 #endif // defined(USE_GIO)
40
41 namespace net {
42
43 namespace {
44
45 // This turns all rules with a hostname into wildcard matches, which will
46 // match not just the indicated hostname but also any hostname that ends with
47 // it.
RewriteRulesForSuffixMatching(ProxyBypassRules * out)48 void RewriteRulesForSuffixMatching(ProxyBypassRules* out) {
49 // Prepend a wildcard (*) to any hostname based rules, provided it isn't an IP
50 // address.
51 for (size_t i = 0; i < out->rules().size(); ++i) {
52 if (!out->rules()[i]->IsHostnamePatternRule())
53 continue;
54
55 const SchemeHostPortMatcherHostnamePatternRule* prev_rule =
56 static_cast<const SchemeHostPortMatcherHostnamePatternRule*>(
57 out->rules()[i].get());
58 out->ReplaceRule(i, prev_rule->GenerateSuffixMatchingRule());
59 }
60 }
61
62 // Given a proxy hostname from a setting, returns that hostname with
63 // an appropriate proxy server scheme prefix.
64 // scheme indicates the desired proxy scheme: usually http, with
65 // socks 4 or 5 as special cases.
66 // TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
FixupProxyHostScheme(ProxyServer::Scheme scheme,std::string host)67 std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
68 std::string host) {
69 if (scheme == ProxyServer::SCHEME_SOCKS5 &&
70 base::StartsWith(host, "socks4://",
71 base::CompareCase::INSENSITIVE_ASCII)) {
72 // We default to socks 5, but if the user specifically set it to
73 // socks4://, then use that.
74 scheme = ProxyServer::SCHEME_SOCKS4;
75 }
76 // Strip the scheme if any.
77 std::string::size_type colon = host.find("://");
78 if (colon != std::string::npos)
79 host = host.substr(colon + 3);
80 // If a username and perhaps password are specified, give a warning.
81 std::string::size_type at_sign = host.find("@");
82 // Should this be supported?
83 if (at_sign != std::string::npos) {
84 // ProxyConfig does not support authentication parameters, but Chrome
85 // will prompt for the password later. Disregard the
86 // authentication parameters and continue with this hostname.
87 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
88 host = host.substr(at_sign + 1);
89 }
90 // If this is a socks proxy, prepend a scheme so as to tell
91 // ProxyServer. This also allows ProxyServer to choose the right
92 // default port.
93 if (scheme == ProxyServer::SCHEME_SOCKS4)
94 host = "socks4://" + host;
95 else if (scheme == ProxyServer::SCHEME_SOCKS5)
96 host = "socks5://" + host;
97 // If there is a trailing slash, remove it so |host| will parse correctly
98 // even if it includes a port number (since the slash is not numeric).
99 if (!host.empty() && host.back() == '/')
100 host.resize(host.length() - 1);
101 return host;
102 }
103
GetConfigOrDirect(const base::Optional<ProxyConfigWithAnnotation> & optional_config)104 ProxyConfigWithAnnotation GetConfigOrDirect(
105 const base::Optional<ProxyConfigWithAnnotation>& optional_config) {
106 if (optional_config)
107 return optional_config.value();
108
109 ProxyConfigWithAnnotation config = ProxyConfigWithAnnotation::CreateDirect();
110 return config;
111 }
112
113 } // namespace
114
115 ProxyConfigServiceLinux::Delegate::~Delegate() = default;
116
GetProxyFromEnvVarForScheme(base::StringPiece variable,ProxyServer::Scheme scheme,ProxyServer * result_server)117 bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
118 base::StringPiece variable,
119 ProxyServer::Scheme scheme,
120 ProxyServer* result_server) {
121 std::string env_value;
122 if (!env_var_getter_->GetVar(variable, &env_value))
123 return false;
124
125 if (env_value.empty())
126 return false;
127
128 env_value = FixupProxyHostScheme(scheme, env_value);
129 ProxyServer proxy_server =
130 ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
131 if (proxy_server.is_valid() && !proxy_server.is_direct()) {
132 *result_server = proxy_server;
133 return true;
134 }
135 LOG(ERROR) << "Failed to parse environment variable " << variable;
136 return false;
137 }
138
GetProxyFromEnvVar(base::StringPiece variable,ProxyServer * result_server)139 bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
140 base::StringPiece variable,
141 ProxyServer* result_server) {
142 return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
143 result_server);
144 }
145
146 base::Optional<ProxyConfigWithAnnotation>
GetConfigFromEnv()147 ProxyConfigServiceLinux::Delegate::GetConfigFromEnv() {
148 ProxyConfig config;
149
150 // Check for automatic configuration first, in
151 // "auto_proxy". Possibly only the "environment_proxy" firefox
152 // extension has ever used this, but it still sounds like a good
153 // idea.
154 std::string auto_proxy;
155 if (env_var_getter_->GetVar("auto_proxy", &auto_proxy)) {
156 if (auto_proxy.empty()) {
157 // Defined and empty => autodetect
158 config.set_auto_detect(true);
159 } else {
160 // specified autoconfig URL
161 config.set_pac_url(GURL(auto_proxy));
162 }
163 return ProxyConfigWithAnnotation(
164 config, NetworkTrafficAnnotationTag(traffic_annotation_));
165 }
166 // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
167 ProxyServer proxy_server;
168 if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
169 config.proxy_rules().type = ProxyConfig::ProxyRules::Type::PROXY_LIST;
170 config.proxy_rules().single_proxies.SetSingleProxyServer(proxy_server);
171 } else {
172 bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
173 if (have_http)
174 config.proxy_rules().proxies_for_http.SetSingleProxyServer(proxy_server);
175 // It would be tempting to let http_proxy apply for all protocols
176 // if https_proxy and ftp_proxy are not defined. Googling turns up
177 // several documents that mention only http_proxy. But then the
178 // user really might not want to proxy https. And it doesn't seem
179 // like other apps do this. So we will refrain.
180 bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
181 if (have_https)
182 config.proxy_rules().proxies_for_https.SetSingleProxyServer(proxy_server);
183 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
184 if (have_ftp)
185 config.proxy_rules().proxies_for_ftp.SetSingleProxyServer(proxy_server);
186 if (have_http || have_https || have_ftp) {
187 // mustn't change type unless some rules are actually set.
188 config.proxy_rules().type =
189 ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME;
190 }
191 }
192 if (config.proxy_rules().empty()) {
193 // If the above were not defined, try for socks.
194 // For environment variables, we default to version 5, per the gnome
195 // documentation: http://library.gnome.org/devel/gnet/stable/gnet-socks.html
196 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS5;
197 std::string env_version;
198 if (env_var_getter_->GetVar("SOCKS_VERSION", &env_version)
199 && env_version == "4")
200 scheme = ProxyServer::SCHEME_SOCKS4;
201 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
202 config.proxy_rules().type = ProxyConfig::ProxyRules::Type::PROXY_LIST;
203 config.proxy_rules().single_proxies.SetSingleProxyServer(proxy_server);
204 }
205 }
206 // Look for the proxy bypass list.
207 std::string no_proxy;
208 env_var_getter_->GetVar("no_proxy", &no_proxy);
209 if (config.proxy_rules().empty()) {
210 // Having only "no_proxy" set, presumably to "*", makes it
211 // explicit that env vars do specify a configuration: having no
212 // rules specified only means the user explicitly asks for direct
213 // connections.
214 return !no_proxy.empty()
215 ? ProxyConfigWithAnnotation(
216 config, NetworkTrafficAnnotationTag(traffic_annotation_))
217 : base::Optional<ProxyConfigWithAnnotation>();
218 }
219 // Note that this uses "suffix" matching. So a bypass of "google.com"
220 // is understood to mean a bypass of "*google.com".
221 config.proxy_rules().bypass_rules.ParseFromString(no_proxy);
222 RewriteRulesForSuffixMatching(&config.proxy_rules().bypass_rules);
223
224 return ProxyConfigWithAnnotation(
225 config, NetworkTrafficAnnotationTag(traffic_annotation_));
226 }
227
228 namespace {
229
230 const int kDebounceTimeoutMilliseconds = 250;
231
232 #if defined(USE_GIO)
233 const char kProxyGSettingsSchema[] = "org.gnome.system.proxy";
234
235 // This setting getter uses gsettings, as used in most GNOME 3 desktops.
236 class SettingGetterImplGSettings
237 : public ProxyConfigServiceLinux::SettingGetter {
238 public:
SettingGetterImplGSettings()239 SettingGetterImplGSettings()
240 : client_(nullptr),
241 http_client_(nullptr),
242 https_client_(nullptr),
243 ftp_client_(nullptr),
244 socks_client_(nullptr),
245 notify_delegate_(nullptr),
246 debounce_timer_(new base::OneShotTimer()) {}
247
~SettingGetterImplGSettings()248 ~SettingGetterImplGSettings() override {
249 // client_ should have been released before now, from
250 // Delegate::OnDestroy(), while running on the UI thread. However
251 // on exiting the process, it may happen that
252 // Delegate::OnDestroy() task is left pending on the glib loop
253 // after the loop was quit, and pending tasks may then be deleted
254 // without being run.
255 if (client_) {
256 // gsettings client was not cleaned up.
257 if (task_runner_->RunsTasksInCurrentSequence()) {
258 // We are on the UI thread so we can clean it safely.
259 VLOG(1) << "~SettingGetterImplGSettings: releasing gsettings client";
260 ShutDown();
261 } else {
262 LOG(WARNING) << "~SettingGetterImplGSettings: leaking gsettings client";
263 client_ = nullptr;
264 }
265 }
266 DCHECK(!client_);
267 }
268
269 // CheckVersion() must be called *before* Init()!
270 bool CheckVersion(base::Environment* env);
271
Init(const scoped_refptr<base::SingleThreadTaskRunner> & glib_task_runner)272 bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner)
273 override {
274 DCHECK(glib_task_runner->RunsTasksInCurrentSequence());
275 DCHECK(!client_);
276 DCHECK(!task_runner_.get());
277
278 if (!g_settings_schema_source_lookup(g_settings_schema_source_get_default(),
279 kProxyGSettingsSchema, FALSE) ||
280 !(client_ = g_settings_new(kProxyGSettingsSchema))) {
281 // It's not clear whether/when this can return NULL.
282 LOG(ERROR) << "Unable to create a gsettings client";
283 return false;
284 }
285 task_runner_ = glib_task_runner;
286 // We assume these all work if the above call worked.
287 http_client_ = g_settings_get_child(client_, "http");
288 https_client_ = g_settings_get_child(client_, "https");
289 ftp_client_ = g_settings_get_child(client_, "ftp");
290 socks_client_ = g_settings_get_child(client_, "socks");
291 DCHECK(http_client_ && https_client_ && ftp_client_ && socks_client_);
292 return true;
293 }
294
ShutDown()295 void ShutDown() override {
296 if (client_) {
297 DCHECK(task_runner_->RunsTasksInCurrentSequence());
298 // This also disables gsettings notifications.
299 g_object_unref(socks_client_);
300 g_object_unref(ftp_client_);
301 g_object_unref(https_client_);
302 g_object_unref(http_client_);
303 g_object_unref(client_);
304 // We only need to null client_ because it's the only one that we check.
305 client_ = nullptr;
306 task_runner_ = nullptr;
307 }
308 debounce_timer_.reset();
309 }
310
SetUpNotifications(ProxyConfigServiceLinux::Delegate * delegate)311 bool SetUpNotifications(
312 ProxyConfigServiceLinux::Delegate* delegate) override {
313 DCHECK(client_);
314 DCHECK(task_runner_->RunsTasksInCurrentSequence());
315 notify_delegate_ = delegate;
316 // We could watch for the change-event signal instead of changed, but
317 // since we have to watch more than one object, we'd still have to
318 // debounce change notifications. This is conceptually simpler.
319 g_signal_connect(G_OBJECT(client_), "changed",
320 G_CALLBACK(OnGSettingsChangeNotification), this);
321 g_signal_connect(G_OBJECT(http_client_), "changed",
322 G_CALLBACK(OnGSettingsChangeNotification), this);
323 g_signal_connect(G_OBJECT(https_client_), "changed",
324 G_CALLBACK(OnGSettingsChangeNotification), this);
325 g_signal_connect(G_OBJECT(ftp_client_), "changed",
326 G_CALLBACK(OnGSettingsChangeNotification), this);
327 g_signal_connect(G_OBJECT(socks_client_), "changed",
328 G_CALLBACK(OnGSettingsChangeNotification), this);
329 // Simulate a change to avoid possibly losing updates before this point.
330 OnChangeNotification();
331 return true;
332 }
333
GetNotificationTaskRunner()334 const scoped_refptr<base::SequencedTaskRunner>& GetNotificationTaskRunner()
335 override {
336 return task_runner_;
337 }
338
GetString(StringSetting key,std::string * result)339 bool GetString(StringSetting key, std::string* result) override {
340 DCHECK(client_);
341 switch (key) {
342 case PROXY_MODE:
343 return GetStringByPath(client_, "mode", result);
344 case PROXY_AUTOCONF_URL:
345 return GetStringByPath(client_, "autoconfig-url", result);
346 case PROXY_HTTP_HOST:
347 return GetStringByPath(http_client_, "host", result);
348 case PROXY_HTTPS_HOST:
349 return GetStringByPath(https_client_, "host", result);
350 case PROXY_FTP_HOST:
351 return GetStringByPath(ftp_client_, "host", result);
352 case PROXY_SOCKS_HOST:
353 return GetStringByPath(socks_client_, "host", result);
354 }
355 return false; // Placate compiler.
356 }
GetBool(BoolSetting key,bool * result)357 bool GetBool(BoolSetting key, bool* result) override {
358 DCHECK(client_);
359 switch (key) {
360 case PROXY_USE_HTTP_PROXY:
361 // Although there is an "enabled" boolean in http_client_, it is not set
362 // to true by the proxy config utility. We ignore it and return false.
363 return false;
364 case PROXY_USE_SAME_PROXY:
365 // Similarly, although there is a "use-same-proxy" boolean in client_,
366 // it is never set to false by the proxy config utility. We ignore it.
367 return false;
368 case PROXY_USE_AUTHENTICATION:
369 // There is also no way to set this in the proxy config utility, but it
370 // doesn't hurt us to get the actual setting (unlike the two above).
371 return GetBoolByPath(http_client_, "use-authentication", result);
372 }
373 return false; // Placate compiler.
374 }
GetInt(IntSetting key,int * result)375 bool GetInt(IntSetting key, int* result) override {
376 DCHECK(client_);
377 switch (key) {
378 case PROXY_HTTP_PORT:
379 return GetIntByPath(http_client_, "port", result);
380 case PROXY_HTTPS_PORT:
381 return GetIntByPath(https_client_, "port", result);
382 case PROXY_FTP_PORT:
383 return GetIntByPath(ftp_client_, "port", result);
384 case PROXY_SOCKS_PORT:
385 return GetIntByPath(socks_client_, "port", result);
386 }
387 return false; // Placate compiler.
388 }
GetStringList(StringListSetting key,std::vector<std::string> * result)389 bool GetStringList(StringListSetting key,
390 std::vector<std::string>* result) override {
391 DCHECK(client_);
392 switch (key) {
393 case PROXY_IGNORE_HOSTS:
394 return GetStringListByPath(client_, "ignore-hosts", result);
395 }
396 return false; // Placate compiler.
397 }
398
BypassListIsReversed()399 bool BypassListIsReversed() override {
400 // This is a KDE-specific setting.
401 return false;
402 }
403
UseSuffixMatching()404 bool UseSuffixMatching() override { return false; }
405
406 private:
GetStringByPath(GSettings * client,base::StringPiece key,std::string * result)407 bool GetStringByPath(GSettings* client,
408 base::StringPiece key,
409 std::string* result) {
410 DCHECK(task_runner_->RunsTasksInCurrentSequence());
411 gchar* value = g_settings_get_string(client, key.data());
412 if (!value)
413 return false;
414 *result = value;
415 g_free(value);
416 return true;
417 }
GetBoolByPath(GSettings * client,base::StringPiece key,bool * result)418 bool GetBoolByPath(GSettings* client, base::StringPiece key, bool* result) {
419 DCHECK(task_runner_->RunsTasksInCurrentSequence());
420 *result = static_cast<bool>(g_settings_get_boolean(client, key.data()));
421 return true;
422 }
GetIntByPath(GSettings * client,base::StringPiece key,int * result)423 bool GetIntByPath(GSettings* client, base::StringPiece key, int* result) {
424 DCHECK(task_runner_->RunsTasksInCurrentSequence());
425 *result = g_settings_get_int(client, key.data());
426 return true;
427 }
GetStringListByPath(GSettings * client,base::StringPiece key,std::vector<std::string> * result)428 bool GetStringListByPath(GSettings* client,
429 base::StringPiece key,
430 std::vector<std::string>* result) {
431 DCHECK(task_runner_->RunsTasksInCurrentSequence());
432 gchar** list = g_settings_get_strv(client, key.data());
433 if (!list)
434 return false;
435 for (size_t i = 0; list[i]; ++i) {
436 result->push_back(static_cast<char*>(list[i]));
437 g_free(list[i]);
438 }
439 g_free(list);
440 return true;
441 }
442
443 // This is the callback from the debounce timer.
OnDebouncedNotification()444 void OnDebouncedNotification() {
445 DCHECK(task_runner_->RunsTasksInCurrentSequence());
446 CHECK(notify_delegate_);
447 // Forward to a method on the proxy config service delegate object.
448 notify_delegate_->OnCheckProxyConfigSettings();
449 }
450
OnChangeNotification()451 void OnChangeNotification() {
452 // We don't use Reset() because the timer may not yet be running.
453 // (In that case Stop() is a no-op.)
454 debounce_timer_->Stop();
455 debounce_timer_->Start(FROM_HERE,
456 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds),
457 this, &SettingGetterImplGSettings::OnDebouncedNotification);
458 }
459
460 // gsettings notification callback, dispatched on the default glib main loop.
OnGSettingsChangeNotification(GSettings * client,gchar * key,gpointer user_data)461 static void OnGSettingsChangeNotification(GSettings* client, gchar* key,
462 gpointer user_data) {
463 VLOG(1) << "gsettings change notification for key " << key;
464 // We don't track which key has changed, just that something did change.
465 SettingGetterImplGSettings* setting_getter =
466 reinterpret_cast<SettingGetterImplGSettings*>(user_data);
467 setting_getter->OnChangeNotification();
468 }
469
470 GSettings* client_;
471 GSettings* http_client_;
472 GSettings* https_client_;
473 GSettings* ftp_client_;
474 GSettings* socks_client_;
475 ProxyConfigServiceLinux::Delegate* notify_delegate_;
476 std::unique_ptr<base::OneShotTimer> debounce_timer_;
477
478 // Task runner for the thread that we make gsettings calls on. It should
479 // be the UI thread and all our methods should be called on this
480 // thread. Only for assertions.
481 scoped_refptr<base::SequencedTaskRunner> task_runner_;
482
483 DISALLOW_COPY_AND_ASSIGN(SettingGetterImplGSettings);
484 };
485
CheckVersion(base::Environment * env)486 bool SettingGetterImplGSettings::CheckVersion(
487 base::Environment* env) {
488 // CheckVersion() must be called *before* Init()!
489 DCHECK(!client_);
490
491 GSettings* client = nullptr;
492 if (g_settings_schema_source_lookup(g_settings_schema_source_get_default(),
493 kProxyGSettingsSchema, FALSE)) {
494 client = g_settings_new(kProxyGSettingsSchema);
495 }
496 if (!client) {
497 VLOG(1) << "Cannot create gsettings client.";
498 return false;
499 }
500 g_object_unref(client);
501
502 VLOG(1) << "All gsettings tests OK. Will get proxy config from gsettings.";
503 return true;
504 }
505 #endif // defined(USE_GIO)
506
507 // Converts |value| from a decimal string to an int. If there was a failure
508 // parsing, returns |default_value|.
StringToIntOrDefault(base::StringPiece value,int default_value)509 int StringToIntOrDefault(base::StringPiece value, int default_value) {
510 int result;
511 if (base::StringToInt(value, &result))
512 return result;
513 return default_value;
514 }
515
516 #if !defined(OS_BSD)
517 // This is the KDE version that reads kioslaverc and simulates gsettings.
518 // Doing this allows the main Delegate code, as well as the unit tests
519 // for it, to stay the same - and the settings map fairly well besides.
520 class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter {
521 public:
SettingGetterImplKDE(base::Environment * env_var_getter)522 explicit SettingGetterImplKDE(base::Environment* env_var_getter)
523 : inotify_fd_(-1),
524 notify_delegate_(nullptr),
525 debounce_timer_(new base::OneShotTimer()),
526 indirect_manual_(false),
527 auto_no_pac_(false),
528 reversed_bypass_list_(false),
529 env_var_getter_(env_var_getter),
530 file_task_runner_(nullptr) {
531 // This has to be called on the UI thread (http://crbug.com/69057).
532 base::ThreadRestrictions::ScopedAllowIO allow_io;
533
534 // Derive the location of the kde config dir from the environment.
535 std::string home;
536 if (env_var_getter->GetVar("KDEHOME", &home) && !home.empty()) {
537 // $KDEHOME is set. Use it unconditionally.
538 kde_config_dir_ = KDEHomeToConfigPath(base::FilePath(home));
539 } else {
540 // $KDEHOME is unset. Try to figure out what to use. This seems to be
541 // the common case on most distributions.
542 if (!env_var_getter->GetVar(base::env_vars::kHome, &home))
543 // User has no $HOME? Give up. Later we'll report the failure.
544 return;
545 if (base::nix::GetDesktopEnvironment(env_var_getter) ==
546 base::nix::DESKTOP_ENVIRONMENT_KDE3) {
547 // KDE3 always uses .kde for its configuration.
548 base::FilePath kde_path = base::FilePath(home).Append(".kde");
549 kde_config_dir_ = KDEHomeToConfigPath(kde_path);
550 } else if (base::nix::GetDesktopEnvironment(env_var_getter) ==
551 base::nix::DESKTOP_ENVIRONMENT_KDE4) {
552 // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
553 // both can be installed side-by-side. Sadly they don't all do this, and
554 // they don't always do this: some distributions have started switching
555 // back as well. So if there is a .kde4 directory, check the timestamps
556 // of the config directories within and use the newest one.
557 // Note that we should currently be running in the UI thread, because in
558 // the gsettings version, that is the only thread that can access the
559 // proxy settings (a gsettings restriction). As noted below, the initial
560 // read of the proxy settings will be done in this thread anyway, so we
561 // check for .kde4 here in this thread as well.
562 base::FilePath kde3_path = base::FilePath(home).Append(".kde");
563 base::FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
564 base::FilePath kde4_path = base::FilePath(home).Append(".kde4");
565 base::FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
566 bool use_kde4 = false;
567 if (base::DirectoryExists(kde4_path)) {
568 base::File::Info kde3_info;
569 base::File::Info kde4_info;
570 if (base::GetFileInfo(kde4_config, &kde4_info)) {
571 if (base::GetFileInfo(kde3_config, &kde3_info)) {
572 use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
573 } else {
574 use_kde4 = true;
575 }
576 }
577 }
578 if (use_kde4) {
579 kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
580 } else {
581 kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
582 }
583 } else {
584 // KDE 5 migrated to ~/.config for storing kioslaverc.
585 kde_config_dir_ = base::FilePath(home).Append(".config");
586 }
587 }
588 }
589
~SettingGetterImplKDE()590 ~SettingGetterImplKDE() override {
591 // inotify_fd_ should have been closed before now, from
592 // Delegate::OnDestroy(), while running on the file thread. However
593 // on exiting the process, it may happen that Delegate::OnDestroy()
594 // task is left pending on the file loop after the loop was quit,
595 // and pending tasks may then be deleted without being run.
596 // Here in the KDE version, we can safely close the file descriptor
597 // anyway. (Not that it really matters; the process is exiting.)
598 if (inotify_fd_ >= 0)
599 ShutDown();
600 DCHECK_LT(inotify_fd_, 0);
601 }
602
Init(const scoped_refptr<base::SingleThreadTaskRunner> & glib_task_runner)603 bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner)
604 override {
605 // This has to be called on the UI thread (http://crbug.com/69057).
606 base::ThreadRestrictions::ScopedAllowIO allow_io;
607 DCHECK_LT(inotify_fd_, 0);
608 inotify_fd_ = inotify_init();
609 if (inotify_fd_ < 0) {
610 PLOG(ERROR) << "inotify_init failed";
611 return false;
612 }
613 if (!base::SetNonBlocking(inotify_fd_)) {
614 PLOG(ERROR) << "base::SetNonBlocking failed";
615 close(inotify_fd_);
616 inotify_fd_ = -1;
617 return false;
618 }
619
620 constexpr base::TaskTraits kTraits = {base::TaskPriority::USER_VISIBLE,
621 base::MayBlock()};
622 file_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(kTraits);
623
624 // The initial read is done on the current thread, not
625 // |file_task_runner_|, since we will need to have it for
626 // SetUpAndFetchInitialConfig().
627 UpdateCachedSettings();
628 return true;
629 }
630
ShutDown()631 void ShutDown() override {
632 if (inotify_fd_ >= 0) {
633 ResetCachedSettings();
634 inotify_watcher_.reset();
635 close(inotify_fd_);
636 inotify_fd_ = -1;
637 }
638 debounce_timer_.reset();
639 }
640
SetUpNotifications(ProxyConfigServiceLinux::Delegate * delegate)641 bool SetUpNotifications(
642 ProxyConfigServiceLinux::Delegate* delegate) override {
643 DCHECK_GE(inotify_fd_, 0);
644 DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
645 // We can't just watch the kioslaverc file directly, since KDE will write
646 // a new copy of it and then rename it whenever settings are changed and
647 // inotify watches inodes (so we'll be watching the old deleted file after
648 // the first change, and it will never change again). So, we watch the
649 // directory instead. We then act only on changes to the kioslaverc entry.
650 // TODO(eroman): What if the file is deleted? (handle with IN_DELETE).
651 if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
652 IN_MODIFY | IN_MOVED_TO) < 0) {
653 return false;
654 }
655 notify_delegate_ = delegate;
656 inotify_watcher_ = base::FileDescriptorWatcher::WatchReadable(
657 inotify_fd_,
658 base::BindRepeating(&SettingGetterImplKDE::OnChangeNotification,
659 base::Unretained(this)));
660 // Simulate a change to avoid possibly losing updates before this point.
661 OnChangeNotification();
662 return true;
663 }
664
GetNotificationTaskRunner()665 const scoped_refptr<base::SequencedTaskRunner>& GetNotificationTaskRunner()
666 override {
667 return file_task_runner_;
668 }
669
GetString(StringSetting key,std::string * result)670 bool GetString(StringSetting key, std::string* result) override {
671 auto it = string_table_.find(key);
672 if (it == string_table_.end())
673 return false;
674 *result = it->second;
675 return true;
676 }
GetBool(BoolSetting key,bool * result)677 bool GetBool(BoolSetting key, bool* result) override {
678 // We don't ever have any booleans.
679 return false;
680 }
GetInt(IntSetting key,int * result)681 bool GetInt(IntSetting key, int* result) override {
682 // We don't ever have any integers. (See AddProxy() below about ports.)
683 return false;
684 }
GetStringList(StringListSetting key,std::vector<std::string> * result)685 bool GetStringList(StringListSetting key,
686 std::vector<std::string>* result) override {
687 auto it = strings_table_.find(key);
688 if (it == strings_table_.end())
689 return false;
690 *result = it->second;
691 return true;
692 }
693
BypassListIsReversed()694 bool BypassListIsReversed() override { return reversed_bypass_list_; }
695
UseSuffixMatching()696 bool UseSuffixMatching() override { return true; }
697
698 private:
ResetCachedSettings()699 void ResetCachedSettings() {
700 string_table_.clear();
701 strings_table_.clear();
702 indirect_manual_ = false;
703 auto_no_pac_ = false;
704 reversed_bypass_list_ = false;
705 }
706
KDEHomeToConfigPath(const base::FilePath & kde_home)707 base::FilePath KDEHomeToConfigPath(const base::FilePath& kde_home) {
708 return kde_home.Append("share").Append("config");
709 }
710
AddProxy(StringSetting host_key,const std::string & value)711 void AddProxy(StringSetting host_key, const std::string& value) {
712 if (value.empty() || value.substr(0, 3) == "//:")
713 // No proxy.
714 return;
715 size_t space = value.find(' ');
716 if (space != std::string::npos) {
717 // Newer versions of KDE use a space rather than a colon to separate the
718 // port number from the hostname. If we find this, we need to convert it.
719 std::string fixed = value;
720 fixed[space] = ':';
721 string_table_[host_key] = fixed;
722 } else {
723 // We don't need to parse the port number out; GetProxyFromSettings()
724 // would only append it right back again. So we just leave the port
725 // number right in the host string.
726 string_table_[host_key] = value;
727 }
728 }
729
AddHostList(StringListSetting key,const std::string & value)730 void AddHostList(StringListSetting key, const std::string& value) {
731 std::vector<std::string> tokens;
732 base::StringTokenizer tk(value, ", ");
733 while (tk.GetNext()) {
734 std::string token = tk.token();
735 if (!token.empty())
736 tokens.push_back(token);
737 }
738 strings_table_[key] = tokens;
739 }
740
AddKDESetting(const std::string & key,const std::string & value)741 void AddKDESetting(const std::string& key, const std::string& value) {
742 if (key == "ProxyType") {
743 const char* mode = "none";
744 indirect_manual_ = false;
745 auto_no_pac_ = false;
746 int int_value = StringToIntOrDefault(value, 0);
747 switch (int_value) {
748 case 1: // Manual configuration.
749 mode = "manual";
750 break;
751 case 2: // PAC URL.
752 mode = "auto";
753 break;
754 case 3: // WPAD.
755 mode = "auto";
756 auto_no_pac_ = true;
757 break;
758 case 4: // Indirect manual via environment variables.
759 mode = "manual";
760 indirect_manual_ = true;
761 break;
762 default: // No proxy, or maybe kioslaverc syntax error.
763 break;
764 }
765 string_table_[PROXY_MODE] = mode;
766 } else if (key == "Proxy Config Script") {
767 string_table_[PROXY_AUTOCONF_URL] = value;
768 } else if (key == "httpProxy") {
769 AddProxy(PROXY_HTTP_HOST, value);
770 } else if (key == "httpsProxy") {
771 AddProxy(PROXY_HTTPS_HOST, value);
772 } else if (key == "ftpProxy") {
773 AddProxy(PROXY_FTP_HOST, value);
774 } else if (key == "socksProxy") {
775 // Older versions of KDE configure SOCKS in a weird way involving
776 // LD_PRELOAD and a library that intercepts network calls to SOCKSify
777 // them. We don't support it. KDE 4.8 added a proper SOCKS setting.
778 AddProxy(PROXY_SOCKS_HOST, value);
779 } else if (key == "ReversedException") {
780 // We count "true" or any nonzero number as true, otherwise false.
781 // A failure parsing the integer will also mean false.
782 reversed_bypass_list_ =
783 (value == "true" || StringToIntOrDefault(value, 0) != 0);
784 } else if (key == "NoProxyFor") {
785 AddHostList(PROXY_IGNORE_HOSTS, value);
786 } else if (key == "AuthMode") {
787 // Check for authentication, just so we can warn.
788 int mode = StringToIntOrDefault(value, 0);
789 if (mode) {
790 // ProxyConfig does not support authentication parameters, but
791 // Chrome will prompt for the password later. So we ignore this.
792 LOG(WARNING) <<
793 "Proxy authentication parameters ignored, see bug 16709";
794 }
795 }
796 }
797
ResolveIndirect(StringSetting key)798 void ResolveIndirect(StringSetting key) {
799 auto it = string_table_.find(key);
800 if (it != string_table_.end()) {
801 std::string value;
802 if (env_var_getter_->GetVar(it->second.c_str(), &value))
803 it->second = value;
804 else
805 string_table_.erase(it);
806 }
807 }
808
ResolveIndirectList(StringListSetting key)809 void ResolveIndirectList(StringListSetting key) {
810 auto it = strings_table_.find(key);
811 if (it != strings_table_.end()) {
812 std::string value;
813 if (!it->second.empty() &&
814 env_var_getter_->GetVar(it->second[0].c_str(), &value))
815 AddHostList(key, value);
816 else
817 strings_table_.erase(it);
818 }
819 }
820
821 // The settings in kioslaverc could occur in any order, but some affect
822 // others. Rather than read the whole file in and then query them in an
823 // order that allows us to handle that, we read the settings in whatever
824 // order they occur and do any necessary tweaking after we finish.
ResolveModeEffects()825 void ResolveModeEffects() {
826 if (indirect_manual_) {
827 ResolveIndirect(PROXY_HTTP_HOST);
828 ResolveIndirect(PROXY_HTTPS_HOST);
829 ResolveIndirect(PROXY_FTP_HOST);
830 ResolveIndirectList(PROXY_IGNORE_HOSTS);
831 }
832 if (auto_no_pac_) {
833 // Remove the PAC URL; we're not supposed to use it.
834 string_table_.erase(PROXY_AUTOCONF_URL);
835 }
836 }
837
838 // Reads kioslaverc one line at a time and calls AddKDESetting() to add
839 // each relevant name-value pair to the appropriate value table.
UpdateCachedSettings()840 void UpdateCachedSettings() {
841 base::FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
842 base::ScopedFILE input(base::OpenFile(kioslaverc, "r"));
843 if (!input.get())
844 return;
845 ResetCachedSettings();
846 bool in_proxy_settings = false;
847 bool line_too_long = false;
848 char line[BUFFER_SIZE];
849 // fgets() will return NULL on EOF or error.
850 while (fgets(line, sizeof(line), input.get())) {
851 // fgets() guarantees the line will be properly terminated.
852 size_t length = strlen(line);
853 if (!length)
854 continue;
855 // This should be true even with CRLF endings.
856 if (line[length - 1] != '\n') {
857 line_too_long = true;
858 continue;
859 }
860 if (line_too_long) {
861 // The previous line had no line ending, but this done does. This is
862 // the end of the line that was too long, so warn here and skip it.
863 LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
864 line_too_long = false;
865 continue;
866 }
867 // Remove the LF at the end, and the CR if there is one.
868 line[--length] = '\0';
869 if (length && line[length - 1] == '\r')
870 line[--length] = '\0';
871 // Now parse the line.
872 if (line[0] == '[') {
873 // Switching sections. All we care about is whether this is
874 // the (a?) proxy settings section, for both KDE3 and KDE4.
875 in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
876 } else if (in_proxy_settings) {
877 // A regular line, in the (a?) proxy settings section.
878 char* split = strchr(line, '=');
879 // Skip this line if it does not contain an = sign.
880 if (!split)
881 continue;
882 // Split the line on the = and advance |split|.
883 *(split++) = 0;
884 std::string key = line;
885 std::string value = split;
886 base::TrimWhitespaceASCII(key, base::TRIM_ALL, &key);
887 base::TrimWhitespaceASCII(value, base::TRIM_ALL, &value);
888 // Skip this line if the key name is empty.
889 if (key.empty())
890 continue;
891 // Is the value name localized?
892 if (key[key.length() - 1] == ']') {
893 // Find the matching bracket.
894 length = key.rfind('[');
895 // Skip this line if the localization indicator is malformed.
896 if (length == std::string::npos)
897 continue;
898 // Trim the localization indicator off.
899 key.resize(length);
900 // Remove any resulting trailing whitespace.
901 base::TrimWhitespaceASCII(key, base::TRIM_TRAILING, &key);
902 // Skip this line if the key name is now empty.
903 if (key.empty())
904 continue;
905 }
906 // Now fill in the tables.
907 AddKDESetting(key, value);
908 }
909 }
910 if (ferror(input.get()))
911 LOG(ERROR) << "error reading " << kioslaverc.value();
912 ResolveModeEffects();
913 }
914
915 // This is the callback from the debounce timer.
OnDebouncedNotification()916 void OnDebouncedNotification() {
917 DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
918 VLOG(1) << "inotify change notification for kioslaverc";
919 UpdateCachedSettings();
920 CHECK(notify_delegate_);
921 // Forward to a method on the proxy config service delegate object.
922 notify_delegate_->OnCheckProxyConfigSettings();
923 }
924
925 // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
926 // from the inotify file descriptor and starts up a debounce timer if
927 // an event for kioslaverc is seen.
OnChangeNotification()928 void OnChangeNotification() {
929 DCHECK_GE(inotify_fd_, 0);
930 DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
931 char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
932 bool kioslaverc_touched = false;
933 ssize_t r;
934 while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
935 // inotify returns variable-length structures, which is why we have
936 // this strange-looking loop instead of iterating through an array.
937 char* event_ptr = event_buf;
938 while (event_ptr < event_buf + r) {
939 inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
940 // The kernel always feeds us whole events.
941 CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
942 CHECK_LE(event->name + event->len, event_buf + r);
943 if (!strcmp(event->name, "kioslaverc"))
944 kioslaverc_touched = true;
945 // Advance the pointer just past the end of the filename.
946 event_ptr = event->name + event->len;
947 }
948 // We keep reading even if |kioslaverc_touched| is true to drain the
949 // inotify event queue.
950 }
951 if (!r)
952 // Instead of returning -1 and setting errno to EINVAL if there is not
953 // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
954 // new behavior (EINVAL) so we can reuse the code below.
955 errno = EINVAL;
956 if (errno != EAGAIN) {
957 PLOG(WARNING) << "error reading inotify file descriptor";
958 if (errno == EINVAL) {
959 // Our buffer is not large enough to read the next event. This should
960 // not happen (because its size is calculated to always be sufficiently
961 // large), but if it does we'd warn continuously since |inotify_fd_|
962 // would be forever ready to read. Close it and stop watching instead.
963 LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
964 inotify_watcher_.reset();
965 close(inotify_fd_);
966 inotify_fd_ = -1;
967 }
968 }
969 if (kioslaverc_touched) {
970 LOG(ERROR) << "kioslaverc_touched";
971 // We don't use Reset() because the timer may not yet be running.
972 // (In that case Stop() is a no-op.)
973 debounce_timer_->Stop();
974 debounce_timer_->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(
975 kDebounceTimeoutMilliseconds), this,
976 &SettingGetterImplKDE::OnDebouncedNotification);
977 }
978 }
979
980 typedef std::map<StringSetting, std::string> string_map_type;
981 typedef std::map<StringListSetting,
982 std::vector<std::string> > strings_map_type;
983
984 int inotify_fd_;
985 std::unique_ptr<base::FileDescriptorWatcher::Controller> inotify_watcher_;
986 ProxyConfigServiceLinux::Delegate* notify_delegate_;
987 std::unique_ptr<base::OneShotTimer> debounce_timer_;
988 base::FilePath kde_config_dir_;
989 bool indirect_manual_;
990 bool auto_no_pac_;
991 bool reversed_bypass_list_;
992 // We don't own |env_var_getter_|. It's safe to hold a pointer to it, since
993 // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
994 // same lifetime.
995 base::Environment* env_var_getter_;
996
997 // We cache these settings whenever we re-read the kioslaverc file.
998 string_map_type string_table_;
999 strings_map_type strings_table_;
1000
1001 // Task runner for doing blocking file IO on, as well as handling inotify
1002 // events on.
1003 scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
1004
1005 DISALLOW_COPY_AND_ASSIGN(SettingGetterImplKDE);
1006 };
1007 #endif
1008
1009 } // namespace
1010
GetProxyFromSettings(SettingGetter::StringSetting host_key,ProxyServer * result_server)1011 bool ProxyConfigServiceLinux::Delegate::GetProxyFromSettings(
1012 SettingGetter::StringSetting host_key,
1013 ProxyServer* result_server) {
1014 std::string host;
1015 if (!setting_getter_->GetString(host_key, &host) || host.empty()) {
1016 // Unset or empty.
1017 return false;
1018 }
1019 // Check for an optional port.
1020 int port = 0;
1021 SettingGetter::IntSetting port_key =
1022 SettingGetter::HostSettingToPortSetting(host_key);
1023 setting_getter_->GetInt(port_key, &port);
1024 if (port != 0) {
1025 // If a port is set and non-zero:
1026 host += ":" + base::NumberToString(port);
1027 }
1028
1029 // gsettings settings do not appear to distinguish between SOCKS version. We
1030 // default to version 5. For more information on this policy decision, see:
1031 // http://code.google.com/p/chromium/issues/detail?id=55912#c2
1032 ProxyServer::Scheme scheme = (host_key == SettingGetter::PROXY_SOCKS_HOST) ?
1033 ProxyServer::SCHEME_SOCKS5 : ProxyServer::SCHEME_HTTP;
1034 host = FixupProxyHostScheme(scheme, host);
1035 ProxyServer proxy_server = ProxyServer::FromURI(host,
1036 ProxyServer::SCHEME_HTTP);
1037 if (proxy_server.is_valid()) {
1038 *result_server = proxy_server;
1039 return true;
1040 }
1041 return false;
1042 }
1043
1044 base::Optional<ProxyConfigWithAnnotation>
GetConfigFromSettings()1045 ProxyConfigServiceLinux::Delegate::GetConfigFromSettings() {
1046 ProxyConfig config;
1047
1048 std::string mode;
1049 if (!setting_getter_->GetString(SettingGetter::PROXY_MODE, &mode)) {
1050 // We expect this to always be set, so if we don't see it then we probably
1051 // have a gsettings problem, and so we don't have a valid proxy config.
1052 return base::nullopt;
1053 }
1054 if (mode == "none") {
1055 // Specifically specifies no proxy.
1056 return ProxyConfigWithAnnotation(
1057 config, NetworkTrafficAnnotationTag(traffic_annotation_));
1058 }
1059
1060 if (mode == "auto") {
1061 // Automatic proxy config.
1062 std::string pac_url_str;
1063 if (setting_getter_->GetString(SettingGetter::PROXY_AUTOCONF_URL,
1064 &pac_url_str)) {
1065 if (!pac_url_str.empty()) {
1066 // If the PAC URL is actually a file path, then put file:// in front.
1067 if (pac_url_str[0] == '/')
1068 pac_url_str = "file://" + pac_url_str;
1069 GURL pac_url(pac_url_str);
1070 if (!pac_url.is_valid())
1071 return base::nullopt;
1072 config.set_pac_url(pac_url);
1073 return ProxyConfigWithAnnotation(
1074 config, NetworkTrafficAnnotationTag(traffic_annotation_));
1075 }
1076 }
1077 config.set_auto_detect(true);
1078 return ProxyConfigWithAnnotation(
1079 config, NetworkTrafficAnnotationTag(traffic_annotation_));
1080 }
1081
1082 if (mode != "manual") {
1083 // Mode is unrecognized.
1084 return base::nullopt;
1085 }
1086 bool use_http_proxy;
1087 if (setting_getter_->GetBool(SettingGetter::PROXY_USE_HTTP_PROXY,
1088 &use_http_proxy)
1089 && !use_http_proxy) {
1090 // Another master switch for some reason. If set to false, then no
1091 // proxy. But we don't panic if the key doesn't exist.
1092 return ProxyConfigWithAnnotation(
1093 config, NetworkTrafficAnnotationTag(traffic_annotation_));
1094 }
1095
1096 bool same_proxy = false;
1097 // Indicates to use the http proxy for all protocols. This one may
1098 // not exist (presumably on older versions); we assume false in that
1099 // case.
1100 setting_getter_->GetBool(SettingGetter::PROXY_USE_SAME_PROXY,
1101 &same_proxy);
1102
1103 ProxyServer proxy_for_http;
1104 ProxyServer proxy_for_https;
1105 ProxyServer proxy_for_ftp;
1106 ProxyServer socks_proxy; // (socks)
1107
1108 // This counts how many of the above ProxyServers were defined and valid.
1109 size_t num_proxies_specified = 0;
1110
1111 // Extract the per-scheme proxies. If we failed to parse it, or no proxy was
1112 // specified for the scheme, then the resulting ProxyServer will be invalid.
1113 if (GetProxyFromSettings(SettingGetter::PROXY_HTTP_HOST, &proxy_for_http))
1114 num_proxies_specified++;
1115 if (GetProxyFromSettings(SettingGetter::PROXY_HTTPS_HOST, &proxy_for_https))
1116 num_proxies_specified++;
1117 if (GetProxyFromSettings(SettingGetter::PROXY_FTP_HOST, &proxy_for_ftp))
1118 num_proxies_specified++;
1119 if (GetProxyFromSettings(SettingGetter::PROXY_SOCKS_HOST, &socks_proxy))
1120 num_proxies_specified++;
1121
1122 if (same_proxy) {
1123 if (proxy_for_http.is_valid()) {
1124 // Use the http proxy for all schemes.
1125 config.proxy_rules().type = ProxyConfig::ProxyRules::Type::PROXY_LIST;
1126 config.proxy_rules().single_proxies.SetSingleProxyServer(proxy_for_http);
1127 }
1128 } else if (num_proxies_specified > 0) {
1129 if (socks_proxy.is_valid() && num_proxies_specified == 1) {
1130 // If the only proxy specified was for SOCKS, use it for all schemes.
1131 config.proxy_rules().type = ProxyConfig::ProxyRules::Type::PROXY_LIST;
1132 config.proxy_rules().single_proxies.SetSingleProxyServer(socks_proxy);
1133 } else {
1134 // Otherwise use the indicated proxies per-scheme.
1135 config.proxy_rules().type =
1136 ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME;
1137 config.proxy_rules().proxies_for_http.SetSingleProxyServer(
1138 proxy_for_http);
1139 config.proxy_rules().proxies_for_https.SetSingleProxyServer(
1140 proxy_for_https);
1141 config.proxy_rules().proxies_for_ftp.SetSingleProxyServer(proxy_for_ftp);
1142 config.proxy_rules().fallback_proxies.SetSingleProxyServer(socks_proxy);
1143 }
1144 }
1145
1146 if (config.proxy_rules().empty()) {
1147 // Manual mode but we couldn't parse any rules.
1148 return base::nullopt;
1149 }
1150
1151 // Check for authentication, just so we can warn.
1152 bool use_auth = false;
1153 setting_getter_->GetBool(SettingGetter::PROXY_USE_AUTHENTICATION,
1154 &use_auth);
1155 if (use_auth) {
1156 // ProxyConfig does not support authentication parameters, but
1157 // Chrome will prompt for the password later. So we ignore
1158 // /system/http_proxy/*auth* settings.
1159 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
1160 }
1161
1162 // Now the bypass list.
1163 std::vector<std::string> ignore_hosts_list;
1164 config.proxy_rules().bypass_rules.Clear();
1165 if (setting_getter_->GetStringList(SettingGetter::PROXY_IGNORE_HOSTS,
1166 &ignore_hosts_list)) {
1167 for (const auto& rule : ignore_hosts_list) {
1168 config.proxy_rules().bypass_rules.AddRuleFromString(rule);
1169 }
1170 }
1171
1172 if (setting_getter_->UseSuffixMatching()) {
1173 RewriteRulesForSuffixMatching(&config.proxy_rules().bypass_rules);
1174 }
1175
1176 // Note that there are no settings with semantics corresponding to
1177 // bypass of local names in GNOME. In KDE, "<local>" is supported
1178 // as a hostname rule.
1179
1180 // KDE allows one to reverse the bypass rules.
1181 config.proxy_rules().reverse_bypass = setting_getter_->BypassListIsReversed();
1182
1183 return ProxyConfigWithAnnotation(
1184 config, NetworkTrafficAnnotationTag(traffic_annotation_));
1185 }
1186
Delegate(std::unique_ptr<base::Environment> env_var_getter,base::Optional<std::unique_ptr<SettingGetter>> setting_getter,base::Optional<NetworkTrafficAnnotationTag> traffic_annotation)1187 ProxyConfigServiceLinux::Delegate::Delegate(
1188 std::unique_ptr<base::Environment> env_var_getter,
1189 base::Optional<std::unique_ptr<SettingGetter>> setting_getter,
1190 base::Optional<NetworkTrafficAnnotationTag> traffic_annotation)
1191 : env_var_getter_(std::move(env_var_getter)) {
1192 if (traffic_annotation) {
1193 traffic_annotation_ =
1194 MutableNetworkTrafficAnnotationTag(traffic_annotation.value());
1195 }
1196
1197 if (setting_getter) {
1198 setting_getter_ = std::move(setting_getter.value());
1199 return;
1200 }
1201
1202 // Figure out which SettingGetterImpl to use, if any.
1203 switch (base::nix::GetDesktopEnvironment(env_var_getter_.get())) {
1204 case base::nix::DESKTOP_ENVIRONMENT_CINNAMON:
1205 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
1206 case base::nix::DESKTOP_ENVIRONMENT_PANTHEON:
1207 case base::nix::DESKTOP_ENVIRONMENT_UNITY:
1208 #if defined(USE_GIO)
1209 {
1210 std::unique_ptr<SettingGetterImplGSettings> gs_getter(
1211 new SettingGetterImplGSettings());
1212 // We have to load symbols and check the GNOME version in use to decide
1213 // if we should use the gsettings getter. See CheckVersion().
1214 if (gs_getter->CheckVersion(env_var_getter_.get()))
1215 setting_getter_ = std::move(gs_getter);
1216 }
1217 #endif
1218 break;
1219 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
1220 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
1221 case base::nix::DESKTOP_ENVIRONMENT_KDE5:
1222 #if !defined(OS_BSD)
1223 setting_getter_.reset(new SettingGetterImplKDE(env_var_getter_.get()));
1224 break;
1225 #endif
1226 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
1227 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
1228 break;
1229 }
1230 }
1231
SetUpAndFetchInitialConfig(const scoped_refptr<base::SingleThreadTaskRunner> & glib_task_runner,const scoped_refptr<base::SequencedTaskRunner> & main_task_runner,const NetworkTrafficAnnotationTag & traffic_annotation)1232 void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
1233 const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
1234 const scoped_refptr<base::SequencedTaskRunner>& main_task_runner,
1235 const NetworkTrafficAnnotationTag& traffic_annotation) {
1236 traffic_annotation_ = MutableNetworkTrafficAnnotationTag(traffic_annotation);
1237
1238 // We should be running on the default glib main loop thread right
1239 // now. gsettings can only be accessed from this thread.
1240 DCHECK(glib_task_runner->RunsTasksInCurrentSequence());
1241 glib_task_runner_ = glib_task_runner;
1242 main_task_runner_ = main_task_runner;
1243
1244 // If we are passed a NULL |main_task_runner|, then don't set up proxy
1245 // setting change notifications. This should not be the usual case but is
1246 // intended to/ simplify test setups.
1247 if (!main_task_runner_.get())
1248 VLOG(1) << "Monitoring of proxy setting changes is disabled";
1249
1250 // Fetch and cache the current proxy config. The config is left in
1251 // cached_config_, where GetLatestProxyConfig() running on the main TaskRunner
1252 // will expect to find it. This is safe to do because we return
1253 // before this ProxyConfigServiceLinux is passed on to
1254 // the ConfiguredProxyResolutionService.
1255
1256 // Note: It would be nice to prioritize environment variables
1257 // and only fall back to gsettings if env vars were unset. But
1258 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1259 // does so even if the proxy mode is set to auto, which would
1260 // mislead us.
1261
1262 cached_config_ = base::nullopt;
1263 if (setting_getter_ && setting_getter_->Init(glib_task_runner)) {
1264 cached_config_ = GetConfigFromSettings();
1265 }
1266 if (cached_config_) {
1267 VLOG(1) << "Obtained proxy settings from annotation hash code "
1268 << cached_config_->traffic_annotation().unique_id_hash_code;
1269
1270 // If gsettings proxy mode is "none", meaning direct, then we take
1271 // that to be a valid config and will not check environment
1272 // variables. The alternative would have been to look for a proxy
1273 // wherever we can find one.
1274
1275 // Keep a copy of the config for use from this thread for
1276 // comparison with updated settings when we get notifications.
1277 reference_config_ = cached_config_;
1278
1279 // We only set up notifications if we have the main and file loops
1280 // available. We do this after getting the initial configuration so that we
1281 // don't have to worry about cancelling it if the initial fetch above fails.
1282 // Note that setting up notifications has the side effect of simulating a
1283 // change, so that we won't lose any updates that may have happened after
1284 // the initial fetch and before setting up notifications. We'll detect the
1285 // common case of no changes in OnCheckProxyConfigSettings() (or sooner) and
1286 // ignore it.
1287 if (main_task_runner.get()) {
1288 scoped_refptr<base::SequencedTaskRunner> required_loop =
1289 setting_getter_->GetNotificationTaskRunner();
1290 if (!required_loop.get() || required_loop->RunsTasksInCurrentSequence()) {
1291 // In this case we are already on an acceptable thread.
1292 SetUpNotifications();
1293 } else {
1294 // Post a task to set up notifications. We don't wait for success.
1295 required_loop->PostTask(
1296 FROM_HERE,
1297 base::BindOnce(
1298 &ProxyConfigServiceLinux::Delegate::SetUpNotifications, this));
1299 }
1300 }
1301 }
1302
1303 if (!cached_config_) {
1304 // We fall back on environment variables.
1305 //
1306 // Consulting environment variables doesn't need to be done from the
1307 // default glib main loop, but it's a tiny enough amount of work.
1308 cached_config_ = GetConfigFromEnv();
1309 if (cached_config_) {
1310 VLOG(1) << "Obtained proxy settings from environment variables";
1311 }
1312 }
1313 }
1314
1315 // Depending on the SettingGetter in use, this method will be called
1316 // on either the UI thread (GSettings) or the file thread (KDE).
SetUpNotifications()1317 void ProxyConfigServiceLinux::Delegate::SetUpNotifications() {
1318 scoped_refptr<base::SequencedTaskRunner> required_loop =
1319 setting_getter_->GetNotificationTaskRunner();
1320 DCHECK(!required_loop.get() || required_loop->RunsTasksInCurrentSequence());
1321 if (!setting_getter_->SetUpNotifications(this))
1322 LOG(ERROR) << "Unable to set up proxy configuration change notifications";
1323 }
1324
AddObserver(Observer * observer)1325 void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) {
1326 observers_.AddObserver(observer);
1327 }
1328
RemoveObserver(Observer * observer)1329 void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) {
1330 observers_.RemoveObserver(observer);
1331 }
1332
1333 ProxyConfigService::ConfigAvailability
GetLatestProxyConfig(ProxyConfigWithAnnotation * config)1334 ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig(
1335 ProxyConfigWithAnnotation* config) {
1336 // This is called from the main TaskRunner.
1337 DCHECK(!main_task_runner_.get() ||
1338 main_task_runner_->RunsTasksInCurrentSequence());
1339
1340 // Simply return the last proxy configuration that glib_default_loop
1341 // notified us of.
1342 *config = GetConfigOrDirect(cached_config_);
1343
1344 // We return CONFIG_VALID to indicate that *config was filled in. It is always
1345 // going to be available since we initialized eagerly on the UI thread.
1346 // TODO(eroman): do lazy initialization instead, so we no longer need
1347 // to construct ProxyConfigServiceLinux on the UI thread.
1348 // In which case, we may return false here.
1349 return CONFIG_VALID;
1350 }
1351
1352 // Depending on the SettingGetter in use, this method will be called
1353 // on either the UI thread (GSettings) or the file thread (KDE).
OnCheckProxyConfigSettings()1354 void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
1355 scoped_refptr<base::SequencedTaskRunner> required_loop =
1356 setting_getter_->GetNotificationTaskRunner();
1357 DCHECK(!required_loop.get() || required_loop->RunsTasksInCurrentSequence());
1358 base::Optional<ProxyConfigWithAnnotation> new_config =
1359 GetConfigFromSettings();
1360
1361 // See if it is different from what we had before.
1362 if (new_config.has_value() != reference_config_.has_value() ||
1363 (new_config.has_value() &&
1364 !new_config->value().Equals(reference_config_->value()))) {
1365 // Post a task to the main TaskRunner with the new configuration, so it can
1366 // update |cached_config_|.
1367 main_task_runner_->PostTask(
1368 FROM_HERE,
1369 base::BindOnce(&ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1370 this, new_config));
1371 // Update the thread-private copy in |reference_config_| as well.
1372 reference_config_ = new_config;
1373 } else {
1374 VLOG(1) << "Detected no-op change to proxy settings. Doing nothing.";
1375 }
1376 }
1377
SetNewProxyConfig(const base::Optional<ProxyConfigWithAnnotation> & new_config)1378 void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1379 const base::Optional<ProxyConfigWithAnnotation>& new_config) {
1380 DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
1381 VLOG(1) << "Proxy configuration changed";
1382 cached_config_ = new_config;
1383 for (auto& observer : observers_) {
1384 observer.OnProxyConfigChanged(GetConfigOrDirect(new_config),
1385 ProxyConfigService::CONFIG_VALID);
1386 }
1387 }
1388
PostDestroyTask()1389 void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
1390 if (!setting_getter_)
1391 return;
1392
1393 scoped_refptr<base::SequencedTaskRunner> shutdown_loop =
1394 setting_getter_->GetNotificationTaskRunner();
1395 if (!shutdown_loop.get() || shutdown_loop->RunsTasksInCurrentSequence()) {
1396 // Already on the right thread, call directly.
1397 // This is the case for the unittests.
1398 OnDestroy();
1399 } else {
1400 // Post to shutdown thread. Note that on browser shutdown, we may quit
1401 // this MessageLoop and exit the program before ever running this.
1402 shutdown_loop->PostTask(
1403 FROM_HERE,
1404 base::BindOnce(&ProxyConfigServiceLinux::Delegate::OnDestroy, this));
1405 }
1406 }
OnDestroy()1407 void ProxyConfigServiceLinux::Delegate::OnDestroy() {
1408 scoped_refptr<base::SequencedTaskRunner> shutdown_loop =
1409 setting_getter_->GetNotificationTaskRunner();
1410 DCHECK(!shutdown_loop.get() || shutdown_loop->RunsTasksInCurrentSequence());
1411 setting_getter_->ShutDown();
1412 }
1413
ProxyConfigServiceLinux()1414 ProxyConfigServiceLinux::ProxyConfigServiceLinux()
1415 : delegate_(new Delegate(base::Environment::Create(),
1416 base::nullopt,
1417 base::nullopt)) {}
1418
~ProxyConfigServiceLinux()1419 ProxyConfigServiceLinux::~ProxyConfigServiceLinux() {
1420 delegate_->PostDestroyTask();
1421 }
1422
ProxyConfigServiceLinux(std::unique_ptr<base::Environment> env_var_getter,const NetworkTrafficAnnotationTag & traffic_annotation)1423 ProxyConfigServiceLinux::ProxyConfigServiceLinux(
1424 std::unique_ptr<base::Environment> env_var_getter,
1425 const NetworkTrafficAnnotationTag& traffic_annotation)
1426 : delegate_(new Delegate(std::move(env_var_getter),
1427 base::nullopt,
1428 traffic_annotation)) {}
1429
ProxyConfigServiceLinux(std::unique_ptr<base::Environment> env_var_getter,SettingGetter * setting_getter,const NetworkTrafficAnnotationTag & traffic_annotation)1430 ProxyConfigServiceLinux::ProxyConfigServiceLinux(
1431 std::unique_ptr<base::Environment> env_var_getter,
1432 SettingGetter* setting_getter,
1433 const NetworkTrafficAnnotationTag& traffic_annotation)
1434 : delegate_(new Delegate(std::move(env_var_getter),
1435 base::WrapUnique(setting_getter),
1436 traffic_annotation)) {}
1437
AddObserver(Observer * observer)1438 void ProxyConfigServiceLinux::AddObserver(Observer* observer) {
1439 delegate_->AddObserver(observer);
1440 }
1441
RemoveObserver(Observer * observer)1442 void ProxyConfigServiceLinux::RemoveObserver(Observer* observer) {
1443 delegate_->RemoveObserver(observer);
1444 }
1445
1446 ProxyConfigService::ConfigAvailability
GetLatestProxyConfig(ProxyConfigWithAnnotation * config)1447 ProxyConfigServiceLinux::GetLatestProxyConfig(
1448 ProxyConfigWithAnnotation* config) {
1449 return delegate_->GetLatestProxyConfig(config);
1450 }
1451
1452 } // namespace net
1453