• 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::Handle<v8::String> s)154 std::string V8StringToUTF8(v8::Handle<v8::String> s) {
155   std::string result;
156   s->WriteUtf8(WriteInto(&result, s->Length() + 1));
157   return result;
158 }
159 
160 // Converts a V8 String to a UTF16 string.
V8StringToUTF16(v8::Handle<v8::String> s)161 std::u16string V8StringToUTF16(v8::Handle<v8::String> s) {
162   int len = s->Length();
163   char16_t* buf = new char16_t[len + 1];
164   s->Write(reinterpret_cast<uint16_t*>(buf), 0, len);
165   std::u16string ret(buf, len);
166   delete[] buf;
167   return ret;
168 }
169 
170 // Converts an ASCII std::string to a V8 string.
ASCIIStringToV8String(v8::Isolate * isolate,const std::string & s)171 v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
172   return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
173 }
174 
UTF16StringToV8String(v8::Isolate * isolate,const std::u16string & s)175 v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const std::u16string& s) {
176   return v8::String::NewFromTwoByte(
177       isolate, reinterpret_cast<const uint16_t*>(s.data()),
178       v8::String::kNormalString, s.size());
179 }
180 
181 // Converts an ASCII string literal to a V8 string.
ASCIILiteralToV8String(v8::Isolate * isolate,const char * ascii)182 v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) {
183 //  DCHECK(IsStringASCII(ascii));
184   size_t length = strlen(ascii);
185   if (length <= kMaxStringBytesForCopy)
186     return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length);
187   return v8::String::NewExternal(isolate, new V8ExternalASCIILiteral(ascii, length));
188 }
189 
190 // Stringizes a V8 object by calling its toString() method. Returns true
191 // on success. This may fail if the toString() throws an exception.
V8ObjectToUTF8String(v8::Handle<v8::Value> object,std::string * utf8_result,v8::Isolate * isolate)192 bool V8ObjectToUTF8String(v8::Handle<v8::Value> object,
193                            std::string* utf8_result,
194                            v8::Isolate* isolate) {
195   if (object.IsEmpty())
196     return false;
197 
198   v8::HandleScope scope(isolate);
199   v8::Local<v8::String> str_object = object->ToString();
200   if (str_object.IsEmpty())
201     return false;
202   *utf8_result = V8StringToUTF8(str_object);
203   return true;
204 }
205 
206 // Extracts an hostname argument from |args|. On success returns true
207 // and fills |*hostname| with the result.
GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value> & args,std::string * hostname)208 bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args, std::string* hostname) {
209   // The first argument should be a string.
210   if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
211     return false;
212 
213   const std::u16string hostname_utf16 = V8StringToUTF16(args[0]->ToString());
214 
215   // If the hostname is already in ASCII, simply return it as is.
216   if (IsStringASCII(hostname_utf16)) {
217     *hostname = V8StringToUTF8(args[0]->ToString());
218     return true;
219   }
220   return false;
221 }
222 
223 // Wrapper for passing around IP address strings and IPAddressNumber objects.
224 struct IPAddress {
IPAddressnet::__anon0abc6af00111::IPAddress225   IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
226       : string_value(ip_string),
227         ip_address_number(ip_number) {
228   }
229 
230   // Used for sorting IP addresses in ascending order in SortIpAddressList().
231   // IP6 addresses are placed ahead of IPv4 addresses.
operator <net::__anon0abc6af00111::IPAddress232   bool operator<(const IPAddress& rhs) const {
233     const IPAddressNumber& ip1 = this->ip_address_number;
234     const IPAddressNumber& ip2 = rhs.ip_address_number;
235     if (ip1.size() != ip2.size())
236       return ip1.size() > ip2.size();  // IPv6 before IPv4.
237     return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0;  // Ascending order.
238   }
239 
240   std::string string_value;
241   IPAddressNumber ip_address_number;
242 };
243 
244 template<typename STR>
RemoveCharsT(const STR & input,const typename STR::value_type remove_chars[],STR * output)245 bool RemoveCharsT(const STR& input,
246                   const typename STR::value_type remove_chars[],
247                   STR* output) {
248   bool removed = false;
249   size_t found;
250 
251   *output = input;
252 
253   found = output->find_first_of(remove_chars);
254   while (found != STR::npos) {
255     removed = true;
256     output->replace(found, 1, STR());
257     found = output->find_first_of(remove_chars, found);
258   }
259 
260   return removed;
261 }
262 
RemoveChars(const std::string & input,const char remove_chars[],std::string * output)263 bool RemoveChars(const std::string& input,
264                  const char remove_chars[],
265                  std::string* output) {
266   return RemoveCharsT(input, remove_chars, output);
267 }
268 
269 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
270 // semi-colon delimited string containing IP addresses.
271 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
272 // IP addresses or an empty string if unable to sort the IP address list.
273 // Returns 'true' if the sorting was successful, and 'false' if the input was an
274 // empty string, a string of separators (";" in this case), or if any of the IP
275 // addresses in the input list failed to parse.
SortIpAddressList(const std::string & ip_address_list,std::string * sorted_ip_address_list)276 bool SortIpAddressList(const std::string& ip_address_list,
277                        std::string* sorted_ip_address_list) {
278   sorted_ip_address_list->clear();
279 
280   // Strip all whitespace (mimics IE behavior).
281   std::string cleaned_ip_address_list;
282   RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
283   if (cleaned_ip_address_list.empty())
284     return false;
285 
286   // Split-up IP addresses and store them in a vector.
287   std::vector<IPAddress> ip_vector;
288   IPAddressNumber ip_num;
289   char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
290   while (tok_list != NULL) {
291     if (!ParseIPLiteralToNumber(tok_list, &ip_num))
292       return false;
293     ip_vector.push_back(IPAddress(tok_list, ip_num));
294     tok_list = strtok(NULL, ";");
295   }
296 
297   if (ip_vector.empty())  // Can happen if we have something like
298     return false;         // sortIpAddressList(";") or sortIpAddressList("; ;")
299 
300   // Sort lists according to ascending numeric value.
301   if (ip_vector.size() > 1)
302     std::stable_sort(ip_vector.begin(), ip_vector.end());
303 
304   // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
305   // IPv4).
306   for (size_t i = 0; i < ip_vector.size(); ++i) {
307     if (i > 0)
308       *sorted_ip_address_list += ";";
309     *sorted_ip_address_list += ip_vector[i].string_value;
310   }
311   return true;
312 }
313 
314 
315 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
316 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
317 // slash-delimited IP prefix with the top 'n' bits specified in the bit
318 // field. This returns 'true' if the address is in the same subnet, and
319 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
320 // format, or if an address and prefix of different types are used (e.g. IPv6
321 // address and IPv4 prefix).
IsInNetEx(const std::string & ip_address,const std::string & ip_prefix)322 bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
323   IPAddressNumber address;
324   std::string cleaned_ip_address;
325   if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
326     return false;
327   if (!ParseIPLiteralToNumber(ip_address, &address))
328     return false;
329 
330   IPAddressNumber prefix;
331   size_t prefix_length_in_bits;
332   if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
333     return false;
334 
335   // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
336   if (address.size() != prefix.size())
337     return false;
338 
339   return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
340 }
341 
342 }  // namespace
343 
344 class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
345   public:
Allocate(size_t length)346    virtual void* Allocate(size_t length) {
347      void* data = AllocateUninitialized(length);
348      return data == NULL ? data : memset(data, 0, length);
349    }
AllocateUninitialized(size_t length)350    virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
Free(void * data,size_t)351    virtual void Free(void* data, size_t) { free(data); }
352 };
353 
354 
355 // ProxyResolverV8::Context ---------------------------------------------------
356 
357 class ProxyResolverV8::Context {
358  public:
Context(ProxyResolverJSBindings * js_bindings,ProxyErrorListener * error_listener,v8::Isolate * isolate)359   explicit Context(ProxyResolverJSBindings* js_bindings,
360           ProxyErrorListener* error_listener, v8::Isolate* isolate)
361       : js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) {
362   }
363 
~Context()364   ~Context() {
365     v8::Locker locked(isolate_);
366     v8::Isolate::Scope isolate_scope(isolate_);
367 
368     v8_this_.Reset();
369     v8_context_.Reset();
370   }
371 
ResolveProxy(const std::u16string url,const std::u16string host,std::u16string * results)372   int ResolveProxy(const std::u16string url, const std::u16string host,
373         std::u16string* results) {
374     v8::Locker locked(isolate_);
375     v8::Isolate::Scope isolate_scope(isolate_);
376     v8::HandleScope scope(isolate_);
377 
378     v8::Local<v8::Context> context =
379         v8::Local<v8::Context>::New(isolate_, v8_context_);
380     v8::Context::Scope function_scope(context);
381 
382     v8::Local<v8::Value> function;
383     if (!GetFindProxyForURL(&function)) {
384       error_listener_->ErrorMessage("FindProxyForURL() is undefined");
385       return ERR_PAC_SCRIPT_FAILED;
386     }
387 
388     v8::Handle<v8::Value> argv[] = {
389         UTF16StringToV8String(isolate_, url),
390         UTF16StringToV8String(isolate_, host) };
391 
392     v8::TryCatch try_catch(isolate_);
393     v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
394         context->Global(), 2, argv);
395 
396     if (try_catch.HasCaught()) {
397       error_listener_->ErrorMessage(V8StringToUTF8(try_catch.Message()->Get()));
398       return ERR_PAC_SCRIPT_FAILED;
399     }
400 
401     if (!ret->IsString()) {
402       error_listener_->ErrorMessage("FindProxyForURL() did not return a string.");
403       return ERR_PAC_SCRIPT_FAILED;
404     }
405 
406     *results = V8StringToUTF16(ret->ToString());
407 
408     if (!IsStringASCII(*results)) {
409       // TODO:         Rather than failing when a wide string is returned, we
410       //               could extend the parsing to handle IDNA hostnames by
411       //               converting them to ASCII punycode.
412       //               crbug.com/47234
413       error_listener_->ErrorMessage("FindProxyForURL() returned a non-ASCII string");
414       return ERR_PAC_SCRIPT_FAILED;
415     }
416 
417     return OK;
418   }
419 
InitV8(const std::u16string & pac_script)420   int InitV8(const std::u16string& pac_script) {
421     v8::Locker locked(isolate_);
422     v8::Isolate::Scope isolate_scope(isolate_);
423     v8::HandleScope scope(isolate_);
424 
425     v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
426     v8::Local<v8::External> v8_this =
427         v8::Local<v8::External>::New(isolate_, v8_this_);
428     v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate_);
429 
430     // Attach the javascript bindings.
431     v8::Local<v8::FunctionTemplate> alert_template =
432         v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
433     global_template->Set(ASCIILiteralToV8String(isolate_, "alert"), alert_template);
434 
435     v8::Local<v8::FunctionTemplate> my_ip_address_template =
436         v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
437     global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
438         my_ip_address_template);
439 
440     v8::Local<v8::FunctionTemplate> dns_resolve_template =
441         v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
442     global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
443         dns_resolve_template);
444 
445     // Microsoft's PAC extensions:
446 
447     v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
448         v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
449     global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
450                          dns_resolve_ex_template);
451 
452     v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
453         v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
454     global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
455                          my_ip_address_ex_template);
456 
457     v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
458         v8::FunctionTemplate::New(isolate_, &SortIpAddressListCallback, v8_this);
459     global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
460                          sort_ip_address_list_template);
461 
462     v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
463         v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
464     global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
465                          is_in_net_ex_template);
466 
467     v8_context_.Reset(
468         isolate_, v8::Context::New(isolate_, NULL, global_template));
469 
470     v8::Local<v8::Context> context =
471         v8::Local<v8::Context>::New(isolate_, v8_context_);
472     v8::Context::Scope ctx(context);
473 
474     // Add the PAC utility functions to the environment.
475     // (This script should never fail, as it is a string literal!)
476     // Note that the two string literals are concatenated.
477     int rv = RunScript(
478         ASCIILiteralToV8String(isolate_,
479             PROXY_RESOLVER_SCRIPT
480             PROXY_RESOLVER_SCRIPT_EX),
481         kPacUtilityResourceName);
482     if (rv != OK) {
483       return rv;
484     }
485 
486     // Add the user's PAC code to the environment.
487     rv = RunScript(UTF16StringToV8String(isolate_, pac_script), kPacResourceName);
488     if (rv != OK) {
489       return rv;
490     }
491 
492     // At a minimum, the FindProxyForURL() function must be defined for this
493     // to be a legitimiate PAC script.
494     v8::Local<v8::Value> function;
495     if (!GetFindProxyForURL(&function))
496       return ERR_PAC_SCRIPT_FAILED;
497 
498     return OK;
499   }
500 
PurgeMemory()501   void PurgeMemory() {
502     v8::Locker locked(isolate_);
503     v8::Isolate::Scope isolate_scope(isolate_);
504     isolate_->LowMemoryNotification();
505   }
506 
507  private:
GetFindProxyForURL(v8::Local<v8::Value> * function)508   bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
509     v8::Local<v8::Context> context =
510         v8::Local<v8::Context>::New(isolate_, v8_context_);
511     *function = context->Global()->Get(
512         ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
513     return (*function)->IsFunction();
514   }
515 
516   // Handle an exception thrown by V8.
HandleError(v8::Handle<v8::Message> message)517   void HandleError(v8::Handle<v8::Message> message) {
518     if (message.IsEmpty())
519       return;
520     error_listener_->ErrorMessage(V8StringToUTF8(message->Get()));
521   }
522 
523   // Compiles and runs |script| in the current V8 context.
524   // Returns OK on success, otherwise an error code.
RunScript(v8::Handle<v8::String> script,const char * script_name)525   int RunScript(v8::Handle<v8::String> script, const char* script_name) {
526     v8::Local<v8::Context> context =
527         v8::Local<v8::Context>::New(isolate_, v8_context_);
528     v8::TryCatch try_catch(isolate_);
529 
530     // Compile the script.
531     v8::ScriptOrigin origin =
532         v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
533     v8::MaybeLocal<v8::Script> code = v8::Script::Compile(context, script, &origin);
534 
535     // Execute.
536     if (!code.IsEmpty())
537       code.ToLocalChecked()->Run(context);
538 
539     // Check for errors.
540     if (try_catch.HasCaught()) {
541       HandleError(try_catch.Message());
542       return ERR_PAC_SCRIPT_FAILED;
543     }
544 
545     return OK;
546   }
547 
548   // V8 callback for when "alert()" is invoked by the PAC script.
AlertCallback(const v8::FunctionCallbackInfo<v8::Value> & args)549   static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
550     Context* context =
551         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
552 
553     // Like firefox we assume "undefined" if no argument was specified, and
554     // disregard any arguments beyond the first.
555     std::string message;
556     if (args.Length() == 0) {
557       message = "undefined";
558     } else {
559       if (!V8ObjectToUTF8String(args[0], &message, args.GetIsolate()))
560         return;  // toString() threw an exception.
561     }
562 
563     context->error_listener_->AlertMessage(message);
564     return;
565   }
566 
567   // V8 callback for when "myIpAddress()" is invoked by the PAC script.
MyIpAddressCallback(const v8::FunctionCallbackInfo<v8::Value> & args)568   static void MyIpAddressCallback(
569       const v8::FunctionCallbackInfo<v8::Value>& args) {
570     Context* context =
571         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
572 
573     std::string result;
574     bool success;
575 
576     {
577       v8::Unlocker unlocker(args.GetIsolate());
578 
579       // We shouldn't be called with any arguments, but will not complain if
580       // we are.
581       success = context->js_bindings_->MyIpAddress(&result);
582     }
583 
584     if (!success) {
585       args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
586     } else {
587       args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result));
588     }
589   }
590 
591   // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
MyIpAddressExCallback(const v8::FunctionCallbackInfo<v8::Value> & args)592   static void MyIpAddressExCallback(
593       const v8::FunctionCallbackInfo<v8::Value>& args) {
594     Context* context =
595         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
596 
597     std::string ip_address_list;
598     bool success;
599 
600     {
601       v8::Unlocker unlocker(args.GetIsolate());
602 
603       // We shouldn't be called with any arguments, but will not complain if
604       // we are.
605       success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
606     }
607 
608     if (!success)
609       ip_address_list = std::string();
610     args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
611   }
612 
613   // V8 callback for when "dnsResolve()" is invoked by the PAC script.
DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value> & args)614   static void DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
615     Context* context =
616         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
617 
618     // We need at least one string argument.
619     std::string hostname;
620     if (!GetHostnameArgument(args, &hostname)) {
621       return;
622     }
623 
624     std::string ip_address;
625     bool success;
626 
627     {
628       v8::Unlocker unlocker(args.GetIsolate());
629       success = context->js_bindings_->DnsResolve(hostname, &ip_address);
630     }
631 
632     if (success) {
633       args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address));
634     } else {
635       args.GetReturnValue().SetNull();
636     }
637   }
638 
639   // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
DnsResolveExCallback(const v8::FunctionCallbackInfo<v8::Value> & args)640   static void DnsResolveExCallback(
641       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       args.GetReturnValue().SetNull();
649       return;
650     }
651 
652     std::string ip_address_list;
653     bool success;
654 
655     {
656       v8::Unlocker unlocker(args.GetIsolate());
657       success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
658     }
659 
660     if (!success)
661       ip_address_list = std::string();
662 
663     args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
664   }
665 
666   // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
SortIpAddressListCallback(const v8::FunctionCallbackInfo<v8::Value> & args)667   static void SortIpAddressListCallback(
668       const v8::FunctionCallbackInfo<v8::Value>& args) {
669     // We need at least one string argument.
670     if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
671       args.GetReturnValue().SetNull();
672       return;
673     }
674 
675     std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
676     std::string sorted_ip_address_list;
677     bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
678     if (!success) {
679       args.GetReturnValue().Set(false);
680       return;
681     }
682     args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
683   }
684 
685   // V8 callback for when "isInNetEx()" is invoked by the PAC script.
IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value> & args)686   static void IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
687     // We need at least 2 string arguments.
688     if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
689         args[1].IsEmpty() || !args[1]->IsString()) {
690       args.GetReturnValue().SetNull();
691       return;
692     }
693 
694     std::string ip_address = V8StringToUTF8(args[0]->ToString());
695     std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
696     args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
697   }
698 
699   ProxyResolverJSBindings* js_bindings_;
700   ProxyErrorListener* error_listener_;
701   v8::Isolate* isolate_;
702   v8::Persistent<v8::External> v8_this_;
703   v8::Persistent<v8::Context> v8_context_;
704 };
705 
706 // ProxyResolverV8 ------------------------------------------------------------
707 
708 bool ProxyResolverV8::initialized_for_this_process_ = false;
709 
ProxyResolverV8(ProxyResolverJSBindings * custom_js_bindings)710 ProxyResolverV8::ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings)
711     : ProxyResolverV8(custom_js_bindings, defaultProxyErrorListener) {
712 }
713 
ProxyResolverV8(ProxyResolverJSBindings * custom_js_bindings,ProxyErrorListener * error_listener)714 ProxyResolverV8::ProxyResolverV8(
715     ProxyResolverJSBindings* custom_js_bindings,
716     ProxyErrorListener* error_listener)
717     : context_(NULL), js_bindings_(custom_js_bindings),
718       error_listener_(error_listener) {
719   if (!initialized_for_this_process_) {
720     v8::Platform* platform = v8::platform::CreateDefaultPlatform();
721     v8::V8::InitializePlatform(platform);
722     v8::V8::Initialize();
723     initialized_for_this_process_ = true;
724   }
725 }
726 
~ProxyResolverV8()727 ProxyResolverV8::~ProxyResolverV8() {
728   if (context_ != NULL) {
729     delete context_;
730     context_ = NULL;
731   }
732   if (js_bindings_ != NULL) {
733     delete js_bindings_;
734   }
735 }
736 
GetProxyForURL(const std::u16string & spec,const std::u16string & host,std::u16string * results)737 int ProxyResolverV8::GetProxyForURL(const std::u16string& spec, const std::u16string& host,
738                                     std::u16string* results) {
739   // If the V8 instance has not been initialized (either because
740   // SetPacScript() wasn't called yet, or because it failed.
741   if (context_ == NULL) {
742     error_listener_->ErrorMessage(std::string("Context is null."));
743     return ERR_FAILED;
744   }
745   // Otherwise call into V8.
746   int rv = context_->ResolveProxy(spec, host, results);
747 
748   return rv;
749 }
750 
PurgeMemory()751 void ProxyResolverV8::PurgeMemory() {
752   context_->PurgeMemory();
753 }
754 
SetPacScript(const std::u16string & script_data)755 int ProxyResolverV8::SetPacScript(const std::u16string& script_data) {
756   if (context_ != NULL) {
757     delete context_;
758     context_ = NULL;
759   }
760   if (script_data.length() == 0)
761     return ERR_PAC_SCRIPT_FAILED;
762 
763   // To help debugging v8 initialization issues, add "--log_all --print_all_exceptions"
764   // to the options
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