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