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