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