blob: 533ef88bff67ec4592a69922e0e139ff31f55e39 [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>
Emily Bernier11e292b2015-06-23 16:53:06 -040013#include <libplatform/libplatform.h>
Jason Monkfc934182013-07-22 13:20:39 -040014#include <vector>
15
Ben Murdoche6933f52014-12-05 12:45:49 +000016#include "net_util.h"
17#include "proxy_resolver_script.h"
Jason Monkfc934182013-07-22 13:20:39 -040018
19// Notes on the javascript environment:
20//
21// For the majority of the PAC utility functions, we use the same code
22// as Firefox. See the javascript library that proxy_resolver_scipt.h
23// pulls in.
24//
25// In addition, we implement a subset of Microsoft's extensions to PAC.
26// - myIpAddressEx()
27// - dnsResolveEx()
28// - isResolvableEx()
29// - isInNetEx()
30// - sortIpAddressList()
31//
32// It is worth noting that the original PAC specification does not describe
33// the return values on failure. Consequently, there are compatibility
34// differences between browsers on what to return on failure, which are
35// illustrated below:
36//
37// --------------------+-------------+-------------------+--------------
38// | Firefox3 | InternetExplorer8 | --> Us <---
39// --------------------+-------------+-------------------+--------------
40// myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
41// dnsResolve() | null | false | null
42// myIpAddressEx() | N/A | "" | ""
43// sortIpAddressList() | N/A | false | false
44// dnsResolveEx() | N/A | "" | ""
45// isInNetEx() | N/A | false | false
46// --------------------+-------------+-------------------+--------------
47//
48// TODO: The cell above reading ??? means I didn't test it.
49//
50// Another difference is in how dnsResolve() and myIpAddress() are
51// implemented -- whether they should restrict to IPv4 results, or
52// include both IPv4 and IPv6. The following table illustrates the
53// differences:
54//
55// --------------------+-------------+-------------------+--------------
56// | Firefox3 | InternetExplorer8 | --> Us <---
57// --------------------+-------------+-------------------+--------------
58// myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
59// dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
60// isResolvable() | IPv4/IPv6 | IPv4 | IPv4
61// myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
62// dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
63// sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
64// isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
65// isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
66// -----------------+-------------+-------------------+--------------
67
Jason Monkf5cf6e32013-07-23 17:26:23 -040068static bool DoIsStringASCII(const android::String16& str) {
69 for (size_t i = 0; i < str.size(); i++) {
70 unsigned short c = str.string()[i];
Jason Monkfc934182013-07-22 13:20:39 -040071 if (c > 0x7F)
72 return false;
73 }
74 return true;
75}
76
Jason Monkf5cf6e32013-07-23 17:26:23 -040077bool IsStringASCII(const android::String16& str) {
Jason Monkfc934182013-07-22 13:20:39 -040078 return DoIsStringASCII(str);
79}
80
Jason Monkfc934182013-07-22 13:20:39 -040081namespace net {
82
83namespace {
84
85// Pseudo-name for the PAC script.
86const char kPacResourceName[] = "proxy-pac-script.js";
87// Pseudo-name for the PAC utility script.
88const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
89
90// External string wrapper so V8 can access the UTF16 string wrapped by
91// ProxyResolverScriptData.
92class V8ExternalStringFromScriptData
93 : public v8::String::ExternalStringResource {
94 public:
95 explicit V8ExternalStringFromScriptData(
Jason Monkf5cf6e32013-07-23 17:26:23 -040096 const android::String16& script_data)
Jason Monkfc934182013-07-22 13:20:39 -040097 : script_data_(script_data) {}
98
99 virtual const uint16_t* data() const {
Dan Albertd438de82014-11-20 11:20:39 -0800100 return reinterpret_cast<const uint16_t*>(script_data_.string());
Jason Monkfc934182013-07-22 13:20:39 -0400101 }
102
103 virtual size_t length() const {
104 return script_data_.size();
105 }
106
107 private:
Jason Monkf5cf6e32013-07-23 17:26:23 -0400108 const android::String16& script_data_;
Jason Monkfc934182013-07-22 13:20:39 -0400109// DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
110};
111
112// External string wrapper so V8 can access a string literal.
Torne (Richard Coles)9ae4ac52014-10-22 11:19:47 +0100113class V8ExternalASCIILiteral
114 : public v8::String::ExternalOneByteStringResource {
Jason Monkfc934182013-07-22 13:20:39 -0400115 public:
116 // |ascii| must be a NULL-terminated C string, and must remain valid
117 // throughout this object's lifetime.
118 V8ExternalASCIILiteral(const char* ascii, size_t length)
119 : ascii_(ascii), length_(length) {
Jason Monkfc934182013-07-22 13:20:39 -0400120 }
121
122 virtual const char* data() const {
123 return ascii_;
124 }
125
126 virtual size_t length() const {
127 return length_;
128 }
129
130 private:
131 const char* ascii_;
132 size_t length_;
133};
134
135// When creating a v8::String from a C++ string we have two choices: create
136// a copy, or create a wrapper that shares the same underlying storage.
137// For small strings it is better to just make a copy, whereas for large
138// strings there are savings by sharing the storage. This number identifies
139// the cutoff length for when to start wrapping rather than creating copies.
140const size_t kMaxStringBytesForCopy = 256;
141
142template <class string_type>
143inline typename string_type::value_type* WriteInto(string_type* str,
144 size_t length_with_null) {
145 str->reserve(length_with_null);
146 str->resize(length_with_null - 1);
147 return &((*str)[0]);
148}
149
150// Converts a V8 String to a UTF8 std::string.
151std::string V8StringToUTF8(v8::Handle<v8::String> s) {
152 std::string result;
153 s->WriteUtf8(WriteInto(&result, s->Length() + 1));
154 return result;
155}
156
157// Converts a V8 String to a UTF16 string.
Jason Monkf5cf6e32013-07-23 17:26:23 -0400158android::String16 V8StringToUTF16(v8::Handle<v8::String> s) {
Jason Monkfc934182013-07-22 13:20:39 -0400159 int len = s->Length();
Jason Monkf5cf6e32013-07-23 17:26:23 -0400160 char16_t* buf = new char16_t[len + 1];
Dan Albertd438de82014-11-20 11:20:39 -0800161 s->Write(reinterpret_cast<uint16_t*>(buf), 0, len);
Jason Monkf5cf6e32013-07-23 17:26:23 -0400162 android::String16 ret(buf, len);
163 delete buf;
164 return ret;
165}
166
167std::string UTF16ToASCII(const android::String16& str) {
Jason Monk40adcb52013-08-23 17:19:31 -0400168 android::String8 rstr(str);
169 return std::string(rstr.string());
Jason Monkfc934182013-07-22 13:20:39 -0400170}
171
172// Converts an ASCII std::string to a V8 string.
Jason Monk9fd69be2014-03-18 11:55:59 -0400173v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
174 return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
Jason Monkfc934182013-07-22 13:20:39 -0400175}
176
Jason Monk9fd69be2014-03-18 11:55:59 -0400177v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const android::String16& s) {
Dan Albertd438de82014-11-20 11:20:39 -0800178 return v8::String::NewFromTwoByte(
179 isolate, reinterpret_cast<const uint16_t*>(s.string()),
180 v8::String::kNormalString, s.size());
Jason Monkf5cf6e32013-07-23 17:26:23 -0400181}
182
Jason Monkfc934182013-07-22 13:20:39 -0400183// Converts an ASCII string literal to a V8 string.
Jason Monk9fd69be2014-03-18 11:55:59 -0400184v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) {
Jason Monkfc934182013-07-22 13:20:39 -0400185// DCHECK(IsStringASCII(ascii));
186 size_t length = strlen(ascii);
187 if (length <= kMaxStringBytesForCopy)
Jason Monk9fd69be2014-03-18 11:55:59 -0400188 return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length);
189 return v8::String::NewExternal(isolate, new V8ExternalASCIILiteral(ascii, length));
Jason Monkfc934182013-07-22 13:20:39 -0400190}
191
192// Stringizes a V8 object by calling its toString() method. Returns true
193// on success. This may fail if the toString() throws an exception.
194bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
Jason Monk9fd69be2014-03-18 11:55:59 -0400195 android::String16* utf16_result,
196 v8::Isolate* isolate) {
Jason Monkfc934182013-07-22 13:20:39 -0400197 if (object.IsEmpty())
198 return false;
199
Jason Monk9fd69be2014-03-18 11:55:59 -0400200 v8::HandleScope scope(isolate);
Jason Monkfc934182013-07-22 13:20:39 -0400201 v8::Local<v8::String> str_object = object->ToString();
202 if (str_object.IsEmpty())
203 return false;
204 *utf16_result = V8StringToUTF16(str_object);
205 return true;
206}
207
208// Extracts an hostname argument from |args|. On success returns true
209// and fills |*hostname| with the result.
Jason Monk9fd69be2014-03-18 11:55:59 -0400210bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args, std::string* hostname) {
Jason Monkfc934182013-07-22 13:20:39 -0400211 // The first argument should be a string.
212 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
213 return false;
214
Jason Monkf5cf6e32013-07-23 17:26:23 -0400215 const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400216
217 // If the hostname is already in ASCII, simply return it as is.
218 if (IsStringASCII(hostname_utf16)) {
219 *hostname = UTF16ToASCII(hostname_utf16);
220 return true;
221 }
222 return false;
223}
224
225// Wrapper for passing around IP address strings and IPAddressNumber objects.
226struct IPAddress {
227 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
228 : string_value(ip_string),
229 ip_address_number(ip_number) {
230 }
231
232 // Used for sorting IP addresses in ascending order in SortIpAddressList().
233 // IP6 addresses are placed ahead of IPv4 addresses.
234 bool operator<(const IPAddress& rhs) const {
235 const IPAddressNumber& ip1 = this->ip_address_number;
236 const IPAddressNumber& ip2 = rhs.ip_address_number;
237 if (ip1.size() != ip2.size())
238 return ip1.size() > ip2.size(); // IPv6 before IPv4.
239 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
240 }
241
242 std::string string_value;
243 IPAddressNumber ip_address_number;
244};
245
246template<typename STR>
247bool RemoveCharsT(const STR& input,
248 const typename STR::value_type remove_chars[],
249 STR* output) {
250 bool removed = false;
251 size_t found;
252
253 *output = input;
254
255 found = output->find_first_of(remove_chars);
256 while (found != STR::npos) {
257 removed = true;
258 output->replace(found, 1, STR());
259 found = output->find_first_of(remove_chars, found);
260 }
261
262 return removed;
263}
264
Jason Monkfc934182013-07-22 13:20:39 -0400265bool RemoveChars(const std::string& input,
266 const char remove_chars[],
267 std::string* output) {
268 return RemoveCharsT(input, remove_chars, output);
269}
270
271// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
272// semi-colon delimited string containing IP addresses.
273// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
274// IP addresses or an empty string if unable to sort the IP address list.
275// Returns 'true' if the sorting was successful, and 'false' if the input was an
276// empty string, a string of separators (";" in this case), or if any of the IP
277// addresses in the input list failed to parse.
278bool SortIpAddressList(const std::string& ip_address_list,
279 std::string* sorted_ip_address_list) {
280 sorted_ip_address_list->clear();
281
282 // Strip all whitespace (mimics IE behavior).
283 std::string cleaned_ip_address_list;
284 RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
285 if (cleaned_ip_address_list.empty())
286 return false;
287
288 // Split-up IP addresses and store them in a vector.
289 std::vector<IPAddress> ip_vector;
290 IPAddressNumber ip_num;
291 char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
292 while (tok_list != NULL) {
293 if (!ParseIPLiteralToNumber(tok_list, &ip_num))
294 return false;
295 ip_vector.push_back(IPAddress(tok_list, ip_num));
Jason Monk86b75a52013-08-23 17:10:51 -0400296 tok_list = strtok(NULL, ";");
Jason Monkfc934182013-07-22 13:20:39 -0400297 }
298
299 if (ip_vector.empty()) // Can happen if we have something like
300 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
301
302 // Sort lists according to ascending numeric value.
303 if (ip_vector.size() > 1)
304 std::stable_sort(ip_vector.begin(), ip_vector.end());
305
306 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
307 // IPv4).
308 for (size_t i = 0; i < ip_vector.size(); ++i) {
309 if (i > 0)
310 *sorted_ip_address_list += ";";
311 *sorted_ip_address_list += ip_vector[i].string_value;
312 }
313 return true;
314}
315
316
317// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
318// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
319// slash-delimited IP prefix with the top 'n' bits specified in the bit
320// field. This returns 'true' if the address is in the same subnet, and
321// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
322// format, or if an address and prefix of different types are used (e.g. IPv6
323// address and IPv4 prefix).
324bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
325 IPAddressNumber address;
Jason Monkeafc0fa2013-08-23 17:05:52 -0400326 std::string cleaned_ip_address;
327 if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
328 return false;
Jason Monkfc934182013-07-22 13:20:39 -0400329 if (!ParseIPLiteralToNumber(ip_address, &address))
330 return false;
331
332 IPAddressNumber prefix;
333 size_t prefix_length_in_bits;
334 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
335 return false;
336
337 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
338 if (address.size() != prefix.size())
339 return false;
340
341 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
342}
343
344} // namespace
345
346// ProxyResolverV8::Context ---------------------------------------------------
347
348class ProxyResolverV8::Context {
349 public:
Jason Monkf5cf6e32013-07-23 17:26:23 -0400350 explicit Context(ProxyResolverJSBindings* js_bindings,
Jason Monk9fd69be2014-03-18 11:55:59 -0400351 ProxyErrorListener* error_listener, v8::Isolate* isolate)
352 : js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) {
Jason Monkfc934182013-07-22 13:20:39 -0400353 }
354
355 ~Context() {
Jason Monk9fd69be2014-03-18 11:55:59 -0400356 v8::Locker locked(isolate_);
357 v8::Isolate::Scope isolate_scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400358
Jason Monk9fd69be2014-03-18 11:55:59 -0400359 v8_this_.Reset();
360 v8_context_.Reset();
Jason Monkfc934182013-07-22 13:20:39 -0400361 }
362
Jason Monk40adcb52013-08-23 17:19:31 -0400363 int ResolveProxy(const android::String16 url, const android::String16 host,
364 android::String16* results) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400365 v8::Locker locked(isolate_);
366 v8::Isolate::Scope isolate_scope(isolate_);
367 v8::HandleScope scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400368
Jason Monk9fd69be2014-03-18 11:55:59 -0400369 v8::Local<v8::Context> context =
370 v8::Local<v8::Context>::New(isolate_, v8_context_);
371 v8::Context::Scope function_scope(context);
Jason Monkfc934182013-07-22 13:20:39 -0400372
373 v8::Local<v8::Value> function;
374 if (!GetFindProxyForURL(&function)) {
Jason Monk40adcb52013-08-23 17:19:31 -0400375 error_listener_->ErrorMessage(
376 android::String16("FindProxyForURL() is undefined"));
Jason Monkfc934182013-07-22 13:20:39 -0400377 return ERR_PAC_SCRIPT_FAILED;
378 }
379
380 v8::Handle<v8::Value> argv[] = {
Jason Monk9fd69be2014-03-18 11:55:59 -0400381 UTF16StringToV8String(isolate_, url),
382 UTF16StringToV8String(isolate_, host) };
Jason Monkfc934182013-07-22 13:20:39 -0400383
384 v8::TryCatch try_catch;
385 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
Jason Monk9fd69be2014-03-18 11:55:59 -0400386 context->Global(), 2, argv);
Jason Monkfc934182013-07-22 13:20:39 -0400387
388 if (try_catch.HasCaught()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400389 error_listener_->ErrorMessage(
390 V8StringToUTF16(try_catch.Message()->Get()));
Jason Monkfc934182013-07-22 13:20:39 -0400391 return ERR_PAC_SCRIPT_FAILED;
392 }
393
394 if (!ret->IsString()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400395 error_listener_->ErrorMessage(
396 android::String16("FindProxyForURL() did not return a string."));
Jason Monkfc934182013-07-22 13:20:39 -0400397 return ERR_PAC_SCRIPT_FAILED;
398 }
399
Jason Monkf5cf6e32013-07-23 17:26:23 -0400400 *results = V8StringToUTF16(ret->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400401
Jason Monkf5cf6e32013-07-23 17:26:23 -0400402 if (!IsStringASCII(*results)) {
Jason Monkfc934182013-07-22 13:20:39 -0400403 // TODO: Rather than failing when a wide string is returned, we
404 // could extend the parsing to handle IDNA hostnames by
405 // converting them to ASCII punycode.
406 // crbug.com/47234
Jason Monk40adcb52013-08-23 17:19:31 -0400407 error_listener_->ErrorMessage(
408 android::String16("FindProxyForURL() returned a non-ASCII string"));
Jason Monkfc934182013-07-22 13:20:39 -0400409 return ERR_PAC_SCRIPT_FAILED;
410 }
411
Jason Monkfc934182013-07-22 13:20:39 -0400412 return OK;
413 }
414
Jason Monkf5cf6e32013-07-23 17:26:23 -0400415 int InitV8(const android::String16& pac_script) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400416 v8::Locker locked(isolate_);
417 v8::Isolate::Scope isolate_scope(isolate_);
418 v8::HandleScope scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400419
Jason Monk9fd69be2014-03-18 11:55:59 -0400420 v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
421 v8::Local<v8::External> v8_this =
422 v8::Local<v8::External>::New(isolate_, v8_this_);
Jason Monkfc934182013-07-22 13:20:39 -0400423 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
424
425 // Attach the javascript bindings.
426 v8::Local<v8::FunctionTemplate> alert_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400427 v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
428 global_template->Set(ASCIILiteralToV8String(isolate_, "alert"), alert_template);
Jason Monkfc934182013-07-22 13:20:39 -0400429
430 v8::Local<v8::FunctionTemplate> my_ip_address_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400431 v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
432 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
Jason Monkfc934182013-07-22 13:20:39 -0400433 my_ip_address_template);
434
435 v8::Local<v8::FunctionTemplate> dns_resolve_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400436 v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
437 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
Jason Monkfc934182013-07-22 13:20:39 -0400438 dns_resolve_template);
439
440 // Microsoft's PAC extensions:
441
442 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400443 v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
444 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400445 dns_resolve_ex_template);
446
447 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400448 v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
449 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400450 my_ip_address_ex_template);
451
452 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400453 v8::FunctionTemplate::New(isolate_, &SortIpAddressListCallback, v8_this);
454 global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
Jason Monkfc934182013-07-22 13:20:39 -0400455 sort_ip_address_list_template);
456
457 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400458 v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
459 global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400460 is_in_net_ex_template);
461
Jason Monk9fd69be2014-03-18 11:55:59 -0400462 v8_context_.Reset(
463 isolate_, v8::Context::New(isolate_, NULL, global_template));
Jason Monkfc934182013-07-22 13:20:39 -0400464
Jason Monk9fd69be2014-03-18 11:55:59 -0400465 v8::Local<v8::Context> context =
466 v8::Local<v8::Context>::New(isolate_, v8_context_);
467 v8::Context::Scope ctx(context);
Jason Monkfc934182013-07-22 13:20:39 -0400468
469 // Add the PAC utility functions to the environment.
470 // (This script should never fail, as it is a string literal!)
471 // Note that the two string literals are concatenated.
472 int rv = RunScript(
Jason Monk9fd69be2014-03-18 11:55:59 -0400473 ASCIILiteralToV8String(isolate_,
Jason Monkfc934182013-07-22 13:20:39 -0400474 PROXY_RESOLVER_SCRIPT
475 PROXY_RESOLVER_SCRIPT_EX),
476 kPacUtilityResourceName);
477 if (rv != OK) {
478 return rv;
479 }
480
481 // Add the user's PAC code to the environment.
Jason Monk9fd69be2014-03-18 11:55:59 -0400482 rv = RunScript(UTF16StringToV8String(isolate_, pac_script), kPacResourceName);
Jason Monkfc934182013-07-22 13:20:39 -0400483 if (rv != OK) {
484 return rv;
485 }
486
487 // At a minimum, the FindProxyForURL() function must be defined for this
488 // to be a legitimiate PAC script.
489 v8::Local<v8::Value> function;
490 if (!GetFindProxyForURL(&function))
491 return ERR_PAC_SCRIPT_FAILED;
492
493 return OK;
494 }
495
496 void PurgeMemory() {
Jason Monk9fd69be2014-03-18 11:55:59 -0400497 v8::Locker locked(isolate_);
498 v8::Isolate::Scope isolate_scope(isolate_);
Ben Murdoche80b5fa2014-07-31 16:47:35 +0100499 isolate_->LowMemoryNotification();
Jason Monkfc934182013-07-22 13:20:39 -0400500 }
501
502 private:
503 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400504 v8::Local<v8::Context> context =
505 v8::Local<v8::Context>::New(isolate_, v8_context_);
506 *function = context->Global()->Get(
507 ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
Jason Monkfc934182013-07-22 13:20:39 -0400508 return (*function)->IsFunction();
509 }
510
511 // Handle an exception thrown by V8.
512 void HandleError(v8::Handle<v8::Message> message) {
513 if (message.IsEmpty())
514 return;
Jason Monkf5cf6e32013-07-23 17:26:23 -0400515 error_listener_->ErrorMessage(V8StringToUTF16(message->Get()));
Jason Monkfc934182013-07-22 13:20:39 -0400516 }
517
518 // Compiles and runs |script| in the current V8 context.
519 // Returns OK on success, otherwise an error code.
520 int RunScript(v8::Handle<v8::String> script, const char* script_name) {
521 v8::TryCatch try_catch;
522
523 // Compile the script.
524 v8::ScriptOrigin origin =
Jason Monk9fd69be2014-03-18 11:55:59 -0400525 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
Jason Monkfc934182013-07-22 13:20:39 -0400526 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
527
528 // Execute.
529 if (!code.IsEmpty())
530 code->Run();
531
532 // Check for errors.
533 if (try_catch.HasCaught()) {
534 HandleError(try_catch.Message());
535 return ERR_PAC_SCRIPT_FAILED;
536 }
537
538 return OK;
539 }
540
541 // V8 callback for when "alert()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400542 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400543 Context* context =
544 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
545
546 // Like firefox we assume "undefined" if no argument was specified, and
547 // disregard any arguments beyond the first.
Jason Monkf5cf6e32013-07-23 17:26:23 -0400548 android::String16 message;
Jason Monkfc934182013-07-22 13:20:39 -0400549 if (args.Length() == 0) {
550 std::string undef = "undefined";
Jason Monkf5cf6e32013-07-23 17:26:23 -0400551 android::String8 undef8(undef.c_str());
552 android::String16 wundef(undef8);
Jason Monkfc934182013-07-22 13:20:39 -0400553 message = wundef;
554 } else {
Jason Monk9fd69be2014-03-18 11:55:59 -0400555 if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
556 return; // toString() threw an exception.
Jason Monkfc934182013-07-22 13:20:39 -0400557 }
558
Jason Monkf5cf6e32013-07-23 17:26:23 -0400559 context->error_listener_->AlertMessage(message);
Jason Monk9fd69be2014-03-18 11:55:59 -0400560 return;
Jason Monkfc934182013-07-22 13:20:39 -0400561 }
562
563 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400564 static void MyIpAddressCallback(
565 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400566 Context* context =
567 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
568
569 std::string result;
570 bool success;
571
572 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400573 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400574
575 // We shouldn't be called with any arguments, but will not complain if
576 // we are.
577 success = context->js_bindings_->MyIpAddress(&result);
578 }
579
Jason Monk9fd69be2014-03-18 11:55:59 -0400580 if (!success) {
581 args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
582 } else {
583 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result));
584 }
Jason Monkfc934182013-07-22 13:20:39 -0400585 }
586
587 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400588 static void MyIpAddressExCallback(
589 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400590 Context* context =
591 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
592
593 std::string ip_address_list;
594 bool success;
595
596 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400597 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400598
599 // We shouldn't be called with any arguments, but will not complain if
600 // we are.
601 success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
602 }
603
604 if (!success)
605 ip_address_list = std::string();
Jason Monk9fd69be2014-03-18 11:55:59 -0400606 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400607 }
608
609 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400610 static void DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400611 Context* context =
612 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
613
614 // We need at least one string argument.
615 std::string hostname;
Jason Monk9fd69be2014-03-18 11:55:59 -0400616 if (!GetHostnameArgument(args, &hostname)) {
617 return;
618 }
Jason Monkfc934182013-07-22 13:20:39 -0400619
620 std::string ip_address;
621 bool success;
622
623 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400624 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400625 success = context->js_bindings_->DnsResolve(hostname, &ip_address);
626 }
627
Jason Monk9fd69be2014-03-18 11:55:59 -0400628 if (success) {
629 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address));
630 } else {
631 args.GetReturnValue().SetNull();
632 }
Jason Monkfc934182013-07-22 13:20:39 -0400633 }
634
635 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400636 static void DnsResolveExCallback(
637 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400638 Context* context =
639 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
640
641 // We need at least one string argument.
642 std::string hostname;
Jason Monk9fd69be2014-03-18 11:55:59 -0400643 if (!GetHostnameArgument(args, &hostname)) {
644 args.GetReturnValue().SetNull();
645 return;
646 }
Jason Monkfc934182013-07-22 13:20:39 -0400647
648 std::string ip_address_list;
649 bool success;
650
651 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400652 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400653 success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
654 }
655
656 if (!success)
657 ip_address_list = std::string();
658
Jason Monk9fd69be2014-03-18 11:55:59 -0400659 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400660 }
661
662 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400663 static void SortIpAddressListCallback(
664 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400665 // We need at least one string argument.
Jason Monk9fd69be2014-03-18 11:55:59 -0400666 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
667 args.GetReturnValue().SetNull();
668 return;
669 }
Jason Monkfc934182013-07-22 13:20:39 -0400670
671 std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
672 std::string sorted_ip_address_list;
673 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
Jason Monk9fd69be2014-03-18 11:55:59 -0400674 if (!success) {
675 args.GetReturnValue().Set(false);
676 return;
677 }
678 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400679 }
680
681 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400682 static void IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400683 // We need at least 2 string arguments.
684 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
Jason Monk9fd69be2014-03-18 11:55:59 -0400685 args[1].IsEmpty() || !args[1]->IsString()) {
686 args.GetReturnValue().SetNull();
687 return;
688 }
Jason Monkfc934182013-07-22 13:20:39 -0400689
690 std::string ip_address = V8StringToUTF8(args[0]->ToString());
691 std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
Jason Monk9fd69be2014-03-18 11:55:59 -0400692 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
Jason Monkfc934182013-07-22 13:20:39 -0400693 }
694
695 ProxyResolverJSBindings* js_bindings_;
Jason Monkf5cf6e32013-07-23 17:26:23 -0400696 ProxyErrorListener* error_listener_;
Jason Monk9fd69be2014-03-18 11:55:59 -0400697 v8::Isolate* isolate_;
Jason Monkfc934182013-07-22 13:20:39 -0400698 v8::Persistent<v8::External> v8_this_;
699 v8::Persistent<v8::Context> v8_context_;
700};
701
702// ProxyResolverV8 ------------------------------------------------------------
703
704ProxyResolverV8::ProxyResolverV8(
Jason Monkf5cf6e32013-07-23 17:26:23 -0400705 ProxyResolverJSBindings* custom_js_bindings,
706 ProxyErrorListener* error_listener)
707 : context_(NULL), js_bindings_(custom_js_bindings),
708 error_listener_(error_listener) {
Emily Bernier11e292b2015-06-23 16:53:06 -0400709 if (v8::V8::GetCurrentPlatform() == NULL) {
710 v8::Platform* platform = v8::platform::CreateDefaultPlatform();
711 v8::V8::InitializePlatform(platform);
712 v8::V8::Initialize();
713 }
Jason Monkfc934182013-07-22 13:20:39 -0400714}
715
716ProxyResolverV8::~ProxyResolverV8() {
Jason Monkc06a8d62013-08-19 10:36:23 -0400717 if (context_ != NULL) {
718 delete context_;
719 context_ = NULL;
720 }
721 if (js_bindings_ != NULL) {
722 delete js_bindings_;
723 }
Jason Monkfc934182013-07-22 13:20:39 -0400724}
725
Jason Monkf5cf6e32013-07-23 17:26:23 -0400726int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
727 android::String16* results) {
Jason Monkfc934182013-07-22 13:20:39 -0400728 // If the V8 instance has not been initialized (either because
729 // SetPacScript() wasn't called yet, or because it failed.
730 if (context_ == NULL)
731 return ERR_FAILED;
732
733 // Otherwise call into V8.
734 int rv = context_->ResolveProxy(spec, host, results);
735
736 return rv;
737}
738
Jason Monkfc934182013-07-22 13:20:39 -0400739void ProxyResolverV8::PurgeMemory() {
740 context_->PurgeMemory();
741}
742
Jason Monk40adcb52013-08-23 17:19:31 -0400743int ProxyResolverV8::SetPacScript(const android::String16& script_data) {
Jason Monkfc934182013-07-22 13:20:39 -0400744 if (context_ != NULL) {
745 delete context_;
Jason Monkc06a8d62013-08-19 10:36:23 -0400746 context_ = NULL;
Jason Monkfc934182013-07-22 13:20:39 -0400747 }
Jason Monkf5cf6e32013-07-23 17:26:23 -0400748 if (script_data.size() == 0)
Jason Monkfc934182013-07-22 13:20:39 -0400749 return ERR_PAC_SCRIPT_FAILED;
750
751 // Try parsing the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400752 context_ = new Context(js_bindings_, error_listener_, v8::Isolate::New());
Jason Monkfc934182013-07-22 13:20:39 -0400753 int rv;
754 if ((rv = context_->InitV8(script_data)) != OK) {
755 context_ = NULL;
756 }
757 if (rv != OK)
758 context_ = NULL;
759 return rv;
760}
761
762} // namespace net