// Copyright (c) 2006-2008 The Chromium 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 "net/proxy/proxy_config.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
namespace net {
ProxyConfig::ProxyConfig()
: auto_detect(false),
proxy_bypass_local_names(false),
id_(INVALID_ID) {
}
bool ProxyConfig::Equals(const ProxyConfig& other) const {
// The two configs can have different IDs. We are just interested in if they
// have the same settings.
return auto_detect == other.auto_detect &&
pac_url == other.pac_url &&
proxy_rules == other.proxy_rules &&
proxy_bypass == other.proxy_bypass &&
proxy_bypass_local_names == other.proxy_bypass_local_names;
}
bool ProxyConfig::MayRequirePACResolver() const {
return auto_detect || pac_url.is_valid();
}
void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
// Reset.
type = TYPE_NO_RULES;
single_proxy = ProxyServer();
proxy_for_http = ProxyServer();
proxy_for_https = ProxyServer();
proxy_for_ftp = ProxyServer();
socks_proxy = ProxyServer();
StringTokenizer proxy_server_list(proxy_rules, ";");
while (proxy_server_list.GetNext()) {
StringTokenizer proxy_server_for_scheme(
proxy_server_list.token_begin(), proxy_server_list.token_end(), "=");
while (proxy_server_for_scheme.GetNext()) {
std::string url_scheme = proxy_server_for_scheme.token();
// If we fail to get the proxy server here, it means that
// this is a regular proxy server configuration, i.e. proxies
// are not configured per protocol.
if (!proxy_server_for_scheme.GetNext()) {
if (type == TYPE_PROXY_PER_SCHEME)
continue; // Unexpected.
single_proxy = ProxyServer::FromURI(url_scheme,
ProxyServer::SCHEME_HTTP);
type = TYPE_SINGLE_PROXY;
return;
}
// Trim whitespace off the url scheme.
TrimWhitespaceASCII(url_scheme, TRIM_ALL, &url_scheme);
// Add it to the per-scheme mappings (if supported scheme).
type = TYPE_PROXY_PER_SCHEME;
if (ProxyServer* entry = MapSchemeToProxy(url_scheme)) {
std::string proxy_server_token = proxy_server_for_scheme.token();
ProxyServer::Scheme scheme = (entry == &socks_proxy) ?
ProxyServer::SCHEME_SOCKS4 : ProxyServer::SCHEME_HTTP;
*entry = ProxyServer::FromURI(proxy_server_token, scheme);
}
}
}
}
const ProxyServer* ProxyConfig::ProxyRules::MapUrlSchemeToProxy(
const std::string& url_scheme) const {
const ProxyServer* proxy_server =
const_cast<ProxyRules*>(this)->MapSchemeToProxy(url_scheme);
if (proxy_server && proxy_server->is_valid())
return proxy_server;
if (socks_proxy.is_valid())
return &socks_proxy;
return NULL; // No mapping for this scheme. Use direct.
}
ProxyServer* ProxyConfig::ProxyRules::MapSchemeToProxy(
const std::string& scheme) {
DCHECK(type == TYPE_PROXY_PER_SCHEME);
if (scheme == "http")
return &proxy_for_http;
if (scheme == "https")
return &proxy_for_https;
if (scheme == "ftp")
return &proxy_for_ftp;
if (scheme == "socks")
return &socks_proxy;
return NULL; // No mapping for this scheme.
}
namespace {
// Returns true if the given string represents an IP address.
bool IsIPAddress(const std::string& domain) {
// From GURL::HostIsIPAddress()
url_canon::RawCanonOutputT<char, 128> ignored_output;
url_canon::CanonHostInfo host_info;
url_parse::Component domain_comp(0, domain.size());
url_canon::CanonicalizeIPAddress(domain.c_str(), domain_comp,
&ignored_output, &host_info);
return host_info.IsIPAddress();
}
} // namespace
void ProxyConfig::ParseNoProxyList(const std::string& no_proxy) {
proxy_bypass.clear();
if (no_proxy.empty())
return;
// Traditional semantics:
// A single "*" is specifically allowed and unproxies anything.
// "*" wildcards other than a single "*" entry are not universally
// supported. We will support them, as we get * wildcards for free
// (see MatchPatternASCII() called from
// ProxyService::ShouldBypassProxyForURL()).
// no_proxy is a comma-separated list of <trailing_domain>[:<port>].
// If no port is specified then any port matches.
// The historical definition has trailing_domain match using a simple
// string "endswith" test, so that the match need not correspond to a
// "." boundary. For example: "google.com" matches "igoogle.com" too.
// Seems like that could be confusing, but we'll obey tradition.
// IP CIDR patterns are supposed to be supported too. We intend
// to do this in proxy_service.cc, but it's currently a TODO.
// See: http://crbug.com/9835.
StringTokenizer no_proxy_list(no_proxy, ",");
while (no_proxy_list.GetNext()) {
std::string bypass_entry = no_proxy_list.token();
TrimWhitespaceASCII(bypass_entry, TRIM_ALL, &bypass_entry);
if (bypass_entry.empty())
continue;
if (bypass_entry.at(0) != '*') {
// Insert a wildcard * to obtain an endsWith match, unless the
// entry looks like it might be an IP or CIDR.
// First look for either a :<port> or CIDR mask length suffix.
std::string::const_iterator begin = bypass_entry.begin();
std::string::const_iterator scan = bypass_entry.end() - 1;
while (scan > begin && IsAsciiDigit(*scan))
--scan;
std::string potential_ip;
if (*scan == '/' || *scan == ':')
potential_ip = std::string(begin, scan - 1);
else
potential_ip = bypass_entry;
if (!IsIPAddress(potential_ip)) {
// Do insert a wildcard.
bypass_entry.insert(0, "*");
}
// TODO(sdoyon): When CIDR matching is implemented in
// proxy_service.cc, consider making proxy_bypass more
// sophisticated to avoid parsing out the string on every
// request.
}
proxy_bypass.push_back(bypass_entry);
}
}
} // namespace net
namespace {
// Helper to stringize a ProxyServer.
std::ostream& operator<<(std::ostream& out,
const net::ProxyServer& proxy_server) {
if (proxy_server.is_valid())
out << proxy_server.ToURI();
return out;
}
const char* BoolToYesNoString(bool b) {
return b ? "Yes" : "No";
}
} // namespace
std::ostream& operator<<(std::ostream& out,
const net::ProxyConfig::ProxyRules& rules) {
// Stringize the type enum.
std::string type;
switch (rules.type) {
case net::ProxyConfig::ProxyRules::TYPE_NO_RULES:
type = "TYPE_NO_RULES";
break;
case net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME:
type = "TYPE_PROXY_PER_SCHEME";
break;
case net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY:
type = "TYPE_SINGLE_PROXY";
break;
default:
type = IntToString(rules.type);
break;
}
return out << " {\n"
<< " type: " << type << "\n"
<< " single_proxy: " << rules.single_proxy << "\n"
<< " proxy_for_http: " << rules.proxy_for_http << "\n"
<< " proxy_for_https: " << rules.proxy_for_https << "\n"
<< " proxy_for_ftp: " << rules.proxy_for_ftp << "\n"
<< " socks_proxy: " << rules.socks_proxy << "\n"
<< " }";
}
std::ostream& operator<<(std::ostream& out, const net::ProxyConfig& config) {
// "Automatic" settings.
out << "Automatic settings:\n";
out << " Auto-detect: " << BoolToYesNoString(config.auto_detect) << "\n";
out << " Custom PAC script: ";
if (config.pac_url.is_valid())
out << config.pac_url;
else
out << "[None]";
out << "\n";
// "Manual" settings.
out << "Manual settings:\n";
out << " Proxy server: ";
switch (config.proxy_rules.type) {
case net::ProxyConfig::ProxyRules::TYPE_NO_RULES:
out << "[None]\n";
break;
case net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY:
out << config.proxy_rules.single_proxy;
out << "\n";
break;
case net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME:
out << "\n";
if (config.proxy_rules.proxy_for_http.is_valid())
out << " HTTP: " << config.proxy_rules.proxy_for_http << "\n";
if (config.proxy_rules.proxy_for_https.is_valid())
out << " HTTPS: " << config.proxy_rules.proxy_for_https << "\n";
if (config.proxy_rules.proxy_for_ftp.is_valid())
out << " FTP: " << config.proxy_rules.proxy_for_ftp << "\n";
if (config.proxy_rules.socks_proxy.is_valid())
out << " SOCKS: " << config.proxy_rules.socks_proxy << "\n";
break;
}
out << " Bypass list: ";
if (config.proxy_bypass.empty()) {
out << "[None]\n";
} else {
out << "\n";
std::vector<std::string>::const_iterator it;
for (it = config.proxy_bypass.begin();
it != config.proxy_bypass.end(); ++it) {
out << " " << *it << "\n";
}
}
out << " Bypass local names: "
<< BoolToYesNoString(config.proxy_bypass_local_names);
return out;
}