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