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