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 16 namespace net { 17 18 namespace { 19 20 class 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 65 class 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. 86 class 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. 136 bool 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 148 ProxyBypassRules::Rule::Rule() { 149 } 150 151 ProxyBypassRules::Rule::~Rule() { 152 } 153 154 bool ProxyBypassRules::Rule::Equals(const Rule& rule) const { 155 return ToString() == rule.ToString(); 156 } 157 158 ProxyBypassRules::ProxyBypassRules() { 159 } 160 161 ProxyBypassRules::ProxyBypassRules(const ProxyBypassRules& rhs) { 162 AssignFrom(rhs); 163 } 164 165 ProxyBypassRules::~ProxyBypassRules() { 166 Clear(); 167 } 168 169 ProxyBypassRules& ProxyBypassRules::operator=(const ProxyBypassRules& rhs) { 170 AssignFrom(rhs); 171 return *this; 172 } 173 174 bool 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 182 bool 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 193 void ProxyBypassRules::ParseFromString(const std::string& raw) { 194 ParseFromStringInternal(raw, false); 195 } 196 197 void ProxyBypassRules::ParseFromStringUsingSuffixMatching( 198 const std::string& raw) { 199 ParseFromStringInternal(raw, true); 200 } 201 202 bool 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 214 void ProxyBypassRules::AddRuleToBypassLocal() { 215 rules_.push_back(new BypassLocalRule); 216 } 217 218 bool ProxyBypassRules::AddRuleFromString(const std::string& raw) { 219 return AddRuleFromStringInternalWithLogging(raw, false); 220 } 221 222 bool ProxyBypassRules::AddRuleFromStringUsingSuffixMatching( 223 const std::string& raw) { 224 return AddRuleFromStringInternalWithLogging(raw, true); 225 } 226 227 std::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 238 void ProxyBypassRules::Clear() { 239 STLDeleteElements(&rules_); 240 } 241 242 void 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 252 void 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 264 bool 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 347 bool 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