File: proxy_resolution_service_provider.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (283 lines) | stat: -rw-r--r-- 10,573 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/dbus/proxy_resolution_service_provider.h"

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/ash/net/system_proxy_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_types.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/storage_partition.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/base/net_errors.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/proxy_lookup_client.mojom.h"
#include "url/gurl.h"

namespace ash {

namespace {

// The proxy result to return when resolution fails.
// It is up to the D-Bus caller to determine how to interpret this in the
// case of errors, but DIRECT is usually a good fallback.
//
// TODO(eroman): This doesn't properly convey the semantics of
// ERR_MANDATORY_PROXY_CONFIGURATION_FAILED. For this error, consumers
// should fail the entire network request rather than falling back to
// DIRECT connections, to behave the same as the browser.
const char kProxyInfoOnFailure[] = "DIRECT";

class ProxyLookupRequest : public network::mojom::ProxyLookupClient {
 public:
  // Sends a proxy lookup request to the Network Service and invokes
  // |notify_callback| on completion. Caller should not manage the memory of
  // |this|, as it will delete itself on completion.
  ProxyLookupRequest(
      network::mojom::NetworkContext* network_context,
      const GURL& source_url,
      const net::NetworkAnonymizationKey& network_anonymization_key,
      ProxyResolutionServiceProvider::NotifyCallback notify_callback,
      chromeos::SystemProxyOverride system_proxy_override)
      : notify_callback_(std::move(notify_callback)),
        system_proxy_override_(system_proxy_override) {
    mojo::PendingRemote<network::mojom::ProxyLookupClient> proxy_lookup_client =
        receiver_.BindNewPipeAndPassRemote();
    receiver_.set_disconnect_handler(
        base::BindOnce(&ProxyLookupRequest::OnProxyLookupComplete,
                       base::Unretained(this), net::ERR_ABORTED, std::nullopt));

    network_context->LookUpProxyForURL(source_url, network_anonymization_key,
                                       std::move(proxy_lookup_client));
  }

  ProxyLookupRequest(const ProxyLookupRequest&) = delete;
  ProxyLookupRequest& operator=(const ProxyLookupRequest&) = delete;

  ~ProxyLookupRequest() override = default;

  void OnProxyLookupComplete(
      int32_t net_error,
      const std::optional<net::ProxyInfo>& proxy_info) override {
    DCHECK_EQ(net_error == net::OK, proxy_info.has_value());

    std::string error;
    std::string result;

    if (!proxy_info) {
      error = net::ErrorToString(net_error);
      result = kProxyInfoOnFailure;
    } else if (proxy_info->ContainsMultiProxyChain()) {
      // Multi-proxy chains cannot be represented as a PAC string.
      error = net::ErrorToString(net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED);
      result = kProxyInfoOnFailure;
    } else {
      result = proxy_info->ToPacString();
      if (!proxy_info->is_empty() && !proxy_info->is_direct() &&
          proxy_info->proxy_chain()
              .GetProxyServer(/*chain_index=*/0)
              .is_http()) {
        AppendSystemProxyIfActive(&result);
      }
    }
    receiver_.reset();
    std::move(notify_callback_).Run(error, result);
    delete this;
  }

 private:
  // Appends the System-proxy address, if active, to the list of existing
  // proxies, which can still be used by system services as a fallback if the
  // local proxy connection fails. System-proxy itself does proxy resolution
  // trough the same Chrome proxy resolution service to connect to the
  // remote proxy server. The availability of this feature is controlled by the
  // |SystemProxySettings| policy and the feature flag
  // `features::kSystemProxyForSystemServices`.
  void AppendSystemProxyIfActive(std::string* pac_proxy_list) {
    SystemProxyManager* system_proxy_manager = SystemProxyManager::Get();
    // |system_proxy_manager| may be missing in tests.
    if (!system_proxy_manager)
      return;

    std::string system_proxy_pac =
        system_proxy_manager->SystemServicesProxyPacString(
            system_proxy_override_);
    if (system_proxy_pac.empty())
      return;

    *pac_proxy_list = base::StringPrintf("%s; %s", system_proxy_pac.c_str(),
                                         pac_proxy_list->c_str());
  }

