OLD | NEW |
(Empty) | |
| 1 // 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 |
| 19 namespace chromeos_update_engine { |
| 20 |
| 21 using google::protobuf::Closure; |
| 22 using google::protobuf::NewCallback; |
| 23 using std::deque; |
| 24 using std::make_pair; |
| 25 using std::multimap; |
| 26 using std::pair; |
| 27 using std::string; |
| 28 |
| 29 #define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved" |
| 30 #define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE \ |
| 31 "org.chromium.UpdateEngineLibcrosProxyResolvedInterface" |
| 32 const char kLibCrosServiceName[] = "org.chromium.LibCrosService"; |
| 33 const char kLibCrosServicePath[] = "/org/chromium/LibCrosService"; |
| 34 const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface"; |
| 35 const char kLibCrosServiceResolveNetworkProxyMethodName[] = |
| 36 "ResolveNetworkProxy"; |
| 37 const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME; |
| 38 const char kLibCrosProxyResolveSignalInterface[] = |
| 39 LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE; |
| 40 const 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 |
| 47 namespace { |
| 48 const int kTimeout = 5; // seconds |
| 49 |
| 50 DBusGProxy* 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 |
| 73 ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(DbusGlibInterface* dbus) |
| 74 : dbus_(dbus), proxy_(NULL), timeout_(kTimeout) {} |
| 75 |
| 76 bool 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 |
| 108 ChromeBrowserProxyResolver::~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 |
| 127 bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url, |
| 128 ProxiesResolvedFn callback, |
| 129 void* data) { |
| 130 GError* error = NULL; |
| 131 TEST_AND_RETURN_FALSE(proxy_); |
| 132 if (!dbus_->ProxyCall( |
| 133 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)) { |
| 140 LOG(ERROR) << "dbus_g_proxy_call failed: " |
| 141 << utils::GetGErrorMessage(error); |
| 142 return false; |
| 143 } |
| 144 callbacks_.insert(make_pair(url, make_pair(callback, data))); |
| 145 Closure* closure = NewCallback(this, |
| 146 &ChromeBrowserProxyResolver::HandleTimeout, |
| 147 url); |
| 148 GSource* timer = g_timeout_source_new_seconds(timeout_); |
| 149 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 |
| 155 DBusHandlerResult 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 |
| 186 bool 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 |
| 209 void 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 |
| 216 void 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 |
| 225 deque<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 |
OLD | NEW |