• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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