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