blob: a39441d0747e7d839966b9144a5cd2c7169f0571 [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>
Steve Fung040dffd2015-02-10 14:25:00 -080031#include <chromeos/daemons/dbus_daemon.h>
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000032#include <chromeos/syslog_logging.h>
33
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) {
78 if (IsAsciiWhitespace(*space)) {
79 break;
80 }
81 }
82
83 std::string scheme = std::string(token.begin(), space);
84 base::StringToLowerASCII(&scheme);
85 // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
86 if (scheme == "socks")
87 scheme += "4";
88 else if (scheme == "proxy")
89 scheme = "http";
90 else if (scheme != "https" &&
91 scheme != "socks4" &&
92 scheme != "socks5" &&
93 scheme != "direct")
94 continue; // Invalid proxy scheme
95
96 std::string host_and_port = std::string(space, token.end());
97 base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
98 if (scheme != "direct" && host_and_port.empty())
99 continue; // Must supply host/port when non-direct proxy used.
100 ret.push_back(scheme + "://" + host_and_port);
101 }
102 if (ret.empty() || *ret.rbegin() != kNoProxy)
103 ret.push_back(kNoProxy);
104 return ret;
105}
106
Steve Fung040dffd2015-02-10 14:25:00 -0800107// A class for interfacing with Chrome to resolve proxies for a given source
108// url. The class is initialized with the given source url to check, the
109// signal interface and name that Chrome will reply to, and how long to wait
110// for the resolve request to timeout. Once initialized, the Run() function
111// must be called, which blocks on the D-Bus call to Chrome. The call returns
112// after either the timeout or the proxy has been resolved. The resolved
113// proxies can then be accessed through the proxies() function.
114class ProxyResolver : public chromeos::DBusDaemon {
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000115 public:
Steve Fung040dffd2015-02-10 14:25:00 -0800116 ProxyResolver(const std::string& source_url,
117 const std::string& signal_interface,
118 const std::string& signal_name,
119 base::TimeDelta timeout)
120 : source_url_(source_url),
121 signal_interface_(signal_interface),
122 signal_name_(signal_name),
123 timeout_(timeout),
124 weak_ptr_factory_(this),
125 timeout_callback_(base::Bind(&ProxyResolver::HandleBrowserTimeout,
126 weak_ptr_factory_.GetWeakPtr())) {}
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000127
Steve Fung040dffd2015-02-10 14:25:00 -0800128 ~ProxyResolver() override {}
129
130 const std::vector<std::string>& proxies() {
131 return proxies_;
132 }
133
134 int Run() override {
135 // Add task for if the browser proxy call times out.
136 base::MessageLoop::current()->PostDelayedTask(
137 FROM_HERE,
138 timeout_callback_.callback(),
139 timeout_);
140
141 return chromeos::DBusDaemon::Run();
142 }
143
144 protected:
145 // If the browser times out, quit the run loop.
146 void HandleBrowserTimeout() {
147 LOG(ERROR) << "Timeout while waiting for browser to resolve proxy";
148 Quit();
149 }
150
151 // If the signal handler connects successfully, call the browser's
152 // ResolveNetworkProxy D-Bus method. Otherwise, don't do anything and let
153 // the timeout task quit the run loop.
154 void HandleDBusSignalConnected(const std::string& interface,
155 const std::string& signal,
156 bool success) {
157 if (!success) {
158 LOG(ERROR) << "Could not connect to signal " << interface << "."
159 << signal;
160 timeout_callback_.Cancel();
161 Quit();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000162 return;
163 }
164
Steve Fung040dffd2015-02-10 14:25:00 -0800165 chromeos::ErrorPtr error;
166 call_proxy_->ResolveNetworkProxy(source_url_,
167 signal_interface_,
168 signal_name_,
169 &error);
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000170
Steve Fung040dffd2015-02-10 14:25:00 -0800171 if (error) {
172 LOG(ERROR) << "Call to ResolveNetworkProxy failed: "
173 << error->GetMessage();
174 timeout_callback_.Cancel();
175 Quit();
176 }
177 }
178
179 // Handle incoming ProxyResolved signal.
180 void HandleProxyResolvedSignal(const std::string& source_url,
181 const std::string& proxy_info,
182 const std::string& error_message) {
183 timeout_callback_.Cancel();
184 proxies_ = ParseProxyString(proxy_info);
185 LOG(INFO) << "Found proxies via browser signal: "
186 << JoinString(proxies_, 'x');
187
188 Quit();
189 }
190
191 int OnInit() override {
192 int return_code = chromeos::DBusDaemon::OnInit();
193 if (return_code != EX_OK)
194 return return_code;
195
196 // Initialize D-Bus proxies.
197 call_proxy_.reset(
198 new org::chromium::LibCrosServiceInterfaceProxy(bus_,
199 kLibCrosServiceName));
200 signal_proxy_.reset(
201 new org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy(
202 bus_,
203 kLibCrosServiceName));
204
205 // Set up the D-Bus signal handler.
206 // TODO(crbug.com/446115): Update ResolveNetworkProxy call to use an
207 // asynchronous return value rather than a return signal.
208 signal_proxy_->RegisterProxyResolvedSignalHandler(
209 base::Bind(&ProxyResolver::HandleProxyResolvedSignal,
210 weak_ptr_factory_.GetWeakPtr()),
211 base::Bind(&ProxyResolver::HandleDBusSignalConnected,
212 weak_ptr_factory_.GetWeakPtr()));
213
214 return EX_OK;
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000215 }
216
217 private:
Steve Fung040dffd2015-02-10 14:25:00 -0800218 unique_ptr<org::chromium::LibCrosServiceInterfaceProxy> call_proxy_;
219 unique_ptr<org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy>
220 signal_proxy_;
221
222 const std::string source_url_;
223 const std::string signal_interface_;
224 const std::string signal_name_;
225 base::TimeDelta timeout_;
226
227 std::vector<std::string> proxies_;
228 base::WeakPtrFactory<ProxyResolver> weak_ptr_factory_;
229
230 base::CancelableClosure timeout_callback_;
231
232 DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000233};
234
Steve Fung040dffd2015-02-10 14:25:00 -0800235static bool ShowBrowserProxies(std::string url, base::TimeDelta timeout) {
236 // Initialize and run the proxy resolver to watch for signals.
237 ProxyResolver resolver(url,
238 kLibCrosProxyResolvedSignalInterface,
239 kLibCrosProxyResolvedName,
240 timeout);
241 resolver.Run();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000242
Steve Fung040dffd2015-02-10 14:25:00 -0800243 std::vector<std::string> proxies = resolver.proxies();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000244
Steve Fung040dffd2015-02-10 14:25:00 -0800245 // If proxies is empty, then the timeout was reached waiting for the proxy
246 // resolved signal. If no proxies are defined, proxies will be populated
247 // with "direct://".
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000248 if (proxies.empty())
249 return false;
250
Steve Fung040dffd2015-02-10 14:25:00 -0800251 for (const auto& proxy : proxies) {
252 printf("%s\n", proxy.c_str());
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000253 }
254 return true;
255}
256
Steve Fung040dffd2015-02-10 14:25:00 -0800257} // namespace
258
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000259int main(int argc, char *argv[]) {
Alex Vakulenko92c0eef2015-04-02 14:31:10 -0700260 base::CommandLine::Init(argc, argv);
261 base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000262
Steve Fung040dffd2015-02-10 14:25:00 -0800263 if (cl->HasSwitch(kHelp)) {
264 LOG(INFO) << kHelpMessage;
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000265 return 0;
266 }
267
Steve Fung040dffd2015-02-10 14:25:00 -0800268 bool quiet = cl->HasSwitch(kQuiet);
269 bool verbose = cl->HasSwitch(kVerbose);
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000270
Steve Fung040dffd2015-02-10 14:25:00 -0800271 int timeout = kTimeoutDefaultSeconds;
272 std::string str_timeout = cl->GetSwitchValueASCII(kTimeout);
273 if (!str_timeout.empty() && !base::StringToInt(str_timeout, &timeout)) {
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000274 LOG(ERROR) << "Invalid timeout value: " << str_timeout;
275 return 1;
276 }
277
278 // Default to logging to syslog.
279 int init_flags = chromeos::kLogToSyslog;
280 // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose"
281 // was passed.
282
283 if ((!quiet && isatty(STDERR_FILENO)) || verbose)
284 init_flags |= chromeos::kLogToStderr;
285 chromeos::InitLog(init_flags);
286
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000287 std::string url;
Alex Vakulenko92c0eef2015-04-02 14:31:10 -0700288 base::CommandLine::StringVector urls = cl->GetArgs();
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000289 if (!urls.empty()) {
290 url = urls[0];
291 LOG(INFO) << "Resolving proxies for URL: " << url;
292 } else {
293 LOG(INFO) << "Resolving proxies without URL";
294 }
295
Steve Fung040dffd2015-02-10 14:25:00 -0800296 if (!ShowBrowserProxies(url, base::TimeDelta::FromSeconds(timeout))) {
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000297 LOG(ERROR) << "Error resolving proxies via the browser";
298 LOG(INFO) << "Assuming direct proxy";
299 printf("%s\n", kNoProxy);
300 }
301
302 return 0;
303}