aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Monk <jmonk@google.com>2013-07-22 13:20:39 -0400
committerJason Monk <jmonk@google.com>2013-07-22 16:07:05 -0400
commitfc93418c483ce474a1f4888b50f92574a1b81be3 (patch)
tree18cc12b9518e70f024acf924b409101b7bc891f5
parent2e12de22c3b9d316711d63e5911f3b6a6cf5bec4 (diff)
downloadchromium-libpac-fc93418c483ce474a1f4888b50f92574a1b81be3.tar.gz
Shared library that provides a PAC file parser.
This adds a shared library that provides a javascript proxy resolver extracted from the chromium project. The resolver is designed to parse proxy auto-config (PAC) files that implement a single javascript function (FindProxyForURL(url, host)). Change-Id: I241fe44555cb7a9f187fe98d265aa6dc8f1bec20
-rw-r--r--Android.mk27
-rw-r--r--LICENSE27
-rw-r--r--MODULE_LICENSE_BSD0
-rw-r--r--NOTICE27
-rw-r--r--README7
-rw-r--r--src/net_util.cc126
-rw-r--r--src/net_util.h68
-rw-r--r--src/proxy_resolver_js_bindings.cc116
-rw-r--r--src/proxy_resolver_js_bindings.h69
-rw-r--r--src/proxy_resolver_script.h276
-rw-r--r--src/proxy_resolver_v8.cc726
-rw-r--r--src/proxy_resolver_v8.h68
12 files changed, 1537 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..2b280b9
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+# Set up the target identity
+LOCAL_MODULE := libpac
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+LOCAL_SRC_FILES := \
+ src/proxy_resolver_v8.cc \
+ src/proxy_resolver_js_bindings.cc \
+ src/net_util.cc
+
+LOCAL_CFLAGS += \
+ -Wno-endif-labels \
+ -Wno-import \
+ -Wno-format \
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src external/v8
+
+LOCAL_STATIC_LIBRARIES := libv8
+LOCAL_SHARED_LIBRARIES := libstlport liblog
+
+include external/stlport/libstlport.mk
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dc3504
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MODULE_LICENSE_BSD b/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_BSD
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..8dc3504
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README b/README
new file mode 100644
index 0000000..59852b5
--- /dev/null
+++ b/README
@@ -0,0 +1,7 @@
+This library is based on the proxy_resolver_v8 and other classes taken from
+chromium project.
+
+Modifications have been made to remove dependencies on chromium utility
+functions and classes. To make this library accessible on Android, the string
+utilities have been modified to use stl and the network functions have been
+modified to use UNIX functions.
diff --git a/src/net_util.cc b/src/net_util.cc
new file mode 100644
index 0000000..32a8193
--- /dev/null
+++ b/src/net_util.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include <algorithm>
+#include <iterator>
+#include <map>
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <string.h>
+
+#include "net_util.h"
+
+namespace net {
+
+#ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */
+#define INET6_ADDRSTRLEN 46
+#endif
+
+bool ParseIPLiteralToNumber(const std::string& ip_literal,
+ IPAddressNumber* ip_number) {
+ char buf[sizeof(struct in6_addr)];
+ int size = sizeof(struct in_addr);
+ int mode = AF_INET;
+ if (ip_literal.find(':') != std::string::npos) {
+ mode = AF_INET6;
+ size = sizeof(struct in6_addr);
+ }
+ inet_pton(mode, ip_literal.c_str(), buf);
+ for (int i = 0; i < size; i++) {
+ (*ip_number)[i] = buf[i];
+ }
+ return true;
+}
+
+IPAddressNumber ConvertIPv4NumberToIPv6Number(
+ const IPAddressNumber& ipv4_number) {
+ // IPv4-mapped addresses are formed by:
+ // <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>.
+ IPAddressNumber ipv6_number;
+ ipv6_number.reserve(16);
+ ipv6_number.insert(ipv6_number.end(), 10, 0);
+ ipv6_number.push_back(0xFF);
+ ipv6_number.push_back(0xFF);
+ ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end());
+ return ipv6_number;
+}
+
+bool ParseCIDRBlock(const std::string& cidr_literal,
+ IPAddressNumber* ip_number,
+ size_t* prefix_length_in_bits) {
+ // We expect CIDR notation to match one of these two templates:
+ // <IPv4-literal> "/" <number of bits>
+ // <IPv6-literal> "/" <number of bits>
+
+ std::vector<std::string> parts;
+ unsigned int split = cidr_literal.find('/');
+ if (split == std::string::npos)
+ return false;
+ parts.push_back(cidr_literal.substr(0, split));
+ parts.push_back(cidr_literal.substr(split + 1));
+ if (parts[1].find('/') != std::string::npos)
+ return false;
+
+ // Parse the IP address.
+ if (!ParseIPLiteralToNumber(parts[0], ip_number))
+ return false;
+
+ // Parse the prefix length.
+ int number_of_bits = atoi(parts[1].c_str());
+
+ // Make sure the prefix length is in a valid range.
+ if (number_of_bits < 0 ||
+ number_of_bits > static_cast<int>(ip_number->size() * 8))
+ return false;
+
+ *prefix_length_in_bits = static_cast<size_t>(number_of_bits);
+ return true;
+}
+
+bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number,
+ const IPAddressNumber& ip_prefix,
+ size_t prefix_length_in_bits) {
+ // Both the input IP address and the prefix IP address should be
+ // either IPv4 or IPv6.
+
+ // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to
+ // IPv6 addresses in order to do the comparison.
+ if (ip_number.size() != ip_prefix.size()) {
+ if (ip_number.size() == 4) {
+ return IPNumberMatchesPrefix(ConvertIPv4NumberToIPv6Number(ip_number),
+ ip_prefix, prefix_length_in_bits);
+ }
+ return IPNumberMatchesPrefix(ip_number,
+ ConvertIPv4NumberToIPv6Number(ip_prefix),
+ 96 + prefix_length_in_bits);
+ }
+
+ // Otherwise we are comparing two IPv4 addresses, or two IPv6 addresses.
+ // Compare all the bytes that fall entirely within the prefix.
+ int num_entire_bytes_in_prefix = prefix_length_in_bits / 8;
+ for (int i = 0; i < num_entire_bytes_in_prefix; ++i) {
+ if (ip_number[i] != ip_prefix[i])
+ return false;
+ }
+
+ // In case the prefix was not a multiple of 8, there will be 1 byte
+ // which is only partially masked.
+ int remaining_bits = prefix_length_in_bits % 8;
+ if (remaining_bits != 0) {
+ unsigned char mask = 0xFF << (8 - remaining_bits);
+ int i = num_entire_bytes_in_prefix;
+ if ((ip_number[i] & mask) != (ip_prefix[i] & mask))
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace net
diff --git a/src/net_util.h b/src/net_util.h
new file mode 100644
index 0000000..e50008f
--- /dev/null
+++ b/src/net_util.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_NET_UTIL_H_
+#define NET_BASE_NET_UTIL_H_
+#pragma once
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <ws2tcpip.h>
+#elif defined(OS_POSIX)
+#include <sys/socket.h>
+#endif
+
+#include <list>
+#include <string>
+#include <set>
+#include <vector>
+
+#include <cstdint>
+
+namespace net {
+
+// IPAddressNumber is used to represent an IP address's numeric value as an
+// array of bytes, from most significant to least significant. This is the
+// network byte ordering.
+//
+// IPv4 addresses will have length 4, whereas IPv6 address will have length 16.
+typedef std::vector<unsigned char> IPAddressNumber;
+typedef std::vector<IPAddressNumber> IPAddressList;
+
+// Parses an IP address literal (either IPv4 or IPv6) to its numeric value.
+// Returns true on success and fills |ip_number| with the numeric value.
+bool ParseIPLiteralToNumber(const std::string& ip_literal,
+ IPAddressNumber* ip_number);
+
+// Parses an IP block specifier from CIDR notation to an
+// (IP address, prefix length) pair. Returns true on success and fills
+// |*ip_number| with the numeric value of the IP address and sets
+// |*prefix_length_in_bits| with the length of the prefix.
+//
+// CIDR notation literals can use either IPv4 or IPv6 literals. Some examples:
+//
+// 10.10.3.1/20
+// a:b:c::/46
+// ::1/128
+bool ParseCIDRBlock(const std::string& cidr_literal,
+ IPAddressNumber* ip_number,
+ size_t* prefix_length_in_bits);
+
+// Compares an IP address to see if it falls within the specified IP block.
+// Returns true if it does, false otherwise.
+//
+// The IP block is given by (|ip_prefix|, |prefix_length_in_bits|) -- any
+// IP address whose |prefix_length_in_bits| most significant bits match
+// |ip_prefix| will be matched.
+//
+// In cases when an IPv4 address is being compared to an IPv6 address prefix
+// and vice versa, the IPv4 addresses will be converted to IPv4-mapped
+// (IPv6) addresses.
+bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number,
+ const IPAddressNumber& ip_prefix,
+ size_t prefix_length_in_bits);
+
+} // namespace net
+
+#endif // NET_BASE_NET_UTIL_H_
diff --git a/src/proxy_resolver_js_bindings.cc b/src/proxy_resolver_js_bindings.cc
new file mode 100644
index 0000000..143179c
--- /dev/null
+++ b/src/proxy_resolver_js_bindings.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "proxy_resolver_js_bindings.h"
+
+#include <netdb.h>
+#include <unistd.h>
+#include <cstddef>
+#include <memory>
+#include <string>
+
+#include "net_util.h"
+
+namespace net {
+
+// ProxyResolverJSBindings implementation.
+class DefaultJSBindings : public ProxyResolverJSBindings {
+ public:
+ DefaultJSBindings() {
+ }
+
+ // Handler for "alert(message)".
+ virtual void Alert(const std::wstring& message) {
+ // TODO: Fix error handling
+ }
+
+ // Handler for "myIpAddress()".
+ // TODO: Perhaps enumerate the interfaces directly, using
+ // getifaddrs().
+ virtual bool MyIpAddress(std::string* first_ip_address) {
+ return MyIpAddressImpl(first_ip_address);
+ }
+
+ // Handler for "myIpAddressEx()".
+ virtual bool MyIpAddressEx(std::string* ip_address_list) {
+ return MyIpAddressExImpl(ip_address_list);
+ }
+
+ // Handler for "dnsResolve(host)".
+ virtual bool DnsResolve(const std::string& host,
+ std::string* first_ip_address) {
+ return DnsResolveImpl(host, first_ip_address);
+ }
+
+ // Handler for "dnsResolveEx(host)".
+ virtual bool DnsResolveEx(const std::string& host,
+ std::string* ip_address_list) {
+ return DnsResolveExImpl(host, ip_address_list);
+ }
+
+ // Handler for when an error is encountered. |line_number| may be -1.
+ virtual void OnError(int line_number, const std::wstring& message) {
+ }
+
+ private:
+ bool MyIpAddressImpl(std::string* first_ip_address) {
+ std::string my_hostname = GetHostName();
+ if (my_hostname.empty())
+ return false;
+ return DnsResolveImpl(my_hostname, first_ip_address);
+ }
+
+ bool MyIpAddressExImpl(std::string* ip_address_list) {
+ std::string my_hostname = GetHostName();
+ if (my_hostname.empty())
+ return false;
+ return DnsResolveExImpl(my_hostname, ip_address_list);
+ }
+
+ bool DnsResolveImpl(const std::string& host,
+ std::string* first_ip_address) {
+ struct hostent* he = gethostbyname(host.c_str());
+
+ if (he == NULL) {
+ return false;
+ }
+ *first_ip_address = std::string(he->h_addr);
+ return true;
+ }
+
+ bool DnsResolveExImpl(const std::string& host,
+ std::string* ip_address_list) {
+ struct hostent* he = gethostbyname(host.c_str());
+
+ if (he == NULL) {
+ return false;
+ }
+ std::string address_list_str;
+ for (char** addr = &he->h_addr; *addr != NULL; ++addr) {
+ if (!address_list_str.empty())
+ address_list_str += ";";
+ const std::string address_string = std::string(*addr);
+ if (address_string.empty())
+ return false;
+ address_list_str += address_string;
+ }
+ *ip_address_list = std::string(he->h_addr);
+ return true;
+ }
+
+ std::string GetHostName() {
+ char buffer[256];
+ if (gethostname(buffer, 256) != 0) {
+ buffer[0] = '\0';
+ }
+ return std::string(buffer);
+ }
+};
+
+// static
+ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault() {
+ return new DefaultJSBindings();
+}
+
+} // namespace net
diff --git a/src/proxy_resolver_js_bindings.h b/src/proxy_resolver_js_bindings.h
new file mode 100644
index 0000000..fd8fad3
--- /dev/null
+++ b/src/proxy_resolver_js_bindings.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_
+#define NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_
+#pragma once
+
+#include <string>
+
+
+namespace net {
+
+class HostResolver;
+class NetLog;
+
+// Interface for the javascript bindings.
+class ProxyResolverJSBindings {
+ public:
+ ProxyResolverJSBindings() {}// : current_request_context_(NULL) {}
+
+ virtual ~ProxyResolverJSBindings() {}
+
+ // Handler for "alert(message)"
+ virtual void Alert(const std::wstring& message) = 0;
+
+ // Handler for "myIpAddress()". Returns true on success and fills
+ // |*first_ip_address| with the result.
+ virtual bool MyIpAddress(std::string* first_ip_address) = 0;
+
+ // Handler for "myIpAddressEx()". Returns true on success and fills
+ // |*ip_address_list| with the result.
+ //
+ // This is a Microsoft extension to PAC for IPv6, see:
+ // http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx
+
+ virtual bool MyIpAddressEx(std::string* ip_address_list) = 0;
+
+ // Handler for "dnsResolve(host)". Returns true on success and fills
+ // |*first_ip_address| with the result.
+ virtual bool DnsResolve(const std::string& host,
+ std::string* first_ip_address) = 0;
+
+ // Handler for "dnsResolveEx(host)". Returns true on success and fills
+ // |*ip_address_list| with the result.
+ //
+ // This is a Microsoft extension to PAC for IPv6, see:
+ // http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx
+ virtual bool DnsResolveEx(const std::string& host,
+ std::string* ip_address_list) = 0;
+
+ // Handler for when an error is encountered. |line_number| may be -1
+ // if a line number is not applicable to this error.
+ virtual void OnError(int line_number, const std::wstring& error) = 0;
+
+ // Creates a default javascript bindings implementation that will:
+ // - Send script error messages to both VLOG(1) and the NetLog.
+ // - Send script alert()s to both VLOG(1) and the NetLog.
+ // - Use the provided host resolver to service dnsResolve().
+ //
+ // Note that |host_resolver| will be used in sync mode mode.
+ static ProxyResolverJSBindings* CreateDefault();
+
+ private:
+};
+
+} // namespace net
+
+#endif // NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_
diff --git a/src/proxy_resolver_script.h b/src/proxy_resolver_script.h
new file mode 100644
index 0000000..283eff9
--- /dev/null
+++ b/src/proxy_resolver_script.h
@@ -0,0 +1,276 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Akhil Arora <akhil.arora@sun.com>
+ * Tomi Leppikangas <Tomi.Leppikangas@oulu.fi>
+ * Darin Fisher <darin@meer.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef NET_PROXY_PROXY_RESOLVER_SCRIPT_H_
+#define NET_PROXY_PROXY_RESOLVER_SCRIPT_H_
+
+// The following code was formatted from:
+// 'mozilla/netwerk/base/src/nsProxyAutoConfig.js' (1.55)
+//
+// Using the command:
+// $ cat nsProxyAutoConfig.js |
+// awk '/var pacUtils/,/EOF/' |
+// sed -e 's/^\s*$/""/g' |
+// sed -e 's/"\s*[+]\s*$/"/g' |
+// sed -e 's/"$/" \\/g' |
+// sed -e 's/\/(ipaddr);/\/.exec(ipaddr);/g' |
+// grep -v '^var pacUtils ='
+#define PROXY_RESOLVER_SCRIPT \
+ "function dnsDomainIs(host, domain) {\n" \
+ " return (host.length >= domain.length &&\n" \
+ " host.substring(host.length - domain.length) == domain);\n" \
+ "}\n" \
+ "" \
+ "function dnsDomainLevels(host) {\n" \
+ " return host.split('.').length-1;\n" \
+ "}\n" \
+ "" \
+ "function convert_addr(ipchars) {\n" \
+ " var bytes = ipchars.split('.');\n" \
+ " var result = ((bytes[0] & 0xff) << 24) |\n" \
+ " ((bytes[1] & 0xff) << 16) |\n" \
+ " ((bytes[2] & 0xff) << 8) |\n" \
+ " (bytes[3] & 0xff);\n" \
+ " return result;\n" \
+ "}\n" \
+ "" \
+ "function isInNet(ipaddr, pattern, maskstr) {\n" \
+ " var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipaddr);\n" \
+ " if (test == null) {\n" \
+ " ipaddr = dnsResolve(ipaddr);\n" \
+ " if (ipaddr == null)\n" \
+ " return false;\n" \
+ " } else if (test[1] > 255 || test[2] > 255 || \n" \
+ " test[3] > 255 || test[4] > 255) {\n" \
+ " return false; // not an IP address\n" \
+ " }\n" \
+ " var host = convert_addr(ipaddr);\n" \
+ " var pat = convert_addr(pattern);\n" \
+ " var mask = convert_addr(maskstr);\n" \
+ " return ((host & mask) == (pat & mask));\n" \
+ " \n" \
+ "}\n" \
+ "" \
+ "function isPlainHostName(host) {\n" \
+ " return (host.search('\\\\.') == -1);\n" \
+ "}\n" \
+ "" \
+ "function isResolvable(host) {\n" \
+ " var ip = dnsResolve(host);\n" \
+ " return (ip != null);\n" \
+ "}\n" \
+ "" \
+ "function localHostOrDomainIs(host, hostdom) {\n" \
+ " return (host == hostdom) ||\n" \
+ " (hostdom.lastIndexOf(host + '.', 0) == 0);\n" \
+ "}\n" \
+ "" \
+ "function shExpMatch(url, pattern) {\n" \
+ " pattern = pattern.replace(/\\./g, '\\\\.');\n" \
+ " pattern = pattern.replace(/\\*/g, '.*');\n" \
+ " pattern = pattern.replace(/\\?/g, '.');\n" \
+ " var newRe = new RegExp('^'+pattern+'$');\n" \
+ " return newRe.test(url);\n" \
+ "}\n" \
+ "" \
+ "var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n" \
+ "" \
+ "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" \
+ "" \
+ "function weekdayRange() {\n" \
+ " function getDay(weekday) {\n" \
+ " if (weekday in wdays) {\n" \
+ " return wdays[weekday];\n" \
+ " }\n" \
+ " return -1;\n" \
+ " }\n" \
+ " var date = new Date();\n" \
+ " var argc = arguments.length;\n" \
+ " var wday;\n" \
+ " if (argc < 1)\n" \
+ " return false;\n" \
+ " if (arguments[argc - 1] == 'GMT') {\n" \
+ " argc--;\n" \
+ " wday = date.getUTCDay();\n" \
+ " } else {\n" \
+ " wday = date.getDay();\n" \
+ " }\n" \
+ " var wd1 = getDay(arguments[0]);\n" \
+ " var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n" \
+ " return (wd1 == -1 || wd2 == -1) ? false\n" \
+ " : (wd1 <= wday && wday <= wd2);\n" \
+ "}\n" \
+ "" \
+ "function dateRange() {\n" \
+ " function getMonth(name) {\n" \
+ " if (name in months) {\n" \
+ " return months[name];\n" \
+ " }\n" \
+ " return -1;\n" \
+ " }\n" \
+ " var date = new Date();\n" \
+ " var argc = arguments.length;\n" \
+ " if (argc < 1) {\n" \
+ " return false;\n" \
+ " }\n" \
+ " var isGMT = (arguments[argc - 1] == 'GMT');\n" \
+ "\n" \
+ " if (isGMT) {\n" \
+ " argc--;\n" \
+ " }\n" \
+ " // function will work even without explict handling of this case\n" \
+ " if (argc == 1) {\n" \
+ " var tmp = parseInt(arguments[0]);\n" \
+ " if (isNaN(tmp)) {\n" \
+ " return ((isGMT ? date.getUTCMonth() : date.getMonth()) ==\n" \
+ "getMonth(arguments[0]));\n" \
+ " } else if (tmp < 32) {\n" \
+ " return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);\n" \
+ " } else { \n" \
+ " return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) ==\n" \
+ "tmp);\n" \
+ " }\n" \
+ " }\n" \
+ " var year = date.getFullYear();\n" \
+ " var date1, date2;\n" \
+ " date1 = new Date(year, 0, 1, 0, 0, 0);\n" \
+ " date2 = new Date(year, 11, 31, 23, 59, 59);\n" \
+ " var adjustMonth = false;\n" \
+ " for (var i = 0; i < (argc >> 1); i++) {\n" \
+ " var tmp = parseInt(arguments[i]);\n" \
+ " if (isNaN(tmp)) {\n" \
+ " var mon = getMonth(arguments[i]);\n" \
+ " date1.setMonth(mon);\n" \
+ " } else if (tmp < 32) {\n" \
+ " adjustMonth = (argc <= 2);\n" \
+ " date1.setDate(tmp);\n" \
+ " } else {\n" \
+ " date1.setFullYear(tmp);\n" \
+ " }\n" \
+ " }\n" \
+ " for (var i = (argc >> 1); i < argc; i++) {\n" \
+ " var tmp = parseInt(arguments[i]);\n" \
+ " if (isNaN(tmp)) {\n" \
+ " var mon = getMonth(arguments[i]);\n" \
+ " date2.setMonth(mon);\n" \
+ " } else if (tmp < 32) {\n" \
+ " date2.setDate(tmp);\n" \
+ " } else {\n" \
+ " date2.setFullYear(tmp);\n" \
+ " }\n" \
+ " }\n" \
+ " if (adjustMonth) {\n" \
+ " date1.setMonth(date.getMonth());\n" \
+ " date2.setMonth(date.getMonth());\n" \
+ " }\n" \
+ " if (isGMT) {\n" \
+ " var tmp = date;\n" \
+ " tmp.setFullYear(date.getUTCFullYear());\n" \
+ " tmp.setMonth(date.getUTCMonth());\n" \
+ " tmp.setDate(date.getUTCDate());\n" \
+ " tmp.setHours(date.getUTCHours());\n" \
+ " tmp.setMinutes(date.getUTCMinutes());\n" \
+ " tmp.setSeconds(date.getUTCSeconds());\n" \
+ " date = tmp;\n" \
+ " }\n" \
+ " return ((date1 <= date) && (date <= date2));\n" \
+ "}\n" \
+ "" \
+ "function timeRange() {\n" \
+ " var argc = arguments.length;\n" \
+ " var date = new Date();\n" \
+ " var isGMT= false;\n" \
+ "\n" \
+ " if (argc < 1) {\n" \
+ " return false;\n" \
+ " }\n" \
+ " if (arguments[argc - 1] == 'GMT') {\n" \
+ " isGMT = true;\n" \
+ " argc--;\n" \
+ " }\n" \
+ "\n" \
+ " var hour = isGMT ? date.getUTCHours() : date.getHours();\n" \
+ " var date1, date2;\n" \
+ " date1 = new Date();\n" \
+ " date2 = new Date();\n" \
+ "\n" \
+ " if (argc == 1) {\n" \
+ " return (hour == arguments[0]);\n" \
+ " } else if (argc == 2) {\n" \
+ " return ((arguments[0] <= hour) && (hour <= arguments[1]));\n" \
+ " } else {\n" \
+ " switch (argc) {\n" \
+ " case 6:\n" \
+ " date1.setSeconds(arguments[2]);\n" \
+ " date2.setSeconds(arguments[5]);\n" \
+ " case 4:\n" \
+ " var middle = argc >> 1;\n" \
+ " date1.setHours(arguments[0]);\n" \
+ " date1.setMinutes(arguments[1]);\n" \
+ " date2.setHours(arguments[middle]);\n" \
+ " date2.setMinutes(arguments[middle + 1]);\n" \
+ " if (middle == 2) {\n" \
+ " date2.setSeconds(59);\n" \
+ " }\n" \
+ " break;\n" \
+ " default:\n" \
+ " throw 'timeRange: bad number of arguments'\n" \
+ " }\n" \
+ " }\n" \
+ "\n" \
+ " if (isGMT) {\n" \
+ " date.setFullYear(date.getUTCFullYear());\n" \
+ " date.setMonth(date.getUTCMonth());\n" \
+ " date.setDate(date.getUTCDate());\n" \
+ " date.setHours(date.getUTCHours());\n" \
+ " date.setMinutes(date.getUTCMinutes());\n" \
+ " date.setSeconds(date.getUTCSeconds());\n" \
+ " }\n" \
+ " return ((date1 <= date) && (date <= date2));\n" \
+ "}\n"
+
+// This is a Microsoft extension to PAC for IPv6, see:
+// http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx
+#define PROXY_RESOLVER_SCRIPT_EX \
+ "function isResolvableEx(host) {\n" \
+ " var ipList = dnsResolveEx(host);\n" \
+ " return (ipList != '');\n" \
+ "}\n"
+
+#endif // NET_PROXY_PROXY_RESOLVER_SCRIPT_H_
diff --git a/src/proxy_resolver_v8.cc b/src/proxy_resolver_v8.cc
new file mode 100644
index 0000000..b6ac654
--- /dev/null
+++ b/src/proxy_resolver_v8.cc
@@ -0,0 +1,726 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <cstdio>
+#include <string>
+
+#include "proxy_resolver_v8.h"
+
+#include "proxy_resolver_script.h"
+#include "net_util.h"
+#include <include/v8.h>
+#include <algorithm>
+#include <vector>
+
+#include <iostream>
+
+#include <string.h>
+
+// Notes on the javascript environment:
+//
+// For the majority of the PAC utility functions, we use the same code
+// as Firefox. See the javascript library that proxy_resolver_scipt.h
+// pulls in.
+//
+// In addition, we implement a subset of Microsoft's extensions to PAC.
+// - myIpAddressEx()
+// - dnsResolveEx()
+// - isResolvableEx()
+// - isInNetEx()
+// - sortIpAddressList()
+//
+// It is worth noting that the original PAC specification does not describe
+// the return values on failure. Consequently, there are compatibility
+// differences between browsers on what to return on failure, which are
+// illustrated below:
+//
+// --------------------+-------------+-------------------+--------------
+// | Firefox3 | InternetExplorer8 | --> Us <---
+// --------------------+-------------+-------------------+--------------
+// myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
+// dnsResolve() | null | false | null
+// myIpAddressEx() | N/A | "" | ""
+// sortIpAddressList() | N/A | false | false
+// dnsResolveEx() | N/A | "" | ""
+// isInNetEx() | N/A | false | false
+// --------------------+-------------+-------------------+--------------
+//
+// TODO: The cell above reading ??? means I didn't test it.
+//
+// Another difference is in how dnsResolve() and myIpAddress() are
+// implemented -- whether they should restrict to IPv4 results, or
+// include both IPv4 and IPv6. The following table illustrates the
+// differences:
+//
+// --------------------+-------------+-------------------+--------------
+// | Firefox3 | InternetExplorer8 | --> Us <---
+// --------------------+-------------+-------------------+--------------
+// myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
+// dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
+// isResolvable() | IPv4/IPv6 | IPv4 | IPv4
+// myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
+// dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
+// sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
+// isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
+// isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
+// -----------------+-------------+-------------------+--------------
+
+static bool DoIsStringASCII(const std::wstring& str) {
+ for (size_t i = 0; i < str.length(); i++) {
+ unsigned char c = str[i];
+ if (c > 0x7F)
+ return false;
+ }
+ return true;
+}
+
+bool IsStringASCII(const std::wstring& str) {
+ return DoIsStringASCII(str);
+}
+
+std::string UTF16ToASCII(const std::wstring& utf16) {
+ return std::string(utf16.begin(), utf16.end());
+}
+
+namespace net {
+
+namespace {
+
+// Pseudo-name for the PAC script.
+const char kPacResourceName[] = "proxy-pac-script.js";
+// Pseudo-name for the PAC utility script.
+const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
+
+// External string wrapper so V8 can access the UTF16 string wrapped by
+// ProxyResolverScriptData.
+class V8ExternalStringFromScriptData
+ : public v8::String::ExternalStringResource {
+ public:
+ explicit V8ExternalStringFromScriptData(
+ const std::wstring& script_data)
+ : script_data_(script_data) {}
+
+ virtual const uint16_t* data() const {
+ return reinterpret_cast<const uint16_t*>(script_data_.data());
+ }
+
+ virtual size_t length() const {
+ return script_data_.size();
+ }
+
+ private:
+ const std::wstring& script_data_;
+// DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
+};
+
+// External string wrapper so V8 can access a string literal.
+class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
+ public:
+ // |ascii| must be a NULL-terminated C string, and must remain valid
+ // throughout this object's lifetime.
+ V8ExternalASCIILiteral(const char* ascii, size_t length)
+ : ascii_(ascii), length_(length) {
+
+ }
+
+ virtual const char* data() const {
+ return ascii_;
+ }
+
+ virtual size_t length() const {
+ return length_;
+ }
+
+ private:
+ const char* ascii_;
+ size_t length_;
+};
+
+// When creating a v8::String from a C++ string we have two choices: create
+// a copy, or create a wrapper that shares the same underlying storage.
+// For small strings it is better to just make a copy, whereas for large
+// strings there are savings by sharing the storage. This number identifies
+// the cutoff length for when to start wrapping rather than creating copies.
+const size_t kMaxStringBytesForCopy = 256;
+
+template <class string_type>
+inline typename string_type::value_type* WriteInto(string_type* str,
+ size_t length_with_null) {
+ str->reserve(length_with_null);
+ str->resize(length_with_null - 1);
+ return &((*str)[0]);
+}
+
+// Converts a V8 String to a UTF8 std::string.
+std::string V8StringToUTF8(v8::Handle<v8::String> s) {
+ std::string result;
+ s->WriteUtf8(WriteInto(&result, s->Length() + 1));
+ return result;
+}
+
+// Converts a V8 String to a UTF16 string.
+std::wstring V8StringToUTF16(v8::Handle<v8::String> s) {
+ int len = s->Length();
+ std::wstring result;
+ // Note that the reinterpret cast is because on Windows string is an alias
+ // to wstring, and hence has character type wchar_t not uint16_t.
+ s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
+ return result;
+}
+
+// Converts an ASCII std::string to a V8 string.
+v8::Local<v8::String> ASCIIStringToV8String(const std::string& s) {
+ return v8::String::New(s.data(), s.size());
+}
+
+// Converts an ASCII string literal to a V8 string.
+v8::Local<v8::String> ASCIILiteralToV8String(const char* ascii) {
+// DCHECK(IsStringASCII(ascii));
+ size_t length = strlen(ascii);
+ if (length <= kMaxStringBytesForCopy)
+ return v8::String::New(ascii, length);
+ return v8::String::NewExternal(new V8ExternalASCIILiteral(ascii, length));
+}
+
+// Stringizes a V8 object by calling its toString() method. Returns true
+// on success. This may fail if the toString() throws an exception.
+bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
+ std::wstring* utf16_result) {
+ if (object.IsEmpty())
+ return false;
+
+ v8::HandleScope scope;
+ v8::Local<v8::String> str_object = object->ToString();
+ if (str_object.IsEmpty())
+ return false;
+ *utf16_result = V8StringToUTF16(str_object);
+ return true;
+}
+
+// Extracts an hostname argument from |args|. On success returns true
+// and fills |*hostname| with the result.
+bool GetHostnameArgument(const v8::Arguments& args, std::string* hostname) {
+ // The first argument should be a string.
+ if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
+ return false;
+
+ const std::wstring hostname_utf16 = V8StringToUTF16(args[0]->ToString());
+
+ // If the hostname is already in ASCII, simply return it as is.
+ if (IsStringASCII(hostname_utf16)) {
+ *hostname = UTF16ToASCII(hostname_utf16);
+ return true;
+ }
+ return false;
+}
+
+// Wrapper for passing around IP address strings and IPAddressNumber objects.
+struct IPAddress {
+ IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
+ : string_value(ip_string),
+ ip_address_number(ip_number) {
+ }
+
+ // Used for sorting IP addresses in ascending order in SortIpAddressList().
+ // IP6 addresses are placed ahead of IPv4 addresses.
+ bool operator<(const IPAddress& rhs) const {
+ const IPAddressNumber& ip1 = this->ip_address_number;
+ const IPAddressNumber& ip2 = rhs.ip_address_number;
+ if (ip1.size() != ip2.size())
+ return ip1.size() > ip2.size(); // IPv6 before IPv4.
+ return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
+ }
+
+ std::string string_value;
+ IPAddressNumber ip_address_number;
+};
+
+template<typename STR>
+bool RemoveCharsT(const STR& input,
+ const typename STR::value_type remove_chars[],
+ STR* output) {
+ bool removed = false;
+ size_t found;
+
+ *output = input;
+
+ found = output->find_first_of(remove_chars);
+ while (found != STR::npos) {
+ removed = true;
+ output->replace(found, 1, STR());
+ found = output->find_first_of(remove_chars, found);
+ }
+
+ return removed;
+}
+
+bool RemoveChars(const std::wstring& input,
+ const wchar_t remove_chars[],
+ std::wstring* output) {
+ return RemoveCharsT(input, remove_chars, output);
+}
+
+bool RemoveChars(const std::string& input,
+ const char remove_chars[],
+ std::string* output) {
+ return RemoveCharsT(input, remove_chars, output);
+}
+
+// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
+// semi-colon delimited string containing IP addresses.
+// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
+// IP addresses or an empty string if unable to sort the IP address list.
+// Returns 'true' if the sorting was successful, and 'false' if the input was an
+// empty string, a string of separators (";" in this case), or if any of the IP
+// addresses in the input list failed to parse.
+bool SortIpAddressList(const std::string& ip_address_list,
+ std::string* sorted_ip_address_list) {
+ sorted_ip_address_list->clear();
+
+ // Strip all whitespace (mimics IE behavior).
+ std::string cleaned_ip_address_list;
+ RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
+ if (cleaned_ip_address_list.empty())
+ return false;
+
+ // Split-up IP addresses and store them in a vector.
+ std::vector<IPAddress> ip_vector;
+ IPAddressNumber ip_num;
+ char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
+ while (tok_list != NULL) {
+ if (!ParseIPLiteralToNumber(tok_list, &ip_num))
+ return false;
+ ip_vector.push_back(IPAddress(tok_list, ip_num));
+ tok_list = strtok(tok_list, ";");
+ }
+
+ if (ip_vector.empty()) // Can happen if we have something like
+ return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
+
+ // Sort lists according to ascending numeric value.
+ if (ip_vector.size() > 1)
+ std::stable_sort(ip_vector.begin(), ip_vector.end());
+
+ // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
+ // IPv4).
+ for (size_t i = 0; i < ip_vector.size(); ++i) {
+ if (i > 0)
+ *sorted_ip_address_list += ";";
+ *sorted_ip_address_list += ip_vector[i].string_value;
+ }
+ return true;
+}
+
+
+// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
+// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
+// slash-delimited IP prefix with the top 'n' bits specified in the bit
+// field. This returns 'true' if the address is in the same subnet, and
+// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
+// format, or if an address and prefix of different types are used (e.g. IPv6
+// address and IPv4 prefix).
+bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
+ IPAddressNumber address;
+ if (!ParseIPLiteralToNumber(ip_address, &address))
+ return false;
+
+ IPAddressNumber prefix;
+ size_t prefix_length_in_bits;
+ if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
+ return false;
+
+ // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
+ if (address.size() != prefix.size())
+ return false;
+
+ return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
+}
+
+} // namespace
+
+// ProxyResolverV8::Context ---------------------------------------------------
+
+class ProxyResolverV8::Context {
+ public:
+ explicit Context(ProxyResolverJSBindings* js_bindings)
+ : js_bindings_(js_bindings) {
+ }
+
+ ~Context() {
+ v8::Locker locked;
+
+ v8_this_.Dispose();
+ v8_context_.Dispose();
+
+ // Run the V8 garbage collector. We do this to be sure the
+ // ExternalStringResource objects we allocated get properly disposed.
+ // Otherwise when running the unit-tests they may get leaked.
+ // See crbug.com/48145.
+ PurgeMemory();
+ }
+
+ int ResolveProxy(const std::string url, const std::string host, std::string* results) {
+ v8::Locker locked;
+ v8::HandleScope scope;
+
+ v8::Context::Scope function_scope(v8_context_);
+
+ v8::Local<v8::Value> function;
+ if (!GetFindProxyForURL(&function)) {
+ *results = "FindProxyForURL() is undefined";
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ v8::Handle<v8::Value> argv[] = {
+ ASCIIStringToV8String(url),
+ ASCIIStringToV8String(host) };
+
+ v8::TryCatch try_catch;
+ v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
+ v8_context_->Global(), 2, argv);
+
+ if (try_catch.HasCaught()) {
+ *results = V8StringToUTF8(try_catch.Message()->Get());
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ if (!ret->IsString()) {
+ *results = "FindProxyForURL() did not return a string.";
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ std::wstring ret_str = V8StringToUTF16(ret->ToString());
+
+ if (!IsStringASCII(ret_str)) {
+ // TODO: Rather than failing when a wide string is returned, we
+ // could extend the parsing to handle IDNA hostnames by
+ // converting them to ASCII punycode.
+ // crbug.com/47234
+ *results = "FindProxyForURL() returned a non-ASCII string";
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ *results = V8StringToUTF8(ret->ToString());
+ return OK;
+ }
+
+ int InitV8(const std::string& pac_script) {
+ v8::Locker locked;
+ v8::HandleScope scope;
+
+ v8_this_ = v8::Persistent<v8::External>::New(v8::External::New(this));
+ v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+
+ // Attach the javascript bindings.
+ v8::Local<v8::FunctionTemplate> alert_template =
+ v8::FunctionTemplate::New(&AlertCallback, v8_this_);
+ global_template->Set(ASCIILiteralToV8String("alert"), alert_template);
+
+ v8::Local<v8::FunctionTemplate> my_ip_address_template =
+ v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this_);
+ global_template->Set(ASCIILiteralToV8String("myIpAddress"),
+ my_ip_address_template);
+
+ v8::Local<v8::FunctionTemplate> dns_resolve_template =
+ v8::FunctionTemplate::New(&DnsResolveCallback, v8_this_);
+ global_template->Set(ASCIILiteralToV8String("dnsResolve"),
+ dns_resolve_template);
+
+ // Microsoft's PAC extensions:
+
+ v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
+ v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this_);
+ global_template->Set(ASCIILiteralToV8String("dnsResolveEx"),
+ dns_resolve_ex_template);
+
+ v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
+ v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this_);
+ global_template->Set(ASCIILiteralToV8String("myIpAddressEx"),
+ my_ip_address_ex_template);
+
+ v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
+ v8::FunctionTemplate::New(&SortIpAddressListCallback, v8_this_);
+ global_template->Set(ASCIILiteralToV8String("sortIpAddressList"),
+ sort_ip_address_list_template);
+
+ v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
+ v8::FunctionTemplate::New(&IsInNetExCallback, v8_this_);
+ global_template->Set(ASCIILiteralToV8String("isInNetEx"),
+ is_in_net_ex_template);
+
+ v8_context_ = v8::Context::New(NULL, global_template);
+
+ v8::Context::Scope ctx(v8_context_);
+
+ // Add the PAC utility functions to the environment.
+ // (This script should never fail, as it is a string literal!)
+ // Note that the two string literals are concatenated.
+ int rv = RunScript(
+ ASCIILiteralToV8String(
+ PROXY_RESOLVER_SCRIPT
+ PROXY_RESOLVER_SCRIPT_EX),
+ kPacUtilityResourceName);
+ if (rv != OK) {
+ return rv;
+ }
+
+ // Add the user's PAC code to the environment.
+ rv = RunScript(ASCIIStringToV8String(pac_script), kPacResourceName);
+ if (rv != OK) {
+ return rv;
+ }
+
+ // At a minimum, the FindProxyForURL() function must be defined for this
+ // to be a legitimiate PAC script.
+ v8::Local<v8::Value> function;
+ if (!GetFindProxyForURL(&function))
+ return ERR_PAC_SCRIPT_FAILED;
+
+ return OK;
+ }
+
+ void PurgeMemory() {
+ v8::Locker locked;
+ // Repeatedly call the V8 idle notification until it returns true ("nothing
+ // more to free"). Note that it makes more sense to do this than to
+ // implement a new "delete everything" pass because object references make
+ // it difficult to free everything possible in just one pass.
+ while (!v8::V8::IdleNotification())
+ ;
+ }
+
+ private:
+ bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
+ *function = v8_context_->Global()->Get(
+ ASCIILiteralToV8String("FindProxyForURL"));
+ return (*function)->IsFunction();
+ }
+
+ // Handle an exception thrown by V8.
+ void HandleError(v8::Handle<v8::Message> message) {
+ if (message.IsEmpty())
+ return;
+ }
+
+ // Compiles and runs |script| in the current V8 context.
+ // Returns OK on success, otherwise an error code.
+ int RunScript(v8::Handle<v8::String> script, const char* script_name) {
+ v8::TryCatch try_catch;
+
+ // Compile the script.
+ v8::ScriptOrigin origin =
+ v8::ScriptOrigin(ASCIILiteralToV8String(script_name));
+ v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
+
+ // Execute.
+ if (!code.IsEmpty())
+ code->Run();
+
+ // Check for errors.
+ if (try_catch.HasCaught()) {
+ HandleError(try_catch.Message());
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ return OK;
+ }
+
+ // V8 callback for when "alert()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> AlertCallback(const v8::Arguments& args) {
+ Context* context =
+ static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
+
+ // Like firefox we assume "undefined" if no argument was specified, and
+ // disregard any arguments beyond the first.
+ std::wstring message;
+ if (args.Length() == 0) {
+ std::string undef = "undefined";
+ std::wstring wundef(undef.begin(), undef.end());
+ message = wundef;
+ } else {
+ if (!V8ObjectToUTF16String(args[0], &message))
+ return v8::Undefined(); // toString() threw an exception.
+ }
+
+ context->js_bindings_->Alert(message);
+ return v8::Undefined();
+ }
+
+ // V8 callback for when "myIpAddress()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> MyIpAddressCallback(const v8::Arguments& args) {
+ Context* context =
+ static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
+
+ std::string result;
+ bool success;
+
+ {
+ v8::Unlocker unlocker;
+
+ // We shouldn't be called with any arguments, but will not complain if
+ // we are.
+ success = context->js_bindings_->MyIpAddress(&result);
+ }
+
+ if (!success)
+ return ASCIILiteralToV8String("127.0.0.1");
+ return ASCIIStringToV8String(result);
+ }
+
+ // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> MyIpAddressExCallback(
+ const v8::Arguments& args) {
+ Context* context =
+ static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
+
+ std::string ip_address_list;
+ bool success;
+
+ {
+ v8::Unlocker unlocker;
+
+ // We shouldn't be called with any arguments, but will not complain if
+ // we are.
+ success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
+ }
+
+ if (!success)
+ ip_address_list = std::string();
+ return ASCIIStringToV8String(ip_address_list);
+ }
+
+ // V8 callback for when "dnsResolve()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> DnsResolveCallback(const v8::Arguments& args) {
+ Context* context =
+ static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
+
+ // We need at least one string argument.
+ std::string hostname;
+ if (!GetHostnameArgument(args, &hostname))
+ return v8::Null();
+
+ std::string ip_address;
+ bool success;
+
+ {
+ v8::Unlocker unlocker;
+ success = context->js_bindings_->DnsResolve(hostname, &ip_address);
+ }
+
+ return success ? ASCIIStringToV8String(ip_address) : v8::Null();
+ }
+
+ // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> DnsResolveExCallback(const v8::Arguments& args) {
+ Context* context =
+ static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
+
+ // We need at least one string argument.
+ std::string hostname;
+ if (!GetHostnameArgument(args, &hostname))
+ return v8::Undefined();
+
+ std::string ip_address_list;
+ bool success;
+
+ {
+ v8::Unlocker unlocker;
+ success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
+ }
+
+ if (!success)
+ ip_address_list = std::string();
+
+ return ASCIIStringToV8String(ip_address_list);
+ }
+
+ // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> SortIpAddressListCallback(
+ const v8::Arguments& args) {
+ // We need at least one string argument.
+ if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
+ return v8::Null();
+
+ std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
+ std::string sorted_ip_address_list;
+ bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
+ if (!success)
+ return v8::False();
+ return ASCIIStringToV8String(sorted_ip_address_list);
+ }
+
+ // V8 callback for when "isInNetEx()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> IsInNetExCallback(const v8::Arguments& args) {
+ // We need at least 2 string arguments.
+ if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
+ args[1].IsEmpty() || !args[1]->IsString())
+ return v8::Null();
+
+ std::string ip_address = V8StringToUTF8(args[0]->ToString());
+ std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
+ return IsInNetEx(ip_address, ip_prefix) ? v8::True() : v8::False();
+ }
+
+ ProxyResolverJSBindings* js_bindings_;
+ v8::Persistent<v8::External> v8_this_;
+ v8::Persistent<v8::Context> v8_context_;
+};
+
+// ProxyResolverV8 ------------------------------------------------------------
+
+ProxyResolverV8::ProxyResolverV8(
+ ProxyResolverJSBindings* custom_js_bindings)
+ : context_(NULL), js_bindings_(custom_js_bindings) {
+}
+
+ProxyResolverV8::~ProxyResolverV8() {
+
+}
+
+int ProxyResolverV8::GetProxyForURL(const std::string spec, const std::string host,
+ std::string* results) {
+ // If the V8 instance has not been initialized (either because
+ // SetPacScript() wasn't called yet, or because it failed.
+ if (context_ == NULL)
+ return ERR_FAILED;
+
+ // Otherwise call into V8.
+ int rv = context_->ResolveProxy(spec, host, results);
+
+ return rv;
+}
+
+void ProxyResolverV8::CancelRequest(RequestHandle request) {
+}
+
+void ProxyResolverV8::CancelSetPacScript() {
+}
+
+void ProxyResolverV8::PurgeMemory() {
+ context_->PurgeMemory();
+}
+
+void ProxyResolverV8::Shutdown() {
+}
+
+int ProxyResolverV8::SetPacScript(std::string& script_data) {
+ if (context_ != NULL) {
+ delete context_;
+ }
+ if (script_data.empty())
+ return ERR_PAC_SCRIPT_FAILED;
+
+ // Try parsing the PAC script.
+ context_ = new Context(js_bindings_);
+ int rv;
+ if ((rv = context_->InitV8(script_data)) != OK) {
+ context_ = NULL;
+ }
+ if (rv != OK)
+ context_ = NULL;
+ return rv;
+}
+
+} // namespace net
diff --git a/src/proxy_resolver_v8.h b/src/proxy_resolver_v8.h
new file mode 100644
index 0000000..22112bf
--- /dev/null
+++ b/src/proxy_resolver_v8.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_PROXY_PROXY_RESOLVER_V8_H_
+#define NET_PROXY_PROXY_RESOLVER_V8_H_
+#pragma once
+#include "proxy_resolver_js_bindings.h"
+
+namespace net {
+
+typedef void* RequestHandle;
+typedef void* CompletionCallback;
+
+#define OK 0
+#define ERR_PAC_SCRIPT_FAILED -1
+#define ERR_FAILED -2
+
+// Implementation of ProxyResolver that uses V8 to evaluate PAC scripts.
+//
+// ----------------------------------------------------------------------------
+// !!! Important note on threading model:
+// ----------------------------------------------------------------------------
+// There can be only one instance of V8 running at a time. To enforce this
+// constraint, ProxyResolverV8 holds a v8::Locker during execution. Therefore
+// it is OK to run multiple instances of ProxyResolverV8 on different threads,
+// since only one will be running inside V8 at a time.
+//
+// It is important that *ALL* instances of V8 in the process be using
+// v8::Locker. If not there can be race conditions beween the non-locked V8
+// instances and the locked V8 instances used by ProxyResolverV8 (assuming they
+// run on different threads).
+//
+// This is the case with the V8 instance used by chromium's renderer -- it runs
+// on a different thread from ProxyResolver (renderer thread vs PAC thread),
+// and does not use locking since it expects to be alone.
+class ProxyResolverV8 {
+ public:
+ // Constructs a ProxyResolverV8 with custom bindings. ProxyResolverV8 takes
+ // ownership of |custom_js_bindings| and deletes it when ProxyResolverV8
+ // is destroyed.
+ explicit ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings);
+
+ virtual ~ProxyResolverV8();
+
+ ProxyResolverJSBindings* js_bindings() { return js_bindings_; }
+
+ virtual int GetProxyForURL(const std::string spec, const std::string host,
+ std::string* results);
+ virtual void CancelRequest(RequestHandle request);
+ virtual void CancelSetPacScript();
+ virtual void PurgeMemory();
+ virtual void Shutdown();
+ virtual int SetPacScript(std::string& script_data);
+
+ private:
+ // Context holds the Javascript state for the most recently loaded PAC
+ // script. It corresponds with the data from the last call to
+ // SetPacScript().
+ class Context;
+ Context* context_;
+
+ ProxyResolverJSBindings* js_bindings_;
+};
+
+} // namespace net
+
+#endif // NET_PROXY_PROXY_RESOLVER_V8_H_