blob: abfec16539799472a0c8f8be087177123d3cc9ec [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 "', "
42 "path='/', "
43 "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) {
64 LOG(ERROR) << "Error getting dbus proxy for "
65 << kLibCrosServiceName << ": " << utils::GetGErrorMessage(error);
66 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
108ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
109 if (proxy_) {
110 GError* gerror = NULL;
111 DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
112 TEST_AND_RETURN(gbus);
113 DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
114 dbus_->DbusConnectionRemoveFilter(
115 connection,
116 &ChromeBrowserProxyResolver::StaticFilterMessage,
117 this);
118 }
119 // Kill outstanding timers
120 for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
121 ++it) {
122 g_source_destroy(it->second);
123 it->second = NULL;
124 }
125}
126
127bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
128 ProxiesResolvedFn callback,
129 void* data) {
130 GError* error = NULL;
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700131 guint timeout = timeout_;
132 if (!proxy_ || !dbus_->ProxyCall(
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800133 proxy_,
134 kLibCrosServiceResolveNetworkProxyMethodName,
135 &error,
136 G_TYPE_STRING, url.c_str(),
137 G_TYPE_STRING, kLibCrosProxyResolveSignalInterface,
138 G_TYPE_STRING, kLibCrosProxyResolveName,
139 G_TYPE_INVALID, G_TYPE_INVALID)) {
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700140 LOG(WARNING) << "dbus_g_proxy_call failed: "
141 << utils::GetGErrorMessage(error)
142 << " Continuing with no proxy.";
143 timeout = 0;
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800144 }
145 callbacks_.insert(make_pair(url, make_pair(callback, data)));
146 Closure* closure = NewCallback(this,
147 &ChromeBrowserProxyResolver::HandleTimeout,
148 url);
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700149 GSource* timer = g_timeout_source_new_seconds(timeout);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800150 g_source_set_callback(timer, &utils::GlibRunClosure, closure, NULL);
151 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.
160 if (!dbus_->DbusMessageIsSignal(message,
161 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);
171 if (!dbus_->DbusMessageGetArgs(message, &arg_error,
172 DBUS_TYPE_STRING, &source_url,
173 DBUS_TYPE_STRING, &proxy_list,
174 DBUS_TYPE_STRING, &error,
175 DBUS_TYPE_INVALID)) {
176 LOG(ERROR) << "Error reading dbus signal.";
177 return DBUS_HANDLER_RESULT_HANDLED;
178 }
179 if (!source_url || !proxy_list) {
180 LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
181 return DBUS_HANDLER_RESULT_HANDLED;
182 }
183 HandleReply(source_url, proxy_list);
184 return DBUS_HANDLER_RESULT_HANDLED;
185}
186
187bool ChromeBrowserProxyResolver::DeleteUrlState(
188 const string& source_url,
189 bool delete_timer,
190 pair<ProxiesResolvedFn, void*>* callback) {
191 {
192 CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
193 TEST_AND_RETURN_FALSE(it != callbacks_.end());
194 TEST_AND_RETURN_FALSE(it->first == source_url);
195 if (callback)
196 *callback = it->second;
197 callbacks_.erase(it);
198 }
199 {
200 TimeoutsMap::iterator it = timers_.lower_bound(source_url);
201 TEST_AND_RETURN_FALSE(it != timers_.end());
202 TEST_AND_RETURN_FALSE(it->first == source_url);
203 if (delete_timer)
204 g_source_destroy(it->second);
205 timers_.erase(it);
206 }
207 return true;
208}
209
210void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
211 const string& proxy_list) {
212 pair<ProxiesResolvedFn, void*> callback;
213 TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
214 (*callback.first)(ParseProxyString(proxy_list), callback.second);
215}
216
217void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
218 LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
219 pair<ProxiesResolvedFn, void*> callback;
220 TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
221 deque<string> proxies;
222 proxies.push_back(kNoProxy);
223 (*callback.first)(proxies, callback.second);
224}
225
226deque<string> ChromeBrowserProxyResolver::ParseProxyString(
227 const string& input) {
228 deque<string> ret;
229 // Some of this code taken from
230 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
231 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
232 StringTokenizer entry_tok(input, ";");
233 while (entry_tok.GetNext()) {
234 string token = entry_tok.token();
235 TrimWhitespaceASCII(token, TRIM_ALL, &token);
236
237 // Start by finding the first space (if any).
238 std::string::iterator space;
239 for (space = token.begin(); space != token.end(); ++space) {
240 if (IsAsciiWhitespace(*space)) {
241 break;
242 }
243 }
244
245 string scheme = string(token.begin(), space);
246 StringToLowerASCII(&scheme);
247 // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
248 if (scheme == "socks")
249 scheme += "4";
250 else if (scheme == "proxy")
251 scheme = "http";
252 else if (scheme != "https" &&
253 scheme != "socks4" &&
254 scheme != "socks5" &&
255 scheme != "direct")
256 continue; // Invalid proxy scheme
257
258 string host_and_port = string(space, token.end());
259 TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port);
260 if (scheme != "direct" && host_and_port.empty())
261 continue; // Must supply host/port when non-direct proxy used.
262 ret.push_back(scheme + "://" + host_and_port);
263 }
264 if (ret.empty() || *ret.rbegin() != kNoProxy)
265 ret.push_back(kNoProxy);
266 return ret;
267}
268
269} // namespace chromeos_update_engine