1 // Copyright 2011 The Chromium Authors
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 "net/proxy_resolution/proxy_bypass_rules.h"
6 
7 #include "base/strings/string_tokenizer.h"
8 #include "base/strings/string_util.h"
9 #include "build/build_config.h"
10 #include "net/base/url_util.h"
11 
12 namespace net {
13 
14 namespace {
15 
16 // The <-loopback> rule corresponds with "remove the implicitly added bypass
17 // rules".
18 //
19 // The name <-loopback> is not a very precise name (as the implicit rules cover
20 // more than strictly loopback addresses), however this is the name that is
21 // used on Windows so re-used here.
22 //
23 // For platform-differences between implicit rules see
24 // ProxyResolverRules::MatchesImplicitRules().
25 const char kSubtractImplicitBypasses[] = "<-loopback>";
26 
27 // The <local> rule bypasses any hostname that has no dots (and is not
28 // an IP literal). The name is misleading as it has nothing to do with
29 // localhost/loopback addresses, and would have better been called
30 // something like "simple hostnames". However this is the name used on
31 // Windows so is matched here.
32 const char kBypassSimpleHostnames[] = "<local>";
33 
IsLinkLocalIP(const GURL & url)34 bool IsLinkLocalIP(const GURL& url) {
35   // Quick fail if definitely not link-local, to avoid doing unnecessary work in
36   // common case.
37   if (!(base::StartsWith(url.host_piece(), "169.254.") ||
38         base::StartsWith(url.host_piece(), "["))) {
39     return false;
40   }
41 
42   IPAddress ip_address;
43   if (!ip_address.AssignFromIPLiteral(url.HostNoBracketsPiece()))
44     return false;
45 
46   return ip_address.IsLinkLocal();
47 }
48 
49 // Returns true if the URL's host is an IPv6 literal in the range
50 // [::ffff:127.0.0.1]/104.
51 //
52 // Note that net::IsLocalhost() does not currently return true for such
53 // addresses. However for proxy resolving such URLs should bypass the use
54 // of a PAC script, since the destination is local.
IsIPv4MappedLoopback(const GURL & url)55 bool IsIPv4MappedLoopback(const GURL& url) {
56   if (!base::StartsWith(url.host_piece(), "[::ffff"))
57     return false;
58 
59   IPAddress ip_address;
60   if (!ip_address.AssignFromIPLiteral(url.HostNoBracketsPiece()))
61     return false;
62 
63   if (!ip_address.IsIPv4MappedIPv6())
64     return false;
65 
66   return ip_address.bytes()[12] == 127;
67 }
68 
69 class BypassSimpleHostnamesRule : public SchemeHostPortMatcherRule {
70  public:
71   BypassSimpleHostnamesRule() = default;
72 
73   BypassSimpleHostnamesRule(const BypassSimpleHostnamesRule&) = delete;
74   BypassSimpleHostnamesRule& operator=(const BypassSimpleHostnamesRule&) =
75       delete;
76 
Evaluate(const GURL & url) const77   SchemeHostPortMatcherResult Evaluate(const GURL& url) const override {
78     return ((url.host_piece().find('.') == std::string::npos) &&
79             !url.HostIsIPAddress())
80                ? SchemeHostPortMatcherResult::kInclude
81                : SchemeHostPortMatcherResult::kNoMatch;
82   }
83 
ToString() const84   std::string ToString() const override { return kBypassSimpleHostnames; }
85 };
86 
87 class SubtractImplicitBypassesRule : public SchemeHostPortMatcherRule {
88  public:
89   SubtractImplicitBypassesRule() = default;
90 
91   SubtractImplicitBypassesRule(const SubtractImplicitBypassesRule&) = delete;
92   SubtractImplicitBypassesRule& operator=(const SubtractImplicitBypassesRule&) =
93       delete;
94 
Evaluate(const GURL & url) const95   SchemeHostPortMatcherResult Evaluate(const GURL& url) const override {
96     return ProxyBypassRules::MatchesImplicitRules(url)
97                ? SchemeHostPortMatcherResult::kExclude
98                : SchemeHostPortMatcherResult::kNoMatch;
99   }
100 
ToString() const101   std::string ToString() const override { return kSubtractImplicitBypasses; }
102 };
103 
ParseRule(base::StringPiece raw_untrimmed)104 std::unique_ptr<SchemeHostPortMatcherRule> ParseRule(
105     base::StringPiece raw_untrimmed) {
106   base::StringPiece raw =
107       base::TrimWhitespaceASCII(raw_untrimmed, base::TRIM_ALL);
108 
109   // <local> and <-loopback> are special syntax used by WinInet's bypass list
110   // -- we allow it on all platforms and interpret it the same way.
111   if (base::EqualsCaseInsensitiveASCII(raw, kBypassSimpleHostnames))
112     return std::make_unique<BypassSimpleHostnamesRule>();
113   if (base::EqualsCaseInsensitiveASCII(raw, kSubtractImplicitBypasses))
114     return std::make_unique<SubtractImplicitBypassesRule>();
115 
116   return SchemeHostPortMatcherRule::FromUntrimmedRawString(raw_untrimmed);
117 }
118 
119 }  // namespace
120 
121 constexpr char net::ProxyBypassRules::kBypassListDelimeter[];
122 
123 ProxyBypassRules::ProxyBypassRules() = default;
124 
ProxyBypassRules(const ProxyBypassRules & rhs)125 ProxyBypassRules::ProxyBypassRules(const ProxyBypassRules& rhs) {
126   *this = rhs;
127 }
128 
ProxyBypassRules(ProxyBypassRules && rhs)129 ProxyBypassRules::ProxyBypassRules(ProxyBypassRules&& rhs) {
130   *this = std::move(rhs);
131 }
132 
133 ProxyBypassRules::~ProxyBypassRules() = default;
134 
operator =(const ProxyBypassRules & rhs)135 ProxyBypassRules& ProxyBypassRules::operator=(const ProxyBypassRules& rhs) {
136   ParseFromString(rhs.ToString());
137   return *this;
138 }
139 
operator =(ProxyBypassRules && rhs)140 ProxyBypassRules& ProxyBypassRules::operator=(ProxyBypassRules&& rhs) {
141   matcher_ = std::move(rhs.matcher_);
142   return *this;
143 }
144 
ReplaceRule(size_t index,std::unique_ptr<SchemeHostPortMatcherRule> rule)145 void ProxyBypassRules::ReplaceRule(
146     size_t index,
147     std::unique_ptr<SchemeHostPortMatcherRule> rule) {
148   matcher_.ReplaceRule(index, std::move(rule));
149 }
150 
Matches(const GURL & url,bool reverse) const151 bool ProxyBypassRules::Matches(const GURL& url, bool reverse) const {
152   switch (matcher_.Evaluate(url)) {
153     case SchemeHostPortMatcherResult::kInclude:
154       return !reverse;
155     case SchemeHostPortMatcherResult::kExclude:
156       return reverse;
157     case SchemeHostPortMatcherResult::kNoMatch:
158       break;
159   }
160 
161   // If none of the explicit rules matched, fall back to the implicit rules.
162   bool matches_implicit = MatchesImplicitRules(url);
163   if (matches_implicit)
164     return matches_implicit;
165 
166   return reverse;
167 }
168 
operator ==(const ProxyBypassRules & other) const169 bool ProxyBypassRules::operator==(const ProxyBypassRules& other) const {
170   if (rules().size() != other.rules().size())
171     return false;
172 
173   for (size_t i = 0; i < rules().size(); ++i) {
174     if (rules()[i]->ToString() != other.rules()[i]->ToString())
175       return false;
176   }
177   return true;
178 }
179 
ParseFromString(const std::string & raw)180 void ProxyBypassRules::ParseFromString(const std::string& raw) {
181   Clear();
182 
183   base::StringTokenizer entries(
184       raw, SchemeHostPortMatcher::kParseRuleListDelimiterList);
185   while (entries.GetNext()) {
186     AddRuleFromString(entries.token_piece());
187   }
188 }
189 
PrependRuleToBypassSimpleHostnames()190 void ProxyBypassRules::PrependRuleToBypassSimpleHostnames() {
191   matcher_.AddAsFirstRule(std::make_unique<BypassSimpleHostnamesRule>());
192 }
193 
AddRuleFromString(base::StringPiece raw_untrimmed)194 bool ProxyBypassRules::AddRuleFromString(base::StringPiece raw_untrimmed) {
195   auto rule = ParseRule(raw_untrimmed);
196 
197   if (rule) {
198     matcher_.AddAsLastRule(std::move(rule));
199     return true;
200   }
201 
202   return false;
203 }
204 
AddRulesToSubtractImplicit()205 void ProxyBypassRules::AddRulesToSubtractImplicit() {
206   matcher_.AddAsLastRule(std::make_unique<SubtractImplicitBypassesRule>());
207 }
208 
GetRulesToSubtractImplicit()209 std::string ProxyBypassRules::GetRulesToSubtractImplicit() {
210   ProxyBypassRules rules;
211   rules.AddRulesToSubtractImplicit();
212   return rules.ToString();
213 }
214 
ToString() const215 std::string ProxyBypassRules::ToString() const {
216   return matcher_.ToString();
217 }
218 
Clear()219 void ProxyBypassRules::Clear() {
220   matcher_.Clear();
221 }
222 
MatchesImplicitRules(const GURL & url)223 bool ProxyBypassRules::MatchesImplicitRules(const GURL& url) {
224   // On Windows the implict rules are:
225   //
226   //     localhost
227   //     loopback
228   //     127.0.0.1
229   //     [::1]
230   //     169.254/16
231   //     [FE80::]/10
232   //
233   // And on macOS they are:
234   //
235   //     localhost
236   //     127.0.0.1/8
237   //     [::1]
238   //     169.254/16
239   //
240   // Our implicit rules are approximately:
241   //
242   //     localhost
243   //     localhost.
244   //     *.localhost
245   //     loopback  [Windows only]
246   //     loopback. [Windows only]
247   //     [::1]
248   //     127.0.0.1/8
249   //     169.254/16
250   //     [FE80::]/10
251   return IsLocalhost(url) || IsIPv4MappedLoopback(url) ||
252          IsLinkLocalIP(url)
253 #if BUILDFLAG(IS_WIN)
254          // See http://crbug.com/904889
255          || (url.host_piece() == "loopback") ||
256          (url.host_piece() == "loopback.")
257 #endif
258       ;
259 }
260 
261 }  // namespace net
262