diff options
Diffstat (limited to 'net/proxy')
-rw-r--r-- | net/proxy/proxy_resolver_js_bindings.cc | 50 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_js_bindings.h | 12 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_js_bindings_unittest.cc | 120 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_script.h | 10 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_v8.cc | 108 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_v8_unittest.cc | 41 |
6 files changed, 322 insertions, 19 deletions
diff --git a/net/proxy/proxy_resolver_js_bindings.cc b/net/proxy/proxy_resolver_js_bindings.cc index f9ea5ae..077e5c6 100644 --- a/net/proxy/proxy_resolver_js_bindings.cc +++ b/net/proxy/proxy_resolver_js_bindings.cc @@ -2,6 +2,14 @@ // source code is governed by a BSD-style license that can be found in the // LICENSE file. +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <ws2tcpip.h> +#else +#include <netdb.h> +#endif + #include "net/proxy/proxy_resolver_js_bindings.h" #include "base/compiler_specific.h" @@ -97,21 +105,24 @@ class DefaultJSBindings : public ProxyResolverJSBindings { } // Handler for "myIpAddress()". Returns empty string on failure. + // TODO(eroman): Perhaps enumerate the interfaces directly, using + // getifaddrs(). virtual std::string MyIpAddress() { // DnsResolve("") returns "", so no need to check for failure. return DnsResolve(GetHostName()); } + // Handler for "myIpAddressEx()". Returns empty string on failure. + virtual std::string MyIpAddressEx() { + return DnsResolveEx(GetHostName()); + } + // Handler for "dnsResolve(host)". Returns empty string on failure. virtual std::string DnsResolve(const std::string& host) { - // TODO(eroman): Should this return our IP address, or fail, or - // simply be unspecified (works differently on windows and mac os x). - if (host.empty()) - return std::string(); - // Do a sync resolve of the hostname. - // Disable IPv6 results. We do this because Internet Explorer does it -- - // consequently a lot of existing PAC scripts assume they will only get + // Disable IPv6 results. We do this because the PAC specification isn't + // really IPv6 friendly, and Internet Explorer also restricts to IPv4. + // Consequently a lot of existing PAC scripts assume they will only get // IPv4 results, and will misbehave if they get an IPv6 result. // See http://crbug.com/24641 for more details. net::AddressList address_list; @@ -130,6 +141,31 @@ class DefaultJSBindings : public ProxyResolverJSBindings { return net::NetAddressToString(address_list.head()); } + // Handler for "dnsResolveEx(host)". Returns empty string on failure. + virtual std::string DnsResolveEx(const std::string& host) { + // Do a sync resolve of the hostname. + net::AddressList address_list; + int result = host_resolver_->Resolve(host, + ADDRESS_FAMILY_UNSPECIFIED, + &address_list); + + if (result != OK) + return std::string(); // Failed. + + // Stringify all of the addresses in the address list, separated + // by semicolons. + std::string address_list_str; + const struct addrinfo* current_address = address_list.head(); + while (current_address) { + if (!address_list_str.empty()) + address_list_str += ";"; + address_list_str += net::NetAddressToString(current_address); + current_address = current_address->ai_next; + } + + return address_list_str; + } + // Handler for when an error is encountered. |line_number| may be -1. virtual void OnError(int line_number, const std::string& message) { if (line_number == -1) diff --git a/net/proxy/proxy_resolver_js_bindings.h b/net/proxy/proxy_resolver_js_bindings.h index 6eff69d..03ad61a 100644 --- a/net/proxy/proxy_resolver_js_bindings.h +++ b/net/proxy/proxy_resolver_js_bindings.h @@ -24,9 +24,21 @@ class ProxyResolverJSBindings { // Handler for "myIpAddress()". Returns empty string on failure. virtual std::string MyIpAddress() = 0; + // Handler for "myIpAddressEx()". Returns empty string on failure. + // + // This is a Microsoft extension to PAC for IPv6, see: + // http://blogs.msdn.com/wndp/articles/IPV6_PAC_Extensions_v0_9.aspx + virtual std::string MyIpAddressEx() = 0; + // Handler for "dnsResolve(host)". Returns empty string on failure. virtual std::string DnsResolve(const std::string& host) = 0; + // Handler for "dnsResolveEx(host)". Returns empty string on failure. + // + // This is a Microsoft extension to PAC for IPv6, see: + // http://blogs.msdn.com/wndp/articles/IPV6_PAC_Extensions_v0_9.aspx + virtual std::string DnsResolveEx(const std::string& host) = 0; + // Handler for when an error is encountered. |line_number| may be -1 // if a line number is not applicable to this error. virtual void OnError(int line_number, const std::string& error) = 0; diff --git a/net/proxy/proxy_resolver_js_bindings_unittest.cc b/net/proxy/proxy_resolver_js_bindings_unittest.cc index b84c829..4e0f6a6 100644 --- a/net/proxy/proxy_resolver_js_bindings_unittest.cc +++ b/net/proxy/proxy_resolver_js_bindings_unittest.cc @@ -2,7 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <ws2tcpip.h> +#else +#include <netdb.h> +#endif + #include "base/scoped_ptr.h" +#include "net/base/address_list.h" #include "net/base/mock_host_resolver.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" @@ -12,6 +21,67 @@ namespace net { namespace { +// This is a HostResolver that synchronously resolves all hosts to the +// following address list of length 3: +// 192.168.1.1 +// 172.22.34.1 +// 200.100.1.2 +class MockHostResolverWithMultipleResults : public HostResolver { + public: + // HostResolver methods: + virtual int Resolve(const RequestInfo& info, + AddressList* addresses, + CompletionCallback* callback, + RequestHandle* out_req, + LoadLog* load_log) { + // Build up the result list (in reverse). + AddressList temp_list = ResolveIPLiteral("200.100.1.2"); + temp_list = PrependAddressToList("172.22.34.1", temp_list); + temp_list = PrependAddressToList("192.168.1.1", temp_list); + *addresses = temp_list; + return OK; + } + virtual void CancelRequest(RequestHandle req) {} + virtual void AddObserver(Observer* observer) {} + virtual void RemoveObserver(Observer* observer) {} + virtual HostCache* GetHostCache() { return NULL; } + virtual void Shutdown() {} + + private: + // Resolves an IP literal to an address list. + AddressList ResolveIPLiteral(const char* ip_literal) { + AddressList result; + int rv = SystemHostResolverProc(ip_literal, + ADDRESS_FAMILY_UNSPECIFIED, + &result); + EXPECT_EQ(OK, rv); + EXPECT_EQ(NULL, result.head()->ai_next); + return result; + } + + // Builds an AddressList that is |ip_literal| + |address_list|. + // |orig_list| must not be empty. + AddressList PrependAddressToList(const char* ip_literal, + const AddressList& orig_list) { + // Build an addrinfo for |ip_literal|. + AddressList result = ResolveIPLiteral(ip_literal); + + struct addrinfo* result_head = const_cast<struct addrinfo*>(result.head()); + + // Temporarily append |orig_list| to |result|. + result_head->ai_next = const_cast<struct addrinfo*>(orig_list.head()); + + // Make a copy of the concatenated list. + AddressList concatenated; + concatenated.Copy(result.head()); + + // Restore |result| (so it is freed properly). + result_head->ai_next = NULL; + + return concatenated; + } +}; + TEST(ProxyResolverJSBindingsTest, DnsResolve) { scoped_refptr<MockHostResolver> host_resolver(new MockHostResolver); @@ -21,6 +91,7 @@ TEST(ProxyResolverJSBindingsTest, DnsResolve) { // Empty string is not considered a valid host (even though on some systems // requesting this will resolve to localhost). + host_resolver->rules()->AddSimulatedFailure(""); EXPECT_EQ("", bindings->DnsResolve("")); // Should call through to the HostResolver. @@ -48,10 +119,19 @@ TEST(ProxyResolverJSBindingsTest, MyIpAddress) { EXPECT_EQ("127.0.0.1", my_ip_address); } -// Tests that myIpAddress() and dnsResolve() pass the flag -// ADDRESS_FAMILY_IPV4 to the host resolver, as we don't want them -// to return IPv6 results. -TEST(ProxyResolverJSBindingsTest, DontUseIPv6) { +// Tests that the regular PAC functions restrict results to IPv4, +// but that the Microsoft extensions to PAC do not. We test this +// by seeing whether ADDRESS_FAMILY_IPV4 or ADDRESS_FAMILY_UNSPECIFIED +// was passed into to the host resolver. +// +// Restricted to IPv4 address family: +// myIpAddress() +// dnsResolve() +// +// Unrestricted address family: +// myIpAddressEx() +// dnsResolveEx() +TEST(ProxyResolverJSBindingsTest, RestrictAddressFamily) { scoped_refptr<MockHostResolver> host_resolver(new MockHostResolver); // Get a hold of a DefaultJSBindings* (it is a hidden impl class). @@ -67,7 +147,9 @@ TEST(ProxyResolverJSBindingsTest, DontUseIPv6) { host_resolver->rules()->AddRuleForAddressFamily( "*", ADDRESS_FAMILY_IPV4, "192.168.1.2"); host_resolver->rules()->AddRuleForAddressFamily( - "*", ADDRESS_FAMILY_UNSPECIFIED, "192.168.2.1"); + "foo", ADDRESS_FAMILY_UNSPECIFIED, "192.168.2.1"); + host_resolver->rules()->AddRuleForAddressFamily( + "*", ADDRESS_FAMILY_UNSPECIFIED, "192.168.2.2"); // Verify that our mock setups works as expected, and we get different results // depending if the address family was IPV4_ONLY or not. @@ -81,9 +163,31 @@ TEST(ProxyResolverJSBindingsTest, DontUseIPv6) { EXPECT_EQ("192.168.1.1", NetAddressToString(address_list.head())); // Now the actual test. - EXPECT_EQ("192.168.1.2", bindings->MyIpAddress()); - EXPECT_EQ("192.168.1.1", bindings->DnsResolve("foo")); - EXPECT_EQ("192.168.1.2", bindings->DnsResolve("foo2")); + EXPECT_EQ("192.168.1.2", bindings->MyIpAddress()); // IPv4 restricted. + EXPECT_EQ("192.168.1.1", bindings->DnsResolve("foo")); // IPv4 restricted. + EXPECT_EQ("192.168.1.2", bindings->DnsResolve("foo2")); // IPv4 restricted. + + EXPECT_EQ("192.168.2.2", bindings->MyIpAddressEx()); // Unrestricted. + EXPECT_EQ("192.168.2.1", bindings->DnsResolveEx("foo")); // Unrestricted. + EXPECT_EQ("192.168.2.2", bindings->DnsResolveEx("foo2")); // Unrestricted. +} + +// Test that myIpAddressEx() and dnsResolveEx() both return a semi-colon +// separated list of addresses (as opposed to the non-Ex versions which +// just return the first result). +TEST(ProxyResolverJSBindingsTest, ExFunctionsReturnList) { + scoped_refptr<HostResolver> host_resolver( + new MockHostResolverWithMultipleResults); + + // Get a hold of a DefaultJSBindings* (it is a hidden impl class). + scoped_ptr<ProxyResolverJSBindings> bindings( + ProxyResolverJSBindings::CreateDefault(host_resolver, NULL)); + + EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", + bindings->MyIpAddressEx()); + + EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", + bindings->DnsResolveEx("FOO")); } } // namespace diff --git a/net/proxy/proxy_resolver_script.h b/net/proxy/proxy_resolver_script.h index be592e4..a872cba 100644 --- a/net/proxy/proxy_resolver_script.h +++ b/net/proxy/proxy_resolver_script.h @@ -263,6 +263,14 @@ " date.setSeconds(date.getUTCSeconds());\n" \ " }\n" \ " return ((date1 <= date) && (date <= date2));\n" \ - "}\n" \ + "}\n" + +// This is a Microsoft extension to PAC for IPv6, see: +// http://blogs.msdn.com/wndp/articles/IPV6_PAC_Extensions_v0_9.aspx +#define PROXY_RESOLVER_SCRIPT_EX \ + "function isResolvableEx(host) {\n" \ + " var ipList = dnsResolveEx(host);\n" \ + " return (ipList != '');\n" \ + "}\n" #endif // NET_PROXY_PROXY_RESOLVER_SCRIPT_H_ diff --git a/net/proxy/proxy_resolver_v8.cc b/net/proxy/proxy_resolver_v8.cc index 7fd00bd..4db6bc3 100644 --- a/net/proxy/proxy_resolver_v8.cc +++ b/net/proxy/proxy_resolver_v8.cc @@ -14,6 +14,51 @@ #include "net/proxy/proxy_resolver_script.h" #include "v8/include/v8.h" +// Notes on the javascript environment: +// +// For the majority of the PAC utility functions, we use the same code +// as Firefox. See the javascript library that proxy_resolver_scipt.h +// pulls in. +// +// In addition, we implement a subset of Microsoft's extensions to PAC. +// TODO(eroman): Implement the rest. +// +// - myIpAddressEx() +// - dnsResolveEx() +// - isResolvableEx() +// +// 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 | "" | "" +// ----------------+-------------+-------------------+-------------- +// +// TODO(eroman): The cell above reading ??? means I didn't test it. +// +// Another difference is in how dnsResolve() and myIpAddress() are +// implemented -- whether they should restrict to IPv4 results, or +// 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 +// -----------------+-------------+-------------------+-------------- + namespace net { namespace { @@ -128,13 +173,28 @@ class ProxyResolverV8::Context { global_template->Set(v8::String::New("dnsResolve"), dns_resolve_template); + // Microsoft's PAC extensions (incomplete): + + v8::Local<v8::FunctionTemplate> dns_resolve_ex_template = + v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this_); + global_template->Set(v8::String::New("dnsResolveEx"), + dns_resolve_ex_template); + + v8::Local<v8::FunctionTemplate> my_ip_address_ex_template = + v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this_); + global_template->Set(v8::String::New("myIpAddressEx"), + my_ip_address_ex_template); + v8_context_ = v8::Context::New(NULL, global_template); v8::Context::Scope ctx(v8_context_); // Add the PAC utility functions to the environment. // (This script should never fail, as it is a string literal!) - int rv = RunScript(PROXY_RESOLVER_SCRIPT, kPacUtilityResourceName); + // Note that the two string literals are concatenated. + int rv = RunScript(PROXY_RESOLVER_SCRIPT + PROXY_RESOLVER_SCRIPT_EX, + kPacUtilityResourceName); if (rv != OK) { NOTREACHED(); return rv; @@ -248,6 +308,25 @@ class ProxyResolverV8::Context { return StdStringToV8String(result); } + // V8 callback for when "myIpAddressEx()" is invoked by the PAC script. + static v8::Handle<v8::Value> MyIpAddressExCallback( + const v8::Arguments& args) { + Context* context = + static_cast<Context*>(v8::External::Cast(*args.Data())->Value()); + + LoadLog::BeginEvent(context->current_request_load_log_, + LoadLog::TYPE_PROXY_RESOLVER_V8_MY_IP_ADDRESS_EX); + + // We shouldn't be called with any arguments, but will not complain if + // we are. + std::string result = context->js_bindings_->MyIpAddressEx(); + + LoadLog::EndEvent(context->current_request_load_log_, + LoadLog::TYPE_PROXY_RESOLVER_V8_MY_IP_ADDRESS_EX); + + return StdStringToV8String(result); + } + // V8 callback for when "dnsResolve()" is invoked by the PAC script. static v8::Handle<v8::Value> DnsResolveCallback(const v8::Arguments& args) { Context* context = @@ -270,10 +349,35 @@ class ProxyResolverV8::Context { LoadLog::EndEvent(context->current_request_load_log_, LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE); - // DoDnsResolve() returns empty string on failure. + // DnsResolve() returns empty string on failure. return result.empty() ? v8::Null() : StdStringToV8String(result); } + // V8 callback for when "dnsResolveEx()" is invoked by the PAC script. + static v8::Handle<v8::Value> DnsResolveExCallback(const v8::Arguments& args) { + Context* context = + static_cast<Context*>(v8::External::Cast(*args.Data())->Value()); + + // We need at least one argument. + std::string host; + if (args.Length() == 0) { + host = "undefined"; + } else { + if (!V8ObjectToString(args[0], &host)) + return v8::Undefined(); + } + + LoadLog::BeginEvent(context->current_request_load_log_, + LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE_EX); + + std::string result = context->js_bindings_->DnsResolveEx(host); + + LoadLog::EndEvent(context->current_request_load_log_, + LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE_EX); + + return StdStringToV8String(result); + } + ProxyResolverJSBindings* js_bindings_; LoadLog* current_request_load_log_; v8::Persistent<v8::External> v8_this_; diff --git a/net/proxy/proxy_resolver_v8_unittest.cc b/net/proxy/proxy_resolver_v8_unittest.cc index c172093..550426a 100644 --- a/net/proxy/proxy_resolver_v8_unittest.cc +++ b/net/proxy/proxy_resolver_v8_unittest.cc @@ -21,7 +21,7 @@ namespace { // list, for later verification. class MockJSBindings : public ProxyResolverJSBindings { public: - MockJSBindings() : my_ip_address_count(0) {} + MockJSBindings() : my_ip_address_count(0), my_ip_address_ex_count(0) {} virtual void Alert(const std::string& message) { LOG(INFO) << "PAC-alert: " << message; // Helpful when debugging. @@ -33,11 +33,21 @@ class MockJSBindings : public ProxyResolverJSBindings { return my_ip_address_result; } + virtual std::string MyIpAddressEx() { + my_ip_address_ex_count++; + return my_ip_address_ex_result; + } + virtual std::string DnsResolve(const std::string& host) { dns_resolves.push_back(host); return dns_resolve_result; } + virtual std::string DnsResolveEx(const std::string& host) { + dns_resolves_ex.push_back(host); + return dns_resolve_ex_result; + } + virtual void OnError(int line_number, const std::string& message) { // Helpful when debugging. LOG(INFO) << "PAC-error: [" << line_number << "] " << message; @@ -48,14 +58,18 @@ class MockJSBindings : public ProxyResolverJSBindings { // Mock values to return. std::string my_ip_address_result; + std::string my_ip_address_ex_result; std::string dns_resolve_result; + std::string dns_resolve_ex_result; // Inputs we got called with. std::vector<std::string> alerts; std::vector<std::string> errors; std::vector<int> errors_line_number; std::vector<std::string> dns_resolves; + std::vector<std::string> dns_resolves_ex; int my_ip_address_count; + int my_ip_address_ex_count; }; // This is the same as ProxyResolverV8, but it uses mock bindings in place of @@ -391,6 +405,14 @@ TEST(ProxyResolverV8Test, V8Bindings) { // MyIpAddress was called two times. EXPECT_EQ(2, bindings->my_ip_address_count); + + // MyIpAddressEx was called once. + EXPECT_EQ(1, bindings->my_ip_address_ex_count); + + // DnsResolveEx was called 2 times. + ASSERT_EQ(2U, bindings->dns_resolves_ex.size()); + EXPECT_EQ("is_resolvable", bindings->dns_resolves_ex[0]); + EXPECT_EQ("foobar", bindings->dns_resolves_ex[1]); } // Test that calls to the myIpAddress() and dnsResolve() bindings get @@ -464,5 +486,22 @@ TEST(ProxyResolverV8Test, EndsWithStatementNoNewline) { EXPECT_EQ("success:3", proxy_info.proxy_server().ToURI()); } +// Test the return values from myIpAddress(), myIpAddressEx(), dnsResolve(), +// dnsResolveEx(), isResolvable(), isResolvableEx(), when the the binding +// returns empty string (failure). This simulates the return values from +// those functions when the underlying DNS resolution fails. +TEST(ProxyResolverV8Test, DNSResolutionFailure) { + ProxyResolverV8WithMockBindings resolver; + int result = resolver.SetPacScriptFromDisk("dns_fail.js"); + EXPECT_EQ(OK, result); + + ProxyInfo proxy_info; + result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL); + + EXPECT_EQ(OK, result); + EXPECT_FALSE(proxy_info.is_direct()); + EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI()); +} + } // namespace } // namespace net |