netwerk/base/src/ProxyAutoConfig.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "ProxyAutoConfig.h"
michael@0 8 #include "nsICancelable.h"
michael@0 9 #include "nsIDNSListener.h"
michael@0 10 #include "nsIDNSRecord.h"
michael@0 11 #include "nsIDNSService.h"
michael@0 12 #include "nsThreadUtils.h"
michael@0 13 #include "nsIConsoleService.h"
michael@0 14 #include "nsJSUtils.h"
michael@0 15 #include "jsfriendapi.h"
michael@0 16 #include "prnetdb.h"
michael@0 17 #include "nsITimer.h"
michael@0 18 #include "mozilla/net/DNS.h"
michael@0 19 #include "nsServiceManagerUtils.h"
michael@0 20 #include "nsNetCID.h"
michael@0 21
michael@0 22 namespace mozilla {
michael@0 23 namespace net {
michael@0 24
michael@0 25 // These are some global helper symbols the PAC format requires that we provide that
michael@0 26 // are initialized as part of the global javascript context used for PAC evaluations.
michael@0 27 // Additionally dnsResolve(host) and myIpAddress() are supplied in the same context
michael@0 28 // but are implemented as c++ helpers. alert(msg) is similarly defined.
michael@0 29
michael@0 30 static const char *sPacUtils =
michael@0 31 "function dnsDomainIs(host, domain) {\n"
michael@0 32 " return (host.length >= domain.length &&\n"
michael@0 33 " host.substring(host.length - domain.length) == domain);\n"
michael@0 34 "}\n"
michael@0 35 ""
michael@0 36 "function dnsDomainLevels(host) {\n"
michael@0 37 " return host.split('.').length - 1;\n"
michael@0 38 "}\n"
michael@0 39 ""
michael@0 40 "function convert_addr(ipchars) {\n"
michael@0 41 " var bytes = ipchars.split('.');\n"
michael@0 42 " var result = ((bytes[0] & 0xff) << 24) |\n"
michael@0 43 " ((bytes[1] & 0xff) << 16) |\n"
michael@0 44 " ((bytes[2] & 0xff) << 8) |\n"
michael@0 45 " (bytes[3] & 0xff);\n"
michael@0 46 " return result;\n"
michael@0 47 "}\n"
michael@0 48 ""
michael@0 49 "function isInNet(ipaddr, pattern, maskstr) {\n"
michael@0 50 " var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipaddr);\n"
michael@0 51 " if (test == null) {\n"
michael@0 52 " ipaddr = dnsResolve(ipaddr);\n"
michael@0 53 " if (ipaddr == null)\n"
michael@0 54 " return false;\n"
michael@0 55 " } else if (test[1] > 255 || test[2] > 255 || \n"
michael@0 56 " test[3] > 255 || test[4] > 255) {\n"
michael@0 57 " return false; // not an IP address\n"
michael@0 58 " }\n"
michael@0 59 " var host = convert_addr(ipaddr);\n"
michael@0 60 " var pat = convert_addr(pattern);\n"
michael@0 61 " var mask = convert_addr(maskstr);\n"
michael@0 62 " return ((host & mask) == (pat & mask));\n"
michael@0 63 " \n"
michael@0 64 "}\n"
michael@0 65 ""
michael@0 66 "function isPlainHostName(host) {\n"
michael@0 67 " return (host.search('\\\\.') == -1);\n"
michael@0 68 "}\n"
michael@0 69 ""
michael@0 70 "function isResolvable(host) {\n"
michael@0 71 " var ip = dnsResolve(host);\n"
michael@0 72 " return (ip != null);\n"
michael@0 73 "}\n"
michael@0 74 ""
michael@0 75 "function localHostOrDomainIs(host, hostdom) {\n"
michael@0 76 " return (host == hostdom) ||\n"
michael@0 77 " (hostdom.lastIndexOf(host + '.', 0) == 0);\n"
michael@0 78 "}\n"
michael@0 79 ""
michael@0 80 "function shExpMatch(url, pattern) {\n"
michael@0 81 " pattern = pattern.replace(/\\./g, '\\\\.');\n"
michael@0 82 " pattern = pattern.replace(/\\*/g, '.*');\n"
michael@0 83 " pattern = pattern.replace(/\\?/g, '.');\n"
michael@0 84 " var newRe = new RegExp('^'+pattern+'$');\n"
michael@0 85 " return newRe.test(url);\n"
michael@0 86 "}\n"
michael@0 87 ""
michael@0 88 "var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n"
michael@0 89 "var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};\n"
michael@0 90 ""
michael@0 91 "function weekdayRange() {\n"
michael@0 92 " function getDay(weekday) {\n"
michael@0 93 " if (weekday in wdays) {\n"
michael@0 94 " return wdays[weekday];\n"
michael@0 95 " }\n"
michael@0 96 " return -1;\n"
michael@0 97 " }\n"
michael@0 98 " var date = new Date();\n"
michael@0 99 " var argc = arguments.length;\n"
michael@0 100 " var wday;\n"
michael@0 101 " if (argc < 1)\n"
michael@0 102 " return false;\n"
michael@0 103 " if (arguments[argc - 1] == 'GMT') {\n"
michael@0 104 " argc--;\n"
michael@0 105 " wday = date.getUTCDay();\n"
michael@0 106 " } else {\n"
michael@0 107 " wday = date.getDay();\n"
michael@0 108 " }\n"
michael@0 109 " var wd1 = getDay(arguments[0]);\n"
michael@0 110 " var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n"
michael@0 111 " return (wd1 == -1 || wd2 == -1) ? false\n"
michael@0 112 " : (wd1 <= wday && wday <= wd2);\n"
michael@0 113 "}\n"
michael@0 114 ""
michael@0 115 "function dateRange() {\n"
michael@0 116 " function getMonth(name) {\n"
michael@0 117 " if (name in months) {\n"
michael@0 118 " return months[name];\n"
michael@0 119 " }\n"
michael@0 120 " return -1;\n"
michael@0 121 " }\n"
michael@0 122 " var date = new Date();\n"
michael@0 123 " var argc = arguments.length;\n"
michael@0 124 " if (argc < 1) {\n"
michael@0 125 " return false;\n"
michael@0 126 " }\n"
michael@0 127 " var isGMT = (arguments[argc - 1] == 'GMT');\n"
michael@0 128 "\n"
michael@0 129 " if (isGMT) {\n"
michael@0 130 " argc--;\n"
michael@0 131 " }\n"
michael@0 132 " // function will work even without explict handling of this case\n"
michael@0 133 " if (argc == 1) {\n"
michael@0 134 " var tmp = parseInt(arguments[0]);\n"
michael@0 135 " if (isNaN(tmp)) {\n"
michael@0 136 " return ((isGMT ? date.getUTCMonth() : date.getMonth()) ==\n"
michael@0 137 " getMonth(arguments[0]));\n"
michael@0 138 " } else if (tmp < 32) {\n"
michael@0 139 " return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);\n"
michael@0 140 " } else { \n"
michael@0 141 " return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) ==\n"
michael@0 142 " tmp);\n"
michael@0 143 " }\n"
michael@0 144 " }\n"
michael@0 145 " var year = date.getFullYear();\n"
michael@0 146 " var date1, date2;\n"
michael@0 147 " date1 = new Date(year, 0, 1, 0, 0, 0);\n"
michael@0 148 " date2 = new Date(year, 11, 31, 23, 59, 59);\n"
michael@0 149 " var adjustMonth = false;\n"
michael@0 150 " for (var i = 0; i < (argc >> 1); i++) {\n"
michael@0 151 " var tmp = parseInt(arguments[i]);\n"
michael@0 152 " if (isNaN(tmp)) {\n"
michael@0 153 " var mon = getMonth(arguments[i]);\n"
michael@0 154 " date1.setMonth(mon);\n"
michael@0 155 " } else if (tmp < 32) {\n"
michael@0 156 " adjustMonth = (argc <= 2);\n"
michael@0 157 " date1.setDate(tmp);\n"
michael@0 158 " } else {\n"
michael@0 159 " date1.setFullYear(tmp);\n"
michael@0 160 " }\n"
michael@0 161 " }\n"
michael@0 162 " for (var i = (argc >> 1); i < argc; i++) {\n"
michael@0 163 " var tmp = parseInt(arguments[i]);\n"
michael@0 164 " if (isNaN(tmp)) {\n"
michael@0 165 " var mon = getMonth(arguments[i]);\n"
michael@0 166 " date2.setMonth(mon);\n"
michael@0 167 " } else if (tmp < 32) {\n"
michael@0 168 " date2.setDate(tmp);\n"
michael@0 169 " } else {\n"
michael@0 170 " date2.setFullYear(tmp);\n"
michael@0 171 " }\n"
michael@0 172 " }\n"
michael@0 173 " if (adjustMonth) {\n"
michael@0 174 " date1.setMonth(date.getMonth());\n"
michael@0 175 " date2.setMonth(date.getMonth());\n"
michael@0 176 " }\n"
michael@0 177 " if (isGMT) {\n"
michael@0 178 " var tmp = date;\n"
michael@0 179 " tmp.setFullYear(date.getUTCFullYear());\n"
michael@0 180 " tmp.setMonth(date.getUTCMonth());\n"
michael@0 181 " tmp.setDate(date.getUTCDate());\n"
michael@0 182 " tmp.setHours(date.getUTCHours());\n"
michael@0 183 " tmp.setMinutes(date.getUTCMinutes());\n"
michael@0 184 " tmp.setSeconds(date.getUTCSeconds());\n"
michael@0 185 " date = tmp;\n"
michael@0 186 " }\n"
michael@0 187 " return ((date1 <= date) && (date <= date2));\n"
michael@0 188 "}\n"
michael@0 189 ""
michael@0 190 "function timeRange() {\n"
michael@0 191 " var argc = arguments.length;\n"
michael@0 192 " var date = new Date();\n"
michael@0 193 " var isGMT= false;\n"
michael@0 194 ""
michael@0 195 " if (argc < 1) {\n"
michael@0 196 " return false;\n"
michael@0 197 " }\n"
michael@0 198 " if (arguments[argc - 1] == 'GMT') {\n"
michael@0 199 " isGMT = true;\n"
michael@0 200 " argc--;\n"
michael@0 201 " }\n"
michael@0 202 "\n"
michael@0 203 " var hour = isGMT ? date.getUTCHours() : date.getHours();\n"
michael@0 204 " var date1, date2;\n"
michael@0 205 " date1 = new Date();\n"
michael@0 206 " date2 = new Date();\n"
michael@0 207 "\n"
michael@0 208 " if (argc == 1) {\n"
michael@0 209 " return (hour == arguments[0]);\n"
michael@0 210 " } else if (argc == 2) {\n"
michael@0 211 " return ((arguments[0] <= hour) && (hour <= arguments[1]));\n"
michael@0 212 " } else {\n"
michael@0 213 " switch (argc) {\n"
michael@0 214 " case 6:\n"
michael@0 215 " date1.setSeconds(arguments[2]);\n"
michael@0 216 " date2.setSeconds(arguments[5]);\n"
michael@0 217 " case 4:\n"
michael@0 218 " var middle = argc >> 1;\n"
michael@0 219 " date1.setHours(arguments[0]);\n"
michael@0 220 " date1.setMinutes(arguments[1]);\n"
michael@0 221 " date2.setHours(arguments[middle]);\n"
michael@0 222 " date2.setMinutes(arguments[middle + 1]);\n"
michael@0 223 " if (middle == 2) {\n"
michael@0 224 " date2.setSeconds(59);\n"
michael@0 225 " }\n"
michael@0 226 " break;\n"
michael@0 227 " default:\n"
michael@0 228 " throw 'timeRange: bad number of arguments'\n"
michael@0 229 " }\n"
michael@0 230 " }\n"
michael@0 231 "\n"
michael@0 232 " if (isGMT) {\n"
michael@0 233 " date.setFullYear(date.getUTCFullYear());\n"
michael@0 234 " date.setMonth(date.getUTCMonth());\n"
michael@0 235 " date.setDate(date.getUTCDate());\n"
michael@0 236 " date.setHours(date.getUTCHours());\n"
michael@0 237 " date.setMinutes(date.getUTCMinutes());\n"
michael@0 238 " date.setSeconds(date.getUTCSeconds());\n"
michael@0 239 " }\n"
michael@0 240 " return ((date1 <= date) && (date <= date2));\n"
michael@0 241 "}\n"
michael@0 242 "";
michael@0 243
michael@0 244 // sRunning is defined for the helper functions only while the
michael@0 245 // Javascript engine is running and the PAC object cannot be deleted
michael@0 246 // or reset.
michael@0 247 static ProxyAutoConfig *sRunning = nullptr;
michael@0 248
michael@0 249 // The PACResolver is used for dnsResolve()
michael@0 250 class PACResolver MOZ_FINAL : public nsIDNSListener
michael@0 251 , public nsITimerCallback
michael@0 252 {
michael@0 253 public:
michael@0 254 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 255
michael@0 256 PACResolver()
michael@0 257 : mStatus(NS_ERROR_FAILURE)
michael@0 258 {
michael@0 259 }
michael@0 260
michael@0 261 // nsIDNSListener
michael@0 262 NS_IMETHODIMP OnLookupComplete(nsICancelable *request,
michael@0 263 nsIDNSRecord *record,
michael@0 264 nsresult status)
michael@0 265 {
michael@0 266 if (mTimer) {
michael@0 267 mTimer->Cancel();
michael@0 268 mTimer = nullptr;
michael@0 269 }
michael@0 270
michael@0 271 mRequest = nullptr;
michael@0 272 mStatus = status;
michael@0 273 mResponse = record;
michael@0 274 return NS_OK;
michael@0 275 }
michael@0 276
michael@0 277 // nsITimerCallback
michael@0 278 NS_IMETHODIMP Notify(nsITimer *timer)
michael@0 279 {
michael@0 280 if (mRequest)
michael@0 281 mRequest->Cancel(NS_ERROR_NET_TIMEOUT);
michael@0 282 mTimer = nullptr;
michael@0 283 return NS_OK;
michael@0 284 }
michael@0 285
michael@0 286 nsresult mStatus;
michael@0 287 nsCOMPtr<nsICancelable> mRequest;
michael@0 288 nsCOMPtr<nsIDNSRecord> mResponse;
michael@0 289 nsCOMPtr<nsITimer> mTimer;
michael@0 290 };
michael@0 291 NS_IMPL_ISUPPORTS(PACResolver, nsIDNSListener, nsITimerCallback)
michael@0 292
michael@0 293 static
michael@0 294 void PACLogToConsole(nsString &aMessage)
michael@0 295 {
michael@0 296 nsCOMPtr<nsIConsoleService> consoleService =
michael@0 297 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
michael@0 298 if (!consoleService)
michael@0 299 return;
michael@0 300
michael@0 301 consoleService->LogStringMessage(aMessage.get());
michael@0 302 }
michael@0 303
michael@0 304 // Javascript errors are logged to the main error console
michael@0 305 static void
michael@0 306 PACErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
michael@0 307 {
michael@0 308 nsString formattedMessage(NS_LITERAL_STRING("PAC Execution Error: "));
michael@0 309 formattedMessage += report->ucmessage;
michael@0 310 formattedMessage += NS_LITERAL_STRING(" [");
michael@0 311 formattedMessage += report->uclinebuf;
michael@0 312 formattedMessage += NS_LITERAL_STRING("]");
michael@0 313 PACLogToConsole(formattedMessage);
michael@0 314 }
michael@0 315
michael@0 316 // timeout of 0 means the normal necko timeout strategy, otherwise the dns request
michael@0 317 // will be canceled after aTimeout milliseconds
michael@0 318 static
michael@0 319 bool PACResolve(const nsCString &aHostName, NetAddr *aNetAddr,
michael@0 320 unsigned int aTimeout)
michael@0 321 {
michael@0 322 if (!sRunning) {
michael@0 323 NS_WARNING("PACResolve without a running ProxyAutoConfig object");
michael@0 324 return false;
michael@0 325 }
michael@0 326
michael@0 327 return sRunning->ResolveAddress(aHostName, aNetAddr, aTimeout);
michael@0 328 }
michael@0 329
michael@0 330 ProxyAutoConfig::ProxyAutoConfig()
michael@0 331 : mJSRuntime(nullptr)
michael@0 332 , mJSNeedsSetup(false)
michael@0 333 , mShutdown(false)
michael@0 334 {
michael@0 335 MOZ_COUNT_CTOR(ProxyAutoConfig);
michael@0 336 }
michael@0 337
michael@0 338 bool
michael@0 339 ProxyAutoConfig::ResolveAddress(const nsCString &aHostName,
michael@0 340 NetAddr *aNetAddr,
michael@0 341 unsigned int aTimeout)
michael@0 342 {
michael@0 343 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
michael@0 344 if (!dns)
michael@0 345 return false;
michael@0 346
michael@0 347 nsRefPtr<PACResolver> helper = new PACResolver();
michael@0 348
michael@0 349 if (NS_FAILED(dns->AsyncResolve(aHostName,
michael@0 350 nsIDNSService::RESOLVE_PRIORITY_MEDIUM,
michael@0 351 helper,
michael@0 352 NS_GetCurrentThread(),
michael@0 353 getter_AddRefs(helper->mRequest))))
michael@0 354 return false;
michael@0 355
michael@0 356 if (aTimeout && helper->mRequest) {
michael@0 357 if (!mTimer)
michael@0 358 mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 359 if (mTimer) {
michael@0 360 mTimer->InitWithCallback(helper, aTimeout, nsITimer::TYPE_ONE_SHOT);
michael@0 361 helper->mTimer = mTimer;
michael@0 362 }
michael@0 363 }
michael@0 364
michael@0 365 // Spin the event loop of the pac thread until lookup is complete.
michael@0 366 // nsPACman is responsible for keeping a queue and only allowing
michael@0 367 // one PAC execution at a time even when it is called re-entrantly.
michael@0 368 while (helper->mRequest)
michael@0 369 NS_ProcessNextEvent(NS_GetCurrentThread());
michael@0 370
michael@0 371 if (NS_FAILED(helper->mStatus) ||
michael@0 372 NS_FAILED(helper->mResponse->GetNextAddr(0, aNetAddr)))
michael@0 373 return false;
michael@0 374 return true;
michael@0 375 }
michael@0 376
michael@0 377 static
michael@0 378 bool PACResolveToString(const nsCString &aHostName,
michael@0 379 nsCString &aDottedDecimal,
michael@0 380 unsigned int aTimeout)
michael@0 381 {
michael@0 382 NetAddr netAddr;
michael@0 383 if (!PACResolve(aHostName, &netAddr, aTimeout))
michael@0 384 return false;
michael@0 385
michael@0 386 char dottedDecimal[128];
michael@0 387 if (!NetAddrToString(&netAddr, dottedDecimal, sizeof(dottedDecimal)))
michael@0 388 return false;
michael@0 389
michael@0 390 aDottedDecimal.Assign(dottedDecimal);
michael@0 391 return true;
michael@0 392 }
michael@0 393
michael@0 394 // dnsResolve(host) javascript implementation
michael@0 395 static
michael@0 396 bool PACDnsResolve(JSContext *cx, unsigned int argc, JS::Value *vp)
michael@0 397 {
michael@0 398 JS::CallArgs args = CallArgsFromVp(argc, vp);
michael@0 399
michael@0 400 if (NS_IsMainThread()) {
michael@0 401 NS_WARNING("DNS Resolution From PAC on Main Thread. How did that happen?");
michael@0 402 return false;
michael@0 403 }
michael@0 404
michael@0 405 JS::Rooted<JSString*> arg1(cx);
michael@0 406 if (!JS_ConvertArguments(cx, args, "S", arg1.address()))
michael@0 407 return false;
michael@0 408
michael@0 409 nsDependentJSString hostName;
michael@0 410 nsAutoCString dottedDecimal;
michael@0 411
michael@0 412 if (!hostName.init(cx, arg1))
michael@0 413 return false;
michael@0 414 if (PACResolveToString(NS_ConvertUTF16toUTF8(hostName), dottedDecimal, 0)) {
michael@0 415 JSString *dottedDecimalString = JS_NewStringCopyZ(cx, dottedDecimal.get());
michael@0 416 if (!dottedDecimalString) {
michael@0 417 return false;
michael@0 418 }
michael@0 419
michael@0 420 args.rval().setString(dottedDecimalString);
michael@0 421 }
michael@0 422 else {
michael@0 423 args.rval().setNull();
michael@0 424 }
michael@0 425
michael@0 426 return true;
michael@0 427 }
michael@0 428
michael@0 429 // myIpAddress() javascript implementation
michael@0 430 static
michael@0 431 bool PACMyIpAddress(JSContext *cx, unsigned int argc, JS::Value *vp)
michael@0 432 {
michael@0 433 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 434
michael@0 435 if (NS_IsMainThread()) {
michael@0 436 NS_WARNING("DNS Resolution From PAC on Main Thread. How did that happen?");
michael@0 437 return false;
michael@0 438 }
michael@0 439
michael@0 440 if (!sRunning) {
michael@0 441 NS_WARNING("PAC myIPAddress without a running ProxyAutoConfig object");
michael@0 442 return false;
michael@0 443 }
michael@0 444
michael@0 445 return sRunning->MyIPAddress(args);
michael@0 446 }
michael@0 447
michael@0 448 // proxyAlert(msg) javascript implementation
michael@0 449 static
michael@0 450 bool PACProxyAlert(JSContext *cx, unsigned int argc, JS::Value *vp)
michael@0 451 {
michael@0 452 JS::CallArgs args = CallArgsFromVp(argc, vp);
michael@0 453
michael@0 454 JS::Rooted<JSString*> arg1(cx);
michael@0 455 if (!JS_ConvertArguments(cx, args, "S", arg1.address()))
michael@0 456 return false;
michael@0 457
michael@0 458 nsDependentJSString message;
michael@0 459 if (!message.init(cx, arg1))
michael@0 460 return false;
michael@0 461
michael@0 462 nsString alertMessage;
michael@0 463 alertMessage.SetCapacity(32 + message.Length());
michael@0 464 alertMessage += NS_LITERAL_STRING("PAC-alert: ");
michael@0 465 alertMessage += message;
michael@0 466 PACLogToConsole(alertMessage);
michael@0 467
michael@0 468 args.rval().setUndefined(); /* return undefined */
michael@0 469 return true;
michael@0 470 }
michael@0 471
michael@0 472 static const JSFunctionSpec PACGlobalFunctions[] = {
michael@0 473 JS_FS("dnsResolve", PACDnsResolve, 1, 0),
michael@0 474 JS_FS("myIpAddress", PACMyIpAddress, 0, 0),
michael@0 475 JS_FS("alert", PACProxyAlert, 1, 0),
michael@0 476 JS_FS_END
michael@0 477 };
michael@0 478
michael@0 479 // JSRuntimeWrapper is a c++ object that manages the runtime and context
michael@0 480 // for the JS engine used on the PAC thread. It is initialized and destroyed
michael@0 481 // on the PAC thread.
michael@0 482 class JSRuntimeWrapper
michael@0 483 {
michael@0 484 public:
michael@0 485 static JSRuntimeWrapper *Create()
michael@0 486 {
michael@0 487 JSRuntimeWrapper *entry = new JSRuntimeWrapper();
michael@0 488
michael@0 489 if (NS_FAILED(entry->Init())) {
michael@0 490 delete entry;
michael@0 491 return nullptr;
michael@0 492 }
michael@0 493
michael@0 494 return entry;
michael@0 495 }
michael@0 496
michael@0 497 JSContext *Context() const
michael@0 498 {
michael@0 499 return mContext;
michael@0 500 }
michael@0 501
michael@0 502 JSObject *Global() const
michael@0 503 {
michael@0 504 return mGlobal;
michael@0 505 }
michael@0 506
michael@0 507 ~JSRuntimeWrapper()
michael@0 508 {
michael@0 509 MOZ_COUNT_DTOR(JSRuntimeWrapper);
michael@0 510 if (mContext) {
michael@0 511 JS_DestroyContext(mContext);
michael@0 512 }
michael@0 513
michael@0 514 if (mRuntime) {
michael@0 515 JS_DestroyRuntime(mRuntime);
michael@0 516 }
michael@0 517 }
michael@0 518
michael@0 519 void SetOK()
michael@0 520 {
michael@0 521 mOK = true;
michael@0 522 }
michael@0 523
michael@0 524 bool IsOK()
michael@0 525 {
michael@0 526 return mOK;
michael@0 527 }
michael@0 528
michael@0 529 private:
michael@0 530 static const unsigned sRuntimeHeapSize = 2 << 20;
michael@0 531
michael@0 532 JSRuntime *mRuntime;
michael@0 533 JSContext *mContext;
michael@0 534 JSObject *mGlobal;
michael@0 535 bool mOK;
michael@0 536
michael@0 537 static const JSClass sGlobalClass;
michael@0 538
michael@0 539 JSRuntimeWrapper()
michael@0 540 : mRuntime(nullptr), mContext(nullptr), mGlobal(nullptr), mOK(false)
michael@0 541 {
michael@0 542 MOZ_COUNT_CTOR(JSRuntimeWrapper);
michael@0 543 }
michael@0 544
michael@0 545 nsresult Init()
michael@0 546 {
michael@0 547 mRuntime = JS_NewRuntime(sRuntimeHeapSize, JS_NO_HELPER_THREADS);
michael@0 548 NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY);
michael@0 549
michael@0 550 /*
michael@0 551 * Not setting this will cause JS_CHECK_RECURSION to report false
michael@0 552 * positives
michael@0 553 */
michael@0 554 JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024);
michael@0 555
michael@0 556 mContext = JS_NewContext(mRuntime, 0);
michael@0 557 NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY);
michael@0 558
michael@0 559 JSAutoRequest ar(mContext);
michael@0 560
michael@0 561 JS::CompartmentOptions options;
michael@0 562 options.setZone(JS::SystemZone)
michael@0 563 .setVersion(JSVERSION_LATEST);
michael@0 564 mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
michael@0 565 JS::DontFireOnNewGlobalHook, options);
michael@0 566 NS_ENSURE_TRUE(mGlobal, NS_ERROR_OUT_OF_MEMORY);
michael@0 567 JS::Rooted<JSObject*> global(mContext, mGlobal);
michael@0 568
michael@0 569 JSAutoCompartment ac(mContext, global);
michael@0 570 js::SetDefaultObjectForContext(mContext, global);
michael@0 571 JS_InitStandardClasses(mContext, global);
michael@0 572
michael@0 573 JS_SetErrorReporter(mContext, PACErrorReporter);
michael@0 574
michael@0 575 if (!JS_DefineFunctions(mContext, global, PACGlobalFunctions))
michael@0 576 return NS_ERROR_FAILURE;
michael@0 577
michael@0 578 JS_FireOnNewGlobalObject(mContext, global);
michael@0 579
michael@0 580 return NS_OK;
michael@0 581 }
michael@0 582 };
michael@0 583
michael@0 584 const JSClass JSRuntimeWrapper::sGlobalClass = {
michael@0 585 "PACResolutionThreadGlobal",
michael@0 586 JSCLASS_GLOBAL_FLAGS,
michael@0 587 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
michael@0 588 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
michael@0 589 nullptr, nullptr, nullptr, nullptr,
michael@0 590 JS_GlobalObjectTraceHook
michael@0 591 };
michael@0 592
michael@0 593 nsresult
michael@0 594 ProxyAutoConfig::Init(const nsCString &aPACURI,
michael@0 595 const nsCString &aPACScript)
michael@0 596 {
michael@0 597 mPACURI = aPACURI;
michael@0 598 mPACScript = sPacUtils;
michael@0 599 mPACScript.Append(aPACScript);
michael@0 600
michael@0 601 if (!sRunning)
michael@0 602 return SetupJS();
michael@0 603
michael@0 604 mJSNeedsSetup = true;
michael@0 605 return NS_OK;
michael@0 606 }
michael@0 607
michael@0 608 nsresult
michael@0 609 ProxyAutoConfig::SetupJS()
michael@0 610 {
michael@0 611 mJSNeedsSetup = false;
michael@0 612 NS_ABORT_IF_FALSE(!sRunning, "JIT is running");
michael@0 613
michael@0 614 delete mJSRuntime;
michael@0 615 mJSRuntime = nullptr;
michael@0 616
michael@0 617 if (mPACScript.IsEmpty())
michael@0 618 return NS_ERROR_FAILURE;
michael@0 619
michael@0 620 mJSRuntime = JSRuntimeWrapper::Create();
michael@0 621 if (!mJSRuntime)
michael@0 622 return NS_ERROR_FAILURE;
michael@0 623
michael@0 624 JSContext* cx = mJSRuntime->Context();
michael@0 625 JSAutoRequest ar(cx);
michael@0 626 JSAutoCompartment ac(cx, mJSRuntime->Global());
michael@0 627
michael@0 628 // check if this is a data: uri so that we don't spam the js console with
michael@0 629 // huge meaningless strings. this is not on the main thread, so it can't
michael@0 630 // use nsIRUI scheme methods
michael@0 631 bool isDataURI = nsDependentCSubstring(mPACURI, 0, 5).LowerCaseEqualsASCII("data:", 5);
michael@0 632
michael@0 633 sRunning = this;
michael@0 634 JS::Rooted<JSObject*> global(cx, mJSRuntime->Global());
michael@0 635 JS::CompileOptions options(cx);
michael@0 636 options.setFileAndLine(mPACURI.get(), 1);
michael@0 637 JS::Rooted<JSScript*> script(cx, JS_CompileScript(cx, global, mPACScript.get(),
michael@0 638 mPACScript.Length(), options));
michael@0 639 if (!script || !JS_ExecuteScript(cx, global, script)) {
michael@0 640 nsString alertMessage(NS_LITERAL_STRING("PAC file failed to install from "));
michael@0 641 if (isDataURI) {
michael@0 642 alertMessage += NS_LITERAL_STRING("data: URI");
michael@0 643 }
michael@0 644 else {
michael@0 645 alertMessage += NS_ConvertUTF8toUTF16(mPACURI);
michael@0 646 }
michael@0 647 PACLogToConsole(alertMessage);
michael@0 648 sRunning = nullptr;
michael@0 649 return NS_ERROR_FAILURE;
michael@0 650 }
michael@0 651 sRunning = nullptr;
michael@0 652
michael@0 653 mJSRuntime->SetOK();
michael@0 654 nsString alertMessage(NS_LITERAL_STRING("PAC file installed from "));
michael@0 655 if (isDataURI) {
michael@0 656 alertMessage += NS_LITERAL_STRING("data: URI");
michael@0 657 }
michael@0 658 else {
michael@0 659 alertMessage += NS_ConvertUTF8toUTF16(mPACURI);
michael@0 660 }
michael@0 661 PACLogToConsole(alertMessage);
michael@0 662
michael@0 663 // we don't need these now
michael@0 664 mPACScript.Truncate();
michael@0 665 mPACURI.Truncate();
michael@0 666
michael@0 667 return NS_OK;
michael@0 668 }
michael@0 669
michael@0 670 nsresult
michael@0 671 ProxyAutoConfig::GetProxyForURI(const nsCString &aTestURI,
michael@0 672 const nsCString &aTestHost,
michael@0 673 nsACString &result)
michael@0 674 {
michael@0 675 if (mJSNeedsSetup)
michael@0 676 SetupJS();
michael@0 677
michael@0 678 if (!mJSRuntime || !mJSRuntime->IsOK())
michael@0 679 return NS_ERROR_NOT_AVAILABLE;
michael@0 680
michael@0 681 JSContext *cx = mJSRuntime->Context();
michael@0 682 JSAutoRequest ar(cx);
michael@0 683 JSAutoCompartment ac(cx, mJSRuntime->Global());
michael@0 684
michael@0 685 // the sRunning flag keeps a new PAC file from being installed
michael@0 686 // while the event loop is spinning on a DNS function. Don't early return.
michael@0 687 sRunning = this;
michael@0 688 mRunningHost = aTestHost;
michael@0 689
michael@0 690 nsresult rv = NS_ERROR_FAILURE;
michael@0 691 JS::RootedString uriString(cx, JS_NewStringCopyZ(cx, aTestURI.get()));
michael@0 692 JS::RootedString hostString(cx, JS_NewStringCopyZ(cx, aTestHost.get()));
michael@0 693
michael@0 694 if (uriString && hostString) {
michael@0 695 JS::AutoValueArray<2> args(cx);
michael@0 696 args[0].setString(uriString);
michael@0 697 args[1].setString(hostString);
michael@0 698
michael@0 699 JS::Rooted<JS::Value> rval(cx);
michael@0 700 JS::Rooted<JSObject*> global(cx, mJSRuntime->Global());
michael@0 701 bool ok = JS_CallFunctionName(cx, global, "FindProxyForURL", args, &rval);
michael@0 702
michael@0 703 if (ok && rval.isString()) {
michael@0 704 nsDependentJSString pacString;
michael@0 705 if (pacString.init(cx, rval.toString())) {
michael@0 706 CopyUTF16toUTF8(pacString, result);
michael@0 707 rv = NS_OK;
michael@0 708 }
michael@0 709 }
michael@0 710 }
michael@0 711
michael@0 712 mRunningHost.Truncate();
michael@0 713 sRunning = nullptr;
michael@0 714 return rv;
michael@0 715 }
michael@0 716
michael@0 717 void
michael@0 718 ProxyAutoConfig::GC()
michael@0 719 {
michael@0 720 if (!mJSRuntime || !mJSRuntime->IsOK())
michael@0 721 return;
michael@0 722
michael@0 723 JSAutoCompartment ac(mJSRuntime->Context(), mJSRuntime->Global());
michael@0 724 JS_MaybeGC(mJSRuntime->Context());
michael@0 725 }
michael@0 726
michael@0 727 ProxyAutoConfig::~ProxyAutoConfig()
michael@0 728 {
michael@0 729 MOZ_COUNT_DTOR(ProxyAutoConfig);
michael@0 730 NS_ASSERTION(!mJSRuntime,
michael@0 731 "~ProxyAutoConfig leaking JS runtime that "
michael@0 732 "should have been deleted on pac thread");
michael@0 733 }
michael@0 734
michael@0 735 void
michael@0 736 ProxyAutoConfig::Shutdown()
michael@0 737 {
michael@0 738 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread for shutdown");
michael@0 739
michael@0 740 if (sRunning || mShutdown)
michael@0 741 return;
michael@0 742
michael@0 743 mShutdown = true;
michael@0 744 delete mJSRuntime;
michael@0 745 mJSRuntime = nullptr;
michael@0 746 }
michael@0 747
michael@0 748 bool
michael@0 749 ProxyAutoConfig::SrcAddress(const NetAddr *remoteAddress, nsCString &localAddress)
michael@0 750 {
michael@0 751 PRFileDesc *fd;
michael@0 752 fd = PR_OpenUDPSocket(remoteAddress->raw.family);
michael@0 753 if (!fd)
michael@0 754 return false;
michael@0 755
michael@0 756 PRNetAddr prRemoteAddress;
michael@0 757 NetAddrToPRNetAddr(remoteAddress, &prRemoteAddress);
michael@0 758 if (PR_Connect(fd, &prRemoteAddress, 0) != PR_SUCCESS) {
michael@0 759 PR_Close(fd);
michael@0 760 return false;
michael@0 761 }
michael@0 762
michael@0 763 PRNetAddr localName;
michael@0 764 if (PR_GetSockName(fd, &localName) != PR_SUCCESS) {
michael@0 765 PR_Close(fd);
michael@0 766 return false;
michael@0 767 }
michael@0 768
michael@0 769 PR_Close(fd);
michael@0 770
michael@0 771 char dottedDecimal[128];
michael@0 772 if (PR_NetAddrToString(&localName, dottedDecimal, sizeof(dottedDecimal)) != PR_SUCCESS)
michael@0 773 return false;
michael@0 774
michael@0 775 localAddress.Assign(dottedDecimal);
michael@0 776
michael@0 777 return true;
michael@0 778 }
michael@0 779
michael@0 780 // hostName is run through a dns lookup and then a udp socket is connected
michael@0 781 // to the result. If that all works, the local IP address of the socket is
michael@0 782 // returned to the javascript caller and |*aResult| is set to true. Otherwise
michael@0 783 // |*aResult| is set to false.
michael@0 784 bool
michael@0 785 ProxyAutoConfig::MyIPAddressTryHost(const nsCString &hostName,
michael@0 786 unsigned int timeout,
michael@0 787 const JS::CallArgs &aArgs,
michael@0 788 bool* aResult)
michael@0 789 {
michael@0 790 *aResult = false;
michael@0 791
michael@0 792 NetAddr remoteAddress;
michael@0 793 nsAutoCString localDottedDecimal;
michael@0 794 JSContext *cx = mJSRuntime->Context();
michael@0 795
michael@0 796 if (PACResolve(hostName, &remoteAddress, timeout) &&
michael@0 797 SrcAddress(&remoteAddress, localDottedDecimal)) {
michael@0 798 JSString *dottedDecimalString =
michael@0 799 JS_NewStringCopyZ(cx, localDottedDecimal.get());
michael@0 800 if (!dottedDecimalString) {
michael@0 801 return false;
michael@0 802 }
michael@0 803
michael@0 804 *aResult = true;
michael@0 805 aArgs.rval().setString(dottedDecimalString);
michael@0 806 }
michael@0 807 return true;
michael@0 808 }
michael@0 809
michael@0 810 bool
michael@0 811 ProxyAutoConfig::MyIPAddress(const JS::CallArgs &aArgs)
michael@0 812 {
michael@0 813 nsAutoCString remoteDottedDecimal;
michael@0 814 nsAutoCString localDottedDecimal;
michael@0 815 JSContext *cx = mJSRuntime->Context();
michael@0 816
michael@0 817 // first, lookup the local address of a socket connected
michael@0 818 // to the host of uri being resolved by the pac file. This is
michael@0 819 // v6 safe.. but is the last step like that
michael@0 820 bool rvalAssigned = false;
michael@0 821 if (!MyIPAddressTryHost(mRunningHost, kTimeout, aArgs, &rvalAssigned) ||
michael@0 822 rvalAssigned) {
michael@0 823 return rvalAssigned;
michael@0 824 }
michael@0 825
michael@0 826 // next, look for a route to a public internet address that doesn't need DNS.
michael@0 827 // This is the google anycast dns address, but it doesn't matter if it
michael@0 828 // remains operable (as we don't contact it) as long as the address stays
michael@0 829 // in commonly routed IP address space.
michael@0 830 remoteDottedDecimal.AssignLiteral("8.8.8.8");
michael@0 831 if (!MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs, &rvalAssigned) ||
michael@0 832 rvalAssigned) {
michael@0 833 return rvalAssigned;
michael@0 834 }
michael@0 835
michael@0 836 // next, use the old algorithm based on the local hostname
michael@0 837 nsAutoCString hostName;
michael@0 838 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
michael@0 839 if (dns && NS_SUCCEEDED(dns->GetMyHostName(hostName)) &&
michael@0 840 PACResolveToString(hostName, localDottedDecimal, kTimeout)) {
michael@0 841 JSString *dottedDecimalString =
michael@0 842 JS_NewStringCopyZ(cx, localDottedDecimal.get());
michael@0 843 if (!dottedDecimalString) {
michael@0 844 return false;
michael@0 845 }
michael@0 846
michael@0 847 aArgs.rval().setString(dottedDecimalString);
michael@0 848 return true;
michael@0 849 }
michael@0 850
michael@0 851 // next try a couple RFC 1918 variants.. maybe there is a
michael@0 852 // local route
michael@0 853 remoteDottedDecimal.AssignLiteral("192.168.0.1");
michael@0 854 if (!MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs, &rvalAssigned) ||
michael@0 855 rvalAssigned) {
michael@0 856 return rvalAssigned;
michael@0 857 }
michael@0 858
michael@0 859 // more RFC 1918
michael@0 860 remoteDottedDecimal.AssignLiteral("10.0.0.1");
michael@0 861 if (!MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs, &rvalAssigned) ||
michael@0 862 rvalAssigned) {
michael@0 863 return rvalAssigned;
michael@0 864 }
michael@0 865
michael@0 866 // who knows? let's fallback to localhost
michael@0 867 localDottedDecimal.AssignLiteral("127.0.0.1");
michael@0 868 JSString *dottedDecimalString =
michael@0 869 JS_NewStringCopyZ(cx, localDottedDecimal.get());
michael@0 870 if (!dottedDecimalString) {
michael@0 871 return false;
michael@0 872 }
michael@0 873
michael@0 874 aArgs.rval().setString(dottedDecimalString);
michael@0 875 return true;
michael@0 876 }
michael@0 877
michael@0 878 } // namespace mozilla
michael@0 879 } // namespace mozilla::net

mercurial