xref: /aosp_15_r20/external/cronet/net/proxy_resolution/proxy_bypass_rules.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 (!(url.host_piece().starts_with("169.254.") ||
38         url.host_piece().starts_with("["))) {
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 (!url.host_piece().starts_with("[::ffff")) {
57     return false;
58   }
59 
60   IPAddress ip_address;
61   if (!ip_address.AssignFromIPLiteral(url.HostNoBracketsPiece()))
62     return false;
63 
64   if (!ip_address.IsIPv4MappedIPv6())
65     return false;
66 
67   return ip_address.bytes()[12] == 127;
68 }
69 
70 class BypassSimpleHostnamesRule : public SchemeHostPortMatcherRule {
71  public:
72   BypassSimpleHostnamesRule() = default;
73 
74   BypassSimpleHostnamesRule(const BypassSimpleHostnamesRule&) = delete;
75   BypassSimpleHostnamesRule& operator=(const BypassSimpleHostnamesRule&) =
76       delete;
77 
Evaluate(const GURL & url) const78   SchemeHostPortMatcherResult Evaluate(const GURL& url) const override {
79     return ((url.host_piece().find('.') == std::string::npos) &&
80             !url.HostIsIPAddress())
81                ? SchemeHostPortMatcherResult::kInclude
82                : SchemeHostPortMatcherResult::kNoMatch;
83   }
84 
ToString() const85   std::string ToString() const override { return kBypassSimpleHostnames; }
86 };
87 
88 class SubtractImplicitBypassesRule : public SchemeHostPortMatcherRule {
89  public:
90   SubtractImplicitBypassesRule() = default;
91 
92   SubtractImplicitBypassesRule(const SubtractImplicitBypassesRule&) = delete;
93   SubtractImplicitBypassesRule& operator=(const SubtractImplicitBypassesRule&) =
94       delete;
95 
Evaluate(const GURL & url) const96   SchemeHostPortMatcherResult Evaluate(const GURL& url) const override {
97     return ProxyBypassRules::MatchesImplicitRules(url)
98                ? SchemeHostPortMatcherResult::kExclude
99                : SchemeHostPortMatcherResult::kNoMatch;
100   }
101 
ToString() const102   std::string ToString() const override { return kSubtractImplicitBypasses; }
103 };
104 
ParseRule(std::string_view raw_untrimmed)105 std::unique_ptr<SchemeHostPortMatcherRule> ParseRule(
106     std::string_view raw_untrimmed) {
107   std::string_view raw =
108       base::TrimWhitespaceASCII(raw_untrimmed, base::TRIM_ALL);
109 
110   // <local> and <-loopback> are special syntax used by WinInet's bypass list
111   // -- we allow it on all platforms and interpret it the same way.
112   if (base::EqualsCaseInsensitiveASCII(raw, kBypassSimpleHostnames))
113     return std::make_unique<BypassSimpleHostnamesRule>();
114   if (base::EqualsCaseInsensitiveASCII(raw, kSubtractImplicitBypasses))
115     return std::make_unique<SubtractImplicitBypassesRule>();
116 
117   return SchemeHostPortMatcherRule::FromUntrimmedRawString(raw_untrimmed);
118 }
119 
120 }  // namespace
121 
122 constexpr char net::ProxyBypassRules::kBypassListDelimeter[];
123 
124 ProxyBypassRules::ProxyBypassRules() = default;
125 
ProxyBypassRules(const ProxyBypassRules & rhs)126 ProxyBypassRules::ProxyBypassRules(const ProxyBypassRules& rhs) {
127   *this = rhs;
128 }
129 
ProxyBypassRules(ProxyBypassRules && rhs)130 ProxyBypassRules::ProxyBypassRules(ProxyBypassRules&& rhs) {
131   *this = std::move(rhs);
132 }
133 
134 ProxyBypassRules::~ProxyBypassRules() = default;
135 
operator =(const ProxyBypassRules & rhs)136 ProxyBypassRules& ProxyBypassRules::operator=(const ProxyBypassRules& rhs) {
137   ParseFromString(rhs.ToString());
138   return *this;
139 }
140 
operator =(ProxyBypassRules && rhs)141 ProxyBypassRules& ProxyBypassRules::operator=(ProxyBypassRules&& rhs) {
142   matcher_ = std::move(rhs.matcher_);
143   return *this;
144 }
145 
ReplaceRule(size_t index,std::unique_ptr<SchemeHostPortMatcherRule> rule)146 void ProxyBypassRules::ReplaceRule(
147     size_t index,
148     std::unique_ptr<SchemeHostPortMatcherRule> rule) {
149   matcher_.ReplaceRule(index, std::move(rule));
150 }
151 
Matches(const GURL & url,bool reverse) const152 bool ProxyBypassRules::Matches(const GURL& url, bool reverse) const {
153   switch (matcher_.Evaluate(url)) {
154     case SchemeHostPortMatcherResult::kInclude:
155       return !reverse;
156     case SchemeHostPortMatcherResult::kExclude:
157       return reverse;
158     case SchemeHostPortMatcherResult::kNoMatch:
159       break;
160   }
161 
162   // If none of the explicit rules matched, fall back to the implicit rules.
163   bool matches_implicit = MatchesImplicitRules(url);
164   if (matches_implicit)
165     return matches_implicit;
166 
167   return reverse;
168 }
169 
operator ==(const ProxyBypassRules & other) const170 bool ProxyBypassRules::operator==(const ProxyBypassRules& other) const {
171   if (rules().size() != other.rules().size())
172     return false;
173 
174   for (size_t i = 0; i < rules().size(); ++i) {
175     if (rules()[i]->ToString() != other.rules()[i]->ToString())
176       return false;
177   }
178   return true;
179 }
180 
ParseFromString(const std::string & raw)181 void ProxyBypassRules::ParseFromString(const std::string& raw) {
182   Clear();
183 
184   base::StringTokenizer entries(
185       raw, SchemeHostPortMatcher::kParseRuleListDelimiterList);
186   while (entries.GetNext()) {
187     AddRuleFromString(entries.token_piece());
188   }
189 }
190 
PrependRuleToBypassSimpleHostnames()191 void ProxyBypassRules::PrependRuleToBypassSimpleHostnames() {
192   matcher_.AddAsFirstRule(std::make_unique<BypassSimpleHostnamesRule>());
193 }
194 
AddRuleFromString(std::string_view raw_untrimmed)195 bool ProxyBypassRules::AddRuleFromString(std::string_view raw_untrimmed) {
196   auto rule = ParseRule(raw_untrimmed);
197 
198   if (rule) {
199     matcher_.AddAsLastRule(std::move(rule));
200     return true;
201   }
202 
203   return false;
204 }
205 
AddRulesToSubtractImplicit()206 void ProxyBypassRules::AddRulesToSubtractImplicit() {
207   matcher_.AddAsLastRule(std::make_unique<SubtractImplicitBypassesRule>());
208 }
209 
GetRulesToSubtractImplicit()210 std::string ProxyBypassRules::GetRulesToSubtractImplicit() {
211   ProxyBypassRules rules;
212   rules.AddRulesToSubtractImplicit();
213   return rules.ToString();
214 }
215 
ToString() const216 std::string ProxyBypassRules::ToString() const {
217   return matcher_.ToString();
218 }
219 
Clear()220 void ProxyBypassRules::Clear() {
221   matcher_.Clear();
222 }
223 
MatchesImplicitRules(const GURL & url)224 bool ProxyBypassRules::MatchesImplicitRules(const GURL& url) {
225   // On Windows the implict rules are:
226   //
227   //     localhost
228   //     loopback
229   //     127.0.0.1
230   //     [::1]
231   //     169.254/16
232   //     [FE80::]/10
233   //
234   // And on macOS they are:
235   //
236   //     localhost
237   //     127.0.0.1/8
238   //     [::1]
239   //     169.254/16
240   //
241   // Our implicit rules are approximately:
242   //
243   //     localhost
244   //     localhost.
245   //     *.localhost
246   //     loopback  [Windows only]
247   //     loopback. [Windows only]
248   //     [::1]
249   //     127.0.0.1/8
250   //     169.254/16
251   //     [FE80::]/10
252   return IsLocalhost(url) || IsIPv4MappedLoopback(url) ||
253          IsLinkLocalIP(url)
254 #if BUILDFLAG(IS_WIN)
255          // See http://crbug.com/904889
256          || (url.host_piece() == "loopback") ||
257          (url.host_piece() == "loopback.")
258 #endif
259       ;
260 }
261 
262 }  // namespace net
263