blob: e8e1a69eec2d401928cafe12a05e7c34fd938da3 [file] [log] [blame]
Andrew de los Reyes000d8952011-03-02 15:21:14 -08001// Copyright (c) 2011 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 "update_engine/chrome_browser_proxy_resolver.h"
6
Alex Vakulenkod2779df2014-06-16 13:19:00 -07007#include <deque>
Andrew de los Reyes000d8952011-03-02 15:21:14 -08008#include <map>
9#include <string>
Alex Vakulenkod2779df2014-06-16 13:19:00 -070010#include <utility>
Andrew de los Reyes000d8952011-03-02 15:21:14 -080011
Alex Vakulenko4906c1c2014-08-21 13:17:44 -070012#include <base/bind.h>
Chris Sosafc661a12013-02-26 14:43:21 -080013#include <base/strings/string_tokenizer.h>
Alex Deymoc4acdf42014-05-28 21:07:10 -070014#include <base/strings/string_util.h>
Andrew de los Reyes000d8952011-03-02 15:21:14 -080015#include <dbus/dbus-glib-lowlevel.h>
Alex Deymoc4acdf42014-05-28 21:07:10 -070016#include <dbus/dbus-glib.h>
Andrew de los Reyes000d8952011-03-02 15:21:14 -080017
18#include "update_engine/dbus_constants.h"
Alex Deymo44666f92014-07-22 20:29:24 -070019#include "update_engine/glib_utils.h"
Andrew de los Reyes000d8952011-03-02 15:21:14 -080020#include "update_engine/utils.h"
21
22namespace chromeos_update_engine {
23
Chris Sosafc661a12013-02-26 14:43:21 -080024using base::StringTokenizer;
Andrew de los Reyes000d8952011-03-02 15:21:14 -080025using std::deque;
26using std::make_pair;
27using std::multimap;
28using std::pair;
29using std::string;
30
31#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved"
32#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE \
33 "org.chromium.UpdateEngineLibcrosProxyResolvedInterface"
34const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
35const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
36const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
37const char kLibCrosServiceResolveNetworkProxyMethodName[] =
38 "ResolveNetworkProxy";
39const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME;
40const char kLibCrosProxyResolveSignalInterface[] =
41 LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE;
42const char kLibCrosProxyResolveSignalFilter[] = "type='signal', "
43 "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', "
Andrew de los Reyes000d8952011-03-02 15:21:14 -080044 "member='" LIB_CROS_PROXY_RESOLVE_NAME "'";
45#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE
46#undef LIB_CROS_PROXY_RESOLVE_NAME
47
48namespace {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080049
Andrew de los Reyes000d8952011-03-02 15:21:14 -080050const int kTimeout = 5; // seconds
51
Alex Vakulenkod2779df2014-06-16 13:19:00 -070052} // namespace
Andrew de los Reyes000d8952011-03-02 15:21:14 -080053
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080054ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(
55 DBusWrapperInterface* dbus)
Alex Vakulenko88b591f2014-08-28 16:48:57 -070056 : dbus_(dbus), proxy_(nullptr), timeout_(kTimeout) {}
Andrew de los Reyes000d8952011-03-02 15:21:14 -080057
58bool ChromeBrowserProxyResolver::Init() {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080059 if (proxy_)
60 return true; // Already initialized.
61
62 // Set up signal handler. Code lifted from libcros.
Alex Vakulenko88b591f2014-08-28 16:48:57 -070063 GError* g_error = nullptr;
Gilad Arnoldb752fb32014-03-03 12:23:39 -080064 DBusGConnection* bus = dbus_->BusGet(DBUS_BUS_SYSTEM, &g_error);
65 TEST_AND_RETURN_FALSE(bus);
66 DBusConnection* connection = dbus_->ConnectionGetConnection(bus);
Andrew de los Reyes000d8952011-03-02 15:21:14 -080067 TEST_AND_RETURN_FALSE(connection);
Gilad Arnoldb752fb32014-03-03 12:23:39 -080068
69 DBusError dbus_error;
70 dbus_error_init(&dbus_error);
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080071 dbus_->DBusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter,
Gilad Arnoldb752fb32014-03-03 12:23:39 -080072 &dbus_error);
73 TEST_AND_RETURN_FALSE(!dbus_error_is_set(&dbus_error));
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080074 TEST_AND_RETURN_FALSE(dbus_->DBusConnectionAddFilter(
Andrew de los Reyes000d8952011-03-02 15:21:14 -080075 connection,
76 &ChromeBrowserProxyResolver::StaticFilterMessage,
77 this,
Alex Vakulenko88b591f2014-08-28 16:48:57 -070078 nullptr));
Andrew de los Reyes000d8952011-03-02 15:21:14 -080079
Gilad Arnoldb752fb32014-03-03 12:23:39 -080080 proxy_ = dbus_->ProxyNewForName(bus, kLibCrosServiceName, kLibCrosServicePath,
81 kLibCrosServiceInterface);
Andrew de los Reyes000d8952011-03-02 15:21:14 -080082 if (!proxy_) {
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080083 dbus_->DBusConnectionRemoveFilter(
Andrew de los Reyes000d8952011-03-02 15:21:14 -080084 connection,
85 &ChromeBrowserProxyResolver::StaticFilterMessage,
86 this);
87 }
88 TEST_AND_RETURN_FALSE(proxy_); // For the error log
89 return true;
90}
91
92ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080093 // Remove DBus connection filters and Kill proxy object.
94 if (proxy_) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -070095 GError* gerror = nullptr;
Gilad Arnoldb752fb32014-03-03 12:23:39 -080096 DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
97 if (gbus) {
98 DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080099 dbus_->DBusConnectionRemoveFilter(
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800100 connection,
101 &ChromeBrowserProxyResolver::StaticFilterMessage,
102 this);
103 }
104 dbus_->ProxyUnref(proxy_);
105 }
106
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800107 // Kill outstanding timers
108 for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
109 ++it) {
110 g_source_destroy(it->second);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700111 it->second = nullptr;
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800112 }
113}
114
115bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
116 ProxiesResolvedFn callback,
117 void* data) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700118 GError* error = nullptr;
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700119 guint timeout = timeout_;
Gilad Arnold1877c392012-02-10 11:34:33 -0800120 if (proxy_) {
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800121 if (!dbus_->ProxyCall_3_0(proxy_,
122 kLibCrosServiceResolveNetworkProxyMethodName,
123 &error,
124 url.c_str(),
125 kLibCrosProxyResolveSignalInterface,
126 kLibCrosProxyResolveName)) {
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800127 if (error) {
128 LOG(WARNING) << "dbus_g_proxy_call failed, continuing with no proxy: "
129 << utils::GetAndFreeGError(&error);
Gilad Arnold1877c392012-02-10 11:34:33 -0800130 } else {
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800131 LOG(WARNING) << "dbus_g_proxy_call failed with no error string, "
132 "continuing with no proxy.";
Gilad Arnold1877c392012-02-10 11:34:33 -0800133 }
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800134 timeout = 0;
135 }
Gilad Arnold1877c392012-02-10 11:34:33 -0800136 } else {
Brandon Philips962873e2013-03-26 13:58:51 -0700137 LOG(WARNING) << "dbus proxy object missing, continuing with no proxy.";
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700138 timeout = 0;
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800139 }
Gilad Arnold1877c392012-02-10 11:34:33 -0800140
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800141 callbacks_.insert(make_pair(url, make_pair(callback, data)));
Alex Vakulenko4906c1c2014-08-21 13:17:44 -0700142 base::Closure* closure = new base::Closure(base::Bind(
Alex Deymoc4acdf42014-05-28 21:07:10 -0700143 &ChromeBrowserProxyResolver::HandleTimeout,
Alex Vakulenko4906c1c2014-08-21 13:17:44 -0700144 base::Unretained(this),
145 url));
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700146 GSource* timer = g_timeout_source_new_seconds(timeout);
Alex Deymoc4acdf42014-05-28 21:07:10 -0700147 g_source_set_callback(
148 timer, utils::GlibRunClosure, closure, utils::GlibDestroyClosure);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700149 g_source_attach(timer, nullptr);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800150 timers_.insert(make_pair(url, timer));
151 return true;
152}
153
154DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
155 DBusConnection* connection,
156 DBusMessage* message) {
157 // Code lifted from libcros.
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -0800158 if (!dbus_->DBusMessageIsSignal(message,
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800159 kLibCrosProxyResolveSignalInterface,
160 kLibCrosProxyResolveName)) {
161 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
162 }
163 // Get args
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700164 char* source_url = nullptr;
165 char* proxy_list = nullptr;
166 char* error = nullptr;
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800167 DBusError arg_error;
168 dbus_error_init(&arg_error);
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -0800169 if (!dbus_->DBusMessageGetArgs_3(message, &arg_error,
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800170 &source_url,
171 &proxy_list,
172 &error)) {
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800173 LOG(ERROR) << "Error reading dbus signal.";
174 return DBUS_HANDLER_RESULT_HANDLED;
175 }
176 if (!source_url || !proxy_list) {
177 LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
178 return DBUS_HANDLER_RESULT_HANDLED;
179 }
180 HandleReply(source_url, proxy_list);
181 return DBUS_HANDLER_RESULT_HANDLED;
182}
183
184bool ChromeBrowserProxyResolver::DeleteUrlState(
185 const string& source_url,
186 bool delete_timer,
187 pair<ProxiesResolvedFn, void*>* callback) {
188 {
189 CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
190 TEST_AND_RETURN_FALSE(it != callbacks_.end());
191 TEST_AND_RETURN_FALSE(it->first == source_url);
192 if (callback)
193 *callback = it->second;
194 callbacks_.erase(it);
195 }
196 {
197 TimeoutsMap::iterator it = timers_.lower_bound(source_url);
198 TEST_AND_RETURN_FALSE(it != timers_.end());
199 TEST_AND_RETURN_FALSE(it->first == source_url);
200 if (delete_timer)
201 g_source_destroy(it->second);
202 timers_.erase(it);
203 }
204 return true;
205}
206
207void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
208 const string& proxy_list) {
209 pair<ProxiesResolvedFn, void*> callback;
210 TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
211 (*callback.first)(ParseProxyString(proxy_list), callback.second);
212}
213
214void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
215 LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
216 pair<ProxiesResolvedFn, void*> callback;
217 TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
218 deque<string> proxies;
219 proxies.push_back(kNoProxy);
220 (*callback.first)(proxies, callback.second);
221}
222
223deque<string> ChromeBrowserProxyResolver::ParseProxyString(
224 const string& input) {
225 deque<string> ret;
226 // Some of this code taken from
227 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
228 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
229 StringTokenizer entry_tok(input, ";");
230 while (entry_tok.GetNext()) {
231 string token = entry_tok.token();
Ben Chan736fcb52014-05-21 18:28:22 -0700232 base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800233
234 // Start by finding the first space (if any).
235 std::string::iterator space;
236 for (space = token.begin(); space != token.end(); ++space) {
237 if (IsAsciiWhitespace(*space)) {
238 break;
239 }
240 }
241
242 string scheme = string(token.begin(), space);
Ben Chan93aabd02014-09-05 08:20:59 -0700243 base::StringToLowerASCII(&scheme);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800244 // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
245 if (scheme == "socks")
246 scheme += "4";
247 else if (scheme == "proxy")
248 scheme = "http";
249 else if (scheme != "https" &&
250 scheme != "socks4" &&
251 scheme != "socks5" &&
252 scheme != "direct")
253 continue; // Invalid proxy scheme
254
255 string host_and_port = string(space, token.end());
Ben Chan736fcb52014-05-21 18:28:22 -0700256 base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800257 if (scheme != "direct" && host_and_port.empty())
258 continue; // Must supply host/port when non-direct proxy used.
259 ret.push_back(scheme + "://" + host_and_port);
260 }
261 if (ret.empty() || *ret.rbegin() != kNoProxy)
262 ret.push_back(kNoProxy);
263 return ret;
264}
265
266} // namespace chromeos_update_engine