blob: dcd27a18ba643834690b5233a843166c97049553 [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.
Jason Monk9fd69be2014-03-18 11:55:59 -0400178v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
179 return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
Jason Monkfc934182013-07-22 13:20:39 -0400180}
181
Jason Monk9fd69be2014-03-18 11:55:59 -0400182v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const android::String16& s) {
183 return v8::String::NewFromTwoByte(isolate, s.string(), v8::String::kNormalString, s.size());
Jason Monkf5cf6e32013-07-23 17:26:23 -0400184}
185
Jason Monkfc934182013-07-22 13:20:39 -0400186// Converts an ASCII string literal to a V8 string.
Jason Monk9fd69be2014-03-18 11:55:59 -0400187v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) {
Jason Monkfc934182013-07-22 13:20:39 -0400188// DCHECK(IsStringASCII(ascii));
189 size_t length = strlen(ascii);
190 if (length <= kMaxStringBytesForCopy)
Jason Monk9fd69be2014-03-18 11:55:59 -0400191 return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length);
192 return v8::String::NewExternal(isolate, new V8ExternalASCIILiteral(ascii, length));
Jason Monkfc934182013-07-22 13:20:39 -0400193}
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 Monk9fd69be2014-03-18 11:55:59 -0400198 android::String16* utf16_result,
199 v8::Isolate* isolate) {
Jason Monkfc934182013-07-22 13:20:39 -0400200 if (object.IsEmpty())
201 return false;
202
Jason Monk9fd69be2014-03-18 11:55:59 -0400203 v8::HandleScope scope(isolate);
Jason Monkfc934182013-07-22 13:20:39 -0400204 v8::Local<v8::String> str_object = object->ToString();
205 if (str_object.IsEmpty())
206 return false;
207 *utf16_result = V8StringToUTF16(str_object);
208 return true;
209}
210
211// Extracts an hostname argument from |args|. On success returns true
212// and fills |*hostname| with the result.
Jason Monk9fd69be2014-03-18 11:55:59 -0400213bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args, std::string* hostname) {
Jason Monkfc934182013-07-22 13:20:39 -0400214 // The first argument should be a string.
215 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
216 return false;
217
Jason Monkf5cf6e32013-07-23 17:26:23 -0400218 const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400219
220 // If the hostname is already in ASCII, simply return it as is.
221 if (IsStringASCII(hostname_utf16)) {
222 *hostname = UTF16ToASCII(hostname_utf16);
223 return true;
224 }
225 return false;
226}
227
228// Wrapper for passing around IP address strings and IPAddressNumber objects.
229struct IPAddress {
230 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
231 : string_value(ip_string),
232 ip_address_number(ip_number) {
233 }
234
235 // Used for sorting IP addresses in ascending order in SortIpAddressList().
236 // IP6 addresses are placed ahead of IPv4 addresses.
237 bool operator<(const IPAddress& rhs) const {
238 const IPAddressNumber& ip1 = this->ip_address_number;
239 const IPAddressNumber& ip2 = rhs.ip_address_number;
240 if (ip1.size() != ip2.size())
241 return ip1.size() > ip2.size(); // IPv6 before IPv4.
242 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
243 }
244
245 std::string string_value;
246 IPAddressNumber ip_address_number;
247};
248
249template<typename STR>
250bool RemoveCharsT(const STR& input,
251 const typename STR::value_type remove_chars[],
252 STR* output) {
253 bool removed = false;
254 size_t found;
255
256 *output = input;
257
258 found = output->find_first_of(remove_chars);
259 while (found != STR::npos) {
260 removed = true;
261 output->replace(found, 1, STR());
262 found = output->find_first_of(remove_chars, found);
263 }
264
265 return removed;
266}
267
Jason Monkfc934182013-07-22 13:20:39 -0400268bool RemoveChars(const std::string& input,
269 const char remove_chars[],
270 std::string* output) {
271 return RemoveCharsT(input, remove_chars, output);
272}
273
274// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
275// semi-colon delimited string containing IP addresses.
276// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
277// IP addresses or an empty string if unable to sort the IP address list.
278// Returns 'true' if the sorting was successful, and 'false' if the input was an
279// empty string, a string of separators (";" in this case), or if any of the IP
280// addresses in the input list failed to parse.
281bool SortIpAddressList(const std::string& ip_address_list,
282 std::string* sorted_ip_address_list) {
283 sorted_ip_address_list->clear();
284
285 // Strip all whitespace (mimics IE behavior).
286 std::string cleaned_ip_address_list;
287 RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
288 if (cleaned_ip_address_list.empty())
289 return false;
290
291 // Split-up IP addresses and store them in a vector.
292 std::vector<IPAddress> ip_vector;
293 IPAddressNumber ip_num;
294 char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
295 while (tok_list != NULL) {
296 if (!ParseIPLiteralToNumber(tok_list, &ip_num))
297 return false;
298 ip_vector.push_back(IPAddress(tok_list, ip_num));
Jason Monk86b75a52013-08-23 17:10:51 -0400299 tok_list = strtok(NULL, ";");
Jason Monkfc934182013-07-22 13:20:39 -0400300 }
301
302 if (ip_vector.empty()) // Can happen if we have something like
303 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
304
305 // Sort lists according to ascending numeric value.
306 if (ip_vector.size() > 1)
307 std::stable_sort(ip_vector.begin(), ip_vector.end());
308
309 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
310 // IPv4).
311 for (size_t i = 0; i < ip_vector.size(); ++i) {
312 if (i > 0)
313 *sorted_ip_address_list += ";";
314 *sorted_ip_address_list += ip_vector[i].string_value;
315 }
316 return true;
317}
318
319
320// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
321// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
322// slash-delimited IP prefix with the top 'n' bits specified in the bit
323// field. This returns 'true' if the address is in the same subnet, and
324// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
325// format, or if an address and prefix of different types are used (e.g. IPv6
326// address and IPv4 prefix).
327bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
328 IPAddressNumber address;
Jason Monkeafc0fa2013-08-23 17:05:52 -0400329 std::string cleaned_ip_address;
330 if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
331 return false;
Jason Monkfc934182013-07-22 13:20:39 -0400332 if (!ParseIPLiteralToNumber(ip_address, &address))
333 return false;
334
335 IPAddressNumber prefix;
336 size_t prefix_length_in_bits;
337 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
338 return false;
339
340 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
341 if (address.size() != prefix.size())
342 return false;
343
344 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
345}
346
347} // namespace
348
349// ProxyResolverV8::Context ---------------------------------------------------
350
351class ProxyResolverV8::Context {
352 public:
Jason Monkf5cf6e32013-07-23 17:26:23 -0400353 explicit Context(ProxyResolverJSBindings* js_bindings,
Jason Monk9fd69be2014-03-18 11:55:59 -0400354 ProxyErrorListener* error_listener, v8::Isolate* isolate)
355 : js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) {
Jason Monkfc934182013-07-22 13:20:39 -0400356 }
357
358 ~Context() {
Jason Monk9fd69be2014-03-18 11:55:59 -0400359 v8::Locker locked(isolate_);
360 v8::Isolate::Scope isolate_scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400361
Jason Monk9fd69be2014-03-18 11:55:59 -0400362 v8_this_.Reset();
363 v8_context_.Reset();
Jason Monkfc934182013-07-22 13:20:39 -0400364 }
365
Jason Monk40adcb52013-08-23 17:19:31 -0400366 int ResolveProxy(const android::String16 url, const android::String16 host,
367 android::String16* results) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400368 v8::Locker locked(isolate_);
369 v8::Isolate::Scope isolate_scope(isolate_);
370 v8::HandleScope scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400371
Jason Monk9fd69be2014-03-18 11:55:59 -0400372 v8::Local<v8::Context> context =
373 v8::Local<v8::Context>::New(isolate_, v8_context_);
374 v8::Context::Scope function_scope(context);
Jason Monkfc934182013-07-22 13:20:39 -0400375
376 v8::Local<v8::Value> function;
377 if (!GetFindProxyForURL(&function)) {
Jason Monk40adcb52013-08-23 17:19:31 -0400378 error_listener_->ErrorMessage(
379 android::String16("FindProxyForURL() is undefined"));
Jason Monkfc934182013-07-22 13:20:39 -0400380 return ERR_PAC_SCRIPT_FAILED;
381 }
382
383 v8::Handle<v8::Value> argv[] = {
Jason Monk9fd69be2014-03-18 11:55:59 -0400384 UTF16StringToV8String(isolate_, url),
385 UTF16StringToV8String(isolate_, host) };
Jason Monkfc934182013-07-22 13:20:39 -0400386
387 v8::TryCatch try_catch;
388 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
Jason Monk9fd69be2014-03-18 11:55:59 -0400389 context->Global(), 2, argv);
Jason Monkfc934182013-07-22 13:20:39 -0400390
391 if (try_catch.HasCaught()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400392 error_listener_->ErrorMessage(
393 V8StringToUTF16(try_catch.Message()->Get()));
Jason Monkfc934182013-07-22 13:20:39 -0400394 return ERR_PAC_SCRIPT_FAILED;
395 }
396
397 if (!ret->IsString()) {
Jason Monk40adcb52013-08-23 17:19:31 -0400398 error_listener_->ErrorMessage(
399 android::String16("FindProxyForURL() did not return a string."));
Jason Monkfc934182013-07-22 13:20:39 -0400400 return ERR_PAC_SCRIPT_FAILED;
401 }
402
Jason Monkf5cf6e32013-07-23 17:26:23 -0400403 *results = V8StringToUTF16(ret->ToString());
Jason Monkfc934182013-07-22 13:20:39 -0400404
Jason Monkf5cf6e32013-07-23 17:26:23 -0400405 if (!IsStringASCII(*results)) {
Jason Monkfc934182013-07-22 13:20:39 -0400406 // TODO: Rather than failing when a wide string is returned, we
407 // could extend the parsing to handle IDNA hostnames by
408 // converting them to ASCII punycode.
409 // crbug.com/47234
Jason Monk40adcb52013-08-23 17:19:31 -0400410 error_listener_->ErrorMessage(
411 android::String16("FindProxyForURL() returned a non-ASCII string"));
Jason Monkfc934182013-07-22 13:20:39 -0400412 return ERR_PAC_SCRIPT_FAILED;
413 }
414
Jason Monkfc934182013-07-22 13:20:39 -0400415 return OK;
416 }
417
Jason Monkf5cf6e32013-07-23 17:26:23 -0400418 int InitV8(const android::String16& pac_script) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400419 v8::Locker locked(isolate_);
420 v8::Isolate::Scope isolate_scope(isolate_);
421 v8::HandleScope scope(isolate_);
Jason Monkfc934182013-07-22 13:20:39 -0400422
Jason Monk9fd69be2014-03-18 11:55:59 -0400423 v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
424 v8::Local<v8::External> v8_this =
425 v8::Local<v8::External>::New(isolate_, v8_this_);
Jason Monkfc934182013-07-22 13:20:39 -0400426 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
427
428 // Attach the javascript bindings.
429 v8::Local<v8::FunctionTemplate> alert_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400430 v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
431 global_template->Set(ASCIILiteralToV8String(isolate_, "alert"), alert_template);
Jason Monkfc934182013-07-22 13:20:39 -0400432
433 v8::Local<v8::FunctionTemplate> my_ip_address_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400434 v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
435 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
Jason Monkfc934182013-07-22 13:20:39 -0400436 my_ip_address_template);
437
438 v8::Local<v8::FunctionTemplate> dns_resolve_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400439 v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
440 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
Jason Monkfc934182013-07-22 13:20:39 -0400441 dns_resolve_template);
442
443 // Microsoft's PAC extensions:
444
445 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400446 v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
447 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400448 dns_resolve_ex_template);
449
450 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400451 v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
452 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400453 my_ip_address_ex_template);
454
455 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400456 v8::FunctionTemplate::New(isolate_, &SortIpAddressListCallback, v8_this);
457 global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
Jason Monkfc934182013-07-22 13:20:39 -0400458 sort_ip_address_list_template);
459
460 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
Jason Monk9fd69be2014-03-18 11:55:59 -0400461 v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
462 global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
Jason Monkfc934182013-07-22 13:20:39 -0400463 is_in_net_ex_template);
464
Jason Monk9fd69be2014-03-18 11:55:59 -0400465 v8_context_.Reset(
466 isolate_, v8::Context::New(isolate_, NULL, global_template));
Jason Monkfc934182013-07-22 13:20:39 -0400467
Jason Monk9fd69be2014-03-18 11:55:59 -0400468 v8::Local<v8::Context> context =
469 v8::Local<v8::Context>::New(isolate_, v8_context_);
470 v8::Context::Scope ctx(context);
Jason Monkfc934182013-07-22 13:20:39 -0400471
472 // Add the PAC utility functions to the environment.
473 // (This script should never fail, as it is a string literal!)
474 // Note that the two string literals are concatenated.
475 int rv = RunScript(
Jason Monk9fd69be2014-03-18 11:55:59 -0400476 ASCIILiteralToV8String(isolate_,
Jason Monkfc934182013-07-22 13:20:39 -0400477 PROXY_RESOLVER_SCRIPT
478 PROXY_RESOLVER_SCRIPT_EX),
479 kPacUtilityResourceName);
480 if (rv != OK) {
481 return rv;
482 }
483
484 // Add the user's PAC code to the environment.
Jason Monk9fd69be2014-03-18 11:55:59 -0400485 rv = RunScript(UTF16StringToV8String(isolate_, pac_script), kPacResourceName);
Jason Monkfc934182013-07-22 13:20:39 -0400486 if (rv != OK) {
487 return rv;
488 }
489
490 // At a minimum, the FindProxyForURL() function must be defined for this
491 // to be a legitimiate PAC script.
492 v8::Local<v8::Value> function;
493 if (!GetFindProxyForURL(&function))
494 return ERR_PAC_SCRIPT_FAILED;
495
496 return OK;
497 }
498
499 void PurgeMemory() {
Jason Monk9fd69be2014-03-18 11:55:59 -0400500 v8::Locker locked(isolate_);
501 v8::Isolate::Scope isolate_scope(isolate_);
Ben Murdoche80b5fa2014-07-31 16:47:35 +0100502 isolate_->LowMemoryNotification();
Jason Monkfc934182013-07-22 13:20:39 -0400503 }
504
505 private:
506 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
Jason Monk9fd69be2014-03-18 11:55:59 -0400507 v8::Local<v8::Context> context =
508 v8::Local<v8::Context>::New(isolate_, v8_context_);
509 *function = context->Global()->Get(
510 ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
Jason Monkfc934182013-07-22 13:20:39 -0400511 return (*function)->IsFunction();
512 }
513
514 // Handle an exception thrown by V8.
515 void HandleError(v8::Handle<v8::Message> message) {
516 if (message.IsEmpty())
517 return;
Jason Monkf5cf6e32013-07-23 17:26:23 -0400518 error_listener_->ErrorMessage(V8StringToUTF16(message->Get()));
Jason Monkfc934182013-07-22 13:20:39 -0400519 }
520
521 // Compiles and runs |script| in the current V8 context.
522 // Returns OK on success, otherwise an error code.
523 int RunScript(v8::Handle<v8::String> script, const char* script_name) {
524 v8::TryCatch try_catch;
525
526 // Compile the script.
527 v8::ScriptOrigin origin =
Jason Monk9fd69be2014-03-18 11:55:59 -0400528 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
Jason Monkfc934182013-07-22 13:20:39 -0400529 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
530
531 // Execute.
532 if (!code.IsEmpty())
533 code->Run();
534
535 // Check for errors.
536 if (try_catch.HasCaught()) {
537 HandleError(try_catch.Message());
538 return ERR_PAC_SCRIPT_FAILED;
539 }
540
541 return OK;
542 }
543
544 // V8 callback for when "alert()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400545 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400546 Context* context =
547 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
548
549 // Like firefox we assume "undefined" if no argument was specified, and
550 // disregard any arguments beyond the first.
Jason Monkf5cf6e32013-07-23 17:26:23 -0400551 android::String16 message;
Jason Monkfc934182013-07-22 13:20:39 -0400552 if (args.Length() == 0) {
553 std::string undef = "undefined";
Jason Monkf5cf6e32013-07-23 17:26:23 -0400554 android::String8 undef8(undef.c_str());
555 android::String16 wundef(undef8);
Jason Monkfc934182013-07-22 13:20:39 -0400556 message = wundef;
557 } else {
Jason Monk9fd69be2014-03-18 11:55:59 -0400558 if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
559 return; // toString() threw an exception.
Jason Monkfc934182013-07-22 13:20:39 -0400560 }
561
Jason Monkf5cf6e32013-07-23 17:26:23 -0400562 context->error_listener_->AlertMessage(message);
Jason Monk9fd69be2014-03-18 11:55:59 -0400563 return;
Jason Monkfc934182013-07-22 13:20:39 -0400564 }
565
566 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400567 static void MyIpAddressCallback(
568 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400569 Context* context =
570 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
571
572 std::string result;
573 bool success;
574
575 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400576 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400577
578 // We shouldn't be called with any arguments, but will not complain if
579 // we are.
580 success = context->js_bindings_->MyIpAddress(&result);
581 }
582
Jason Monk9fd69be2014-03-18 11:55:59 -0400583 if (!success) {
584 args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
585 } else {
586 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result));
587 }
Jason Monkfc934182013-07-22 13:20:39 -0400588 }
589
590 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400591 static void MyIpAddressExCallback(
592 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400593 Context* context =
594 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
595
596 std::string ip_address_list;
597 bool success;
598
599 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400600 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400601
602 // We shouldn't be called with any arguments, but will not complain if
603 // we are.
604 success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
605 }
606
607 if (!success)
608 ip_address_list = std::string();
Jason Monk9fd69be2014-03-18 11:55:59 -0400609 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400610 }
611
612 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400613 static void DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400614 Context* context =
615 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
616
617 // We need at least one string argument.
618 std::string hostname;
Jason Monk9fd69be2014-03-18 11:55:59 -0400619 if (!GetHostnameArgument(args, &hostname)) {
620 return;
621 }
Jason Monkfc934182013-07-22 13:20:39 -0400622
623 std::string ip_address;
624 bool success;
625
626 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400627 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400628 success = context->js_bindings_->DnsResolve(hostname, &ip_address);
629 }
630
Jason Monk9fd69be2014-03-18 11:55:59 -0400631 if (success) {
632 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address));
633 } else {
634 args.GetReturnValue().SetNull();
635 }
Jason Monkfc934182013-07-22 13:20:39 -0400636 }
637
638 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400639 static void DnsResolveExCallback(
640 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400641 Context* context =
642 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
643
644 // We need at least one string argument.
645 std::string hostname;
Jason Monk9fd69be2014-03-18 11:55:59 -0400646 if (!GetHostnameArgument(args, &hostname)) {
647 args.GetReturnValue().SetNull();
648 return;
649 }
Jason Monkfc934182013-07-22 13:20:39 -0400650
651 std::string ip_address_list;
652 bool success;
653
654 {
Jason Monk9fd69be2014-03-18 11:55:59 -0400655 v8::Unlocker unlocker(args.GetIsolate());
Jason Monkfc934182013-07-22 13:20:39 -0400656 success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
657 }
658
659 if (!success)
660 ip_address_list = std::string();
661
Jason Monk9fd69be2014-03-18 11:55:59 -0400662 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400663 }
664
665 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400666 static void SortIpAddressListCallback(
667 const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400668 // We need at least one string argument.
Jason Monk9fd69be2014-03-18 11:55:59 -0400669 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
670 args.GetReturnValue().SetNull();
671 return;
672 }
Jason Monkfc934182013-07-22 13:20:39 -0400673
674 std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
675 std::string sorted_ip_address_list;
676 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
Jason Monk9fd69be2014-03-18 11:55:59 -0400677 if (!success) {
678 args.GetReturnValue().Set(false);
679 return;
680 }
681 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
Jason Monkfc934182013-07-22 13:20:39 -0400682 }
683
684 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400685 static void IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Jason Monkfc934182013-07-22 13:20:39 -0400686 // We need at least 2 string arguments.
687 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
Jason Monk9fd69be2014-03-18 11:55:59 -0400688 args[1].IsEmpty() || !args[1]->IsString()) {
689 args.GetReturnValue().SetNull();
690 return;
691 }
Jason Monkfc934182013-07-22 13:20:39 -0400692
693 std::string ip_address = V8StringToUTF8(args[0]->ToString());
694 std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
Jason Monk9fd69be2014-03-18 11:55:59 -0400695 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
Jason Monkfc934182013-07-22 13:20:39 -0400696 }
697
698 ProxyResolverJSBindings* js_bindings_;
Jason Monkf5cf6e32013-07-23 17:26:23 -0400699 ProxyErrorListener* error_listener_;
Jason Monk9fd69be2014-03-18 11:55:59 -0400700 v8::Isolate* isolate_;
Jason Monkfc934182013-07-22 13:20:39 -0400701 v8::Persistent<v8::External> v8_this_;
702 v8::Persistent<v8::Context> v8_context_;
703};
704
705// ProxyResolverV8 ------------------------------------------------------------
706
707ProxyResolverV8::ProxyResolverV8(
Jason Monkf5cf6e32013-07-23 17:26:23 -0400708 ProxyResolverJSBindings* custom_js_bindings,
709 ProxyErrorListener* error_listener)
710 : context_(NULL), js_bindings_(custom_js_bindings),
711 error_listener_(error_listener) {
712
Jason Monkfc934182013-07-22 13:20:39 -0400713}
714
715ProxyResolverV8::~ProxyResolverV8() {
Jason Monkc06a8d62013-08-19 10:36:23 -0400716 if (context_ != NULL) {
717 delete context_;
718 context_ = NULL;
719 }
720 if (js_bindings_ != NULL) {
721 delete js_bindings_;
722 }
Jason Monkfc934182013-07-22 13:20:39 -0400723}
724
Jason Monkf5cf6e32013-07-23 17:26:23 -0400725int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
726 android::String16* results) {
Jason Monkfc934182013-07-22 13:20:39 -0400727 // If the V8 instance has not been initialized (either because
728 // SetPacScript() wasn't called yet, or because it failed.
729 if (context_ == NULL)
730 return ERR_FAILED;
731
732 // Otherwise call into V8.
733 int rv = context_->ResolveProxy(spec, host, results);
734
735 return rv;
736}
737
Jason Monkfc934182013-07-22 13:20:39 -0400738void ProxyResolverV8::PurgeMemory() {
739 context_->PurgeMemory();
740}
741
Jason Monk40adcb52013-08-23 17:19:31 -0400742int ProxyResolverV8::SetPacScript(const android::String16& script_data) {
Jason Monkfc934182013-07-22 13:20:39 -0400743 if (context_ != NULL) {
744 delete context_;
Jason Monkc06a8d62013-08-19 10:36:23 -0400745 context_ = NULL;
Jason Monkfc934182013-07-22 13:20:39 -0400746 }
Jason Monkf5cf6e32013-07-23 17:26:23 -0400747 if (script_data.size() == 0)
Jason Monkfc934182013-07-22 13:20:39 -0400748 return ERR_PAC_SCRIPT_FAILED;
749
750 // Try parsing the PAC script.
Jason Monk9fd69be2014-03-18 11:55:59 -0400751 context_ = new Context(js_bindings_, error_listener_, v8::Isolate::New());
Jason Monkfc934182013-07-22 13:20:39 -0400752 int rv;
753 if ((rv = context_->InitV8(script_data)) != OK) {
754 context_ = NULL;
755 }
756 if (rv != OK)
757 context_ = NULL;
758 return rv;
759}
760
761} // namespace net