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