blob: 3374b5f87ba7a4eb8adeabc25c974d3bb497397e [file] [log] [blame]
Steve Fung6c34c252015-08-20 00:27:30 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000016
Steve Fung040dffd2015-02-10 14:25:00 -080017#include <sysexits.h>
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000018#include <unistd.h> // for isatty()
19
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000020#include <string>
Steve Fung040dffd2015-02-10 14:25:00 -080021#include <vector>
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000022
Steve Fung040dffd2015-02-10 14:25:00 -080023#include <base/cancelable_callback.h>
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000024#include <base/command_line.h>
25#include <base/files/file_util.h>
Steve Fung040dffd2015-02-10 14:25:00 -080026#include <base/memory/weak_ptr.h>
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000027#include <base/strings/string_number_conversions.h>
28#include <base/strings/string_tokenizer.h>
29#include <base/strings/string_util.h>
30#include <base/values.h>
Alex Vakulenko74dc6242015-10-13 09:23:34 -070031#include <brillo/daemons/dbus_daemon.h>
32#include <brillo/syslog_logging.h>
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000033
Steve Fung040dffd2015-02-10 14:25:00 -080034#include "libcrosservice/dbus-proxies.h"
35
36using std::unique_ptr;
37
38namespace {
39
40const char kLibCrosProxyResolvedSignalInterface[] =
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000041 "org.chromium.CrashReporterLibcrosProxyResolvedInterface";
Steve Fung040dffd2015-02-10 14:25:00 -080042const char kLibCrosProxyResolvedName[] = "ProxyResolved";
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000043const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000044const char kNoProxy[] = "direct://";
45
Steve Fung040dffd2015-02-10 14:25:00 -080046const int kTimeoutDefaultSeconds = 5;
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000047
48const char kHelp[] = "help";
49const char kQuiet[] = "quiet";
50const char kTimeout[] = "timeout";
51const char kVerbose[] = "verbose";
52// Help message to show when the --help command line switch is specified.
53const char kHelpMessage[] =
54 "Chromium OS Crash helper: proxy lister\n"
55 "\n"
56 "Available Switches:\n"
57 " --quiet Only print the proxies\n"
58 " --verbose Print additional messages even when not run from a TTY\n"
59 " --timeout=N Set timeout for browser resolving proxies (default is 5)\n"
60 " --help Show this help.\n";
61
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000062// Copied from src/update_engine/chrome_browser_proxy_resolver.cc
63// Parses the browser's answer for resolved proxies. It returns a
64// list of strings, each of which is a resolved proxy.
Steve Fung040dffd2015-02-10 14:25:00 -080065std::vector<std::string> ParseProxyString(const std::string& input) {
66 std::vector<std::string> ret;
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000067 // Some of this code taken from
68 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
69 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
70 base::StringTokenizer entry_tok(input, ";");
71 while (entry_tok.GetNext()) {
72 std::string token = entry_tok.token();
73 base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
74
75 // Start by finding the first space (if any).
76 std::string::iterator space;
77 for (space = token.begin(); space != token.end(); ++space) {
Alex Vakulenkoea05ff92016-01-20 07:53:57 -080078 if (base::IsAsciiWhitespace(*space)) {
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000079 break;
80 }
81 }
82
Alex Vakulenkoea05ff92016-01-20 07:53:57 -080083 std::string scheme = base::ToLowerASCII(std::string(token.begin(), space));
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000084 // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
85 if (scheme == "socks")
86 scheme += "4";
87 else if (scheme == "proxy")
88 scheme = "http";
89 else if (scheme != "https" &&
90 scheme != "socks4" &&
91 scheme != "socks5" &&
92 scheme != "direct")
93 continue; // Invalid proxy scheme
94
95 std::string host_and_port = std::string(space, token.end());
96 base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
97 if (scheme != "direct" && host_and_port.empty())
98 continue; // Must supply host/port when non-direct proxy used.
99 ret.push_back(scheme + "://" + host_and_port);
100 }
101 if (ret.empty() || *ret.rbegin() != kNoProxy)
102 ret.push_back(kNoProxy);
103 return ret;
104}
105
Steve Fung040dffd2015-02-10 14:25:00 -0800106// A class for interfacing with Chrome to resolve proxies for a given source
107// url. The class is initialized with the given source url to check, the
108// signal interface and name that Chrome will reply to, and how long to wait
109// for the resolve request to timeout. Once initialized, the Run() function
110// must be called, which blocks on the D-Bus call to Chrome. The call returns
111// after either the timeout or the proxy has been resolved. The resolved
112// proxies can then be accessed through the proxies() function.
Alex Vakulenko74dc6242015-10-13 09:23:34 -0700113class ProxyResolver : public brillo::DBusDaemon {
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000114 public:
Steve Fung040dffd2015-02-10 14:25:00 -0800115 ProxyResolver(const std::string& source_url,
116 const std::string& signal_interface,
117 const std::string& signal_name,
118 base::TimeDelta timeout)
119 : source_url_(source_url),
120 signal_interface_(signal_interface),
121 signal_name_(signal_name),
122 timeout_(timeout),
123 weak_ptr_factory_(this),
124 timeout_callback_(base::Bind(&ProxyResolver::HandleBrowserTimeout,
125 weak_ptr_factory_.GetWeakPtr())) {}
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000126
Steve Fung040dffd2015-02-10 14:25:00 -0800127 ~ProxyResolver() override {}
128
129 const std::vector<std::string>& proxies() {
130 return proxies_;
131 }
132
133 int Run() override {
134 // Add task for if the browser proxy call times out.
135 base::MessageLoop::current()->PostDelayedTask(
136 FROM_HERE,
137 timeout_callback_.callback(),
138 timeout_);
139
Alex Vakulenko74dc6242015-10-13 09:23:34 -0700140 return brillo::DBusDaemon::Run();
Steve Fung040dffd2015-02-10 14:25:00 -0800141 }
142
143 protected:
144 // If the browser times out, quit the run loop.
145 void HandleBrowserTimeout() {
146 LOG(ERROR) << "Timeout while waiting for browser to resolve proxy";
147 Quit();
148 }
149
150 // If the signal handler connects successfully, call the browser's
151 // ResolveNetworkProxy D-Bus method. Otherwise, don't do anything and let
152 // the timeout task quit the run loop.
153 void HandleDBusSignalConnected(const std::string& interface,
154 const std::string& signal,
155 bool success) {
156 if (!success) {
157 LOG(ERROR) << "Could not connect to signal " << interface << "."
158 << signal;
159 timeout_callback_.Cancel();
160 Quit();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000161 return;
162 }
163
Alex Vakulenko74dc6242015-10-13 09:23:34 -0700164 brillo::ErrorPtr error;
Steve Fung040dffd2015-02-10 14:25:00 -0800165 call_proxy_->ResolveNetworkProxy(source_url_,
166 signal_interface_,
167 signal_name_,
168 &error);
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000169
Steve Fung040dffd2015-02-10 14:25:00 -0800170 if (error) {
171 LOG(ERROR) << "Call to ResolveNetworkProxy failed: "
172 << error->GetMessage();
173 timeout_callback_.Cancel();
174 Quit();
175 }
176 }
177
178 // Handle incoming ProxyResolved signal.
179 void HandleProxyResolvedSignal(const std::string& source_url,
180 const std::string& proxy_info,
181 const std::string& error_message) {
182 timeout_callback_.Cancel();
183 proxies_ = ParseProxyString(proxy_info);
184 LOG(INFO) << "Found proxies via browser signal: "
Alex Vakulenkoea05ff92016-01-20 07:53:57 -0800185 << base::JoinString(proxies_, "x");
Steve Fung040dffd2015-02-10 14:25:00 -0800186
187 Quit();
188 }
189
190 int OnInit() override {
Alex Vakulenko74dc6242015-10-13 09:23:34 -0700191 int return_code = brillo::DBusDaemon::OnInit();
Steve Fung040dffd2015-02-10 14:25:00 -0800192 if (return_code != EX_OK)
193 return return_code;
194
195 // Initialize D-Bus proxies.
196 call_proxy_.reset(
197 new org::chromium::LibCrosServiceInterfaceProxy(bus_,
198 kLibCrosServiceName));
199 signal_proxy_.reset(
200 new org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy(
201 bus_,
202 kLibCrosServiceName));
203
204 // Set up the D-Bus signal handler.
205 // TODO(crbug.com/446115): Update ResolveNetworkProxy call to use an
206 // asynchronous return value rather than a return signal.
207 signal_proxy_->RegisterProxyResolvedSignalHandler(
208 base::Bind(&ProxyResolver::HandleProxyResolvedSignal,
209 weak_ptr_factory_.GetWeakPtr()),
210 base::Bind(&ProxyResolver::HandleDBusSignalConnected,
211 weak_ptr_factory_.GetWeakPtr()));
212
213 return EX_OK;
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000214 }
215
216 private:
Steve Fung040dffd2015-02-10 14:25:00 -0800217 unique_ptr<org::chromium::LibCrosServiceInterfaceProxy> call_proxy_;
218 unique_ptr<org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy>
219 signal_proxy_;
220
221 const std::string source_url_;
222 const std::string signal_interface_;
223 const std::string signal_name_;
224 base::TimeDelta timeout_;
225
226 std::vector<std::string> proxies_;
227 base::WeakPtrFactory<ProxyResolver> weak_ptr_factory_;
228
229 base::CancelableClosure timeout_callback_;
230
231 DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000232};
233
Steve Fung040dffd2015-02-10 14:25:00 -0800234static bool ShowBrowserProxies(std::string url, base::TimeDelta timeout) {
235 // Initialize and run the proxy resolver to watch for signals.
236 ProxyResolver resolver(url,
237 kLibCrosProxyResolvedSignalInterface,
238 kLibCrosProxyResolvedName,
239 timeout);
240 resolver.Run();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000241
Steve Fung040dffd2015-02-10 14:25:00 -0800242 std::vector<std::string> proxies = resolver.proxies();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000243
Steve Fung040dffd2015-02-10 14:25:00 -0800244 // If proxies is empty, then the timeout was reached waiting for the proxy
245 // resolved signal. If no proxies are defined, proxies will be populated
246 // with "direct://".
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000247 if (proxies.empty())
248 return false;
249
Steve Fung040dffd2015-02-10 14:25:00 -0800250 for (const auto& proxy : proxies) {
251 printf("%s\n", proxy.c_str());
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000252 }
253 return true;
254}
255
Steve Fung040dffd2015-02-10 14:25:00 -0800256} // namespace
257
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000258int main(int argc, char *argv[]) {
Alex Vakulenko92c0eef2015-04-02 14:31:10 -0700259 base::CommandLine::Init(argc, argv);
260 base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000261
Steve Fung040dffd2015-02-10 14:25:00 -0800262 if (cl->HasSwitch(kHelp)) {
263 LOG(INFO) << kHelpMessage;
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000264 return 0;
265 }
266
Steve Fung040dffd2015-02-10 14:25:00 -0800267 bool quiet = cl->HasSwitch(kQuiet);
268 bool verbose = cl->HasSwitch(kVerbose);
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000269
Steve Fung040dffd2015-02-10 14:25:00 -0800270 int timeout = kTimeoutDefaultSeconds;
271 std::string str_timeout = cl->GetSwitchValueASCII(kTimeout);
272 if (!str_timeout.empty() && !base::StringToInt(str_timeout, &timeout)) {
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000273 LOG(ERROR) << "Invalid timeout value: " << str_timeout;
274 return 1;
275 }
276
277 // Default to logging to syslog.
Alex Vakulenko74dc6242015-10-13 09:23:34 -0700278 int init_flags = brillo::kLogToSyslog;
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000279 // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose"
280 // was passed.
281
282 if ((!quiet && isatty(STDERR_FILENO)) || verbose)
Alex Vakulenko74dc6242015-10-13 09:23:34 -0700283 init_flags |= brillo::kLogToStderr;
284 brillo::InitLog(init_flags);
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000285
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000286 std::string url;
Alex Vakulenko92c0eef2015-04-02 14:31:10 -0700287 base::CommandLine::StringVector urls = cl->GetArgs();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000288 if (!urls.empty()) {
289 url = urls[0];
290 LOG(INFO) << "Resolving proxies for URL: " << url;
291 } else {
292 LOG(INFO) << "Resolving proxies without URL";
293 }
294
Steve Fung040dffd2015-02-10 14:25:00 -0800295 if (!ShowBrowserProxies(url, base::TimeDelta::FromSeconds(timeout))) {
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000296 LOG(ERROR) << "Error resolving proxies via the browser";
297 LOG(INFO) << "Assuming direct proxy";
298 printf("%s\n", kNoProxy);
299 }
300
301 return 0;
302}