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