1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
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/proxy_bypass_rules.h"
6
7 #include "base/stl_util-inl.h"
8 #include "base/string_number_conversions.h"
9 #include "base/string_tokenizer.h"
10 #include "base/string_util.h"
11 #include "net/base/net_util.h"
12
13 namespace net {
14
15 namespace {
16
17 class HostnamePatternRule : public ProxyBypassRules::Rule {
18 public:
HostnamePatternRule(const std::string & optional_scheme,const std::string & hostname_pattern,int optional_port)19 HostnamePatternRule(const std::string& optional_scheme,
20 const std::string& hostname_pattern,
21 int optional_port)
22 : optional_scheme_(StringToLowerASCII(optional_scheme)),
23 hostname_pattern_(StringToLowerASCII(hostname_pattern)),
24 optional_port_(optional_port) {
25 }
26
Matches(const GURL & url) const27 virtual bool Matches(const GURL& url) const {
28 if (optional_port_ != -1 && url.EffectiveIntPort() != optional_port_)
29 return false; // Didn't match port expectation.
30
31 if (!optional_scheme_.empty() && url.scheme() != optional_scheme_)
32 return false; // Didn't match scheme expectation.
33
34 // Note it is necessary to lower-case the host, since GURL uses capital
35 // letters for percent-escaped characters.
36 return MatchPattern(StringToLowerASCII(url.host()), hostname_pattern_);
37 }
38
ToString() const39 virtual std::string ToString() const {
40 std::string str;
41 if (!optional_scheme_.empty())
42 base::StringAppendF(&str, "%s://", optional_scheme_.c_str());
43 str += hostname_pattern_;
44 if (optional_port_ != -1)
45 base::StringAppendF(&str, ":%d", optional_port_);
46 return str;
47 }
48
Clone() const49 virtual Rule* Clone() const {
50 return new HostnamePatternRule(optional_scheme_,
51 hostname_pattern_,
52 optional_port_);
53 }
54
55 private:
56 const std::string optional_scheme_;
57 const std::string hostname_pattern_;
58 const int optional_port_;
59 };
60
61 class BypassLocalRule : public ProxyBypassRules::Rule {
62 public:
Matches(const GURL & url) const63 virtual bool Matches(const GURL& url) const {
64 const std::string& host = url.host();
65 if (host == "127.0.0.1" || host == "[::1]")
66 return true;
67 return host.find('.') == std::string::npos;
68 }
69
ToString() const70 virtual std::string ToString() const {
71 return "<local>";
72 }
73
Clone() const74 virtual Rule* Clone() const {
75 return new BypassLocalRule();
76 }
77 };
78
79 // Rule for matching a URL that is an IP address, if that IP address falls
80 // within a certain numeric range. For example, you could use this rule to
81 // match all the IPs in the CIDR block 10.10.3.4/24.
82 class BypassIPBlockRule : public ProxyBypassRules::Rule {
83 public:
84 // |ip_prefix| + |prefix_length| define the IP block to match.
BypassIPBlockRule(const std::string & description,const std::string & optional_scheme,const IPAddressNumber & ip_prefix,size_t prefix_length_in_bits)85 BypassIPBlockRule(const std::string& description,
86 const std::string& optional_scheme,
87 const IPAddressNumber& ip_prefix,
88 size_t prefix_length_in_bits)
89 : description_(description),
90 optional_scheme_(optional_scheme),
91 ip_prefix_(ip_prefix),
92 prefix_length_in_bits_(prefix_length_in_bits) {
93 }
94
Matches(const GURL & url) const95 virtual bool Matches(const GURL& url) const {
96 if (!url.HostIsIPAddress())
97 return false;
98
99 if (!optional_scheme_.empty() && url.scheme() != optional_scheme_)
100 return false; // Didn't match scheme expectation.
101
102 // Parse the input IP literal to a number.
103 IPAddressNumber ip_number;
104 if (!ParseIPLiteralToNumber(url.HostNoBrackets(), &ip_number))
105 return false;
106
107 // Test if it has the expected prefix.
108 return IPNumberMatchesPrefix(ip_number, ip_prefix_,
109 prefix_length_in_bits_);
110 }
111
ToString() const112 virtual std::string ToString() const {
113 return description_;
114 }
115
Clone() const116 virtual Rule* Clone() const {
117 return new BypassIPBlockRule(description_,
118 optional_scheme_,
119 ip_prefix_,
120 prefix_length_in_bits_);
121 }
122
123 private:
124 const std::string description_;
125 const std::string optional_scheme_;
126 const IPAddressNumber ip_prefix_;
127 const size_t prefix_length_in_bits_;
128 };
129
130 // Returns true if the given string represents an IP address.
IsIPAddress(const std::string & domain)131 bool IsIPAddress(const std::string& domain) {
132 // From GURL::HostIsIPAddress()
133 url_canon::RawCanonOutputT<char, 128> ignored_output;
134 url_canon::CanonHostInfo host_info;
135 url_parse::Component domain_comp(0, domain.size());
136 url_canon::CanonicalizeIPAddress(domain.c_str(), domain_comp,
137 &ignored_output, &host_info);
138 return host_info.IsIPAddress();
139 }
140
141 } // namespace
142
Rule()143 ProxyBypassRules::Rule::Rule() {
144 }
145
~Rule()146 ProxyBypassRules::Rule::~Rule() {
147 }
148
Equals(const Rule & rule) const149 bool ProxyBypassRules::Rule::Equals(const Rule& rule) const {
150 return ToString() == rule.ToString();
151 }
152
ProxyBypassRules()153 ProxyBypassRules::ProxyBypassRules() {
154 }
155
ProxyBypassRules(const ProxyBypassRules & rhs)156 ProxyBypassRules::ProxyBypassRules(const ProxyBypassRules& rhs) {
157 AssignFrom(rhs);
158 }
159
~ProxyBypassRules()160 ProxyBypassRules::~ProxyBypassRules() {
161 Clear();
162 }
163
operator =(const ProxyBypassRules & rhs)164 ProxyBypassRules& ProxyBypassRules::operator=(const ProxyBypassRules& rhs) {
165 AssignFrom(rhs);
166 return *this;
167 }
168
Matches(const GURL & url) const169 bool ProxyBypassRules::Matches(const GURL& url) const {
170 for (RuleList::const_iterator it = rules_.begin(); it != rules_.end(); ++it) {
171 if ((*it)->Matches(url))
172 return true;
173 }
174 return false;
175 }
176
Equals(const ProxyBypassRules & other) const177 bool ProxyBypassRules::Equals(const ProxyBypassRules& other) const {
178 if (rules_.size() != other.rules_.size())
179 return false;
180
181 for (size_t i = 0; i < rules_.size(); ++i) {
182 if (!rules_[i]->Equals(*other.rules_[i]))
183 return false;
184 }
185 return true;
186 }
187
ParseFromString(const std::string & raw)188 void ProxyBypassRules::ParseFromString(const std::string& raw) {
189 ParseFromStringInternal(raw, false);
190 }
191
ParseFromStringUsingSuffixMatching(const std::string & raw)192 void ProxyBypassRules::ParseFromStringUsingSuffixMatching(
193 const std::string& raw) {
194 ParseFromStringInternal(raw, true);
195 }
196
AddRuleForHostname(const std::string & optional_scheme,const std::string & hostname_pattern,int optional_port)197 bool ProxyBypassRules::AddRuleForHostname(const std::string& optional_scheme,
198 const std::string& hostname_pattern,
199 int optional_port) {
200 if (hostname_pattern.empty())
201 return false;
202
203 rules_.push_back(new HostnamePatternRule(optional_scheme,
204 hostname_pattern,
205 optional_port));
206 return true;
207 }
208
AddRuleToBypassLocal()209 void ProxyBypassRules::AddRuleToBypassLocal() {
210 rules_.push_back(new BypassLocalRule);
211 }
212
AddRuleFromString(const std::string & raw)213 bool ProxyBypassRules::AddRuleFromString(const std::string& raw) {
214 return AddRuleFromStringInternalWithLogging(raw, false);
215 }
216
AddRuleFromStringUsingSuffixMatching(const std::string & raw)217 bool ProxyBypassRules::AddRuleFromStringUsingSuffixMatching(
218 const std::string& raw) {
219 return AddRuleFromStringInternalWithLogging(raw, true);
220 }
221
ToString() const222 std::string ProxyBypassRules::ToString() const {
223 std::string result;
224 for (RuleList::const_iterator rule(rules_.begin());
225 rule != rules_.end();
226 ++rule) {
227 result += (*rule)->ToString();
228 result += ";";
229 }
230 return result;
231 }
232
Clear()233 void ProxyBypassRules::Clear() {
234 STLDeleteElements(&rules_);
235 }
236
AssignFrom(const ProxyBypassRules & other)237 void ProxyBypassRules::AssignFrom(const ProxyBypassRules& other) {
238 Clear();
239
240 // Make a copy of the rules list.
241 for (RuleList::const_iterator it = other.rules_.begin();
242 it != other.rules_.end(); ++it) {
243 rules_.push_back((*it)->Clone());
244 }
245 }
246
ParseFromStringInternal(const std::string & raw,bool use_hostname_suffix_matching)247 void ProxyBypassRules::ParseFromStringInternal(
248 const std::string& raw,
249 bool use_hostname_suffix_matching) {
250 Clear();
251
252 StringTokenizer entries(raw, ",;");
253 while (entries.GetNext()) {
254 AddRuleFromStringInternalWithLogging(entries.token(),
255 use_hostname_suffix_matching);
256 }
257 }
258
AddRuleFromStringInternal(const std::string & raw_untrimmed,bool use_hostname_suffix_matching)259 bool ProxyBypassRules::AddRuleFromStringInternal(
260 const std::string& raw_untrimmed,
261 bool use_hostname_suffix_matching) {
262 std::string raw;
263 TrimWhitespaceASCII(raw_untrimmed, TRIM_ALL, &raw);
264
265 // This is the special syntax used by WinInet's bypass list -- we allow it
266 // on all platforms and interpret it the same way.
267 if (LowerCaseEqualsASCII(raw, "<local>")) {
268 AddRuleToBypassLocal();
269 return true;
270 }
271
272 // Extract any scheme-restriction.
273 std::string::size_type scheme_pos = raw.find("://");
274 std::string scheme;
275 if (scheme_pos != std::string::npos) {
276 scheme = raw.substr(0, scheme_pos);
277 raw = raw.substr(scheme_pos + 3);
278 if (scheme.empty())
279 return false;
280 }
281
282 if (raw.empty())
283 return false;
284
285 // If there is a forward slash in the input, it is probably a CIDR style
286 // mask.
287 if (raw.find('/') != std::string::npos) {
288 IPAddressNumber ip_prefix;
289 size_t prefix_length_in_bits;
290
291 if (!ParseCIDRBlock(raw, &ip_prefix, &prefix_length_in_bits))
292 return false;
293
294 rules_.push_back(
295 new BypassIPBlockRule(raw, scheme, ip_prefix, prefix_length_in_bits));
296
297 return true;
298 }
299
300 // Check if we have an <ip-address>[:port] input. We need to treat this
301 // separately since the IP literal may not be in a canonical form.
302 std::string host;
303 int port;
304 if (ParseHostAndPort(raw, &host, &port)) {
305 if (IsIPAddress(host)) {
306 // Canonicalize the IP literal before adding it as a string pattern.
307 GURL tmp_url("http://" + host);
308 return AddRuleForHostname(scheme, tmp_url.host(), port);
309 }
310 }
311
312 // Otherwise assume we have <hostname-pattern>[:port].
313 std::string::size_type pos_colon = raw.rfind(':');
314 host = raw;
315 port = -1;
316 if (pos_colon != std::string::npos) {
317 if (!base::StringToInt(raw.begin() + pos_colon + 1, raw.end(), &port) ||
318 (port < 0 || port > 0xFFFF)) {
319 return false; // Port was invalid.
320 }
321 raw = raw.substr(0, pos_colon);
322 }
323
324 // Special-case hostnames that begin with a period.
325 // For example, we remap ".google.com" --> "*.google.com".
326 if (StartsWithASCII(raw, ".", false))
327 raw = "*" + raw;
328
329 // If suffix matching was asked for, make sure the pattern starts with a
330 // wildcard.
331 if (use_hostname_suffix_matching && !StartsWithASCII(raw, "*", false))
332 raw = "*" + raw;
333
334 return AddRuleForHostname(scheme, raw, port);
335 }
336
AddRuleFromStringInternalWithLogging(const std::string & raw,bool use_hostname_suffix_matching)337 bool ProxyBypassRules::AddRuleFromStringInternalWithLogging(
338 const std::string& raw,
339 bool use_hostname_suffix_matching) {
340 return AddRuleFromStringInternal(raw, use_hostname_suffix_matching);
341 }
342
343 } // namespace net
344