summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorziadh@chromium.org <ziadh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-26 23:31:00 +0000
committerziadh@chromium.org <ziadh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-26 23:31:00 +0000
commita37b8bb51f71508637a40afe7f35aeb02f79179c (patch)
treebf08491f3d7a9d71fa941c975a6962ad6d9f24a5 /net
parent8d8a5eb7170f38c8cab2006502282c6b58288ef0 (diff)
downloadchromium_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.js81
-rw-r--r--net/proxy/proxy_resolver_js_bindings.h5
-rw-r--r--net/proxy/proxy_resolver_script.h2
-rw-r--r--net/proxy/proxy_resolver_v8.cc203
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_;