LCOV - code coverage report
Current view: top level - netwerk/base - ProxyAutoConfig.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 7 378 1.9 %
Date: 2017-07-14 16:53:18 Functions: 2 41 4.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13