blob: 340096782bd300cc57c160593bff0bbe0c95bab8 [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
5#include <algorithm>
6#include <cstdio>
7#include <string>
8
Jason Monkf5cf6e32013-07-23 17:26:23 -04009#include <utils/String16.h>
10
Jason Monkfc934182013-07-22 13:20:39 -040011#include "proxy_resolver_v8.h"
12
13#include "proxy_resolver_script.h"
14#include "net_util.h"
15#include <include/v8.h>
16#include <algorithm>
17#include <vector>
18
19#include <iostream>
20
21#include <string.h>
Jason Monkf5cf6e32013-07-23 17:26:23 -040022#include <utils/String8.h>
23#include <utils/String16.h>
Jason Monkfc934182013-07-22 13:20:39 -040024
25// Notes on the javascript environment:
26//
27// For the majority of the PAC utility functions, we use the same code
28// as Firefox. See the javascript library that proxy_resolver_scipt.h
29// pulls in.
30//
31// In addition, we implement a subset of Microsoft's extensions to PAC.
32// - myIpAddressEx()
33// - dnsResolveEx()
34// - isResolvableEx()
35// - isInNetEx()
36// - sortIpAddressList()
37//
38// It is worth noting that the original PAC specification does not describe
39// the return values on failure. Consequently, there are compatibility
40// differences between browsers on what to return on failure, which are
41// illustrated below:
42//
43// --------------------+-------------+-------------------+--------------
44// | Firefox3 | InternetExplorer8 | --> Us <---
45// --------------------+-------------+-------------------+--------------
46// myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
47// dnsResolve() | null | false | null
48// myIpAddressEx() | N/A | "" | ""
49// sortIpAddressList() | N/A | false | false
50// dnsResolveEx() | N/A | "" | ""
51// isInNetEx() | N/A | false | false
52// --------------------+-------------+-------------------+--------------
53//
54// TODO: The cell above reading ??? means I didn't test it.
55//
56// Another difference is in how dnsResolve() and myIpAddress() are
57// implemented -- whether they should restrict to IPv4 results, or
58// include both IPv4 and IPv6. The following table illustrates the
59// differences:
60//
61// --------------------+-------------+-------------------+--------------
62// | Firefox3 | InternetExplorer8 | --> Us <---
63// --------------------+-------------+-------------------+--------------
64// myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
65// dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
66// isResolvable() | IPv4/IPv6 | IPv4 | IPv4
67// myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
68// dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
69// sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
70// isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
71// isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
72// -----------------+-------------+-------------------+--------------
73
Jason Monkf5cf6e32013-07-23 17:26:23 -040074static bool DoIsStringASCII(const android::String16& str) {
75 for (size_t i = 0; i < str.size(); i++) {
76 unsigned short c = str.string()[i];
Jason Monkfc934182013-07-22 13:20:39 -040077 if (c > 0x7F)
78 return false;
79 }
80 return true;
81}
82
Jason Monkf5cf6e32013-07-23 17:26:23 -040083bool IsStringASCII(const android::String16& str) {
Jason Monkfc934182013-07-22 13:20:39 -040084 return DoIsStringASCII(str);
85}
86
Jason Monkfc934182013-07-22 13:20:39 -040087namespace net {
88
89namespace {
90
91// Pseudo-name for the PAC script.
92const char kPacResourceName[] = "proxy-pac-script.js";
93// Pseudo-name for the PAC utility script.
94const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
95
96// External string wrapper so V8 can access the UTF16 string wrapped by
97// ProxyResolverScriptData.
98class V8ExternalStringFromScriptData
99 : public v8::String::ExternalStringResource {
100 public:
101 explicit V8ExternalStringFromScriptData(
Jason Monkf5cf6e32013-07-23 17:26:23 -0400102 const android::String16& script_data)
Jason Monkfc934182013-07-22 13:20:39 -0400103 : script_data_(script_data) {}
104
105 virtual const uint16_t* data() const {
Dan Albertd438de82014-11-20 11:20:39 -0800106 return reinterpret_cast<const uint16_t*>(script_data_.string());
Jason Monkfc934182013-07-22 13:20:39 -0400107 }
108
109 virtual size_t length() const {
110 return script_data_.size();
111 }
112
113 private:
Jason Monkf5cf6e32013-07-23 17:26:23 -0400114 const android::String16& script_data_;
Jason Monkfc934182013-07-22 13:20:39 -0400115// DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
116};
117
118// External string wrapper so V8 can access a string literal.
Torne (Richard Coles)9ae4ac52014-10-22 11:19:47 +0100119class V8ExternalASCIILiteral
120 : public v8::String::ExternalOneByteStringResource {
Jason Monkfc934182013-07-22 13:20:39 -0400121 public:
122 // |ascii| must be a NULL-terminated C string, and must remain valid
123 // throughout this object's lifetime.
124 V8ExternalASCIILiteral(const char* ascii, size_t length)
125 : ascii_(ascii), length_(length) {
Jason Monkfc934182013-07-22 13:20:39 -0400126 }
127
128 virtual const char* data() const {
129 return ascii_;
130 }
131
132 virtual size_t length() const {
133 return length_;
134 }
135
136 private:
137 const char* ascii_;
138 size_t length_;
139};
140
141// When creating a v8::String from a C++ string we have two choices: create
142// a copy, or create a wrapper that shares the same underlying storage.
143// For small strings it is better to just make a copy, whereas for large
144// strings there are savings by sharing the storage. This number identifies
145// the cutoff length for when to start wrapping rather than creating copies.
146const size_t kMaxStringBytesForCopy = 256;
147
148template <class string_type>
149inline typename string_type::value_type* WriteInto(string_type* str,
150 size_t length_with_null) {
151 str->reserve(length_with_null);
152 str->resize(length_with_null - 1);
153 return &((*str)[0]);
154}
155
156// Converts a V8 String to a UTF8 std::string.
157std::string V8StringToUTF8(v8::Handle<v8::String> s) {
158 std::string result;
159 s->WriteUtf8(WriteInto(&result, s->Length() + 1));
160 return result;
161}
162
163// Converts a V8 String to a UTF16 string.
Jason Monkf5cf6e32013-07-23 17:26:23 -0400164android::String16 V8StringToUTF16(v8::Handle<v8::String> s) {
Jason Monkfc934182013-07-22 13:20:39 -0400165 int len = s->Length();
Jason Monkf5cf6e32013-07-23 17:26:23 -0400166 char16_t* buf = new char16_t[len + 1];
Dan Albertd438de82014-11-20 11:20:39 -0800167 s->Write(reinterpret_cast<uint16_t*>(buf), 0, len);
Jason Monkf5cf6e32013-07-23 17:26:23 -0400168 android::String16 ret(buf, len);
169 delete buf;
170 return ret;
171}
172
173std::string UTF16ToASCII(const android::String16& str) {
Jason Monk40adcb52013-08-23 17:19:31 -0400174 android::String8 rstr(str);
175 return std::string(rstr.string());
Jason Monkfc934182013-07-22 13:20:39 -0400176}
177
178// Converts an ASCII std::string to a V8 string.
Jason Monk9fd69be2014-03-18 11:55:59 -0400179v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
180 return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
Jason Monkfc934182013-07-22 13:20:39 -0400181}
182
Jason Monk9fd69be2014-03-18 11:55:59 -0400183v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const android::String16& s) {
Dan Albertd438de82014-11-20 11:20:39 -0800184 return v8::String::NewFromTwoByte(
185 isolate, reinterpret_cast<const uint16_t*>(s.string()),
186 v8::String::kNormalString, s.size());
Jason Monkf5cf6e32013-07-23 17:26:23 -0400187}
188
Jason Monkfc934182013-07-22 13:20:39 -0400189// Converts an ASCII string literal to a V8 string.
Jason Monk9fd69be2014-03-18 11:55:59 -0400190v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) {
Jason Monkfc934182013-07-22 13:20:39 -0400191// DCHECK(IsStringASCII(ascii));
192 size_t length = strlen(ascii);
193 if (length <= kMaxStringBytesForCopy)
Jason Monk9fd69be2014-03-18 11:55:59 -0400194 return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length);
195 return v8::String::NewExternal(isolate, new V8ExternalASCIILiteral(ascii, length));
Jason Monkfc934182013-07-22 13:20:39 -0400196}
197
198// Stringizes a V8 object by calling its toString() method. Returns true
199// on success. This may fail if the toString() throws an exception.
200bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
Jason Monk9fd69be2014-03-18 11:55:59 -0400201 android::String16* utf16_result,
202 v8::Isolate* isolate) {
Jason Monkfc934182013-07-22 13:20:39 -0400203 if (object.IsEmpty())
204 return false;
205
Jason Monk9fd69be2014-03-18 11:55:59 -0400206 v8::HandleScope scope(isolate);
Jason Monkfc934182013-07-22 13:20:39 -0400207 v8::Local<v8::String> str_object = object->ToString();
208 if (str_object.IsEmpty())
209 return false;
210 *utf16_result = V8StringToUTF16(str_object);
211 return true;
212}
213
214// Extracts an hostname argument from |args|. On success returns true
215// and fills |*hostname| with the result.
Jason Monk9fd69be2014-03-18 11:55:59 -0400216bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args, std::string* hostname) {
Jason Monkfc934182013-07-22 13:20:39 -0400217 // The first argument should be a string.
218 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
219 return false;
220
Jason Monkf5cf6e32013-07-23 17:26:23 -0400221 const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400222
223 // If the hostname is already in ASCII, simply return it as is.
224 if (IsStringASCII(hostname_utf16)) {
225 *hostname = UTF16ToASCII(hostname_utf16);
226 return true;
227 }
228 return false;
229}
230
231// Wrapper for passing around IP address strings and IPAddressNumber objects.
232struct IPAddress {
233 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
234 : string_value(ip_string),
235 ip_address_number(ip_number) {
236 }
237
238 // Used for sorting IP addresses in ascending order in SortIpAddressList().
239 // IP6 addresses are placed ahead of IPv4 addresses.
240 bool operator<(const IPAddress& rhs) const {
241 const IPAddressNumber& ip1 = this->ip_address_number;
242 const IPAddressNumber& ip2 = rhs.ip_address_number;
243 if (ip1.size() != ip2.size())
244 return ip1.size() > ip2.size(); // IPv6 before IPv4.
245 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
246 }
247
248 std::string string_value;
249 IPAddressNumber ip_address_number;
250};
251
252template<typename STR>
253bool RemoveCharsT(const STR& input,
254 const typename STR::value_type remove_chars[],
255 STR* output) {
256 bool removed = false;
257 size_t found;
258
259 *output = input;
260
261 found = output->find_first_of(remove_chars);
262 while (found != STR::npos) {
263 removed = true;
264 output->replace(found, 1, STR());
265 found = output->find_first_of(remove_chars, found);
266 }
267
268 return removed;
269}
270
Jason Monkfc934182013-07-22 13:20:39 -0400271bool RemoveChars(const std::string& input,
272 const char remove_chars[],
273 std::string* output) {
274 return RemoveCharsT(input, remove_chars, output);
275}
276
277// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
278// semi-colon delimited string containing IP addresses.
279// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
280// IP addresses or an empty string if unable to sort the IP address list.
281// Returns 'true' if the sorting was successful, and 'false' if the input was an
282// empty string, a string of separators (";" in this case), or if any of the IP
283// addresses in the input list failed to parse.
284bool SortIpAddressList(const std::string& ip_address_list,
285 std::string* sorted_ip_address_list) {
286 sorted_ip_address_list->clear();
287
288 // Strip all whitespace (mimics IE behavior).
289 std::string cleaned_ip_address_list;
290 RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
291 if (cleaned_ip_address_list.empty())
292 return false;
293
294 // Split-up IP addresses and store them in a vector.
295 std::vector<IPAddress> ip_vector;
296 IPAddressNumber ip_num;
297 char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
298 while (tok_list != NULL) {
299 if (!ParseIPLiteralToNumber(tok_list, &ip_num))
300 return false;
301 ip_vector.push_back(IPAddress(tok_list, ip_num));
Jason Monk86b75a52013-08-23 17:10:51 -0400302 tok_list = strtok(NULL, ";");
Jason Monkfc934182013-07-22 13:20:39 -0400303 }
304
305 if (ip_vector.empty()) // Can happen if we have something like
306 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
307
308 // Sort lists according to ascending numeric value.
309 if (ip_vector.size() > 1)
310 std::stable_sort(ip_vector.begin(), ip_vector.end());
311
312 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
313 // IPv4).
314 for (size_t i = 0; i < ip_vector.size(); ++i) {
315 if (i > 0)
316 *sorted_ip_address_list += ";";
317 *sorted_ip_address_list += ip_vector[i].string_value;
318 }
319 return true;
320}
321
322
323// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
324// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
325// slash-delimited IP prefix with the top 'n' bits specified in the bit
326// field. This returns 'true' if the address is in the same subnet, and
327// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
328// format, or if an address and prefix of different types are used (e.g. IPv6
329// address and IPv4 prefix).
330bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
331 IPAddressNumber address;
Jason Monkeafc0fa2013-08-23 17:05:52 -0400332 std::string cleaned_ip_address;
333 if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
334 return false;
Jason Monkfc934182013-07-22 13:20:39 -0400335 if (!ParseIPLiteralToNumber(ip_address, &address))
336 return false;
337
338 IPAddressNumber prefix;
339 size_t prefix_length_in_bits;
340 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
341 return false;
342
343 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
344 if (address.size() != prefix.size())
345 return false;
346
347 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
348}
349
350} // namespace
351
352// ProxyResolverV8::Context ---------------------------------------------------
353
354class ProxyResolverV8::Context {
355 public:
Jason Monkf5cf6e32013-07-23 17:26:23 -0400356 explicit Context(ProxyResolverJSBindings* js_bindings,
Jason Monk9fd69be2014-03-18 11:55:59 -0400357 ProxyErrorListener* error_listener, v8::Isolate* isolate)
358 : js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) {
Jason Monkfc934182013-07-22 13:20:39 -0400359 }
360
361 ~Context() {
Jason Monk9fd69be2014-03-18 11:55:59 -0400362 v8::Locker locked(isolate_);
363 v8::Isolate::Scope isolate_scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400364
Jason Monk9fd69be2014-03-18 11:55:59 -0400365 v8_this_.Reset();
366 v8_context_.Reset();
Jason Monkfc934182013-07-22 13:20:39 -0400367 }
368
Jason Monk40adcb52013-08-23 17:19:31 -0400369 int ResolveProxy(const android::String16 url, const android::String16 host,
370 android::String16* results) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400371 v8::Locker locked(isolate_);
372 v8::Isolate::Scope isolate_scope(isolate_);
373 v8::HandleScope scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400374
Jason Monk9fd69be2014-03-18 11:55:59 -0400375 v8::Local<v8::Context> context =
376 v8::Local<v8::Context>::New(isolate_, v8_context_);
377 v8::Context::Scope function_scope(context);
Jason Monkfc934182013-07-22 13:20:39 -0400378
379 v8::Local<v8::Value> function;
380 if (!GetFindProxyForURL(&function)) {
Jason Monk40adcb52013-08-23 17:19:31 -0400381 error_listener_->ErrorMessage(
382 android::String16("FindProxyForURL() is undefined"));
Jason Monkfc934182013-07-22 13:20:39 -0400383 return ERR_PAC_SCRIPT_FAILED;
384 }
385
386 v8::Handle<v8::Value> argv[] = {
Jason Monk9fd69be2014-03-18 11:55:59 -0400387 UTF16StringToV8String(isolate_, url),
388 UTF16StringToV8String(isolate_, host) };
Jason Monkfc934182013-07-22 13:20:39 -0400389
390 v8::TryCatch try_catch;
391 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
Jason Monk9fd69be2014-03-18 11:55:59 -0400392 context->Global(), 2, argv);
Jason Monkfc934182013-07-22 13:20:39 -0400393
394 if (try_catch.HasCaught()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400395 error_listener_->ErrorMessage(
396 V8StringToUTF16(try_catch.Message()->Get()));
Jason Monkfc934182013-07-22 13:20:39 -0400397 return ERR_PAC_SCRIPT_FAILED;
398 }
399
400 if (!ret->IsString()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400401 error_listener_->ErrorMessage(
402 android::String16("FindProxyForURL() did not return a string."));
Jason Monkfc934182013-07-22 13:20:39 -0400403 return ERR_PAC_SCRIPT_FAILED;
404 }
405
Jason Monkf5cf6e32013-07-23 17:26:23 -0400406 *results = V8StringToUTF16(ret->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400407
Jason Monkf5cf6e32013-07-23 17:26:23 -0400408 if (!IsStringASCII(*results)) {
Jason Monkfc934182013-07-22 13:20:39 -0400409 // TODO: Rather than failing when a wide string is returned, we
410 // could extend the parsing to handle IDNA hostnames by
411 // converting them to ASCII punycode.
412 // crbug.com/47234
Jason Monk40adcb52013-08-23 17:19:31 -0400413 error_listener_->ErrorMessage(
414 android::String16("FindProxyForURL() returned a non-ASCII string"));
Jason Monkfc934182013-07-22 13:20:39 -0400415 return ERR_PAC_SCRIPT_FAILED;
416 }
417
Jason Monkfc934182013-07-22 13:20:39 -0400418 return OK;
419 }
420
Jason Monkf5cf6e32013-07-23 17:26:23 -0400421 int InitV8(const android::String16& pac_script) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400422 v8::Locker locked(isolate_);
423 v8::Isolate::Scope isolate_scope(isolate_);
424 v8::HandleScope scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400425
Jason Monk9fd69be2014-03-18 11:55:59 -0400426 v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
427 v8::Local<v8::External> v8_this =
428 v8::Local<v8::External>::New(isolate_, v8_this_);
Jason Monkfc934182013-07-22 13:20:39 -0400429 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
430
431 // Attach the javascript bindings.
432 v8::Local<v8::FunctionTemplate> alert_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400433 v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
434 global_template->Set(ASCIILiteralToV8String(isolate_, "alert"), alert_template);
Jason Monkfc934182013-07-22 13:20:39 -0400435
436 v8::Local<v8::FunctionTemplate> my_ip_address_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400437 v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
438 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
Jason Monkfc934182013-07-22 13:20:39 -0400439 my_ip_address_template);
440
441 v8::Local<v8::FunctionTemplate> dns_resolve_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400442 v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
443 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
Jason Monkfc934182013-07-22 13:20:39 -0400444 dns_resolve_template);
445
446 // Microsoft's PAC extensions:
447
448 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400449 v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
450 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400451 dns_resolve_ex_template);
452
453 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400454 v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
455 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400456 my_ip_address_ex_template);
457
458 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400459 v8::FunctionTemplate::New(isolate_, &SortIpAddressListCallback, v8_this);
460 global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
Jason Monkfc934182013-07-22 13:20:39 -0400461 sort_ip_address_list_template);
462
463 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400464 v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
465 global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400466 is_in_net_ex_template);
467
Jason Monk9fd69be2014-03-18 11:55:59 -0400468 v8_context_.Reset(
469 isolate_, v8::Context::New(isolate_, NULL, global_template));
Jason Monkfc934182013-07-22 13:20:39 -0400470
Jason Monk9fd69be2014-03-18 11:55:59 -0400471 v8::Local<v8::Context> context =
472 v8::Local<v8::Context>::New(isolate_, v8_context_);
473 v8::Context::Scope ctx(context);
Jason Monkfc934182013-07-22 13:20:39 -0400474
475 // Add the PAC utility functions to the environment.
476 // (This script should never fail, as it is a string literal!)
477 // Note that the two string literals are concatenated.
478 int rv = RunScript(
Jason Monk9fd69be2014-03-18 11:55:59 -0400479 ASCIILiteralToV8String(isolate_,
Jason Monkfc934182013-07-22 13:20:39 -0400480 PROXY_RESOLVER_SCRIPT
481 PROXY_RESOLVER_SCRIPT_EX),
482 kPacUtilityResourceName);
483 if (rv != OK) {
484 return rv;
485 }
486
487 // Add the user's PAC code to the environment.
Jason Monk9fd69be2014-03-18 11:55:59 -0400488 rv = RunScript(UTF16StringToV8String(isolate_, pac_script), kPacResourceName);
Jason Monkfc934182013-07-22 13:20:39 -0400489 if (rv != OK) {
490 return rv;
491 }
492
493 // At a minimum, the FindProxyForURL() function must be defined for this
494 // to be a legitimiate PAC script.
495 v8::Local<v8::Value> function;
496 if (!GetFindProxyForURL(&function))
497 return ERR_PAC_SCRIPT_FAILED;
498
499 return OK;
500 }
501
502 void PurgeMemory() {
Jason Monk9fd69be2014-03-18 11:55:59 -0400503 v8::Locker locked(isolate_);
504 v8::Isolate::Scope isolate_scope(isolate_);
Ben Murdoche80b5fa2014-07-31 16:47:35 +0100505 isolate_->LowMemoryNotification();
Jason Monkfc934182013-07-22 13:20:39 -0400506 }
507
508 private:
509 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400510 v8::Local<v8::Context> context =
511 v8::Local<v8::Context>::New(isolate_, v8_context_);
512 *function = context->Global()->Get(
513 ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
Jason Monkfc934182013-07-22 13:20:39 -0400514 return (*function)->IsFunction();
515 }
516
517 // Handle an exception thrown by V8.
518 void HandleError(v8::Handle<v8::Message> message) {
519 if (message.IsEmpty())
520 return;
Jason Monkf5cf6e32013-07-23 17:26:23 -0400521 error_listener_->ErrorMessage(V8StringToUTF16(message->Get()));
Jason Monkfc934182013-07-22 13:20:39 -0400522 }
523
524 // Compiles and runs |script| in the current V8 context.
525 // Returns OK on success, otherwise an error code.
526 int RunScript(v8::Handle<v8::String> script, const char* script_name) {
527 v8::TryCatch try_catch;
528
529 // Compile the script.
530 v8::ScriptOrigin origin =
Jason Monk9fd69be2014-03-18 11:55:59 -0400531 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
Jason Monkfc934182013-07-22 13:20:39 -0400532 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
533
534 // Execute.
535 if (!code.IsEmpty())
536 code->Run();
537
538 // Check for errors.
539 if (try_catch.HasCaught()) {
540 HandleError(try_catch.Message());
541 return ERR_PAC_SCRIPT_FAILED;
542 }
543
544 return OK;
545 }
546
547 // V8 callback for when "alert()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400548 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400549 Context* context =
550 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
551
552 // Like firefox we assume "undefined" if no argument was specified, and
553 // disregard any arguments beyond the first.
Jason Monkf5cf6e32013-07-23 17:26:23 -0400554 android::String16 message;
Jason Monkfc934182013-07-22 13:20:39 -0400555 if (args.Length() == 0) {
556 std::string undef = "undefined";
Jason Monkf5cf6e32013-07-23 17:26:23 -0400557 android::String8 undef8(undef.c_str());
558 android::String16 wundef(undef8);
Jason Monkfc934182013-07-22 13:20:39 -0400559 message = wundef;
560 } else {
Jason Monk9fd69be2014-03-18 11:55:59 -0400561 if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
562 return; // toString() threw an exception.
Jason Monkfc934182013-07-22 13:20:39 -0400563 }
564
Jason Monkf5cf6e32013-07-23 17:26:23 -0400565 context->error_listener_->AlertMessage(message);
Jason Monk9fd69be2014-03-18 11:55:59 -0400566 return;
Jason Monkfc934182013-07-22 13:20:39 -0400567 }
568
569 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400570 static void MyIpAddressCallback(
571 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400572 Context* context =
573 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
574
575 std::string result;
576 bool success;
577
578 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400579 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400580
581 // We shouldn't be called with any arguments, but will not complain if
582 // we are.
583 success = context->js_bindings_->MyIpAddress(&result);
584 }
585
Jason Monk9fd69be2014-03-18 11:55:59 -0400586 if (!success) {
587 args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
588 } else {
589 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result));
590 }
Jason Monkfc934182013-07-22 13:20:39 -0400591 }
592
593 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400594 static void MyIpAddressExCallback(
595 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400596 Context* context =
597 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
598
599 std::string ip_address_list;
600 bool success;
601
602 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400603 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400604
605 // We shouldn't be called with any arguments, but will not complain if
606 // we are.
607 success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
608 }
609
610 if (!success)
611 ip_address_list = std::string();
Jason Monk9fd69be2014-03-18 11:55:59 -0400612 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400613 }
614
615 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400616 static void DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400617 Context* context =
618 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
619
620 // We need at least one string argument.
621 std::string hostname;
Jason Monk9fd69be2014-03-18 11:55:59 -0400622 if (!GetHostnameArgument(args, &hostname)) {
623 return;
624 }
Jason Monkfc934182013-07-22 13:20:39 -0400625
626 std::string ip_address;
627 bool success;
628
629 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400630 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400631 success = context->js_bindings_->DnsResolve(hostname, &ip_address);
632 }
633
Jason Monk9fd69be2014-03-18 11:55:59 -0400634 if (success) {
635 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address));
636 } else {
637 args.GetReturnValue().SetNull();
638 }
Jason Monkfc934182013-07-22 13:20:39 -0400639 }
640
641 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400642 static void DnsResolveExCallback(
643 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400644 Context* context =
645 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
646
647 // We need at least one string argument.
648 std::string hostname;
Jason Monk9fd69be2014-03-18 11:55:59 -0400649 if (!GetHostnameArgument(args, &hostname)) {
650 args.GetReturnValue().SetNull();
651 return;
652 }
Jason Monkfc934182013-07-22 13:20:39 -0400653
654 std::string ip_address_list;
655 bool success;
656
657 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400658 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400659 success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
660 }
661
662 if (!success)
663 ip_address_list = std::string();
664
Jason Monk9fd69be2014-03-18 11:55:59 -0400665 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400666 }
667
668 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400669 static void SortIpAddressListCallback(
670 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400671 // We need at least one string argument.
Jason Monk9fd69be2014-03-18 11:55:59 -0400672 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
673 args.GetReturnValue().SetNull();
674 return;
675 }
Jason Monkfc934182013-07-22 13:20:39 -0400676
677 std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
678 std::string sorted_ip_address_list;
679 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
Jason Monk9fd69be2014-03-18 11:55:59 -0400680 if (!success) {
681 args.GetReturnValue().Set(false);
682 return;
683 }
684 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400685 }
686
687 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400688 static void IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400689 // We need at least 2 string arguments.
690 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
Jason Monk9fd69be2014-03-18 11:55:59 -0400691 args[1].IsEmpty() || !args[1]->IsString()) {
692 args.GetReturnValue().SetNull();
693 return;
694 }
Jason Monkfc934182013-07-22 13:20:39 -0400695
696 std::string ip_address = V8StringToUTF8(args[0]->ToString());
697 std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
Jason Monk9fd69be2014-03-18 11:55:59 -0400698 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
Jason Monkfc934182013-07-22 13:20:39 -0400699 }
700
701 ProxyResolverJSBindings* js_bindings_;
Jason Monkf5cf6e32013-07-23 17:26:23 -0400702 ProxyErrorListener* error_listener_;
Jason Monk9fd69be2014-03-18 11:55:59 -0400703 v8::Isolate* isolate_;
Jason Monkfc934182013-07-22 13:20:39 -0400704 v8::Persistent<v8::External> v8_this_;
705 v8::Persistent<v8::Context> v8_context_;
706};
707
708// ProxyResolverV8 ------------------------------------------------------------
709
710ProxyResolverV8::ProxyResolverV8(
Jason Monkf5cf6e32013-07-23 17:26:23 -0400711 ProxyResolverJSBindings* custom_js_bindings,
712 ProxyErrorListener* error_listener)
713 : context_(NULL), js_bindings_(custom_js_bindings),
714 error_listener_(error_listener) {
Jason Monk7439df12014-10-29 12:48:55 -0400715 v8::V8::Initialize();
Jason Monkfc934182013-07-22 13:20:39 -0400716}
717
718ProxyResolverV8::~ProxyResolverV8() {
Jason Monkc06a8d62013-08-19 10:36:23 -0400719 if (context_ != NULL) {
720 delete context_;
721 context_ = NULL;
722 }
723 if (js_bindings_ != NULL) {
724 delete js_bindings_;
725 }
Jason Monkfc934182013-07-22 13:20:39 -0400726}
727
Jason Monkf5cf6e32013-07-23 17:26:23 -0400728int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
729 android::String16* results) {
Jason Monkfc934182013-07-22 13:20:39 -0400730 // If the V8 instance has not been initialized (either because
731 // SetPacScript() wasn't called yet, or because it failed.
732 if (context_ == NULL)
733 return ERR_FAILED;
734
735 // Otherwise call into V8.
736 int rv = context_->ResolveProxy(spec, host, results);
737
738 return rv;
739}
740
Jason Monkfc934182013-07-22 13:20:39 -0400741void ProxyResolverV8::PurgeMemory() {
742 context_->PurgeMemory();
743}
744
Jason Monk40adcb52013-08-23 17:19:31 -0400745int ProxyResolverV8::SetPacScript(const android::String16& script_data) {
Jason Monkfc934182013-07-22 13:20:39 -0400746 if (context_ != NULL) {
747 delete context_;
Jason Monkc06a8d62013-08-19 10:36:23 -0400748 context_ = NULL;
Jason Monkfc934182013-07-22 13:20:39 -0400749 }
Jason Monkf5cf6e32013-07-23 17:26:23 -0400750 if (script_data.size() == 0)
Jason Monkfc934182013-07-22 13:20:39 -0400751 return ERR_PAC_SCRIPT_FAILED;
752
753 // Try parsing the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400754 context_ = new Context(js_bindings_, error_listener_, v8::Isolate::New());
Jason Monkfc934182013-07-22 13:20:39 -0400755 int rv;
756 if ((rv = context_->InitV8(script_data)) != OK) {
757 context_ = NULL;
758 }
759 if (rv != OK)
760 context_ = NULL;
761 return rv;
762}
763
764} // namespace net