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