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::Handle<v8::String> s)154 std::string V8StringToUTF8(v8::Handle<v8::String> s) {
155 std::string result;
156 s->WriteUtf8(WriteInto(&result, s->Length() + 1));
157 return result;
158 }
159
160 // Converts a V8 String to a UTF16 string.
V8StringToUTF16(v8::Handle<v8::String> s)161 std::u16string V8StringToUTF16(v8::Handle<v8::String> s) {
162 int len = s->Length();
163 char16_t* buf = new char16_t[len + 1];
164 s->Write(reinterpret_cast<uint16_t*>(buf), 0, len);
165 std::u16string ret(buf, len);
166 delete[] buf;
167 return ret;
168 }
169
170 // Converts an ASCII std::string to a V8 string.
ASCIIStringToV8String(v8::Isolate * isolate,const std::string & s)171 v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
172 return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
173 }
174
UTF16StringToV8String(v8::Isolate * isolate,const std::u16string & s)175 v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const std::u16string& s) {
176 return v8::String::NewFromTwoByte(
177 isolate, reinterpret_cast<const uint16_t*>(s.data()),
178 v8::String::kNormalString, s.size());
179 }
180
181 // Converts an ASCII string literal to a V8 string.
ASCIILiteralToV8String(v8::Isolate * isolate,const char * ascii)182 v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) {
183 // DCHECK(IsStringASCII(ascii));
184 size_t length = strlen(ascii);
185 if (length <= kMaxStringBytesForCopy)
186 return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length);
187 return v8::String::NewExternal(isolate, new V8ExternalASCIILiteral(ascii, length));
188 }
189
190 // Stringizes a V8 object by calling its toString() method. Returns true
191 // on success. This may fail if the toString() throws an exception.
V8ObjectToUTF8String(v8::Handle<v8::Value> object,std::string * utf8_result,v8::Isolate * isolate)192 bool V8ObjectToUTF8String(v8::Handle<v8::Value> object,
193 std::string* utf8_result,
194 v8::Isolate* isolate) {
195 if (object.IsEmpty())
196 return false;
197
198 v8::HandleScope scope(isolate);
199 v8::Local<v8::String> str_object = object->ToString();
200 if (str_object.IsEmpty())
201 return false;
202 *utf8_result = V8StringToUTF8(str_object);
203 return true;
204 }
205
206 // Extracts an hostname argument from |args|. On success returns true
207 // and fills |*hostname| with the result.
GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value> & args,std::string * hostname)208 bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args, std::string* hostname) {
209 // The first argument should be a string.
210 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
211 return false;
212
213 const std::u16string hostname_utf16 = V8StringToUTF16(args[0]->ToString());
214
215 // If the hostname is already in ASCII, simply return it as is.
216 if (IsStringASCII(hostname_utf16)) {
217 *hostname = V8StringToUTF8(args[0]->ToString());
218 return true;
219 }
220 return false;
221 }
222
223 // Wrapper for passing around IP address strings and IPAddressNumber objects.
224 struct IPAddress {
IPAddressnet::__anon0abc6af00111::IPAddress225 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
226 : string_value(ip_string),
227 ip_address_number(ip_number) {
228 }
229
230 // Used for sorting IP addresses in ascending order in SortIpAddressList().
231 // IP6 addresses are placed ahead of IPv4 addresses.
operator <net::__anon0abc6af00111::IPAddress232 bool operator<(const IPAddress& rhs) const {
233 const IPAddressNumber& ip1 = this->ip_address_number;
234 const IPAddressNumber& ip2 = rhs.ip_address_number;
235 if (ip1.size() != ip2.size())
236 return ip1.size() > ip2.size(); // IPv6 before IPv4.
237 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
238 }
239
240 std::string string_value;
241 IPAddressNumber ip_address_number;
242 };
243
244 template<typename STR>
RemoveCharsT(const STR & input,const typename STR::value_type remove_chars[],STR * output)245 bool RemoveCharsT(const STR& input,
246 const typename STR::value_type remove_chars[],
247 STR* output) {
248 bool removed = false;
249 size_t found;
250
251 *output = input;
252
253 found = output->find_first_of(remove_chars);
254 while (found != STR::npos) {
255 removed = true;
256 output->replace(found, 1, STR());
257 found = output->find_first_of(remove_chars, found);
258 }
259
260 return removed;
261 }
262
RemoveChars(const std::string & input,const char remove_chars[],std::string * output)263 bool RemoveChars(const std::string& input,
264 const char remove_chars[],
265 std::string* output) {
266 return RemoveCharsT(input, remove_chars, output);
267 }
268
269 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
270 // semi-colon delimited string containing IP addresses.
271 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
272 // IP addresses or an empty string if unable to sort the IP address list.
273 // Returns 'true' if the sorting was successful, and 'false' if the input was an
274 // empty string, a string of separators (";" in this case), or if any of the IP
275 // addresses in the input list failed to parse.
SortIpAddressList(const std::string & ip_address_list,std::string * sorted_ip_address_list)276 bool SortIpAddressList(const std::string& ip_address_list,
277 std::string* sorted_ip_address_list) {
278 sorted_ip_address_list->clear();
279
280 // Strip all whitespace (mimics IE behavior).
281 std::string cleaned_ip_address_list;
282 RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
283 if (cleaned_ip_address_list.empty())
284 return false;
285
286 // Split-up IP addresses and store them in a vector.
287 std::vector<IPAddress> ip_vector;
288 IPAddressNumber ip_num;
289 char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
290 while (tok_list != NULL) {
291 if (!ParseIPLiteralToNumber(tok_list, &ip_num))
292 return false;
293 ip_vector.push_back(IPAddress(tok_list, ip_num));
294 tok_list = strtok(NULL, ";");
295 }
296
297 if (ip_vector.empty()) // Can happen if we have something like
298 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
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
315 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
316 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
317 // slash-delimited IP prefix with the top 'n' bits specified in the bit
318 // field. This returns 'true' if the address is in the same subnet, and
319 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
320 // format, or if an address and prefix of different types are used (e.g. IPv6
321 // address and IPv4 prefix).
IsInNetEx(const std::string & ip_address,const std::string & ip_prefix)322 bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
323 IPAddressNumber address;
324 std::string cleaned_ip_address;
325 if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
326 return false;
327 if (!ParseIPLiteralToNumber(ip_address, &address))
328 return false;
329
330 IPAddressNumber prefix;
331 size_t prefix_length_in_bits;
332 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
333 return false;
334
335 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
336 if (address.size() != prefix.size())
337 return false;
338
339 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
340 }
341
342 } // namespace
343
344 class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
345 public:
Allocate(size_t length)346 virtual void* Allocate(size_t length) {
347 void* data = AllocateUninitialized(length);
348 return data == NULL ? data : memset(data, 0, length);
349 }
AllocateUninitialized(size_t length)350 virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
Free(void * data,size_t)351 virtual void Free(void* data, size_t) { free(data); }
352 };
353
354
355 // ProxyResolverV8::Context ---------------------------------------------------
356
357 class ProxyResolverV8::Context {
358 public:
Context(ProxyResolverJSBindings * js_bindings,ProxyErrorListener * error_listener,v8::Isolate * isolate)359 explicit Context(ProxyResolverJSBindings* js_bindings,
360 ProxyErrorListener* error_listener, v8::Isolate* isolate)
361 : js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) {
362 }
363
~Context()364 ~Context() {
365 v8::Locker locked(isolate_);
366 v8::Isolate::Scope isolate_scope(isolate_);
367
368 v8_this_.Reset();
369 v8_context_.Reset();
370 }
371
ResolveProxy(const std::u16string url,const std::u16string host,std::u16string * results)372 int ResolveProxy(const std::u16string url, const std::u16string host,
373 std::u16string* results) {
374 v8::Locker locked(isolate_);
375 v8::Isolate::Scope isolate_scope(isolate_);
376 v8::HandleScope scope(isolate_);
377
378 v8::Local<v8::Context> context =
379 v8::Local<v8::Context>::New(isolate_, v8_context_);
380 v8::Context::Scope function_scope(context);
381
382 v8::Local<v8::Value> function;
383 if (!GetFindProxyForURL(&function)) {
384 error_listener_->ErrorMessage("FindProxyForURL() is undefined");
385 return ERR_PAC_SCRIPT_FAILED;
386 }
387
388 v8::Handle<v8::Value> argv[] = {
389 UTF16StringToV8String(isolate_, url),
390 UTF16StringToV8String(isolate_, host) };
391
392 v8::TryCatch try_catch(isolate_);
393 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
394 context->Global(), 2, argv);
395
396 if (try_catch.HasCaught()) {
397 error_listener_->ErrorMessage(V8StringToUTF8(try_catch.Message()->Get()));
398 return ERR_PAC_SCRIPT_FAILED;
399 }
400
401 if (!ret->IsString()) {
402 error_listener_->ErrorMessage("FindProxyForURL() did not return a string.");
403 return ERR_PAC_SCRIPT_FAILED;
404 }
405
406 *results = V8StringToUTF16(ret->ToString());
407
408 if (!IsStringASCII(*results)) {
409 // TODO: Rather than failing when a wide string is returned, we
410 // could extend the parsing to handle IDNA hostnames by
411 // converting them to ASCII punycode.
412 // crbug.com/47234
413 error_listener_->ErrorMessage("FindProxyForURL() returned a non-ASCII string");
414 return ERR_PAC_SCRIPT_FAILED;
415 }
416
417 return OK;
418 }
419
InitV8(const std::u16string & pac_script)420 int InitV8(const std::u16string& pac_script) {
421 v8::Locker locked(isolate_);
422 v8::Isolate::Scope isolate_scope(isolate_);
423 v8::HandleScope scope(isolate_);
424
425 v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
426 v8::Local<v8::External> v8_this =
427 v8::Local<v8::External>::New(isolate_, v8_this_);
428 v8::Local<v8::ObjectTemplate> global_template = 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"), 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_, &SortIpAddressListCallback, v8_this);
459 global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
460 sort_ip_address_list_template);
461
462 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
463 v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
464 global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
465 is_in_net_ex_template);
466
467 v8_context_.Reset(
468 isolate_, v8::Context::New(isolate_, NULL, global_template));
469
470 v8::Local<v8::Context> context =
471 v8::Local<v8::Context>::New(isolate_, v8_context_);
472 v8::Context::Scope ctx(context);
473
474 // Add the PAC utility functions to the environment.
475 // (This script should never fail, as it is a string literal!)
476 // Note that the two string literals are concatenated.
477 int rv = RunScript(
478 ASCIILiteralToV8String(isolate_,
479 PROXY_RESOLVER_SCRIPT
480 PROXY_RESOLVER_SCRIPT_EX),
481 kPacUtilityResourceName);
482 if (rv != OK) {
483 return rv;
484 }
485
486 // Add the user's PAC code to the environment.
487 rv = RunScript(UTF16StringToV8String(isolate_, pac_script), kPacResourceName);
488 if (rv != OK) {
489 return rv;
490 }
491
492 // At a minimum, the FindProxyForURL() function must be defined for this
493 // to be a legitimiate PAC script.
494 v8::Local<v8::Value> function;
495 if (!GetFindProxyForURL(&function))
496 return ERR_PAC_SCRIPT_FAILED;
497
498 return OK;
499 }
500
PurgeMemory()501 void PurgeMemory() {
502 v8::Locker locked(isolate_);
503 v8::Isolate::Scope isolate_scope(isolate_);
504 isolate_->LowMemoryNotification();
505 }
506
507 private:
GetFindProxyForURL(v8::Local<v8::Value> * function)508 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
509 v8::Local<v8::Context> context =
510 v8::Local<v8::Context>::New(isolate_, v8_context_);
511 *function = context->Global()->Get(
512 ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
513 return (*function)->IsFunction();
514 }
515
516 // Handle an exception thrown by V8.
HandleError(v8::Handle<v8::Message> message)517 void HandleError(v8::Handle<v8::Message> message) {
518 if (message.IsEmpty())
519 return;
520 error_listener_->ErrorMessage(V8StringToUTF8(message->Get()));
521 }
522
523 // Compiles and runs |script| in the current V8 context.
524 // Returns OK on success, otherwise an error code.
RunScript(v8::Handle<v8::String> script,const char * script_name)525 int RunScript(v8::Handle<v8::String> script, const char* script_name) {
526 v8::Local<v8::Context> context =
527 v8::Local<v8::Context>::New(isolate_, v8_context_);
528 v8::TryCatch try_catch(isolate_);
529
530 // Compile the script.
531 v8::ScriptOrigin origin =
532 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
533 v8::MaybeLocal<v8::Script> code = v8::Script::Compile(context, script, &origin);
534
535 // Execute.
536 if (!code.IsEmpty())
537 code.ToLocalChecked()->Run(context);
538
539 // Check for errors.
540 if (try_catch.HasCaught()) {
541 HandleError(try_catch.Message());
542 return ERR_PAC_SCRIPT_FAILED;
543 }
544
545 return OK;
546 }
547
548 // V8 callback for when "alert()" is invoked by the PAC script.
AlertCallback(const v8::FunctionCallbackInfo<v8::Value> & args)549 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
550 Context* context =
551 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
552
553 // Like firefox we assume "undefined" if no argument was specified, and
554 // disregard any arguments beyond the first.
555 std::string message;
556 if (args.Length() == 0) {
557 message = "undefined";
558 } else {
559 if (!V8ObjectToUTF8String(args[0], &message, args.GetIsolate()))
560 return; // toString() threw an exception.
561 }
562
563 context->error_listener_->AlertMessage(message);
564 return;
565 }
566
567 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
MyIpAddressCallback(const v8::FunctionCallbackInfo<v8::Value> & args)568 static void MyIpAddressCallback(
569 const v8::FunctionCallbackInfo<v8::Value>& args) {
570 Context* context =
571 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
572
573 std::string result;
574 bool success;
575
576 {
577 v8::Unlocker unlocker(args.GetIsolate());
578
579 // We shouldn't be called with any arguments, but will not complain if
580 // we are.
581 success = context->js_bindings_->MyIpAddress(&result);
582 }
583
584 if (!success) {
585 args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
586 } else {
587 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result));
588 }
589 }
590
591 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
MyIpAddressExCallback(const v8::FunctionCallbackInfo<v8::Value> & args)592 static void MyIpAddressExCallback(
593 const v8::FunctionCallbackInfo<v8::Value>& args) {
594 Context* context =
595 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
596
597 std::string ip_address_list;
598 bool success;
599
600 {
601 v8::Unlocker unlocker(args.GetIsolate());
602
603 // We shouldn't be called with any arguments, but will not complain if
604 // we are.
605 success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
606 }
607
608 if (!success)
609 ip_address_list = std::string();
610 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
611 }
612
613 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value> & args)614 static void DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
615 Context* context =
616 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
617
618 // We need at least one string argument.
619 std::string hostname;
620 if (!GetHostnameArgument(args, &hostname)) {
621 return;
622 }
623
624 std::string ip_address;
625 bool success;
626
627 {
628 v8::Unlocker unlocker(args.GetIsolate());
629 success = context->js_bindings_->DnsResolve(hostname, &ip_address);
630 }
631
632 if (success) {
633 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address));
634 } else {
635 args.GetReturnValue().SetNull();
636 }
637 }
638
639 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
DnsResolveExCallback(const v8::FunctionCallbackInfo<v8::Value> & args)640 static void DnsResolveExCallback(
641 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 args.GetReturnValue().SetNull();
649 return;
650 }
651
652 std::string ip_address_list;
653 bool success;
654
655 {
656 v8::Unlocker unlocker(args.GetIsolate());
657 success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
658 }
659
660 if (!success)
661 ip_address_list = std::string();
662
663 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
664 }
665
666 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
SortIpAddressListCallback(const v8::FunctionCallbackInfo<v8::Value> & args)667 static void SortIpAddressListCallback(
668 const v8::FunctionCallbackInfo<v8::Value>& args) {
669 // We need at least one string argument.
670 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
671 args.GetReturnValue().SetNull();
672 return;
673 }
674
675 std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
676 std::string sorted_ip_address_list;
677 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
678 if (!success) {
679 args.GetReturnValue().Set(false);
680 return;
681 }
682 args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
683 }
684
685 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value> & args)686 static void IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
687 // We need at least 2 string arguments.
688 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
689 args[1].IsEmpty() || !args[1]->IsString()) {
690 args.GetReturnValue().SetNull();
691 return;
692 }
693
694 std::string ip_address = V8StringToUTF8(args[0]->ToString());
695 std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
696 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
697 }
698
699 ProxyResolverJSBindings* js_bindings_;
700 ProxyErrorListener* error_listener_;
701 v8::Isolate* isolate_;
702 v8::Persistent<v8::External> v8_this_;
703 v8::Persistent<v8::Context> v8_context_;
704 };
705
706 // ProxyResolverV8 ------------------------------------------------------------
707
708 bool ProxyResolverV8::initialized_for_this_process_ = false;
709
ProxyResolverV8(ProxyResolverJSBindings * custom_js_bindings)710 ProxyResolverV8::ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings)
711 : ProxyResolverV8(custom_js_bindings, defaultProxyErrorListener) {
712 }
713
ProxyResolverV8(ProxyResolverJSBindings * custom_js_bindings,ProxyErrorListener * error_listener)714 ProxyResolverV8::ProxyResolverV8(
715 ProxyResolverJSBindings* custom_js_bindings,
716 ProxyErrorListener* error_listener)
717 : context_(NULL), js_bindings_(custom_js_bindings),
718 error_listener_(error_listener) {
719 if (!initialized_for_this_process_) {
720 v8::Platform* platform = v8::platform::CreateDefaultPlatform();
721 v8::V8::InitializePlatform(platform);
722 v8::V8::Initialize();
723 initialized_for_this_process_ = true;
724 }
725 }
726
~ProxyResolverV8()727 ProxyResolverV8::~ProxyResolverV8() {
728 if (context_ != NULL) {
729 delete context_;
730 context_ = NULL;
731 }
732 if (js_bindings_ != NULL) {
733 delete js_bindings_;
734 }
735 }
736
GetProxyForURL(const std::u16string & spec,const std::u16string & host,std::u16string * results)737 int ProxyResolverV8::GetProxyForURL(const std::u16string& spec, const std::u16string& host,
738 std::u16string* results) {
739 // If the V8 instance has not been initialized (either because
740 // SetPacScript() wasn't called yet, or because it failed.
741 if (context_ == NULL) {
742 error_listener_->ErrorMessage(std::string("Context is null."));
743 return ERR_FAILED;
744 }
745 // Otherwise call into V8.
746 int rv = context_->ResolveProxy(spec, host, results);
747
748 return rv;
749 }
750
PurgeMemory()751 void ProxyResolverV8::PurgeMemory() {
752 context_->PurgeMemory();
753 }
754
SetPacScript(const std::u16string & script_data)755 int ProxyResolverV8::SetPacScript(const std::u16string& script_data) {
756 if (context_ != NULL) {
757 delete context_;
758 context_ = NULL;
759 }
760 if (script_data.length() == 0)
761 return ERR_PAC_SCRIPT_FAILED;
762
763 // To help debugging v8 initialization issues, add "--log_all --print_all_exceptions"
764 // to the options
765 // Disable JIT
766 static const char kNoOpt[] = "--no-opt";
767 v8::V8::SetFlagsFromString(kNoOpt, strlen(kNoOpt));
768
769 // Try parsing the PAC script.
770 v8::Isolate::CreateParams create_params;
771 create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
772
773 context_ = new Context(js_bindings_, error_listener_, v8::Isolate::New(create_params));
774 int rv;
775 if ((rv = context_->InitV8(script_data)) != OK) {
776 context_ = NULL;
777 }
778 if (rv != OK)
779 context_ = NULL;
780 return rv;
781 }
782
783 } // namespace net
784