blob: e273d675b94c728dd1e9f815968396a88d99a403 [file] [log] [blame]
[email protected]7541206c2010-02-19 20:24:061// Copyright (c) 2010 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/logging.h"
8#include "base/string_tokenizer.h"
9#include "base/string_util.h"
10#include "net/base/net_util.h"
11
12namespace net {
13
14namespace {
15
16class HostnamePatternRule : public ProxyBypassRules::Rule {
17 public:
18 HostnamePatternRule(const std::string& optional_scheme,
19 const std::string& hostname_pattern,
20 int optional_port)
21 : optional_scheme_(StringToLowerASCII(optional_scheme)),
22 hostname_pattern_(StringToLowerASCII(hostname_pattern)),
23 optional_port_(optional_port) {
24 }
25
26 virtual bool Matches(const GURL& url) const {
27 if (optional_port_ != -1 && url.EffectiveIntPort() != optional_port_)
28 return false; // Didn't match port expectation.
29
30 if (!optional_scheme_.empty() && url.scheme() != optional_scheme_)
31 return false; // Didn't match scheme expectation.
32
33 // Note it is necessary to lower-case the host, since GURL uses capital
34 // letters for percent-escaped characters.
35 return MatchPatternASCII(StringToLowerASCII(url.host()),
36 hostname_pattern_);
37 }
38
39 virtual std::string ToString() const {
40 std::string str;
41 if (!optional_scheme_.empty())
42 StringAppendF(&str, "%s://", optional_scheme_.c_str());
43 str += hostname_pattern_;
44 if (optional_port_ != -1)
45 StringAppendF(&str, ":%d", optional_port_);
46 return str;
47 }
48
49 private:
50 const std::string optional_scheme_;
51 const std::string hostname_pattern_;
52 const int optional_port_;
53};
54
55class BypassLocalRule : public ProxyBypassRules::Rule {
56 public:
57 virtual bool Matches(const GURL& url) const {
58 const std::string& host = url.host();
59 if (host == "127.0.0.1" || host == "[::1]")
60 return true;
61 return host.find('.') == std::string::npos;
62 }
63
64 virtual std::string ToString() const {
65 return "<local>";
66 }
67};
68
[email protected]54392832010-06-08 23:25:0469// Rule for matching a URL that is an IP address, if that IP address falls
70// within a certain numeric range. For example, you could use this rule to
71// match all the IPs in the CIDR block 10.10.3.4/24.
72class BypassIPBlockRule : public ProxyBypassRules::Rule {
73 public:
74 // |ip_prefix| + |prefix_length| define the IP block to match.
75 BypassIPBlockRule(const std::string& description,
76 const std::string& optional_scheme,
77 const IPAddressNumber& ip_prefix,
78 size_t prefix_length_in_bits)
79 : description_(description),
80 optional_scheme_(optional_scheme),
81 ip_prefix_(ip_prefix),
82 prefix_length_in_bits_(prefix_length_in_bits) {
83 }
84
85 virtual bool Matches(const GURL& url) const {
86 if (!url.HostIsIPAddress())
87 return false;
88
89 if (!optional_scheme_.empty() && url.scheme() != optional_scheme_)
90 return false; // Didn't match scheme expectation.
91
92 // Parse the input IP literal to a number.
93 IPAddressNumber ip_number;
94 if (!ParseIPLiteralToNumber(url.HostNoBrackets(), &ip_number))
95 return false;
96
97 // Test if it has the expected prefix.
98 return IPNumberMatchesPrefix(ip_number, ip_prefix_,
99 prefix_length_in_bits_);
100 }
101
102 virtual std::string ToString() const {
103 return description_;
104 }
105
106 private:
107 const std::string description_;
108 const std::string optional_scheme_;
109 const IPAddressNumber ip_prefix_;
110 const size_t prefix_length_in_bits_;
111};
112
[email protected]7541206c2010-02-19 20:24:06113// Returns true if the given string represents an IP address.
114bool IsIPAddress(const std::string& domain) {
115 // From GURL::HostIsIPAddress()
116 url_canon::RawCanonOutputT<char, 128> ignored_output;
117 url_canon::CanonHostInfo host_info;
118 url_parse::Component domain_comp(0, domain.size());
119 url_canon::CanonicalizeIPAddress(domain.c_str(), domain_comp,
120 &ignored_output, &host_info);
121 return host_info.IsIPAddress();
122}
123
124} // namespace
125
126ProxyBypassRules::~ProxyBypassRules() {
127}
128
129bool ProxyBypassRules::Matches(const GURL& url) const {
130 for (RuleList::const_iterator it = rules_.begin(); it != rules_.end(); ++it) {
131 if ((*it)->Matches(url))
132 return true;
133 }
134 return false;
135}
136
137bool ProxyBypassRules::Equals(const ProxyBypassRules& other) const {
138 if (rules_.size() != other.rules().size())
139 return false;
140
141 for (size_t i = 0; i < rules_.size(); ++i) {
142 if (!rules_[i]->Equals(*other.rules()[i]))
143 return false;
144 }
145 return true;
146}
147
148void ProxyBypassRules::ParseFromString(const std::string& raw) {
149 ParseFromStringInternal(raw, false);
150}
151
152void ProxyBypassRules::ParseFromStringUsingSuffixMatching(
153 const std::string& raw) {
154 ParseFromStringInternal(raw, true);
155}
156
157bool ProxyBypassRules::AddRuleForHostname(const std::string& optional_scheme,
158 const std::string& hostname_pattern,
159 int optional_port) {
160 if (hostname_pattern.empty())
161 return false;
162
163 rules_.push_back(new HostnamePatternRule(optional_scheme,
164 hostname_pattern,
165 optional_port));
166 return true;
167}
168
169void ProxyBypassRules::AddRuleToBypassLocal() {
170 rules_.push_back(new BypassLocalRule);
171}
172
173bool ProxyBypassRules::AddRuleFromString(const std::string& raw) {
174 return AddRuleFromStringInternalWithLogging(raw, false);
175}
176
[email protected]1a597192010-07-09 16:58:38177bool ProxyBypassRules::AddRuleFromStringUsingSuffixMatching(
178 const std::string& raw) {
179 return AddRuleFromStringInternalWithLogging(raw, true);
180}
181
[email protected]7541206c2010-02-19 20:24:06182void ProxyBypassRules::Clear() {
183 rules_.clear();
184}
185
186void ProxyBypassRules::ParseFromStringInternal(
187 const std::string& raw,
188 bool use_hostname_suffix_matching) {
189 Clear();
190
191 StringTokenizer entries(raw, ",;");
192 while (entries.GetNext()) {
193 AddRuleFromStringInternalWithLogging(entries.token(),
194 use_hostname_suffix_matching);
195 }
196}
197
198bool ProxyBypassRules::AddRuleFromStringInternal(
199 const std::string& raw_untrimmed,
200 bool use_hostname_suffix_matching) {
201 std::string raw;
202 TrimWhitespaceASCII(raw_untrimmed, TRIM_ALL, &raw);
203
204 // This is the special syntax used by WinInet's bypass list -- we allow it
205 // on all platforms and interpret it the same way.
206 if (LowerCaseEqualsASCII(raw, "<local>")) {
207 AddRuleToBypassLocal();
208 return true;
209 }
210
211 // Extract any scheme-restriction.
212 std::string::size_type scheme_pos = raw.find("://");
213 std::string scheme;
214 if (scheme_pos != std::string::npos) {
215 scheme = raw.substr(0, scheme_pos);
216 raw = raw.substr(scheme_pos + 3);
217 if (scheme.empty())
218 return false;
219 }
220
221 if (raw.empty())
222 return false;
223
224 // If there is a forward slash in the input, it is probably a CIDR style
225 // mask.
226 if (raw.find('/') != std::string::npos) {
[email protected]54392832010-06-08 23:25:04227 IPAddressNumber ip_prefix;
228 size_t prefix_length_in_bits;
229
230 if (!ParseCIDRBlock(raw, &ip_prefix, &prefix_length_in_bits))
231 return false;
232
233 rules_.push_back(
234 new BypassIPBlockRule(raw, scheme, ip_prefix, prefix_length_in_bits));
235
236 return true;
[email protected]7541206c2010-02-19 20:24:06237 }
238
239 // Check if we have an <ip-address>[:port] input. We need to treat this
240 // separately since the IP literal may not be in a canonical form.
241 std::string host;
242 int port;
243 if (ParseHostAndPort(raw, &host, &port)) {
244 if (IsIPAddress(host)) {
245 // Canonicalize the IP literal before adding it as a string pattern.
246 GURL tmp_url("http://" + host);
247 return AddRuleForHostname(scheme, tmp_url.host(), port);
248 }
249 }
250
251 // Otherwise assume we have <hostname-pattern>[:port].
252 std::string::size_type pos_colon = raw.rfind(':');
253 host = raw;
254 port = -1;
255 if (pos_colon != std::string::npos) {
256 if (!StringToInt(raw.substr(pos_colon + 1), &port) ||
257 (port < 0 || port > 0xFFFF)) {
258 return false; // Port was invalid.
259 }
260 raw = raw.substr(0, pos_colon);
261 }
262
263 // Special-case hostnames that begin with a period.
264 // For example, we remap ".google.com" --> "*.google.com".
265 if (StartsWithASCII(raw, ".", false))
266 raw = "*" + raw;
267
268 // If suffix matching was asked for, make sure the pattern starts with a
269 // wildcard.
270 if (use_hostname_suffix_matching && !StartsWithASCII(raw, "*", false))
271 raw = "*" + raw;
272
273 return AddRuleForHostname(scheme, raw, port);
274}
275
276bool ProxyBypassRules::AddRuleFromStringInternalWithLogging(
277 const std::string& raw,
278 bool use_hostname_suffix_matching) {
[email protected]e5e61662010-03-11 02:32:39279 return AddRuleFromStringInternal(raw, use_hostname_suffix_matching);
[email protected]7541206c2010-02-19 20:24:06280}
281
282} // namespace net