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