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