1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/ProxyAutoConfig.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,879 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "ProxyAutoConfig.h" 1.11 +#include "nsICancelable.h" 1.12 +#include "nsIDNSListener.h" 1.13 +#include "nsIDNSRecord.h" 1.14 +#include "nsIDNSService.h" 1.15 +#include "nsThreadUtils.h" 1.16 +#include "nsIConsoleService.h" 1.17 +#include "nsJSUtils.h" 1.18 +#include "jsfriendapi.h" 1.19 +#include "prnetdb.h" 1.20 +#include "nsITimer.h" 1.21 +#include "mozilla/net/DNS.h" 1.22 +#include "nsServiceManagerUtils.h" 1.23 +#include "nsNetCID.h" 1.24 + 1.25 +namespace mozilla { 1.26 +namespace net { 1.27 + 1.28 +// These are some global helper symbols the PAC format requires that we provide that 1.29 +// are initialized as part of the global javascript context used for PAC evaluations. 1.30 +// Additionally dnsResolve(host) and myIpAddress() are supplied in the same context 1.31 +// but are implemented as c++ helpers. alert(msg) is similarly defined. 1.32 + 1.33 +static const char *sPacUtils = 1.34 + "function dnsDomainIs(host, domain) {\n" 1.35 + " return (host.length >= domain.length &&\n" 1.36 + " host.substring(host.length - domain.length) == domain);\n" 1.37 + "}\n" 1.38 + "" 1.39 + "function dnsDomainLevels(host) {\n" 1.40 + " return host.split('.').length - 1;\n" 1.41 + "}\n" 1.42 + "" 1.43 + "function convert_addr(ipchars) {\n" 1.44 + " var bytes = ipchars.split('.');\n" 1.45 + " var result = ((bytes[0] & 0xff) << 24) |\n" 1.46 + " ((bytes[1] & 0xff) << 16) |\n" 1.47 + " ((bytes[2] & 0xff) << 8) |\n" 1.48 + " (bytes[3] & 0xff);\n" 1.49 + " return result;\n" 1.50 + "}\n" 1.51 + "" 1.52 + "function isInNet(ipaddr, pattern, maskstr) {\n" 1.53 + " var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipaddr);\n" 1.54 + " if (test == null) {\n" 1.55 + " ipaddr = dnsResolve(ipaddr);\n" 1.56 + " if (ipaddr == null)\n" 1.57 + " return false;\n" 1.58 + " } else if (test[1] > 255 || test[2] > 255 || \n" 1.59 + " test[3] > 255 || test[4] > 255) {\n" 1.60 + " return false; // not an IP address\n" 1.61 + " }\n" 1.62 + " var host = convert_addr(ipaddr);\n" 1.63 + " var pat = convert_addr(pattern);\n" 1.64 + " var mask = convert_addr(maskstr);\n" 1.65 + " return ((host & mask) == (pat & mask));\n" 1.66 + " \n" 1.67 + "}\n" 1.68 + "" 1.69 + "function isPlainHostName(host) {\n" 1.70 + " return (host.search('\\\\.') == -1);\n" 1.71 + "}\n" 1.72 + "" 1.73 + "function isResolvable(host) {\n" 1.74 + " var ip = dnsResolve(host);\n" 1.75 + " return (ip != null);\n" 1.76 + "}\n" 1.77 + "" 1.78 + "function localHostOrDomainIs(host, hostdom) {\n" 1.79 + " return (host == hostdom) ||\n" 1.80 + " (hostdom.lastIndexOf(host + '.', 0) == 0);\n" 1.81 + "}\n" 1.82 + "" 1.83 + "function shExpMatch(url, pattern) {\n" 1.84 + " pattern = pattern.replace(/\\./g, '\\\\.');\n" 1.85 + " pattern = pattern.replace(/\\*/g, '.*');\n" 1.86 + " pattern = pattern.replace(/\\?/g, '.');\n" 1.87 + " var newRe = new RegExp('^'+pattern+'$');\n" 1.88 + " return newRe.test(url);\n" 1.89 + "}\n" 1.90 + "" 1.91 + "var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n" 1.92 + "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" 1.93 + "" 1.94 + "function weekdayRange() {\n" 1.95 + " function getDay(weekday) {\n" 1.96 + " if (weekday in wdays) {\n" 1.97 + " return wdays[weekday];\n" 1.98 + " }\n" 1.99 + " return -1;\n" 1.100 + " }\n" 1.101 + " var date = new Date();\n" 1.102 + " var argc = arguments.length;\n" 1.103 + " var wday;\n" 1.104 + " if (argc < 1)\n" 1.105 + " return false;\n" 1.106 + " if (arguments[argc - 1] == 'GMT') {\n" 1.107 + " argc--;\n" 1.108 + " wday = date.getUTCDay();\n" 1.109 + " } else {\n" 1.110 + " wday = date.getDay();\n" 1.111 + " }\n" 1.112 + " var wd1 = getDay(arguments[0]);\n" 1.113 + " var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n" 1.114 + " return (wd1 == -1 || wd2 == -1) ? false\n" 1.115 + " : (wd1 <= wday && wday <= wd2);\n" 1.116 + "}\n" 1.117 + "" 1.118 + "function dateRange() {\n" 1.119 + " function getMonth(name) {\n" 1.120 + " if (name in months) {\n" 1.121 + " return months[name];\n" 1.122 + " }\n" 1.123 + " return -1;\n" 1.124 + " }\n" 1.125 + " var date = new Date();\n" 1.126 + " var argc = arguments.length;\n" 1.127 + " if (argc < 1) {\n" 1.128 + " return false;\n" 1.129 + " }\n" 1.130 + " var isGMT = (arguments[argc - 1] == 'GMT');\n" 1.131 + "\n" 1.132 + " if (isGMT) {\n" 1.133 + " argc--;\n" 1.134 + " }\n" 1.135 + " // function will work even without explict handling of this case\n" 1.136 + " if (argc == 1) {\n" 1.137 + " var tmp = parseInt(arguments[0]);\n" 1.138 + " if (isNaN(tmp)) {\n" 1.139 + " return ((isGMT ? date.getUTCMonth() : date.getMonth()) ==\n" 1.140 + " getMonth(arguments[0]));\n" 1.141 + " } else if (tmp < 32) {\n" 1.142 + " return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);\n" 1.143 + " } else { \n" 1.144 + " return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) ==\n" 1.145 + " tmp);\n" 1.146 + " }\n" 1.147 + " }\n" 1.148 + " var year = date.getFullYear();\n" 1.149 + " var date1, date2;\n" 1.150 + " date1 = new Date(year, 0, 1, 0, 0, 0);\n" 1.151 + " date2 = new Date(year, 11, 31, 23, 59, 59);\n" 1.152 + " var adjustMonth = false;\n" 1.153 + " for (var i = 0; i < (argc >> 1); i++) {\n" 1.154 + " var tmp = parseInt(arguments[i]);\n" 1.155 + " if (isNaN(tmp)) {\n" 1.156 + " var mon = getMonth(arguments[i]);\n" 1.157 + " date1.setMonth(mon);\n" 1.158 + " } else if (tmp < 32) {\n" 1.159 + " adjustMonth = (argc <= 2);\n" 1.160 + " date1.setDate(tmp);\n" 1.161 + " } else {\n" 1.162 + " date1.setFullYear(tmp);\n" 1.163 + " }\n" 1.164 + " }\n" 1.165 + " for (var i = (argc >> 1); i < argc; i++) {\n" 1.166 + " var tmp = parseInt(arguments[i]);\n" 1.167 + " if (isNaN(tmp)) {\n" 1.168 + " var mon = getMonth(arguments[i]);\n" 1.169 + " date2.setMonth(mon);\n" 1.170 + " } else if (tmp < 32) {\n" 1.171 + " date2.setDate(tmp);\n" 1.172 + " } else {\n" 1.173 + " date2.setFullYear(tmp);\n" 1.174 + " }\n" 1.175 + " }\n" 1.176 + " if (adjustMonth) {\n" 1.177 + " date1.setMonth(date.getMonth());\n" 1.178 + " date2.setMonth(date.getMonth());\n" 1.179 + " }\n" 1.180 + " if (isGMT) {\n" 1.181 + " var tmp = date;\n" 1.182 + " tmp.setFullYear(date.getUTCFullYear());\n" 1.183 + " tmp.setMonth(date.getUTCMonth());\n" 1.184 + " tmp.setDate(date.getUTCDate());\n" 1.185 + " tmp.setHours(date.getUTCHours());\n" 1.186 + " tmp.setMinutes(date.getUTCMinutes());\n" 1.187 + " tmp.setSeconds(date.getUTCSeconds());\n" 1.188 + " date = tmp;\n" 1.189 + " }\n" 1.190 + " return ((date1 <= date) && (date <= date2));\n" 1.191 + "}\n" 1.192 + "" 1.193 + "function timeRange() {\n" 1.194 + " var argc = arguments.length;\n" 1.195 + " var date = new Date();\n" 1.196 + " var isGMT= false;\n" 1.197 + "" 1.198 + " if (argc < 1) {\n" 1.199 + " return false;\n" 1.200 + " }\n" 1.201 + " if (arguments[argc - 1] == 'GMT') {\n" 1.202 + " isGMT = true;\n" 1.203 + " argc--;\n" 1.204 + " }\n" 1.205 + "\n" 1.206 + " var hour = isGMT ? date.getUTCHours() : date.getHours();\n" 1.207 + " var date1, date2;\n" 1.208 + " date1 = new Date();\n" 1.209 + " date2 = new Date();\n" 1.210 + "\n" 1.211 + " if (argc == 1) {\n" 1.212 + " return (hour == arguments[0]);\n" 1.213 + " } else if (argc == 2) {\n" 1.214 + " return ((arguments[0] <= hour) && (hour <= arguments[1]));\n" 1.215 + " } else {\n" 1.216 + " switch (argc) {\n" 1.217 + " case 6:\n" 1.218 + " date1.setSeconds(arguments[2]);\n" 1.219 + " date2.setSeconds(arguments[5]);\n" 1.220 + " case 4:\n" 1.221 + " var middle = argc >> 1;\n" 1.222 + " date1.setHours(arguments[0]);\n" 1.223 + " date1.setMinutes(arguments[1]);\n" 1.224 + " date2.setHours(arguments[middle]);\n" 1.225 + " date2.setMinutes(arguments[middle + 1]);\n" 1.226 + " if (middle == 2) {\n" 1.227 + " date2.setSeconds(59);\n" 1.228 + " }\n" 1.229 + " break;\n" 1.230 + " default:\n" 1.231 + " throw 'timeRange: bad number of arguments'\n" 1.232 + " }\n" 1.233 + " }\n" 1.234 + "\n" 1.235 + " if (isGMT) {\n" 1.236 + " date.setFullYear(date.getUTCFullYear());\n" 1.237 + " date.setMonth(date.getUTCMonth());\n" 1.238 + " date.setDate(date.getUTCDate());\n" 1.239 + " date.setHours(date.getUTCHours());\n" 1.240 + " date.setMinutes(date.getUTCMinutes());\n" 1.241 + " date.setSeconds(date.getUTCSeconds());\n" 1.242 + " }\n" 1.243 + " return ((date1 <= date) && (date <= date2));\n" 1.244 + "}\n" 1.245 + ""; 1.246 + 1.247 +// sRunning is defined for the helper functions only while the 1.248 +// Javascript engine is running and the PAC object cannot be deleted 1.249 +// or reset. 1.250 +static ProxyAutoConfig *sRunning = nullptr; 1.251 + 1.252 +// The PACResolver is used for dnsResolve() 1.253 +class PACResolver MOZ_FINAL : public nsIDNSListener 1.254 + , public nsITimerCallback 1.255 +{ 1.256 +public: 1.257 + NS_DECL_THREADSAFE_ISUPPORTS 1.258 + 1.259 + PACResolver() 1.260 + : mStatus(NS_ERROR_FAILURE) 1.261 + { 1.262 + } 1.263 + 1.264 + // nsIDNSListener 1.265 + NS_IMETHODIMP OnLookupComplete(nsICancelable *request, 1.266 + nsIDNSRecord *record, 1.267 + nsresult status) 1.268 + { 1.269 + if (mTimer) { 1.270 + mTimer->Cancel(); 1.271 + mTimer = nullptr; 1.272 + } 1.273 + 1.274 + mRequest = nullptr; 1.275 + mStatus = status; 1.276 + mResponse = record; 1.277 + return NS_OK; 1.278 + } 1.279 + 1.280 + // nsITimerCallback 1.281 + NS_IMETHODIMP Notify(nsITimer *timer) 1.282 + { 1.283 + if (mRequest) 1.284 + mRequest->Cancel(NS_ERROR_NET_TIMEOUT); 1.285 + mTimer = nullptr; 1.286 + return NS_OK; 1.287 + } 1.288 + 1.289 + nsresult mStatus; 1.290 + nsCOMPtr<nsICancelable> mRequest; 1.291 + nsCOMPtr<nsIDNSRecord> mResponse; 1.292 + nsCOMPtr<nsITimer> mTimer; 1.293 +}; 1.294 +NS_IMPL_ISUPPORTS(PACResolver, nsIDNSListener, nsITimerCallback) 1.295 + 1.296 +static 1.297 +void PACLogToConsole(nsString &aMessage) 1.298 +{ 1.299 + nsCOMPtr<nsIConsoleService> consoleService = 1.300 + do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.301 + if (!consoleService) 1.302 + return; 1.303 + 1.304 + consoleService->LogStringMessage(aMessage.get()); 1.305 +} 1.306 + 1.307 +// Javascript errors are logged to the main error console 1.308 +static void 1.309 +PACErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) 1.310 +{ 1.311 + nsString formattedMessage(NS_LITERAL_STRING("PAC Execution Error: ")); 1.312 + formattedMessage += report->ucmessage; 1.313 + formattedMessage += NS_LITERAL_STRING(" ["); 1.314 + formattedMessage += report->uclinebuf; 1.315 + formattedMessage += NS_LITERAL_STRING("]"); 1.316 + PACLogToConsole(formattedMessage); 1.317 +} 1.318 + 1.319 +// timeout of 0 means the normal necko timeout strategy, otherwise the dns request 1.320 +// will be canceled after aTimeout milliseconds 1.321 +static 1.322 +bool PACResolve(const nsCString &aHostName, NetAddr *aNetAddr, 1.323 + unsigned int aTimeout) 1.324 +{ 1.325 + if (!sRunning) { 1.326 + NS_WARNING("PACResolve without a running ProxyAutoConfig object"); 1.327 + return false; 1.328 + } 1.329 + 1.330 + return sRunning->ResolveAddress(aHostName, aNetAddr, aTimeout); 1.331 +} 1.332 + 1.333 +ProxyAutoConfig::ProxyAutoConfig() 1.334 + : mJSRuntime(nullptr) 1.335 + , mJSNeedsSetup(false) 1.336 + , mShutdown(false) 1.337 +{ 1.338 + MOZ_COUNT_CTOR(ProxyAutoConfig); 1.339 +} 1.340 + 1.341 +bool 1.342 +ProxyAutoConfig::ResolveAddress(const nsCString &aHostName, 1.343 + NetAddr *aNetAddr, 1.344 + unsigned int aTimeout) 1.345 +{ 1.346 + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); 1.347 + if (!dns) 1.348 + return false; 1.349 + 1.350 + nsRefPtr<PACResolver> helper = new PACResolver(); 1.351 + 1.352 + if (NS_FAILED(dns->AsyncResolve(aHostName, 1.353 + nsIDNSService::RESOLVE_PRIORITY_MEDIUM, 1.354 + helper, 1.355 + NS_GetCurrentThread(), 1.356 + getter_AddRefs(helper->mRequest)))) 1.357 + return false; 1.358 + 1.359 + if (aTimeout && helper->mRequest) { 1.360 + if (!mTimer) 1.361 + mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.362 + if (mTimer) { 1.363 + mTimer->InitWithCallback(helper, aTimeout, nsITimer::TYPE_ONE_SHOT); 1.364 + helper->mTimer = mTimer; 1.365 + } 1.366 + } 1.367 + 1.368 + // Spin the event loop of the pac thread until lookup is complete. 1.369 + // nsPACman is responsible for keeping a queue and only allowing 1.370 + // one PAC execution at a time even when it is called re-entrantly. 1.371 + while (helper->mRequest) 1.372 + NS_ProcessNextEvent(NS_GetCurrentThread()); 1.373 + 1.374 + if (NS_FAILED(helper->mStatus) || 1.375 + NS_FAILED(helper->mResponse->GetNextAddr(0, aNetAddr))) 1.376 + return false; 1.377 + return true; 1.378 +} 1.379 + 1.380 +static 1.381 +bool PACResolveToString(const nsCString &aHostName, 1.382 + nsCString &aDottedDecimal, 1.383 + unsigned int aTimeout) 1.384 +{ 1.385 + NetAddr netAddr; 1.386 + if (!PACResolve(aHostName, &netAddr, aTimeout)) 1.387 + return false; 1.388 + 1.389 + char dottedDecimal[128]; 1.390 + if (!NetAddrToString(&netAddr, dottedDecimal, sizeof(dottedDecimal))) 1.391 + return false; 1.392 + 1.393 + aDottedDecimal.Assign(dottedDecimal); 1.394 + return true; 1.395 +} 1.396 + 1.397 +// dnsResolve(host) javascript implementation 1.398 +static 1.399 +bool PACDnsResolve(JSContext *cx, unsigned int argc, JS::Value *vp) 1.400 +{ 1.401 + JS::CallArgs args = CallArgsFromVp(argc, vp); 1.402 + 1.403 + if (NS_IsMainThread()) { 1.404 + NS_WARNING("DNS Resolution From PAC on Main Thread. How did that happen?"); 1.405 + return false; 1.406 + } 1.407 + 1.408 + JS::Rooted<JSString*> arg1(cx); 1.409 + if (!JS_ConvertArguments(cx, args, "S", arg1.address())) 1.410 + return false; 1.411 + 1.412 + nsDependentJSString hostName; 1.413 + nsAutoCString dottedDecimal; 1.414 + 1.415 + if (!hostName.init(cx, arg1)) 1.416 + return false; 1.417 + if (PACResolveToString(NS_ConvertUTF16toUTF8(hostName), dottedDecimal, 0)) { 1.418 + JSString *dottedDecimalString = JS_NewStringCopyZ(cx, dottedDecimal.get()); 1.419 + if (!dottedDecimalString) { 1.420 + return false; 1.421 + } 1.422 + 1.423 + args.rval().setString(dottedDecimalString); 1.424 + } 1.425 + else { 1.426 + args.rval().setNull(); 1.427 + } 1.428 + 1.429 + return true; 1.430 +} 1.431 + 1.432 +// myIpAddress() javascript implementation 1.433 +static 1.434 +bool PACMyIpAddress(JSContext *cx, unsigned int argc, JS::Value *vp) 1.435 +{ 1.436 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.437 + 1.438 + if (NS_IsMainThread()) { 1.439 + NS_WARNING("DNS Resolution From PAC on Main Thread. How did that happen?"); 1.440 + return false; 1.441 + } 1.442 + 1.443 + if (!sRunning) { 1.444 + NS_WARNING("PAC myIPAddress without a running ProxyAutoConfig object"); 1.445 + return false; 1.446 + } 1.447 + 1.448 + return sRunning->MyIPAddress(args); 1.449 +} 1.450 + 1.451 +// proxyAlert(msg) javascript implementation 1.452 +static 1.453 +bool PACProxyAlert(JSContext *cx, unsigned int argc, JS::Value *vp) 1.454 +{ 1.455 + JS::CallArgs args = CallArgsFromVp(argc, vp); 1.456 + 1.457 + JS::Rooted<JSString*> arg1(cx); 1.458 + if (!JS_ConvertArguments(cx, args, "S", arg1.address())) 1.459 + return false; 1.460 + 1.461 + nsDependentJSString message; 1.462 + if (!message.init(cx, arg1)) 1.463 + return false; 1.464 + 1.465 + nsString alertMessage; 1.466 + alertMessage.SetCapacity(32 + message.Length()); 1.467 + alertMessage += NS_LITERAL_STRING("PAC-alert: "); 1.468 + alertMessage += message; 1.469 + PACLogToConsole(alertMessage); 1.470 + 1.471 + args.rval().setUndefined(); /* return undefined */ 1.472 + return true; 1.473 +} 1.474 + 1.475 +static const JSFunctionSpec PACGlobalFunctions[] = { 1.476 + JS_FS("dnsResolve", PACDnsResolve, 1, 0), 1.477 + JS_FS("myIpAddress", PACMyIpAddress, 0, 0), 1.478 + JS_FS("alert", PACProxyAlert, 1, 0), 1.479 + JS_FS_END 1.480 +}; 1.481 + 1.482 +// JSRuntimeWrapper is a c++ object that manages the runtime and context 1.483 +// for the JS engine used on the PAC thread. It is initialized and destroyed 1.484 +// on the PAC thread. 1.485 +class JSRuntimeWrapper 1.486 +{ 1.487 + public: 1.488 + static JSRuntimeWrapper *Create() 1.489 + { 1.490 + JSRuntimeWrapper *entry = new JSRuntimeWrapper(); 1.491 + 1.492 + if (NS_FAILED(entry->Init())) { 1.493 + delete entry; 1.494 + return nullptr; 1.495 + } 1.496 + 1.497 + return entry; 1.498 + } 1.499 + 1.500 + JSContext *Context() const 1.501 + { 1.502 + return mContext; 1.503 + } 1.504 + 1.505 + JSObject *Global() const 1.506 + { 1.507 + return mGlobal; 1.508 + } 1.509 + 1.510 + ~JSRuntimeWrapper() 1.511 + { 1.512 + MOZ_COUNT_DTOR(JSRuntimeWrapper); 1.513 + if (mContext) { 1.514 + JS_DestroyContext(mContext); 1.515 + } 1.516 + 1.517 + if (mRuntime) { 1.518 + JS_DestroyRuntime(mRuntime); 1.519 + } 1.520 + } 1.521 + 1.522 + void SetOK() 1.523 + { 1.524 + mOK = true; 1.525 + } 1.526 + 1.527 + bool IsOK() 1.528 + { 1.529 + return mOK; 1.530 + } 1.531 + 1.532 +private: 1.533 + static const unsigned sRuntimeHeapSize = 2 << 20; 1.534 + 1.535 + JSRuntime *mRuntime; 1.536 + JSContext *mContext; 1.537 + JSObject *mGlobal; 1.538 + bool mOK; 1.539 + 1.540 + static const JSClass sGlobalClass; 1.541 + 1.542 + JSRuntimeWrapper() 1.543 + : mRuntime(nullptr), mContext(nullptr), mGlobal(nullptr), mOK(false) 1.544 + { 1.545 + MOZ_COUNT_CTOR(JSRuntimeWrapper); 1.546 + } 1.547 + 1.548 + nsresult Init() 1.549 + { 1.550 + mRuntime = JS_NewRuntime(sRuntimeHeapSize, JS_NO_HELPER_THREADS); 1.551 + NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY); 1.552 + 1.553 + /* 1.554 + * Not setting this will cause JS_CHECK_RECURSION to report false 1.555 + * positives 1.556 + */ 1.557 + JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024); 1.558 + 1.559 + mContext = JS_NewContext(mRuntime, 0); 1.560 + NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY); 1.561 + 1.562 + JSAutoRequest ar(mContext); 1.563 + 1.564 + JS::CompartmentOptions options; 1.565 + options.setZone(JS::SystemZone) 1.566 + .setVersion(JSVERSION_LATEST); 1.567 + mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr, 1.568 + JS::DontFireOnNewGlobalHook, options); 1.569 + NS_ENSURE_TRUE(mGlobal, NS_ERROR_OUT_OF_MEMORY); 1.570 + JS::Rooted<JSObject*> global(mContext, mGlobal); 1.571 + 1.572 + JSAutoCompartment ac(mContext, global); 1.573 + js::SetDefaultObjectForContext(mContext, global); 1.574 + JS_InitStandardClasses(mContext, global); 1.575 + 1.576 + JS_SetErrorReporter(mContext, PACErrorReporter); 1.577 + 1.578 + if (!JS_DefineFunctions(mContext, global, PACGlobalFunctions)) 1.579 + return NS_ERROR_FAILURE; 1.580 + 1.581 + JS_FireOnNewGlobalObject(mContext, global); 1.582 + 1.583 + return NS_OK; 1.584 + } 1.585 +}; 1.586 + 1.587 +const JSClass JSRuntimeWrapper::sGlobalClass = { 1.588 + "PACResolutionThreadGlobal", 1.589 + JSCLASS_GLOBAL_FLAGS, 1.590 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.591 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, 1.592 + nullptr, nullptr, nullptr, nullptr, 1.593 + JS_GlobalObjectTraceHook 1.594 +}; 1.595 + 1.596 +nsresult 1.597 +ProxyAutoConfig::Init(const nsCString &aPACURI, 1.598 + const nsCString &aPACScript) 1.599 +{ 1.600 + mPACURI = aPACURI; 1.601 + mPACScript = sPacUtils; 1.602 + mPACScript.Append(aPACScript); 1.603 + 1.604 + if (!sRunning) 1.605 + return SetupJS(); 1.606 + 1.607 + mJSNeedsSetup = true; 1.608 + return NS_OK; 1.609 +} 1.610 + 1.611 +nsresult 1.612 +ProxyAutoConfig::SetupJS() 1.613 +{ 1.614 + mJSNeedsSetup = false; 1.615 + NS_ABORT_IF_FALSE(!sRunning, "JIT is running"); 1.616 + 1.617 + delete mJSRuntime; 1.618 + mJSRuntime = nullptr; 1.619 + 1.620 + if (mPACScript.IsEmpty()) 1.621 + return NS_ERROR_FAILURE; 1.622 + 1.623 + mJSRuntime = JSRuntimeWrapper::Create(); 1.624 + if (!mJSRuntime) 1.625 + return NS_ERROR_FAILURE; 1.626 + 1.627 + JSContext* cx = mJSRuntime->Context(); 1.628 + JSAutoRequest ar(cx); 1.629 + JSAutoCompartment ac(cx, mJSRuntime->Global()); 1.630 + 1.631 + // check if this is a data: uri so that we don't spam the js console with 1.632 + // huge meaningless strings. this is not on the main thread, so it can't 1.633 + // use nsIRUI scheme methods 1.634 + bool isDataURI = nsDependentCSubstring(mPACURI, 0, 5).LowerCaseEqualsASCII("data:", 5); 1.635 + 1.636 + sRunning = this; 1.637 + JS::Rooted<JSObject*> global(cx, mJSRuntime->Global()); 1.638 + JS::CompileOptions options(cx); 1.639 + options.setFileAndLine(mPACURI.get(), 1); 1.640 + JS::Rooted<JSScript*> script(cx, JS_CompileScript(cx, global, mPACScript.get(), 1.641 + mPACScript.Length(), options)); 1.642 + if (!script || !JS_ExecuteScript(cx, global, script)) { 1.643 + nsString alertMessage(NS_LITERAL_STRING("PAC file failed to install from ")); 1.644 + if (isDataURI) { 1.645 + alertMessage += NS_LITERAL_STRING("data: URI"); 1.646 + } 1.647 + else { 1.648 + alertMessage += NS_ConvertUTF8toUTF16(mPACURI); 1.649 + } 1.650 + PACLogToConsole(alertMessage); 1.651 + sRunning = nullptr; 1.652 + return NS_ERROR_FAILURE; 1.653 + } 1.654 + sRunning = nullptr; 1.655 + 1.656 + mJSRuntime->SetOK(); 1.657 + nsString alertMessage(NS_LITERAL_STRING("PAC file installed from ")); 1.658 + if (isDataURI) { 1.659 + alertMessage += NS_LITERAL_STRING("data: URI"); 1.660 + } 1.661 + else { 1.662 + alertMessage += NS_ConvertUTF8toUTF16(mPACURI); 1.663 + } 1.664 + PACLogToConsole(alertMessage); 1.665 + 1.666 + // we don't need these now 1.667 + mPACScript.Truncate(); 1.668 + mPACURI.Truncate(); 1.669 + 1.670 + return NS_OK; 1.671 +} 1.672 + 1.673 +nsresult 1.674 +ProxyAutoConfig::GetProxyForURI(const nsCString &aTestURI, 1.675 + const nsCString &aTestHost, 1.676 + nsACString &result) 1.677 +{ 1.678 + if (mJSNeedsSetup) 1.679 + SetupJS(); 1.680 + 1.681 + if (!mJSRuntime || !mJSRuntime->IsOK()) 1.682 + return NS_ERROR_NOT_AVAILABLE; 1.683 + 1.684 + JSContext *cx = mJSRuntime->Context(); 1.685 + JSAutoRequest ar(cx); 1.686 + JSAutoCompartment ac(cx, mJSRuntime->Global()); 1.687 + 1.688 + // the sRunning flag keeps a new PAC file from being installed 1.689 + // while the event loop is spinning on a DNS function. Don't early return. 1.690 + sRunning = this; 1.691 + mRunningHost = aTestHost; 1.692 + 1.693 + nsresult rv = NS_ERROR_FAILURE; 1.694 + JS::RootedString uriString(cx, JS_NewStringCopyZ(cx, aTestURI.get())); 1.695 + JS::RootedString hostString(cx, JS_NewStringCopyZ(cx, aTestHost.get())); 1.696 + 1.697 + if (uriString && hostString) { 1.698 + JS::AutoValueArray<2> args(cx); 1.699 + args[0].setString(uriString); 1.700 + args[1].setString(hostString); 1.701 + 1.702 + JS::Rooted<JS::Value> rval(cx); 1.703 + JS::Rooted<JSObject*> global(cx, mJSRuntime->Global()); 1.704 + bool ok = JS_CallFunctionName(cx, global, "FindProxyForURL", args, &rval); 1.705 + 1.706 + if (ok && rval.isString()) { 1.707 + nsDependentJSString pacString; 1.708 + if (pacString.init(cx, rval.toString())) { 1.709 + CopyUTF16toUTF8(pacString, result); 1.710 + rv = NS_OK; 1.711 + } 1.712 + } 1.713 + } 1.714 + 1.715 + mRunningHost.Truncate(); 1.716 + sRunning = nullptr; 1.717 + return rv; 1.718 +} 1.719 + 1.720 +void 1.721 +ProxyAutoConfig::GC() 1.722 +{ 1.723 + if (!mJSRuntime || !mJSRuntime->IsOK()) 1.724 + return; 1.725 + 1.726 + JSAutoCompartment ac(mJSRuntime->Context(), mJSRuntime->Global()); 1.727 + JS_MaybeGC(mJSRuntime->Context()); 1.728 +} 1.729 + 1.730 +ProxyAutoConfig::~ProxyAutoConfig() 1.731 +{ 1.732 + MOZ_COUNT_DTOR(ProxyAutoConfig); 1.733 + NS_ASSERTION(!mJSRuntime, 1.734 + "~ProxyAutoConfig leaking JS runtime that " 1.735 + "should have been deleted on pac thread"); 1.736 +} 1.737 + 1.738 +void 1.739 +ProxyAutoConfig::Shutdown() 1.740 +{ 1.741 + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread for shutdown"); 1.742 + 1.743 + if (sRunning || mShutdown) 1.744 + return; 1.745 + 1.746 + mShutdown = true; 1.747 + delete mJSRuntime; 1.748 + mJSRuntime = nullptr; 1.749 +} 1.750 + 1.751 +bool 1.752 +ProxyAutoConfig::SrcAddress(const NetAddr *remoteAddress, nsCString &localAddress) 1.753 +{ 1.754 + PRFileDesc *fd; 1.755 + fd = PR_OpenUDPSocket(remoteAddress->raw.family); 1.756 + if (!fd) 1.757 + return false; 1.758 + 1.759 + PRNetAddr prRemoteAddress; 1.760 + NetAddrToPRNetAddr(remoteAddress, &prRemoteAddress); 1.761 + if (PR_Connect(fd, &prRemoteAddress, 0) != PR_SUCCESS) { 1.762 + PR_Close(fd); 1.763 + return false; 1.764 + } 1.765 + 1.766 + PRNetAddr localName; 1.767 + if (PR_GetSockName(fd, &localName) != PR_SUCCESS) { 1.768 + PR_Close(fd); 1.769 + return false; 1.770 + } 1.771 + 1.772 + PR_Close(fd); 1.773 + 1.774 + char dottedDecimal[128]; 1.775 + if (PR_NetAddrToString(&localName, dottedDecimal, sizeof(dottedDecimal)) != PR_SUCCESS) 1.776 + return false; 1.777 + 1.778 + localAddress.Assign(dottedDecimal); 1.779 + 1.780 + return true; 1.781 +} 1.782 + 1.783 +// hostName is run through a dns lookup and then a udp socket is connected 1.784 +// to the result. If that all works, the local IP address of the socket is 1.785 +// returned to the javascript caller and |*aResult| is set to true. Otherwise 1.786 +// |*aResult| is set to false. 1.787 +bool 1.788 +ProxyAutoConfig::MyIPAddressTryHost(const nsCString &hostName, 1.789 + unsigned int timeout, 1.790 + const JS::CallArgs &aArgs, 1.791 + bool* aResult) 1.792 +{ 1.793 + *aResult = false; 1.794 + 1.795 + NetAddr remoteAddress; 1.796 + nsAutoCString localDottedDecimal; 1.797 + JSContext *cx = mJSRuntime->Context(); 1.798 + 1.799 + if (PACResolve(hostName, &remoteAddress, timeout) && 1.800 + SrcAddress(&remoteAddress, localDottedDecimal)) { 1.801 + JSString *dottedDecimalString = 1.802 + JS_NewStringCopyZ(cx, localDottedDecimal.get()); 1.803 + if (!dottedDecimalString) { 1.804 + return false; 1.805 + } 1.806 + 1.807 + *aResult = true; 1.808 + aArgs.rval().setString(dottedDecimalString); 1.809 + } 1.810 + return true; 1.811 +} 1.812 + 1.813 +bool 1.814 +ProxyAutoConfig::MyIPAddress(const JS::CallArgs &aArgs) 1.815 +{ 1.816 + nsAutoCString remoteDottedDecimal; 1.817 + nsAutoCString localDottedDecimal; 1.818 + JSContext *cx = mJSRuntime->Context(); 1.819 + 1.820 + // first, lookup the local address of a socket connected 1.821 + // to the host of uri being resolved by the pac file. This is 1.822 + // v6 safe.. but is the last step like that 1.823 + bool rvalAssigned = false; 1.824 + if (!MyIPAddressTryHost(mRunningHost, kTimeout, aArgs, &rvalAssigned) || 1.825 + rvalAssigned) { 1.826 + return rvalAssigned; 1.827 + } 1.828 + 1.829 + // next, look for a route to a public internet address that doesn't need DNS. 1.830 + // This is the google anycast dns address, but it doesn't matter if it 1.831 + // remains operable (as we don't contact it) as long as the address stays 1.832 + // in commonly routed IP address space. 1.833 + remoteDottedDecimal.AssignLiteral("8.8.8.8"); 1.834 + if (!MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs, &rvalAssigned) || 1.835 + rvalAssigned) { 1.836 + return rvalAssigned; 1.837 + } 1.838 + 1.839 + // next, use the old algorithm based on the local hostname 1.840 + nsAutoCString hostName; 1.841 + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); 1.842 + if (dns && NS_SUCCEEDED(dns->GetMyHostName(hostName)) && 1.843 + PACResolveToString(hostName, localDottedDecimal, kTimeout)) { 1.844 + JSString *dottedDecimalString = 1.845 + JS_NewStringCopyZ(cx, localDottedDecimal.get()); 1.846 + if (!dottedDecimalString) { 1.847 + return false; 1.848 + } 1.849 + 1.850 + aArgs.rval().setString(dottedDecimalString); 1.851 + return true; 1.852 + } 1.853 + 1.854 + // next try a couple RFC 1918 variants.. maybe there is a 1.855 + // local route 1.856 + remoteDottedDecimal.AssignLiteral("192.168.0.1"); 1.857 + if (!MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs, &rvalAssigned) || 1.858 + rvalAssigned) { 1.859 + return rvalAssigned; 1.860 + } 1.861 + 1.862 + // more RFC 1918 1.863 + remoteDottedDecimal.AssignLiteral("10.0.0.1"); 1.864 + if (!MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs, &rvalAssigned) || 1.865 + rvalAssigned) { 1.866 + return rvalAssigned; 1.867 + } 1.868 + 1.869 + // who knows? let's fallback to localhost 1.870 + localDottedDecimal.AssignLiteral("127.0.0.1"); 1.871 + JSString *dottedDecimalString = 1.872 + JS_NewStringCopyZ(cx, localDottedDecimal.get()); 1.873 + if (!dottedDecimalString) { 1.874 + return false; 1.875 + } 1.876 + 1.877 + aArgs.rval().setString(dottedDecimalString); 1.878 + return true; 1.879 +} 1.880 + 1.881 +} // namespace mozilla 1.882 +} // namespace mozilla::net