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