netwerk/base/src/ProxyAutoConfig.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial