summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-08 23:25:04 +0000
committereroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-08 23:25:04 +0000
commit5439283ff95a6f7d8b9e6d86c0fd178b0238acaa (patch)
tree6b067f14016e3c39adf8eb654ff18fc2ff1886de
parentec561333496a9b4f7f29718aef43094596f2d489 (diff)
downloadchromium_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.cc6
-rw-r--r--net/base/mock_host_resolver.cc86
-rw-r--r--net/base/mock_host_resolver.h24
-rw-r--r--net/base/net_util.cc114
-rw-r--r--net/base/net_util.h46
-rw-r--r--net/base/net_util_unittest.cc172
-rw-r--r--net/http/http_auth_handler_negotiate_unittest.cc8
-rw-r--r--net/proxy/proxy_bypass_rules.cc57
-rw-r--r--net/proxy/proxy_bypass_rules_unittest.cc28
-rw-r--r--net/proxy/proxy_config_service_linux_unittest.cc3
-rw-r--r--net/socket/socks_client_socket_unittest.cc3
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));