blob: 285091d76b408fa07c5cdc10bcfedec7c29610b2 [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 {
Jason Monkf5cf6e32013-07-23 17:26:23 -0400106 return 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.
119class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
120 public:
121 // |ascii| must be a NULL-terminated C string, and must remain valid
122 // throughout this object's lifetime.
123 V8ExternalASCIILiteral(const char* ascii, size_t length)
124 : ascii_(ascii), length_(length) {
Jason Monkfc934182013-07-22 13:20:39 -0400125 }
126
127 virtual const char* data() const {
128 return ascii_;
129 }
130
131 virtual size_t length() const {
132 return length_;
133 }
134
135 private:
136 const char* ascii_;
137 size_t length_;
138};
139
140// When creating a v8::String from a C++ string we have two choices: create
141// a copy, or create a wrapper that shares the same underlying storage.
142// For small strings it is better to just make a copy, whereas for large
143// strings there are savings by sharing the storage. This number identifies
144// the cutoff length for when to start wrapping rather than creating copies.
145const size_t kMaxStringBytesForCopy = 256;
146
147template <class string_type>
148inline typename string_type::value_type* WriteInto(string_type* str,
149 size_t length_with_null) {
150 str->reserve(length_with_null);
151 str->resize(length_with_null - 1);
152 return &((*str)[0]);
153}
154
155// Converts a V8 String to a UTF8 std::string.
156std::string V8StringToUTF8(v8::Handle<v8::String> s) {
157 std::string result;
158 s->WriteUtf8(WriteInto(&result, s->Length() + 1));
159 return result;
160}
161
162// Converts a V8 String to a UTF16 string.
Jason Monkf5cf6e32013-07-23 17:26:23 -0400163android::String16 V8StringToUTF16(v8::Handle<v8::String> s) {
Jason Monkfc934182013-07-22 13:20:39 -0400164 int len = s->Length();
Jason Monkf5cf6e32013-07-23 17:26:23 -0400165 char16_t* buf = new char16_t[len + 1];
166 s->Write(buf, 0, len);
167 android::String16 ret(buf, len);
168 delete buf;
169 return ret;
170}
171
172std::string UTF16ToASCII(const android::String16& str) {
Jason Monk40adcb52013-08-23 17:19:31 -0400173 android::String8 rstr(str);
174 return std::string(rstr.string());
Jason Monkfc934182013-07-22 13:20:39 -0400175}
176
177// Converts an ASCII std::string to a V8 string.
178v8::Local<v8::String> ASCIIStringToV8String(const std::string& s) {
179 return v8::String::New(s.data(), s.size());
180}
181
Jason Monk235ec0f2014-03-20 18:36:26 +0000182v8::Local<v8::String> UTF16StringToV8String(const android::String16& s) {
Jason Monkf5cf6e32013-07-23 17:26:23 -0400183 return v8::String::New(s.string(), s.size());
184}
185
Jason Monkfc934182013-07-22 13:20:39 -0400186// Converts an ASCII string literal to a V8 string.
Jason Monk235ec0f2014-03-20 18:36:26 +0000187v8::Local<v8::String> ASCIILiteralToV8String(const char* ascii) {
Jason Monkfc934182013-07-22 13:20:39 -0400188// DCHECK(IsStringASCII(ascii));
189 size_t length = strlen(ascii);
190 if (length <= kMaxStringBytesForCopy)
191 return v8::String::New(ascii, length);
192 return v8::String::NewExternal(new V8ExternalASCIILiteral(ascii, length));
193}
194
195// Stringizes a V8 object by calling its toString() method. Returns true
196// on success. This may fail if the toString() throws an exception.
197bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
Jason Monk235ec0f2014-03-20 18:36:26 +0000198 android::String16* utf16_result) {
Jason Monkfc934182013-07-22 13:20:39 -0400199 if (object.IsEmpty())
200 return false;
201
Jason Monk235ec0f2014-03-20 18:36:26 +0000202 v8::HandleScope scope;
Jason Monkfc934182013-07-22 13:20:39 -0400203 v8::Local<v8::String> str_object = object->ToString();
204 if (str_object.IsEmpty())
205 return false;
206 *utf16_result = V8StringToUTF16(str_object);
207 return true;
208}
209
210// Extracts an hostname argument from |args|. On success returns true
211// and fills |*hostname| with the result.
Jason Monk235ec0f2014-03-20 18:36:26 +0000212bool GetHostnameArgument(const v8::Arguments& args, std::string* hostname) {
Jason Monkfc934182013-07-22 13:20:39 -0400213 // The first argument should be a string.
214 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
215 return false;
216
Jason Monkf5cf6e32013-07-23 17:26:23 -0400217 const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400218
219 // If the hostname is already in ASCII, simply return it as is.
220 if (IsStringASCII(hostname_utf16)) {
221 *hostname = UTF16ToASCII(hostname_utf16);
222 return true;
223 }
224 return false;
225}
226
227// Wrapper for passing around IP address strings and IPAddressNumber objects.
228struct IPAddress {
229 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
230 : string_value(ip_string),
231 ip_address_number(ip_number) {
232 }
233
234 // Used for sorting IP addresses in ascending order in SortIpAddressList().
235 // IP6 addresses are placed ahead of IPv4 addresses.
236 bool operator<(const IPAddress& rhs) const {
237 const IPAddressNumber& ip1 = this->ip_address_number;
238 const IPAddressNumber& ip2 = rhs.ip_address_number;
239 if (ip1.size() != ip2.size())
240 return ip1.size() > ip2.size(); // IPv6 before IPv4.
241 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
242 }
243
244 std::string string_value;
245 IPAddressNumber ip_address_number;
246};
247
248template<typename STR>
249bool RemoveCharsT(const STR& input,
250 const typename STR::value_type remove_chars[],
251 STR* output) {
252 bool removed = false;
253 size_t found;
254
255 *output = input;
256
257 found = output->find_first_of(remove_chars);
258 while (found != STR::npos) {
259 removed = true;
260 output->replace(found, 1, STR());
261 found = output->find_first_of(remove_chars, found);
262 }
263
264 return removed;
265}
266
Jason Monkfc934182013-07-22 13:20:39 -0400267bool RemoveChars(const std::string& input,
268 const char remove_chars[],
269 std::string* output) {
270 return RemoveCharsT(input, remove_chars, output);
271}
272
273// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
274// semi-colon delimited string containing IP addresses.
275// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
276// IP addresses or an empty string if unable to sort the IP address list.
277// Returns 'true' if the sorting was successful, and 'false' if the input was an
278// empty string, a string of separators (";" in this case), or if any of the IP
279// addresses in the input list failed to parse.
280bool SortIpAddressList(const std::string& ip_address_list,
281 std::string* sorted_ip_address_list) {
282 sorted_ip_address_list->clear();
283
284 // Strip all whitespace (mimics IE behavior).
285 std::string cleaned_ip_address_list;
286 RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
287 if (cleaned_ip_address_list.empty())
288 return false;
289
290 // Split-up IP addresses and store them in a vector.
291 std::vector<IPAddress> ip_vector;
292 IPAddressNumber ip_num;
293 char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
294 while (tok_list != NULL) {
295 if (!ParseIPLiteralToNumber(tok_list, &ip_num))
296 return false;
297 ip_vector.push_back(IPAddress(tok_list, ip_num));
Jason Monk86b75a52013-08-23 17:10:51 -0400298 tok_list = strtok(NULL, ";");
Jason Monkfc934182013-07-22 13:20:39 -0400299 }
300
301 if (ip_vector.empty()) // Can happen if we have something like
302 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
303
304 // Sort lists according to ascending numeric value.
305 if (ip_vector.size() > 1)
306 std::stable_sort(ip_vector.begin(), ip_vector.end());
307
308 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
309 // IPv4).
310 for (size_t i = 0; i < ip_vector.size(); ++i) {
311 if (i > 0)
312 *sorted_ip_address_list += ";";
313 *sorted_ip_address_list += ip_vector[i].string_value;
314 }
315 return true;
316}
317
318
319// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
320// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
321// slash-delimited IP prefix with the top 'n' bits specified in the bit
322// field. This returns 'true' if the address is in the same subnet, and
323// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
324// format, or if an address and prefix of different types are used (e.g. IPv6
325// address and IPv4 prefix).
326bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
327 IPAddressNumber address;
Jason Monkeafc0fa2013-08-23 17:05:52 -0400328 std::string cleaned_ip_address;
329 if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
330 return false;
Jason Monkfc934182013-07-22 13:20:39 -0400331 if (!ParseIPLiteralToNumber(ip_address, &address))
332 return false;
333
334 IPAddressNumber prefix;
335 size_t prefix_length_in_bits;
336 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
337 return false;
338
339 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
340 if (address.size() != prefix.size())
341 return false;
342
343 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
344}
345
346} // namespace
347
348// ProxyResolverV8::Context ---------------------------------------------------
349
350class ProxyResolverV8::Context {
351 public:
Jason Monkf5cf6e32013-07-23 17:26:23 -0400352 explicit Context(ProxyResolverJSBindings* js_bindings,
Jason Monk235ec0f2014-03-20 18:36:26 +0000353 ProxyErrorListener* error_listener)
354 : js_bindings_(js_bindings), error_listener_(error_listener) {
Jason Monkfc934182013-07-22 13:20:39 -0400355 }
356
357 ~Context() {
Jason Monk235ec0f2014-03-20 18:36:26 +0000358 v8::Locker locked;
Jason Monkfc934182013-07-22 13:20:39 -0400359
Jason Monk235ec0f2014-03-20 18:36:26 +0000360 v8_this_.Dispose();
361 v8_context_.Dispose();
362
363 // Run the V8 garbage collector. We do this to be sure the
364 // ExternalStringResource objects we allocated get properly disposed.
365 // Otherwise when running the unit-tests they may get leaked.
366 // See crbug.com/48145.
367 PurgeMemory();
Jason Monkfc934182013-07-22 13:20:39 -0400368 }
369
Jason Monk40adcb52013-08-23 17:19:31 -0400370 int ResolveProxy(const android::String16 url, const android::String16 host,
371 android::String16* results) {
Jason Monk235ec0f2014-03-20 18:36:26 +0000372 v8::Locker locked;
373 v8::HandleScope scope;
Jason Monkfc934182013-07-22 13:20:39 -0400374
Jason Monk235ec0f2014-03-20 18:36:26 +0000375 v8::Context::Scope function_scope(v8_context_);
Jason Monkfc934182013-07-22 13:20:39 -0400376
377 v8::Local<v8::Value> function;
378 if (!GetFindProxyForURL(&function)) {
Jason Monk40adcb52013-08-23 17:19:31 -0400379 error_listener_->ErrorMessage(
380 android::String16("FindProxyForURL() is undefined"));
Jason Monkfc934182013-07-22 13:20:39 -0400381 return ERR_PAC_SCRIPT_FAILED;
382 }
383
384 v8::Handle<v8::Value> argv[] = {
Jason Monk235ec0f2014-03-20 18:36:26 +0000385 UTF16StringToV8String(url),
386 UTF16StringToV8String(host) };
Jason Monkfc934182013-07-22 13:20:39 -0400387
388 v8::TryCatch try_catch;
389 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
Jason Monk235ec0f2014-03-20 18:36:26 +0000390 v8_context_->Global(), 2, argv);
Jason Monkfc934182013-07-22 13:20:39 -0400391
392 if (try_catch.HasCaught()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400393 error_listener_->ErrorMessage(
394 V8StringToUTF16(try_catch.Message()->Get()));
Jason Monkfc934182013-07-22 13:20:39 -0400395 return ERR_PAC_SCRIPT_FAILED;
396 }
397
398 if (!ret->IsString()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400399 error_listener_->ErrorMessage(
400 android::String16("FindProxyForURL() did not return a string."));
Jason Monkfc934182013-07-22 13:20:39 -0400401 return ERR_PAC_SCRIPT_FAILED;
402 }
403
Jason Monkf5cf6e32013-07-23 17:26:23 -0400404 *results = V8StringToUTF16(ret->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400405
Jason Monkf5cf6e32013-07-23 17:26:23 -0400406 if (!IsStringASCII(*results)) {
Jason Monkfc934182013-07-22 13:20:39 -0400407 // TODO: Rather than failing when a wide string is returned, we
408 // could extend the parsing to handle IDNA hostnames by
409 // converting them to ASCII punycode.
410 // crbug.com/47234
Jason Monk40adcb52013-08-23 17:19:31 -0400411 error_listener_->ErrorMessage(
412 android::String16("FindProxyForURL() returned a non-ASCII string"));
Jason Monkfc934182013-07-22 13:20:39 -0400413 return ERR_PAC_SCRIPT_FAILED;
414 }
415
Jason Monkfc934182013-07-22 13:20:39 -0400416 return OK;
417 }
418
Jason Monkf5cf6e32013-07-23 17:26:23 -0400419 int InitV8(const android::String16& pac_script) {
Jason Monk235ec0f2014-03-20 18:36:26 +0000420 v8::Locker locked;
421 v8::HandleScope scope;
Jason Monkfc934182013-07-22 13:20:39 -0400422
Jason Monk235ec0f2014-03-20 18:36:26 +0000423 v8_this_ = v8::Persistent<v8::External>::New(v8::External::New(this));
Jason Monkfc934182013-07-22 13:20:39 -0400424 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
425
426 // Attach the javascript bindings.
427 v8::Local<v8::FunctionTemplate> alert_template =
Jason Monk235ec0f2014-03-20 18:36:26 +0000428 v8::FunctionTemplate::New(&AlertCallback, v8_this_);
429 global_template->Set(ASCIILiteralToV8String("alert"), alert_template);
Jason Monkfc934182013-07-22 13:20:39 -0400430
431 v8::Local<v8::FunctionTemplate> my_ip_address_template =
Jason Monk235ec0f2014-03-20 18:36:26 +0000432 v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this_);
433 global_template->Set(ASCIILiteralToV8String("myIpAddress"),
Jason Monkfc934182013-07-22 13:20:39 -0400434 my_ip_address_template);
435
436 v8::Local<v8::FunctionTemplate> dns_resolve_template =
Jason Monk235ec0f2014-03-20 18:36:26 +0000437 v8::FunctionTemplate::New(&DnsResolveCallback, v8_this_);
438 global_template->Set(ASCIILiteralToV8String("dnsResolve"),
Jason Monkfc934182013-07-22 13:20:39 -0400439 dns_resolve_template);
440
441 // Microsoft's PAC extensions:
442
443 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
Jason Monk235ec0f2014-03-20 18:36:26 +0000444 v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this_);
445 global_template->Set(ASCIILiteralToV8String("dnsResolveEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400446 dns_resolve_ex_template);
447
448 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
Jason Monk235ec0f2014-03-20 18:36:26 +0000449 v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this_);
450 global_template->Set(ASCIILiteralToV8String("myIpAddressEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400451 my_ip_address_ex_template);
452
453 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
Jason Monk235ec0f2014-03-20 18:36:26 +0000454 v8::FunctionTemplate::New(&SortIpAddressListCallback, v8_this_);
455 global_template->Set(ASCIILiteralToV8String("sortIpAddressList"),
Jason Monkfc934182013-07-22 13:20:39 -0400456 sort_ip_address_list_template);
457
458 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
Jason Monk235ec0f2014-03-20 18:36:26 +0000459 v8::FunctionTemplate::New(&IsInNetExCallback, v8_this_);
460 global_template->Set(ASCIILiteralToV8String("isInNetEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400461 is_in_net_ex_template);
462
Jason Monk235ec0f2014-03-20 18:36:26 +0000463 v8_context_ = v8::Context::New(NULL, global_template);
Jason Monkfc934182013-07-22 13:20:39 -0400464
Jason Monk235ec0f2014-03-20 18:36:26 +0000465 v8::Context::Scope ctx(v8_context_);
Jason Monkfc934182013-07-22 13:20:39 -0400466
467 // Add the PAC utility functions to the environment.
468 // (This script should never fail, as it is a string literal!)
469 // Note that the two string literals are concatenated.
470 int rv = RunScript(
Jason Monk235ec0f2014-03-20 18:36:26 +0000471 ASCIILiteralToV8String(
Jason Monkfc934182013-07-22 13:20:39 -0400472 PROXY_RESOLVER_SCRIPT
473 PROXY_RESOLVER_SCRIPT_EX),
474 kPacUtilityResourceName);
475 if (rv != OK) {
476 return rv;
477 }
478
479 // Add the user's PAC code to the environment.
Jason Monk235ec0f2014-03-20 18:36:26 +0000480 rv = RunScript(UTF16StringToV8String(pac_script), kPacResourceName);
Jason Monkfc934182013-07-22 13:20:39 -0400481 if (rv != OK) {
482 return rv;
483 }
484
485 // At a minimum, the FindProxyForURL() function must be defined for this
486 // to be a legitimiate PAC script.
487 v8::Local<v8::Value> function;
488 if (!GetFindProxyForURL(&function))
489 return ERR_PAC_SCRIPT_FAILED;
490
491 return OK;
492 }
493
494 void PurgeMemory() {
Jason Monk235ec0f2014-03-20 18:36:26 +0000495 v8::Locker locked;
496 // Repeatedly call the V8 idle notification until it returns true ("nothing
497 // more to free"). Note that it makes more sense to do this than to
498 // implement a new "delete everything" pass because object references make
499 // it difficult to free everything possible in just one pass.
500 while (!v8::V8::IdleNotification())
501 ;
Jason Monkfc934182013-07-22 13:20:39 -0400502 }
503
504 private:
505 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
Jason Monk235ec0f2014-03-20 18:36:26 +0000506 *function = v8_context_->Global()->Get(
507 ASCIILiteralToV8String("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 Monk235ec0f2014-03-20 18:36:26 +0000525 v8::ScriptOrigin(ASCIILiteralToV8String(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 Monk235ec0f2014-03-20 18:36:26 +0000542 static v8::Handle<v8::Value> AlertCallback(const v8::Arguments& 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 Monk235ec0f2014-03-20 18:36:26 +0000555 if (!V8ObjectToUTF16String(args[0], &message))
556 return v8::Undefined(); // 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 Monk235ec0f2014-03-20 18:36:26 +0000560 return v8::Undefined();
Jason Monkfc934182013-07-22 13:20:39 -0400561 }
562
563 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
Jason Monk235ec0f2014-03-20 18:36:26 +0000564 static v8::Handle<v8::Value> MyIpAddressCallback(const v8::Arguments& 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 Monk235ec0f2014-03-20 18:36:26 +0000572 v8::Unlocker unlocker;
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 Monk235ec0f2014-03-20 18:36:26 +0000579 if (!success)
580 return ASCIILiteralToV8String("127.0.0.1");
581 return ASCIIStringToV8String(result);
Jason Monkfc934182013-07-22 13:20:39 -0400582 }
583
584 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
Jason Monk235ec0f2014-03-20 18:36:26 +0000585 static v8::Handle<v8::Value> MyIpAddressExCallback(
586 const v8::Arguments& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400587 Context* context =
588 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
589
590 std::string ip_address_list;
591 bool success;
592
593 {
Jason Monk235ec0f2014-03-20 18:36:26 +0000594 v8::Unlocker unlocker;
Jason Monkfc934182013-07-22 13:20:39 -0400595
596 // We shouldn't be called with any arguments, but will not complain if
597 // we are.
598 success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
599 }
600
601 if (!success)
602 ip_address_list = std::string();
Jason Monk235ec0f2014-03-20 18:36:26 +0000603 return ASCIIStringToV8String(ip_address_list);
Jason Monkfc934182013-07-22 13:20:39 -0400604 }
605
606 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
Jason Monk235ec0f2014-03-20 18:36:26 +0000607 static v8::Handle<v8::Value> DnsResolveCallback(const v8::Arguments& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400608 Context* context =
609 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
610
611 // We need at least one string argument.
612 std::string hostname;
Jason Monk235ec0f2014-03-20 18:36:26 +0000613 if (!GetHostnameArgument(args, &hostname))
614 return v8::Null();
Jason Monkfc934182013-07-22 13:20:39 -0400615
616 std::string ip_address;
617 bool success;
618
619 {
Jason Monk235ec0f2014-03-20 18:36:26 +0000620 v8::Unlocker unlocker;
Jason Monkfc934182013-07-22 13:20:39 -0400621 success = context->js_bindings_->DnsResolve(hostname, &ip_address);
622 }
623
Jason Monk235ec0f2014-03-20 18:36:26 +0000624 return success ? ASCIIStringToV8String(ip_address) : v8::Null();
Jason Monkfc934182013-07-22 13:20:39 -0400625 }
626
627 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
Jason Monk235ec0f2014-03-20 18:36:26 +0000628 static v8::Handle<v8::Value> DnsResolveExCallback(const v8::Arguments& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400629 Context* context =
630 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
631
632 // We need at least one string argument.
633 std::string hostname;
Jason Monk235ec0f2014-03-20 18:36:26 +0000634 if (!GetHostnameArgument(args, &hostname))
635 return v8::Undefined();
Jason Monkfc934182013-07-22 13:20:39 -0400636
637 std::string ip_address_list;
638 bool success;
639
640 {
Jason Monk235ec0f2014-03-20 18:36:26 +0000641 v8::Unlocker unlocker;
Jason Monkfc934182013-07-22 13:20:39 -0400642 success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
643 }
644
645 if (!success)
646 ip_address_list = std::string();
647
Jason Monk235ec0f2014-03-20 18:36:26 +0000648 return ASCIIStringToV8String(ip_address_list);
Jason Monkfc934182013-07-22 13:20:39 -0400649 }
650
651 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
Jason Monk235ec0f2014-03-20 18:36:26 +0000652 static v8::Handle<v8::Value> SortIpAddressListCallback(
653 const v8::Arguments& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400654 // We need at least one string argument.
Jason Monk235ec0f2014-03-20 18:36:26 +0000655 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
656 return v8::Null();
Jason Monkfc934182013-07-22 13:20:39 -0400657
658 std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
659 std::string sorted_ip_address_list;
660 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
Jason Monk235ec0f2014-03-20 18:36:26 +0000661 if (!success)
662 return v8::False();
663 return ASCIIStringToV8String(sorted_ip_address_list);
Jason Monkfc934182013-07-22 13:20:39 -0400664 }
665
666 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
Jason Monk235ec0f2014-03-20 18:36:26 +0000667 static v8::Handle<v8::Value> IsInNetExCallback(const v8::Arguments& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400668 // We need at least 2 string arguments.
669 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
Jason Monk235ec0f2014-03-20 18:36:26 +0000670 args[1].IsEmpty() || !args[1]->IsString())
671 return v8::Null();
Jason Monkfc934182013-07-22 13:20:39 -0400672
673 std::string ip_address = V8StringToUTF8(args[0]->ToString());
674 std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
Jason Monk235ec0f2014-03-20 18:36:26 +0000675 return IsInNetEx(ip_address, ip_prefix) ? v8::True() : v8::False();
Jason Monkfc934182013-07-22 13:20:39 -0400676 }
677
678 ProxyResolverJSBindings* js_bindings_;
Jason Monkf5cf6e32013-07-23 17:26:23 -0400679 ProxyErrorListener* error_listener_;
Jason Monkfc934182013-07-22 13:20:39 -0400680 v8::Persistent<v8::External> v8_this_;
681 v8::Persistent<v8::Context> v8_context_;
682};
683
684// ProxyResolverV8 ------------------------------------------------------------
685
686ProxyResolverV8::ProxyResolverV8(
Jason Monkf5cf6e32013-07-23 17:26:23 -0400687 ProxyResolverJSBindings* custom_js_bindings,
688 ProxyErrorListener* error_listener)
689 : context_(NULL), js_bindings_(custom_js_bindings),
690 error_listener_(error_listener) {
691
Jason Monkfc934182013-07-22 13:20:39 -0400692}
693
694ProxyResolverV8::~ProxyResolverV8() {
Jason Monkc06a8d62013-08-19 10:36:23 -0400695 if (context_ != NULL) {
696 delete context_;
697 context_ = NULL;
698 }
699 if (js_bindings_ != NULL) {
700 delete js_bindings_;
701 }
Jason Monkfc934182013-07-22 13:20:39 -0400702}
703
Jason Monkf5cf6e32013-07-23 17:26:23 -0400704int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
705 android::String16* results) {
Jason Monkfc934182013-07-22 13:20:39 -0400706 // If the V8 instance has not been initialized (either because
707 // SetPacScript() wasn't called yet, or because it failed.
708 if (context_ == NULL)
709 return ERR_FAILED;
710
711 // Otherwise call into V8.
712 int rv = context_->ResolveProxy(spec, host, results);
713
714 return rv;
715}
716
Jason Monkfc934182013-07-22 13:20:39 -0400717void ProxyResolverV8::PurgeMemory() {
718 context_->PurgeMemory();
719}
720
Jason Monk40adcb52013-08-23 17:19:31 -0400721int ProxyResolverV8::SetPacScript(const android::String16& script_data) {
Jason Monkfc934182013-07-22 13:20:39 -0400722 if (context_ != NULL) {
723 delete context_;
Jason Monkc06a8d62013-08-19 10:36:23 -0400724 context_ = NULL;
Jason Monkfc934182013-07-22 13:20:39 -0400725 }
Jason Monkf5cf6e32013-07-23 17:26:23 -0400726 if (script_data.size() == 0)
Jason Monkfc934182013-07-22 13:20:39 -0400727 return ERR_PAC_SCRIPT_FAILED;
728
729 // Try parsing the PAC script.
Jason Monk235ec0f2014-03-20 18:36:26 +0000730 context_ = new Context(js_bindings_, error_listener_);
Jason Monkfc934182013-07-22 13:20:39 -0400731 int rv;
732 if ((rv = context_->InitV8(script_data)) != OK) {
733 context_ = NULL;
734 }
735 if (rv != OK)
736 context_ = NULL;
737 return rv;
738}
739
740} // namespace net