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: 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 27 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 39 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 49 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: 63 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 70 virtual std::string ToString() const { 71 return "<local>"; 72 } 73 74 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. 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 95 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 112 virtual std::string ToString() const { 113 return description_; 114 } 115 116 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. 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 143 ProxyBypassRules::Rule::Rule() { 144 } 145 146 ProxyBypassRules::Rule::~Rule() { 147 } 148 149 bool ProxyBypassRules::Rule::Equals(const Rule& rule) const { 150 return ToString() == rule.ToString(); 151 } 152 153 ProxyBypassRules::ProxyBypassRules() { 154 } 155 156 ProxyBypassRules::ProxyBypassRules(const ProxyBypassRules& rhs) { 157 AssignFrom(rhs); 158 } 159 160 ProxyBypassRules::~ProxyBypassRules() { 161 Clear(); 162 } 163 164 ProxyBypassRules& ProxyBypassRules::operator=(const ProxyBypassRules& rhs) { 165 AssignFrom(rhs); 166 return *this; 167 } 168 169 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 177 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 188 void ProxyBypassRules::ParseFromString(const std::string& raw) { 189 ParseFromStringInternal(raw, false); 190 } 191 192 void ProxyBypassRules::ParseFromStringUsingSuffixMatching( 193 const std::string& raw) { 194 ParseFromStringInternal(raw, true); 195 } 196 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 209 void ProxyBypassRules::AddRuleToBypassLocal() { 210 rules_.push_back(new BypassLocalRule); 211 } 212 213 bool ProxyBypassRules::AddRuleFromString(const std::string& raw) { 214 return AddRuleFromStringInternalWithLogging(raw, false); 215 } 216 217 bool ProxyBypassRules::AddRuleFromStringUsingSuffixMatching( 218 const std::string& raw) { 219 return AddRuleFromStringInternalWithLogging(raw, true); 220 } 221 222 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 233 void ProxyBypassRules::Clear() { 234 STLDeleteElements(&rules_); 235 } 236 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 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 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 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