// 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.
|
|
#ifdef ANDROID
|
#define LOG_TAG "libpac"
|
#include <utils/Log.h>
|
#endif
|
|
#include "proxy_resolver_v8.h"
|
|
#include <algorithm>
|
#include <cstdio>
|
#include <iostream>
|
#include <string>
|
#include <v8.h>
|
#include <libplatform/libplatform.h>
|
#include <vector>
|
|
#include "net_util.h"
|
#include "proxy_resolver_script.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::u16string& str) {
|
for (size_t i = 0; i < str.length(); i++) {
|
unsigned short c = str.data()[i];
|
if (c > 0x7F)
|
return false;
|
}
|
return true;
|
}
|
|
bool IsStringASCII(const std::u16string& str) {
|
return DoIsStringASCII(str);
|
}
|
|
#ifdef ANDROID
|
#define LOG_ALERT(...) ALOGD(__VA_ARGS__)
|
#define LOG_ERROR(...) ALOGE(__VA_ARGS__)
|
#else
|
#define LOG_ALERT(...) vprintf(__VA_ARGS__)
|
#define LOG_ERROR(...) vfprintf(stderr, __VA_ARGS__)
|
#endif
|
|
namespace net {
|
|
namespace {
|
|
class DefaultProxyErrorListener : public ProxyErrorListener {
|
public:
|
~DefaultProxyErrorListener() {}
|
void AlertMessage(const std::string& message) {
|
LOG_ALERT("Alert: %s", message.data());
|
}
|
void ErrorMessage(const std::string& message) {
|
LOG_ERROR("Error: %s", message.data());
|
}
|
};
|
|
ProxyErrorListener* const defaultProxyErrorListener = new DefaultProxyErrorListener();
|
|
// 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 a string literal.
|
class V8ExternalASCIILiteral
|
: public v8::String::ExternalOneByteStringResource {
|
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::u16string V8StringToUTF16(v8::Handle<v8::String> s) {
|
int len = s->Length();
|
char16_t* buf = new char16_t[len + 1];
|
s->Write(reinterpret_cast<uint16_t*>(buf), 0, len);
|
std::u16string ret(buf, len);
|
delete[] buf;
|
return ret;
|
}
|
|
// Converts an ASCII std::string to a V8 string.
|
v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
|
return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
|
}
|
|
v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const std::u16string& s) {
|
return v8::String::NewFromTwoByte(
|
isolate, reinterpret_cast<const uint16_t*>(s.data()),
|
v8::String::kNormalString, s.size());
|
}
|
|
// Converts an ASCII string literal to a V8 string.
|
v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) {
|
// DCHECK(IsStringASCII(ascii));
|
size_t length = strlen(ascii);
|
if (length <= kMaxStringBytesForCopy)
|
return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length);
|
return v8::String::NewExternal(isolate, 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 V8ObjectToUTF8String(v8::Handle<v8::Value> object,
|
std::string* utf8_result,
|
v8::Isolate* isolate) {
|
if (object.IsEmpty())
|
return false;
|
|
v8::HandleScope scope(isolate);
|
v8::Local<v8::String> str_object = object->ToString();
|
if (str_object.IsEmpty())
|
return false;
|
*utf8_result = V8StringToUTF8(str_object);
|
return true;
|
}
|
|
// Extracts an hostname argument from |args|. On success returns true
|
// and fills |*hostname| with the result.
|
bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& 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::u16string hostname_utf16 = V8StringToUTF16(args[0]->ToString());
|
|
// If the hostname is already in ASCII, simply return it as is.
|
if (IsStringASCII(hostname_utf16)) {
|
*hostname = V8StringToUTF8(args[0]->ToString());
|
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::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(NULL, ";");
|
}
|
|
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;
|
std::string cleaned_ip_address;
|
if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
|
return false;
|
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
|
|
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
|
public:
|
virtual void* Allocate(size_t length) {
|
void* data = AllocateUninitialized(length);
|
return data == NULL ? data : memset(data, 0, length);
|
}
|
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
|
virtual void Free(void* data, size_t) { free(data); }
|
};
|
|
|
// ProxyResolverV8::Context ---------------------------------------------------
|
|
class ProxyResolverV8::Context {
|
public:
|
explicit Context(ProxyResolverJSBindings* js_bindings,
|
ProxyErrorListener* error_listener, v8::Isolate* isolate)
|
: js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) {
|
}
|
|
~Context() {
|
v8::Locker locked(isolate_);
|
v8::Isolate::Scope isolate_scope(isolate_);
|
|
v8_this_.Reset();
|
v8_context_.Reset();
|
}
|
|
int ResolveProxy(const std::u16string url, const std::u16string host,
|
std::u16string* results) {
|
v8::Locker locked(isolate_);
|
v8::Isolate::Scope isolate_scope(isolate_);
|
v8::HandleScope scope(isolate_);
|
|
v8::Local<v8::Context> context =
|
v8::Local<v8::Context>::New(isolate_, v8_context_);
|
v8::Context::Scope function_scope(context);
|
|
v8::Local<v8::Value> function;
|
if (!GetFindProxyForURL(&function)) {
|
error_listener_->ErrorMessage("FindProxyForURL() is undefined");
|
return ERR_PAC_SCRIPT_FAILED;
|
}
|
|
v8::Handle<v8::Value> argv[] = {
|
UTF16StringToV8String(isolate_, url),
|
UTF16StringToV8String(isolate_, host) };
|
|
v8::TryCatch try_catch(isolate_);
|
v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
|
context->Global(), 2, argv);
|
|
if (try_catch.HasCaught()) {
|
error_listener_->ErrorMessage(V8StringToUTF8(try_catch.Message()->Get()));
|
return ERR_PAC_SCRIPT_FAILED;
|
}
|
|
if (!ret->IsString()) {
|
error_listener_->ErrorMessage("FindProxyForURL() did not return a string.");
|
return ERR_PAC_SCRIPT_FAILED;
|
}
|
|
*results = V8StringToUTF16(ret->ToString());
|
|
if (!IsStringASCII(*results)) {
|
// 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
|
error_listener_->ErrorMessage("FindProxyForURL() returned a non-ASCII string");
|
return ERR_PAC_SCRIPT_FAILED;
|
}
|
|
return OK;
|
}
|
|
int InitV8(const std::u16string& pac_script) {
|
v8::Locker locked(isolate_);
|
v8::Isolate::Scope isolate_scope(isolate_);
|
v8::HandleScope scope(isolate_);
|
|
v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
|
v8::Local<v8::External> v8_this =
|
v8::Local<v8::External>::New(isolate_, v8_this_);
|
v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate_);
|
|
// Attach the javascript bindings.
|
v8::Local<v8::FunctionTemplate> alert_template =
|
v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
|
global_template->Set(ASCIILiteralToV8String(isolate_, "alert"), alert_template);
|
|
v8::Local<v8::FunctionTemplate> my_ip_address_template =
|
v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
|
global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
|
my_ip_address_template);
|
|
v8::Local<v8::FunctionTemplate> dns_resolve_template =
|
v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
|
global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
|
dns_resolve_template);
|
|
// Microsoft's PAC extensions:
|
|
v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
|
v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
|
global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
|
dns_resolve_ex_template);
|
|
v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
|
v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
|
global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
|
my_ip_address_ex_template);
|
|
v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
|
v8::FunctionTemplate::New(isolate_, &SortIpAddressListCallback, v8_this);
|
global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
|
sort_ip_address_list_template);
|
|
v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
|
v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
|
global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
|
is_in_net_ex_template);
|
|
v8_context_.Reset(
|
isolate_, v8::Context::New(isolate_, NULL, global_template));
|
|
v8::Local<v8::Context> context =
|
v8::Local<v8::Context>::New(isolate_, v8_context_);
|
v8::Context::Scope ctx(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(isolate_,
|
PROXY_RESOLVER_SCRIPT
|
PROXY_RESOLVER_SCRIPT_EX),
|
kPacUtilityResourceName);
|
if (rv != OK) {
|
return rv;
|
}
|
|
// Add the user's PAC code to the environment.
|
rv = RunScript(UTF16StringToV8String(isolate_, 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(isolate_);
|
v8::Isolate::Scope isolate_scope(isolate_);
|
isolate_->LowMemoryNotification();
|
}
|
|
private:
|
bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
|
v8::Local<v8::Context> context =
|
v8::Local<v8::Context>::New(isolate_, v8_context_);
|
*function = context->Global()->Get(
|
ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
|
return (*function)->IsFunction();
|
}
|
|
// Handle an exception thrown by V8.
|
void HandleError(v8::Handle<v8::Message> message) {
|
if (message.IsEmpty())
|
return;
|
error_listener_->ErrorMessage(V8StringToUTF8(message->Get()));
|
}
|
|
// 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::Local<v8::Context> context =
|
v8::Local<v8::Context>::New(isolate_, v8_context_);
|
v8::TryCatch try_catch(isolate_);
|
|
// Compile the script.
|
v8::ScriptOrigin origin =
|
v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
|
v8::MaybeLocal<v8::Script> code = v8::Script::Compile(context, script, &origin);
|
|
// Execute.
|
if (!code.IsEmpty())
|
code.ToLocalChecked()->Run(context);
|
|
// 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 void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& 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::string message;
|
if (args.Length() == 0) {
|
message = "undefined";
|
} else {
|
if (!V8ObjectToUTF8String(args[0], &message, args.GetIsolate()))
|
return; // toString() threw an exception.
|
}
|
|
context->error_listener_->AlertMessage(message);
|
return;
|
}
|
|
// V8 callback for when "myIpAddress()" is invoked by the PAC script.
|
static void MyIpAddressCallback(
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
Context* context =
|
static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
|
|
std::string result;
|
bool success;
|
|
{
|
v8::Unlocker unlocker(args.GetIsolate());
|
|
// We shouldn't be called with any arguments, but will not complain if
|
// we are.
|
success = context->js_bindings_->MyIpAddress(&result);
|
}
|
|
if (!success) {
|
args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
|
} else {
|
args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result));
|
}
|
}
|
|
// V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
|
static void MyIpAddressExCallback(
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
Context* context =
|
static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
|
|
std::string ip_address_list;
|
bool success;
|
|
{
|
v8::Unlocker unlocker(args.GetIsolate());
|
|
// 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();
|
args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
|
}
|
|
// V8 callback for when "dnsResolve()" is invoked by the PAC script.
|
static void DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& 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;
|
}
|
|
std::string ip_address;
|
bool success;
|
|
{
|
v8::Unlocker unlocker(args.GetIsolate());
|
success = context->js_bindings_->DnsResolve(hostname, &ip_address);
|
}
|
|
if (success) {
|
args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address));
|
} else {
|
args.GetReturnValue().SetNull();
|
}
|
}
|
|
// V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
|
static void DnsResolveExCallback(
|
const v8::FunctionCallbackInfo<v8::Value>& 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)) {
|
args.GetReturnValue().SetNull();
|
return;
|
}
|
|
std::string ip_address_list;
|
bool success;
|
|
{
|
v8::Unlocker unlocker(args.GetIsolate());
|
success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
|
}
|
|
if (!success)
|
ip_address_list = std::string();
|
|
args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
|
}
|
|
// V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
|
static void SortIpAddressListCallback(
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
// We need at least one string argument.
|
if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
|
args.GetReturnValue().SetNull();
|
return;
|
}
|
|
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) {
|
args.GetReturnValue().Set(false);
|
return;
|
}
|
args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
|
}
|
|
// V8 callback for when "isInNetEx()" is invoked by the PAC script.
|
static void IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
// We need at least 2 string arguments.
|
if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
|
args[1].IsEmpty() || !args[1]->IsString()) {
|
args.GetReturnValue().SetNull();
|
return;
|
}
|
|
std::string ip_address = V8StringToUTF8(args[0]->ToString());
|
std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
|
args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
|
}
|
|
ProxyResolverJSBindings* js_bindings_;
|
ProxyErrorListener* error_listener_;
|
v8::Isolate* isolate_;
|
v8::Persistent<v8::External> v8_this_;
|
v8::Persistent<v8::Context> v8_context_;
|
};
|
|
// ProxyResolverV8 ------------------------------------------------------------
|
|
bool ProxyResolverV8::initialized_for_this_process_ = false;
|
|
ProxyResolverV8::ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings)
|
: ProxyResolverV8(custom_js_bindings, defaultProxyErrorListener) {
|
}
|
|
ProxyResolverV8::ProxyResolverV8(
|
ProxyResolverJSBindings* custom_js_bindings,
|
ProxyErrorListener* error_listener)
|
: context_(NULL), js_bindings_(custom_js_bindings),
|
error_listener_(error_listener) {
|
if (!initialized_for_this_process_) {
|
v8::Platform* platform = v8::platform::CreateDefaultPlatform();
|
v8::V8::InitializePlatform(platform);
|
v8::V8::Initialize();
|
initialized_for_this_process_ = true;
|
}
|
}
|
|
ProxyResolverV8::~ProxyResolverV8() {
|
if (context_ != NULL) {
|
delete context_;
|
context_ = NULL;
|
}
|
if (js_bindings_ != NULL) {
|
delete js_bindings_;
|
}
|
}
|
|
int ProxyResolverV8::GetProxyForURL(const std::u16string& spec, const std::u16string& host,
|
std::u16string* results) {
|
// If the V8 instance has not been initialized (either because
|
// SetPacScript() wasn't called yet, or because it failed.
|
if (context_ == NULL) {
|
error_listener_->ErrorMessage(std::string("Context is null."));
|
return ERR_FAILED;
|
}
|
// Otherwise call into V8.
|
int rv = context_->ResolveProxy(spec, host, results);
|
|
return rv;
|
}
|
|
void ProxyResolverV8::PurgeMemory() {
|
context_->PurgeMemory();
|
}
|
|
int ProxyResolverV8::SetPacScript(const std::u16string& script_data) {
|
if (context_ != NULL) {
|
delete context_;
|
context_ = NULL;
|
}
|
if (script_data.length() == 0)
|
return ERR_PAC_SCRIPT_FAILED;
|
|
// To help debugging v8 initialization issues, add "--log_all --print_all_exceptions"
|
// to the options
|
// Disable JIT
|
static const char kNoOpt[] = "--no-opt";
|
v8::V8::SetFlagsFromString(kNoOpt, strlen(kNoOpt));
|
|
// Try parsing the PAC script.
|
v8::Isolate::CreateParams create_params;
|
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
|
|
context_ = new Context(js_bindings_, error_listener_, v8::Isolate::New(create_params));
|
int rv;
|
if ((rv = context_->InitV8(script_data)) != OK) {
|
context_ = NULL;
|
}
|
if (rv != OK)
|
context_ = NULL;
|
return rv;
|
}
|
|
} // namespace net
|