262 lines
7.5 KiB
C++
262 lines
7.5 KiB
C++
|
|
// Copyright 2011 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "net/proxy_resolution/proxy_bypass_rules.h"
|
||
|
|
|
||
|
|
#include "base/strings/string_tokenizer.h"
|
||
|
|
#include "base/strings/string_util.h"
|
||
|
|
#include "build/build_config.h"
|
||
|
|
#include "net/base/url_util.h"
|
||
|
|
|
||
|
|
namespace net {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
// The <-loopback> rule corresponds with "remove the implicitly added bypass
|
||
|
|
// rules".
|
||
|
|
//
|
||
|
|
// The name <-loopback> is not a very precise name (as the implicit rules cover
|
||
|
|
// more than strictly loopback addresses), however this is the name that is
|
||
|
|
// used on Windows so re-used here.
|
||
|
|
//
|
||
|
|
// For platform-differences between implicit rules see
|
||
|
|
// ProxyResolverRules::MatchesImplicitRules().
|
||
|
|
const char kSubtractImplicitBypasses[] = "<-loopback>";
|
||
|
|
|
||
|
|
// The <local> rule bypasses any hostname that has no dots (and is not
|
||
|
|
// an IP literal). The name is misleading as it has nothing to do with
|
||
|
|
// localhost/loopback addresses, and would have better been called
|
||
|
|
// something like "simple hostnames". However this is the name used on
|
||
|
|
// Windows so is matched here.
|
||
|
|
const char kBypassSimpleHostnames[] = "<local>";
|
||
|
|
|
||
|
|
bool IsLinkLocalIP(const GURL& url) {
|
||
|
|
// Quick fail if definitely not link-local, to avoid doing unnecessary work in
|
||
|
|
// common case.
|
||
|
|
if (!(base::StartsWith(url.host_piece(), "169.254.") ||
|
||
|
|
base::StartsWith(url.host_piece(), "["))) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
IPAddress ip_address;
|
||
|
|
if (!ip_address.AssignFromIPLiteral(url.HostNoBracketsPiece()))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
return ip_address.IsLinkLocal();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Returns true if the URL's host is an IPv6 literal in the range
|
||
|
|
// [::ffff:127.0.0.1]/104.
|
||
|
|
//
|
||
|
|
// Note that net::IsLocalhost() does not currently return true for such
|
||
|
|
// addresses. However for proxy resolving such URLs should bypass the use
|
||
|
|
// of a PAC script, since the destination is local.
|
||
|
|
bool IsIPv4MappedLoopback(const GURL& url) {
|
||
|
|
if (!base::StartsWith(url.host_piece(), "[::ffff"))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
IPAddress ip_address;
|
||
|
|
if (!ip_address.AssignFromIPLiteral(url.HostNoBracketsPiece()))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
if (!ip_address.IsIPv4MappedIPv6())
|
||
|
|
return false;
|
||
|
|
|
||
|
|
return ip_address.bytes()[12] == 127;
|
||
|
|
}
|
||
|
|
|
||
|
|
class BypassSimpleHostnamesRule : public SchemeHostPortMatcherRule {
|
||
|
|
public:
|
||
|
|
BypassSimpleHostnamesRule() = default;
|
||
|
|
|
||
|
|
BypassSimpleHostnamesRule(const BypassSimpleHostnamesRule&) = delete;
|
||
|
|
BypassSimpleHostnamesRule& operator=(const BypassSimpleHostnamesRule&) =
|
||
|
|
delete;
|
||
|
|
|
||
|
|
SchemeHostPortMatcherResult Evaluate(const GURL& url) const override {
|
||
|
|
return ((url.host_piece().find('.') == std::string::npos) &&
|
||
|
|
!url.HostIsIPAddress())
|
||
|
|
? SchemeHostPortMatcherResult::kInclude
|
||
|
|
: SchemeHostPortMatcherResult::kNoMatch;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string ToString() const override { return kBypassSimpleHostnames; }
|
||
|
|
};
|
||
|
|
|
||
|
|
class SubtractImplicitBypassesRule : public SchemeHostPortMatcherRule {
|
||
|
|
public:
|
||
|
|
SubtractImplicitBypassesRule() = default;
|
||
|
|
|
||
|
|
SubtractImplicitBypassesRule(const SubtractImplicitBypassesRule&) = delete;
|
||
|
|
SubtractImplicitBypassesRule& operator=(const SubtractImplicitBypassesRule&) =
|
||
|
|
delete;
|
||
|
|
|
||
|
|
SchemeHostPortMatcherResult Evaluate(const GURL& url) const override {
|
||
|
|
return ProxyBypassRules::MatchesImplicitRules(url)
|
||
|
|
? SchemeHostPortMatcherResult::kExclude
|
||
|
|
: SchemeHostPortMatcherResult::kNoMatch;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string ToString() const override { return kSubtractImplicitBypasses; }
|
||
|
|
};
|
||
|
|
|
||
|
|
std::unique_ptr<SchemeHostPortMatcherRule> ParseRule(
|
||
|
|
base::StringPiece raw_untrimmed) {
|
||
|
|
base::StringPiece raw =
|
||
|
|
base::TrimWhitespaceASCII(raw_untrimmed, base::TRIM_ALL);
|
||
|
|
|
||
|
|
// <local> and <-loopback> are special syntax used by WinInet's bypass list
|
||
|
|
// -- we allow it on all platforms and interpret it the same way.
|
||
|
|
if (base::EqualsCaseInsensitiveASCII(raw, kBypassSimpleHostnames))
|
||
|
|
return std::make_unique<BypassSimpleHostnamesRule>();
|
||
|
|
if (base::EqualsCaseInsensitiveASCII(raw, kSubtractImplicitBypasses))
|
||
|
|
return std::make_unique<SubtractImplicitBypassesRule>();
|
||
|
|
|
||
|
|
return SchemeHostPortMatcherRule::FromUntrimmedRawString(raw_untrimmed);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
constexpr char net::ProxyBypassRules::kBypassListDelimeter[];
|
||
|
|
|
||
|
|
ProxyBypassRules::ProxyBypassRules() = default;
|
||
|
|
|
||
|
|
ProxyBypassRules::ProxyBypassRules(const ProxyBypassRules& rhs) {
|
||
|
|
*this = rhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
ProxyBypassRules::ProxyBypassRules(ProxyBypassRules&& rhs) {
|
||
|
|
*this = std::move(rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
ProxyBypassRules::~ProxyBypassRules() = default;
|
||
|
|
|
||
|
|
ProxyBypassRules& ProxyBypassRules::operator=(const ProxyBypassRules& rhs) {
|
||
|
|
ParseFromString(rhs.ToString());
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ProxyBypassRules& ProxyBypassRules::operator=(ProxyBypassRules&& rhs) {
|
||
|
|
matcher_ = std::move(rhs.matcher_);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProxyBypassRules::ReplaceRule(
|
||
|
|
size_t index,
|
||
|
|
std::unique_ptr<SchemeHostPortMatcherRule> rule) {
|
||
|
|
matcher_.ReplaceRule(index, std::move(rule));
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ProxyBypassRules::Matches(const GURL& url, bool reverse) const {
|
||
|
|
switch (matcher_.Evaluate(url)) {
|
||
|
|
case SchemeHostPortMatcherResult::kInclude:
|
||
|
|
return !reverse;
|
||
|
|
case SchemeHostPortMatcherResult::kExclude:
|
||
|
|
return reverse;
|
||
|
|
case SchemeHostPortMatcherResult::kNoMatch:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// If none of the explicit rules matched, fall back to the implicit rules.
|
||
|
|
bool matches_implicit = MatchesImplicitRules(url);
|
||
|
|
if (matches_implicit)
|
||
|
|
return matches_implicit;
|
||
|
|
|
||
|
|
return reverse;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ProxyBypassRules::operator==(const ProxyBypassRules& other) const {
|
||
|
|
if (rules().size() != other.rules().size())
|
||
|
|
return false;
|
||
|
|
|
||
|
|
for (size_t i = 0; i < rules().size(); ++i) {
|
||
|
|
if (rules()[i]->ToString() != other.rules()[i]->ToString())
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProxyBypassRules::ParseFromString(const std::string& raw) {
|
||
|
|
Clear();
|
||
|
|
|
||
|
|
base::StringTokenizer entries(
|
||
|
|
raw, SchemeHostPortMatcher::kParseRuleListDelimiterList);
|
||
|
|
while (entries.GetNext()) {
|
||
|
|
AddRuleFromString(entries.token_piece());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProxyBypassRules::PrependRuleToBypassSimpleHostnames() {
|
||
|
|
matcher_.AddAsFirstRule(std::make_unique<BypassSimpleHostnamesRule>());
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ProxyBypassRules::AddRuleFromString(base::StringPiece raw_untrimmed) {
|
||
|
|
auto rule = ParseRule(raw_untrimmed);
|
||
|
|
|
||
|
|
if (rule) {
|
||
|
|
matcher_.AddAsLastRule(std::move(rule));
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProxyBypassRules::AddRulesToSubtractImplicit() {
|
||
|
|
matcher_.AddAsLastRule(std::make_unique<SubtractImplicitBypassesRule>());
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string ProxyBypassRules::GetRulesToSubtractImplicit() {
|
||
|
|
ProxyBypassRules rules;
|
||
|
|
rules.AddRulesToSubtractImplicit();
|
||
|
|
return rules.ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string ProxyBypassRules::ToString() const {
|
||
|
|
return matcher_.ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
void ProxyBypassRules::Clear() {
|
||
|
|
matcher_.Clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ProxyBypassRules::MatchesImplicitRules(const GURL& url) {
|
||
|
|
// On Windows the implict rules are:
|
||
|
|
//
|
||
|
|
// localhost
|
||
|
|
// loopback
|
||
|
|
// 127.0.0.1
|
||
|
|
// [::1]
|
||
|
|
// 169.254/16
|
||
|
|
// [FE80::]/10
|
||
|
|
//
|
||
|
|
// And on macOS they are:
|
||
|
|
//
|
||
|
|
// localhost
|
||
|
|
// 127.0.0.1/8
|
||
|
|
// [::1]
|
||
|
|
// 169.254/16
|
||
|
|
//
|
||
|
|
// Our implicit rules are approximately:
|
||
|
|
//
|
||
|
|
// localhost
|
||
|
|
// localhost.
|
||
|
|
// *.localhost
|
||
|
|
// loopback [Windows only]
|
||
|
|
// loopback. [Windows only]
|
||
|
|
// [::1]
|
||
|
|
// 127.0.0.1/8
|
||
|
|
// 169.254/16
|
||
|
|
// [FE80::]/10
|
||
|
|
return IsLocalhost(url) || IsIPv4MappedLoopback(url) ||
|
||
|
|
IsLinkLocalIP(url)
|
||
|
|
#if BUILDFLAG(IS_WIN)
|
||
|
|
// See http://crbug.com/904889
|
||
|
|
|| (url.host_piece() == "loopback") ||
|
||
|
|
(url.host_piece() == "loopback.")
|
||
|
|
#endif
|
||
|
|
;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace net
|