blob: 25af4f6adaded3cf9af3a65179061cd46a50c78f [file] [log] [blame]
// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "update_engine/chrome_proxy_resolver.h"
#include <base/json/json_reader.h>
#include <base/memory/scoped_ptr.h>
#include <base/values.h>
#include "update_engine/utils.h"
using google::protobuf::Closure;
using google::protobuf::NewCallback;
using std::deque;
using std::string;
using std::vector;
namespace chromeos_update_engine {
const char kSessionManagerService[] = "org.chromium.SessionManager";
const char kSessionManagerPath[] = "/org/chromium/SessionManager";
const char kSessionManagerInterface[] = "org.chromium.SessionManagerInterface";
const char kSessionManagerRetrievePropertyMethod[] =
"RetrieveProperty";
const char kSessionManagerProxySettingsKey[] = "cros.proxy.everywhere";
bool ChromeProxyResolver::GetProxiesForUrl(
const std::string& url,
ProxiesResolvedFn callback,
void* data) {
ChromeProxyResolverClosureArgs args;
args.url = url;
args.callback = callback;
args.data = data;
Closure* closure = NewCallback(this,
&ChromeProxyResolver::GetProxiesForUrlCallback,
args);
g_idle_add(utils::GlibRunClosure, closure);
return true;
}
void ChromeProxyResolver::GetProxiesForUrlCallback(
ChromeProxyResolverClosureArgs args) {
deque<string> proxies;
// First, query dbus for the currently stored settings
DBusGProxy* proxy = DbusProxy();
if (proxy) {
string json_settings;
if (GetJsonProxySettings(proxy, &json_settings)) {
LOG(INFO) << "got settings:" << json_settings;
GetProxiesForUrlWithSettings(args.url, json_settings, &proxies);
}
}
(*args.callback)(proxies, args.data);
}
bool ChromeProxyResolver::GetJsonProxySettings(DBusGProxy* proxy,
std::string* out_json) {
gchar* value = NULL;
GArray* sig = NULL;
GError* error = NULL;
TEST_AND_RETURN_FALSE(
dbus_->ProxyCall(proxy,
kSessionManagerRetrievePropertyMethod,
&error,
G_TYPE_STRING, kSessionManagerProxySettingsKey,
G_TYPE_INVALID,
G_TYPE_STRING, &value,
DBUS_TYPE_G_UCHAR_ARRAY, &sig,
G_TYPE_INVALID));
g_array_free(sig, false);
out_json->assign(value);
g_free(value);
return true;
}
DBusGProxy* ChromeProxyResolver::DbusProxy() {
GError* error = NULL;
DBusGConnection* bus = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
if (!bus) {
LOG(ERROR) << "Failed to get System Dbus: "
<< utils::GetAndFreeGError(&error);
return NULL;
}
DBusGProxy* proxy = dbus_->ProxyNewForNameOwner(bus,
kSessionManagerService,
kSessionManagerPath,
kSessionManagerInterface,
&error);
if (!proxy) {
LOG(ERROR) << "Error getting FlimFlam proxy: "
<< utils::GetAndFreeGError(&error);
}
return proxy;
}
namespace {
enum ProxyMode {
kProxyModeDirect = 0,
kProxyModeAutoDetect,
kProxyModePACScript,
kProxyModeSingle,
kProxyModeProxyPerScheme
};
} // namespace {}
bool ChromeProxyResolver::GetProxiesForUrlWithSettings(
const string& url,
const string& json_settings,
std::deque<std::string>* out_proxies) {
base::JSONReader parser;
scoped_ptr<Value> root(
parser.JsonToValue(json_settings,
true, // check root is obj/arr
false)); // false = disallow trailing comma
if (!root.get()) {
LOG(ERROR) << "Unable to parse \"" << json_settings << "\": "
<< parser.GetErrorMessage();
return false;
}
TEST_AND_RETURN_FALSE(root->IsType(Value::TYPE_DICTIONARY));
DictionaryValue* root_dict = dynamic_cast<DictionaryValue*>(root.get());
TEST_AND_RETURN_FALSE(root_dict);
int mode = -1;
TEST_AND_RETURN_FALSE(root_dict->GetInteger("mode", &mode));
LOG(INFO) << "proxy mode: " << mode;
if (mode != kProxyModeSingle &&
mode != kProxyModeProxyPerScheme) {
LOG(INFO) << "unsupported proxy scheme";
out_proxies->clear();
out_proxies->push_back(kNoProxy);
return true;
}
if (mode == kProxyModeSingle) {
LOG(INFO) << "single proxy mode";
string proxy_string;
TEST_AND_RETURN_FALSE(root_dict->GetString("single.server", &proxy_string));
if (proxy_string.find("://") == string::npos) {
// missing protocol, assume http.
proxy_string = string("http://") + proxy_string;
}
out_proxies->clear();
out_proxies->push_back(proxy_string);
LOG(INFO) << "single proxy: " << (*out_proxies)[0];
out_proxies->push_back(kNoProxy);
return true;
}
// Proxy per scheme mode.
LOG(INFO) << "proxy per scheme mode";
// Find which scheme we are
bool url_is_http = utils::StringHasPrefix(url, "http://");
if (!url_is_http)
TEST_AND_RETURN_FALSE(utils::StringHasPrefix(url, "https://"));
// Using "proto_*" variables to refer to http or https
const string proto_path = url_is_http ? "http.server" : "https.server";
const string socks_path = "socks.server";
out_proxies->clear();
string proto_server, socks_server;
if (root_dict->GetString(proto_path, &proto_server)) {
if (proto_server.find("://") == string::npos) {
// missing protocol, assume http.
proto_server = string("http://") + proto_server;
}
out_proxies->push_back(proto_server);
LOG(INFO) << "got http/https server: " << proto_server;
}
if (root_dict->GetString(socks_path, &socks_server)) {
out_proxies->push_back(socks_server);
LOG(INFO) << "got socks server: " << proto_server;
}
out_proxies->push_back(kNoProxy);
return true;
}
bool ChromeProxyResolver::GetProxyType(const std::string& proxy,
curl_proxytype* out_type) {
if (utils::StringHasPrefix(proxy, "socks5://") ||
utils::StringHasPrefix(proxy, "socks://")) {
*out_type = CURLPROXY_SOCKS5_HOSTNAME;
return true;
}
if (utils::StringHasPrefix(proxy, "socks4://")) {
*out_type = CURLPROXY_SOCKS4A;
return true;
}
if (utils::StringHasPrefix(proxy, "http://") ||
utils::StringHasPrefix(proxy, "https://")) {
*out_type = CURLPROXY_HTTP;
return true;
}
if (utils::StringHasPrefix(proxy, kNoProxy)) {
// known failure case. don't log.
return false;
}
LOG(INFO) << "Unknown proxy type: " << proxy;
return false;
}
} // namespace chromeos_update_engine