blob: 6d489d8349df9dc244090cc018036f04f569c664 [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
Chris Sosafc661a12013-02-26 14:43:21 -080012#include <base/strings/string_tokenizer.h>
Alex Deymoc4acdf42014-05-28 21:07:10 -070013#include <base/strings/string_util.h>
Andrew de los Reyes000d8952011-03-02 15:21:14 -080014#include <dbus/dbus-glib-lowlevel.h>
Alex Deymoc4acdf42014-05-28 21:07:10 -070015#include <dbus/dbus-glib.h>
Andrew de los Reyes000d8952011-03-02 15:21:14 -080016#include <google/protobuf/stubs/common.h>
17
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 google::protobuf::Closure;
Alex Deymoc4acdf42014-05-28 21:07:10 -070026using google::protobuf::NewPermanentCallback;
Andrew de los Reyes000d8952011-03-02 15:21:14 -080027using std::deque;
28using std::make_pair;
29using std::multimap;
30using std::pair;
31using std::string;
32
33#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved"
34#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE \
35 "org.chromium.UpdateEngineLibcrosProxyResolvedInterface"
36const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
37const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
38const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
39const char kLibCrosServiceResolveNetworkProxyMethodName[] =
40 "ResolveNetworkProxy";
41const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME;
42const char kLibCrosProxyResolveSignalInterface[] =
43 LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE;
44const char kLibCrosProxyResolveSignalFilter[] = "type='signal', "
45 "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', "
Andrew de los Reyes000d8952011-03-02 15:21:14 -080046 "member='" LIB_CROS_PROXY_RESOLVE_NAME "'";
47#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE
48#undef LIB_CROS_PROXY_RESOLVE_NAME
49
50namespace {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080051
Andrew de los Reyes000d8952011-03-02 15:21:14 -080052const int kTimeout = 5; // seconds
53
Alex Vakulenkod2779df2014-06-16 13:19:00 -070054} // namespace
Andrew de los Reyes000d8952011-03-02 15:21:14 -080055
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080056ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(
57 DBusWrapperInterface* dbus)
Andrew de los Reyes000d8952011-03-02 15:21:14 -080058 : dbus_(dbus), proxy_(NULL), timeout_(kTimeout) {}
59
60bool ChromeBrowserProxyResolver::Init() {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080061 if (proxy_)
62 return true; // Already initialized.
63
64 // Set up signal handler. Code lifted from libcros.
65 GError* g_error = NULL;
66 DBusGConnection* bus = dbus_->BusGet(DBUS_BUS_SYSTEM, &g_error);
67 TEST_AND_RETURN_FALSE(bus);
68 DBusConnection* connection = dbus_->ConnectionGetConnection(bus);
Andrew de los Reyes000d8952011-03-02 15:21:14 -080069 TEST_AND_RETURN_FALSE(connection);
Gilad Arnoldb752fb32014-03-03 12:23:39 -080070
71 DBusError dbus_error;
72 dbus_error_init(&dbus_error);
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080073 dbus_->DBusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter,
Gilad Arnoldb752fb32014-03-03 12:23:39 -080074 &dbus_error);
75 TEST_AND_RETURN_FALSE(!dbus_error_is_set(&dbus_error));
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080076 TEST_AND_RETURN_FALSE(dbus_->DBusConnectionAddFilter(
Andrew de los Reyes000d8952011-03-02 15:21:14 -080077 connection,
78 &ChromeBrowserProxyResolver::StaticFilterMessage,
79 this,
80 NULL));
81
Gilad Arnoldb752fb32014-03-03 12:23:39 -080082 proxy_ = dbus_->ProxyNewForName(bus, kLibCrosServiceName, kLibCrosServicePath,
83 kLibCrosServiceInterface);
Andrew de los Reyes000d8952011-03-02 15:21:14 -080084 if (!proxy_) {
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080085 dbus_->DBusConnectionRemoveFilter(
Andrew de los Reyes000d8952011-03-02 15:21:14 -080086 connection,
87 &ChromeBrowserProxyResolver::StaticFilterMessage,
88 this);
89 }
90 TEST_AND_RETURN_FALSE(proxy_); // For the error log
91 return true;
92}
93
94ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080095 // Remove DBus connection filters and Kill proxy object.
96 if (proxy_) {
97 GError* gerror = NULL;
98 DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
99 if (gbus) {
100 DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -0800101 dbus_->DBusConnectionRemoveFilter(
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800102 connection,
103 &ChromeBrowserProxyResolver::StaticFilterMessage,
104 this);
105 }
106 dbus_->ProxyUnref(proxy_);
107 }
108
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800109 // Kill outstanding timers
110 for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
111 ++it) {
112 g_source_destroy(it->second);
113 it->second = NULL;
114 }
115}
116
117bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
118 ProxiesResolvedFn callback,
119 void* data) {
120 GError* error = NULL;
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700121 guint timeout = timeout_;
Gilad Arnold1877c392012-02-10 11:34:33 -0800122 if (proxy_) {
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800123 if (!dbus_->ProxyCall_3_0(proxy_,
124 kLibCrosServiceResolveNetworkProxyMethodName,
125 &error,
126 url.c_str(),
127 kLibCrosProxyResolveSignalInterface,
128 kLibCrosProxyResolveName)) {
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800129 if (error) {
130 LOG(WARNING) << "dbus_g_proxy_call failed, continuing with no proxy: "
131 << utils::GetAndFreeGError(&error);
Gilad Arnold1877c392012-02-10 11:34:33 -0800132 } else {
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800133 LOG(WARNING) << "dbus_g_proxy_call failed with no error string, "
134 "continuing with no proxy.";
Gilad Arnold1877c392012-02-10 11:34:33 -0800135 }
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800136 timeout = 0;
137 }
Gilad Arnold1877c392012-02-10 11:34:33 -0800138 } else {
Brandon Philips962873e2013-03-26 13:58:51 -0700139 LOG(WARNING) << "dbus proxy object missing, continuing with no proxy.";
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700140 timeout = 0;
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800141 }
Gilad Arnold1877c392012-02-10 11:34:33 -0800142
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800143 callbacks_.insert(make_pair(url, make_pair(callback, data)));
Alex Deymoc4acdf42014-05-28 21:07:10 -0700144 Closure* closure = NewPermanentCallback(
145 this,
146 &ChromeBrowserProxyResolver::HandleTimeout,
147 url);
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700148 GSource* timer = g_timeout_source_new_seconds(timeout);
Alex Deymoc4acdf42014-05-28 21:07:10 -0700149 g_source_set_callback(
150 timer, utils::GlibRunClosure, closure, utils::GlibDestroyClosure);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800151 g_source_attach(timer, NULL);
152 timers_.insert(make_pair(url, timer));
153 return true;
154}
155
156DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
157 DBusConnection* connection,
158 DBusMessage* message) {
159 // Code lifted from libcros.
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -0800160 if (!dbus_->DBusMessageIsSignal(message,
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800161 kLibCrosProxyResolveSignalInterface,
162 kLibCrosProxyResolveName)) {
163 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
164 }
165 // Get args
166 char* source_url = NULL;
167 char* proxy_list = NULL;
168 char* error = NULL;
169 DBusError arg_error;
170 dbus_error_init(&arg_error);
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -0800171 if (!dbus_->DBusMessageGetArgs_3(message, &arg_error,
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800172 &source_url,
173 &proxy_list,
174 &error)) {
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800175 LOG(ERROR) << "Error reading dbus signal.";
176 return DBUS_HANDLER_RESULT_HANDLED;
177 }
178 if (!source_url || !proxy_list) {
179 LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
180 return DBUS_HANDLER_RESULT_HANDLED;
181 }
182 HandleReply(source_url, proxy_list);
183 return DBUS_HANDLER_RESULT_HANDLED;
184}
185
186bool ChromeBrowserProxyResolver::DeleteUrlState(
187 const string& source_url,
188 bool delete_timer,
189 pair<ProxiesResolvedFn, void*>* callback) {
190 {
191 CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
192 TEST_AND_RETURN_FALSE(it != callbacks_.end());
193 TEST_AND_RETURN_FALSE(it->first == source_url);
194 if (callback)
195 *callback = it->second;
196 callbacks_.erase(it);
197 }
198 {
199 TimeoutsMap::iterator it = timers_.lower_bound(source_url);
200 TEST_AND_RETURN_FALSE(it != timers_.end());
201 TEST_AND_RETURN_FALSE(it->first == source_url);
202 if (delete_timer)
203 g_source_destroy(it->second);
204 timers_.erase(it);
205 }
206 return true;
207}
208
209void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
210 const string& proxy_list) {
211 pair<ProxiesResolvedFn, void*> callback;
212 TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
213 (*callback.first)(ParseProxyString(proxy_list), callback.second);
214}
215
216void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
217 LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
218 pair<ProxiesResolvedFn, void*> callback;
219 TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
220 deque<string> proxies;
221 proxies.push_back(kNoProxy);
222 (*callback.first)(proxies, callback.second);
223}
224
225deque<string> ChromeBrowserProxyResolver::ParseProxyString(
226 const string& input) {
227 deque<string> ret;
228 // Some of this code taken from
229 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
230 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
231 StringTokenizer entry_tok(input, ";");
232 while (entry_tok.GetNext()) {
233 string token = entry_tok.token();
Ben Chan736fcb52014-05-21 18:28:22 -0700234 base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800235
236 // Start by finding the first space (if any).
237 std::string::iterator space;
238 for (space = token.begin(); space != token.end(); ++space) {
239 if (IsAsciiWhitespace(*space)) {
240 break;
241 }
242 }
243
244 string scheme = string(token.begin(), space);
245 StringToLowerASCII(&scheme);
246 // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
247 if (scheme == "socks")
248 scheme += "4";
249 else if (scheme == "proxy")
250 scheme = "http";
251 else if (scheme != "https" &&
252 scheme != "socks4" &&
253 scheme != "socks5" &&
254 scheme != "direct")
255 continue; // Invalid proxy scheme
256
257 string host_and_port = string(space, token.end());
Ben Chan736fcb52014-05-21 18:28:22 -0700258 base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800259 if (scheme != "direct" && host_and_port.empty())
260 continue; // Must supply host/port when non-direct proxy used.
261 ret.push_back(scheme + "://" + host_and_port);
262 }
263 if (ret.empty() || *ret.rbegin() != kNoProxy)
264 ret.push_back(kNoProxy);
265 return ret;
266}
267
268} // namespace chromeos_update_engine