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