1 // Copyright 2017 The Chromium OS 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 <brillo/http/http_proxy.h>
6
7 #include <memory>
8 #include <string>
9 #include <vector>
10
11 #include <base/bind.h>
12 #include <base/callback.h>
13 #include <base/logging.h>
14 #include <base/strings/string_tokenizer.h>
15 #include <base/strings/string_util.h>
16 #include <brillo/http/http_transport.h>
17 #include <chromeos/dbus/service_constants.h>
18 #include <dbus/bus.h>
19 #include <dbus/message.h>
20 #include <dbus/object_proxy.h>
21
22 namespace {
ParseProxyInfo(dbus::Response * response,std::vector<std::string> * proxies_out)23 bool ParseProxyInfo(dbus::Response* response,
24 std::vector<std::string>* proxies_out) {
25 DCHECK(proxies_out);
26 if (!response) {
27 LOG(ERROR) << chromeos::kNetworkProxyServiceName << " D-Bus call to "
28 << chromeos::kNetworkProxyServiceResolveProxyMethod
29 << " failed";
30 proxies_out->assign({brillo::http::kDirectProxy});
31 return false;
32 }
33 dbus::MessageReader reader(response);
34 std::string proxy_info;
35 std::string proxy_err;
36 if (!reader.PopString(&proxy_info) || !reader.PopString(&proxy_err)) {
37 LOG(ERROR) << chromeos::kNetworkProxyServiceName << " D-Bus call to "
38 << chromeos::kNetworkProxyServiceResolveProxyMethod
39 << " returned an invalid D-Bus response";
40 proxies_out->assign({brillo::http::kDirectProxy});
41 return false;
42 }
43 if (!proxy_err.empty()) {
44 // This case occurs when on the Chrome side of things it can't connect to
45 // the proxy resolver service, we just let this fall through and will end
46 // up returning success with only the direct proxy listed.
47 LOG(WARNING) << "Got error resolving proxy: " << proxy_err;
48 }
49
50 base::StringTokenizer toker(proxy_info, ";");
51 while (toker.GetNext()) {
52 std::string token = toker.token();
53 base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
54
55 // Start by finding the first space (if any).
56 std::string::iterator space;
57 for (space = ++token.begin(); space != token.end(); ++space) {
58 if (base::IsAsciiWhitespace(*space)) {
59 break;
60 }
61 }
62
63 std::string scheme = base::ToLowerASCII(std::string(token.begin(), space));
64 // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
65 if (scheme == "socks") {
66 scheme += "4";
67 } else if (scheme == "proxy") {
68 scheme = "http";
69 } else if (scheme != "https" && scheme != "socks4" && scheme != "socks5" &&
70 scheme != "direct") {
71 LOG(ERROR) << "Invalid proxy scheme found of: " << scheme;
72 continue;
73 }
74
75 std::string host_and_port = std::string(space, token.end());
76 base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
77 if (scheme != "direct" && host_and_port.empty()) {
78 LOG(ERROR) << "Invalid host/port information for proxy: " << token;
79 continue;
80 }
81 proxies_out->push_back(scheme + "://" + host_and_port);
82 }
83 // Always add the direct proxy (i.e. no proxy) as a last resort if not there.
84 if (proxies_out->empty() ||
85 proxies_out->back() != brillo::http::kDirectProxy) {
86 proxies_out->push_back(brillo::http::kDirectProxy);
87 }
88 return true;
89 }
90
OnResolveProxy(const brillo::http::GetChromeProxyServersCallback & callback,dbus::Response * response)91 void OnResolveProxy(const brillo::http::GetChromeProxyServersCallback& callback,
92 dbus::Response* response) {
93 std::vector<std::string> proxies;
94 bool result = ParseProxyInfo(response, &proxies);
95 callback.Run(result, std::move(proxies));
96 }
97 } // namespace
98
99 namespace brillo {
100 namespace http {
101
GetChromeProxyServers(scoped_refptr<dbus::Bus> bus,const std::string & url,std::vector<std::string> * proxies_out)102 bool GetChromeProxyServers(scoped_refptr<dbus::Bus> bus, const std::string& url,
103 std::vector<std::string>* proxies_out) {
104 dbus::ObjectProxy* proxy =
105 bus->GetObjectProxy(chromeos::kNetworkProxyServiceName,
106 dbus::ObjectPath(chromeos::kNetworkProxyServicePath));
107 dbus::MethodCall method_call(
108 chromeos::kNetworkProxyServiceInterface,
109 chromeos::kNetworkProxyServiceResolveProxyMethod);
110 dbus::MessageWriter writer(&method_call);
111 writer.AppendString(url);
112 std::unique_ptr<dbus::Response> response = proxy->CallMethodAndBlock(
113 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
114 return ParseProxyInfo(response.get(), proxies_out);
115 }
116
GetChromeProxyServersAsync(scoped_refptr<dbus::Bus> bus,const std::string & url,const GetChromeProxyServersCallback & callback)117 void GetChromeProxyServersAsync(scoped_refptr<dbus::Bus> bus,
118 const std::string& url,
119 const GetChromeProxyServersCallback& callback) {
120 dbus::ObjectProxy* proxy = bus->GetObjectProxy(
121 chromeos::kNetworkProxyServiceName,
122 dbus::ObjectPath(chromeos::kNetworkProxyServicePath));
123 dbus::MethodCall method_call(
124 chromeos::kNetworkProxyServiceInterface,
125 chromeos::kNetworkProxyServiceResolveProxyMethod);
126 dbus::MessageWriter writer(&method_call);
127 writer.AppendString(url);
128 proxy->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
129 base::Bind(&OnResolveProxy, callback));
130 }
131
132 } // namespace http
133 } // namespace brillo
134