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