blob: 7b0bdc66242c656b17d2dbe6f5057c0a6c53ed0d [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
107ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
108 if (proxy_) {
109 GError* gerror = NULL;
110 DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
111 TEST_AND_RETURN(gbus);
112 DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
113 dbus_->DbusConnectionRemoveFilter(
114 connection,
115 &ChromeBrowserProxyResolver::StaticFilterMessage,
116 this);
117 }
118 // Kill outstanding timers
119 for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
120 ++it) {
121 g_source_destroy(it->second);
122 it->second = NULL;
123 }
124}
125
126bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
127 ProxiesResolvedFn callback,
128 void* data) {
129 GError* error = NULL;
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700130 guint timeout = timeout_;
131 if (!proxy_ || !dbus_->ProxyCall(
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800132 proxy_,
133 kLibCrosServiceResolveNetworkProxyMethodName,
134 &error,
135 G_TYPE_STRING, url.c_str(),
136 G_TYPE_STRING, kLibCrosProxyResolveSignalInterface,
137 G_TYPE_STRING, kLibCrosProxyResolveName,
138 G_TYPE_INVALID, G_TYPE_INVALID)) {
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700139 LOG(WARNING) << "dbus_g_proxy_call failed: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700140 << utils::GetAndFreeGError(&error)
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700141 << " Continuing with no proxy.";
142 timeout = 0;
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800143 }
144 callbacks_.insert(make_pair(url, make_pair(callback, data)));
145 Closure* closure = NewCallback(this,
146 &ChromeBrowserProxyResolver::HandleTimeout,
147 url);
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700148 GSource* timer = g_timeout_source_new_seconds(timeout);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800149 g_source_set_callback(timer, &utils::GlibRunClosure, closure, NULL);
150 g_source_attach(timer, NULL);
151 timers_.insert(make_pair(url, timer));
152 return true;
153}
154
155DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
156 DBusConnection* connection,
157 DBusMessage* message) {
158 // Code lifted from libcros.
159 if (!dbus_->DbusMessageIsSignal(message,
160 kLibCrosProxyResolveSignalInterface,
161 kLibCrosProxyResolveName)) {
162 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
163 }
164 // Get args
165 char* source_url = NULL;
166 char* proxy_list = NULL;
167 char* error = NULL;
168 DBusError arg_error;
169 dbus_error_init(&arg_error);
170 if (!dbus_->DbusMessageGetArgs(message, &arg_error,
171 DBUS_TYPE_STRING, &source_url,
172 DBUS_TYPE_STRING, &proxy_list,
173 DBUS_TYPE_STRING, &error,
174 DBUS_TYPE_INVALID)) {
175 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();
234 TrimWhitespaceASCII(token, TRIM_ALL, &token);
235
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());
258 TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port);
259 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