• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 "proxy_resolver_v8.h"
6 
7 #include <algorithm>
8 #include <cstdio>
9 #include <iostream>
10 #include <string>
11 #include <utils/String8.h>
12 #include <v8.h>
13 #include <libplatform/libplatform.h>
14 #include <vector>
15 
16 #include "net_util.h"
17 #include "proxy_resolver_script.h"
18 
19 // Notes on the javascript environment:
20 //
21 // For the majority of the PAC utility functions, we use the same code
22 // as Firefox. See the javascript library that proxy_resolver_scipt.h
23 // pulls in.
24 //
25 // In addition, we implement a subset of Microsoft's extensions to PAC.
26 // - myIpAddressEx()
27 // - dnsResolveEx()
28 // - isResolvableEx()
29 // - isInNetEx()
30 // - sortIpAddressList()
31 //
32 // It is worth noting that the original PAC specification does not describe
33 // the return values on failure. Consequently, there are compatibility
34 // differences between browsers on what to return on failure, which are
35 // illustrated below:
36 //
37 // --------------------+-------------+-------------------+--------------
38 //                     | Firefox3    | InternetExplorer8 |  --> Us <---
39 // --------------------+-------------+-------------------+--------------
40 // myIpAddress()       | "127.0.0.1" |  ???              |  "127.0.0.1"
41 // dnsResolve()        | null        |  false            |  null
42 // myIpAddressEx()     | N/A         |  ""               |  ""
43 // sortIpAddressList() | N/A         |  false            |  false
44 // dnsResolveEx()      | N/A         |  ""               |  ""
45 // isInNetEx()         | N/A         |  false            |  false
46 // --------------------+-------------+-------------------+--------------
47 //
48 // TODO: The cell above reading ??? means I didn't test it.
49 //
50 // Another difference is in how dnsResolve() and myIpAddress() are
51 // implemented -- whether they should restrict to IPv4 results, or
52 // include both IPv4 and IPv6. The following table illustrates the
53 // differences:
54 //
55 // --------------------+-------------+-------------------+--------------
56 //                     | Firefox3    | InternetExplorer8 |  --> Us <---
57 // --------------------+-------------+-------------------+--------------
58 // myIpAddress()       | IPv4/IPv6   |  IPv4             |  IPv4
59 // dnsResolve()        | IPv4/IPv6   |  IPv4             |  IPv4
60 // isResolvable()      | IPv4/IPv6   |  IPv4             |  IPv4
61 // myIpAddressEx()     | N/A         |  IPv4/IPv6        |  IPv4/IPv6
62 // dnsResolveEx()      | N/A         |  IPv4/IPv6        |  IPv4/IPv6
63 // sortIpAddressList() | N/A         |  IPv4/IPv6        |  IPv4/IPv6
64 // isResolvableEx()    | N/A         |  IPv4/IPv6        |  IPv4/IPv6
65 // isInNetEx()         | N/A         |  IPv4/IPv6        |  IPv4/IPv6
66 // -----------------+-------------+-------------------+--------------
67 
DoIsStringASCII(const android::String16 & str)68 static bool DoIsStringASCII(const android::String16& str) {
69   for (size_t i = 0; i < str.size(); i++) {
70     unsigned short c = str.string()[i];
71     if (c > 0x7F)
72       return false;
73   }
74   return true;
75 }
76 
IsStringASCII(const android::String16 & str)77 bool IsStringASCII(const android::String16& str) {
78   return DoIsStringASCII(str);
79 }
80 
81 namespace net {
82 
83 namespace {
84 
85 // Pseudo-name for the PAC script.
86 const char kPacResourceName[] = "proxy-pac-script.js";
87 // Pseudo-name for the PAC utility script.
88 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
89 
90 // External string wrapper so V8 can access the UTF16 string wrapped by
91 // ProxyResolverScriptData.
92 class V8ExternalStringFromScriptData
93     : public v8::String::ExternalStringResource {
94  public:
V8ExternalStringFromScriptData(const android::String16 & script_data)95   explicit V8ExternalStringFromScriptData(
96       const android::String16& script_data)
97       : script_data_(script_data) {}
98 
data() const99   virtual const uint16_t* data() const {
100     return reinterpret_cast<const uint16_t*>(script_data_.string());
101   }
102 
length() const103   virtual size_t length() const {
104     return script_data_.size();
105   }
106 
107  private:
108   const android::String16& script_data_;
109 //  DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
110 };
111 
112 // External string wrapper so V8 can access a string literal.
113 class V8ExternalASCIILiteral
114     : public v8::String::ExternalOneByteStringResource {
115  public:
116   // |ascii| must be a NULL-terminated C string, and must remain valid
117   // throughout this object's lifetime.
V8ExternalASCIILiteral(const char * ascii,size_t length)118   V8ExternalASCIILiteral(const char* ascii, size_t length)
119       : ascii_(ascii), length_(length) {
120   }
121 
data() const122   virtual const char* data() const {
123     return ascii_;
124   }
125 
length() const126   virtual size_t length() const {
127     return length_;
128   }
129 
130  private:
131   const char* ascii_;
132   size_t length_;
133 };
134 
135 // When creating a v8::String from a C++ string we have two choices: create
136 // a copy, or create a wrapper that shares the same underlying storage.
137 // For small strings it is better to just make a copy, whereas for large
138 // strings there are savings by sharing the storage. This number identifies
139 // the cutoff length for when to start wrapping rather than creating copies.
140 const size_t kMaxStringBytesForCopy = 256;
141 
142 template <class string_type>
WriteInto(string_type * str,size_t length_with_null)143 inline typename string_type::value_type* WriteInto(string_type* str,
144                                                    size_t length_with_null) {
145   str->reserve(length_with_null);
146   str->resize(length_with_null - 1);
147   return &((*str)[0]);
148 }
149 
150 // Converts a V8 String to a UTF8 std::string.
V8StringToUTF8(v8::Handle<v8::String> s)151 std::string V8StringToUTF8(v8::Handle<v8::String> s) {
152   std::string result;
153   s->WriteUtf8(WriteInto(&result, s->Length() + 1));
154   return result;
155 }
156 
157 // Converts a V8 String to a UTF16 string.
V8StringToUTF16(v8::Handle<v8::String> s)158 android::String16 V8StringToUTF16(v8::Handle<v8::String> s) {
159   int len = s->Length();
160   char16_t* buf = new char16_t[len + 1];
161   s->Write(reinterpret_cast<uint16_t*>(buf), 0, len);
162   android::String16 ret(buf, len);
163   delete[] buf;
164   return ret;
165 }
166 
UTF16ToASCII(const android::String16 & str)167 std::string UTF16ToASCII(const android::String16& str) {
168   android::String8 rstr(str);
169   return std::string(rstr.string());
170 }
171 
172 // Converts an ASCII std::string to a V8 string.
ASCIIStringToV8String(v8::Isolate * isolate,const std::string & s)173 v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
174   return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
175 }
176 
UTF16StringToV8String(v8::Isolate * isolate,const android::String16 & s)177 v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const android::String16& s) {
178   return v8::String::NewFromTwoByte(
179       isolate, reinterpret_cast<const uint16_t*>(s.string()),
180       v8::String::kNormalString, s.size());
181 }
182 
183 // Converts an ASCII string literal to a V8 string.
ASCIILiteralToV8String(v8::Isolate * isolate,const char * ascii)184 v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) {
185 //  DCHECK(IsStringASCII(ascii));
186   size_t length = strlen(ascii);
187   if (length <= kMaxStringBytesForCopy)
188     return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length);
189   return v8::String::NewExternal(isolate, new V8ExternalASCIILiteral(ascii, length));
190 }
191 
192 // Stringizes a V8 object by calling its toString() method. Returns true
193 // on success. This may fail if the toString() throws an exception.
V8ObjectToUTF16String(v8::Handle<v8::Value> object,android::String16 * utf16_result,v8::Isolate * isolate)194 bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
195                            android::String16* utf16_result,
196                            v8::Isolate* isolate) {
197   if (object.IsEmpty())
198     return false;
199 
200   v8::HandleScope scope(isolate);
201   v8::Local<v8::String> str_object = object->ToString();
202   if (str_object.IsEmpty())
203     return false;
204   *utf16_result = V8StringToUTF16(str_object);
205   return true;
206 }
207 
208 // Extracts an hostname argument from |args|. On success returns true
209 // and fills |*hostname| with the result.
GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value> & args,std::string * hostname)210 bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args, std::string* hostname) {
211   // The first argument should be a string.
212   if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
213     return false;
214 
215   const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
216 
217   // If the hostname is already in ASCII, simply return it as is.
218   if (IsStringASCII(hostname_utf16)) {
219     *hostname = UTF16ToASCII(hostname_utf16);
220     return true;
221   }
222   return false;
223 }
224 
225 // Wrapper for passing around IP address strings and IPAddressNumber objects.
226 struct IPAddress {
IPAddressnet::__anon1b0a9e660111::IPAddress227   IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
228       : string_value(ip_string),
229         ip_address_number(ip_number) {
230   }
231 
232   // Used for sorting IP addresses in ascending order in SortIpAddressList().
233   // IP6 addresses are placed ahead of IPv4 addresses.
operator <net::__anon1b0a9e660111::IPAddress234   bool operator<(const IPAddress& rhs) const {
235     const IPAddressNumber& ip1 = this->ip_address_number;
236     const IPAddressNumber& ip2 = rhs.ip_address_number;
237     if (ip1.size() != ip2.size())
238       return ip1.size() > ip2.size();  // IPv6 before IPv4.
239     return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0;  // Ascending order.
240   }
241 
242   std::string string_value;
243   IPAddressNumber ip_address_number;
244 };
245 
246 template<typename STR>
RemoveCharsT(const STR & input,const typename STR::value_type remove_chars[],STR * output)247 bool RemoveCharsT(const STR& input,
248                   const typename STR::value_type remove_chars[],
249                   STR* output) {
250   bool removed = false;
251   size_t found;
252 
253   *output = input;
254 
255   found = output->find_first_of(remove_chars);
256   while (found != STR::npos) {
257     removed = true;
258     output->replace(found, 1, STR());
259     found = output->find_first_of(remove_chars, found);
260   }
261 
262   return removed;
263 }
264 
RemoveChars(const std::string & input,const char remove_chars[],std::string * output)265 bool RemoveChars(const std::string& input,
266                  const char remove_chars[],
267                  std::string* output) {
268   return RemoveCharsT(input, remove_chars, output);
269 }
270 
271 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
272 // semi-colon delimited string containing IP addresses.
273 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
274 // IP addresses or an empty string if unable to sort the IP address list.
275 // Returns 'true' if the sorting was successful, and 'false' if the input was an
276 // empty string, a string of separators (";" in this case), or if any of the IP
277 // addresses in the input list failed to parse.
SortIpAddressList(const std::string & ip_address_list,std::string * sorted_ip_address_list)278 bool SortIpAddressList(const std::string& ip_address_list,
279                        std::string* sorted_ip_address_list) {
280   sorted_ip_address_list->clear();
281 
282   // Strip all whitespace (mimics IE behavior).
283   std::string cleaned_ip_address_list;
284   RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
285   if (cleaned_ip_address_list.empty())
286     return false;
287 
288   // Split-up IP addresses and store them in a vector.
289   std::vector<IPAddress> ip_vector;
290   IPAddressNumber ip_num;
291   char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
292   while (tok_list != NULL) {
293     if (!ParseIPLiteralToNumber(tok_list, &ip_num))
294       return false;
295     ip_vector.push_back(IPAddress(tok_list, ip_num));
296     tok_list = strtok(NULL, ";");
297   }
298 
299   if (ip_vector.empty())  // Can happen if we have something like
300     return false;         // sortIpAddressList(";") or sortIpAddressList("; ;")
301 
302   // Sort lists according to ascending numeric value.
303   if (ip_vector.size() > 1)
304     std::stable_sort(ip_vector.begin(), ip_vector.end());
305 
306   // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
307   // IPv4).
308   for (size_t i = 0; i < ip_vector.size(); ++i) {
309     if (i > 0)
310       *sorted_ip_address_list += ";";
311     *sorted_ip_address_list += ip_vector[i].string_value;
312   }
313   return true;
314 }
315 
316 
317 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
318 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
319 // slash-delimited IP prefix with the top 'n' bits specified in the bit
320 // field. This returns 'true' if the address is in the same subnet, and
321 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
322 // format, or if an address and prefix of different types are used (e.g. IPv6
323 // address and IPv4 prefix).
IsInNetEx(const std::string & ip_address,const std::string & ip_prefix)324 bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
325   IPAddressNumber address;
326   std::string cleaned_ip_address;
327   if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
328     return false;
329   if (!ParseIPLiteralToNumber(ip_address, &address))
330     return false;
331 
332   IPAddressNumber prefix;
333   size_t prefix_length_in_bits;
334   if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
335     return false;
336 
337   // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
338   if (address.size() != prefix.size())
339     return false;
340 
341   return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
342 }
343 
344 }  // namespace
345 
346 class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
347   public:
Allocate(size_t length)348    virtual void* Allocate(size_t length) {
349      void* data = AllocateUninitialized(length);
350      return data == NULL ? data : memset(data, 0, length);
351    }
AllocateUninitialized(size_t length)352    virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
Free(void * data,size_t)353    virtual void Free(void* data, size_t) { free(data); }
354 };
355 
356 
357 // ProxyResolverV8::Context ---------------------------------------------------
358 
359 class ProxyResolverV8::Context {
360  public:
Context(ProxyResolverJSBindings * js_bindings,ProxyErrorListener * error_listener,v8::Isolate * isolate)361   explicit Context(ProxyResolverJSBindings* js_bindings,
362           ProxyErrorListener* error_listener, v8::Isolate* isolate)
363       : js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) {
364   }
365 
~Context()366   ~Context() {
367     v8::Locker locked(isolate_);
368     v8::Isolate::Scope isolate_scope(isolate_);
369 
370     v8_this_.Reset();
371     v8_context_.Reset();
372   }
373 
ResolveProxy(const android::String16 url,const android::String16 host,android::String16 * results)374   int ResolveProxy(const android::String16 url, const android::String16 host,
375         android::String16* results) {
376     v8::Locker locked(isolate_);
377     v8::Isolate::Scope isolate_scope(isolate_);
378     v8::HandleScope scope(isolate_);
379 
380     v8::Local<v8::Context> context =
381         v8::Local<v8::Context>::New(isolate_, v8_context_);
382     v8::Context::Scope function_scope(context);
383 
384     v8::Local<v8::Value> function;
385     if (!GetFindProxyForURL(&function)) {
386       error_listener_->ErrorMessage(
387           android::String16("FindProxyForURL() is undefined"));
388       return ERR_PAC_SCRIPT_FAILED;
389     }
390 
391     v8::Handle<v8::Value> argv[] = {
392         UTF16StringToV8String(isolate_, url),
393         UTF16StringToV8String(isolate_, host) };
394 
395     v8::TryCatch try_catch;
396     v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
397         context->Global(), 2, argv);
398 
399     if (try_catch.HasCaught()) {
400       error_listener_->ErrorMessage(
401           V8StringToUTF16(try_catch.Message()->Get()));
402       return ERR_PAC_SCRIPT_FAILED;
403     }
404 
405     if (!ret->IsString()) {
406       error_listener_->ErrorMessage(
407           android::String16("FindProxyForURL() did not return a string."));
408       return ERR_PAC_SCRIPT_FAILED;
409     }
410 
411     *results = V8StringToUTF16(ret->ToString());
412 
413     if (!IsStringASCII(*results)) {
414       // TODO:         Rather than failing when a wide string is returned, we
415       //               could extend the parsing to handle IDNA hostnames by
416       //               converting them to ASCII punycode.
417       //               crbug.com/47234
418       error_listener_->ErrorMessage(
419           android::String16("FindProxyForURL() returned a non-ASCII string"));
420       return ERR_PAC_SCRIPT_FAILED;
421     }
422 
423     return OK;
424   }
425 
InitV8(const android::String16 & pac_script)426   int InitV8(const android::String16& pac_script) {
427     v8::Locker locked(isolate_);
428     v8::Isolate::Scope isolate_scope(isolate_);
429     v8::HandleScope scope(isolate_);
430 
431     v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
432     v8::Local<v8::External> v8_this =
433         v8::Local<v8::External>::New(isolate_, v8_this_);
434     v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
435 
436     // Attach the javascript bindings.
437     v8::Local<v8::FunctionTemplate> alert_template =
438         v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
439     global_template->Set(ASCIILiteralToV8String(isolate_, "alert"), alert_template);
440 
441     v8::Local<v8::FunctionTemplate> my_ip_address_template =
442         v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
443     global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
444         my_ip_address_template);
445 
446     v8::Local<v8::FunctionTemplate> dns_resolve_template =
447         v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
448     global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
449         dns_resolve_template);
450 
451     // Microsoft's PAC extensions:
452 
453     v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
454         v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
455     global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
456                          dns_resolve_ex_template);
457 
458     v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
459         v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
460     global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
461                          my_ip_address_ex_template);
462 
463     v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
464         v8::FunctionTemplate::New(isolate_, &SortIpAddressListCallback, v8_this);
465     global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
466                          sort_ip_address_list_template);
467 
468     v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
469         v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
470     global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
471                          is_in_net_ex_template);
472 
473     v8_context_.Reset(
474         isolate_, v8::Context::New(isolate_, NULL, global_template));
475 
476     v8::Local<v8::Context> context =
477         v8::Local<v8::Context>::New(isolate_, v8_context_);
478     v8::Context::Scope ctx(context);
479 
480     // Add the PAC utility functions to the environment.
481     // (This script should never fail, as it is a string literal!)
482     // Note that the two string literals are concatenated.
483     int rv = RunScript(
484         ASCIILiteralToV8String(isolate_,
485             PROXY_RESOLVER_SCRIPT
486             PROXY_RESOLVER_SCRIPT_EX),
487         kPacUtilityResourceName);
488     if (rv != OK) {
489       return rv;
490     }
491 
492     // Add the user's PAC code to the environment.
493     rv = RunScript(UTF16StringToV8String(isolate_, pac_script), kPacResourceName);
494     if (rv != OK) {
495       return rv;
496     }
497 
498     // At a minimum, the FindProxyForURL() function must be defined for this
499     // to be a legitimiate PAC script.
500     v8::Local<v8::Value> function;
501     if (!GetFindProxyForURL(&function))
502       return ERR_PAC_SCRIPT_FAILED;
503 
504     return OK;
505   }
506 
PurgeMemory()507   void PurgeMemory() {
508     v8::Locker locked(isolate_);
509     v8::Isolate::Scope isolate_scope(isolate_);
510     isolate_->LowMemoryNotification();
511   }
512 
513  private:
GetFindProxyForURL(v8::Local<v8::Value> * function)514   bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
515     v8::Local<v8::Context> context =
516         v8::Local<v8::Context>::New(isolate_, v8_context_);
517     *function = context->Global()->Get(
518         ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
519     return (*function)->IsFunction();
520   }
521 
522   // Handle an exception thrown by V8.
HandleError(v8::Handle<v8::Message> message)523   void HandleError(v8::Handle<v8::Message> message) {
524     if (message.IsEmpty())
525       return;
526     error_listener_->ErrorMessage(V8StringToUTF16(message->Get()));
527   }
528 
529   // Compiles and runs |script| in the current V8 context.
530   // Returns OK on success, otherwise an error code.
RunScript(v8::Handle<v8::String> script,const char * script_name)531   int RunScript(v8::Handle<v8::String> script, const char* script_name) {
532     v8::TryCatch try_catch;
533 
534     // Compile the script.
535     v8::ScriptOrigin origin =
536         v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
537     v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
538 
539     // Execute.
540     if (!code.IsEmpty())
541       code->Run();
542 
543     // Check for errors.
544     if (try_catch.HasCaught()) {
545       HandleError(try_catch.Message());
546       return ERR_PAC_SCRIPT_FAILED;
547     }
548 
549     return OK;
550   }
551 
552   // V8 callback for when "alert()" is invoked by the PAC script.
AlertCallback(const v8::FunctionCallbackInfo<v8::Value> & args)553   static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
554     Context* context =
555         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
556 
557     // Like firefox we assume "undefined" if no argument was specified, and
558     // disregard any arguments beyond the first.
559     android::String16 message;
560     if (args.Length() == 0) {
561       std::string undef = "undefined";
562       android::String8 undef8(undef.c_str());
563       android::String16 wundef(undef8);
564       message = wundef;
565     } else {
566       if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
567         return;  // toString() threw an exception.
568     }
569 
570     context->error_listener_->AlertMessage(message);
571     return;
572   }
573 
574   // V8 callback for when "myIpAddress()" is invoked by the PAC script.
MyIpAddressCallback(const v8::FunctionCallbackInfo<v8::Value> & args)575   static void MyIpAddressCallback(
576       const v8::FunctionCallbackInfo<v8::Value>& args) {
577     Context* context =
578         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
579 
580     std::string result;
581     bool success;
582 
583     {
584       v8::Unlocker unlocker(args.GetIsolate());
585 
586       // We shouldn't be called with any arguments, but will not complain if
587       // we are.
588       success = context->js_bindings_->MyIpAddress(&result);
589     }
590 
591     if (!success) {
592       args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
593     } else {
594       args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result));
595     }
596   }
597 
598   // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
MyIpAddressExCallback(const v8::FunctionCallbackInfo<v8::Value> & args)599   static void MyIpAddressExCallback(
600       const v8::FunctionCallbackInfo<v8::Value>& args) {
601     Context* context =
602         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
603 
604     std::string ip_address_list;
605     bool success;
606 
607     {
608       v8::Unlocker unlocker(args.GetIsolate());
609 
610       // We shouldn't be called with any arguments, but will not complain if
611       // we are.
612       success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
613     }
614 
615     if (!success)
616       ip_address_list = std::string();
617     args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
618   }
619 
620   // V8 callback for when "dnsResolve()" is invoked by the PAC script.
DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value> & args)621   static void DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
622     Context* context =
623         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
624 
625     // We need at least one string argument.
626     std::string hostname;
627     if (!GetHostnameArgument(args, &hostname)) {
628       return;
629     }
630 
631     std::string ip_address;
632     bool success;
633 
634     {
635       v8::Unlocker unlocker(args.GetIsolate());
636       success = context->js_bindings_->DnsResolve(hostname, &ip_address);
637     }
638 
639     if (success) {
640       args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address));
641     } else {
642       args.GetReturnValue().SetNull();
643     }
644   }
645 
646   // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
DnsResolveExCallback(const v8::FunctionCallbackInfo<v8::Value> & args)647   static void DnsResolveExCallback(
648       const v8::FunctionCallbackInfo<v8::Value>& args) {
649     Context* context =
650         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
651 
652     // We need at least one string argument.
653     std::string hostname;
654     if (!GetHostnameArgument(args, &hostname)) {
655       args.GetReturnValue().SetNull();
656       return;
657     }
658 
659     std::string ip_address_list;
660     bool success;
661 
662     {
663       v8::Unlocker unlocker(args.GetIsolate());
664       success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
665     }
666 
667     if (!success)
668       ip_address_list = std::string();
669 
670     args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
671   }
672 
673   // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
SortIpAddressListCallback(const v8::FunctionCallbackInfo<v8::Value> & args)674   static void SortIpAddressListCallback(
675       const v8::FunctionCallbackInfo<v8::Value>& args) {
676     // We need at least one string argument.
677     if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
678       args.GetReturnValue().SetNull();
679       return;
680     }
681 
682     std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
683     std::string sorted_ip_address_list;
684     bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
685     if (!success) {
686       args.GetReturnValue().Set(false);
687       return;
688     }
689     args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
690   }
691 
692   // V8 callback for when "isInNetEx()" is invoked by the PAC script.
IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value> & args)693   static void IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
694     // We need at least 2 string arguments.
695     if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
696         args[1].IsEmpty() || !args[1]->IsString()) {
697       args.GetReturnValue().SetNull();
698       return;
699     }
700 
701     std::string ip_address = V8StringToUTF8(args[0]->ToString());
702     std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
703     args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
704   }
705 
706   ProxyResolverJSBindings* js_bindings_;
707   ProxyErrorListener* error_listener_;
708   v8::Isolate* isolate_;
709   v8::Persistent<v8::External> v8_this_;
710   v8::Persistent<v8::Context> v8_context_;
711 };
712 
713 // ProxyResolverV8 ------------------------------------------------------------
714 
715 bool ProxyResolverV8::initialized_for_this_process_ = false;
716 
ProxyResolverV8(ProxyResolverJSBindings * custom_js_bindings,ProxyErrorListener * error_listener)717 ProxyResolverV8::ProxyResolverV8(
718     ProxyResolverJSBindings* custom_js_bindings,
719     ProxyErrorListener* error_listener)
720     : context_(NULL), js_bindings_(custom_js_bindings),
721       error_listener_(error_listener) {
722   if (!initialized_for_this_process_) {
723     v8::Platform* platform = v8::platform::CreateDefaultPlatform();
724     v8::V8::InitializePlatform(platform);
725     v8::V8::Initialize();
726     initialized_for_this_process_ = true;
727   }
728 }
729 
~ProxyResolverV8()730 ProxyResolverV8::~ProxyResolverV8() {
731   if (context_ != NULL) {
732     delete context_;
733     context_ = NULL;
734   }
735   if (js_bindings_ != NULL) {
736     delete js_bindings_;
737   }
738 }
739 
GetProxyForURL(const android::String16 spec,const android::String16 host,android::String16 * results)740 int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
741                                     android::String16* results) {
742   // If the V8 instance has not been initialized (either because
743   // SetPacScript() wasn't called yet, or because it failed.
744   if (context_ == NULL)
745     return ERR_FAILED;
746 
747   // Otherwise call into V8.
748   int rv = context_->ResolveProxy(spec, host, results);
749 
750   return rv;
751 }
752 
PurgeMemory()753 void ProxyResolverV8::PurgeMemory() {
754   context_->PurgeMemory();
755 }
756 
SetPacScript(const android::String16 & script_data)757 int ProxyResolverV8::SetPacScript(const android::String16& script_data) {
758   if (context_ != NULL) {
759     delete context_;
760     context_ = NULL;
761   }
762   if (script_data.size() == 0)
763     return ERR_PAC_SCRIPT_FAILED;
764 
765   // Disable JIT
766   static const char kNoOpt[] = "--no-opt";
767   v8::V8::SetFlagsFromString(kNoOpt, strlen(kNoOpt));
768 
769   // Try parsing the PAC script.
770   v8::Isolate::CreateParams create_params;
771   create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
772 
773   context_ = new Context(js_bindings_, error_listener_, v8::Isolate::New(create_params));
774   int rv;
775   if ((rv = context_->InitV8(script_data)) != OK) {
776     context_ = NULL;
777   }
778   if (rv != OK)
779     context_ = NULL;
780   return rv;
781 }
782 
783 }  // namespace net
784