blob: 7e52dc671468b09f9a82b604fb6f623e5272109c [file] [log] [blame]
Jason Monkfc934182013-07-22 13:20:39 -04001// 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
Jason Monkfc934182013-07-22 13:20:39 -04005#include "proxy_resolver_v8.h"
6
Jason Monkfc934182013-07-22 13:20:39 -04007#include <algorithm>
Ben Murdoche6933f52014-12-05 12:45:49 +00008#include <cstdio>
9#include <iostream>
10#include <string>
11#include <utils/String8.h>
12#include <v8.h>
Jason Monkfc934182013-07-22 13:20:39 -040013#include <vector>
14
Ben Murdoche6933f52014-12-05 12:45:49 +000015#include "net_util.h"
16#include "proxy_resolver_script.h"
Jason Monkfc934182013-07-22 13:20:39 -040017
18// Notes on the javascript environment:
19//
20// For the majority of the PAC utility functions, we use the same code
21// as Firefox. See the javascript library that proxy_resolver_scipt.h
22// pulls in.
23//
24// In addition, we implement a subset of Microsoft's extensions to PAC.
25// - myIpAddressEx()
26// - dnsResolveEx()
27// - isResolvableEx()
28// - isInNetEx()
29// - sortIpAddressList()
30//
31// It is worth noting that the original PAC specification does not describe
32// the return values on failure. Consequently, there are compatibility
33// differences between browsers on what to return on failure, which are
34// illustrated below:
35//
36// --------------------+-------------+-------------------+--------------
37// | Firefox3 | InternetExplorer8 | --> Us <---
38// --------------------+-------------+-------------------+--------------
39// myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
40// dnsResolve() | null | false | null
41// myIpAddressEx() | N/A | "" | ""
42// sortIpAddressList() | N/A | false | false
43// dnsResolveEx() | N/A | "" | ""
44// isInNetEx() | N/A | false | false
45// --------------------+-------------+-------------------+--------------
46//
47// TODO: The cell above reading ??? means I didn't test it.
48//
49// Another difference is in how dnsResolve() and myIpAddress() are
50// implemented -- whether they should restrict to IPv4 results, or
51// include both IPv4 and IPv6. The following table illustrates the
52// differences:
53//
54// --------------------+-------------+-------------------+--------------
55// | Firefox3 | InternetExplorer8 | --> Us <---
56// --------------------+-------------+-------------------+--------------
57// myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
58// dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
59// isResolvable() | IPv4/IPv6 | IPv4 | IPv4
60// myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
61// dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
62// sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
63// isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
64// isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
65// -----------------+-------------+-------------------+--------------
66
Jason Monkf5cf6e32013-07-23 17:26:23 -040067static bool DoIsStringASCII(const android::String16& str) {
68 for (size_t i = 0; i < str.size(); i++) {
69 unsigned short c = str.string()[i];
Jason Monkfc934182013-07-22 13:20:39 -040070 if (c > 0x7F)
71 return false;
72 }
73 return true;
74}
75
Jason Monkf5cf6e32013-07-23 17:26:23 -040076bool IsStringASCII(const android::String16& str) {
Jason Monkfc934182013-07-22 13:20:39 -040077 return DoIsStringASCII(str);
78}
79
Jason Monkfc934182013-07-22 13:20:39 -040080namespace net {
81
82namespace {
83
84// Pseudo-name for the PAC script.
85const char kPacResourceName[] = "proxy-pac-script.js";
86// Pseudo-name for the PAC utility script.
87const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
88
89// External string wrapper so V8 can access the UTF16 string wrapped by
90// ProxyResolverScriptData.
91class V8ExternalStringFromScriptData
92 : public v8::String::ExternalStringResource {
93 public:
94 explicit V8ExternalStringFromScriptData(
Jason Monkf5cf6e32013-07-23 17:26:23 -040095 const android::String16& script_data)
Jason Monkfc934182013-07-22 13:20:39 -040096 : script_data_(script_data) {}
97
98 virtual const uint16_t* data() const {
Dan Albertd438de82014-11-20 11:20:39 -080099 return reinterpret_cast<const uint16_t*>(script_data_.string());
Jason Monkfc934182013-07-22 13:20:39 -0400100 }
101
102 virtual size_t length() const {
103 return script_data_.size();
104 }
105
106 private:
Jason Monkf5cf6e32013-07-23 17:26:23 -0400107 const android::String16& script_data_;
Jason Monkfc934182013-07-22 13:20:39 -0400108// DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
109};
110
111// External string wrapper so V8 can access a string literal.
Torne (Richard Coles)9ae4ac52014-10-22 11:19:47 +0100112class V8ExternalASCIILiteral
113 : public v8::String::ExternalOneByteStringResource {
Jason Monkfc934182013-07-22 13:20:39 -0400114 public:
115 // |ascii| must be a NULL-terminated C string, and must remain valid
116 // throughout this object's lifetime.
117 V8ExternalASCIILiteral(const char* ascii, size_t length)
118 : ascii_(ascii), length_(length) {
Jason Monkfc934182013-07-22 13:20:39 -0400119 }
120
121 virtual const char* data() const {
122 return ascii_;
123 }
124
125 virtual size_t length() const {
126 return length_;
127 }
128
129 private:
130 const char* ascii_;
131 size_t length_;
132};
133
134// When creating a v8::String from a C++ string we have two choices: create
135// a copy, or create a wrapper that shares the same underlying storage.
136// For small strings it is better to just make a copy, whereas for large
137// strings there are savings by sharing the storage. This number identifies
138// the cutoff length for when to start wrapping rather than creating copies.
139const size_t kMaxStringBytesForCopy = 256;
140
141template <class string_type>
142inline typename string_type::value_type* WriteInto(string_type* str,
143 size_t length_with_null) {
144 str->reserve(length_with_null);
145 str->resize(length_with_null - 1);
146 return &((*str)[0]);
147}
148
149// Converts a V8 String to a UTF8 std::string.
150std::string V8StringToUTF8(v8::Handle<v8::String> s) {
151 std::string result;
152 s->WriteUtf8(WriteInto(&result, s->Length() + 1));
153 return result;
154}
155
156// Converts a V8 String to a UTF16 string.
Jason Monkf5cf6e32013-07-23 17:26:23 -0400157android::String16 V8StringToUTF16(v8::Handle<v8::String> s) {
Jason Monkfc934182013-07-22 13:20:39 -0400158 int len = s->Length();
Jason Monkf5cf6e32013-07-23 17:26:23 -0400159 char16_t* buf = new char16_t[len + 1];
Dan Albertd438de82014-11-20 11:20:39 -0800160 s->Write(reinterpret_cast<uint16_t*>(buf), 0, len);
Jason Monkf5cf6e32013-07-23 17:26:23 -0400161 android::String16 ret(buf, len);
162 delete buf;
163 return ret;
164}
165
166std::string UTF16ToASCII(const android::String16& str) {
Jason Monk40adcb52013-08-23 17:19:31 -0400167 android::String8 rstr(str);
168 return std::string(rstr.string());
Jason Monkfc934182013-07-22 13:20:39 -0400169}
170
171// Converts an ASCII std::string to a V8 string.
Jason Monk9fd69be2014-03-18 11:55:59 -0400172v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
173 return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
Jason Monkfc934182013-07-22 13:20:39 -0400174}
175
Jason Monk9fd69be2014-03-18 11:55:59 -0400176v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const android::String16& s) {
Dan Albertd438de82014-11-20 11:20:39 -0800177 return v8::String::NewFromTwoByte(
178 isolate, reinterpret_cast<const uint16_t*>(s.string()),
179 v8::String::kNormalString, s.size());
Jason Monkf5cf6e32013-07-23 17:26:23 -0400180}
181
Jason Monkfc934182013-07-22 13:20:39 -0400182// Converts an ASCII string literal to a V8 string.
Jason Monk9fd69be2014-03-18 11:55:59 -0400183v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) {
Jason Monkfc934182013-07-22 13:20:39 -0400184// DCHECK(IsStringASCII(ascii));
185 size_t length = strlen(ascii);
186 if (length <= kMaxStringBytesForCopy)
Jason Monk9fd69be2014-03-18 11:55:59 -0400187 return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length);
188 return v8::String::NewExternal(isolate, new V8ExternalASCIILiteral(ascii, length));
Jason Monkfc934182013-07-22 13:20:39 -0400189}
190
191// Stringizes a V8 object by calling its toString() method. Returns true
192// on success. This may fail if the toString() throws an exception.
193bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
Jason Monk9fd69be2014-03-18 11:55:59 -0400194 android::String16* utf16_result,
195 v8::Isolate* isolate) {
Jason Monkfc934182013-07-22 13:20:39 -0400196 if (object.IsEmpty())
197 return false;
198
Jason Monk9fd69be2014-03-18 11:55:59 -0400199 v8::HandleScope scope(isolate);
Jason Monkfc934182013-07-22 13:20:39 -0400200 v8::Local<v8::String> str_object = object->ToString();
201 if (str_object.IsEmpty())
202 return false;
203 *utf16_result = V8StringToUTF16(str_object);
204 return true;
205}
206
207// Extracts an hostname argument from |args|. On success returns true
208// and fills |*hostname| with the result.
Jason Monk9fd69be2014-03-18 11:55:59 -0400209bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args, std::string* hostname) {
Jason Monkfc934182013-07-22 13:20:39 -0400210 // The first argument should be a string.
211 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
212 return false;
213
Jason Monkf5cf6e32013-07-23 17:26:23 -0400214 const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400215
216 // If the hostname is already in ASCII, simply return it as is.
217 if (IsStringASCII(hostname_utf16)) {
218 *hostname = UTF16ToASCII(hostname_utf16);
219 return true;
220 }
221 return false;
222}
223
224// Wrapper for passing around IP address strings and IPAddressNumber objects.
225struct IPAddress {
226 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
227 : string_value(ip_string),
228 ip_address_number(ip_number) {
229 }
230
231 // Used for sorting IP addresses in ascending order in SortIpAddressList().
232 // IP6 addresses are placed ahead of IPv4 addresses.
233 bool operator<(const IPAddress& rhs) const {
234 const IPAddressNumber& ip1 = this->ip_address_number;
235 const IPAddressNumber& ip2 = rhs.ip_address_number;
236 if (ip1.size() != ip2.size())
237 return ip1.size() > ip2.size(); // IPv6 before IPv4.
238 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
239 }
240
241 std::string string_value;
242 IPAddressNumber ip_address_number;
243};
244
245template<typename STR>
246bool RemoveCharsT(const STR& input,
247 const typename STR::value_type remove_chars[],
248 STR* output) {
249 bool removed = false;
250 size_t found;
251
252 *output = input;
253
254 found = output->find_first_of(remove_chars);
255 while (found != STR::npos) {
256 removed = true;
257 output->replace(found, 1, STR());
258 found = output->find_first_of(remove_chars, found);
259 }
260
261 return removed;
262}
263
Jason Monkfc934182013-07-22 13:20:39 -0400264bool RemoveChars(const std::string& input,
265 const char remove_chars[],
266 std::string* output) {
267 return RemoveCharsT(input, remove_chars, output);
268}
269
270// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
271// semi-colon delimited string containing IP addresses.
272// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
273// IP addresses or an empty string if unable to sort the IP address list.
274// Returns 'true' if the sorting was successful, and 'false' if the input was an
275// empty string, a string of separators (";" in this case), or if any of the IP
276// addresses in the input list failed to parse.
277bool SortIpAddressList(const std::string& ip_address_list,
278 std::string* sorted_ip_address_list) {
279 sorted_ip_address_list->clear();
280
281 // Strip all whitespace (mimics IE behavior).
282 std::string cleaned_ip_address_list;
283 RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
284 if (cleaned_ip_address_list.empty())
285 return false;
286
287 // Split-up IP addresses and store them in a vector.
288 std::vector<IPAddress> ip_vector;
289 IPAddressNumber ip_num;
290 char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
291 while (tok_list != NULL) {
292 if (!ParseIPLiteralToNumber(tok_list, &ip_num))
293 return false;
294 ip_vector.push_back(IPAddress(tok_list, ip_num));
Jason Monk86b75a52013-08-23 17:10:51 -0400295 tok_list = strtok(NULL, ";");
Jason Monkfc934182013-07-22 13:20:39 -0400296 }
297
298 if (ip_vector.empty()) // Can happen if we have something like
299 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
300
301 // Sort lists according to ascending numeric value.
302 if (ip_vector.size() > 1)
303 std::stable_sort(ip_vector.begin(), ip_vector.end());
304
305 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
306 // IPv4).
307 for (size_t i = 0; i < ip_vector.size(); ++i) {
308 if (i > 0)
309 *sorted_ip_address_list += ";";
310 *sorted_ip_address_list += ip_vector[i].string_value;
311 }
312 return true;
313}
314
315
316// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
317// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
318// slash-delimited IP prefix with the top 'n' bits specified in the bit
319// field. This returns 'true' if the address is in the same subnet, and
320// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
321// format, or if an address and prefix of different types are used (e.g. IPv6
322// address and IPv4 prefix).
323bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
324 IPAddressNumber address;
Jason Monkeafc0fa2013-08-23 17:05:52 -0400325 std::string cleaned_ip_address;
326 if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
327 return false;
Jason Monkfc934182013-07-22 13:20:39 -0400328 if (!ParseIPLiteralToNumber(ip_address, &address))
329 return false;
330
331 IPAddressNumber prefix;
332 size_t prefix_length_in_bits;
333 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
334 return false;
335
336 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
337 if (address.size() != prefix.size())
338 return false;
339
340 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
341}
342
343} // namespace
344
345// ProxyResolverV8::Context ---------------------------------------------------
346
347class ProxyResolverV8::Context {
348 public:
Jason Monkf5cf6e32013-07-23 17:26:23 -0400349 explicit Context(ProxyResolverJSBindings* js_bindings,
Jason Monk9fd69be2014-03-18 11:55:59 -0400350 ProxyErrorListener* error_listener, v8::Isolate* isolate)
351 : js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) {
Jason Monkfc934182013-07-22 13:20:39 -0400352 }
353
354 ~Context() {
Jason Monk9fd69be2014-03-18 11:55:59 -0400355 v8::Locker locked(isolate_);
356 v8::Isolate::Scope isolate_scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400357
Jason Monk9fd69be2014-03-18 11:55:59 -0400358 v8_this_.Reset();
359 v8_context_.Reset();
Jason Monkfc934182013-07-22 13:20:39 -0400360 }
361
Jason Monk40adcb52013-08-23 17:19:31 -0400362 int ResolveProxy(const android::String16 url, const android::String16 host,
363 android::String16* results) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400364 v8::Locker locked(isolate_);
365 v8::Isolate::Scope isolate_scope(isolate_);
366 v8::HandleScope scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400367
Jason Monk9fd69be2014-03-18 11:55:59 -0400368 v8::Local<v8::Context> context =
369 v8::Local<v8::Context>::New(isolate_, v8_context_);
370 v8::Context::Scope function_scope(context);
Jason Monkfc934182013-07-22 13:20:39 -0400371
372 v8::Local<v8::Value> function;
373 if (!GetFindProxyForURL(&function)) {
Jason Monk40adcb52013-08-23 17:19:31 -0400374 error_listener_->ErrorMessage(
375 android::String16("FindProxyForURL() is undefined"));
Jason Monkfc934182013-07-22 13:20:39 -0400376 return ERR_PAC_SCRIPT_FAILED;
377 }
378
379 v8::Handle<v8::Value> argv[] = {
Jason Monk9fd69be2014-03-18 11:55:59 -0400380 UTF16StringToV8String(isolate_, url),
381 UTF16StringToV8String(isolate_, host) };
Jason Monkfc934182013-07-22 13:20:39 -0400382
383 v8::TryCatch try_catch;
384 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
Jason Monk9fd69be2014-03-18 11:55:59 -0400385 context->Global(), 2, argv);
Jason Monkfc934182013-07-22 13:20:39 -0400386
387 if (try_catch.HasCaught()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400388 error_listener_->ErrorMessage(
389 V8StringToUTF16(try_catch.Message()->Get()));
Jason Monkfc934182013-07-22 13:20:39 -0400390 return ERR_PAC_SCRIPT_FAILED;
391 }
392
393 if (!ret->IsString()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400394 error_listener_->ErrorMessage(
395 android::String16("FindProxyForURL() did not return a string."));
Jason Monkfc934182013-07-22 13:20:39 -0400396 return ERR_PAC_SCRIPT_FAILED;
397 }
398
Jason Monkf5cf6e32013-07-23 17:26:23 -0400399 *results = V8StringToUTF16(ret->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400400
Jason Monkf5cf6e32013-07-23 17:26:23 -0400401 if (!IsStringASCII(*results)) {
Jason Monkfc934182013-07-22 13:20:39 -0400402 // TODO: Rather than failing when a wide string is returned, we
403 // could extend the parsing to handle IDNA hostnames by
404 // converting them to ASCII punycode.
405 // crbug.com/47234
Jason Monk40adcb52013-08-23 17:19:31 -0400406 error_listener_->ErrorMessage(
407 android::String16("FindProxyForURL() returned a non-ASCII string"));
Jason Monkfc934182013-07-22 13:20:39 -0400408 return ERR_PAC_SCRIPT_FAILED;
409 }
410
Jason Monkfc934182013-07-22 13:20:39 -0400411 return OK;
412 }
413
Jason Monkf5cf6e32013-07-23 17:26:23 -0400414 int InitV8(const android::String16& pac_script) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400415 v8::Locker locked(isolate_);
416 v8::Isolate::Scope isolate_scope(isolate_);
417 v8::HandleScope scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400418
Jason Monk9fd69be2014-03-18 11:55:59 -0400419 v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
420 v8::Local<v8::External> v8_this =
421 v8::Local<v8::External>::New(isolate_, v8_this_);
Jason Monkfc934182013-07-22 13:20:39 -0400422 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
423
424 // Attach the javascript bindings.
425 v8::Local<v8::FunctionTemplate> alert_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400426 v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
427 global_template->Set(ASCIILiteralToV8String(isolate_, "alert"), alert_template);
Jason Monkfc934182013-07-22 13:20:39 -0400428
429 v8::Local<v8::FunctionTemplate> my_ip_address_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400430 v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
431 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
Jason Monkfc934182013-07-22 13:20:39 -0400432 my_ip_address_template);
433
434 v8::Local<v8::FunctionTemplate> dns_resolve_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400435 v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
436 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
Jason Monkfc934182013-07-22 13:20:39 -0400437 dns_resolve_template);
438
439 // Microsoft's PAC extensions:
440
441 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400442 v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
443 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400444 dns_resolve_ex_template);
445
446 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400447 v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
448 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400449 my_ip_address_ex_template);
450
451 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400452 v8::FunctionTemplate::New(isolate_, &SortIpAddressListCallback, v8_this);
453 global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
Jason Monkfc934182013-07-22 13:20:39 -0400454 sort_ip_address_list_template);
455
456 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400457 v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
458 global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400459 is_in_net_ex_template);
460
Jason Monk9fd69be2014-03-18 11:55:59 -0400461 v8_context_.Reset(
462 isolate_, v8::Context::New(isolate_, NULL, global_template));
Jason Monkfc934182013-07-22 13:20:39 -0400463
Jason Monk9fd69be2014-03-18 11:55:59 -0400464 v8::Local<v8::Context> context =
465 v8::Local<v8::Context>::New(isolate_, v8_context_);
466 v8::Context::Scope ctx(context);
Jason Monkfc934182013-07-22 13:20:39 -0400467
468 // Add the PAC utility functions to the environment.
469 // (This script should never fail, as it is a string literal!)
470 // Note that the two string literals are concatenated.
471 int rv = RunScript(
Jason Monk9fd69be2014-03-18 11:55:59 -0400472 ASCIILiteralToV8String(isolate_,
Jason Monkfc934182013-07-22 13:20:39 -0400473 PROXY_RESOLVER_SCRIPT
474 PROXY_RESOLVER_SCRIPT_EX),
475 kPacUtilityResourceName);
476 if (rv != OK) {
477 return rv;
478 }
479
480 // Add the user's PAC code to the environment.
Jason Monk9fd69be2014-03-18 11:55:59 -0400481 rv = RunScript(UTF16StringToV8String(isolate_, pac_script), kPacResourceName);
Jason Monkfc934182013-07-22 13:20:39 -0400482 if (rv != OK) {
483 return rv;
484 }
485
486 // At a minimum, the FindProxyForURL() function must be defined for this
487 // to be a legitimiate PAC script.
488 v8::Local<v8::Value> function;
489 if (!GetFindProxyForURL(&function))
490 return ERR_PAC_SCRIPT_FAILED;
491
492 return OK;
493 }
494
495 void PurgeMemory() {
Jason Monk9fd69be2014-03-18 11:55:59 -0400496 v8::Locker locked(isolate_);
497 v8::Isolate::Scope isolate_scope(isolate_);
Ben Murdoche80b5fa2014-07-31 16:47:35 +0100498 isolate_->LowMemoryNotification();
Jason Monkfc934182013-07-22 13:20:39 -0400499 }
500
501 private:
502 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400503 v8::Local<v8::Context> context =
504 v8::Local<v8::Context>::New(isolate_, v8_context_);
505 *function = context->Global()->Get(
506 ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
Jason Monkfc934182013-07-22 13:20:39 -0400507 return (*function)->IsFunction();
508 }
509
510 // Handle an exception thrown by V8.
511 void HandleError(v8::Handle<v8::Message> message) {
512 if (message.IsEmpty())
513 return;
Jason Monkf5cf6e32013-07-23 17:26:23 -0400514 error_listener_->ErrorMessage(V8StringToUTF16(message->Get()));
Jason Monkfc934182013-07-22 13:20:39 -0400515 }
516
517 // Compiles and runs |script| in the current V8 context.
518 // Returns OK on success, otherwise an error code.
519 int RunScript(v8::Handle<v8::String> script, const char* script_name) {
520 v8::TryCatch try_catch;
521
522 // Compile the script.
523 v8::ScriptOrigin origin =
Jason Monk9fd69be2014-03-18 11:55:59 -0400524 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
Jason Monkfc934182013-07-22 13:20:39 -0400525 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
526
527 // Execute.
528 if (!code.IsEmpty())
529 code->Run();
530
531 // Check for errors.
532 if (try_catch.HasCaught()) {
533 HandleError(try_catch.Message());
534 return ERR_PAC_SCRIPT_FAILED;
535 }
536
537 return OK;
538 }
539
540 // V8 callback for when "alert()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400541 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400542 Context* context =
543 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
544
545 // Like firefox we assume "undefined" if no argument was specified, and
546 // disregard any arguments beyond the first.
Jason Monkf5cf6e32013-07-23 17:26:23 -0400547 android::String16 message;
Jason Monkfc934182013-07-22 13:20:39 -0400548 if (args.Length() == 0) {
549 std::string undef = "undefined";
Jason Monkf5cf6e32013-07-23 17:26:23 -0400550 android::String8 undef8(undef.c_str());
551 android::String16 wundef(undef8);
Jason Monkfc934182013-07-22 13:20:39 -0400552 message = wundef;
553 } else {
Jason Monk9fd69be2014-03-18 11:55:59 -0400554 if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
555 return; // toString() threw an exception.
Jason Monkfc934182013-07-22 13:20:39 -0400556 }
557
Jason Monkf5cf6e32013-07-23 17:26:23 -0400558 context->error_listener_->AlertMessage(message);
Jason Monk9fd69be2014-03-18 11:55:59 -0400559 return;
Jason Monkfc934182013-07-22 13:20:39 -0400560 }
561
562 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400563 static void MyIpAddressCallback(
564 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400565 Context* context =
566 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
567
568 std::string result;
569 bool success;
570
571 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400572 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400573
574 // We shouldn't be called with any arguments, but will not complain if
575 // we are.
576 success = context->js_bindings_->MyIpAddress(&result);
577 }
578
Jason Monk9fd69be2014-03-18 11:55:59 -0400579 if (!success) {
580 args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
581 } else {
582 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result));
583 }
Jason Monkfc934182013-07-22 13:20:39 -0400584 }
585
586 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400587 static void MyIpAddressExCallback(
588 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400589 Context* context =
590 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
591
592 std::string ip_address_list;
593 bool success;
594
595 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400596 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400597
598 // We shouldn't be called with any arguments, but will not complain if
599 // we are.
600 success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
601 }
602
603 if (!success)
604 ip_address_list = std::string();
Jason Monk9fd69be2014-03-18 11:55:59 -0400605 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400606 }
607
608 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400609 static void DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400610 Context* context =
611 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
612
613 // We need at least one string argument.
614 std::string hostname;
Jason Monk9fd69be2014-03-18 11:55:59 -0400615 if (!GetHostnameArgument(args, &hostname)) {
616 return;
617 }
Jason Monkfc934182013-07-22 13:20:39 -0400618
619 std::string ip_address;
620 bool success;
621
622 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400623 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400624 success = context->js_bindings_->DnsResolve(hostname, &ip_address);
625 }
626
Jason Monk9fd69be2014-03-18 11:55:59 -0400627 if (success) {
628 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address));
629 } else {
630 args.GetReturnValue().SetNull();
631 }
Jason Monkfc934182013-07-22 13:20:39 -0400632 }
633
634 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400635 static void DnsResolveExCallback(
636 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400637 Context* context =
638 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
639
640 // We need at least one string argument.
641 std::string hostname;
Jason Monk9fd69be2014-03-18 11:55:59 -0400642 if (!GetHostnameArgument(args, &hostname)) {
643 args.GetReturnValue().SetNull();
644 return;
645 }
Jason Monkfc934182013-07-22 13:20:39 -0400646
647 std::string ip_address_list;
648 bool success;
649
650 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400651 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400652 success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
653 }
654
655 if (!success)
656 ip_address_list = std::string();
657
Jason Monk9fd69be2014-03-18 11:55:59 -0400658 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400659 }
660
661 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400662 static void SortIpAddressListCallback(
663 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400664 // We need at least one string argument.
Jason Monk9fd69be2014-03-18 11:55:59 -0400665 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
666 args.GetReturnValue().SetNull();
667 return;
668 }
Jason Monkfc934182013-07-22 13:20:39 -0400669
670 std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
671 std::string sorted_ip_address_list;
672 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
Jason Monk9fd69be2014-03-18 11:55:59 -0400673 if (!success) {
674 args.GetReturnValue().Set(false);
675 return;
676 }
677 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400678 }
679
680 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400681 static void IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400682 // We need at least 2 string arguments.
683 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
Jason Monk9fd69be2014-03-18 11:55:59 -0400684 args[1].IsEmpty() || !args[1]->IsString()) {
685 args.GetReturnValue().SetNull();
686 return;
687 }
Jason Monkfc934182013-07-22 13:20:39 -0400688
689 std::string ip_address = V8StringToUTF8(args[0]->ToString());
690 std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
Jason Monk9fd69be2014-03-18 11:55:59 -0400691 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
Jason Monkfc934182013-07-22 13:20:39 -0400692 }
693
694 ProxyResolverJSBindings* js_bindings_;
Jason Monkf5cf6e32013-07-23 17:26:23 -0400695 ProxyErrorListener* error_listener_;
Jason Monk9fd69be2014-03-18 11:55:59 -0400696 v8::Isolate* isolate_;
Jason Monkfc934182013-07-22 13:20:39 -0400697 v8::Persistent<v8::External> v8_this_;
698 v8::Persistent<v8::Context> v8_context_;
699};
700
701// ProxyResolverV8 ------------------------------------------------------------
702
703ProxyResolverV8::ProxyResolverV8(
Jason Monkf5cf6e32013-07-23 17:26:23 -0400704 ProxyResolverJSBindings* custom_js_bindings,
705 ProxyErrorListener* error_listener)
706 : context_(NULL), js_bindings_(custom_js_bindings),
707 error_listener_(error_listener) {
Jason Monk7439df12014-10-29 12:48:55 -0400708 v8::V8::Initialize();
Jason Monkfc934182013-07-22 13:20:39 -0400709}
710
711ProxyResolverV8::~ProxyResolverV8() {
Jason Monkc06a8d62013-08-19 10:36:23 -0400712 if (context_ != NULL) {
713 delete context_;
714 context_ = NULL;
715 }
716 if (js_bindings_ != NULL) {
717 delete js_bindings_;
718 }
Jason Monkfc934182013-07-22 13:20:39 -0400719}
720
Jason Monkf5cf6e32013-07-23 17:26:23 -0400721int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
722 android::String16* results) {
Jason Monkfc934182013-07-22 13:20:39 -0400723 // If the V8 instance has not been initialized (either because
724 // SetPacScript() wasn't called yet, or because it failed.
725 if (context_ == NULL)
726 return ERR_FAILED;
727
728 // Otherwise call into V8.
729 int rv = context_->ResolveProxy(spec, host, results);
730
731 return rv;
732}
733
Jason Monkfc934182013-07-22 13:20:39 -0400734void ProxyResolverV8::PurgeMemory() {
735 context_->PurgeMemory();
736}
737
Jason Monk40adcb52013-08-23 17:19:31 -0400738int ProxyResolverV8::SetPacScript(const android::String16& script_data) {
Jason Monkfc934182013-07-22 13:20:39 -0400739 if (context_ != NULL) {
740 delete context_;
Jason Monkc06a8d62013-08-19 10:36:23 -0400741 context_ = NULL;
Jason Monkfc934182013-07-22 13:20:39 -0400742 }
Jason Monkf5cf6e32013-07-23 17:26:23 -0400743 if (script_data.size() == 0)
Jason Monkfc934182013-07-22 13:20:39 -0400744 return ERR_PAC_SCRIPT_FAILED;
745
746 // Try parsing the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400747 context_ = new Context(js_bindings_, error_listener_, v8::Isolate::New());
Jason Monkfc934182013-07-22 13:20:39 -0400748 int rv;
749 if ((rv = context_->InitV8(script_data)) != OK) {
750 context_ = NULL;
751 }
752 if (rv != OK)
753 context_ = NULL;
754 return rv;
755}
756
757} // namespace net