├── MODULE_LICENSE_BSD ├── test ├── js-unittest │ ├── direct.js │ ├── return_integer.js │ ├── return_null.js │ ├── return_empty_string.js │ ├── return_undefined.js │ ├── return_function.js │ ├── return_object.js │ ├── b_139806216.js │ ├── no_entrypoint.js │ ├── return_unicode.js │ ├── unhandled_exception.js │ ├── ends_with_statement_no_semicolon.js │ ├── missing_close_brace.js │ ├── binding_from_global.js │ ├── side_effects.js │ ├── ends_with_comment.js │ ├── change_element_kind.js │ ├── b_132073833.js │ ├── international_domain_names.js │ ├── simple.js │ ├── dns_fail.js │ ├── passthrough.js │ ├── bindings.js │ └── pac_library_unittest.js ├── README ├── Android.mk ├── jstocstring.pl ├── proxy_resolver_v8_unittest.cc └── proxy_test_script.h ├── README ├── Android.mk ├── LICENSE ├── NOTICE └── src ├── proxy_resolver_js_bindings.h ├── net_util.h ├── proxy_resolver_v8.h ├── proxy_resolver_js_bindings.cc ├── net_util.cc ├── proxy_resolver_script.h └── proxy_resolver_v8.cc /MODULE_LICENSE_BSD: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/js-unittest/direct.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | return "DIRECT"; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/js-unittest/return_integer.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | return 0; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/js-unittest/return_null.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | return null; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/js-unittest/return_empty_string.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | return ""; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/js-unittest/return_undefined.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | return undefined; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/js-unittest/return_function.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | return FindProxyForURL; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/js-unittest/return_object.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | return {result: "PROXY foo"}; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/js-unittest/b_139806216.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host){ 2 | var x = new ArrayBuffer(1); 3 | return "DIRECT"; 4 | } 5 | -------------------------------------------------------------------------------- /test/js-unittest/no_entrypoint.js: -------------------------------------------------------------------------------- 1 | var x = "This is an invalid PAC script because it lacks a " + 2 | "FindProxyForURL() function"; 3 | -------------------------------------------------------------------------------- /test/js-unittest/return_unicode.js: -------------------------------------------------------------------------------- 1 | // U+200B is the codepoint for zero-width-space. 2 | function FindProxyForURL(url, host) { 3 | return "PROXY foo.com\u200B"; 4 | } 5 | -------------------------------------------------------------------------------- /test/js-unittest/unhandled_exception.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | // This will throw a runtime exception. 3 | return "PROXY x" + undefined_variable; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test/js-unittest/ends_with_statement_no_semicolon.js: -------------------------------------------------------------------------------- 1 | // Ends with a statement, and no terminal newline. 2 | function FindProxyForURL(url, host) { return "PROXY success:" + x; } 3 | x = 3 -------------------------------------------------------------------------------- /test/js-unittest/missing_close_brace.js: -------------------------------------------------------------------------------- 1 | // This PAC script is invalid, because there is a missing close brace 2 | // on the function FindProxyForURL(). 3 | 4 | function FindProxyForURL(url, host) { 5 | return "DIRECT"; 6 | 7 | -------------------------------------------------------------------------------- /test/js-unittest/binding_from_global.js: -------------------------------------------------------------------------------- 1 | // Calls a bindings outside of FindProxyForURL(). This causes the code to 2 | // get exercised during initialization. 3 | 4 | var x = myIpAddress(); 5 | 6 | function FindProxyForURL(url, host) { 7 | return "PROXY " + x + ":80"; 8 | } 9 | -------------------------------------------------------------------------------- /test/js-unittest/side_effects.js: -------------------------------------------------------------------------------- 1 | if (!gCounter) { 2 | // We write it this way so if the script gets loaded twice, 3 | // gCounter remains dirty. 4 | var gCounter = 0; 5 | } 6 | 7 | function FindProxyForURL(url, host) { 8 | return "PROXY sideffect_" + gCounter++; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | This directory contains a script that converts the javascript directory 2 | to a header file containing strings for use with testing. 3 | 4 | Do not modify proxy_script_test.h. Instead modify the files contained 5 | within js-unittest/ and then run the following command. 6 | ./jstocstring.pl js-unittest proxy_test_script.h 7 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This library is based on the proxy_resolver_v8 and other classes taken from 2 | chromium project. 3 | 4 | Modifications have been made to remove dependencies on chromium utility 5 | functions and classes. To make this library accessible on Android, the string 6 | utilities have been modified to use stl and the network functions have been 7 | modified to use UNIX functions. 8 | -------------------------------------------------------------------------------- /test/js-unittest/ends_with_comment.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | return "PROXY success:80"; 3 | } 4 | 5 | // We end the script with a comment (and no trailing newline). 6 | // This used to cause problems, because internally ProxyResolverV8 7 | // would append some functions to the script; the first line of 8 | // those extra functions was being considered part of the comment. -------------------------------------------------------------------------------- /test/js-unittest/change_element_kind.js: -------------------------------------------------------------------------------- 1 | // PAC script with getter that changes element kind. 2 | 3 | function FindProxyForURL(url, host) { 4 | let arr = []; 5 | arr[1000] = 0x1234; 6 | 7 | arr.__defineGetter__(256, function () { 8 | delete arr[256]; 9 | arr.unshift(1.1); 10 | }); 11 | 12 | let results = Object.entries(arr); 13 | let str = results.toString(); 14 | return "DIRECT"; 15 | } 16 | -------------------------------------------------------------------------------- /test/js-unittest/b_132073833.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host){ 2 | function opt() { 3 | opt['x'] = 1.1; 4 | try { 5 | Object.create(object); 6 | } catch (e) { 7 | } 8 | 9 | for (let i = 0; i < 100000; i++) { 10 | 11 | } 12 | } 13 | 14 | opt(); 15 | object = opt; 16 | opt(); 17 | 18 | return "DIRECT"; 19 | } 20 | 21 | var object; -------------------------------------------------------------------------------- /test/js-unittest/international_domain_names.js: -------------------------------------------------------------------------------- 1 | // Try resolving hostnames containing non-ASCII characters. 2 | 3 | function FindProxyForURL(url, host) { 4 | // This international hostname has a non-ASCII character. It is represented 5 | // in punycode as 'xn--bcher-kva.ch' 6 | var idn = 'B\u00fccher.ch'; 7 | 8 | // We disregard the actual return value -- all we care about is that on 9 | // the C++ end the bindings were passed the punycode equivalent of this 10 | // unicode hostname. 11 | dnsResolve(idn); 12 | dnsResolveEx(idn); 13 | 14 | return "DIRECT"; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /test/js-unittest/simple.js: -------------------------------------------------------------------------------- 1 | // PAC script which uses isInNet on both IP addresses and hosts, and calls 2 | // isResolvable(). 3 | 4 | function FindProxyForURL(url, host) { 5 | var my_ip = myIpAddress(); 6 | 7 | if (isInNet(my_ip, "172.16.0.0", "255.248.0.0")) { 8 | return "PROXY a:80"; 9 | } 10 | 11 | if (url.substring(0, 6) != "https:" && 12 | isInNet(host, "10.0.0.0", "255.0.0.0")) { 13 | return "PROXY b:80"; 14 | } 15 | 16 | if (dnsDomainIs(host, "foo.bar.baz.com") || !isResolvable(host)) { 17 | return "PROXY c:100"; 18 | } 19 | 20 | return "DIRECT"; 21 | } 22 | -------------------------------------------------------------------------------- /test/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk 5 | 6 | LOCAL_CPP_EXTENSION := .cc 7 | 8 | # Set up the target identity 9 | LOCAL_MODULE := proxy_resolver_v8_unittest 10 | 11 | LOCAL_SRC_FILES := \ 12 | proxy_resolver_v8_unittest.cc 13 | 14 | LOCAL_CFLAGS += \ 15 | -Wno-endif-labels \ 16 | -Wno-import \ 17 | -Wno-format \ 18 | 19 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/../src $(LOCAL_PATH)/ external/v8 20 | 21 | LOCAL_SHARED_LIBRARIES := libpac libutils liblog libandroid_runtime 22 | 23 | include $(BUILD_NATIVE_TEST) 24 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk 4 | 5 | LOCAL_CPP_EXTENSION := .cc 6 | 7 | # Set up the target identity 8 | LOCAL_MODULE := libpac 9 | LOCAL_MODULE_CLASS := SHARED_LIBRARIES 10 | 11 | LOCAL_SRC_FILES := \ 12 | src/proxy_resolver_v8.cc \ 13 | src/proxy_resolver_js_bindings.cc \ 14 | src/net_util.cc 15 | 16 | LOCAL_CFLAGS += \ 17 | -Wno-endif-labels \ 18 | -Wno-import \ 19 | -Wno-format \ 20 | -Wno-unused-parameter \ 21 | -Werror 22 | 23 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/src $(LOCAL_PATH)/../v8 24 | 25 | LOCAL_STATIC_LIBRARIES := libv8 26 | 27 | LOCAL_SHARED_LIBRARIES := libutils liblog libicuuc libicui18n 28 | 29 | LOCAL_CXX_STL := libc++ 30 | 31 | include $(BUILD_SHARED_LIBRARY) 32 | 33 | include $(LOCAL_PATH)/test/Android.mk 34 | -------------------------------------------------------------------------------- /test/js-unittest/dns_fail.js: -------------------------------------------------------------------------------- 1 | // This script should be run in an environment where all DNS resolution are 2 | // failing. It tests that functions return the expected values. 3 | // 4 | // Returns "PROXY success:80" on success. 5 | function FindProxyForURL(url, host) { 6 | try { 7 | expectEq("127.0.0.1", myIpAddress()); 8 | expectEq("", myIpAddressEx()); 9 | 10 | expectEq(null, dnsResolve("not-found")); 11 | expectEq("", dnsResolveEx("not-found")); 12 | 13 | expectEq(false, isResolvable("not-found")); 14 | expectEq(false, isResolvableEx("not-found")); 15 | 16 | return "PROXY success:80"; 17 | } catch(e) { 18 | alert(e); 19 | return "PROXY failed:80"; 20 | } 21 | } 22 | 23 | function expectEq(expected, actual) { 24 | if (expected != actual) 25 | throw "Expected " + expected + " but was " + actual; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/jstocstring.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | print "Reading from $ARGV[0]\nWriting to $ARGV[1]\n"; 4 | open(LS, "ls $ARGV[0]|"); 5 | open(FILE, "> $ARGV[1]"); 6 | print FILE "// This file is auto generated using the following command.\n"; 7 | print FILE "// Do not modify.\n"; 8 | print FILE "// \t./jstocstring.pl $ARGV[0] $ARGV[1]\n"; 9 | print FILE "#ifndef PROXY_TEST_SCRIPT_H_\n"; 10 | print FILE "#define PROXY_TEST_SCRIPT_H_\n\n"; 11 | 12 | while () { 13 | chomp(); 14 | open(FH, "cat $ARGV[0]/$_|"); 15 | if (s/\.js/_JS/) { 16 | $upper = uc(); 17 | print FILE "#define $upper \\\n"; 18 | while () { 19 | s/\"/\\\"/g; 20 | chomp(); 21 | print FILE " \"",$_,"\\n\" \\\n"; 22 | } 23 | } 24 | print FILE "\n" 25 | } 26 | print FILE "#endif //PROXY_TEST_SCRIPT_H_\n"; 27 | close(FILE); 28 | -------------------------------------------------------------------------------- /test/js-unittest/passthrough.js: -------------------------------------------------------------------------------- 1 | // Return a single-proxy result, which encodes ALL the arguments that were 2 | // passed to FindProxyForURL(). 3 | 4 | function FindProxyForURL(url, host) { 5 | if (arguments.length != 2) { 6 | throw "Wrong number of arguments passed to FindProxyForURL!"; 7 | return "FAIL"; 8 | } 9 | 10 | return "PROXY " + makePseudoHost(url + "." + host); 11 | } 12 | 13 | // Form a string that kind-of resembles a host. We will replace any 14 | // non-alphanumeric character with a dot, then fix up the oddly placed dots. 15 | function makePseudoHost(str) { 16 | var result = ""; 17 | 18 | for (var i = 0; i < str.length; ++i) { 19 | var c = str.charAt(i); 20 | if (!isValidPseudoHostChar(c)) { 21 | c = '.'; // Replace unsupported characters with a dot. 22 | } 23 | 24 | // Take care not to place multiple adjacent dots, 25 | // a dot at the beginning, or a dot at the end. 26 | if (c == '.' && 27 | (result.length == 0 || 28 | i == str.length - 1 || 29 | result.charAt(result.length - 1) == '.')) { 30 | continue; 31 | } 32 | result += c; 33 | } 34 | return result; 35 | } 36 | 37 | function isValidPseudoHostChar(c) { 38 | if (c >= '0' && c <= '9') 39 | return true; 40 | if (c >= 'a' && c <= 'z') 41 | return true; 42 | if (c >= 'A' && c <= 'Z') 43 | return true; 44 | return false; 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google Inc. nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google Inc. nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /test/js-unittest/bindings.js: -------------------------------------------------------------------------------- 1 | // Try calling the browser-side bound functions with varying (invalid) 2 | // inputs. There is no notion of "success" for this test, other than 3 | // verifying the correct C++ bindings were reached with expected values. 4 | 5 | function MyObject() { 6 | this.x = "3"; 7 | } 8 | 9 | MyObject.prototype.toString = function() { 10 | throw "exception from calling toString()"; 11 | } 12 | 13 | function expectEquals(expectation, actual) { 14 | if (!(expectation === actual)) { 15 | throw "FAIL: expected: " + expectation + ", actual: " + actual; 16 | } 17 | } 18 | 19 | function FindProxyForURL(url, host) { 20 | // Call dnsResolve with some wonky arguments. 21 | // Those expected to fail (because we have passed a non-string parameter) 22 | // will return |null|, whereas those that have called through to the C++ 23 | // bindings will return '127.0.0.1'. 24 | expectEquals(undefined, dnsResolve()); 25 | expectEquals(undefined, dnsResolve(null)); 26 | expectEquals(undefined, dnsResolve(undefined)); 27 | expectEquals('127.0.0.1', dnsResolve("")); 28 | expectEquals(undefined, dnsResolve({foo: 'bar'})); 29 | expectEquals(undefined, dnsResolve(fn)); 30 | expectEquals(undefined, dnsResolve(['3'])); 31 | expectEquals('127.0.0.1', dnsResolve("arg1", "arg2", "arg3", "arg4")); 32 | 33 | // Call alert with some wonky arguments. 34 | alert(); 35 | alert(null); 36 | alert(undefined); 37 | alert({foo:'bar'}); 38 | 39 | // This should throw an exception when we toString() the argument 40 | // to alert in the bindings. 41 | try { 42 | alert(new MyObject()); 43 | } catch (e) { 44 | alert(e); 45 | } 46 | 47 | // Call myIpAddress() with wonky arguments 48 | myIpAddress(null); 49 | myIpAddress(null, null); 50 | 51 | // Call myIpAddressEx() correctly (no arguments). 52 | myIpAddressEx(); 53 | 54 | // Call dnsResolveEx() (note that isResolvableEx() implicity calls it.) 55 | isResolvableEx("is_resolvable"); 56 | dnsResolveEx("foobar"); 57 | 58 | return "DIRECT"; 59 | } 60 | 61 | function fn() {} 62 | 63 | -------------------------------------------------------------------------------- /src/proxy_resolver_js_bindings.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_ 6 | #define NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_ 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace net { 13 | 14 | class ProxyErrorListener; 15 | 16 | // Interface for the javascript bindings. 17 | class ProxyResolverJSBindings { 18 | public: 19 | ProxyResolverJSBindings() {}// : current_request_context_(NULL) {} 20 | 21 | virtual ~ProxyResolverJSBindings() {} 22 | 23 | // Handler for "myIpAddress()". Returns true on success and fills 24 | // |*first_ip_address| with the result. 25 | virtual bool MyIpAddress(std::string* first_ip_address) = 0; 26 | 27 | // Handler for "myIpAddressEx()". Returns true on success and fills 28 | // |*ip_address_list| with the result. 29 | // 30 | // This is a Microsoft extension to PAC for IPv6, see: 31 | // http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx 32 | 33 | virtual bool MyIpAddressEx(std::string* ip_address_list) = 0; 34 | 35 | // Handler for "dnsResolve(host)". Returns true on success and fills 36 | // |*first_ip_address| with the result. 37 | virtual bool DnsResolve(const std::string& host, 38 | std::string* first_ip_address) = 0; 39 | 40 | // Handler for "dnsResolveEx(host)". Returns true on success and fills 41 | // |*ip_address_list| with the result. 42 | // 43 | // This is a Microsoft extension to PAC for IPv6, see: 44 | // http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx 45 | virtual bool DnsResolveEx(const std::string& host, 46 | std::string* ip_address_list) = 0; 47 | 48 | // Creates a default javascript bindings implementation that will: 49 | // - Use the provided host resolver to service dnsResolve(). 50 | // 51 | // Note that |host_resolver| will be used in sync mode mode. 52 | static ProxyResolverJSBindings* CreateDefault(); 53 | 54 | private: 55 | }; 56 | 57 | } // namespace net 58 | 59 | #endif // NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_ 60 | -------------------------------------------------------------------------------- /src/net_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef NET_BASE_NET_UTIL_H_ 6 | #define NET_BASE_NET_UTIL_H_ 7 | #pragma once 8 | 9 | #if defined(OS_WIN) 10 | #include 11 | #include 12 | #elif defined(OS_POSIX) 13 | #include 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | namespace net { 24 | 25 | // IPAddressNumber is used to represent an IP address's numeric value as an 26 | // array of bytes, from most significant to least significant. This is the 27 | // network byte ordering. 28 | // 29 | // IPv4 addresses will have length 4, whereas IPv6 address will have length 16. 30 | typedef std::vector IPAddressNumber; 31 | typedef std::vector IPAddressList; 32 | 33 | // Parses an IP address literal (either IPv4 or IPv6) to its numeric value. 34 | // Returns true on success and fills |ip_number| with the numeric value. 35 | bool ParseIPLiteralToNumber(const std::string& ip_literal, 36 | IPAddressNumber* ip_number); 37 | 38 | // Parses an IP block specifier from CIDR notation to an 39 | // (IP address, prefix length) pair. Returns true on success and fills 40 | // |*ip_number| with the numeric value of the IP address and sets 41 | // |*prefix_length_in_bits| with the length of the prefix. 42 | // 43 | // CIDR notation literals can use either IPv4 or IPv6 literals. Some examples: 44 | // 45 | // 10.10.3.1/20 46 | // a:b:c::/46 47 | // ::1/128 48 | bool ParseCIDRBlock(const std::string& cidr_literal, 49 | IPAddressNumber* ip_number, 50 | size_t* prefix_length_in_bits); 51 | 52 | // Compares an IP address to see if it falls within the specified IP block. 53 | // Returns true if it does, false otherwise. 54 | // 55 | // The IP block is given by (|ip_prefix|, |prefix_length_in_bits|) -- any 56 | // IP address whose |prefix_length_in_bits| most significant bits match 57 | // |ip_prefix| will be matched. 58 | // 59 | // In cases when an IPv4 address is being compared to an IPv6 address prefix 60 | // and vice versa, the IPv4 addresses will be converted to IPv4-mapped 61 | // (IPv6) addresses. 62 | bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number, 63 | const IPAddressNumber& ip_prefix, 64 | size_t prefix_length_in_bits); 65 | 66 | } // namespace net 67 | 68 | #endif // NET_BASE_NET_UTIL_H_ 69 | -------------------------------------------------------------------------------- /src/proxy_resolver_v8.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef NET_PROXY_PROXY_RESOLVER_V8_H_ 6 | #define NET_PROXY_PROXY_RESOLVER_V8_H_ 7 | #pragma once 8 | 9 | #include 10 | 11 | #include "proxy_resolver_js_bindings.h" 12 | 13 | namespace net { 14 | 15 | typedef void* RequestHandle; 16 | typedef void* CompletionCallback; 17 | 18 | #define OK 0 19 | #define ERR_PAC_SCRIPT_FAILED -1 20 | #define ERR_FAILED -2 21 | 22 | class ProxyErrorListener { 23 | protected: 24 | virtual ~ProxyErrorListener() {} 25 | public: 26 | virtual void AlertMessage(android::String16 message) = 0; 27 | virtual void ErrorMessage(android::String16 error) = 0; 28 | }; 29 | 30 | // Implementation of ProxyResolver that uses V8 to evaluate PAC scripts. 31 | // 32 | // ---------------------------------------------------------------------------- 33 | // !!! Important note on threading model: 34 | // ---------------------------------------------------------------------------- 35 | // There can be only one instance of V8 running at a time. To enforce this 36 | // constraint, ProxyResolverV8 holds a v8::Locker during execution. Therefore 37 | // it is OK to run multiple instances of ProxyResolverV8 on different threads, 38 | // since only one will be running inside V8 at a time. 39 | // 40 | // It is important that *ALL* instances of V8 in the process be using 41 | // v8::Locker. If not there can be race conditions beween the non-locked V8 42 | // instances and the locked V8 instances used by ProxyResolverV8 (assuming they 43 | // run on different threads). 44 | // 45 | // This is the case with the V8 instance used by chromium's renderer -- it runs 46 | // on a different thread from ProxyResolver (renderer thread vs PAC thread), 47 | // and does not use locking since it expects to be alone. 48 | class ProxyResolverV8 { 49 | public: 50 | // Constructs a ProxyResolverV8 with custom bindings. ProxyResolverV8 takes 51 | // ownership of |custom_js_bindings| and deletes it when ProxyResolverV8 52 | // is destroyed. 53 | explicit ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings, 54 | ProxyErrorListener* error_listener); 55 | 56 | virtual ~ProxyResolverV8(); 57 | 58 | ProxyResolverJSBindings* js_bindings() { return js_bindings_; } 59 | 60 | virtual int GetProxyForURL(const android::String16 spec, const android::String16 host, 61 | android::String16* results); 62 | virtual void PurgeMemory(); 63 | virtual int SetPacScript(const android::String16& script_data); 64 | 65 | private: 66 | // Context holds the Javascript state for the most recently loaded PAC 67 | // script. It corresponds with the data from the last call to 68 | // SetPacScript(). 69 | class Context; 70 | Context* context_; 71 | 72 | ProxyResolverJSBindings* js_bindings_; 73 | ProxyErrorListener* error_listener_; 74 | static bool initialized_for_this_process_; 75 | }; 76 | 77 | } // namespace net 78 | 79 | #endif // NET_PROXY_PROXY_RESOLVER_V8_H_ 80 | -------------------------------------------------------------------------------- /src/proxy_resolver_js_bindings.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "proxy_resolver_js_bindings.h" 6 | #include "proxy_resolver_v8.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "net_util.h" 16 | 17 | namespace net { 18 | 19 | // ProxyResolverJSBindings implementation. 20 | class DefaultJSBindings : public ProxyResolverJSBindings { 21 | public: 22 | DefaultJSBindings() { 23 | } 24 | 25 | // Handler for "myIpAddress()". 26 | // TODO: Perhaps enumerate the interfaces directly, using 27 | // getifaddrs(). 28 | virtual bool MyIpAddress(std::string* first_ip_address) { 29 | return MyIpAddressImpl(first_ip_address); 30 | } 31 | 32 | // Handler for "myIpAddressEx()". 33 | virtual bool MyIpAddressEx(std::string* ip_address_list) { 34 | return MyIpAddressExImpl(ip_address_list); 35 | } 36 | 37 | // Handler for "dnsResolve(host)". 38 | virtual bool DnsResolve(const std::string& host, 39 | std::string* first_ip_address) { 40 | return DnsResolveImpl(host, first_ip_address); 41 | } 42 | 43 | // Handler for "dnsResolveEx(host)". 44 | virtual bool DnsResolveEx(const std::string& host, 45 | std::string* ip_address_list) { 46 | return DnsResolveExImpl(host, ip_address_list); 47 | } 48 | 49 | private: 50 | bool MyIpAddressImpl(std::string* first_ip_address) { 51 | std::string my_hostname = GetHostName(); 52 | if (my_hostname.empty()) 53 | return false; 54 | return DnsResolveImpl(my_hostname, first_ip_address); 55 | } 56 | 57 | bool MyIpAddressExImpl(std::string* ip_address_list) { 58 | std::string my_hostname = GetHostName(); 59 | if (my_hostname.empty()) 60 | return false; 61 | return DnsResolveExImpl(my_hostname, ip_address_list); 62 | } 63 | 64 | bool DnsResolveImpl(const std::string& host, 65 | std::string* first_ip_address) { 66 | struct hostent* he = gethostbyname(host.c_str()); 67 | 68 | if (he == NULL || he->h_addr == NULL || he->h_addrtype != AF_INET) { 69 | return false; 70 | } 71 | 72 | char tmp[INET_ADDRSTRLEN]; 73 | if (inet_ntop(he->h_addrtype, he->h_addr, tmp, sizeof(tmp)) == NULL) { 74 | return false; 75 | } 76 | 77 | *first_ip_address = std::string(tmp); 78 | return true; 79 | } 80 | 81 | bool DnsResolveExImpl(const std::string& host, 82 | std::string* ip_address_list) { 83 | struct hostent* he = gethostbyname(host.c_str()); 84 | 85 | if (he == NULL) { 86 | return false; 87 | } 88 | std::string address_list_str; 89 | for (char** addr = &he->h_addr; *addr != NULL; ++addr) { 90 | if (!address_list_str.empty()) 91 | address_list_str += ";"; 92 | const std::string address_string = std::string(*addr); 93 | if (address_string.empty()) 94 | return false; 95 | address_list_str += address_string; 96 | } 97 | *ip_address_list = std::string(he->h_addr); 98 | return true; 99 | } 100 | 101 | std::string GetHostName() { 102 | char buffer[256]; 103 | if (gethostname(buffer, 256) != 0) { 104 | buffer[0] = '\0'; 105 | } 106 | return std::string(buffer); 107 | } 108 | }; 109 | 110 | // static 111 | ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault() { 112 | return new DefaultJSBindings(); 113 | } 114 | 115 | } // namespace net 116 | -------------------------------------------------------------------------------- /src/net_util.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include "net_util.h" 20 | 21 | namespace net { 22 | 23 | #ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */ 24 | #define INET6_ADDRSTRLEN 46 25 | #endif 26 | 27 | bool ParseIPLiteralToNumber(const std::string& ip_literal, 28 | IPAddressNumber* ip_number) { 29 | char buf[sizeof(struct in6_addr)]; 30 | int size = sizeof(struct in_addr); 31 | int mode = AF_INET; 32 | if (ip_literal.find(':') != std::string::npos) { 33 | mode = AF_INET6; 34 | size = sizeof(struct in6_addr); 35 | } 36 | if (inet_pton(mode, ip_literal.c_str(), buf) != 1) { 37 | return false; 38 | } 39 | ip_number->resize(size); 40 | for (int i = 0; i < size; i++) { 41 | (*ip_number)[i] = buf[i]; 42 | } 43 | return true; 44 | } 45 | 46 | IPAddressNumber ConvertIPv4NumberToIPv6Number( 47 | const IPAddressNumber& ipv4_number) { 48 | // IPv4-mapped addresses are formed by: 49 | // <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>. 50 | IPAddressNumber ipv6_number; 51 | ipv6_number.reserve(16); 52 | ipv6_number.insert(ipv6_number.end(), 10, 0); 53 | ipv6_number.push_back(0xFF); 54 | ipv6_number.push_back(0xFF); 55 | ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end()); 56 | return ipv6_number; 57 | } 58 | 59 | bool ParseCIDRBlock(const std::string& cidr_literal, 60 | IPAddressNumber* ip_number, 61 | size_t* prefix_length_in_bits) { 62 | // We expect CIDR notation to match one of these two templates: 63 | // "/" 64 | // "/" 65 | 66 | std::vector parts; 67 | size_t split = cidr_literal.find('/'); 68 | if (split == std::string::npos) 69 | return false; 70 | parts.push_back(cidr_literal.substr(0, split)); 71 | parts.push_back(cidr_literal.substr(split + 1)); 72 | if (parts[1].find('/') != std::string::npos) 73 | return false; 74 | 75 | // Parse the IP address. 76 | if (!ParseIPLiteralToNumber(parts[0], ip_number)) 77 | return false; 78 | 79 | // Parse the prefix length. 80 | int number_of_bits = atoi(parts[1].c_str()); 81 | 82 | // Make sure the prefix length is in a valid range. 83 | if (number_of_bits < 0 || 84 | number_of_bits > static_cast(ip_number->size() * 8)) 85 | return false; 86 | 87 | *prefix_length_in_bits = static_cast(number_of_bits); 88 | return true; 89 | } 90 | 91 | bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number, 92 | const IPAddressNumber& ip_prefix, 93 | size_t prefix_length_in_bits) { 94 | // Both the input IP address and the prefix IP address should be 95 | // either IPv4 or IPv6. 96 | 97 | // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to 98 | // IPv6 addresses in order to do the comparison. 99 | if (ip_number.size() != ip_prefix.size()) { 100 | if (ip_number.size() == 4) { 101 | return IPNumberMatchesPrefix(ConvertIPv4NumberToIPv6Number(ip_number), 102 | ip_prefix, prefix_length_in_bits); 103 | } 104 | return IPNumberMatchesPrefix(ip_number, 105 | ConvertIPv4NumberToIPv6Number(ip_prefix), 106 | 96 + prefix_length_in_bits); 107 | } 108 | 109 | // Otherwise we are comparing two IPv4 addresses, or two IPv6 addresses. 110 | // Compare all the bytes that fall entirely within the prefix. 111 | int num_entire_bytes_in_prefix = prefix_length_in_bits / 8; 112 | for (int i = 0; i < num_entire_bytes_in_prefix; ++i) { 113 | if (ip_number[i] != ip_prefix[i]) 114 | return false; 115 | } 116 | 117 | // In case the prefix was not a multiple of 8, there will be 1 byte 118 | // which is only partially masked. 119 | int remaining_bits = prefix_length_in_bits % 8; 120 | if (remaining_bits != 0) { 121 | unsigned char mask = 0xFF << (8 - remaining_bits); 122 | int i = num_entire_bytes_in_prefix; 123 | if ((ip_number[i] & mask) != (ip_prefix[i] & mask)) 124 | return false; 125 | } 126 | 127 | return true; 128 | } 129 | 130 | } // namespace net 131 | -------------------------------------------------------------------------------- /src/proxy_resolver_script.h: -------------------------------------------------------------------------------- 1 | /* ***** BEGIN LICENSE BLOCK ***** 2 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 | * 4 | * The contents of this file are subject to the Mozilla Public License Version 5 | * 1.1 (the "License"); you may not use this file except in compliance with 6 | * the License. You may obtain a copy of the License at 7 | * http://www.mozilla.org/MPL/ 8 | * 9 | * Software distributed under the License is distributed on an "AS IS" basis, 10 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing rights and limitations under the 12 | * License. 13 | * 14 | * The Original Code is mozilla.org code. 15 | * 16 | * The Initial Developer of the Original Code is 17 | * Netscape Communications Corporation. 18 | * Portions created by the Initial Developer are Copyright (C) 1998 19 | * the Initial Developer. All Rights Reserved. 20 | * 21 | * Contributor(s): 22 | * Akhil Arora 23 | * Tomi Leppikangas 24 | * Darin Fisher 25 | * 26 | * Alternatively, the contents of this file may be used under the terms of 27 | * either the GNU General Public License Version 2 or later (the "GPL"), or 28 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 29 | * in which case the provisions of the GPL or the LGPL are applicable instead 30 | * of those above. If you wish to allow use of your version of this file only 31 | * under the terms of either the GPL or the LGPL, and not to allow others to 32 | * use your version of this file under the terms of the MPL, indicate your 33 | * decision by deleting the provisions above and replace them with the notice 34 | * and other provisions required by the GPL or the LGPL. If you do not delete 35 | * the provisions above, a recipient may use your version of this file under 36 | * the terms of any one of the MPL, the GPL or the LGPL. 37 | * 38 | * ***** END LICENSE BLOCK ***** */ 39 | 40 | #ifndef NET_PROXY_PROXY_RESOLVER_SCRIPT_H_ 41 | #define NET_PROXY_PROXY_RESOLVER_SCRIPT_H_ 42 | 43 | // The following code was formatted from: 44 | // 'mozilla/netwerk/base/src/nsProxyAutoConfig.js' (1.55) 45 | // 46 | // Using the command: 47 | // $ cat nsProxyAutoConfig.js | 48 | // awk '/var pacUtils/,/EOF/' | 49 | // sed -e 's/^\s*$/""/g' | 50 | // sed -e 's/"\s*[+]\s*$/"/g' | 51 | // sed -e 's/"$/" \\/g' | 52 | // sed -e 's/\/(ipaddr);/\/.exec(ipaddr);/g' | 53 | // grep -v '^var pacUtils =' 54 | #define PROXY_RESOLVER_SCRIPT \ 55 | "function dnsDomainIs(host, domain) {\n" \ 56 | " return (host.length >= domain.length &&\n" \ 57 | " host.substring(host.length - domain.length) == domain);\n" \ 58 | "}\n" \ 59 | "" \ 60 | "function dnsDomainLevels(host) {\n" \ 61 | " return host.split('.').length-1;\n" \ 62 | "}\n" \ 63 | "" \ 64 | "function convert_addr(ipchars) {\n" \ 65 | " var bytes = ipchars.split('.');\n" \ 66 | " var result = ((bytes[0] & 0xff) << 24) |\n" \ 67 | " ((bytes[1] & 0xff) << 16) |\n" \ 68 | " ((bytes[2] & 0xff) << 8) |\n" \ 69 | " (bytes[3] & 0xff);\n" \ 70 | " return result;\n" \ 71 | "}\n" \ 72 | "" \ 73 | "function isInNet(ipaddr, pattern, maskstr) {\n" \ 74 | " var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipaddr);\n" \ 75 | " if (test == null) {\n" \ 76 | " ipaddr = dnsResolve(ipaddr);\n" \ 77 | " if (ipaddr == null)\n" \ 78 | " return false;\n" \ 79 | " } else if (test[1] > 255 || test[2] > 255 || \n" \ 80 | " test[3] > 255 || test[4] > 255) {\n" \ 81 | " return false; // not an IP address\n" \ 82 | " }\n" \ 83 | " var host = convert_addr(ipaddr);\n" \ 84 | " var pat = convert_addr(pattern);\n" \ 85 | " var mask = convert_addr(maskstr);\n" \ 86 | " return ((host & mask) == (pat & mask));\n" \ 87 | " \n" \ 88 | "}\n" \ 89 | "" \ 90 | "function isPlainHostName(host) {\n" \ 91 | " return (host.search('\\\\.') == -1);\n" \ 92 | "}\n" \ 93 | "" \ 94 | "function isResolvable(host) {\n" \ 95 | " var ip = dnsResolve(host);\n" \ 96 | " return (ip != null);\n" \ 97 | "}\n" \ 98 | "" \ 99 | "function localHostOrDomainIs(host, hostdom) {\n" \ 100 | " return (host == hostdom) ||\n" \ 101 | " (hostdom.lastIndexOf(host + '.', 0) == 0);\n" \ 102 | "}\n" \ 103 | "" \ 104 | "function shExpMatch(url, pattern) {\n" \ 105 | " pattern = pattern.replace(/\\./g, '\\\\.');\n" \ 106 | " pattern = pattern.replace(/\\*/g, '.*');\n" \ 107 | " pattern = pattern.replace(/\\?/g, '.');\n" \ 108 | " var newRe = new RegExp('^'+pattern+'$');\n" \ 109 | " return newRe.test(url);\n" \ 110 | "}\n" \ 111 | "" \ 112 | "var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n" \ 113 | "" \ 114 | "var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};\n" \ 115 | "" \ 116 | "function weekdayRange() {\n" \ 117 | " function getDay(weekday) {\n" \ 118 | " if (weekday in wdays) {\n" \ 119 | " return wdays[weekday];\n" \ 120 | " }\n" \ 121 | " return -1;\n" \ 122 | " }\n" \ 123 | " var date = new Date();\n" \ 124 | " var argc = arguments.length;\n" \ 125 | " var wday;\n" \ 126 | " if (argc < 1)\n" \ 127 | " return false;\n" \ 128 | " if (arguments[argc - 1] == 'GMT') {\n" \ 129 | " argc--;\n" \ 130 | " wday = date.getUTCDay();\n" \ 131 | " } else {\n" \ 132 | " wday = date.getDay();\n" \ 133 | " }\n" \ 134 | " var wd1 = getDay(arguments[0]);\n" \ 135 | " var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n" \ 136 | " return (wd1 == -1 || wd2 == -1) ? false\n" \ 137 | " : (wd1 <= wday && wday <= wd2);\n" \ 138 | "}\n" \ 139 | "" \ 140 | "function dateRange() {\n" \ 141 | " function getMonth(name) {\n" \ 142 | " if (name in months) {\n" \ 143 | " return months[name];\n" \ 144 | " }\n" \ 145 | " return -1;\n" \ 146 | " }\n" \ 147 | " var date = new Date();\n" \ 148 | " var argc = arguments.length;\n" \ 149 | " if (argc < 1) {\n" \ 150 | " return false;\n" \ 151 | " }\n" \ 152 | " var isGMT = (arguments[argc - 1] == 'GMT');\n" \ 153 | "\n" \ 154 | " if (isGMT) {\n" \ 155 | " argc--;\n" \ 156 | " }\n" \ 157 | " // function will work even without explict handling of this case\n" \ 158 | " if (argc == 1) {\n" \ 159 | " var tmp = parseInt(arguments[0]);\n" \ 160 | " if (isNaN(tmp)) {\n" \ 161 | " return ((isGMT ? date.getUTCMonth() : date.getMonth()) ==\n" \ 162 | "getMonth(arguments[0]));\n" \ 163 | " } else if (tmp < 32) {\n" \ 164 | " return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);\n" \ 165 | " } else { \n" \ 166 | " return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) ==\n" \ 167 | "tmp);\n" \ 168 | " }\n" \ 169 | " }\n" \ 170 | " var year = date.getFullYear();\n" \ 171 | " var date1, date2;\n" \ 172 | " date1 = new Date(year, 0, 1, 0, 0, 0);\n" \ 173 | " date2 = new Date(year, 11, 31, 23, 59, 59);\n" \ 174 | " var adjustMonth = false;\n" \ 175 | " for (var i = 0; i < (argc >> 1); i++) {\n" \ 176 | " var tmp = parseInt(arguments[i]);\n" \ 177 | " if (isNaN(tmp)) {\n" \ 178 | " var mon = getMonth(arguments[i]);\n" \ 179 | " date1.setMonth(mon);\n" \ 180 | " } else if (tmp < 32) {\n" \ 181 | " adjustMonth = (argc <= 2);\n" \ 182 | " date1.setDate(tmp);\n" \ 183 | " } else {\n" \ 184 | " date1.setFullYear(tmp);\n" \ 185 | " }\n" \ 186 | " }\n" \ 187 | " for (var i = (argc >> 1); i < argc; i++) {\n" \ 188 | " var tmp = parseInt(arguments[i]);\n" \ 189 | " if (isNaN(tmp)) {\n" \ 190 | " var mon = getMonth(arguments[i]);\n" \ 191 | " date2.setMonth(mon);\n" \ 192 | " } else if (tmp < 32) {\n" \ 193 | " date2.setDate(tmp);\n" \ 194 | " } else {\n" \ 195 | " date2.setFullYear(tmp);\n" \ 196 | " }\n" \ 197 | " }\n" \ 198 | " if (adjustMonth) {\n" \ 199 | " date1.setMonth(date.getMonth());\n" \ 200 | " date2.setMonth(date.getMonth());\n" \ 201 | " }\n" \ 202 | " if (isGMT) {\n" \ 203 | " var tmp = date;\n" \ 204 | " tmp.setFullYear(date.getUTCFullYear());\n" \ 205 | " tmp.setMonth(date.getUTCMonth());\n" \ 206 | " tmp.setDate(date.getUTCDate());\n" \ 207 | " tmp.setHours(date.getUTCHours());\n" \ 208 | " tmp.setMinutes(date.getUTCMinutes());\n" \ 209 | " tmp.setSeconds(date.getUTCSeconds());\n" \ 210 | " date = tmp;\n" \ 211 | " }\n" \ 212 | " return ((date1 <= date) && (date <= date2));\n" \ 213 | "}\n" \ 214 | "" \ 215 | "function timeRange() {\n" \ 216 | " var argc = arguments.length;\n" \ 217 | " var date = new Date();\n" \ 218 | " var isGMT= false;\n" \ 219 | "\n" \ 220 | " if (argc < 1) {\n" \ 221 | " return false;\n" \ 222 | " }\n" \ 223 | " if (arguments[argc - 1] == 'GMT') {\n" \ 224 | " isGMT = true;\n" \ 225 | " argc--;\n" \ 226 | " }\n" \ 227 | "\n" \ 228 | " var hour = isGMT ? date.getUTCHours() : date.getHours();\n" \ 229 | " var date1, date2;\n" \ 230 | " date1 = new Date();\n" \ 231 | " date2 = new Date();\n" \ 232 | "\n" \ 233 | " if (argc == 1) {\n" \ 234 | " return (hour == arguments[0]);\n" \ 235 | " } else if (argc == 2) {\n" \ 236 | " return ((arguments[0] <= hour) && (hour <= arguments[1]));\n" \ 237 | " } else {\n" \ 238 | " switch (argc) {\n" \ 239 | " case 6:\n" \ 240 | " date1.setSeconds(arguments[2]);\n" \ 241 | " date2.setSeconds(arguments[5]);\n" \ 242 | " case 4:\n" \ 243 | " var middle = argc >> 1;\n" \ 244 | " date1.setHours(arguments[0]);\n" \ 245 | " date1.setMinutes(arguments[1]);\n" \ 246 | " date2.setHours(arguments[middle]);\n" \ 247 | " date2.setMinutes(arguments[middle + 1]);\n" \ 248 | " if (middle == 2) {\n" \ 249 | " date2.setSeconds(59);\n" \ 250 | " }\n" \ 251 | " break;\n" \ 252 | " default:\n" \ 253 | " throw 'timeRange: bad number of arguments'\n" \ 254 | " }\n" \ 255 | " }\n" \ 256 | "\n" \ 257 | " if (isGMT) {\n" \ 258 | " date.setFullYear(date.getUTCFullYear());\n" \ 259 | " date.setMonth(date.getUTCMonth());\n" \ 260 | " date.setDate(date.getUTCDate());\n" \ 261 | " date.setHours(date.getUTCHours());\n" \ 262 | " date.setMinutes(date.getUTCMinutes());\n" \ 263 | " date.setSeconds(date.getUTCSeconds());\n" \ 264 | " }\n" \ 265 | " return ((date1 <= date) && (date <= date2));\n" \ 266 | "}\n" 267 | 268 | // This is a Microsoft extension to PAC for IPv6, see: 269 | // http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx 270 | #define PROXY_RESOLVER_SCRIPT_EX \ 271 | "function isResolvableEx(host) {\n" \ 272 | " var ipList = dnsResolveEx(host);\n" \ 273 | " return (ipList != '');\n" \ 274 | "}\n" 275 | 276 | #endif // NET_PROXY_PROXY_RESOLVER_SCRIPT_H_ 277 | -------------------------------------------------------------------------------- /test/js-unittest/pac_library_unittest.js: -------------------------------------------------------------------------------- 1 | // This should output "PROXY success:80" if all the tests pass. 2 | // Otherwise it will output "PROXY failure:". 3 | // 4 | // This aims to unit-test the PAC library functions, which are 5 | // exposed in the PAC's execution environment. (Namely, dnsDomainLevels, 6 | // timeRange, etc.) 7 | 8 | function FindProxyForURL(url, host) { 9 | var numTestsFailed = 0; 10 | 11 | // Run all the tests 12 | for (var test in Tests) { 13 | var t = new TestContext(test); 14 | 15 | // Run the test. 16 | Tests[test](t); 17 | 18 | if (t.failed()) { 19 | numTestsFailed++; 20 | } 21 | } 22 | 23 | if (numTestsFailed == 0) { 24 | return "PROXY success:80"; 25 | } 26 | return "PROXY failure:" + numTestsFailed; 27 | } 28 | 29 | // -------------------------- 30 | // Tests 31 | // -------------------------- 32 | 33 | var Tests = {}; 34 | 35 | Tests.testDnsDomainIs = function(t) { 36 | t.expectTrue(dnsDomainIs("google.com", ".com")); 37 | t.expectTrue(dnsDomainIs("google.co.uk", ".co.uk")); 38 | t.expectFalse(dnsDomainIs("google.com", ".co.uk")); 39 | t.expectFalse(dnsDomainIs("www.adobe.com", ".ad")); 40 | }; 41 | 42 | Tests.testDnsDomainLevels = function(t) { 43 | t.expectEquals(0, dnsDomainLevels("www")); 44 | t.expectEquals(2, dnsDomainLevels("www.google.com")); 45 | t.expectEquals(3, dnsDomainLevels("192.168.1.1")); 46 | }; 47 | 48 | Tests.testIsInNet = function(t) { 49 | t.expectTrue( 50 | isInNet("192.89.132.25", "192.89.132.25", "255.255.255.255")); 51 | t.expectFalse( 52 | isInNet("193.89.132.25", "192.89.132.25", "255.255.255.255")); 53 | 54 | t.expectTrue(isInNet("192.89.132.25", "192.89.0.0", "255.255.0.0")); 55 | t.expectFalse(isInNet("193.89.132.25", "192.89.0.0", "255.255.0.0")); 56 | 57 | t.expectFalse( 58 | isInNet("192.89.132.a", "192.89.0.0", "255.255.0.0")); 59 | }; 60 | 61 | Tests.testIsPlainHostName = function(t) { 62 | t.expectTrue(isPlainHostName("google")); 63 | t.expectFalse(isPlainHostName("google.com")); 64 | }; 65 | 66 | Tests.testLocalHostOrDomainIs = function(t) { 67 | t.expectTrue(localHostOrDomainIs("www.google.com", "www.google.com")); 68 | t.expectTrue(localHostOrDomainIs("www", "www.google.com")); 69 | t.expectFalse(localHostOrDomainIs("maps.google.com", "www.google.com")); 70 | }; 71 | 72 | Tests.testShExpMatch = function(t) { 73 | t.expectTrue(shExpMatch("foo.jpg", "*.jpg")); 74 | t.expectTrue(shExpMatch("foo5.jpg", "*o?.jpg")); 75 | t.expectFalse(shExpMatch("foo.jpg", ".jpg")); 76 | t.expectFalse(shExpMatch("foo.jpg", "foo")); 77 | }; 78 | 79 | Tests.testSortIpAddressList = function(t) { 80 | t.expectEquals("::1;::2;::3", sortIpAddressList("::2;::3;::1")); 81 | t.expectEquals( 82 | "2001:4898:28:3:201:2ff:feea:fc14;fe80::5efe:157:9d3b:8b16;157.59.139.22", 83 | sortIpAddressList("157.59.139.22;" + 84 | "2001:4898:28:3:201:2ff:feea:fc14;" + 85 | "fe80::5efe:157:9d3b:8b16")); 86 | 87 | // Single IP address (v4 and v6). 88 | t.expectEquals("127.0.0.1", sortIpAddressList("127.0.0.1")); 89 | t.expectEquals("::1", sortIpAddressList("::1")) 90 | 91 | // Verify that IPv6 address is not re-written (not reduced). 92 | t.expectEquals("0:0::1;192.168.1.1", sortIpAddressList("192.168.1.1;0:0::1")); 93 | 94 | // Input is already sorted. 95 | t.expectEquals("::1;192.168.1.3", sortIpAddressList("::1;192.168.1.3")); 96 | 97 | // Same-valued IP addresses (also tests stability). 98 | t.expectEquals("0::1;::1;0:0::1", sortIpAddressList("0::1;::1;0:0::1")); 99 | 100 | // Contains extra semi-colons. 101 | t.expectEquals("127.0.0.1", sortIpAddressList(";127.0.0.1;")); 102 | 103 | // Contains whitespace (spaces and tabs). 104 | t.expectEquals("192.168.0.1;192.168.0.2", 105 | sortIpAddressList("192.168.0.1; 192.168.0.2")); 106 | t.expectEquals("127.0.0.0;127.0.0.1;127.0.0.2", 107 | sortIpAddressList("127.0.0.1; 127.0.0.2; 127.0.0.0")); 108 | 109 | // Empty lists. 110 | t.expectFalse(sortIpAddressList("")); 111 | t.expectFalse(sortIpAddressList(" ")); 112 | t.expectFalse(sortIpAddressList(";")); 113 | t.expectFalse(sortIpAddressList(";;")); 114 | t.expectFalse(sortIpAddressList(" ; ; ")); 115 | 116 | // Invalid IP addresses. 117 | t.expectFalse(sortIpAddressList("256.0.0.1")); 118 | t.expectFalse(sortIpAddressList("192.168.1.1;0:0:0:1;127.0.0.1")); 119 | 120 | // Call sortIpAddressList() with wonky arguments. 121 | t.expectEquals(null, sortIpAddressList()); 122 | t.expectEquals(null, sortIpAddressList(null)); 123 | t.expectEquals(null, sortIpAddressList(null, null)); 124 | }; 125 | 126 | Tests.testIsInNetEx = function(t) { 127 | t.expectTrue(isInNetEx("198.95.249.79", "198.95.249.79/32")); 128 | t.expectTrue(isInNetEx("198.95.115.10", "198.95.0.0/16")); 129 | t.expectTrue(isInNetEx("198.95.1.1", "198.95.0.0/16")); 130 | t.expectTrue(isInNetEx("198.95.1.1", "198.95.3.3/16")); 131 | t.expectTrue(isInNetEx("0:0:0:0:0:0:7f00:1", "0:0:0:0:0:0:7f00:1/32")); 132 | t.expectTrue(isInNetEx("3ffe:8311:ffff:abcd:1234:dead:beef:101", 133 | "3ffe:8311:ffff::/48")); 134 | 135 | // IPv4 and IPv6 mix. 136 | t.expectFalse(isInNetEx("127.0.0.1", "0:0:0:0:0:0:7f00:1/16")); 137 | t.expectFalse(isInNetEx("192.168.24.3", "fe80:0:0:0:0:0:c0a8:1803/32")); 138 | 139 | t.expectFalse(isInNetEx("198.95.249.78", "198.95.249.79/32")); 140 | t.expectFalse(isInNetEx("198.96.115.10", "198.95.0.0/16")); 141 | t.expectFalse(isInNetEx("3fff:8311:ffff:abcd:1234:dead:beef:101", 142 | "3ffe:8311:ffff::/48")); 143 | 144 | // Call isInNetEx with wonky arguments. 145 | t.expectEquals(null, isInNetEx()); 146 | t.expectEquals(null, isInNetEx(null)); 147 | t.expectEquals(null, isInNetEx(null, null)); 148 | t.expectEquals(null, isInNetEx(null, null, null)); 149 | t.expectEquals(null, isInNetEx("198.95.249.79")); 150 | 151 | // Invalid IP address. 152 | t.expectFalse(isInNetEx("256.0.0.1", "198.95.249.79")); 153 | t.expectFalse(isInNetEx("127.0.0.1 ", "127.0.0.1/32")); // Extra space. 154 | 155 | // Invalid prefix. 156 | t.expectFalse(isInNetEx("198.95.115.10", "198.95.0.0/34")); 157 | t.expectFalse(isInNetEx("127.0.0.1", "127.0.0.1")); // Missing '/' in prefix. 158 | }; 159 | 160 | Tests.testWeekdayRange = function(t) { 161 | // Test with local time. 162 | MockDate.setCurrent("Tue Mar 03 2009"); 163 | t.expectEquals(true, weekdayRange("MON", "FRI")); 164 | t.expectEquals(true, weekdayRange("TUE", "FRI")); 165 | t.expectEquals(true, weekdayRange("TUE", "TUE")); 166 | t.expectEquals(true, weekdayRange("TUE")); 167 | t.expectEquals(false, weekdayRange("WED", "FRI")); 168 | t.expectEquals(false, weekdayRange("SUN", "MON")); 169 | t.expectEquals(false, weekdayRange("SAT")); 170 | t.expectEquals(false, weekdayRange("FRI", "MON")); 171 | 172 | // Test with GMT time. 173 | MockDate.setCurrent("Tue Mar 03 2009 GMT"); 174 | t.expectEquals(true, weekdayRange("MON", "FRI", "GMT")); 175 | t.expectEquals(true, weekdayRange("TUE", "FRI", "GMT")); 176 | t.expectEquals(true, weekdayRange("TUE", "TUE", "GMT")); 177 | t.expectEquals(true, weekdayRange("TUE", "GMT")); 178 | t.expectEquals(false, weekdayRange("WED", "FRI", "GMT")); 179 | t.expectEquals(false, weekdayRange("SUN", "MON", "GMT")); 180 | t.expectEquals(false, weekdayRange("SAT", "GMT")); 181 | }; 182 | 183 | Tests.testDateRange = function(t) { 184 | // dateRange(day) 185 | MockDate.setCurrent("Mar 03 2009"); 186 | t.expectEquals(true, dateRange(3)); 187 | t.expectEquals(false, dateRange(1)); 188 | 189 | // dateRange(day, "GMT") 190 | MockDate.setCurrent("Mar 03 2009 GMT"); 191 | t.expectEquals(true, dateRange(3, "GMT")); 192 | t.expectEquals(false, dateRange(1, "GMT")); 193 | 194 | // dateRange(day1, day2) 195 | MockDate.setCurrent("Mar 03 2009"); 196 | t.expectEquals(true, dateRange(1, 4)); 197 | t.expectEquals(false, dateRange(4, 20)); 198 | 199 | // dateRange(day, month) 200 | MockDate.setCurrent("Mar 03 2009"); 201 | t.expectEquals(true, dateRange(3, "MAR")); 202 | MockDate.setCurrent("Mar 03 2014"); 203 | t.expectEquals(true, dateRange(3, "MAR")); 204 | // TODO(eroman): 205 | //t.expectEquals(false, dateRange(2, "MAR")); 206 | //t.expectEquals(false, dateRange(3, "JAN")); 207 | 208 | // dateRange(day, month, year) 209 | MockDate.setCurrent("Mar 03 2009"); 210 | t.expectEquals(true, dateRange(3, "MAR", 2009)); 211 | t.expectEquals(false, dateRange(4, "MAR", 2009)); 212 | t.expectEquals(false, dateRange(3, "FEB", 2009)); 213 | MockDate.setCurrent("Mar 03 2014"); 214 | t.expectEquals(false, dateRange(3, "MAR", 2009)); 215 | 216 | // dateRange(month1, month2) 217 | MockDate.setCurrent("Mar 03 2009"); 218 | t.expectEquals(true, dateRange("JAN", "MAR")); 219 | t.expectEquals(true, dateRange("MAR", "APR")); 220 | t.expectEquals(false, dateRange("MAY", "SEP")); 221 | 222 | // dateRange(day1, month1, day2, month2) 223 | MockDate.setCurrent("Mar 03 2009"); 224 | t.expectEquals(true, dateRange(1, "JAN", 3, "MAR")); 225 | t.expectEquals(true, dateRange(3, "MAR", 4, "SEP")); 226 | t.expectEquals(false, dateRange(4, "MAR", 4, "SEP")); 227 | 228 | // dateRange(month1, year1, month2, year2) 229 | MockDate.setCurrent("Mar 03 2009"); 230 | t.expectEquals(true, dateRange("FEB", 2009, "MAR", 2009)); 231 | MockDate.setCurrent("Apr 03 2009"); 232 | t.expectEquals(true, dateRange("FEB", 2009, "MAR", 2010)); 233 | t.expectEquals(false, dateRange("FEB", 2009, "MAR", 2009)); 234 | 235 | // dateRange(day1, month1, year1, day2, month2, year2) 236 | MockDate.setCurrent("Mar 03 2009"); 237 | t.expectEquals(true, dateRange(1, "JAN", 2009, 3, "MAR", 2009)); 238 | t.expectEquals(true, dateRange(3, "MAR", 2009, 4, "SEP", 2009)); 239 | t.expectEquals(true, dateRange(3, "JAN", 2009, 4, "FEB", 2010)); 240 | t.expectEquals(false, dateRange(4, "MAR", 2009, 4, "SEP", 2009)); 241 | }; 242 | 243 | Tests.testTimeRange = function(t) { 244 | // timeRange(hour) 245 | MockDate.setCurrent("Mar 03, 2009 03:34:01"); 246 | t.expectEquals(true, timeRange(3)); 247 | t.expectEquals(false, timeRange(2)); 248 | 249 | // timeRange(hour1, hour2) 250 | MockDate.setCurrent("Mar 03, 2009 03:34:01"); 251 | t.expectEquals(true, timeRange(2, 3)); 252 | t.expectEquals(true, timeRange(2, 4)); 253 | t.expectEquals(true, timeRange(3, 5)); 254 | t.expectEquals(false, timeRange(1, 2)); 255 | t.expectEquals(false, timeRange(11, 12)); 256 | 257 | // timeRange(hour1, min1, hour2, min2) 258 | MockDate.setCurrent("Mar 03, 2009 03:34:01"); 259 | t.expectEquals(true, timeRange(1, 0, 3, 34)); 260 | t.expectEquals(true, timeRange(1, 0, 3, 35)); 261 | t.expectEquals(true, timeRange(3, 34, 5, 0)); 262 | t.expectEquals(false, timeRange(1, 0, 3, 0)); 263 | t.expectEquals(false, timeRange(11, 0, 16, 0)); 264 | 265 | // timeRange(hour1, min1, sec1, hour2, min2, sec2) 266 | MockDate.setCurrent("Mar 03, 2009 03:34:14"); 267 | t.expectEquals(true, timeRange(1, 0, 0, 3, 34, 14)); 268 | t.expectEquals(false, timeRange(1, 0, 0, 3, 34, 0)); 269 | t.expectEquals(true, timeRange(1, 0, 0, 3, 35, 0)); 270 | t.expectEquals(true, timeRange(3, 34, 0, 5, 0, 0)); 271 | t.expectEquals(false, timeRange(1, 0, 0, 3, 0, 0)); 272 | t.expectEquals(false, timeRange(11, 0, 0, 16, 0, 0)); 273 | }; 274 | 275 | // -------------------------- 276 | // TestContext 277 | // -------------------------- 278 | 279 | // |name| is the name of the test being executed, it will be used when logging 280 | // errors. 281 | function TestContext(name) { 282 | this.numFailures_ = 0; 283 | this.name_ = name; 284 | }; 285 | 286 | TestContext.prototype.failed = function() { 287 | return this.numFailures_ != 0; 288 | }; 289 | 290 | TestContext.prototype.expectEquals = function(expectation, actual) { 291 | if (!(expectation === actual)) { 292 | this.numFailures_++; 293 | this.log("FAIL: expected: " + expectation + ", actual: " + actual); 294 | } 295 | }; 296 | 297 | TestContext.prototype.expectTrue = function(x) { 298 | this.expectEquals(true, x); 299 | }; 300 | 301 | TestContext.prototype.expectFalse = function(x) { 302 | this.expectEquals(false, x); 303 | }; 304 | 305 | TestContext.prototype.log = function(x) { 306 | // Prefix with the test name that generated the log. 307 | try { 308 | alert(this.name_ + ": " + x); 309 | } catch(e) { 310 | // In case alert() is not defined. 311 | } 312 | }; 313 | 314 | // -------------------------- 315 | // MockDate 316 | // -------------------------- 317 | 318 | function MockDate() { 319 | this.wrappedDate_ = new MockDate.super_(MockDate.currentDateString_); 320 | }; 321 | 322 | // Setup the MockDate so it forwards methods to "this.wrappedDate_" (which is a 323 | // real Date object). We can't simply chain the prototypes since Date() doesn't 324 | // allow it. 325 | MockDate.init = function() { 326 | MockDate.super_ = Date; 327 | 328 | function createProxyMethod(methodName) { 329 | return function() { 330 | return this.wrappedDate_[methodName] 331 | .apply(this.wrappedDate_, arguments); 332 | } 333 | }; 334 | 335 | for (i in MockDate.methodNames_) { 336 | var methodName = MockDate.methodNames_[i]; 337 | // Don't define the closure directly in the loop body, since Javascript's 338 | // crazy scoping rules mean |methodName| actually bleeds out of the loop! 339 | MockDate.prototype[methodName] = createProxyMethod(methodName); 340 | } 341 | 342 | // Replace the native Date() with our mock. 343 | Date = MockDate; 344 | }; 345 | 346 | // Unfortunately Date()'s methods are non-enumerable, therefore list manually. 347 | MockDate.methodNames_ = [ 348 | "toString", "toDateString", "toTimeString", "toLocaleString", 349 | "toLocaleDateString", "toLocaleTimeString", "valueOf", "getTime", 350 | "getFullYear", "getUTCFullYear", "getMonth", "getUTCMonth", 351 | "getDate", "getUTCDate", "getDay", "getUTCDay", "getHours", "getUTCHours", 352 | "getMinutes", "getUTCMinutes", "getSeconds", "getUTCSeconds", 353 | "getMilliseconds", "getUTCMilliseconds", "getTimezoneOffset", "setTime", 354 | "setMilliseconds", "setUTCMilliseconds", "setSeconds", "setUTCSeconds", 355 | "setMinutes", "setUTCMinutes", "setHours", "setUTCHours", "setDate", 356 | "setUTCDate", "setMonth", "setUTCMonth", "setFullYear", "setUTCFullYear", 357 | "toGMTString", "toUTCString", "getYear", "setYear" 358 | ]; 359 | 360 | MockDate.setCurrent = function(currentDateString) { 361 | MockDate.currentDateString_ = currentDateString; 362 | } 363 | 364 | // Bind the methods to proxy requests to the wrapped Date(). 365 | MockDate.init(); 366 | 367 | -------------------------------------------------------------------------------- /test/proxy_resolver_v8_unittest.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #define LOG_TAG "ProxyResolverTest" 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include "android_runtime/AndroidRuntime.h" 13 | #include "proxy_test_script.h" 14 | #include "proxy_resolver_v8.h" 15 | 16 | using namespace android; 17 | namespace net { 18 | namespace { 19 | 20 | // Javascript bindings for ProxyResolverV8, which returns mock values. 21 | // Each time one of the bindings is called into, we push the input into a 22 | // list, for later verification. 23 | class MockJSBindings : public ProxyResolverJSBindings, public ProxyErrorListener { 24 | public: 25 | MockJSBindings() : my_ip_address_count(0), my_ip_address_ex_count(0) {} 26 | 27 | virtual bool MyIpAddress(std::string* ip_address) { 28 | my_ip_address_count++; 29 | *ip_address = my_ip_address_result; 30 | return !my_ip_address_result.empty(); 31 | } 32 | 33 | virtual bool MyIpAddressEx(std::string* ip_address_list) { 34 | my_ip_address_ex_count++; 35 | *ip_address_list = my_ip_address_ex_result; 36 | return !my_ip_address_ex_result.empty(); 37 | } 38 | 39 | virtual bool DnsResolve(const std::string& host, std::string* ip_address) { 40 | dns_resolves.push_back(host); 41 | *ip_address = dns_resolve_result; 42 | return !dns_resolve_result.empty(); 43 | } 44 | 45 | virtual bool DnsResolveEx(const std::string& host, 46 | std::string* ip_address_list) { 47 | dns_resolves_ex.push_back(host); 48 | *ip_address_list = dns_resolve_ex_result; 49 | return !dns_resolve_ex_result.empty(); 50 | } 51 | 52 | virtual void AlertMessage(String16 message) { 53 | String8 m8(message); 54 | std::string mstd(m8.string()); 55 | 56 | ALOGD("PAC-alert: %s\n", mstd.c_str()); // Helpful when debugging. 57 | alerts.push_back(mstd); 58 | } 59 | 60 | virtual void ErrorMessage(const String16 message) { 61 | String8 m8(message); 62 | std::string mstd(m8.string()); 63 | 64 | ALOGD("PAC-error: %s\n", mstd.c_str()); // Helpful when debugging. 65 | errors.push_back(mstd); 66 | } 67 | 68 | virtual void Shutdown() {} 69 | 70 | // Mock values to return. 71 | std::string my_ip_address_result; 72 | std::string my_ip_address_ex_result; 73 | std::string dns_resolve_result; 74 | std::string dns_resolve_ex_result; 75 | 76 | // Inputs we got called with. 77 | std::vector alerts; 78 | std::vector errors; 79 | std::vector dns_resolves; 80 | std::vector dns_resolves_ex; 81 | int my_ip_address_count; 82 | int my_ip_address_ex_count; 83 | }; 84 | 85 | // This is the same as ProxyResolverV8, but it uses mock bindings in place of 86 | // the default bindings, and has a helper function to load PAC scripts from 87 | // disk. 88 | class ProxyResolverV8WithMockBindings : public ProxyResolverV8 { 89 | public: 90 | ProxyResolverV8WithMockBindings(MockJSBindings* mock_js_bindings) : 91 | ProxyResolverV8(mock_js_bindings, mock_js_bindings), mock_js_bindings_(mock_js_bindings) { 92 | } 93 | 94 | MockJSBindings* mock_js_bindings() const { 95 | return mock_js_bindings_; 96 | } 97 | 98 | private: 99 | MockJSBindings* mock_js_bindings_; 100 | }; 101 | 102 | // Doesn't really matter what these values are for many of the tests. 103 | const String16 kQueryUrl("http://www.google.com"); 104 | const String16 kQueryHost("www.google.com"); 105 | String16 kResults; 106 | 107 | String16 currentPac; 108 | #define SCRIPT(x) (currentPac = String16(x)) 109 | 110 | void addString(std::vector* list, std::string str) { 111 | if (str.compare(0, 6, "DIRECT") == 0) { 112 | list->push_back("DIRECT"); 113 | } else if (str.compare(0, 6, "PROXY ") == 0) { 114 | list->push_back(str.substr(6)); 115 | } else { 116 | ALOGE("Unrecognized proxy string"); 117 | } 118 | } 119 | 120 | std::vector string16ToProxyList(String16 response) { 121 | std::vector ret; 122 | String8 response8(response); 123 | std::string rstr(response8.string()); 124 | if (rstr.find(';') == std::string::npos) { 125 | addString(&ret, rstr); 126 | return ret; 127 | } 128 | char str[128]; 129 | rstr.copy(str, 0, rstr.length()); 130 | const char* pch = strtok(str, ";"); 131 | 132 | while (pch != NULL) { 133 | // Skip leading whitespace 134 | while ((*pch) == ' ') ++pch; 135 | std::string pstring(pch); 136 | addString(&ret, pstring); 137 | 138 | pch = strtok(NULL, "; \t"); 139 | } 140 | 141 | return ret; 142 | } 143 | 144 | std::string StringPrintf(std::string str, int d) { 145 | char buf[30]; 146 | sprintf(buf, str.c_str(), d); 147 | return std::string(buf); 148 | } 149 | 150 | TEST(ProxyResolverV8Test, Direct) { 151 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 152 | int result = resolver.SetPacScript(SCRIPT(DIRECT_JS)); 153 | EXPECT_EQ(OK, result); 154 | 155 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 156 | 157 | EXPECT_EQ(OK, result); 158 | std::vector proxies = string16ToProxyList(kResults); 159 | EXPECT_EQ(proxies.size(), 1U); 160 | EXPECT_EQ("DIRECT",proxies[0]); 161 | 162 | EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 163 | EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 164 | } 165 | 166 | TEST(ProxyResolverV8Test, ReturnEmptyString) { 167 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 168 | int result = resolver.SetPacScript(SCRIPT(RETURN_EMPTY_STRING_JS)); 169 | EXPECT_EQ(OK, result); 170 | 171 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 172 | 173 | EXPECT_EQ(OK, result); 174 | std::vector proxies = string16ToProxyList(kResults); 175 | EXPECT_EQ(proxies.size(), 0U); 176 | 177 | EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 178 | EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 179 | } 180 | 181 | TEST(ProxyResolverV8Test, Basic) { 182 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 183 | int result = resolver.SetPacScript(SCRIPT(PASSTHROUGH_JS)); 184 | EXPECT_EQ(OK, result); 185 | 186 | // The "FindProxyForURL" of this PAC script simply concatenates all of the 187 | // arguments into a pseudo-host. The purpose of this test is to verify that 188 | // the correct arguments are being passed to FindProxyForURL(). 189 | { 190 | String16 queryUrl("http://query.com/path"); 191 | String16 queryHost("query.com"); 192 | result = resolver.GetProxyForURL(queryUrl, queryHost, &kResults); 193 | EXPECT_EQ(OK, result); 194 | std::vector proxies = string16ToProxyList(kResults); 195 | EXPECT_EQ(1U, proxies.size()); 196 | EXPECT_EQ("http.query.com.path.query.com", proxies[0]); 197 | } 198 | { 199 | String16 queryUrl("ftp://query.com:90/path"); 200 | String16 queryHost("query.com"); 201 | int result = resolver.GetProxyForURL(queryUrl, queryHost, &kResults); 202 | 203 | EXPECT_EQ(OK, result); 204 | // Note that FindProxyForURL(url, host) does not expect |host| to contain 205 | // the port number. 206 | std::vector proxies = string16ToProxyList(kResults); 207 | EXPECT_EQ(1U, proxies.size()); 208 | EXPECT_EQ("ftp.query.com.90.path.query.com", proxies[0]); 209 | 210 | EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 211 | EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 212 | } 213 | 214 | // We call this so we'll have code coverage of the function and valgrind will 215 | // make sure nothing bad happens. 216 | // 217 | // NOTE: This is here instead of in its own test so that we'll be calling it 218 | // after having done something, in hopes it won't be a no-op. 219 | resolver.PurgeMemory(); 220 | } 221 | 222 | TEST(ProxyResolverV8Test, BadReturnType) { 223 | // These are the files of PAC scripts which each return a non-string 224 | // types for FindProxyForURL(). They should all fail with 225 | // ERR_PAC_SCRIPT_FAILED. 226 | static const String16 files[] = { 227 | String16(RETURN_UNDEFINED_JS), 228 | String16(RETURN_INTEGER_JS), 229 | String16(RETURN_FUNCTION_JS), 230 | String16(RETURN_OBJECT_JS), 231 | String16(RETURN_NULL_JS) 232 | }; 233 | 234 | for (size_t i = 0; i < 5; ++i) { 235 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 236 | int result = resolver.SetPacScript(files[i]); 237 | EXPECT_EQ(OK, result); 238 | 239 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 240 | 241 | EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 242 | 243 | MockJSBindings* bindings = resolver.mock_js_bindings(); 244 | EXPECT_EQ(0U, bindings->alerts.size()); 245 | ASSERT_EQ(1U, bindings->errors.size()); 246 | EXPECT_EQ("FindProxyForURL() did not return a string.", 247 | bindings->errors[0]); 248 | } 249 | } 250 | 251 | // Try using a PAC script which defines no "FindProxyForURL" function. 252 | TEST(ProxyResolverV8Test, NoEntryPoint) { 253 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 254 | int result = resolver.SetPacScript(SCRIPT(NO_ENTRYPOINT_JS)); 255 | EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 256 | 257 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 258 | 259 | EXPECT_EQ(ERR_FAILED, result); 260 | } 261 | 262 | // Try loading a malformed PAC script. 263 | TEST(ProxyResolverV8Test, ParseError) { 264 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 265 | int result = resolver.SetPacScript(SCRIPT(MISSING_CLOSE_BRACE_JS)); 266 | EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 267 | 268 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 269 | 270 | EXPECT_EQ(ERR_FAILED, result); 271 | 272 | MockJSBindings* bindings = resolver.mock_js_bindings(); 273 | EXPECT_EQ(0U, bindings->alerts.size()); 274 | 275 | // We get one error during compilation. 276 | ASSERT_EQ(1U, bindings->errors.size()); 277 | 278 | EXPECT_EQ("Uncaught SyntaxError: Unexpected end of input", 279 | bindings->errors[0]); 280 | } 281 | 282 | // Run a PAC script several times, which has side-effects. 283 | TEST(ProxyResolverV8Test, SideEffects) { 284 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 285 | int result = resolver.SetPacScript(SCRIPT(SIDE_EFFECTS_JS)); 286 | 287 | // The PAC script increments a counter each time we invoke it. 288 | for (int i = 0; i < 3; ++i) { 289 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 290 | EXPECT_EQ(OK, result); 291 | std::vector proxies = string16ToProxyList(kResults); 292 | EXPECT_EQ(1U, proxies.size()); 293 | EXPECT_EQ(StringPrintf("sideffect_%d", i), 294 | proxies[0]); 295 | } 296 | 297 | // Reload the script -- the javascript environment should be reset, hence 298 | // the counter starts over. 299 | result = resolver.SetPacScript(SCRIPT(SIDE_EFFECTS_JS)); 300 | EXPECT_EQ(OK, result); 301 | 302 | for (int i = 0; i < 3; ++i) { 303 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 304 | EXPECT_EQ(OK, result); 305 | std::vector proxies = string16ToProxyList(kResults); 306 | EXPECT_EQ(1U, proxies.size()); 307 | EXPECT_EQ(StringPrintf("sideffect_%d", i), 308 | proxies[0]); 309 | } 310 | } 311 | 312 | // Execute a PAC script which throws an exception in FindProxyForURL. 313 | TEST(ProxyResolverV8Test, UnhandledException) { 314 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 315 | int result = resolver.SetPacScript(SCRIPT(UNHANDLED_EXCEPTION_JS)); 316 | EXPECT_EQ(OK, result); 317 | 318 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 319 | 320 | EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 321 | 322 | MockJSBindings* bindings = resolver.mock_js_bindings(); 323 | EXPECT_EQ(0U, bindings->alerts.size()); 324 | ASSERT_EQ(1U, bindings->errors.size()); 325 | EXPECT_EQ("Uncaught ReferenceError: undefined_variable is not defined", 326 | bindings->errors[0]); 327 | } 328 | 329 | TEST(ProxyResolverV8Test, ReturnUnicode) { 330 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 331 | int result = resolver.SetPacScript(SCRIPT(RETURN_UNICODE_JS)); 332 | EXPECT_EQ(OK, result); 333 | 334 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 335 | 336 | // The result from this resolve was unparseable, because it 337 | // wasn't ASCII. 338 | EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 339 | } 340 | 341 | // Test the PAC library functions that we expose in the JS environmnet. 342 | TEST(ProxyResolverV8Test, JavascriptLibrary) { 343 | ALOGE("Javascript start"); 344 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 345 | int result = resolver.SetPacScript(SCRIPT(PAC_LIBRARY_UNITTEST_JS)); 346 | EXPECT_EQ(OK, result); 347 | 348 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 349 | 350 | // If the javascript side of this unit-test fails, it will throw a javascript 351 | // exception. Otherwise it will return "PROXY success:80". 352 | EXPECT_EQ(OK, result); 353 | std::vector proxies = string16ToProxyList(kResults); 354 | EXPECT_EQ(1U, proxies.size()); 355 | EXPECT_EQ("success:80", proxies[0]); 356 | 357 | EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 358 | EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 359 | } 360 | 361 | // Try resolving when SetPacScriptByData() has not been called. 362 | TEST(ProxyResolverV8Test, NoSetPacScript) { 363 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 364 | 365 | 366 | // Resolve should fail, as we are not yet initialized with a script. 367 | int result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 368 | EXPECT_EQ(ERR_FAILED, result); 369 | 370 | // Initialize it. 371 | result = resolver.SetPacScript(SCRIPT(DIRECT_JS)); 372 | EXPECT_EQ(OK, result); 373 | 374 | // Resolve should now succeed. 375 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 376 | EXPECT_EQ(OK, result); 377 | 378 | // Clear it, by initializing with an empty string. 379 | resolver.SetPacScript(SCRIPT()); 380 | 381 | // Resolve should fail again now. 382 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 383 | EXPECT_EQ(ERR_FAILED, result); 384 | 385 | // Load a good script once more. 386 | result = resolver.SetPacScript(SCRIPT(DIRECT_JS)); 387 | EXPECT_EQ(OK, result); 388 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 389 | EXPECT_EQ(OK, result); 390 | 391 | EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 392 | EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 393 | } 394 | 395 | // Test marshalling/un-marshalling of values between C++/V8. 396 | TEST(ProxyResolverV8Test, V8Bindings) { 397 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 398 | MockJSBindings* bindings = resolver.mock_js_bindings(); 399 | bindings->dns_resolve_result = "127.0.0.1"; 400 | int result = resolver.SetPacScript(SCRIPT(BINDINGS_JS)); 401 | EXPECT_EQ(OK, result); 402 | 403 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 404 | 405 | EXPECT_EQ(OK, result); 406 | std::vector proxies = string16ToProxyList(kResults); 407 | EXPECT_EQ(1U, proxies.size()); 408 | EXPECT_EQ("DIRECT", proxies[0]); 409 | 410 | EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 411 | 412 | // Alert was called 5 times. 413 | ASSERT_EQ(5U, bindings->alerts.size()); 414 | EXPECT_EQ("undefined", bindings->alerts[0]); 415 | EXPECT_EQ("null", bindings->alerts[1]); 416 | EXPECT_EQ("undefined", bindings->alerts[2]); 417 | EXPECT_EQ("[object Object]", bindings->alerts[3]); 418 | EXPECT_EQ("exception from calling toString()", bindings->alerts[4]); 419 | 420 | // DnsResolve was called 8 times, however only 2 of those were string 421 | // parameters. (so 6 of them failed immediately). 422 | ASSERT_EQ(2U, bindings->dns_resolves.size()); 423 | EXPECT_EQ("", bindings->dns_resolves[0]); 424 | EXPECT_EQ("arg1", bindings->dns_resolves[1]); 425 | 426 | // MyIpAddress was called two times. 427 | EXPECT_EQ(2, bindings->my_ip_address_count); 428 | 429 | // MyIpAddressEx was called once. 430 | EXPECT_EQ(1, bindings->my_ip_address_ex_count); 431 | 432 | // DnsResolveEx was called 2 times. 433 | ASSERT_EQ(2U, bindings->dns_resolves_ex.size()); 434 | EXPECT_EQ("is_resolvable", bindings->dns_resolves_ex[0]); 435 | EXPECT_EQ("foobar", bindings->dns_resolves_ex[1]); 436 | } 437 | 438 | // Test calling a binding (myIpAddress()) from the script's global scope. 439 | // http://crbug.com/40026 440 | TEST(ProxyResolverV8Test, BindingCalledDuringInitialization) { 441 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 442 | 443 | int result = resolver.SetPacScript(SCRIPT(BINDING_FROM_GLOBAL_JS)); 444 | EXPECT_EQ(OK, result); 445 | 446 | MockJSBindings* bindings = resolver.mock_js_bindings(); 447 | 448 | // myIpAddress() got called during initialization of the script. 449 | EXPECT_EQ(1, bindings->my_ip_address_count); 450 | 451 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 452 | 453 | EXPECT_EQ(OK, result); 454 | std::vector proxies = string16ToProxyList(kResults); 455 | EXPECT_EQ(1U, proxies.size()); 456 | EXPECT_NE("DIRECT", proxies[0]); 457 | EXPECT_EQ("127.0.0.1:80", proxies[0]); 458 | 459 | // Check that no other bindings were called. 460 | EXPECT_EQ(0U, bindings->errors.size()); 461 | ASSERT_EQ(0U, bindings->alerts.size()); 462 | ASSERT_EQ(0U, bindings->dns_resolves.size()); 463 | EXPECT_EQ(0, bindings->my_ip_address_ex_count); 464 | ASSERT_EQ(0U, bindings->dns_resolves_ex.size()); 465 | } 466 | 467 | // Try loading a PAC script that ends with a comment and has no terminal 468 | // newline. This should not cause problems with the PAC utility functions 469 | // that we add to the script's environment. 470 | // http://crbug.com/22864 471 | TEST(ProxyResolverV8Test, EndsWithCommentNoNewline) { 472 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 473 | int result = resolver.SetPacScript(SCRIPT(ENDS_WITH_COMMENT_JS)); 474 | EXPECT_EQ(OK, result); 475 | 476 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 477 | 478 | EXPECT_EQ(OK, result); 479 | std::vector proxies = string16ToProxyList(kResults); 480 | EXPECT_EQ(1U, proxies.size()); 481 | EXPECT_NE("DIRECT", proxies[0]); 482 | EXPECT_EQ("success:80", proxies[0]); 483 | } 484 | 485 | // Try loading a PAC script that ends with a statement and has no terminal 486 | // newline. This should not cause problems with the PAC utility functions 487 | // that we add to the script's environment. 488 | // http://crbug.com/22864 489 | TEST(ProxyResolverV8Test, EndsWithStatementNoNewline) { 490 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 491 | int result = resolver.SetPacScript( 492 | SCRIPT(ENDS_WITH_STATEMENT_NO_SEMICOLON_JS)); 493 | EXPECT_EQ(OK, result); 494 | 495 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 496 | 497 | EXPECT_EQ(OK, result); 498 | std::vector proxies = string16ToProxyList(kResults); 499 | EXPECT_EQ(1U, proxies.size()); 500 | EXPECT_NE("DIRECT", proxies[0]); 501 | EXPECT_EQ("success:3", proxies[0]); 502 | } 503 | 504 | // Test the return values from myIpAddress(), myIpAddressEx(), dnsResolve(), 505 | // dnsResolveEx(), isResolvable(), isResolvableEx(), when the the binding 506 | // returns empty string (failure). This simulates the return values from 507 | // those functions when the underlying DNS resolution fails. 508 | TEST(ProxyResolverV8Test, DNSResolutionFailure) { 509 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 510 | int result = resolver.SetPacScript(SCRIPT(DNS_FAIL_JS)); 511 | EXPECT_EQ(OK, result); 512 | 513 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 514 | 515 | EXPECT_EQ(OK, result); 516 | std::vector proxies = string16ToProxyList(kResults); 517 | EXPECT_EQ(1U, proxies.size()); 518 | EXPECT_NE("DIRECT", proxies[0]); 519 | EXPECT_EQ("success:80", proxies[0]); 520 | } 521 | 522 | TEST(ProxyResolverV8Test, DNSResolutionOfInternationDomainName) { 523 | return; 524 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 525 | int result = resolver.SetPacScript(String16(INTERNATIONAL_DOMAIN_NAMES_JS)); 526 | EXPECT_EQ(OK, result); 527 | 528 | // Execute FindProxyForURL(). 529 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 530 | 531 | EXPECT_EQ(OK, result); 532 | std::vector proxies = string16ToProxyList(kResults); 533 | EXPECT_EQ(1U, proxies.size()); 534 | EXPECT_EQ("DIRECT", proxies[0]); 535 | 536 | // Check that the international domain name was converted to punycode 537 | // before passing it onto the bindings layer. 538 | MockJSBindings* bindings = resolver.mock_js_bindings(); 539 | 540 | ASSERT_EQ(1u, bindings->dns_resolves.size()); 541 | EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves[0]); 542 | 543 | ASSERT_EQ(1u, bindings->dns_resolves_ex.size()); 544 | EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves_ex[0]); 545 | } 546 | 547 | TEST(ProxyResolverV8Test, GetterChangesElementKind) { 548 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 549 | int result = resolver.SetPacScript(String16(CHANGE_ELEMENT_KIND_JS)); 550 | EXPECT_EQ(OK, result); 551 | 552 | // Execute FindProxyForURL(). 553 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 554 | 555 | EXPECT_EQ(OK, result); 556 | std::vector proxies = string16ToProxyList(kResults); 557 | EXPECT_EQ(1U, proxies.size()); 558 | EXPECT_EQ("DIRECT", proxies[0]); 559 | } 560 | 561 | TEST(ProxyResolverV8Test, B_132073833) { 562 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 563 | int result = resolver.SetPacScript(String16(B_132073833_JS)); 564 | EXPECT_EQ(OK, result); 565 | 566 | // Execute FindProxyForURL(). 567 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 568 | 569 | EXPECT_EQ(OK, result); 570 | std::vector proxies = string16ToProxyList(kResults); 571 | EXPECT_EQ(1U, proxies.size()); 572 | EXPECT_EQ("DIRECT", proxies[0]); 573 | } 574 | 575 | TEST(ProxyResolverV8Test, B_139806216) { 576 | ProxyResolverV8WithMockBindings resolver(new MockJSBindings()); 577 | int result = resolver.SetPacScript(String16(B_139806216_JS)); 578 | EXPECT_EQ(OK, result); 579 | 580 | // Execute FindProxyForURL(). 581 | result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults); 582 | 583 | EXPECT_EQ(OK, result); 584 | std::vector proxies = string16ToProxyList(kResults); 585 | EXPECT_EQ(1U, proxies.size()); 586 | EXPECT_EQ("DIRECT", proxies[0]); 587 | } 588 | 589 | 590 | } // namespace 591 | } // namespace net 592 | -------------------------------------------------------------------------------- /test/proxy_test_script.h: -------------------------------------------------------------------------------- 1 | // This file is auto generated using the following command. 2 | // Do not modify. 3 | // ./jstocstring.pl js-unittest proxy_test_script.h 4 | #ifndef PROXY_TEST_SCRIPT_H_ 5 | #define PROXY_TEST_SCRIPT_H_ 6 | 7 | #define B_132073833_JS \ 8 | "function FindProxyForURL(url, host){\n" \ 9 | " function opt() {\n" \ 10 | " opt['x'] = 1.1;\n" \ 11 | " try {\n" \ 12 | " Object.create(object);\n" \ 13 | " } catch (e) {\n" \ 14 | " }\n" \ 15 | "\n" \ 16 | " for (let i = 0; i < 100000; i++) {\n" \ 17 | "\n" \ 18 | " }\n" \ 19 | " }\n" \ 20 | "\n" \ 21 | " opt();\n" \ 22 | " object = opt;\n" \ 23 | " opt();\n" \ 24 | "\n" \ 25 | " return \"DIRECT\";\n" \ 26 | "}\n" \ 27 | "\n" \ 28 | "var object;\n" \ 29 | 30 | #define B_139806216_JS \ 31 | "function FindProxyForURL(url, host){\n" \ 32 | " var x = new ArrayBuffer(1);\n" \ 33 | " return \"DIRECT\";\n" \ 34 | "}\n" \ 35 | 36 | #define BINDING_FROM_GLOBAL_JS \ 37 | "// Calls a bindings outside of FindProxyForURL(). This causes the code to\n" \ 38 | "// get exercised during initialization.\n" \ 39 | "\n" \ 40 | "var x = myIpAddress();\n" \ 41 | "\n" \ 42 | "function FindProxyForURL(url, host) {\n" \ 43 | " return \"PROXY \" + x + \":80\";\n" \ 44 | "}\n" \ 45 | 46 | #define BINDINGS_JS \ 47 | "// Try calling the browser-side bound functions with varying (invalid)\n" \ 48 | "// inputs. There is no notion of \"success\" for this test, other than\n" \ 49 | "// verifying the correct C++ bindings were reached with expected values.\n" \ 50 | "\n" \ 51 | "function MyObject() {\n" \ 52 | " this.x = \"3\";\n" \ 53 | "}\n" \ 54 | "\n" \ 55 | "MyObject.prototype.toString = function() {\n" \ 56 | " throw \"exception from calling toString()\";\n" \ 57 | "}\n" \ 58 | "\n" \ 59 | "function expectEquals(expectation, actual) {\n" \ 60 | " if (!(expectation === actual)) {\n" \ 61 | " throw \"FAIL: expected: \" + expectation + \", actual: \" + actual;\n" \ 62 | " }\n" \ 63 | "}\n" \ 64 | "\n" \ 65 | "function FindProxyForURL(url, host) {\n" \ 66 | " // Call dnsResolve with some wonky arguments.\n" \ 67 | " // Those expected to fail (because we have passed a non-string parameter)\n" \ 68 | " // will return |null|, whereas those that have called through to the C++\n" \ 69 | " // bindings will return '127.0.0.1'.\n" \ 70 | " expectEquals(undefined, dnsResolve());\n" \ 71 | " expectEquals(undefined, dnsResolve(null));\n" \ 72 | " expectEquals(undefined, dnsResolve(undefined));\n" \ 73 | " expectEquals('127.0.0.1', dnsResolve(\"\"));\n" \ 74 | " expectEquals(undefined, dnsResolve({foo: 'bar'}));\n" \ 75 | " expectEquals(undefined, dnsResolve(fn));\n" \ 76 | " expectEquals(undefined, dnsResolve(['3']));\n" \ 77 | " expectEquals('127.0.0.1', dnsResolve(\"arg1\", \"arg2\", \"arg3\", \"arg4\"));\n" \ 78 | "\n" \ 79 | " // Call alert with some wonky arguments.\n" \ 80 | " alert();\n" \ 81 | " alert(null);\n" \ 82 | " alert(undefined);\n" \ 83 | " alert({foo:'bar'});\n" \ 84 | "\n" \ 85 | " // This should throw an exception when we toString() the argument\n" \ 86 | " // to alert in the bindings.\n" \ 87 | " try {\n" \ 88 | " alert(new MyObject());\n" \ 89 | " } catch (e) {\n" \ 90 | " alert(e);\n" \ 91 | " }\n" \ 92 | "\n" \ 93 | " // Call myIpAddress() with wonky arguments\n" \ 94 | " myIpAddress(null);\n" \ 95 | " myIpAddress(null, null);\n" \ 96 | "\n" \ 97 | " // Call myIpAddressEx() correctly (no arguments).\n" \ 98 | " myIpAddressEx();\n" \ 99 | "\n" \ 100 | " // Call dnsResolveEx() (note that isResolvableEx() implicity calls it.)\n" \ 101 | " isResolvableEx(\"is_resolvable\");\n" \ 102 | " dnsResolveEx(\"foobar\");\n" \ 103 | "\n" \ 104 | " return \"DIRECT\";\n" \ 105 | "}\n" \ 106 | "\n" \ 107 | "function fn() {}\n" \ 108 | "\n" \ 109 | 110 | #define CHANGE_ELEMENT_KIND_JS \ 111 | "// PAC script with getter that changes element kind.\n" \ 112 | "\n" \ 113 | "function FindProxyForURL(url, host) {\n" \ 114 | " let arr = [];\n" \ 115 | " arr[1000] = 0x1234;\n" \ 116 | "\n" \ 117 | " arr.__defineGetter__(256, function () {\n" \ 118 | " delete arr[256];\n" \ 119 | " arr.unshift(1.1);\n" \ 120 | " });\n" \ 121 | "\n" \ 122 | " let results = Object.entries(arr);\n" \ 123 | " let str = results.toString();\n" \ 124 | " return \"DIRECT\";\n" \ 125 | "}\n" \ 126 | 127 | #define DIRECT_JS \ 128 | "function FindProxyForURL(url, host) {\n" \ 129 | " return \"DIRECT\";\n" \ 130 | "}\n" \ 131 | "\n" \ 132 | 133 | #define DNS_FAIL_JS \ 134 | "// This script should be run in an environment where all DNS resolution are\n" \ 135 | "// failing. It tests that functions return the expected values.\n" \ 136 | "//\n" \ 137 | "// Returns \"PROXY success:80\" on success.\n" \ 138 | "function FindProxyForURL(url, host) {\n" \ 139 | " try {\n" \ 140 | " expectEq(\"127.0.0.1\", myIpAddress());\n" \ 141 | " expectEq(\"\", myIpAddressEx());\n" \ 142 | "\n" \ 143 | " expectEq(null, dnsResolve(\"not-found\"));\n" \ 144 | " expectEq(\"\", dnsResolveEx(\"not-found\"));\n" \ 145 | "\n" \ 146 | " expectEq(false, isResolvable(\"not-found\"));\n" \ 147 | " expectEq(false, isResolvableEx(\"not-found\"));\n" \ 148 | "\n" \ 149 | " return \"PROXY success:80\";\n" \ 150 | " } catch(e) {\n" \ 151 | " alert(e);\n" \ 152 | " return \"PROXY failed:80\";\n" \ 153 | " }\n" \ 154 | "}\n" \ 155 | "\n" \ 156 | "function expectEq(expected, actual) {\n" \ 157 | " if (expected != actual)\n" \ 158 | " throw \"Expected \" + expected + \" but was \" + actual;\n" \ 159 | "}\n" \ 160 | "\n" \ 161 | 162 | #define ENDS_WITH_COMMENT_JS \ 163 | "function FindProxyForURL(url, host) {\n" \ 164 | " return \"PROXY success:80\";\n" \ 165 | "}\n" \ 166 | "\n" \ 167 | "// We end the script with a comment (and no trailing newline).\n" \ 168 | "// This used to cause problems, because internally ProxyResolverV8\n" \ 169 | "// would append some functions to the script; the first line of\n" \ 170 | "// those extra functions was being considered part of the comment.\n" \ 171 | 172 | #define ENDS_WITH_STATEMENT_NO_SEMICOLON_JS \ 173 | "// Ends with a statement, and no terminal newline.\n" \ 174 | "function FindProxyForURL(url, host) { return \"PROXY success:\" + x; }\n" \ 175 | "x = 3\n" \ 176 | 177 | #define INTERNATIONAL_DOMAIN_NAMES_JS \ 178 | "// Try resolving hostnames containing non-ASCII characters.\n" \ 179 | "\n" \ 180 | "function FindProxyForURL(url, host) {\n" \ 181 | " // This international hostname has a non-ASCII character. It is represented\n" \ 182 | " // in punycode as 'xn--bcher-kva.ch'\n" \ 183 | " var idn = 'B\u00fccher.ch';\n" \ 184 | "\n" \ 185 | " // We disregard the actual return value -- all we care about is that on\n" \ 186 | " // the C++ end the bindings were passed the punycode equivalent of this\n" \ 187 | " // unicode hostname.\n" \ 188 | " dnsResolve(idn);\n" \ 189 | " dnsResolveEx(idn);\n" \ 190 | "\n" \ 191 | " return \"DIRECT\";\n" \ 192 | "}\n" \ 193 | "\n" \ 194 | 195 | #define MISSING_CLOSE_BRACE_JS \ 196 | "// This PAC script is invalid, because there is a missing close brace\n" \ 197 | "// on the function FindProxyForURL().\n" \ 198 | "\n" \ 199 | "function FindProxyForURL(url, host) {\n" \ 200 | " return \"DIRECT\";\n" \ 201 | "\n" \ 202 | 203 | #define NO_ENTRYPOINT_JS \ 204 | "var x = \"This is an invalid PAC script because it lacks a \" +\n" \ 205 | " \"FindProxyForURL() function\";\n" \ 206 | 207 | #define PAC_LIBRARY_UNITTEST_JS \ 208 | "// This should output \"PROXY success:80\" if all the tests pass.\n" \ 209 | "// Otherwise it will output \"PROXY failure:\".\n" \ 210 | "//\n" \ 211 | "// This aims to unit-test the PAC library functions, which are\n" \ 212 | "// exposed in the PAC's execution environment. (Namely, dnsDomainLevels,\n" \ 213 | "// timeRange, etc.)\n" \ 214 | "\n" \ 215 | "function FindProxyForURL(url, host) {\n" \ 216 | " var numTestsFailed = 0;\n" \ 217 | "\n" \ 218 | " // Run all the tests\n" \ 219 | " for (var test in Tests) {\n" \ 220 | " var t = new TestContext(test);\n" \ 221 | "\n" \ 222 | " // Run the test.\n" \ 223 | " Tests[test](t);\n" \ 224 | "\n" \ 225 | " if (t.failed()) {\n" \ 226 | " numTestsFailed++;\n" \ 227 | " }\n" \ 228 | " }\n" \ 229 | "\n" \ 230 | " if (numTestsFailed == 0) {\n" \ 231 | " return \"PROXY success:80\";\n" \ 232 | " }\n" \ 233 | " return \"PROXY failure:\" + numTestsFailed;\n" \ 234 | "}\n" \ 235 | "\n" \ 236 | "// --------------------------\n" \ 237 | "// Tests\n" \ 238 | "// --------------------------\n" \ 239 | "\n" \ 240 | "var Tests = {};\n" \ 241 | "\n" \ 242 | "Tests.testDnsDomainIs = function(t) {\n" \ 243 | " t.expectTrue(dnsDomainIs(\"google.com\", \".com\"));\n" \ 244 | " t.expectTrue(dnsDomainIs(\"google.co.uk\", \".co.uk\"));\n" \ 245 | " t.expectFalse(dnsDomainIs(\"google.com\", \".co.uk\"));\n" \ 246 | " t.expectFalse(dnsDomainIs(\"www.adobe.com\", \".ad\"));\n" \ 247 | "};\n" \ 248 | "\n" \ 249 | "Tests.testDnsDomainLevels = function(t) {\n" \ 250 | " t.expectEquals(0, dnsDomainLevels(\"www\"));\n" \ 251 | " t.expectEquals(2, dnsDomainLevels(\"www.google.com\"));\n" \ 252 | " t.expectEquals(3, dnsDomainLevels(\"192.168.1.1\"));\n" \ 253 | "};\n" \ 254 | "\n" \ 255 | "Tests.testIsInNet = function(t) {\n" \ 256 | " t.expectTrue(\n" \ 257 | " isInNet(\"192.89.132.25\", \"192.89.132.25\", \"255.255.255.255\"));\n" \ 258 | " t.expectFalse(\n" \ 259 | " isInNet(\"193.89.132.25\", \"192.89.132.25\", \"255.255.255.255\"));\n" \ 260 | "\n" \ 261 | " t.expectTrue(isInNet(\"192.89.132.25\", \"192.89.0.0\", \"255.255.0.0\"));\n" \ 262 | " t.expectFalse(isInNet(\"193.89.132.25\", \"192.89.0.0\", \"255.255.0.0\"));\n" \ 263 | "\n" \ 264 | " t.expectFalse(\n" \ 265 | " isInNet(\"192.89.132.a\", \"192.89.0.0\", \"255.255.0.0\"));\n" \ 266 | "};\n" \ 267 | "\n" \ 268 | "Tests.testIsPlainHostName = function(t) {\n" \ 269 | " t.expectTrue(isPlainHostName(\"google\"));\n" \ 270 | " t.expectFalse(isPlainHostName(\"google.com\"));\n" \ 271 | "};\n" \ 272 | "\n" \ 273 | "Tests.testLocalHostOrDomainIs = function(t) {\n" \ 274 | " t.expectTrue(localHostOrDomainIs(\"www.google.com\", \"www.google.com\"));\n" \ 275 | " t.expectTrue(localHostOrDomainIs(\"www\", \"www.google.com\"));\n" \ 276 | " t.expectFalse(localHostOrDomainIs(\"maps.google.com\", \"www.google.com\"));\n" \ 277 | "};\n" \ 278 | "\n" \ 279 | "Tests.testShExpMatch = function(t) {\n" \ 280 | " t.expectTrue(shExpMatch(\"foo.jpg\", \"*.jpg\"));\n" \ 281 | " t.expectTrue(shExpMatch(\"foo5.jpg\", \"*o?.jpg\"));\n" \ 282 | " t.expectFalse(shExpMatch(\"foo.jpg\", \".jpg\"));\n" \ 283 | " t.expectFalse(shExpMatch(\"foo.jpg\", \"foo\"));\n" \ 284 | "};\n" \ 285 | "\n" \ 286 | "Tests.testSortIpAddressList = function(t) {\n" \ 287 | " t.expectEquals(\"::1;::2;::3\", sortIpAddressList(\"::2;::3;::1\"));\n" \ 288 | " t.expectEquals(\n" \ 289 | " \"2001:4898:28:3:201:2ff:feea:fc14;fe80::5efe:157:9d3b:8b16;157.59.139.22\",\n" \ 290 | " sortIpAddressList(\"157.59.139.22;\" +\n" \ 291 | " \"2001:4898:28:3:201:2ff:feea:fc14;\" +\n" \ 292 | " \"fe80::5efe:157:9d3b:8b16\"));\n" \ 293 | "\n" \ 294 | " // Single IP address (v4 and v6).\n" \ 295 | " t.expectEquals(\"127.0.0.1\", sortIpAddressList(\"127.0.0.1\"));\n" \ 296 | " t.expectEquals(\"::1\", sortIpAddressList(\"::1\"))\n" \ 297 | "\n" \ 298 | " // Verify that IPv6 address is not re-written (not reduced).\n" \ 299 | " t.expectEquals(\"0:0::1;192.168.1.1\", sortIpAddressList(\"192.168.1.1;0:0::1\"));\n" \ 300 | "\n" \ 301 | " // Input is already sorted.\n" \ 302 | " t.expectEquals(\"::1;192.168.1.3\", sortIpAddressList(\"::1;192.168.1.3\"));\n" \ 303 | "\n" \ 304 | " // Same-valued IP addresses (also tests stability).\n" \ 305 | " t.expectEquals(\"0::1;::1;0:0::1\", sortIpAddressList(\"0::1;::1;0:0::1\"));\n" \ 306 | "\n" \ 307 | " // Contains extra semi-colons.\n" \ 308 | " t.expectEquals(\"127.0.0.1\", sortIpAddressList(\";127.0.0.1;\"));\n" \ 309 | "\n" \ 310 | " // Contains whitespace (spaces and tabs).\n" \ 311 | " t.expectEquals(\"192.168.0.1;192.168.0.2\",\n" \ 312 | " sortIpAddressList(\"192.168.0.1; 192.168.0.2\"));\n" \ 313 | " t.expectEquals(\"127.0.0.0;127.0.0.1;127.0.0.2\",\n" \ 314 | " sortIpAddressList(\"127.0.0.1; 127.0.0.2; 127.0.0.0\"));\n" \ 315 | "\n" \ 316 | " // Empty lists.\n" \ 317 | " t.expectFalse(sortIpAddressList(\"\"));\n" \ 318 | " t.expectFalse(sortIpAddressList(\" \"));\n" \ 319 | " t.expectFalse(sortIpAddressList(\";\"));\n" \ 320 | " t.expectFalse(sortIpAddressList(\";;\"));\n" \ 321 | " t.expectFalse(sortIpAddressList(\" ; ; \"));\n" \ 322 | "\n" \ 323 | " // Invalid IP addresses.\n" \ 324 | " t.expectFalse(sortIpAddressList(\"256.0.0.1\"));\n" \ 325 | " t.expectFalse(sortIpAddressList(\"192.168.1.1;0:0:0:1;127.0.0.1\"));\n" \ 326 | "\n" \ 327 | " // Call sortIpAddressList() with wonky arguments.\n" \ 328 | " t.expectEquals(null, sortIpAddressList());\n" \ 329 | " t.expectEquals(null, sortIpAddressList(null));\n" \ 330 | " t.expectEquals(null, sortIpAddressList(null, null));\n" \ 331 | "};\n" \ 332 | "\n" \ 333 | "Tests.testIsInNetEx = function(t) {\n" \ 334 | " t.expectTrue(isInNetEx(\"198.95.249.79\", \"198.95.249.79/32\"));\n" \ 335 | " t.expectTrue(isInNetEx(\"198.95.115.10\", \"198.95.0.0/16\"));\n" \ 336 | " t.expectTrue(isInNetEx(\"198.95.1.1\", \"198.95.0.0/16\"));\n" \ 337 | " t.expectTrue(isInNetEx(\"198.95.1.1\", \"198.95.3.3/16\"));\n" \ 338 | " t.expectTrue(isInNetEx(\"0:0:0:0:0:0:7f00:1\", \"0:0:0:0:0:0:7f00:1/32\"));\n" \ 339 | " t.expectTrue(isInNetEx(\"3ffe:8311:ffff:abcd:1234:dead:beef:101\",\n" \ 340 | " \"3ffe:8311:ffff::/48\"));\n" \ 341 | "\n" \ 342 | " // IPv4 and IPv6 mix.\n" \ 343 | " t.expectFalse(isInNetEx(\"127.0.0.1\", \"0:0:0:0:0:0:7f00:1/16\"));\n" \ 344 | " t.expectFalse(isInNetEx(\"192.168.24.3\", \"fe80:0:0:0:0:0:c0a8:1803/32\"));\n" \ 345 | "\n" \ 346 | " t.expectFalse(isInNetEx(\"198.95.249.78\", \"198.95.249.79/32\"));\n" \ 347 | " t.expectFalse(isInNetEx(\"198.96.115.10\", \"198.95.0.0/16\"));\n" \ 348 | " t.expectFalse(isInNetEx(\"3fff:8311:ffff:abcd:1234:dead:beef:101\",\n" \ 349 | " \"3ffe:8311:ffff::/48\"));\n" \ 350 | "\n" \ 351 | " // Call isInNetEx with wonky arguments.\n" \ 352 | " t.expectEquals(null, isInNetEx());\n" \ 353 | " t.expectEquals(null, isInNetEx(null));\n" \ 354 | " t.expectEquals(null, isInNetEx(null, null));\n" \ 355 | " t.expectEquals(null, isInNetEx(null, null, null));\n" \ 356 | " t.expectEquals(null, isInNetEx(\"198.95.249.79\"));\n" \ 357 | "\n" \ 358 | " // Invalid IP address.\n" \ 359 | " t.expectFalse(isInNetEx(\"256.0.0.1\", \"198.95.249.79\"));\n" \ 360 | " t.expectFalse(isInNetEx(\"127.0.0.1 \", \"127.0.0.1/32\")); // Extra space.\n" \ 361 | "\n" \ 362 | " // Invalid prefix.\n" \ 363 | " t.expectFalse(isInNetEx(\"198.95.115.10\", \"198.95.0.0/34\"));\n" \ 364 | " t.expectFalse(isInNetEx(\"127.0.0.1\", \"127.0.0.1\")); // Missing '/' in prefix.\n" \ 365 | "};\n" \ 366 | "\n" \ 367 | "Tests.testWeekdayRange = function(t) {\n" \ 368 | " // Test with local time.\n" \ 369 | " MockDate.setCurrent(\"Tue Mar 03 2009\");\n" \ 370 | " t.expectEquals(true, weekdayRange(\"MON\", \"FRI\"));\n" \ 371 | " t.expectEquals(true, weekdayRange(\"TUE\", \"FRI\"));\n" \ 372 | " t.expectEquals(true, weekdayRange(\"TUE\", \"TUE\"));\n" \ 373 | " t.expectEquals(true, weekdayRange(\"TUE\"));\n" \ 374 | " t.expectEquals(false, weekdayRange(\"WED\", \"FRI\"));\n" \ 375 | " t.expectEquals(false, weekdayRange(\"SUN\", \"MON\"));\n" \ 376 | " t.expectEquals(false, weekdayRange(\"SAT\"));\n" \ 377 | " t.expectEquals(false, weekdayRange(\"FRI\", \"MON\"));\n" \ 378 | "\n" \ 379 | " // Test with GMT time.\n" \ 380 | " MockDate.setCurrent(\"Tue Mar 03 2009 GMT\");\n" \ 381 | " t.expectEquals(true, weekdayRange(\"MON\", \"FRI\", \"GMT\"));\n" \ 382 | " t.expectEquals(true, weekdayRange(\"TUE\", \"FRI\", \"GMT\"));\n" \ 383 | " t.expectEquals(true, weekdayRange(\"TUE\", \"TUE\", \"GMT\"));\n" \ 384 | " t.expectEquals(true, weekdayRange(\"TUE\", \"GMT\"));\n" \ 385 | " t.expectEquals(false, weekdayRange(\"WED\", \"FRI\", \"GMT\"));\n" \ 386 | " t.expectEquals(false, weekdayRange(\"SUN\", \"MON\", \"GMT\"));\n" \ 387 | " t.expectEquals(false, weekdayRange(\"SAT\", \"GMT\"));\n" \ 388 | "};\n" \ 389 | "\n" \ 390 | "Tests.testDateRange = function(t) {\n" \ 391 | " // dateRange(day)\n" \ 392 | " MockDate.setCurrent(\"Mar 03 2009\");\n" \ 393 | " t.expectEquals(true, dateRange(3));\n" \ 394 | " t.expectEquals(false, dateRange(1));\n" \ 395 | "\n" \ 396 | " // dateRange(day, \"GMT\")\n" \ 397 | " MockDate.setCurrent(\"Mar 03 2009 GMT\");\n" \ 398 | " t.expectEquals(true, dateRange(3, \"GMT\"));\n" \ 399 | " t.expectEquals(false, dateRange(1, \"GMT\"));\n" \ 400 | "\n" \ 401 | " // dateRange(day1, day2)\n" \ 402 | " MockDate.setCurrent(\"Mar 03 2009\");\n" \ 403 | " t.expectEquals(true, dateRange(1, 4));\n" \ 404 | " t.expectEquals(false, dateRange(4, 20));\n" \ 405 | "\n" \ 406 | " // dateRange(day, month)\n" \ 407 | " MockDate.setCurrent(\"Mar 03 2009\");\n" \ 408 | " t.expectEquals(true, dateRange(3, \"MAR\"));\n" \ 409 | " MockDate.setCurrent(\"Mar 03 2014\");\n" \ 410 | " t.expectEquals(true, dateRange(3, \"MAR\"));\n" \ 411 | " // TODO(eroman):\n" \ 412 | " //t.expectEquals(false, dateRange(2, \"MAR\"));\n" \ 413 | " //t.expectEquals(false, dateRange(3, \"JAN\"));\n" \ 414 | "\n" \ 415 | " // dateRange(day, month, year)\n" \ 416 | " MockDate.setCurrent(\"Mar 03 2009\");\n" \ 417 | " t.expectEquals(true, dateRange(3, \"MAR\", 2009));\n" \ 418 | " t.expectEquals(false, dateRange(4, \"MAR\", 2009));\n" \ 419 | " t.expectEquals(false, dateRange(3, \"FEB\", 2009));\n" \ 420 | " MockDate.setCurrent(\"Mar 03 2014\");\n" \ 421 | " t.expectEquals(false, dateRange(3, \"MAR\", 2009));\n" \ 422 | "\n" \ 423 | " // dateRange(month1, month2)\n" \ 424 | " MockDate.setCurrent(\"Mar 03 2009\");\n" \ 425 | " t.expectEquals(true, dateRange(\"JAN\", \"MAR\"));\n" \ 426 | " t.expectEquals(true, dateRange(\"MAR\", \"APR\"));\n" \ 427 | " t.expectEquals(false, dateRange(\"MAY\", \"SEP\"));\n" \ 428 | "\n" \ 429 | " // dateRange(day1, month1, day2, month2)\n" \ 430 | " MockDate.setCurrent(\"Mar 03 2009\");\n" \ 431 | " t.expectEquals(true, dateRange(1, \"JAN\", 3, \"MAR\"));\n" \ 432 | " t.expectEquals(true, dateRange(3, \"MAR\", 4, \"SEP\"));\n" \ 433 | " t.expectEquals(false, dateRange(4, \"MAR\", 4, \"SEP\"));\n" \ 434 | "\n" \ 435 | " // dateRange(month1, year1, month2, year2)\n" \ 436 | " MockDate.setCurrent(\"Mar 03 2009\");\n" \ 437 | " t.expectEquals(true, dateRange(\"FEB\", 2009, \"MAR\", 2009));\n" \ 438 | " MockDate.setCurrent(\"Apr 03 2009\");\n" \ 439 | " t.expectEquals(true, dateRange(\"FEB\", 2009, \"MAR\", 2010));\n" \ 440 | " t.expectEquals(false, dateRange(\"FEB\", 2009, \"MAR\", 2009));\n" \ 441 | "\n" \ 442 | " // dateRange(day1, month1, year1, day2, month2, year2)\n" \ 443 | " MockDate.setCurrent(\"Mar 03 2009\");\n" \ 444 | " t.expectEquals(true, dateRange(1, \"JAN\", 2009, 3, \"MAR\", 2009));\n" \ 445 | " t.expectEquals(true, dateRange(3, \"MAR\", 2009, 4, \"SEP\", 2009));\n" \ 446 | " t.expectEquals(true, dateRange(3, \"JAN\", 2009, 4, \"FEB\", 2010));\n" \ 447 | " t.expectEquals(false, dateRange(4, \"MAR\", 2009, 4, \"SEP\", 2009));\n" \ 448 | "};\n" \ 449 | "\n" \ 450 | "Tests.testTimeRange = function(t) {\n" \ 451 | " // timeRange(hour)\n" \ 452 | " MockDate.setCurrent(\"Mar 03, 2009 03:34:01\");\n" \ 453 | " t.expectEquals(true, timeRange(3));\n" \ 454 | " t.expectEquals(false, timeRange(2));\n" \ 455 | "\n" \ 456 | " // timeRange(hour1, hour2)\n" \ 457 | " MockDate.setCurrent(\"Mar 03, 2009 03:34:01\");\n" \ 458 | " t.expectEquals(true, timeRange(2, 3));\n" \ 459 | " t.expectEquals(true, timeRange(2, 4));\n" \ 460 | " t.expectEquals(true, timeRange(3, 5));\n" \ 461 | " t.expectEquals(false, timeRange(1, 2));\n" \ 462 | " t.expectEquals(false, timeRange(11, 12));\n" \ 463 | "\n" \ 464 | " // timeRange(hour1, min1, hour2, min2)\n" \ 465 | " MockDate.setCurrent(\"Mar 03, 2009 03:34:01\");\n" \ 466 | " t.expectEquals(true, timeRange(1, 0, 3, 34));\n" \ 467 | " t.expectEquals(true, timeRange(1, 0, 3, 35));\n" \ 468 | " t.expectEquals(true, timeRange(3, 34, 5, 0));\n" \ 469 | " t.expectEquals(false, timeRange(1, 0, 3, 0));\n" \ 470 | " t.expectEquals(false, timeRange(11, 0, 16, 0));\n" \ 471 | "\n" \ 472 | " // timeRange(hour1, min1, sec1, hour2, min2, sec2)\n" \ 473 | " MockDate.setCurrent(\"Mar 03, 2009 03:34:14\");\n" \ 474 | " t.expectEquals(true, timeRange(1, 0, 0, 3, 34, 14));\n" \ 475 | " t.expectEquals(false, timeRange(1, 0, 0, 3, 34, 0));\n" \ 476 | " t.expectEquals(true, timeRange(1, 0, 0, 3, 35, 0));\n" \ 477 | " t.expectEquals(true, timeRange(3, 34, 0, 5, 0, 0));\n" \ 478 | " t.expectEquals(false, timeRange(1, 0, 0, 3, 0, 0));\n" \ 479 | " t.expectEquals(false, timeRange(11, 0, 0, 16, 0, 0));\n" \ 480 | "};\n" \ 481 | "\n" \ 482 | "// --------------------------\n" \ 483 | "// TestContext\n" \ 484 | "// --------------------------\n" \ 485 | "\n" \ 486 | "// |name| is the name of the test being executed, it will be used when logging\n" \ 487 | "// errors.\n" \ 488 | "function TestContext(name) {\n" \ 489 | " this.numFailures_ = 0;\n" \ 490 | " this.name_ = name;\n" \ 491 | "};\n" \ 492 | "\n" \ 493 | "TestContext.prototype.failed = function() {\n" \ 494 | " return this.numFailures_ != 0;\n" \ 495 | "};\n" \ 496 | "\n" \ 497 | "TestContext.prototype.expectEquals = function(expectation, actual) {\n" \ 498 | " if (!(expectation === actual)) {\n" \ 499 | " this.numFailures_++;\n" \ 500 | " this.log(\"FAIL: expected: \" + expectation + \", actual: \" + actual);\n" \ 501 | " }\n" \ 502 | "};\n" \ 503 | "\n" \ 504 | "TestContext.prototype.expectTrue = function(x) {\n" \ 505 | " this.expectEquals(true, x);\n" \ 506 | "};\n" \ 507 | "\n" \ 508 | "TestContext.prototype.expectFalse = function(x) {\n" \ 509 | " this.expectEquals(false, x);\n" \ 510 | "};\n" \ 511 | "\n" \ 512 | "TestContext.prototype.log = function(x) {\n" \ 513 | " // Prefix with the test name that generated the log.\n" \ 514 | " try {\n" \ 515 | " alert(this.name_ + \": \" + x);\n" \ 516 | " } catch(e) {\n" \ 517 | " // In case alert() is not defined.\n" \ 518 | " }\n" \ 519 | "};\n" \ 520 | "\n" \ 521 | "// --------------------------\n" \ 522 | "// MockDate\n" \ 523 | "// --------------------------\n" \ 524 | "\n" \ 525 | "function MockDate() {\n" \ 526 | " this.wrappedDate_ = new MockDate.super_(MockDate.currentDateString_);\n" \ 527 | "};\n" \ 528 | "\n" \ 529 | "// Setup the MockDate so it forwards methods to \"this.wrappedDate_\" (which is a\n" \ 530 | "// real Date object). We can't simply chain the prototypes since Date() doesn't\n" \ 531 | "// allow it.\n" \ 532 | "MockDate.init = function() {\n" \ 533 | " MockDate.super_ = Date;\n" \ 534 | "\n" \ 535 | " function createProxyMethod(methodName) {\n" \ 536 | " return function() {\n" \ 537 | " return this.wrappedDate_[methodName]\n" \ 538 | " .apply(this.wrappedDate_, arguments);\n" \ 539 | " }\n" \ 540 | " };\n" \ 541 | "\n" \ 542 | " for (i in MockDate.methodNames_) {\n" \ 543 | " var methodName = MockDate.methodNames_[i];\n" \ 544 | " // Don't define the closure directly in the loop body, since Javascript's\n" \ 545 | " // crazy scoping rules mean |methodName| actually bleeds out of the loop!\n" \ 546 | " MockDate.prototype[methodName] = createProxyMethod(methodName);\n" \ 547 | " }\n" \ 548 | "\n" \ 549 | " // Replace the native Date() with our mock.\n" \ 550 | " Date = MockDate;\n" \ 551 | "};\n" \ 552 | "\n" \ 553 | "// Unfortunately Date()'s methods are non-enumerable, therefore list manually.\n" \ 554 | "MockDate.methodNames_ = [\n" \ 555 | " \"toString\", \"toDateString\", \"toTimeString\", \"toLocaleString\",\n" \ 556 | " \"toLocaleDateString\", \"toLocaleTimeString\", \"valueOf\", \"getTime\",\n" \ 557 | " \"getFullYear\", \"getUTCFullYear\", \"getMonth\", \"getUTCMonth\",\n" \ 558 | " \"getDate\", \"getUTCDate\", \"getDay\", \"getUTCDay\", \"getHours\", \"getUTCHours\",\n" \ 559 | " \"getMinutes\", \"getUTCMinutes\", \"getSeconds\", \"getUTCSeconds\",\n" \ 560 | " \"getMilliseconds\", \"getUTCMilliseconds\", \"getTimezoneOffset\", \"setTime\",\n" \ 561 | " \"setMilliseconds\", \"setUTCMilliseconds\", \"setSeconds\", \"setUTCSeconds\",\n" \ 562 | " \"setMinutes\", \"setUTCMinutes\", \"setHours\", \"setUTCHours\", \"setDate\",\n" \ 563 | " \"setUTCDate\", \"setMonth\", \"setUTCMonth\", \"setFullYear\", \"setUTCFullYear\",\n" \ 564 | " \"toGMTString\", \"toUTCString\", \"getYear\", \"setYear\"\n" \ 565 | "];\n" \ 566 | "\n" \ 567 | "MockDate.setCurrent = function(currentDateString) {\n" \ 568 | " MockDate.currentDateString_ = currentDateString;\n" \ 569 | "}\n" \ 570 | "\n" \ 571 | "// Bind the methods to proxy requests to the wrapped Date().\n" \ 572 | "MockDate.init();\n" \ 573 | "\n" \ 574 | 575 | #define PASSTHROUGH_JS \ 576 | "// Return a single-proxy result, which encodes ALL the arguments that were\n" \ 577 | "// passed to FindProxyForURL().\n" \ 578 | "\n" \ 579 | "function FindProxyForURL(url, host) {\n" \ 580 | " if (arguments.length != 2) {\n" \ 581 | " throw \"Wrong number of arguments passed to FindProxyForURL!\";\n" \ 582 | " return \"FAIL\";\n" \ 583 | " }\n" \ 584 | "\n" \ 585 | " return \"PROXY \" + makePseudoHost(url + \".\" + host);\n" \ 586 | "}\n" \ 587 | "\n" \ 588 | "// Form a string that kind-of resembles a host. We will replace any\n" \ 589 | "// non-alphanumeric character with a dot, then fix up the oddly placed dots.\n" \ 590 | "function makePseudoHost(str) {\n" \ 591 | " var result = \"\";\n" \ 592 | "\n" \ 593 | " for (var i = 0; i < str.length; ++i) {\n" \ 594 | " var c = str.charAt(i);\n" \ 595 | " if (!isValidPseudoHostChar(c)) {\n" \ 596 | " c = '.'; // Replace unsupported characters with a dot.\n" \ 597 | " }\n" \ 598 | "\n" \ 599 | " // Take care not to place multiple adjacent dots,\n" \ 600 | " // a dot at the beginning, or a dot at the end.\n" \ 601 | " if (c == '.' &&\n" \ 602 | " (result.length == 0 || \n" \ 603 | " i == str.length - 1 ||\n" \ 604 | " result.charAt(result.length - 1) == '.')) {\n" \ 605 | " continue;\n" \ 606 | " }\n" \ 607 | " result += c;\n" \ 608 | " }\n" \ 609 | " return result;\n" \ 610 | "}\n" \ 611 | "\n" \ 612 | "function isValidPseudoHostChar(c) {\n" \ 613 | " if (c >= '0' && c <= '9')\n" \ 614 | " return true;\n" \ 615 | " if (c >= 'a' && c <= 'z')\n" \ 616 | " return true;\n" \ 617 | " if (c >= 'A' && c <= 'Z')\n" \ 618 | " return true;\n" \ 619 | " return false;\n" \ 620 | "}\n" \ 621 | 622 | #define RETURN_EMPTY_STRING_JS \ 623 | "function FindProxyForURL(url, host) {\n" \ 624 | " return \"\";\n" \ 625 | "}\n" \ 626 | "\n" \ 627 | 628 | #define RETURN_FUNCTION_JS \ 629 | "function FindProxyForURL(url, host) {\n" \ 630 | " return FindProxyForURL;\n" \ 631 | "}\n" \ 632 | "\n" \ 633 | 634 | #define RETURN_INTEGER_JS \ 635 | "function FindProxyForURL(url, host) {\n" \ 636 | " return 0;\n" \ 637 | "}\n" \ 638 | "\n" \ 639 | 640 | #define RETURN_NULL_JS \ 641 | "function FindProxyForURL(url, host) {\n" \ 642 | " return null;\n" \ 643 | "}\n" \ 644 | "\n" \ 645 | 646 | #define RETURN_OBJECT_JS \ 647 | "function FindProxyForURL(url, host) {\n" \ 648 | " return {result: \"PROXY foo\"};\n" \ 649 | "}\n" \ 650 | "\n" \ 651 | 652 | #define RETURN_UNDEFINED_JS \ 653 | "function FindProxyForURL(url, host) {\n" \ 654 | " return undefined;\n" \ 655 | "}\n" \ 656 | "\n" \ 657 | 658 | #define RETURN_UNICODE_JS \ 659 | "// U+200B is the codepoint for zero-width-space.\n" \ 660 | "function FindProxyForURL(url, host) {\n" \ 661 | " return \"PROXY foo.com\u200B\";\n" \ 662 | "}\n" \ 663 | 664 | #define SIDE_EFFECTS_JS \ 665 | "if (!gCounter) {\n" \ 666 | " // We write it this way so if the script gets loaded twice,\n" \ 667 | " // gCounter remains dirty.\n" \ 668 | " var gCounter = 0;\n" \ 669 | "}\n" \ 670 | "\n" \ 671 | "function FindProxyForURL(url, host) {\n" \ 672 | " return \"PROXY sideffect_\" + gCounter++;\n" \ 673 | "}\n" \ 674 | "\n" \ 675 | 676 | #define SIMPLE_JS \ 677 | "// PAC script which uses isInNet on both IP addresses and hosts, and calls\n" \ 678 | "// isResolvable().\n" \ 679 | "\n" \ 680 | "function FindProxyForURL(url, host) {\n" \ 681 | " var my_ip = myIpAddress();\n" \ 682 | "\n" \ 683 | " if (isInNet(my_ip, \"172.16.0.0\", \"255.248.0.0\")) {\n" \ 684 | " return \"PROXY a:80\";\n" \ 685 | " }\n" \ 686 | "\n" \ 687 | " if (url.substring(0, 6) != \"https:\" &&\n" \ 688 | " isInNet(host, \"10.0.0.0\", \"255.0.0.0\")) {\n" \ 689 | " return \"PROXY b:80\";\n" \ 690 | " }\n" \ 691 | "\n" \ 692 | " if (dnsDomainIs(host, \"foo.bar.baz.com\") || !isResolvable(host)) {\n" \ 693 | " return \"PROXY c:100\";\n" \ 694 | " }\n" \ 695 | "\n" \ 696 | " return \"DIRECT\";\n" \ 697 | "}\n" \ 698 | 699 | #define UNHANDLED_EXCEPTION_JS \ 700 | "function FindProxyForURL(url, host) {\n" \ 701 | " // This will throw a runtime exception.\n" \ 702 | " return \"PROXY x\" + undefined_variable;\n" \ 703 | "}\n" \ 704 | "\n" \ 705 | 706 | #endif //PROXY_TEST_SCRIPT_H_ 707 | -------------------------------------------------------------------------------- /src/proxy_resolver_v8.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "proxy_resolver_v8.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "net_util.h" 17 | #include "proxy_resolver_script.h" 18 | 19 | // Notes on the javascript environment: 20 | // 21 | // For the majority of the PAC utility functions, we use the same code 22 | // as Firefox. See the javascript library that proxy_resolver_scipt.h 23 | // pulls in. 24 | // 25 | // In addition, we implement a subset of Microsoft's extensions to PAC. 26 | // - myIpAddressEx() 27 | // - dnsResolveEx() 28 | // - isResolvableEx() 29 | // - isInNetEx() 30 | // - sortIpAddressList() 31 | // 32 | // It is worth noting that the original PAC specification does not describe 33 | // the return values on failure. Consequently, there are compatibility 34 | // differences between browsers on what to return on failure, which are 35 | // illustrated below: 36 | // 37 | // --------------------+-------------+-------------------+-------------- 38 | // | Firefox3 | InternetExplorer8 | --> Us <--- 39 | // --------------------+-------------+-------------------+-------------- 40 | // myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1" 41 | // dnsResolve() | null | false | null 42 | // myIpAddressEx() | N/A | "" | "" 43 | // sortIpAddressList() | N/A | false | false 44 | // dnsResolveEx() | N/A | "" | "" 45 | // isInNetEx() | N/A | false | false 46 | // --------------------+-------------+-------------------+-------------- 47 | // 48 | // TODO: The cell above reading ??? means I didn't test it. 49 | // 50 | // Another difference is in how dnsResolve() and myIpAddress() are 51 | // implemented -- whether they should restrict to IPv4 results, or 52 | // include both IPv4 and IPv6. The following table illustrates the 53 | // differences: 54 | // 55 | // --------------------+-------------+-------------------+-------------- 56 | // | Firefox3 | InternetExplorer8 | --> Us <--- 57 | // --------------------+-------------+-------------------+-------------- 58 | // myIpAddress() | IPv4/IPv6 | IPv4 | IPv4 59 | // dnsResolve() | IPv4/IPv6 | IPv4 | IPv4 60 | // isResolvable() | IPv4/IPv6 | IPv4 | IPv4 61 | // myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6 62 | // dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6 63 | // sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6 64 | // isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6 65 | // isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6 66 | // -----------------+-------------+-------------------+-------------- 67 | 68 | static bool DoIsStringASCII(const android::String16& str) { 69 | for (size_t i = 0; i < str.size(); i++) { 70 | unsigned short c = str.string()[i]; 71 | if (c > 0x7F) 72 | return false; 73 | } 74 | return true; 75 | } 76 | 77 | bool IsStringASCII(const android::String16& str) { 78 | return DoIsStringASCII(str); 79 | } 80 | 81 | namespace net { 82 | 83 | namespace { 84 | 85 | // Pseudo-name for the PAC script. 86 | const char kPacResourceName[] = "proxy-pac-script.js"; 87 | // Pseudo-name for the PAC utility script. 88 | const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js"; 89 | 90 | // External string wrapper so V8 can access the UTF16 string wrapped by 91 | // ProxyResolverScriptData. 92 | class V8ExternalStringFromScriptData 93 | : public v8::String::ExternalStringResource { 94 | public: 95 | explicit V8ExternalStringFromScriptData( 96 | const android::String16& script_data) 97 | : script_data_(script_data) {} 98 | 99 | virtual const uint16_t* data() const { 100 | return reinterpret_cast(script_data_.string()); 101 | } 102 | 103 | virtual size_t length() const { 104 | return script_data_.size(); 105 | } 106 | 107 | private: 108 | const android::String16& script_data_; 109 | // DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData); 110 | }; 111 | 112 | // External string wrapper so V8 can access a string literal. 113 | class V8ExternalASCIILiteral 114 | : public v8::String::ExternalOneByteStringResource { 115 | public: 116 | // |ascii| must be a NULL-terminated C string, and must remain valid 117 | // throughout this object's lifetime. 118 | V8ExternalASCIILiteral(const char* ascii, size_t length) 119 | : ascii_(ascii), length_(length) { 120 | } 121 | 122 | virtual const char* data() const { 123 | return ascii_; 124 | } 125 | 126 | virtual size_t length() const { 127 | return length_; 128 | } 129 | 130 | private: 131 | const char* ascii_; 132 | size_t length_; 133 | }; 134 | 135 | // When creating a v8::String from a C++ string we have two choices: create 136 | // a copy, or create a wrapper that shares the same underlying storage. 137 | // For small strings it is better to just make a copy, whereas for large 138 | // strings there are savings by sharing the storage. This number identifies 139 | // the cutoff length for when to start wrapping rather than creating copies. 140 | const size_t kMaxStringBytesForCopy = 256; 141 | 142 | template 143 | inline typename string_type::value_type* WriteInto(string_type* str, 144 | size_t length_with_null) { 145 | str->reserve(length_with_null); 146 | str->resize(length_with_null - 1); 147 | return &((*str)[0]); 148 | } 149 | 150 | // Converts a V8 String to a UTF8 std::string. 151 | std::string V8StringToUTF8(v8::Handle s) { 152 | std::string result; 153 | s->WriteUtf8(WriteInto(&result, s->Length() + 1)); 154 | return result; 155 | } 156 | 157 | // Converts a V8 String to a UTF16 string. 158 | android::String16 V8StringToUTF16(v8::Handle s) { 159 | int len = s->Length(); 160 | char16_t* buf = new char16_t[len + 1]; 161 | s->Write(reinterpret_cast(buf), 0, len); 162 | android::String16 ret(buf, len); 163 | delete[] buf; 164 | return ret; 165 | } 166 | 167 | std::string UTF16ToASCII(const android::String16& str) { 168 | android::String8 rstr(str); 169 | return std::string(rstr.string()); 170 | } 171 | 172 | // Converts an ASCII std::string to a V8 string. 173 | v8::Local ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) { 174 | return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size()); 175 | } 176 | 177 | v8::Local UTF16StringToV8String(v8::Isolate* isolate, const android::String16& s) { 178 | return v8::String::NewFromTwoByte( 179 | isolate, reinterpret_cast(s.string()), 180 | v8::String::kNormalString, s.size()); 181 | } 182 | 183 | // Converts an ASCII string literal to a V8 string. 184 | v8::Local ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) { 185 | // DCHECK(IsStringASCII(ascii)); 186 | size_t length = strlen(ascii); 187 | if (length <= kMaxStringBytesForCopy) 188 | return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length); 189 | return v8::String::NewExternal(isolate, new V8ExternalASCIILiteral(ascii, length)); 190 | } 191 | 192 | // Stringizes a V8 object by calling its toString() method. Returns true 193 | // on success. This may fail if the toString() throws an exception. 194 | bool V8ObjectToUTF16String(v8::Handle object, 195 | android::String16* utf16_result, 196 | v8::Isolate* isolate) { 197 | if (object.IsEmpty()) 198 | return false; 199 | 200 | v8::HandleScope scope(isolate); 201 | v8::Local str_object = object->ToString(); 202 | if (str_object.IsEmpty()) 203 | return false; 204 | *utf16_result = V8StringToUTF16(str_object); 205 | return true; 206 | } 207 | 208 | // Extracts an hostname argument from |args|. On success returns true 209 | // and fills |*hostname| with the result. 210 | bool GetHostnameArgument(const v8::FunctionCallbackInfo& args, std::string* hostname) { 211 | // The first argument should be a string. 212 | if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) 213 | return false; 214 | 215 | const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString()); 216 | 217 | // If the hostname is already in ASCII, simply return it as is. 218 | if (IsStringASCII(hostname_utf16)) { 219 | *hostname = UTF16ToASCII(hostname_utf16); 220 | return true; 221 | } 222 | return false; 223 | } 224 | 225 | // Wrapper for passing around IP address strings and IPAddressNumber objects. 226 | struct IPAddress { 227 | IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number) 228 | : string_value(ip_string), 229 | ip_address_number(ip_number) { 230 | } 231 | 232 | // Used for sorting IP addresses in ascending order in SortIpAddressList(). 233 | // IP6 addresses are placed ahead of IPv4 addresses. 234 | bool operator<(const IPAddress& rhs) const { 235 | const IPAddressNumber& ip1 = this->ip_address_number; 236 | const IPAddressNumber& ip2 = rhs.ip_address_number; 237 | if (ip1.size() != ip2.size()) 238 | return ip1.size() > ip2.size(); // IPv6 before IPv4. 239 | return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order. 240 | } 241 | 242 | std::string string_value; 243 | IPAddressNumber ip_address_number; 244 | }; 245 | 246 | template 247 | bool RemoveCharsT(const STR& input, 248 | const typename STR::value_type remove_chars[], 249 | STR* output) { 250 | bool removed = false; 251 | size_t found; 252 | 253 | *output = input; 254 | 255 | found = output->find_first_of(remove_chars); 256 | while (found != STR::npos) { 257 | removed = true; 258 | output->replace(found, 1, STR()); 259 | found = output->find_first_of(remove_chars, found); 260 | } 261 | 262 | return removed; 263 | } 264 | 265 | bool RemoveChars(const std::string& input, 266 | const char remove_chars[], 267 | std::string* output) { 268 | return RemoveCharsT(input, remove_chars, output); 269 | } 270 | 271 | // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a 272 | // semi-colon delimited string containing IP addresses. 273 | // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited 274 | // IP addresses or an empty string if unable to sort the IP address list. 275 | // Returns 'true' if the sorting was successful, and 'false' if the input was an 276 | // empty string, a string of separators (";" in this case), or if any of the IP 277 | // addresses in the input list failed to parse. 278 | bool SortIpAddressList(const std::string& ip_address_list, 279 | std::string* sorted_ip_address_list) { 280 | sorted_ip_address_list->clear(); 281 | 282 | // Strip all whitespace (mimics IE behavior). 283 | std::string cleaned_ip_address_list; 284 | RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list); 285 | if (cleaned_ip_address_list.empty()) 286 | return false; 287 | 288 | // Split-up IP addresses and store them in a vector. 289 | std::vector ip_vector; 290 | IPAddressNumber ip_num; 291 | char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";"); 292 | while (tok_list != NULL) { 293 | if (!ParseIPLiteralToNumber(tok_list, &ip_num)) 294 | return false; 295 | ip_vector.push_back(IPAddress(tok_list, ip_num)); 296 | tok_list = strtok(NULL, ";"); 297 | } 298 | 299 | if (ip_vector.empty()) // Can happen if we have something like 300 | return false; // sortIpAddressList(";") or sortIpAddressList("; ;") 301 | 302 | // Sort lists according to ascending numeric value. 303 | if (ip_vector.size() > 1) 304 | std::stable_sort(ip_vector.begin(), ip_vector.end()); 305 | 306 | // Return a semi-colon delimited list of sorted addresses (IPv6 followed by 307 | // IPv4). 308 | for (size_t i = 0; i < ip_vector.size(); ++i) { 309 | if (i > 0) 310 | *sorted_ip_address_list += ";"; 311 | *sorted_ip_address_list += ip_vector[i].string_value; 312 | } 313 | return true; 314 | } 315 | 316 | 317 | // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string 318 | // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a 319 | // slash-delimited IP prefix with the top 'n' bits specified in the bit 320 | // field. This returns 'true' if the address is in the same subnet, and 321 | // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect 322 | // format, or if an address and prefix of different types are used (e.g. IPv6 323 | // address and IPv4 prefix). 324 | bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) { 325 | IPAddressNumber address; 326 | std::string cleaned_ip_address; 327 | if (RemoveChars(ip_address, " \t", &cleaned_ip_address)) 328 | return false; 329 | if (!ParseIPLiteralToNumber(ip_address, &address)) 330 | return false; 331 | 332 | IPAddressNumber prefix; 333 | size_t prefix_length_in_bits; 334 | if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits)) 335 | return false; 336 | 337 | // Both |address| and |prefix| must be of the same type (IPv4 or IPv6). 338 | if (address.size() != prefix.size()) 339 | return false; 340 | 341 | return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits); 342 | } 343 | 344 | } // namespace 345 | 346 | class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { 347 | public: 348 | virtual void* Allocate(size_t length) { 349 | void* data = AllocateUninitialized(length); 350 | return data == NULL ? data : memset(data, 0, length); 351 | } 352 | virtual void* AllocateUninitialized(size_t length) { return malloc(length); } 353 | virtual void Free(void* data, size_t) { free(data); } 354 | }; 355 | 356 | 357 | // ProxyResolverV8::Context --------------------------------------------------- 358 | 359 | class ProxyResolverV8::Context { 360 | public: 361 | explicit Context(ProxyResolverJSBindings* js_bindings, 362 | ProxyErrorListener* error_listener, v8::Isolate* isolate) 363 | : js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) { 364 | } 365 | 366 | ~Context() { 367 | v8::Locker locked(isolate_); 368 | v8::Isolate::Scope isolate_scope(isolate_); 369 | 370 | v8_this_.Reset(); 371 | v8_context_.Reset(); 372 | } 373 | 374 | int ResolveProxy(const android::String16 url, const android::String16 host, 375 | android::String16* results) { 376 | v8::Locker locked(isolate_); 377 | v8::Isolate::Scope isolate_scope(isolate_); 378 | v8::HandleScope scope(isolate_); 379 | 380 | v8::Local context = 381 | v8::Local::New(isolate_, v8_context_); 382 | v8::Context::Scope function_scope(context); 383 | 384 | v8::Local function; 385 | if (!GetFindProxyForURL(&function)) { 386 | error_listener_->ErrorMessage( 387 | android::String16("FindProxyForURL() is undefined")); 388 | return ERR_PAC_SCRIPT_FAILED; 389 | } 390 | 391 | v8::Handle argv[] = { 392 | UTF16StringToV8String(isolate_, url), 393 | UTF16StringToV8String(isolate_, host) }; 394 | 395 | v8::TryCatch try_catch; 396 | v8::Local ret = v8::Function::Cast(*function)->Call( 397 | context->Global(), 2, argv); 398 | 399 | if (try_catch.HasCaught()) { 400 | error_listener_->ErrorMessage( 401 | V8StringToUTF16(try_catch.Message()->Get())); 402 | return ERR_PAC_SCRIPT_FAILED; 403 | } 404 | 405 | if (!ret->IsString()) { 406 | error_listener_->ErrorMessage( 407 | android::String16("FindProxyForURL() did not return a string.")); 408 | return ERR_PAC_SCRIPT_FAILED; 409 | } 410 | 411 | *results = V8StringToUTF16(ret->ToString()); 412 | 413 | if (!IsStringASCII(*results)) { 414 | // TODO: Rather than failing when a wide string is returned, we 415 | // could extend the parsing to handle IDNA hostnames by 416 | // converting them to ASCII punycode. 417 | // crbug.com/47234 418 | error_listener_->ErrorMessage( 419 | android::String16("FindProxyForURL() returned a non-ASCII string")); 420 | return ERR_PAC_SCRIPT_FAILED; 421 | } 422 | 423 | return OK; 424 | } 425 | 426 | int InitV8(const android::String16& pac_script) { 427 | v8::Locker locked(isolate_); 428 | v8::Isolate::Scope isolate_scope(isolate_); 429 | v8::HandleScope scope(isolate_); 430 | 431 | v8_this_.Reset(isolate_, v8::External::New(isolate_, this)); 432 | v8::Local v8_this = 433 | v8::Local::New(isolate_, v8_this_); 434 | v8::Local global_template = v8::ObjectTemplate::New(); 435 | 436 | // Attach the javascript bindings. 437 | v8::Local alert_template = 438 | v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this); 439 | global_template->Set(ASCIILiteralToV8String(isolate_, "alert"), alert_template); 440 | 441 | v8::Local my_ip_address_template = 442 | v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this); 443 | global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"), 444 | my_ip_address_template); 445 | 446 | v8::Local dns_resolve_template = 447 | v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this); 448 | global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"), 449 | dns_resolve_template); 450 | 451 | // Microsoft's PAC extensions: 452 | 453 | v8::Local dns_resolve_ex_template = 454 | v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this); 455 | global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"), 456 | dns_resolve_ex_template); 457 | 458 | v8::Local my_ip_address_ex_template = 459 | v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this); 460 | global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"), 461 | my_ip_address_ex_template); 462 | 463 | v8::Local sort_ip_address_list_template = 464 | v8::FunctionTemplate::New(isolate_, &SortIpAddressListCallback, v8_this); 465 | global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"), 466 | sort_ip_address_list_template); 467 | 468 | v8::Local is_in_net_ex_template = 469 | v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this); 470 | global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"), 471 | is_in_net_ex_template); 472 | 473 | v8_context_.Reset( 474 | isolate_, v8::Context::New(isolate_, NULL, global_template)); 475 | 476 | v8::Local context = 477 | v8::Local::New(isolate_, v8_context_); 478 | v8::Context::Scope ctx(context); 479 | 480 | // Add the PAC utility functions to the environment. 481 | // (This script should never fail, as it is a string literal!) 482 | // Note that the two string literals are concatenated. 483 | int rv = RunScript( 484 | ASCIILiteralToV8String(isolate_, 485 | PROXY_RESOLVER_SCRIPT 486 | PROXY_RESOLVER_SCRIPT_EX), 487 | kPacUtilityResourceName); 488 | if (rv != OK) { 489 | return rv; 490 | } 491 | 492 | // Add the user's PAC code to the environment. 493 | rv = RunScript(UTF16StringToV8String(isolate_, pac_script), kPacResourceName); 494 | if (rv != OK) { 495 | return rv; 496 | } 497 | 498 | // At a minimum, the FindProxyForURL() function must be defined for this 499 | // to be a legitimiate PAC script. 500 | v8::Local function; 501 | if (!GetFindProxyForURL(&function)) 502 | return ERR_PAC_SCRIPT_FAILED; 503 | 504 | return OK; 505 | } 506 | 507 | void PurgeMemory() { 508 | v8::Locker locked(isolate_); 509 | v8::Isolate::Scope isolate_scope(isolate_); 510 | isolate_->LowMemoryNotification(); 511 | } 512 | 513 | private: 514 | bool GetFindProxyForURL(v8::Local* function) { 515 | v8::Local context = 516 | v8::Local::New(isolate_, v8_context_); 517 | *function = context->Global()->Get( 518 | ASCIILiteralToV8String(isolate_, "FindProxyForURL")); 519 | return (*function)->IsFunction(); 520 | } 521 | 522 | // Handle an exception thrown by V8. 523 | void HandleError(v8::Handle message) { 524 | if (message.IsEmpty()) 525 | return; 526 | error_listener_->ErrorMessage(V8StringToUTF16(message->Get())); 527 | } 528 | 529 | // Compiles and runs |script| in the current V8 context. 530 | // Returns OK on success, otherwise an error code. 531 | int RunScript(v8::Handle script, const char* script_name) { 532 | v8::TryCatch try_catch; 533 | 534 | // Compile the script. 535 | v8::ScriptOrigin origin = 536 | v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name)); 537 | v8::Local code = v8::Script::Compile(script, &origin); 538 | 539 | // Execute. 540 | if (!code.IsEmpty()) 541 | code->Run(); 542 | 543 | // Check for errors. 544 | if (try_catch.HasCaught()) { 545 | HandleError(try_catch.Message()); 546 | return ERR_PAC_SCRIPT_FAILED; 547 | } 548 | 549 | return OK; 550 | } 551 | 552 | // V8 callback for when "alert()" is invoked by the PAC script. 553 | static void AlertCallback(const v8::FunctionCallbackInfo& args) { 554 | Context* context = 555 | static_cast(v8::External::Cast(*args.Data())->Value()); 556 | 557 | // Like firefox we assume "undefined" if no argument was specified, and 558 | // disregard any arguments beyond the first. 559 | android::String16 message; 560 | if (args.Length() == 0) { 561 | std::string undef = "undefined"; 562 | android::String8 undef8(undef.c_str()); 563 | android::String16 wundef(undef8); 564 | message = wundef; 565 | } else { 566 | if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate())) 567 | return; // toString() threw an exception. 568 | } 569 | 570 | context->error_listener_->AlertMessage(message); 571 | return; 572 | } 573 | 574 | // V8 callback for when "myIpAddress()" is invoked by the PAC script. 575 | static void MyIpAddressCallback( 576 | const v8::FunctionCallbackInfo& args) { 577 | Context* context = 578 | static_cast(v8::External::Cast(*args.Data())->Value()); 579 | 580 | std::string result; 581 | bool success; 582 | 583 | { 584 | v8::Unlocker unlocker(args.GetIsolate()); 585 | 586 | // We shouldn't be called with any arguments, but will not complain if 587 | // we are. 588 | success = context->js_bindings_->MyIpAddress(&result); 589 | } 590 | 591 | if (!success) { 592 | args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1")); 593 | } else { 594 | args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result)); 595 | } 596 | } 597 | 598 | // V8 callback for when "myIpAddressEx()" is invoked by the PAC script. 599 | static void MyIpAddressExCallback( 600 | const v8::FunctionCallbackInfo& args) { 601 | Context* context = 602 | static_cast(v8::External::Cast(*args.Data())->Value()); 603 | 604 | std::string ip_address_list; 605 | bool success; 606 | 607 | { 608 | v8::Unlocker unlocker(args.GetIsolate()); 609 | 610 | // We shouldn't be called with any arguments, but will not complain if 611 | // we are. 612 | success = context->js_bindings_->MyIpAddressEx(&ip_address_list); 613 | } 614 | 615 | if (!success) 616 | ip_address_list = std::string(); 617 | args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list)); 618 | } 619 | 620 | // V8 callback for when "dnsResolve()" is invoked by the PAC script. 621 | static void DnsResolveCallback(const v8::FunctionCallbackInfo& args) { 622 | Context* context = 623 | static_cast(v8::External::Cast(*args.Data())->Value()); 624 | 625 | // We need at least one string argument. 626 | std::string hostname; 627 | if (!GetHostnameArgument(args, &hostname)) { 628 | return; 629 | } 630 | 631 | std::string ip_address; 632 | bool success; 633 | 634 | { 635 | v8::Unlocker unlocker(args.GetIsolate()); 636 | success = context->js_bindings_->DnsResolve(hostname, &ip_address); 637 | } 638 | 639 | if (success) { 640 | args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address)); 641 | } else { 642 | args.GetReturnValue().SetNull(); 643 | } 644 | } 645 | 646 | // V8 callback for when "dnsResolveEx()" is invoked by the PAC script. 647 | static void DnsResolveExCallback( 648 | const v8::FunctionCallbackInfo& args) { 649 | Context* context = 650 | static_cast(v8::External::Cast(*args.Data())->Value()); 651 | 652 | // We need at least one string argument. 653 | std::string hostname; 654 | if (!GetHostnameArgument(args, &hostname)) { 655 | args.GetReturnValue().SetNull(); 656 | return; 657 | } 658 | 659 | std::string ip_address_list; 660 | bool success; 661 | 662 | { 663 | v8::Unlocker unlocker(args.GetIsolate()); 664 | success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list); 665 | } 666 | 667 | if (!success) 668 | ip_address_list = std::string(); 669 | 670 | args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list)); 671 | } 672 | 673 | // V8 callback for when "sortIpAddressList()" is invoked by the PAC script. 674 | static void SortIpAddressListCallback( 675 | const v8::FunctionCallbackInfo& args) { 676 | // We need at least one string argument. 677 | if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) { 678 | args.GetReturnValue().SetNull(); 679 | return; 680 | } 681 | 682 | std::string ip_address_list = V8StringToUTF8(args[0]->ToString()); 683 | std::string sorted_ip_address_list; 684 | bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list); 685 | if (!success) { 686 | args.GetReturnValue().Set(false); 687 | return; 688 | } 689 | args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list)); 690 | } 691 | 692 | // V8 callback for when "isInNetEx()" is invoked by the PAC script. 693 | static void IsInNetExCallback(const v8::FunctionCallbackInfo& args) { 694 | // We need at least 2 string arguments. 695 | if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() || 696 | args[1].IsEmpty() || !args[1]->IsString()) { 697 | args.GetReturnValue().SetNull(); 698 | return; 699 | } 700 | 701 | std::string ip_address = V8StringToUTF8(args[0]->ToString()); 702 | std::string ip_prefix = V8StringToUTF8(args[1]->ToString()); 703 | args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix)); 704 | } 705 | 706 | ProxyResolverJSBindings* js_bindings_; 707 | ProxyErrorListener* error_listener_; 708 | v8::Isolate* isolate_; 709 | v8::Persistent v8_this_; 710 | v8::Persistent v8_context_; 711 | }; 712 | 713 | // ProxyResolverV8 ------------------------------------------------------------ 714 | 715 | bool ProxyResolverV8::initialized_for_this_process_ = false; 716 | 717 | ProxyResolverV8::ProxyResolverV8( 718 | ProxyResolverJSBindings* custom_js_bindings, 719 | ProxyErrorListener* error_listener) 720 | : context_(NULL), js_bindings_(custom_js_bindings), 721 | error_listener_(error_listener) { 722 | if (!initialized_for_this_process_) { 723 | v8::Platform* platform = v8::platform::CreateDefaultPlatform(); 724 | v8::V8::InitializePlatform(platform); 725 | v8::V8::Initialize(); 726 | initialized_for_this_process_ = true; 727 | } 728 | } 729 | 730 | ProxyResolverV8::~ProxyResolverV8() { 731 | if (context_ != NULL) { 732 | delete context_; 733 | context_ = NULL; 734 | } 735 | if (js_bindings_ != NULL) { 736 | delete js_bindings_; 737 | } 738 | } 739 | 740 | int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host, 741 | android::String16* results) { 742 | // If the V8 instance has not been initialized (either because 743 | // SetPacScript() wasn't called yet, or because it failed. 744 | if (context_ == NULL) 745 | return ERR_FAILED; 746 | 747 | // Otherwise call into V8. 748 | int rv = context_->ResolveProxy(spec, host, results); 749 | 750 | return rv; 751 | } 752 | 753 | void ProxyResolverV8::PurgeMemory() { 754 | context_->PurgeMemory(); 755 | } 756 | 757 | int ProxyResolverV8::SetPacScript(const android::String16& script_data) { 758 | if (context_ != NULL) { 759 | delete context_; 760 | context_ = NULL; 761 | } 762 | if (script_data.size() == 0) 763 | return ERR_PAC_SCRIPT_FAILED; 764 | 765 | // Disable JIT 766 | static const char kNoOpt[] = "--no-opt"; 767 | v8::V8::SetFlagsFromString(kNoOpt, strlen(kNoOpt)); 768 | 769 | // Try parsing the PAC script. 770 | v8::Isolate::CreateParams create_params; 771 | create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); 772 | 773 | context_ = new Context(js_bindings_, error_listener_, v8::Isolate::New(create_params)); 774 | int rv; 775 | if ((rv = context_->InitV8(script_data)) != OK) { 776 | context_ = NULL; 777 | } 778 | if (rv != OK) 779 | context_ = NULL; 780 | return rv; 781 | } 782 | 783 | } // namespace net 784 | --------------------------------------------------------------------------------