blob: 3c1fe237d9df29e0bb29bb4df12bd37805e80e00 [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
7#include <map>
8#include <string>
9
Andrew de los Reyes000d8952011-03-02 15:21:14 -080010#include <base/string_util.h>
Chris Sosafc661a12013-02-26 14:43:21 -080011#include <base/strings/string_tokenizer.h>
Andrew de los Reyes000d8952011-03-02 15:21:14 -080012#include <dbus/dbus-glib.h>
13#include <dbus/dbus-glib-lowlevel.h>
14#include <google/protobuf/stubs/common.h>
15
16#include "update_engine/dbus_constants.h"
17#include "update_engine/utils.h"
18
19namespace chromeos_update_engine {
20
Chris Sosafc661a12013-02-26 14:43:21 -080021using base::StringTokenizer;
Andrew de los Reyes000d8952011-03-02 15:21:14 -080022using google::protobuf::Closure;
23using google::protobuf::NewCallback;
24using std::deque;
25using std::make_pair;
26using std::multimap;
27using std::pair;
28using std::string;
29
30#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved"
31#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE \
32 "org.chromium.UpdateEngineLibcrosProxyResolvedInterface"
33const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
34const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
35const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
36const char kLibCrosServiceResolveNetworkProxyMethodName[] =
37 "ResolveNetworkProxy";
38const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME;
39const char kLibCrosProxyResolveSignalInterface[] =
40 LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE;
41const char kLibCrosProxyResolveSignalFilter[] = "type='signal', "
42 "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', "
Andrew de los Reyes000d8952011-03-02 15:21:14 -080043 "member='" LIB_CROS_PROXY_RESOLVE_NAME "'";
44#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE
45#undef LIB_CROS_PROXY_RESOLVE_NAME
46
47namespace {
48const int kTimeout = 5; // seconds
49
50DBusGProxy* GetProxy(DbusGlibInterface* dbus) {
51 GError* error = NULL;
52
53 DBusGConnection* bus = dbus->BusGet(DBUS_BUS_SYSTEM, &error);
54 if (!bus) {
55 LOG(ERROR) << "Failed to get bus";
56 return NULL;
57 }
58 DBusGProxy* proxy = dbus->ProxyNewForNameOwner(bus,
59 kLibCrosServiceName,
60 kLibCrosServicePath,
61 kLibCrosServiceInterface,
62 &error);
63 if (!proxy) {
Darin Petkova0b9e772011-10-06 05:05:56 -070064 LOG(ERROR) << "Error getting dbus proxy for " << kLibCrosServiceName << ": "
65 << utils::GetAndFreeGError(&error);
Andrew de los Reyes000d8952011-03-02 15:21:14 -080066 return NULL;
67 }
68 return proxy;
69}
70
71} // namespace {}
72
73ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(DbusGlibInterface* dbus)
74 : dbus_(dbus), proxy_(NULL), timeout_(kTimeout) {}
75
76bool ChromeBrowserProxyResolver::Init() {
77 // Set up signal handler. Code lifted from libcros
78 if (proxy_) {
79 // Already initialized
80 return true;
81 }
82 GError* gerror = NULL;
83 DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
84 TEST_AND_RETURN_FALSE(gbus);
85 DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
86 TEST_AND_RETURN_FALSE(connection);
87 DBusError error;
88 dbus_error_init(&error);
89 dbus_->DbusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter, &error);
90 TEST_AND_RETURN_FALSE(!dbus_error_is_set(&error));
91 TEST_AND_RETURN_FALSE(dbus_->DbusConnectionAddFilter(
92 connection,
93 &ChromeBrowserProxyResolver::StaticFilterMessage,
94 this,
95 NULL));
96
97 proxy_ = GetProxy(dbus_);
98 if (!proxy_) {
99 dbus_->DbusConnectionRemoveFilter(
100 connection,
101 &ChromeBrowserProxyResolver::StaticFilterMessage,
102 this);
103 }
104 TEST_AND_RETURN_FALSE(proxy_); // For the error log
105 return true;
106}
107
Gilad Arnold1877c392012-02-10 11:34:33 -0800108void ChromeBrowserProxyResolver::Shutdown() {
109 if (!proxy_)
110 return;
111
112 GError* gerror = NULL;
113 DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
114 TEST_AND_RETURN(gbus);
115 DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
116 dbus_->DbusConnectionRemoveFilter(
117 connection,
118 &ChromeBrowserProxyResolver::StaticFilterMessage,
119 this);
120 proxy_ = NULL;
121}
122
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800123ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
Gilad Arnold1877c392012-02-10 11:34:33 -0800124 // Kill proxy object.
125 Shutdown();
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800126 // Kill outstanding timers
127 for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
128 ++it) {
129 g_source_destroy(it->second);
130 it->second = NULL;
131 }
132}
133
134bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
135 ProxiesResolvedFn callback,
136 void* data) {
137 GError* error = NULL;
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700138 guint timeout = timeout_;
Gilad Arnold1877c392012-02-10 11:34:33 -0800139 if (proxy_) {
140 bool dbus_success = true;
141 bool dbus_reinit = false;
142 bool dbus_got_error;
143
144 do {
145 if (!dbus_success) {
146 // We failed with a null error, time to re-init the dbus proxy.
147 LOG(WARNING) << "attempting to reinitialize dbus proxy and retrying";
148 Shutdown();
149 if (!Init()) {
150 LOG(WARNING) << "failed to reinitialize the dbus proxy";
151 break;
152 }
153 dbus_reinit = true;
154 }
155
156 dbus_success = dbus_->ProxyCall(
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800157 proxy_,
158 kLibCrosServiceResolveNetworkProxyMethodName,
159 &error,
160 G_TYPE_STRING, url.c_str(),
161 G_TYPE_STRING, kLibCrosProxyResolveSignalInterface,
162 G_TYPE_STRING, kLibCrosProxyResolveName,
Gilad Arnold1877c392012-02-10 11:34:33 -0800163 G_TYPE_INVALID, G_TYPE_INVALID);
164
165 dbus_got_error = false;
166
167 if (dbus_success) {
Brandon Philips962873e2013-03-26 13:58:51 -0700168 LOG(INFO) << "dbus_g_proxy_call succeeded!";
Gilad Arnold1877c392012-02-10 11:34:33 -0800169 } else {
170 if (error) {
171 // Register the fact that we did receive an error, as it is nullified
172 // on the next line.
173 dbus_got_error = true;
174 LOG(WARNING) << "dbus_g_proxy_call failed: "
175 << utils::GetAndFreeGError(&error)
176 << " Continuing with no proxy.";
177 } else {
Brandon Philips962873e2013-03-26 13:58:51 -0700178 LOG(WARNING) << "dbus_g_proxy_call failed with no error string, "
Gilad Arnold1877c392012-02-10 11:34:33 -0800179 "continuing with no proxy.";
180 }
181 timeout = 0;
182 }
183 } while (!(dbus_success || dbus_got_error || dbus_reinit));
184 } else {
Brandon Philips962873e2013-03-26 13:58:51 -0700185 LOG(WARNING) << "dbus proxy object missing, continuing with no proxy.";
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700186 timeout = 0;
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800187 }
Gilad Arnold1877c392012-02-10 11:34:33 -0800188
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800189 callbacks_.insert(make_pair(url, make_pair(callback, data)));
190 Closure* closure = NewCallback(this,
191 &ChromeBrowserProxyResolver::HandleTimeout,
192 url);
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700193 GSource* timer = g_timeout_source_new_seconds(timeout);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800194 g_source_set_callback(timer, &utils::GlibRunClosure, closure, NULL);
195 g_source_attach(timer, NULL);
196 timers_.insert(make_pair(url, timer));
197 return true;
198}
199
200DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
201 DBusConnection* connection,
202 DBusMessage* message) {
203 // Code lifted from libcros.
204 if (!dbus_->DbusMessageIsSignal(message,
205 kLibCrosProxyResolveSignalInterface,
206 kLibCrosProxyResolveName)) {
207 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
208 }
209 // Get args
210 char* source_url = NULL;
211 char* proxy_list = NULL;
212 char* error = NULL;
213 DBusError arg_error;
214 dbus_error_init(&arg_error);
215 if (!dbus_->DbusMessageGetArgs(message, &arg_error,
216 DBUS_TYPE_STRING, &source_url,
217 DBUS_TYPE_STRING, &proxy_list,
218 DBUS_TYPE_STRING, &error,
219 DBUS_TYPE_INVALID)) {
220 LOG(ERROR) << "Error reading dbus signal.";
221 return DBUS_HANDLER_RESULT_HANDLED;
222 }
223 if (!source_url || !proxy_list) {
224 LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
225 return DBUS_HANDLER_RESULT_HANDLED;
226 }
227 HandleReply(source_url, proxy_list);
228 return DBUS_HANDLER_RESULT_HANDLED;
229}
230
231bool ChromeBrowserProxyResolver::DeleteUrlState(
232 const string& source_url,
233 bool delete_timer,
234 pair<ProxiesResolvedFn, void*>* callback) {
235 {
236 CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
237 TEST_AND_RETURN_FALSE(it != callbacks_.end());
238 TEST_AND_RETURN_FALSE(it->first == source_url);
239 if (callback)
240 *callback = it->second;
241 callbacks_.erase(it);
242 }
243 {
244 TimeoutsMap::iterator it = timers_.lower_bound(source_url);
245 TEST_AND_RETURN_FALSE(it != timers_.end());
246 TEST_AND_RETURN_FALSE(it->first == source_url);
247 if (delete_timer)
248 g_source_destroy(it->second);
249 timers_.erase(it);
250 }
251 return true;
252}
253
254void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
255 const string& proxy_list) {
256 pair<ProxiesResolvedFn, void*> callback;
257 TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
258 (*callback.first)(ParseProxyString(proxy_list), callback.second);
259}
260
261void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
262 LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
263 pair<ProxiesResolvedFn, void*> callback;
264 TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
265 deque<string> proxies;
266 proxies.push_back(kNoProxy);
267 (*callback.first)(proxies, callback.second);
268}
269
270deque<string> ChromeBrowserProxyResolver::ParseProxyString(
271 const string& input) {
272 deque<string> ret;
273 // Some of this code taken from
274 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
275 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
276 StringTokenizer entry_tok(input, ";");
277 while (entry_tok.GetNext()) {
278 string token = entry_tok.token();
279 TrimWhitespaceASCII(token, TRIM_ALL, &token);
280
281 // Start by finding the first space (if any).
282 std::string::iterator space;
283 for (space = token.begin(); space != token.end(); ++space) {
284 if (IsAsciiWhitespace(*space)) {
285 break;
286 }
287 }
288
289 string scheme = string(token.begin(), space);
290 StringToLowerASCII(&scheme);
291 // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
292 if (scheme == "socks")
293 scheme += "4";
294 else if (scheme == "proxy")
295 scheme = "http";
296 else if (scheme != "https" &&
297 scheme != "socks4" &&
298 scheme != "socks5" &&
299 scheme != "direct")
300 continue; // Invalid proxy scheme
301
302 string host_and_port = string(space, token.end());
303 TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port);
304 if (scheme != "direct" && host_and_port.empty())
305 continue; // Must supply host/port when non-direct proxy used.
306 ret.push_back(scheme + "://" + host_and_port);
307 }
308 if (ret.empty() || *ret.rbegin() != kNoProxy)
309 ret.push_back(kNoProxy);
310 return ret;
311}
312
313} // namespace chromeos_update_engine