diff options
author | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-08 23:25:04 +0000 |
---|---|---|
committer | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-08 23:25:04 +0000 |
commit | 5439283ff95a6f7d8b9e6d86c0fd178b0238acaa (patch) | |
tree | 6b067f14016e3c39adf8eb654ff18fc2ff1886de | |
parent | ec561333496a9b4f7f29718aef43094596f2d489 (diff) | |
download | chromium_src-5439283ff95a6f7d8b9e6d86c0fd178b0238acaa.zip chromium_src-5439283ff95a6f7d8b9e6d86c0fd178b0238acaa.tar.gz chromium_src-5439283ff95a6f7d8b9e6d86c0fd178b0238acaa.tar.bz2 |
Allow bypassing a block of IP addresses using CIDR notation in the proxy bypass list.
For example:
10.3.1.3/16
2020:ffff::/96
Note that similar to firefox, this is only applied to IP literals in URLs, and NOT to the resolved addresses of URLs.
BUG=9835
Review URL: http://codereview.chromium.org/2663001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49211 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/net/chrome_url_request_context_unittest.cc | 6 | ||||
-rw-r--r-- | net/base/mock_host_resolver.cc | 86 | ||||
-rw-r--r-- | net/base/mock_host_resolver.h | 24 | ||||
-rw-r--r-- | net/base/net_util.cc | 114 | ||||
-rw-r--r-- | net/base/net_util.h | 46 | ||||
-rw-r--r-- | net/base/net_util_unittest.cc | 172 | ||||
-rw-r--r-- | net/http/http_auth_handler_negotiate_unittest.cc | 8 | ||||
-rw-r--r-- | net/proxy/proxy_bypass_rules.cc | 57 | ||||
-rw-r--r-- | net/proxy/proxy_bypass_rules_unittest.cc | 28 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_linux_unittest.cc | 3 | ||||
-rw-r--r-- | net/socket/socks_client_socket_unittest.cc | 3 |
11 files changed, 455 insertions, 92 deletions
diff --git a/chrome/browser/net/chrome_url_request_context_unittest.cc b/chrome/browser/net/chrome_url_request_context_unittest.cc index 676340e..ea084c8 100644 --- a/chrome/browser/net/chrome_url_request_context_unittest.cc +++ b/chrome/browser/net/chrome_url_request_context_unittest.cc @@ -127,8 +127,7 @@ TEST(ChromeUrlRequestContextTest, CreateProxyConfigTest) { "httpproxy:8888", // http "", // https "ftpproxy:8889", // ftp - // TODO(eroman): 127.0.0.1/8 is unsupported, so it was dropped - "*.google.com,foo.com:99,1.2.3.4:22"), + "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"), }, { TEST_DESC("Pac URL with proxy bypass URLs"), @@ -139,8 +138,7 @@ TEST(ChromeUrlRequestContextTest, CreateProxyConfigTest) { false, // auto_detect GURL("http://wpad/wpad.dat"), // pac_url net::ProxyRulesExpectation::EmptyWithBypass( - // TODO(eroman): 127.0.0.1/8 is unsupported, so it was dropped - "*.google.com,foo.com:99,1.2.3.4:22"), + "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"), }, { TEST_DESC("Autodetect"), diff --git a/net/base/mock_host_resolver.cc b/net/base/mock_host_resolver.cc index b16b304..1ee9bb1 100644 --- a/net/base/mock_host_resolver.cc +++ b/net/base/mock_host_resolver.cc @@ -7,54 +7,35 @@ #include "base/string_util.h" #include "base/platform_thread.h" #include "base/ref_counted.h" -#include "googleurl/src/url_canon_ip.h" #include "net/base/net_errors.h" +#include "net/base/net_util.h" namespace net { namespace { // Fills |*addrlist| with a socket address for |host| which should be an -// IPv6 literal without enclosing brackets. If |canonical_name| is non-empty -// it is used as the DNS canonical name for the host. Returns OK on success, -// ERR_UNEXPECTED otherwise. -int CreateIPv6Address(const std::string& host, - const std::string& canonical_name, - AddressList* addrlist) { - // GURL expects the hostname to be surrounded with brackets. - std::string host_brackets = "[" + host + "]"; - url_parse::Component host_comp(0, host_brackets.size()); - - // Try parsing the hostname as an IPv6 literal. - unsigned char ipv6_addr[16]; // 128 bits. - bool ok = url_canon::IPv6AddressToNumber(host_brackets.data(), - host_comp, - ipv6_addr); - if (!ok) { - LOG(WARNING) << "Not an IPv6 literal: " << host; +// IPv4 or IPv6 literal without enclosing brackets. If |canonical_name| is +// non-empty it is used as the DNS canonical name for the host. Returns OK on +// success, ERR_UNEXPECTED otherwise. +int CreateIPAddress(const std::string& host, + const std::string& canonical_name, + AddressList* addrlist) { + IPAddressNumber ip_number; + if (!ParseIPLiteralToNumber(host, &ip_number)) { + LOG(WARNING) << "Not a supported IP literal: " << host; return ERR_UNEXPECTED; } - *addrlist = AddressList::CreateIPv6Address(ipv6_addr, canonical_name); - return OK; -} - -// Fills |*addrlist| with a socket address for |host| which should be an -// IPv4 literal. If |canonical_name| is non-empty it is used as the DNS -// canonical name for the host. Returns OK on success, ERR_UNEXPECTED otherwise. -int CreateIPv4Address(const std::string& host, - const std::string& canonical_name, - AddressList* addrlist) { - unsigned char ipv4_addr[4]; - url_parse::Component host_comp(0, host.size()); - int num_components; - url_canon::CanonHostInfo::Family family = url_canon::IPv4AddressToNumber( - host.data(), host_comp, ipv4_addr, &num_components); - if (family != url_canon::CanonHostInfo::IPV4) { - LOG(WARNING) << "Not an IPv4 literal: " << host; + if (ip_number.size() == 4) { + *addrlist = AddressList::CreateIPv4Address(&ip_number[0], canonical_name); + } else if (ip_number.size() == 16) { + *addrlist = AddressList::CreateIPv6Address(&ip_number[0], canonical_name); + } else { + NOTREACHED(); return ERR_UNEXPECTED; } - *addrlist = AddressList::CreateIPv4Address(ipv4_addr, canonical_name); + return OK; } @@ -126,8 +107,7 @@ struct RuleBasedHostResolverProc::Rule { enum ResolverType { kResolverTypeFail, kResolverTypeSystem, - kResolverTypeIPV6Literal, - kResolverTypeIPV4Literal, + kResolverTypeIPLiteral, }; ResolverType resolver_type; @@ -177,27 +157,15 @@ void RuleBasedHostResolverProc::AddRuleForAddressFamily( rules_.push_back(rule); } -void RuleBasedHostResolverProc::AddIPv4Rule(const std::string& host_pattern, - const std::string& ipv4_literal, - const std::string& canonical_name) { - Rule rule(Rule::kResolverTypeIPV4Literal, - host_pattern, - ADDRESS_FAMILY_UNSPECIFIED, - canonical_name.empty() ? 0 : HOST_RESOLVER_CANONNAME, - ipv4_literal, - canonical_name, - 0); - rules_.push_back(rule); -} - -void RuleBasedHostResolverProc::AddIPv6Rule(const std::string& host_pattern, - const std::string& ipv6_literal, - const std::string& canonical_name) { - Rule rule(Rule::kResolverTypeIPV6Literal, +void RuleBasedHostResolverProc::AddIPLiteralRule( + const std::string& host_pattern, + const std::string& ip_literal, + const std::string& canonical_name) { + Rule rule(Rule::kResolverTypeIPLiteral, host_pattern, ADDRESS_FAMILY_UNSPECIFIED, canonical_name.empty() ? 0 : HOST_RESOLVER_CANONNAME, - ipv6_literal, + ip_literal, canonical_name, 0); rules_.push_back(rule); @@ -261,10 +229,8 @@ int RuleBasedHostResolverProc::Resolve(const std::string& host, address_family, host_resolver_flags, addrlist, os_error); - case Rule::kResolverTypeIPV6Literal: - return CreateIPv6Address(effective_host, r->canonical_name, addrlist); - case Rule::kResolverTypeIPV4Literal: - return CreateIPv4Address(effective_host, r->canonical_name, addrlist); + case Rule::kResolverTypeIPLiteral: + return CreateIPAddress(effective_host, r->canonical_name, addrlist); default: NOTREACHED(); return ERR_UNEXPECTED; diff --git a/net/base/mock_host_resolver.h b/net/base/mock_host_resolver.h index 1fe4f06..ee1e55a 100644 --- a/net/base/mock_host_resolver.h +++ b/net/base/mock_host_resolver.h @@ -107,26 +107,14 @@ class RuleBasedHostResolverProc : public HostResolverProc { AddressFamily address_family, const std::string& replacement); - // Same as AddRule(), but the replacement is expected to be an IPV4 literal. - // This can be used in place of AddRule() to bypass the system's host - // resolver. |ipv4_literal| must be an IPv4 literal, typically taking the form - // of "[0-255].[0-255].[0-255].[0-255]". + // Same as AddRule(), but the replacement is expected to be an IPv4 or IPv6 + // literal. This can be used in place of AddRule() to bypass the system's + // host resolver (the address list will be constructed manually). // If |canonical-name| is non-empty, it is copied to the resulting AddressList // but does not impact DNS resolution. - void AddIPv4Rule(const std::string& host_pattern, - const std::string& ipv4_literal, - const std::string& canonical_name); - - // Same as AddRule(), but |ipv6_literal| is expected to be an IPV6 literal, - // without enclosing brackets. You should use this in place of AddRule(), - // since the system's host resolver may not support IPv6 literals on all - // systems. This variant constructs the socket address directly so it will - // always work. - // If |canonical-name| is non-empty, it is copied to the resulting AddressList - // but does not impact DNS resolution. - void AddIPv6Rule(const std::string& host_pattern, - const std::string& ipv6_literal, - const std::string& canonical_name); + void AddIPLiteralRule(const std::string& host_pattern, + const std::string& ip_literal, + const std::string& canonical_name); void AddRuleWithLatency(const std::string& host_pattern, const std::string& replacement, diff --git a/net/base/net_util.cc b/net/base/net_util.cc index 2417bba..60f626d4 100644 --- a/net/base/net_util.cc +++ b/net/base/net_util.cc @@ -53,6 +53,7 @@ #include "grit/net_resources.h" #include "googleurl/src/gurl.h" #include "googleurl/src/url_canon.h" +#include "googleurl/src/url_canon_ip.h" #include "googleurl/src/url_parse.h" #include "net/base/dns_util.h" #include "net/base/escape.h" @@ -1752,4 +1753,117 @@ bool IPv6Supported() { #endif // defined(various platforms) } +bool ParseIPLiteralToNumber(const std::string& ip_literal, + IPAddressNumber* ip_number) { + // |ip_literal| could be either a IPv4 or an IPv6 literal. If it contains + // a colon however, it must be an IPv6 address. + if (ip_literal.find(':') != std::string::npos) { + // GURL expects IPv6 hostnames to be surrounded with brackets. + std::string host_brackets = "[" + ip_literal + "]"; + url_parse::Component host_comp(0, host_brackets.size()); + + // Try parsing the hostname as an IPv6 literal. + ip_number->resize(16); // 128 bits. + return url_canon::IPv6AddressToNumber(host_brackets.data(), + host_comp, + &(*ip_number)[0]); + } + + // Otherwise the string is an IPv4 address. + ip_number->resize(4); // 32 bits. + url_parse::Component host_comp(0, ip_literal.size()); + int num_components; + url_canon::CanonHostInfo::Family family = url_canon::IPv4AddressToNumber( + ip_literal.data(), host_comp, &(*ip_number)[0], &num_components); + return family == url_canon::CanonHostInfo::IPV4; +} + +IPAddressNumber ConvertIPv4NumberToIPv6Number( + const IPAddressNumber& ipv4_number) { + DCHECK(ipv4_number.size() == 4); + + // IPv4-mapped addresses are formed by: + // <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>. + IPAddressNumber ipv6_number; + ipv6_number.reserve(16); + ipv6_number.insert(ipv6_number.end(), 10, 0); + ipv6_number.push_back(0xFF); + ipv6_number.push_back(0xFF); + ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end()); + return ipv6_number; +} + +bool ParseCIDRBlock(const std::string& cidr_literal, + IPAddressNumber* ip_number, + size_t* prefix_length_in_bits) { + // We expect CIDR notation to match one of these two templates: + // <IPv4-literal> "/" <number of bits> + // <IPv6-literal> "/" <number of bits> + + std::vector<std::string> parts; + SplitString(cidr_literal, '/', &parts); + if (parts.size() != 2) + return false; + + // Parse the IP address. + if (!ParseIPLiteralToNumber(parts[0], ip_number)) + return false; + + // Parse the prefix length. + int number_of_bits = -1; + if (!StringToInt(parts[1], &number_of_bits)) + return false; + + // Make sure the prefix length is in a valid range. + if (number_of_bits < 0 || + number_of_bits > static_cast<int>(ip_number->size() * 8)) + return false; + + *prefix_length_in_bits = static_cast<size_t>(number_of_bits); + return true; +} + +bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number, + const IPAddressNumber& ip_prefix, + size_t prefix_length_in_bits) { + // Both the input IP address and the prefix IP address should be + // either IPv4 or IPv6. + DCHECK(ip_number.size() == 4 || ip_number.size() == 16); + DCHECK(ip_prefix.size() == 4 || ip_prefix.size() == 16); + + DCHECK_LE(prefix_length_in_bits, ip_prefix.size() * 8); + + // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to + // IPv6 addresses in order to do the comparison. + if (ip_number.size() != ip_prefix.size()) { + if (ip_number.size() == 4) { + return IPNumberMatchesPrefix(ConvertIPv4NumberToIPv6Number(ip_number), + ip_prefix, prefix_length_in_bits); + } + return IPNumberMatchesPrefix(ip_number, + ConvertIPv4NumberToIPv6Number(ip_prefix), + 96 + prefix_length_in_bits); + } + + // Otherwise we are comparing two IPv4 addresses, or two IPv6 addresses. + // Compare all the bytes that fall entirely within the prefix. + int num_entire_bytes_in_prefix = prefix_length_in_bits / 8; + for (int i = 0; i < num_entire_bytes_in_prefix; ++i) { + if (ip_number[i] != ip_prefix[i]) + return false; + } + + // In case the prefix was not a multiple of 8, there will be 1 byte + // which is only partially masked. + int remaining_bits = prefix_length_in_bits % 8; + if (remaining_bits != 0) { + unsigned char mask = 0xFF << (8 - remaining_bits); + int i = num_entire_bytes_in_prefix; + if ((ip_number[i] & mask) != (ip_prefix[i] & mask)) + return false; + } + + return true; +} + } // namespace net diff --git a/net/base/net_util.h b/net/base/net_util.h index a66b45c..61e1957 100644 --- a/net/base/net_util.h +++ b/net/base/net_util.h @@ -13,6 +13,7 @@ #include <string> #include <set> +#include <vector> #include "base/basictypes.h" #include "base/string16.h" @@ -322,6 +323,51 @@ void SetExplicitlyAllowedPorts(const std::wstring& allowed_ports); // TODO(jar): Make test more in-depth as needed. bool IPv6Supported(); +// IPAddressNumber is used to represent an IP address's numeric value as an +// array of bytes, from most significant to least significant. This is the +// network byte ordering. +// +// IPv4 addresses will have length 4, whereas IPv6 address will have length 16. +typedef std::vector<unsigned char> IPAddressNumber; + +// Parses an IP address literal (either IPv4 or IPv6) to its numeric value. +// Returns true on success and fills |ip_number| with the numeric value. +bool ParseIPLiteralToNumber(const std::string& ip_literal, + IPAddressNumber* ip_number); + +// Converts an IPv4 address to an IPv4-mapped IPv6 address. +// For example 192.168.0.1 would be converted to ::ffff:192.168.0.1. +IPAddressNumber ConvertIPv4NumberToIPv6Number( + const IPAddressNumber& ipv4_number); + +// Parses an IP block specifier from CIDR notation to an +// (IP address, prefix length) pair. Returns true on success and fills +// |*ip_number| with the numeric value of the IP address and sets +// |*prefix_length_in_bits| with the length of the prefix. +// +// CIDR notation literals can use either IPv4 or IPv6 literals. Some examples: +// +// 10.10.3.1/20 +// a:b:c::/46 +// ::1/128 +bool ParseCIDRBlock(const std::string& cidr_literal, + IPAddressNumber* ip_number, + size_t* prefix_length_in_bits); + +// Compares an IP address to see if it falls within the specified IP block. +// Returns true if it does, false otherwise. +// +// The IP block is given by (|ip_prefix|, |prefix_length_in_bits|) -- any +// IP address whose |prefix_length_in_bits| most significant bits match +// |ip_prefix| will be matched. +// +// In cases when an IPv4 address is being compared to an IPv6 address prefix +// and vice versa, the IPv4 addresses will be converted to IPv4-mapped +// (IPv6) addresses. +bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number, + const IPAddressNumber& ip_prefix, + size_t prefix_length_in_bits); + } // namespace net #endif // NET_BASE_NET_UTIL_H_ diff --git a/net/base/net_util_unittest.cc b/net/base/net_util_unittest.cc index 44cdd06..dff3d1c 100644 --- a/net/base/net_util_unittest.cc +++ b/net/base/net_util_unittest.cc @@ -431,6 +431,17 @@ void AppendLanguagesToOutputs(const wchar_t* languages, actual->append(languages); } +// Helper to strignize an IP number (used to define expectations). +std::string DumpIPNumber(const net::IPAddressNumber& v) { + std::string out; + for (size_t i = 0; i < v.size(); ++i) { + if (i != 0) + out.append(","); + out.append(IntToString(static_cast<int>(v[i]))); + } + return out; +} + } // anonymous namespace TEST(NetUtilTest, FileURLConversion) { @@ -1787,3 +1798,164 @@ TEST(NetUtilTest, GetHostOrSpecFromURL) { EXPECT_EQ("file:///tmp/test.html", net::GetHostOrSpecFromURL(GURL("file:///tmp/test.html"))); } + +// Test that invalid IP literals fail to parse. +TEST(NetUtilTest, ParseIPLiteralToNumber_FailParse) { + net::IPAddressNumber number; + + EXPECT_FALSE(net::ParseIPLiteralToNumber("bad value", &number)); + EXPECT_FALSE(net::ParseIPLiteralToNumber("bad:value", &number)); + EXPECT_FALSE(net::ParseIPLiteralToNumber("", &number)); + EXPECT_FALSE(net::ParseIPLiteralToNumber("192.168.0.1:30", &number)); + EXPECT_FALSE(net::ParseIPLiteralToNumber(" 192.168.0.1 ", &number)); + EXPECT_FALSE(net::ParseIPLiteralToNumber("[::1]", &number)); +} + +// Test parsing an IPv4 literal. +TEST(NetUtilTest, ParseIPLiteralToNumber_IPv4) { + net::IPAddressNumber number; + EXPECT_TRUE(net::ParseIPLiteralToNumber("192.168.0.1", &number)); + EXPECT_EQ("192,168,0,1", DumpIPNumber(number)); +} + +// Test parsing an IPv6 literal. +TEST(NetUtilTest, ParseIPLiteralToNumber_IPv6) { + net::IPAddressNumber number; + EXPECT_TRUE(net::ParseIPLiteralToNumber("1:abcd::3:4:ff", &number)); + EXPECT_EQ("0,1,171,205,0,0,0,0,0,0,0,3,0,4,0,255", DumpIPNumber(number)); +} + +// Test mapping an IPv4 address to an IPv6 address. +TEST(NetUtilTest, ConvertIPv4NumberToIPv6Number) { + net::IPAddressNumber ipv4_number; + EXPECT_TRUE(net::ParseIPLiteralToNumber("192.168.0.1", &ipv4_number)); + + net::IPAddressNumber ipv6_number = + net::ConvertIPv4NumberToIPv6Number(ipv4_number); + + // ::ffff:192.168.1.1 + EXPECT_EQ("0,0,0,0,0,0,0,0,0,0,255,255,192,168,0,1", + DumpIPNumber(ipv6_number)); +} + +// Test parsing invalid CIDR notation literals. +TEST(NetUtilTest, ParseCIDRBlock_Invalid) { + const char* bad_literals[] = { + "foobar", + "", + "192.168.0.1", + "::1", + "/", + "/1", + "1", + "192.168.1.1/-1", + "192.168.1.1/33", + "::1/-3", + "a::3/129", + "::1/x", + "192.168.0.1//11" + }; + + for (size_t i = 0; i < arraysize(bad_literals); ++i) { + net::IPAddressNumber ip_number; + size_t prefix_length_in_bits; + + EXPECT_FALSE(net::ParseCIDRBlock(bad_literals[i], + &ip_number, + &prefix_length_in_bits)); + } +} + +// Test parsing a valid CIDR notation literal. +TEST(NetUtilTest, ParseCIDRBlock_Valid) { + net::IPAddressNumber ip_number; + size_t prefix_length_in_bits; + + EXPECT_TRUE(net::ParseCIDRBlock("192.168.0.1/11", + &ip_number, + &prefix_length_in_bits)); + + EXPECT_EQ("192,168,0,1", DumpIPNumber(ip_number)); + EXPECT_EQ(11u, prefix_length_in_bits); +} + +TEST(NetUtilTest, IPNumberMatchesPrefix) { + struct { + const char* cidr_literal; + const char* ip_literal; + bool expected_to_match; + } tests[] = { + // IPv4 prefix with IPv4 inputs. + { + "10.10.1.32/27", + "10.10.1.44", + true + }, + { + "10.10.1.32/27", + "10.10.1.90", + false + }, + { + "10.10.1.32/27", + "10.10.1.90", + false + }, + + // IPv6 prefix with IPv6 inputs. + { + "2001:db8::/32", + "2001:DB8:3:4::5", + true + }, + { + "2001:db8::/32", + "2001:c8::", + false + }, + + // IPv6 prefix with IPv4 inputs. + { + "2001:db8::/33", + "192.168.0.1", + false + }, + { + "::ffff:192.168.0.1/112", + "192.168.33.77", + true + }, + + // IPv4 prefix with IPv6 inputs. + { + "10.11.33.44/16", + "::ffff:0a0b:89", + true + }, + { + "10.11.33.44/16", + "::ffff:10.12.33.44", + false + }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + SCOPED_TRACE(StringPrintf("Test[%" PRIuS "]: %s, %s", i, + tests[i].cidr_literal, + tests[i].ip_literal)); + + net::IPAddressNumber ip_number; + EXPECT_TRUE(net::ParseIPLiteralToNumber(tests[i].ip_literal, &ip_number)); + + net::IPAddressNumber ip_prefix; + size_t prefix_length_in_bits; + + EXPECT_TRUE(net::ParseCIDRBlock(tests[i].cidr_literal, + &ip_prefix, + &prefix_length_in_bits)); + + EXPECT_EQ(tests[i].expected_to_match, + net::IPNumberMatchesPrefix(ip_number, + ip_prefix, + prefix_length_in_bits)); + } +} diff --git a/net/http/http_auth_handler_negotiate_unittest.cc b/net/http/http_auth_handler_negotiate_unittest.cc index 910b5ef..a5b3011 100644 --- a/net/http/http_auth_handler_negotiate_unittest.cc +++ b/net/http/http_auth_handler_negotiate_unittest.cc @@ -67,8 +67,8 @@ TEST(HttpAuthHandlerNegotiateTest, CnameSync) { MockHostResolver* mock_resolver = new MockHostResolver(); scoped_refptr<HostResolver> scoped_resolver(mock_resolver); mock_resolver->set_synchronous_mode(true); - mock_resolver->rules()->AddIPv4Rule("alias", "10.0.0.2", - "canonical.example.com"); + mock_resolver->rules()->AddIPLiteralRule("alias", "10.0.0.2", + "canonical.example.com"); TestCompletionCallback callback; EXPECT_EQ(OK, auth_handler->ResolveCanonicalName(mock_resolver, &callback)); EXPECT_EQ(L"HTTP/canonical.example.com", auth_handler->spn()); @@ -82,8 +82,8 @@ TEST(HttpAuthHandlerNegotiateTest, CnameAsync) { MockHostResolver* mock_resolver = new MockHostResolver(); scoped_refptr<HostResolver> scoped_resolver(mock_resolver); mock_resolver->set_synchronous_mode(false); - mock_resolver->rules()->AddIPv4Rule("alias", "10.0.0.2", - "canonical.example.com"); + mock_resolver->rules()->AddIPLiteralRule("alias", "10.0.0.2", + "canonical.example.com"); TestCompletionCallback callback; EXPECT_EQ(ERR_IO_PENDING, auth_handler->ResolveCanonicalName(mock_resolver, &callback)); diff --git a/net/proxy/proxy_bypass_rules.cc b/net/proxy/proxy_bypass_rules.cc index 0fb14a7..50481c4 100644 --- a/net/proxy/proxy_bypass_rules.cc +++ b/net/proxy/proxy_bypass_rules.cc @@ -66,6 +66,50 @@ class BypassLocalRule : public ProxyBypassRules::Rule { } }; +// Rule for matching a URL that is an IP address, if that IP address falls +// within a certain numeric range. For example, you could use this rule to +// match all the IPs in the CIDR block 10.10.3.4/24. +class BypassIPBlockRule : public ProxyBypassRules::Rule { + public: + // |ip_prefix| + |prefix_length| define the IP block to match. + BypassIPBlockRule(const std::string& description, + const std::string& optional_scheme, + const IPAddressNumber& ip_prefix, + size_t prefix_length_in_bits) + : description_(description), + optional_scheme_(optional_scheme), + ip_prefix_(ip_prefix), + prefix_length_in_bits_(prefix_length_in_bits) { + } + + virtual bool Matches(const GURL& url) const { + if (!url.HostIsIPAddress()) + return false; + + if (!optional_scheme_.empty() && url.scheme() != optional_scheme_) + return false; // Didn't match scheme expectation. + + // Parse the input IP literal to a number. + IPAddressNumber ip_number; + if (!ParseIPLiteralToNumber(url.HostNoBrackets(), &ip_number)) + return false; + + // Test if it has the expected prefix. + return IPNumberMatchesPrefix(ip_number, ip_prefix_, + prefix_length_in_bits_); + } + + virtual std::string ToString() const { + return description_; + } + + private: + const std::string description_; + const std::string optional_scheme_; + const IPAddressNumber ip_prefix_; + const size_t prefix_length_in_bits_; +}; + // Returns true if the given string represents an IP address. bool IsIPAddress(const std::string& domain) { // From GURL::HostIsIPAddress() @@ -175,9 +219,16 @@ bool ProxyBypassRules::AddRuleFromStringInternal( // If there is a forward slash in the input, it is probably a CIDR style // mask. if (raw.find('/') != std::string::npos) { - // TODO(eroman): support CIDR-style proxy bypass entries - // (http://crbug.com/9835) - return false; + IPAddressNumber ip_prefix; + size_t prefix_length_in_bits; + + if (!ParseCIDRBlock(raw, &ip_prefix, &prefix_length_in_bits)) + return false; + + rules_.push_back( + new BypassIPBlockRule(raw, scheme, ip_prefix, prefix_length_in_bits)); + + return true; } // Check if we have an <ip-address>[:port] input. We need to treat this diff --git a/net/proxy/proxy_bypass_rules_unittest.cc b/net/proxy/proxy_bypass_rules_unittest.cc index 1bf24cb..208f9cd 100644 --- a/net/proxy/proxy_bypass_rules_unittest.cc +++ b/net/proxy/proxy_bypass_rules_unittest.cc @@ -281,6 +281,34 @@ TEST(ProxyBypassRulesTest, BypassLocalNames) { } } +TEST(ProxyBypassRulesTest, ParseAndMatchCIDR_IPv4) { + ProxyBypassRules rules; + rules.ParseFromString("192.168.1.1/16"); + ASSERT_EQ(1u, rules.rules().size()); + EXPECT_EQ("192.168.1.1/16", rules.rules()[0]->ToString()); + + EXPECT_TRUE(rules.Matches(GURL("http://192.168.1.1"))); + EXPECT_TRUE(rules.Matches(GURL("ftp://192.168.4.4"))); + EXPECT_TRUE(rules.Matches(GURL("https://192.168.0.0:81"))); + EXPECT_TRUE(rules.Matches(GURL("http://[::ffff:192.168.11.11]"))); + + EXPECT_FALSE(rules.Matches(GURL("http://foobar.com"))); + EXPECT_FALSE(rules.Matches(GURL("http://192.169.1.1"))); + EXPECT_FALSE(rules.Matches(GURL("http://xxx.192.168.1.1"))); + EXPECT_FALSE(rules.Matches(GURL("http://192.168.1.1.xx"))); +} + +TEST(ProxyBypassRulesTest, ParseAndMatchCIDR_IPv6) { + ProxyBypassRules rules; + rules.ParseFromString("a:b:c:d::/48"); + ASSERT_EQ(1u, rules.rules().size()); + EXPECT_EQ("a:b:c:d::/48", rules.rules()[0]->ToString()); + + EXPECT_TRUE(rules.Matches(GURL("http://[A:b:C:9::]"))); + EXPECT_FALSE(rules.Matches(GURL("http://foobar.com"))); + EXPECT_FALSE(rules.Matches(GURL("http://192.169.1.1"))); +} + } // namespace } // namespace net diff --git a/net/proxy/proxy_config_service_linux_unittest.cc b/net/proxy/proxy_config_service_linux_unittest.cc index bf8c850..fb1b016 100644 --- a/net/proxy/proxy_config_service_linux_unittest.cc +++ b/net/proxy/proxy_config_service_linux_unittest.cc @@ -873,8 +873,7 @@ TEST_F(ProxyConfigServiceLinuxTest, BasicEnvTest) { GURL(), // pac_url ProxyRulesExpectation::Single( "www.google.com:80", - // TODO(eroman): 127.0.0.1/8 is unsupported, so it was dropped - "*.google.com,*foo.com:99,1.2.3.4:22"), + "*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8"), }, }; diff --git a/net/socket/socks_client_socket_unittest.cc b/net/socket/socks_client_socket_unittest.cc index f05a7db..aaabc4b 100644 --- a/net/socket/socks_client_socket_unittest.cc +++ b/net/socket/socks_client_socket_unittest.cc @@ -341,7 +341,8 @@ TEST_F(SOCKSClientSocketTest, SOCKS4AFailedDNS) { TEST_F(SOCKSClientSocketTest, SOCKS4AIfDomainInIPv6) { const char hostname[] = "an.ipv6.address"; - host_resolver_->rules()->AddIPv6Rule(hostname, "2001:db8:8714:3a90::12", ""); + host_resolver_->rules()->AddIPLiteralRule(hostname, + "2001:db8:8714:3a90::12", ""); std::string request(kSOCKS4aInitialRequest, arraysize(kSOCKS4aInitialRequest)); |