blob: 3841fe9cbd8292c77316bd2d5c0420391cab5ebe [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 {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080048
Andrew de los Reyes000d8952011-03-02 15:21:14 -080049const int kTimeout = 5; // seconds
50
Andrew de los Reyes000d8952011-03-02 15:21:14 -080051} // namespace {}
52
53ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(DbusGlibInterface* dbus)
54 : dbus_(dbus), proxy_(NULL), timeout_(kTimeout) {}
55
56bool ChromeBrowserProxyResolver::Init() {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080057 if (proxy_)
58 return true; // Already initialized.
59
60 // Set up signal handler. Code lifted from libcros.
61 GError* g_error = NULL;
62 DBusGConnection* bus = dbus_->BusGet(DBUS_BUS_SYSTEM, &g_error);
63 TEST_AND_RETURN_FALSE(bus);
64 DBusConnection* connection = dbus_->ConnectionGetConnection(bus);
Andrew de los Reyes000d8952011-03-02 15:21:14 -080065 TEST_AND_RETURN_FALSE(connection);
Gilad Arnoldb752fb32014-03-03 12:23:39 -080066
67 DBusError dbus_error;
68 dbus_error_init(&dbus_error);
69 dbus_->DbusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter,
70 &dbus_error);
71 TEST_AND_RETURN_FALSE(!dbus_error_is_set(&dbus_error));
Andrew de los Reyes000d8952011-03-02 15:21:14 -080072 TEST_AND_RETURN_FALSE(dbus_->DbusConnectionAddFilter(
73 connection,
74 &ChromeBrowserProxyResolver::StaticFilterMessage,
75 this,
76 NULL));
77
Gilad Arnoldb752fb32014-03-03 12:23:39 -080078 proxy_ = dbus_->ProxyNewForName(bus, kLibCrosServiceName, kLibCrosServicePath,
79 kLibCrosServiceInterface);
Andrew de los Reyes000d8952011-03-02 15:21:14 -080080 if (!proxy_) {
81 dbus_->DbusConnectionRemoveFilter(
82 connection,
83 &ChromeBrowserProxyResolver::StaticFilterMessage,
84 this);
85 }
86 TEST_AND_RETURN_FALSE(proxy_); // For the error log
87 return true;
88}
89
90ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080091 // Remove DBus connection filters and Kill proxy object.
92 if (proxy_) {
93 GError* gerror = NULL;
94 DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
95 if (gbus) {
96 DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
97 dbus_->DbusConnectionRemoveFilter(
98 connection,
99 &ChromeBrowserProxyResolver::StaticFilterMessage,
100 this);
101 }
102 dbus_->ProxyUnref(proxy_);
103 }
104
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800105 // Kill outstanding timers
106 for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
107 ++it) {
108 g_source_destroy(it->second);
109 it->second = NULL;
110 }
111}
112
113bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
114 ProxiesResolvedFn callback,
115 void* data) {
116 GError* error = NULL;
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700117 guint timeout = timeout_;
Gilad Arnold1877c392012-02-10 11:34:33 -0800118 if (proxy_) {
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800119 if (!dbus_->ProxyCall_3_0(proxy_,
120 kLibCrosServiceResolveNetworkProxyMethodName,
121 &error,
122 url.c_str(),
123 kLibCrosProxyResolveSignalInterface,
124 kLibCrosProxyResolveName)) {
Gilad Arnold1877c392012-02-10 11:34:33 -0800125
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800126 if (error) {
127 LOG(WARNING) << "dbus_g_proxy_call failed, continuing with no proxy: "
128 << utils::GetAndFreeGError(&error);
Gilad Arnold1877c392012-02-10 11:34:33 -0800129 } else {
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800130 LOG(WARNING) << "dbus_g_proxy_call failed with no error string, "
131 "continuing with no proxy.";
Gilad Arnold1877c392012-02-10 11:34:33 -0800132 }
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800133 timeout = 0;
134 }
Gilad Arnold1877c392012-02-10 11:34:33 -0800135 } else {
Brandon Philips962873e2013-03-26 13:58:51 -0700136 LOG(WARNING) << "dbus proxy object missing, continuing with no proxy.";
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700137 timeout = 0;
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800138 }
Gilad Arnold1877c392012-02-10 11:34:33 -0800139
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800140 callbacks_.insert(make_pair(url, make_pair(callback, data)));
141 Closure* closure = NewCallback(this,
142 &ChromeBrowserProxyResolver::HandleTimeout,
143 url);
Andrew de los Reyes518502a2011-03-14 14:19:39 -0700144 GSource* timer = g_timeout_source_new_seconds(timeout);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800145 g_source_set_callback(timer, &utils::GlibRunClosure, closure, NULL);
146 g_source_attach(timer, NULL);
147 timers_.insert(make_pair(url, timer));
148 return true;
149}
150
151DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
152 DBusConnection* connection,
153 DBusMessage* message) {
154 // Code lifted from libcros.
155 if (!dbus_->DbusMessageIsSignal(message,
156 kLibCrosProxyResolveSignalInterface,
157 kLibCrosProxyResolveName)) {
158 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
159 }
160 // Get args
161 char* source_url = NULL;
162 char* proxy_list = NULL;
163 char* error = NULL;
164 DBusError arg_error;
165 dbus_error_init(&arg_error);
Gilad Arnoldb752fb32014-03-03 12:23:39 -0800166 if (!dbus_->DbusMessageGetArgs_3(message, &arg_error,
167 &source_url,
168 &proxy_list,
169 &error)) {
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800170 LOG(ERROR) << "Error reading dbus signal.";
171 return DBUS_HANDLER_RESULT_HANDLED;
172 }
173 if (!source_url || !proxy_list) {
174 LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
175 return DBUS_HANDLER_RESULT_HANDLED;
176 }
177 HandleReply(source_url, proxy_list);
178 return DBUS_HANDLER_RESULT_HANDLED;
179}
180
181bool ChromeBrowserProxyResolver::DeleteUrlState(
182 const string& source_url,
183 bool delete_timer,
184 pair<ProxiesResolvedFn, void*>* callback) {
185 {
186 CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
187 TEST_AND_RETURN_FALSE(it != callbacks_.end());
188 TEST_AND_RETURN_FALSE(it->first == source_url);
189 if (callback)
190 *callback = it->second;
191 callbacks_.erase(it);
192 }
193 {
194 TimeoutsMap::iterator it = timers_.lower_bound(source_url);
195 TEST_AND_RETURN_FALSE(it != timers_.end());
196 TEST_AND_RETURN_FALSE(it->first == source_url);
197 if (delete_timer)
198 g_source_destroy(it->second);
199 timers_.erase(it);
200 }
201 return true;
202}
203
204void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
205 const string& proxy_list) {
206 pair<ProxiesResolvedFn, void*> callback;
207 TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
208 (*callback.first)(ParseProxyString(proxy_list), callback.second);
209}
210
211void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
212 LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
213 pair<ProxiesResolvedFn, void*> callback;
214 TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
215 deque<string> proxies;
216 proxies.push_back(kNoProxy);
217 (*callback.first)(proxies, callback.second);
218}
219
220deque<string> ChromeBrowserProxyResolver::ParseProxyString(
221 const string& input) {
222 deque<string> ret;
223 // Some of this code taken from
224 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
225 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
226 StringTokenizer entry_tok(input, ";");
227 while (entry_tok.GetNext()) {
228 string token = entry_tok.token();
229 TrimWhitespaceASCII(token, TRIM_ALL, &token);
230
231 // Start by finding the first space (if any).
232 std::string::iterator space;
233 for (space = token.begin(); space != token.end(); ++space) {
234 if (IsAsciiWhitespace(*space)) {
235 break;
236 }
237 }
238
239 string scheme = string(token.begin(), space);
240 StringToLowerASCII(&scheme);
241 // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
242 if (scheme == "socks")
243 scheme += "4";
244 else if (scheme == "proxy")
245 scheme = "http";
246 else if (scheme != "https" &&
247 scheme != "socks4" &&
248 scheme != "socks5" &&
249 scheme != "direct")
250 continue; // Invalid proxy scheme
251
252 string host_and_port = string(space, token.end());
253 TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port);
254 if (scheme != "direct" && host_and_port.empty())
255 continue; // Must supply host/port when non-direct proxy used.
256 ret.push_back(scheme + "://" + host_and_port);
257 }
258 if (ret.empty() || *ret.rbegin() != kNoProxy)
259 ret.push_back(kNoProxy);
260 return ret;
261}
262
263} // namespace chromeos_update_engine