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