  mojo::Receiver<network::mojom::ProxyLookupClient> receiver_{this};
  ProxyResolutionServiceProvider::NotifyCallback notify_callback_;
  chromeos::SystemProxyOverride system_proxy_override_;
};

}  // namespace

ProxyResolutionServiceProvider::ProxyResolutionServiceProvider()
    : origin_thread_(base::SingleThreadTaskRunner::GetCurrentDefault()),
      network_anonymization_key_(
          net::NetworkAnonymizationKey::CreateTransient()) {}

ProxyResolutionServiceProvider::~ProxyResolutionServiceProvider() {
  DCHECK(OnOriginThread());
}

void ProxyResolutionServiceProvider::Start(
    scoped_refptr<dbus::ExportedObject> exported_object) {
  DCHECK(OnOriginThread());
  exported_object_ = exported_object;
  VLOG(1) << "ProxyResolutionServiceProvider started";
  exported_object_->ExportMethod(
      chromeos::kNetworkProxyServiceInterface,
      chromeos::kNetworkProxyServiceResolveProxyMethod,
      base::BindRepeating(&ProxyResolutionServiceProvider::DbusResolveProxy,
                          weak_ptr_factory_.GetWeakPtr()),
      base::BindOnce(&ProxyResolutionServiceProvider::OnExported,
                     weak_ptr_factory_.GetWeakPtr()));
}

bool ProxyResolutionServiceProvider::OnOriginThread() {
  return origin_thread_->BelongsToCurrentThread();
}

void ProxyResolutionServiceProvider::OnExported(
    const std::string& interface_name,
    const std::string& method_name,
    bool success) {
  if (success)
    VLOG(1) << "Method exported: " << interface_name << "." << method_name;
  else
    LOG(ERROR) << "Failed to export " << interface_name << "." << method_name;
}

void ProxyResolutionServiceProvider::DbusResolveProxy(
    dbus::MethodCall* method_call,
    dbus::ExportedObject::ResponseSender response_sender) {
  DCHECK(OnOriginThread());

  VLOG(1) << "Handling method call: " << method_call->ToString();
  dbus::MessageReader reader(method_call);
  std::string source_url;
  if (!reader.PopString(&source_url)) {
    LOG(ERROR) << "Method call lacks source URL: " << method_call->ToString();
    std::move(response_sender)
        .Run(dbus::ErrorResponse::FromMethodCall(
            method_call, DBUS_ERROR_INVALID_ARGS, "No source URL string arg"));
    return;
  }

  // Read `chromeos::SystemProxyOverride` option.
  int int_state = 0;
  if (!reader.PopInt32(&int_state)) {
    VLOG(1) << "No SystemProxyOverride option specified.";
  }
  chromeos::SystemProxyOverride system_proxy_override;

  if (int_state < 0 || int_state > chromeos::SystemProxyOverride::kOptOut) {
    LOG(ERROR) << "Invalid value for SystemProxyOverride " << int_state;
    // Fallback to default state,
    system_proxy_override = chromeos::SystemProxyOverride::kDefault;
  } else {
    system_proxy_override =
        static_cast<chromeos::SystemProxyOverride>(int_state);
  }

  std::unique_ptr<dbus::Response> response =
      dbus::Response::FromMethodCall(method_call);

  NotifyCallback notify_dbus_callback =
      base::BindOnce(&ProxyResolutionServiceProvider::NotifyProxyResolved,
                     weak_ptr_factory_.GetWeakPtr(), std::move(response),
                     std::move(response_sender));

  ResolveProxyInternal(source_url, std::move(notify_dbus_callback),
                       system_proxy_override);
}

void ProxyResolutionServiceProvider::ResolveProxyInternal(
    const std::string& source_url,
    NotifyCallback callback,
    chromeos::SystemProxyOverride system_proxy_override) {
  auto* network_context = GetNetworkContext();

  if (!network_context) {
    std::move(callback).Run("No NetworkContext", kProxyInfoOnFailure);
    return;
  }

  GURL url(source_url);
  if (!url.is_valid()) {
    std::move(callback).Run("Invalid URL", kProxyInfoOnFailure);
    return;
  }

  VLOG(1) << "Starting network proxy resolution for " << url;
  new ProxyLookupRequest(network_context, url, network_anonymization_key_,
                         std::move(callback), system_proxy_override);
}

void ProxyResolutionServiceProvider::NotifyProxyResolved(
    std::unique_ptr<dbus::Response> response,
    dbus::ExportedObject::ResponseSender response_sender,
    const std::string& error,
    const std::string& pac_string) {
  DCHECK(OnOriginThread());

  // Reply to the original D-Bus method call.
  dbus::MessageWriter writer(response.get());
  writer.AppendString(pac_string);
  writer.AppendString(error);
  std::move(response_sender).Run(std::move(response));
}

network::mojom::NetworkContext*
ProxyResolutionServiceProvider::GetNetworkContext() {
  if (use_network_context_for_test_)
    return network_context_for_test_;

  // TODO(eroman): Instead of retrieving the profile globally (which could be in
  // a variety of states during startup/shutdown), pass the BrowserContext in as
  // a dependency.

  // Can be the profile of the primary user logged in the session or the profile
  // associated with the sign-in screen.
  Profile* profile = nullptr;
  auto* primary_user = user_manager::UserManager::Get()->GetPrimaryUser();
  if (primary_user) {
    profile = Profile::FromBrowserContext(
        ash::BrowserContextHelper::Get()->GetBrowserContextByUser(
            primary_user));
  }

  if (!profile) {
    profile = ProfileManager::GetActiveUserProfile();
    if (!profile || !ash::IsSigninBrowserContext(profile)) {
      return nullptr;
    }
  }

  auto* storage_partition = profile->GetDefaultStoragePartition();

  if (!storage_partition)
    return nullptr;

  return storage_partition->GetNetworkContext();
}

}  // namespace ash