diff options
author | ziadh@chromium.org <ziadh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-26 23:31:00 +0000 |
---|---|---|
committer | ziadh@chromium.org <ziadh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-26 23:31:00 +0000 |
commit | a37b8bb51f71508637a40afe7f35aeb02f79179c (patch) | |
tree | bf08491f3d7a9d71fa941c975a6962ad6d9f24a5 /net | |
parent | 8d8a5eb7170f38c8cab2006502282c6b58288ef0 (diff) | |
download | chromium_src-a37b8bb51f71508637a40afe7f35aeb02f79179c.zip chromium_src-a37b8bb51f71508637a40afe7f35aeb02f79179c.tar.gz chromium_src-a37b8bb51f71508637a40afe7f35aeb02f79179c.tar.bz2 |
Added missing MS PAC extensions for IPv6:
+ sortIpAddressList()
+ isInNetEx()
BUG=25407
r=eroman
Review URL: http://codereview.chromium.org/2818006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57606 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/data/proxy_resolver_v8_unittest/pac_library_unittest.js | 81 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_js_bindings.h | 5 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_script.h | 2 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_v8.cc | 203 |
4 files changed, 263 insertions, 28 deletions
diff --git a/net/data/proxy_resolver_v8_unittest/pac_library_unittest.js b/net/data/proxy_resolver_v8_unittest/pac_library_unittest.js index e3b3504..0c0a4a9 100644 --- a/net/data/proxy_resolver_v8_unittest/pac_library_unittest.js +++ b/net/data/proxy_resolver_v8_unittest/pac_library_unittest.js @@ -76,6 +76,87 @@ Tests.testShExpMatch = function(t) { t.expectFalse(shExpMatch("foo.jpg", "foo")); }; +Tests.testSortIpAddressList = function(t) { + t.expectEquals("::1;::2;::3", sortIpAddressList("::2;::3;::1")); + t.expectEquals( + "2001:4898:28:3:201:2ff:feea:fc14;fe80::5efe:157:9d3b:8b16;157.59.139.22", + sortIpAddressList("157.59.139.22;" + + "2001:4898:28:3:201:2ff:feea:fc14;" + + "fe80::5efe:157:9d3b:8b16")); + + // Single IP address (v4 and v6). + t.expectEquals("127.0.0.1", sortIpAddressList("127.0.0.1")); + t.expectEquals("::1", sortIpAddressList("::1")) + + // Verify that IPv6 address is not re-written (not reduced). + t.expectEquals("0:0::1;192.168.1.1", sortIpAddressList("192.168.1.1;0:0::1")); + + // Input is already sorted. + t.expectEquals("::1;192.168.1.3", sortIpAddressList("::1;192.168.1.3")); + + // Same-valued IP addresses (also tests stability). + t.expectEquals("0::1;::1;0:0::1", sortIpAddressList("0::1;::1;0:0::1")); + + // Contains extra semi-colons. + t.expectEquals("127.0.0.1", sortIpAddressList(";127.0.0.1;")); + + // Contains whitespace (spaces and tabs). + t.expectEquals("192.168.0.1;192.168.0.2", + sortIpAddressList("192.168.0.1; 192.168.0.2")); + t.expectEquals("127.0.0.0;127.0.0.1;127.0.0.2", + sortIpAddressList("127.0.0.1; 127.0.0.2; 127.0.0.0")); + + // Empty lists. + t.expectFalse(sortIpAddressList("")); + t.expectFalse(sortIpAddressList(" ")); + t.expectFalse(sortIpAddressList(";")); + t.expectFalse(sortIpAddressList(";;")); + t.expectFalse(sortIpAddressList(" ; ; ")); + + // Invalid IP addresses. + t.expectFalse(sortIpAddressList("256.0.0.1")); + t.expectFalse(sortIpAddressList("192.168.1.1;0:0:0:1;127.0.0.1")); + + // Call sortIpAddressList() with wonky arguments. + t.expectEquals(null, sortIpAddressList()); + t.expectEquals(null, sortIpAddressList(null)); + t.expectEquals(null, sortIpAddressList(null, null)); +}; + +Tests.testIsInNetEx = function(t) { + t.expectTrue(isInNetEx("198.95.249.79", "198.95.249.79/32")); + t.expectTrue(isInNetEx("198.95.115.10", "198.95.0.0/16")); + t.expectTrue(isInNetEx("198.95.1.1", "198.95.0.0/16")); + t.expectTrue(isInNetEx("198.95.1.1", "198.95.3.3/16")); + t.expectTrue(isInNetEx("0:0:0:0:0:0:7f00:1", "0:0:0:0:0:0:7f00:1/32")); + t.expectTrue(isInNetEx("3ffe:8311:ffff:abcd:1234:dead:beef:101", + "3ffe:8311:ffff::/48")); + + // IPv4 and IPv6 mix. + t.expectFalse(isInNetEx("127.0.0.1", "0:0:0:0:0:0:7f00:1/16")); + t.expectFalse(isInNetEx("192.168.24.3", "fe80:0:0:0:0:0:c0a8:1803/32")); + + t.expectFalse(isInNetEx("198.95.249.78", "198.95.249.79/32")); + t.expectFalse(isInNetEx("198.96.115.10", "198.95.0.0/16")); + t.expectFalse(isInNetEx("3fff:8311:ffff:abcd:1234:dead:beef:101", + "3ffe:8311:ffff::/48")); + + // Call isInNetEx with wonky arguments. + t.expectEquals(null, isInNetEx()); + t.expectEquals(null, isInNetEx(null)); + t.expectEquals(null, isInNetEx(null, null)); + t.expectEquals(null, isInNetEx(null, null, null)); + t.expectEquals(null, isInNetEx("198.95.249.79")); + + // Invalid IP address. + t.expectFalse(isInNetEx("256.0.0.1", "198.95.249.79")); + t.expectFalse(isInNetEx("127.0.0.1 ", "127.0.0.1/32")); // Extra space. + + // Invalid prefix. + t.expectFalse(isInNetEx("198.95.115.10", "198.95.0.0/34")); + t.expectFalse(isInNetEx("127.0.0.1", "127.0.0.1")); // Missing '/' in prefix. +}; + Tests.testWeekdayRange = function(t) { // Test with local time. MockDate.setCurrent("Tue Mar 03 2009"); diff --git a/net/proxy/proxy_resolver_js_bindings.h b/net/proxy/proxy_resolver_js_bindings.h index e3939c38..cbfdb75 100644 --- a/net/proxy/proxy_resolver_js_bindings.h +++ b/net/proxy/proxy_resolver_js_bindings.h @@ -34,7 +34,8 @@ class ProxyResolverJSBindings { // |*ip_address_list| with the result. // // This is a Microsoft extension to PAC for IPv6, see: - // http://blogs.msdn.com/wndp/articles/IPV6_PAC_Extensions_v0_9.aspx + // http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx + virtual bool MyIpAddressEx(std::string* ip_address_list) = 0; // Handler for "dnsResolve(host)". Returns true on success and fills @@ -46,7 +47,7 @@ class ProxyResolverJSBindings { // |*ip_address_list| with the result. // // This is a Microsoft extension to PAC for IPv6, see: - // http://blogs.msdn.com/wndp/articles/IPV6_PAC_Extensions_v0_9.aspx + // http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx virtual bool DnsResolveEx(const std::string& host, std::string* ip_address_list) = 0; diff --git a/net/proxy/proxy_resolver_script.h b/net/proxy/proxy_resolver_script.h index 21c5d27..fa80798 100644 --- a/net/proxy/proxy_resolver_script.h +++ b/net/proxy/proxy_resolver_script.h @@ -267,7 +267,7 @@ "}\n" // This is a Microsoft extension to PAC for IPv6, see: -// http://blogs.msdn.com/wndp/articles/IPV6_PAC_Extensions_v0_9.aspx +// http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx #define PROXY_RESOLVER_SCRIPT_EX \ "function isResolvableEx(host) {\n" \ " var ipList = dnsResolveEx(host);\n" \ diff --git a/net/proxy/proxy_resolver_v8.cc b/net/proxy/proxy_resolver_v8.cc index 495bbe9..2bf3b6d 100644 --- a/net/proxy/proxy_resolver_v8.cc +++ b/net/proxy/proxy_resolver_v8.cc @@ -2,10 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <algorithm> +#include <cstdio> + #include "net/proxy/proxy_resolver_v8.h" #include "base/basictypes.h" #include "base/logging.h" +#include "base/string_tokenizer.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "googleurl/src/gurl.h" @@ -13,6 +17,7 @@ #include "net/base/host_cache.h" #include "net/base/net_errors.h" #include "net/base/net_log.h" +#include "net/base/net_util.h" #include "net/proxy/proxy_info.h" #include "net/proxy/proxy_resolver_js_bindings.h" #include "net/proxy/proxy_resolver_request_context.h" @@ -26,25 +31,27 @@ // pulls in. // // In addition, we implement a subset of Microsoft's extensions to PAC. -// TODO(eroman): Implement the rest. -// -// - myIpAddressEx() -// - dnsResolveEx() -// - isResolvableEx() +// - myIpAddressEx() +// - dnsResolveEx() +// - isResolvableEx() +// - isInNetEx() +// - sortIpAddressList() // // It is worth noting that the original PAC specification does not describe // the return values on failure. Consequently, there are compatibility // differences between browsers on what to return on failure, which are // illustrated below: // -// ----------------+-------------+-------------------+-------------- -// | Firefox3 | InternetExplorer8 | --> Us <--- -// ----------------+-------------+-------------------+-------------- -// myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1" -// dnsResolve() | null | false | null -// myIpAddressEx() | N/A | "" | "" -// dnsResolveEx() | N/A | "" | "" -// ----------------+-------------+-------------------+-------------- +// --------------------+-------------+-------------------+-------------- +// | Firefox3 | InternetExplorer8 | --> Us <--- +// --------------------+-------------+-------------------+-------------- +// myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1" +// dnsResolve() | null | false | null +// myIpAddressEx() | N/A | "" | "" +// sortIpAddressList() | N/A | false | false +// dnsResolveEx() | N/A | "" | "" +// isInNetEx() | N/A | false | false +// --------------------+-------------+-------------------+-------------- // // TODO(eroman): The cell above reading ??? means I didn't test it. // @@ -53,15 +60,17 @@ // include both IPv4 and IPv6. The following table illustrates the // differences: // -// -----------------+-------------+-------------------+-------------- -// | Firefox3 | InternetExplorer8 | --> Us <--- -// -----------------+-------------+-------------------+-------------- -// myIpAddress() | IPv4/IPv6 | IPv4 | IPv4 -// dnsResolve() | IPv4/IPv6 | IPv4 | IPv4 -// isResolvable() | IPv4/IPv6 | IPv4 | IPv4 -// myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6 -// dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6 -// isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6 +// --------------------+-------------+-------------------+-------------- +// | Firefox3 | InternetExplorer8 | --> Us <--- +// --------------------+-------------+-------------------+-------------- +// myIpAddress() | IPv4/IPv6 | IPv4 | IPv4 +// dnsResolve() | IPv4/IPv6 | IPv4 | IPv4 +// isResolvable() | IPv4/IPv6 | IPv4 | IPv4 +// myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6 +// dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6 +// sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6 +// isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6 +// isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6 // -----------------+-------------+-------------------+-------------- namespace net { @@ -126,6 +135,13 @@ class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource { // the cutoff length for when to start wrapping rather than creating copies. const size_t kMaxStringBytesForCopy = 256; +// Converts a V8 String to a UTF8 std::string. +std::string V8StringToUTF8(v8::Handle<v8::String> s) { + std::string result; + s->WriteUtf8(WriteInto(&result, s->Length() + 1)); + return result; +} + // Converts a V8 String to a UTF16 string16. string16 V8StringToUTF16(v8::Handle<v8::String> s) { int len = s->Length(); @@ -213,6 +229,101 @@ bool GetHostnameArgument(const v8::Arguments& args, std::string* hostname) { return success; } +// Wrapper for passing around IP address strings and IPAddressNumber objects. +struct IPAddress { + IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number) + : string_value(ip_string), + ip_address_number(ip_number) { + } + + // Used for sorting IP addresses in ascending order in SortIpAddressList(). + // IP6 addresses are placed ahead of IPv4 addresses. + bool operator<(const IPAddress& rhs) const { + const IPAddressNumber& ip1 = this->ip_address_number; + const IPAddressNumber& ip2 = rhs.ip_address_number; + if (ip1.size() != ip2.size()) + return ip1.size() > ip2.size(); // IPv6 before IPv4. + DCHECK(ip1.size() == ip2.size()); + return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order. + } + + std::string string_value; + IPAddressNumber ip_address_number; +}; + +// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a +// semi-colon delimited string containing IP addresses. +// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited +// IP addresses or an empty string if unable to sort the IP address list. +// Returns 'true' if the sorting was successful, and 'false' if the input was an +// empty string, a string of separators (";" in this case), or if any of the IP +// addresses in the input list failed to parse. +bool SortIpAddressList(const std::string& ip_address_list, + std::string* sorted_ip_address_list) { + sorted_ip_address_list->clear(); + + // Strip all whitespace (mimics IE behavior). + std::string cleaned_ip_address_list; + RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list); + if (cleaned_ip_address_list.empty()) + return false; + + // Split-up IP addresses and store them in a vector. + std::vector<IPAddress> ip_vector; + IPAddressNumber ip_num; + StringTokenizer str_tok(cleaned_ip_address_list, ";"); + while (str_tok.GetNext()) { + if (!ParseIPLiteralToNumber(str_tok.token(), &ip_num)) + return false; + ip_vector.push_back(IPAddress(str_tok.token(), ip_num)); + } + + if (ip_vector.empty()) // Can happen if we have something like + return false; // sortIpAddressList(";") or sortIpAddressList("; ;") + + DCHECK(!ip_vector.empty()); + + // Sort lists according to ascending numeric value. + if (ip_vector.size() > 1) + std::stable_sort(ip_vector.begin(), ip_vector.end()); + + // Return a semi-colon delimited list of sorted addresses (IPv6 followed by + // IPv4). + for (size_t i = 0; i < ip_vector.size(); ++i) { + if (i > 0) + *sorted_ip_address_list += ";"; + *sorted_ip_address_list += ip_vector[i].string_value; + } + return true; +} + +// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string +// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a +// slash-delimited IP prefix with the top 'n' bits specified in the bit +// field. This returns 'true' if the address is in the same subnet, and +// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect +// format, or if an address and prefix of different types are used (e.g. IPv6 +// address and IPv4 prefix). +bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) { + IPAddressNumber address; + if (!ParseIPLiteralToNumber(ip_address, &address)) + return false; + + IPAddressNumber prefix; + size_t prefix_length_in_bits; + if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits)) + return false; + + // Both |address| and |prefix| must be of the same type (IPv4 or IPv6). + if (address.size() != prefix.size()) + return false; + + DCHECK((address.size() == 4 && prefix.size() == 4) || + (address.size() == 16 && prefix.size() == 16)); + + return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits); +} + } // namespace // ProxyResolverV8::Context --------------------------------------------------- @@ -310,7 +421,7 @@ class ProxyResolverV8::Context { global_template->Set(ASCIILiteralToV8String("dnsResolve"), dns_resolve_template); - // Microsoft's PAC extensions (incomplete): + // Microsoft's PAC extensions: v8::Local<v8::FunctionTemplate> dns_resolve_ex_template = v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this_); @@ -322,6 +433,16 @@ class ProxyResolverV8::Context { global_template->Set(ASCIILiteralToV8String("myIpAddressEx"), my_ip_address_ex_template); + v8::Local<v8::FunctionTemplate> sort_ip_address_list_template = + v8::FunctionTemplate::New(&SortIpAddressListCallback, v8_this_); + global_template->Set(ASCIILiteralToV8String("sortIpAddressList"), + sort_ip_address_list_template); + + v8::Local<v8::FunctionTemplate> is_in_net_ex_template = + v8::FunctionTemplate::New(&IsInNetExCallback, v8_this_); + global_template->Set(ASCIILiteralToV8String("isInNetEx"), + is_in_net_ex_template); + v8_context_ = v8::Context::New(NULL, global_template); v8::Context::Scope ctx(v8_context_); @@ -507,8 +628,7 @@ class ProxyResolverV8::Context { { v8::Unlocker unlocker; - success = context->js_bindings_->DnsResolveEx(hostname, - &ip_address_list); + success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list); } if (!success) @@ -517,6 +637,39 @@ class ProxyResolverV8::Context { return ASCIIStringToV8String(ip_address_list); } + // V8 callback for when "sortIpAddressList()" is invoked by the PAC script. + static v8::Handle<v8::Value> SortIpAddressListCallback( + const v8::Arguments& args) { + // We need at least one string argument. + if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) + return v8::Null(); + + std::string ip_address_list = V8StringToUTF8(args[0]->ToString()); + if (!IsStringASCII(ip_address_list)) + return v8::Null(); + std::string sorted_ip_address_list; + bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list); + if (!success) + return v8::False(); + return ASCIIStringToV8String(sorted_ip_address_list); + } + + // V8 callback for when "isInNetEx()" is invoked by the PAC script. + static v8::Handle<v8::Value> IsInNetExCallback(const v8::Arguments& args) { + // We need at least 2 string arguments. + if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() || + args[1].IsEmpty() || !args[1]->IsString()) + return v8::Null(); + + std::string ip_address = V8StringToUTF8(args[0]->ToString()); + if (!IsStringASCII(ip_address)) + return v8::False(); + std::string ip_prefix = V8StringToUTF8(args[1]->ToString()); + if (!IsStringASCII(ip_prefix)) + return v8::False(); + return IsInNetEx(ip_address, ip_prefix) ? v8::True() : v8::False(); + } + ProxyResolverJSBindings* js_bindings_; v8::Persistent<v8::External> v8_this_; v8::Persistent<v8::Context> v8_context_; |