tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsProtocolProxyService.cpp (81218B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=4 sw=2 sts=2 et: */
      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 "SimpleChannel.h"
      8 #include "mozilla/AutoRestore.h"
      9 
     10 #include "nsProtocolProxyService.h"
     11 #include "nsProxyInfo.h"
     12 #include "nsIClassInfoImpl.h"
     13 #include "nsIIOService.h"
     14 #include "nsIObserverService.h"
     15 #include "nsIProtocolHandler.h"
     16 #include "nsIProtocolProxyCallback.h"
     17 #include "nsIChannel.h"
     18 #include "nsICancelable.h"
     19 #include "nsDNSService2.h"
     20 #include "nsPIDNSService.h"
     21 #include "nsIPrefBranch.h"
     22 #include "nsIPrefService.h"
     23 #include "nsContentUtils.h"
     24 #include "nsCRT.h"
     25 #include "nsThreadUtils.h"
     26 #include "nsQueryObject.h"
     27 #include "nsSOCKSIOLayer.h"
     28 #include "nsString.h"
     29 #include "nsNetUtil.h"
     30 #include "nsNetCID.h"
     31 #include "prnetdb.h"
     32 #include "nsPACMan.h"
     33 #include "nsProxyRelease.h"
     34 #include "mozilla/Mutex.h"
     35 #include "mozilla/CondVar.h"
     36 #include "nsISystemProxySettings.h"
     37 #include "nsINetworkLinkService.h"
     38 #include "nsIHttpChannelInternal.h"
     39 #include "mozilla/dom/nsMixedContentBlocker.h"
     40 #include "mozilla/Logging.h"
     41 #include "mozilla/ScopeExit.h"
     42 #include "mozilla/StaticPrefs_network.h"
     43 #include "mozilla/Tokenizer.h"
     44 
     45 //----------------------------------------------------------------------------
     46 
     47 namespace mozilla {
     48 namespace net {
     49 
     50 extern const char kProxyType_HTTP[];
     51 extern const char kProxyType_HTTPS[];
     52 extern const char kProxyType_SOCKS[];
     53 extern const char kProxyType_SOCKS4[];
     54 extern const char kProxyType_SOCKS5[];
     55 extern const char kProxyType_DIRECT[];
     56 extern const char kProxyType_PROXY[];
     57 extern const char kProxyType_MASQUE[];
     58 
     59 #undef LOG
     60 #define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
     61 
     62 //----------------------------------------------------------------------------
     63 
     64 #define PROXY_PREF_BRANCH "network.proxy"
     65 #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
     66 
     67 //----------------------------------------------------------------------------
     68 
     69 // This structure is intended to be allocated on the stack
     70 struct nsProtocolInfo {
     71  nsAutoCString scheme;
     72  uint32_t flags = 0;
     73  int32_t defaultPort = 0;
     74 };
     75 
     76 //----------------------------------------------------------------------------
     77 
     78 // Return the channel's proxy URI, or if it doesn't exist, the
     79 // channel's main URI.
     80 static nsresult GetProxyURI(nsIChannel* channel, nsIURI** aOut) {
     81  nsresult rv = NS_OK;
     82  nsCOMPtr<nsIURI> proxyURI;
     83  nsCOMPtr<nsIHttpChannelInternal> httpChannel(do_QueryInterface(channel));
     84  if (httpChannel) {
     85    rv = httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
     86  }
     87  if (!proxyURI) {
     88    rv = channel->GetURI(getter_AddRefs(proxyURI));
     89  }
     90  if (NS_FAILED(rv)) {
     91    return rv;
     92  }
     93  proxyURI.forget(aOut);
     94  return NS_OK;
     95 }
     96 
     97 //-----------------------------------------------------------------------------
     98 
     99 nsProtocolProxyService::FilterLink::FilterLink(uint32_t p,
    100                                               nsIProtocolProxyFilter* f)
    101    : position(p), filter(f), channelFilter(nullptr) {
    102  LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, filter=%p", this,
    103       f));
    104 }
    105 nsProtocolProxyService::FilterLink::FilterLink(
    106    uint32_t p, nsIProtocolProxyChannelFilter* cf)
    107    : position(p), filter(nullptr), channelFilter(cf) {
    108  LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, channel-filter=%p",
    109       this, cf));
    110 }
    111 
    112 nsProtocolProxyService::FilterLink::~FilterLink() {
    113  LOG(("nsProtocolProxyService::FilterLink::~FilterLink %p", this));
    114 }
    115 
    116 //-----------------------------------------------------------------------------
    117 
    118 // Calls onProxyAvailable after making sure we don't pass a nsIProxyInfo
    119 // for loopback URLs or URLs that should not use a proxy.
    120 // static
    121 void nsProtocolProxyService::CallOnProxyAvailableCallback(
    122    nsProtocolProxyService* aService, nsIProtocolProxyCallback* aCallback,
    123    nsICancelable* aRequest, nsIChannel* aChannel, nsIProxyInfo* aProxyInfo,
    124    nsresult aStatus) {
    125  nsresult rv;
    126  nsCOMPtr<nsIURI> channelURI;
    127  if (aChannel) {
    128    aChannel->GetURI(getter_AddRefs(channelURI));
    129  }
    130 
    131  // This check makes sure that we don't accidentally proxy loopback URLs if
    132  // one of the proxy filters allows it.
    133  if (aProxyInfo && channelURI) {
    134    nsProtocolInfo info;
    135    rv = aService->GetProtocolInfo(channelURI, &info);
    136 
    137    if (NS_SUCCEEDED(rv) &&
    138        !aService->CanUseProxy(channelURI, info.defaultPort)) {
    139      aProxyInfo = nullptr;
    140    }
    141  }
    142 
    143  aCallback->OnProxyAvailable(aRequest, aChannel, aProxyInfo, aStatus);
    144 }
    145 
    146 // The nsPACManCallback portion of this implementation should be run
    147 // on the main thread - so call nsPACMan::AsyncGetProxyForURI() with
    148 // a true mainThreadResponse parameter.
    149 class nsAsyncResolveRequest final : public nsIRunnable,
    150                                    public nsPACManCallback,
    151                                    public nsICancelable {
    152 public:
    153  NS_DECL_THREADSAFE_ISUPPORTS
    154 
    155  nsAsyncResolveRequest(nsProtocolProxyService* pps, nsIChannel* channel,
    156                        uint32_t aResolveFlags,
    157                        nsIProtocolProxyCallback* callback)
    158      : mResolveFlags(aResolveFlags),
    159        mPPS(pps),
    160        mXPComPPS(pps),
    161        mChannel(channel),
    162        mCallback(callback) {
    163    NS_ASSERTION(mCallback, "null callback");
    164  }
    165 
    166 private:
    167  ~nsAsyncResolveRequest() {
    168    if (!NS_IsMainThread()) {
    169      // these xpcom pointers might need to be proxied back to the
    170      // main thread to delete safely, but if this request had its
    171      // callbacks called normally they will all be null and this is a nop
    172 
    173      if (mChannel) {
    174        NS_ReleaseOnMainThread("nsAsyncResolveRequest::mChannel",
    175                               mChannel.forget());
    176      }
    177 
    178      if (mCallback) {
    179        NS_ReleaseOnMainThread("nsAsyncResolveRequest::mCallback",
    180                               mCallback.forget());
    181      }
    182 
    183      if (mProxyInfo) {
    184        NS_ReleaseOnMainThread("nsAsyncResolveRequest::mProxyInfo",
    185                               mProxyInfo.forget());
    186      }
    187 
    188      if (mXPComPPS) {
    189        NS_ReleaseOnMainThread("nsAsyncResolveRequest::mXPComPPS",
    190                               mXPComPPS.forget());
    191      }
    192    }
    193  }
    194 
    195  // Helper class to loop over all registered asynchronous filters.
    196  // There is a cycle between nsAsyncResolveRequest and this class that
    197  // is broken after the last filter has called back on this object.
    198  class AsyncApplyFilters final : public nsIProxyProtocolFilterResult,
    199                                  public nsIRunnable,
    200                                  public nsICancelable {
    201    // The reference counter is thread-safe, but the processing logic is
    202    // considered single thread only.  We want the counter be thread safe,
    203    // since this class can be released on a background thread.
    204    NS_DECL_THREADSAFE_ISUPPORTS
    205    NS_DECL_NSIPROXYPROTOCOLFILTERRESULT
    206    NS_DECL_NSIRUNNABLE
    207    NS_DECL_NSICANCELABLE
    208 
    209    using Callback =
    210        std::function<nsresult(nsAsyncResolveRequest*, nsIProxyInfo*, bool)>;
    211 
    212    explicit AsyncApplyFilters(nsProtocolInfo& aInfo,
    213                               Callback const& aCallback);
    214    // This method starts the processing or filters.  If all of them
    215    // answer synchronously (call back from within applyFilters) this method
    216    // will return immediately and the returning result will carry return
    217    // result of the callback given in constructor.
    218    // This method is looping the registered filters (that have been copied
    219    // locally) as long as an answer from a filter is obtained synchronously.
    220    // Note that filters are processed serially to let them build a list
    221    // of proxy info.
    222    nsresult AsyncProcess(nsAsyncResolveRequest* aRequest);
    223 
    224   private:
    225    using FilterLink = nsProtocolProxyService::FilterLink;
    226 
    227    virtual ~AsyncApplyFilters();
    228    // Processes the next filter and loops until a filter is successfully
    229    // called on or it has called back to us.
    230    nsresult ProcessNextFilter();
    231    // Called after the last filter has been processed (=called back or failed
    232    // to be called on)
    233    nsresult Finish();
    234 
    235    nsProtocolInfo mInfo;
    236    // This is nullified before we call back on the request or when
    237    // Cancel() on this object has been called to break the cycle
    238    // and signal to stop.
    239    RefPtr<nsAsyncResolveRequest> mRequest;
    240    Callback mCallback;
    241    // A shallow snapshot of filters as they were registered at the moment
    242    // we started to process filters for the given resolve request.
    243    nsTArray<RefPtr<FilterLink>> mFiltersCopy;
    244 
    245    nsTArray<RefPtr<FilterLink>>::index_type mNextFilterIndex;
    246    // true when we are calling ProcessNextFilter() from inside AsyncProcess(),
    247    // false otherwise.
    248    bool mProcessingInLoop;
    249    // true after a filter called back to us with a result, dropped to false
    250    // just before we call a filter.
    251    bool mFilterCalledBack;
    252 
    253    // This keeps the initial value we pass to the first filter in line and also
    254    // collects the result from each filter call.
    255    nsCOMPtr<nsIProxyInfo> mProxyInfo;
    256 
    257    // The logic is written as non-thread safe, assert single-thread usage.
    258    nsCOMPtr<nsISerialEventTarget> mProcessingThread;
    259  };
    260 
    261  void EnsureResolveFlagsMatch() {
    262    nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(mProxyInfo);
    263    if (!pi || pi->ResolveFlags() == mResolveFlags) {
    264      return;
    265    }
    266 
    267    nsCOMPtr<nsIProxyInfo> proxyInfo =
    268        pi->CloneProxyInfoWithNewResolveFlags(mResolveFlags);
    269    mProxyInfo.swap(proxyInfo);
    270  }
    271 
    272 public:
    273  nsresult ProcessLocally(nsProtocolInfo& info, nsIProxyInfo* pi,
    274                          bool isSyncOK) {
    275    LOG(("nsAsyncResolveRequest::ProcessLocally"));
    276    SetResult(NS_OK, pi);
    277 
    278    auto consumeFiltersResult = [isSyncOK](nsAsyncResolveRequest* ctx,
    279                                           nsIProxyInfo* pi,
    280                                           bool aCalledAsync) -> nsresult {
    281      ctx->SetResult(NS_OK, pi);
    282      if (isSyncOK || aCalledAsync) {
    283        ctx->Run();
    284        return NS_OK;
    285      }
    286 
    287      return ctx->DispatchCallback();
    288    };
    289 
    290    mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
    291    // may call consumeFiltersResult() directly
    292    return mAsyncFilterApplier->AsyncProcess(this);
    293  }
    294 
    295  void SetResult(nsresult status, nsIProxyInfo* pi) {
    296    mStatus = status;
    297    mProxyInfo = pi;
    298  }
    299 
    300  NS_IMETHOD Run() override {
    301    if (mCallback) DoCallback();
    302    return NS_OK;
    303  }
    304 
    305  NS_IMETHOD Cancel(nsresult reason) override {
    306    NS_ENSURE_ARG(NS_FAILED(reason));
    307 
    308    if (mAsyncFilterApplier) {
    309      mAsyncFilterApplier->Cancel(reason);
    310    }
    311 
    312    // If we've already called DoCallback then, nothing more to do.
    313    if (!mCallback) return NS_OK;
    314 
    315    SetResult(reason, nullptr);
    316    return DispatchCallback();
    317  }
    318 
    319  nsresult DispatchCallback() {
    320    if (mDispatched) {  // Only need to dispatch once
    321      return NS_OK;
    322    }
    323 
    324    nsresult rv = NS_DispatchToCurrentThread(this);
    325    if (NS_FAILED(rv)) {
    326      NS_WARNING("unable to dispatch callback event");
    327    } else {
    328      mDispatched = true;
    329      return NS_OK;
    330    }
    331 
    332    mCallback = nullptr;  // break possible reference cycle
    333    return rv;
    334  }
    335 
    336 private:
    337  // Called asynchronously, so we do not need to post another PLEvent
    338  // before calling DoCallback.
    339  void OnQueryComplete(nsresult status, const nsACString& pacString,
    340                       const nsACString& newPACURL) override {
    341    // If we've already called DoCallback then, nothing more to do.
    342    if (!mCallback) return;
    343 
    344    // Provided we haven't been canceled...
    345    if (mStatus == NS_OK) {
    346      mStatus = status;
    347      mPACString = pacString;
    348      mPACURL = newPACURL;
    349    }
    350 
    351    // In the cancelation case, we may still have another PLEvent in
    352    // the queue that wants to call DoCallback.  No need to wait for
    353    // it, just run the callback now.
    354    DoCallback();
    355  }
    356 
    357  void DoCallback() {
    358    bool pacAvailable = true;
    359    if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) {
    360      // If the PAC service is not avail (e.g. failed pac load
    361      // or shutdown) then we will be going direct. Make that
    362      // mapping now so that any filters are still applied.
    363      mPACString = "DIRECT;"_ns;
    364      mStatus = NS_OK;
    365 
    366      LOG(("pac not available, use DIRECT\n"));
    367      pacAvailable = false;
    368    }
    369 
    370    if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
    371      // Generate proxy info from the PAC string if appropriate
    372      mPPS->ProcessPACString(mPACString, mResolveFlags,
    373                             getter_AddRefs(mProxyInfo));
    374      nsCOMPtr<nsIURI> proxyURI;
    375      GetProxyURI(mChannel, getter_AddRefs(proxyURI));
    376 
    377      // Now apply proxy filters
    378      nsProtocolInfo info;
    379      mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
    380 
    381      auto consumeFiltersResult = [pacAvailable](nsAsyncResolveRequest* self,
    382                                                 nsIProxyInfo* pi,
    383                                                 bool async) -> nsresult {
    384        LOG(("DoCallback::consumeFiltersResult this=%p, pi=%p, async=%d", self,
    385             pi, async));
    386 
    387        self->mProxyInfo = pi;
    388 
    389        if (pacAvailable) {
    390          // if !pacAvailable, it was already logged above
    391          LOG(("pac thread callback %s\n", self->mPACString.get()));
    392        }
    393 
    394        if (NS_SUCCEEDED(self->mStatus)) {
    395          self->mPPS->MaybeDisableDNSPrefetch(self->mProxyInfo);
    396        }
    397 
    398        self->EnsureResolveFlagsMatch();
    399        nsProtocolProxyService::CallOnProxyAvailableCallback(
    400            self->mPPS, self->mCallback, self, self->mChannel, self->mProxyInfo,
    401            self->mStatus);
    402 
    403        return NS_OK;
    404      };
    405 
    406      if (NS_SUCCEEDED(mStatus)) {
    407        mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
    408        // This may call consumeFiltersResult() directly.
    409        mAsyncFilterApplier->AsyncProcess(this);
    410        return;
    411      }
    412 
    413      consumeFiltersResult(this, nullptr, false);
    414    } else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
    415      LOG(("pac thread callback indicates new pac file load\n"));
    416 
    417      nsCOMPtr<nsIURI> proxyURI;
    418      GetProxyURI(mChannel, getter_AddRefs(proxyURI));
    419 
    420      // trigger load of new pac url
    421      nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
    422      if (NS_SUCCEEDED(rv)) {
    423        // now that the load is triggered, we can resubmit the query
    424        RefPtr<nsAsyncResolveRequest> newRequest =
    425            new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback);
    426        rv = mPPS->mPACMan->AsyncGetProxyForURI(proxyURI, newRequest,
    427                                                mResolveFlags, true);
    428      }
    429 
    430      if (NS_FAILED(rv)) {
    431        nsProtocolProxyService::CallOnProxyAvailableCallback(
    432            mPPS, mCallback, this, mChannel, nullptr, rv);
    433      }
    434 
    435      // do not call onproxyavailable() in SUCCESS case - the newRequest will
    436      // take care of that
    437    } else {
    438      LOG(("pac thread callback did not provide information %" PRIX32 "\n",
    439           static_cast<uint32_t>(mStatus)));
    440      if (NS_SUCCEEDED(mStatus)) mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
    441      EnsureResolveFlagsMatch();
    442      nsProtocolProxyService::CallOnProxyAvailableCallback(
    443          mPPS, mCallback, this, mChannel, mProxyInfo, mStatus);
    444    }
    445 
    446    // We are on the main thread now and don't need these any more so
    447    // release them to avoid having to proxy them back to the main thread
    448    // in the dtor
    449    mCallback = nullptr;  // in case the callback holds an owning ref to us
    450    mPPS = nullptr;
    451    mXPComPPS = nullptr;
    452    mChannel = nullptr;
    453    mProxyInfo = nullptr;
    454  }
    455 
    456 private:
    457  nsresult mStatus{NS_OK};
    458  nsCString mPACString;
    459  nsCString mPACURL;
    460  bool mDispatched{false};
    461  uint32_t mResolveFlags;
    462 
    463  nsProtocolProxyService* mPPS;
    464  nsCOMPtr<nsIProtocolProxyService> mXPComPPS;
    465  nsCOMPtr<nsIChannel> mChannel;
    466  nsCOMPtr<nsIProtocolProxyCallback> mCallback;
    467  nsCOMPtr<nsIProxyInfo> mProxyInfo;
    468 
    469  RefPtr<AsyncApplyFilters> mAsyncFilterApplier;
    470 };
    471 
    472 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
    473 
    474 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest::AsyncApplyFilters,
    475                  nsIProxyProtocolFilterResult, nsICancelable, nsIRunnable)
    476 
    477 nsAsyncResolveRequest::AsyncApplyFilters::AsyncApplyFilters(
    478    nsProtocolInfo& aInfo, Callback const& aCallback)
    479    : mInfo(aInfo),
    480      mCallback(aCallback),
    481      mNextFilterIndex(0),
    482      mProcessingInLoop(false),
    483      mFilterCalledBack(false) {
    484  LOG(("AsyncApplyFilters %p", this));
    485 }
    486 
    487 nsAsyncResolveRequest::AsyncApplyFilters::~AsyncApplyFilters() {
    488  LOG(("~AsyncApplyFilters %p", this));
    489 
    490  MOZ_ASSERT(!mRequest);
    491  MOZ_ASSERT(!mProxyInfo);
    492  MOZ_ASSERT(!mFiltersCopy.Length());
    493 }
    494 
    495 nsresult nsAsyncResolveRequest::AsyncApplyFilters::AsyncProcess(
    496    nsAsyncResolveRequest* aRequest) {
    497  LOG(("AsyncApplyFilters::AsyncProcess %p for req %p", this, aRequest));
    498 
    499  MOZ_ASSERT(!mRequest, "AsyncApplyFilters started more than once!");
    500 
    501  if (!(mInfo.flags & nsIProtocolHandler::ALLOWS_PROXY)) {
    502    // Calling the callback directly (not via Finish()) since we
    503    // don't want to prune.
    504    return mCallback(aRequest, aRequest->mProxyInfo, false);
    505  }
    506 
    507  mProcessingThread = NS_GetCurrentThread();
    508 
    509  mRequest = aRequest;
    510  mProxyInfo = aRequest->mProxyInfo;
    511 
    512  aRequest->mPPS->CopyFilters(mFiltersCopy);
    513 
    514  // We want to give filters a chance to process in a single loop to prevent
    515  // any current-thread dispatch delays when those are not needed.
    516  // This code is rather "loopy" than "recursive" to prevent long stack traces.
    517  do {
    518    MOZ_ASSERT(!mProcessingInLoop);
    519 
    520    mozilla::AutoRestore<bool> restore(mProcessingInLoop);
    521    mProcessingInLoop = true;
    522 
    523    nsresult rv = ProcessNextFilter();
    524    if (NS_FAILED(rv)) {
    525      return rv;
    526    }
    527  } while (mFilterCalledBack);
    528 
    529  return NS_OK;
    530 }
    531 
    532 nsresult nsAsyncResolveRequest::AsyncApplyFilters::ProcessNextFilter() {
    533  LOG(("AsyncApplyFilters::ProcessNextFilter %p ENTER pi=%p", this,
    534       mProxyInfo.get()));
    535 
    536  RefPtr<FilterLink> filter;
    537  do {
    538    mFilterCalledBack = false;
    539 
    540    if (!mRequest) {
    541      // We got canceled
    542      LOG(("  canceled"));
    543      return NS_OK;  // should we let the consumer know?
    544    }
    545 
    546    if (mNextFilterIndex == mFiltersCopy.Length()) {
    547      return Finish();
    548    }
    549 
    550    filter = mFiltersCopy[mNextFilterIndex++];
    551 
    552    // Loop until a call to a filter succeeded.  Other option is to recurse
    553    // but that would waste stack trace when a number of filters gets registered
    554    // and all from some reason tend to fail.
    555    // The !mFilterCalledBack part of the condition is there to protect us from
    556    // calling on another filter when the current one managed to call back and
    557    // then threw. We already have the result so take it and use it since
    558    // the next filter will be processed by the root loop or a call to
    559    // ProcessNextFilter has already been dispatched to this thread.
    560    LOG(("  calling filter %p pi=%p", filter.get(), mProxyInfo.get()));
    561  } while (!mRequest->mPPS->ApplyFilter(filter, mRequest->mChannel, mInfo,
    562                                        mProxyInfo, this) &&
    563           !mFilterCalledBack);
    564 
    565  LOG(("AsyncApplyFilters::ProcessNextFilter %p LEAVE pi=%p", this,
    566       mProxyInfo.get()));
    567  return NS_OK;
    568 }
    569 
    570 NS_IMETHODIMP
    571 nsAsyncResolveRequest::AsyncApplyFilters::OnProxyFilterResult(
    572    nsIProxyInfo* aProxyInfo) {
    573  LOG(("AsyncApplyFilters::OnProxyFilterResult %p pi=%p", this, aProxyInfo));
    574 
    575  MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
    576  MOZ_ASSERT(!mFilterCalledBack);
    577 
    578  if (mFilterCalledBack) {
    579    LOG(("  duplicate notification?"));
    580    return NS_OK;
    581  }
    582 
    583  mFilterCalledBack = true;
    584 
    585  if (!mRequest) {
    586    // We got canceled
    587    LOG(("  canceled"));
    588    return NS_OK;
    589  }
    590 
    591  mProxyInfo = aProxyInfo;
    592 
    593  if (mProcessingInLoop) {
    594    // No need to call/dispatch ProcessNextFilter(), we are in a control
    595    // loop that will do this for us and save recursion/dispatching.
    596    LOG(("  in a root loop"));
    597    return NS_OK;
    598  }
    599 
    600  if (mNextFilterIndex == mFiltersCopy.Length()) {
    601    // We are done, all filters have been called on!
    602    Finish();
    603    return NS_OK;
    604  }
    605 
    606  // Redispatch, since we don't want long stacks when filters respond
    607  // synchronously.
    608  LOG(("  redispatching"));
    609  NS_DispatchToCurrentThread(this);
    610  return NS_OK;
    611 }
    612 
    613 NS_IMETHODIMP
    614 nsAsyncResolveRequest::AsyncApplyFilters::Run() {
    615  LOG(("AsyncApplyFilters::Run %p", this));
    616 
    617  MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
    618 
    619  ProcessNextFilter();
    620  return NS_OK;
    621 }
    622 
    623 nsresult nsAsyncResolveRequest::AsyncApplyFilters::Finish() {
    624  LOG(("AsyncApplyFilters::Finish %p pi=%p", this, mProxyInfo.get()));
    625 
    626  MOZ_ASSERT(mRequest);
    627 
    628  mFiltersCopy.Clear();
    629 
    630  RefPtr<nsAsyncResolveRequest> request;
    631  request.swap(mRequest);
    632 
    633  nsCOMPtr<nsIProxyInfo> pi;
    634  pi.swap(mProxyInfo);
    635 
    636  request->mPPS->PruneProxyInfo(mInfo, pi);
    637  return mCallback(request, pi, !mProcessingInLoop);
    638 }
    639 
    640 NS_IMETHODIMP
    641 nsAsyncResolveRequest::AsyncApplyFilters::Cancel(nsresult reason) {
    642  LOG(("AsyncApplyFilters::Cancel %p", this));
    643 
    644  MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
    645 
    646  // This will be called only from inside the request, so don't call
    647  // its's callback.  Dropping the members means we simply break the cycle.
    648  mFiltersCopy.Clear();
    649  mProxyInfo = nullptr;
    650  mRequest = nullptr;
    651 
    652  return NS_OK;
    653 }
    654 
    655 // Bug 1366133: make GetPACURI and GetSystemWPADSetting off-main-thread since it
    656 // may hang on Windows platform
    657 class AsyncGetPACURIRequestOrSystemWPADSetting final : public nsIRunnable {
    658 public:
    659  NS_DECL_THREADSAFE_ISUPPORTS
    660 
    661  using CallbackFunc = nsresult (nsProtocolProxyService::*)(bool, bool,
    662                                                            nsresult,
    663                                                            const nsACString&,
    664                                                            bool);
    665 
    666  AsyncGetPACURIRequestOrSystemWPADSetting(
    667      nsProtocolProxyService* aService, CallbackFunc aCallback,
    668      nsISystemProxySettings* aSystemProxySettings, bool aMainThreadOnly,
    669      bool aForceReload, bool aResetPACThread, bool aSystemWPADAllowed)
    670      : mIsMainThreadOnly(aMainThreadOnly),
    671        mService(aService),
    672        mServiceHolder(do_QueryObject(aService)),
    673        mCallback(aCallback),
    674        mSystemProxySettings(aSystemProxySettings),
    675        mForceReload(aForceReload),
    676        mResetPACThread(aResetPACThread),
    677        mSystemWPADAllowed(aSystemWPADAllowed) {
    678    MOZ_ASSERT(NS_IsMainThread());
    679    (void)mIsMainThreadOnly;
    680  }
    681 
    682  NS_IMETHOD Run() override {
    683    MOZ_ASSERT(NS_IsMainThread() == mIsMainThreadOnly);
    684 
    685    nsresult rv;
    686    nsCString pacUri;
    687    bool systemWPADSetting = false;
    688    if (mSystemWPADAllowed) {
    689      mSystemProxySettings->GetSystemWPADSetting(&systemWPADSetting);
    690    }
    691 
    692    rv = mSystemProxySettings->GetPACURI(pacUri);
    693 
    694    nsCOMPtr<nsIRunnable> event =
    695        NewNonOwningCancelableRunnableMethod<bool, bool, nsresult, nsCString,
    696                                             bool>(
    697            "AsyncGetPACURIRequestOrSystemWPADSettingCallback", mService,
    698            mCallback, mForceReload, mResetPACThread, rv, pacUri,
    699            systemWPADSetting);
    700 
    701    return NS_DispatchToMainThread(event);
    702  }
    703 
    704 private:
    705  ~AsyncGetPACURIRequestOrSystemWPADSetting() {
    706    NS_ReleaseOnMainThread(
    707        "AsyncGetPACURIRequestOrSystemWPADSetting::mServiceHolder",
    708        mServiceHolder.forget());
    709  }
    710 
    711  bool mIsMainThreadOnly;
    712 
    713  nsProtocolProxyService* mService;  // ref-count is hold by mServiceHolder
    714  nsCOMPtr<nsIProtocolProxyService2> mServiceHolder;
    715  CallbackFunc mCallback;
    716  nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
    717 
    718  bool mForceReload;
    719  bool mResetPACThread;
    720  bool mSystemWPADAllowed;
    721 };
    722 
    723 NS_IMPL_ISUPPORTS(AsyncGetPACURIRequestOrSystemWPADSetting, nsIRunnable)
    724 
    725 //----------------------------------------------------------------------------
    726 
    727 //
    728 // apply mask to address (zeros out excluded bits).
    729 //
    730 // NOTE: we do the byte swapping here to minimize overall swapping.
    731 //
    732 static void proxy_MaskIPv6Addr(PRIPv6Addr& addr, uint16_t mask_len) {
    733  if (mask_len == 128) return;
    734 
    735  if (mask_len > 96) {
    736    addr.pr_s6_addr32[3] =
    737        PR_htonl(PR_ntohl(addr.pr_s6_addr32[3]) & (~0uL << (128 - mask_len)));
    738  } else if (mask_len > 64) {
    739    addr.pr_s6_addr32[3] = 0;
    740    addr.pr_s6_addr32[2] =
    741        PR_htonl(PR_ntohl(addr.pr_s6_addr32[2]) & (~0uL << (96 - mask_len)));
    742  } else if (mask_len > 32) {
    743    addr.pr_s6_addr32[3] = 0;
    744    addr.pr_s6_addr32[2] = 0;
    745    addr.pr_s6_addr32[1] =
    746        PR_htonl(PR_ntohl(addr.pr_s6_addr32[1]) & (~0uL << (64 - mask_len)));
    747  } else {
    748    addr.pr_s6_addr32[3] = 0;
    749    addr.pr_s6_addr32[2] = 0;
    750    addr.pr_s6_addr32[1] = 0;
    751    addr.pr_s6_addr32[0] =
    752        PR_htonl(PR_ntohl(addr.pr_s6_addr32[0]) & (~0uL << (32 - mask_len)));
    753  }
    754 }
    755 
    756 static void proxy_GetStringPref(nsIPrefBranch* aPrefBranch, const char* aPref,
    757                                nsCString& aResult) {
    758  nsAutoCString temp;
    759  nsresult rv = aPrefBranch->GetCharPref(aPref, temp);
    760  if (NS_FAILED(rv)) {
    761    aResult.Truncate();
    762  } else {
    763    aResult.Assign(temp);
    764    // all of our string prefs are hostnames, so we should remove any
    765    // whitespace characters that the user might have unknowingly entered.
    766    aResult.StripWhitespace();
    767  }
    768 }
    769 
    770 static void proxy_GetIntPref(nsIPrefBranch* aPrefBranch, const char* aPref,
    771                             int32_t& aResult) {
    772  int32_t temp;
    773  nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
    774  if (NS_FAILED(rv)) {
    775    aResult = -1;
    776  } else {
    777    aResult = temp;
    778  }
    779 }
    780 
    781 static void proxy_GetBoolPref(nsIPrefBranch* aPrefBranch, const char* aPref,
    782                              bool& aResult) {
    783  bool temp;
    784  nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
    785  if (NS_FAILED(rv)) {
    786    aResult = false;
    787  } else {
    788    aResult = temp;
    789  }
    790 }
    791 
    792 //----------------------------------------------------------------------------
    793 
    794 static const int32_t PROXYCONFIG_DIRECT4X = 3;
    795 static const int32_t PROXYCONFIG_COUNT = 6;
    796 
    797 NS_IMPL_ADDREF(nsProtocolProxyService)
    798 NS_IMPL_RELEASE(nsProtocolProxyService)
    799 NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
    800                  NS_PROTOCOLPROXYSERVICE_CID)
    801 
    802 // NS_IMPL_QUERY_INTERFACE_CI with the nsProtocolProxyService QI change
    803 NS_INTERFACE_MAP_BEGIN(nsProtocolProxyService)
    804  NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService)
    805  NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService2)
    806  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    807  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
    808  NS_INTERFACE_MAP_ENTRY(nsINamed)
    809  NS_INTERFACE_MAP_ENTRY_CONCRETE(nsProtocolProxyService)
    810  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolProxyService)
    811  NS_IMPL_QUERY_CLASSINFO(nsProtocolProxyService)
    812 NS_INTERFACE_MAP_END
    813 
    814 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService, nsIProtocolProxyService,
    815                            nsIProtocolProxyService2)
    816 
    817 nsProtocolProxyService::nsProtocolProxyService() : mSessionStart(PR_Now()) {}
    818 
    819 nsProtocolProxyService::~nsProtocolProxyService() {
    820  // These should have been cleaned up in our Observe method.
    821  NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters.Length() == 0 &&
    822                   mPACMan == nullptr,
    823               "what happened to xpcom-shutdown?");
    824 }
    825 
    826 // nsProtocolProxyService methods
    827 nsresult nsProtocolProxyService::Init() {
    828  // failure to access prefs is non-fatal
    829  nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
    830  if (prefBranch) {
    831    // monitor proxy prefs
    832    prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
    833 
    834    // read all prefs
    835    PrefsChanged(prefBranch, nullptr);
    836  }
    837 
    838  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    839  if (obs) {
    840    // register for shutdown notification so we can clean ourselves up
    841    // properly.
    842    obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
    843    obs->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
    844  }
    845 
    846  return NS_OK;
    847 }
    848 
    849 // ReloadNetworkPAC() checks if there's a non-networked PAC in use then avoids
    850 // to call ReloadPAC()
    851 nsresult nsProtocolProxyService::ReloadNetworkPAC() {
    852  LOG(("nsProtocolProxyService::ReloadNetworkPAC"));
    853  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
    854  if (!prefs) {
    855    return NS_OK;
    856  }
    857 
    858  int32_t type;
    859  nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
    860  if (NS_FAILED(rv)) {
    861    return NS_OK;
    862  }
    863 
    864  if (type == PROXYCONFIG_PAC) {
    865    nsAutoCString pacSpec;
    866    prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
    867    if (!pacSpec.IsEmpty()) {
    868      nsCOMPtr<nsIURI> pacURI;
    869      rv = NS_NewURI(getter_AddRefs(pacURI), pacSpec);
    870      if (!NS_SUCCEEDED(rv)) {
    871        return rv;
    872      }
    873 
    874      nsProtocolInfo pac;
    875      rv = GetProtocolInfo(pacURI, &pac);
    876      if (!NS_SUCCEEDED(rv)) {
    877        return rv;
    878      }
    879 
    880      if (!pac.scheme.EqualsLiteral("file") &&
    881          !pac.scheme.EqualsLiteral("data")) {
    882        LOG((": received network changed event, reload PAC"));
    883        ReloadPAC();
    884      }
    885    }
    886  } else if ((type == PROXYCONFIG_WPAD) || (type == PROXYCONFIG_SYSTEM)) {
    887    ReloadPAC();
    888  }
    889 
    890  return NS_OK;
    891 }
    892 
    893 nsresult nsProtocolProxyService::AsyncConfigureWPADOrFromPAC(
    894    bool aForceReload, bool aResetPACThread, bool aSystemWPADAllowed) {
    895  LOG(("nsProtocolProxyService::AsyncConfigureWPADOrFromPAC"));
    896  MOZ_ASSERT(NS_IsMainThread());
    897 
    898  bool mainThreadOnly;
    899  nsresult rv = mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly);
    900  if (NS_WARN_IF(NS_FAILED(rv))) {
    901    return rv;
    902  }
    903 
    904  nsCOMPtr<nsIRunnable> req = new AsyncGetPACURIRequestOrSystemWPADSetting(
    905      this, &nsProtocolProxyService::OnAsyncGetPACURIOrSystemWPADSetting,
    906      mSystemProxySettings, mainThreadOnly, aForceReload, aResetPACThread,
    907      aSystemWPADAllowed);
    908 
    909  if (mainThreadOnly) {
    910    return req->Run();
    911  }
    912 
    913  return NS_DispatchBackgroundTask(req.forget(),
    914                                   nsIEventTarget::DISPATCH_NORMAL);
    915 }
    916 
    917 nsresult nsProtocolProxyService::OnAsyncGetPACURIOrSystemWPADSetting(
    918    bool aForceReload, bool aResetPACThread, nsresult aResult,
    919    const nsACString& aUri, bool aSystemWPADSetting) {
    920  LOG(("nsProtocolProxyService::OnAsyncGetPACURIOrSystemWPADSetting"));
    921  MOZ_ASSERT(NS_IsMainThread());
    922 
    923  if (aResetPACThread) {
    924    ResetPACThread();
    925  }
    926 
    927  if (aSystemWPADSetting) {
    928    if (mSystemProxySettings || !mPACMan) {
    929      mSystemProxySettings = nullptr;
    930      ResetPACThread();
    931    }
    932 
    933    nsAutoCString tempString;
    934    ConfigureFromPAC(EmptyCString(), false);
    935  } else if (NS_SUCCEEDED(aResult) && !aUri.IsEmpty()) {
    936    ConfigureFromPAC(PromiseFlatCString(aUri), aForceReload);
    937  }
    938 
    939  return NS_OK;
    940 }
    941 
    942 NS_IMETHODIMP
    943 nsProtocolProxyService::Observe(nsISupports* aSubject, const char* aTopic,
    944                                const char16_t* aData) {
    945  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
    946    mIsShutdown = true;
    947    // cleanup
    948    mHostFiltersArray.Clear();
    949    mFilters.Clear();
    950 
    951    if (mPACMan) {
    952      mPACMan->Shutdown();
    953      mPACMan = nullptr;
    954    }
    955 
    956    if (mReloadPACTimer) {
    957      mReloadPACTimer->Cancel();
    958      mReloadPACTimer = nullptr;
    959    }
    960 
    961    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    962    if (obs) {
    963      obs->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
    964      obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
    965    }
    966 
    967  } else if (strcmp(aTopic, NS_NETWORK_LINK_TOPIC) == 0) {
    968    nsCString converted = NS_ConvertUTF16toUTF8(aData);
    969    const char* state = converted.get();
    970    if (!strcmp(state, NS_NETWORK_LINK_DATA_CHANGED)) {
    971      uint32_t delay = StaticPrefs::network_proxy_reload_pac_delay();
    972      LOG(("nsProtocolProxyService::Observe call ReloadNetworkPAC() delay=%u",
    973           delay));
    974 
    975      if (delay) {
    976        if (mReloadPACTimer) {
    977          mReloadPACTimer->Cancel();
    978          mReloadPACTimer = nullptr;
    979        }
    980        NS_NewTimerWithCallback(getter_AddRefs(mReloadPACTimer), this, delay,
    981                                nsITimer::TYPE_ONE_SHOT);
    982      } else {
    983        ReloadNetworkPAC();
    984      }
    985    }
    986  } else {
    987    NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
    988                 "what is this random observer event?");
    989    nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
    990    if (prefs) PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
    991  }
    992  return NS_OK;
    993 }
    994 
    995 NS_IMETHODIMP
    996 nsProtocolProxyService::Notify(nsITimer* aTimer) {
    997  MOZ_ASSERT(aTimer == mReloadPACTimer);
    998  ReloadNetworkPAC();
    999  return NS_OK;
   1000 }
   1001 
   1002 NS_IMETHODIMP
   1003 nsProtocolProxyService::GetName(nsACString& aName) {
   1004  aName.AssignLiteral("nsProtocolProxyService");
   1005  return NS_OK;
   1006 }
   1007 
   1008 void nsProtocolProxyService::PrefsChanged(nsIPrefBranch* prefBranch,
   1009                                          const char* pref) {
   1010  nsresult rv = NS_OK;
   1011  bool reloadPAC = false;
   1012  nsAutoCString tempString;
   1013  auto invokeCallback =
   1014      MakeScopeExit([&] { NotifyProxyConfigChangedInternal(); });
   1015 
   1016  if (!pref || !strcmp(pref, PROXY_PREF("type")) ||
   1017      !strcmp(pref, PROXY_PREF("system_wpad"))) {
   1018    int32_t type = -1;
   1019    rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
   1020    if (NS_SUCCEEDED(rv)) {
   1021      // bug 115720 - for ns4.x backwards compatibility
   1022      if (type == PROXYCONFIG_DIRECT4X) {
   1023        type = PROXYCONFIG_DIRECT;
   1024        // Reset the type so that the dialog looks correct, and we
   1025        // don't have to handle this case everywhere else
   1026        // I'm paranoid about a loop of some sort - only do this
   1027        // if we're enumerating all prefs, and ignore any error
   1028        if (!pref) prefBranch->SetIntPref(PROXY_PREF("type"), type);
   1029      } else if (type >= PROXYCONFIG_COUNT) {
   1030        LOG(("unknown proxy type: %" PRId32 "; assuming direct\n", type));
   1031        type = PROXYCONFIG_DIRECT;
   1032      }
   1033      mProxyConfig = type;
   1034      reloadPAC = true;
   1035    }
   1036 
   1037    if (mProxyConfig == PROXYCONFIG_SYSTEM) {
   1038      mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
   1039      if (!mSystemProxySettings) mProxyConfig = PROXYCONFIG_DIRECT;
   1040      ResetPACThread();
   1041    } else {
   1042      if (mSystemProxySettings) {
   1043        mSystemProxySettings = nullptr;
   1044        ResetPACThread();
   1045      }
   1046    }
   1047  }
   1048 
   1049  if (!pref || !strcmp(pref, PROXY_PREF("http"))) {
   1050    proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
   1051  }
   1052 
   1053  if (!pref || !strcmp(pref, PROXY_PREF("http_port"))) {
   1054    proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
   1055  }
   1056 
   1057  if (!pref || !strcmp(pref, PROXY_PREF("ssl"))) {
   1058    proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
   1059  }
   1060 
   1061  if (!pref || !strcmp(pref, PROXY_PREF("ssl_port"))) {
   1062    proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
   1063  }
   1064 
   1065  if (!pref || !strcmp(pref, PROXY_PREF("socks"))) {
   1066    proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyTarget);
   1067  }
   1068 
   1069  if (!pref || !strcmp(pref, PROXY_PREF("socks_port"))) {
   1070    proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
   1071  }
   1072 
   1073  if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
   1074    int32_t version;
   1075    proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
   1076    // make sure this preference value remains sane
   1077    if (version == nsIProxyInfo::SOCKS_V5) {
   1078      mSOCKSProxyVersion = nsIProxyInfo::SOCKS_V5;
   1079    } else {
   1080      mSOCKSProxyVersion = nsIProxyInfo::SOCKS_V4;
   1081    }
   1082  }
   1083 
   1084  if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns"))) {
   1085    proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
   1086                      mSOCKS4ProxyRemoteDNS);
   1087  }
   1088 
   1089  if (!pref || !strcmp(pref, PROXY_PREF("socks5_remote_dns"))) {
   1090    proxy_GetBoolPref(prefBranch, PROXY_PREF("socks5_remote_dns"),
   1091                      mSOCKS5ProxyRemoteDNS);
   1092  }
   1093 
   1094  if (!pref || !strcmp(pref, PROXY_PREF("proxy_over_tls"))) {
   1095    proxy_GetBoolPref(prefBranch, PROXY_PREF("proxy_over_tls"), mProxyOverTLS);
   1096  }
   1097 
   1098  if (!pref || !strcmp(pref, PROXY_PREF("enable_wpad_over_dhcp"))) {
   1099    proxy_GetBoolPref(prefBranch, PROXY_PREF("enable_wpad_over_dhcp"),
   1100                      mWPADOverDHCPEnabled);
   1101    reloadPAC = reloadPAC || mProxyConfig == PROXYCONFIG_WPAD;
   1102  }
   1103 
   1104  if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout"))) {
   1105    proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
   1106                     mFailedProxyTimeout);
   1107  }
   1108 
   1109  if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
   1110    rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"), tempString);
   1111    if (NS_SUCCEEDED(rv)) LoadHostFilters(tempString);
   1112  }
   1113 
   1114  // We're done if not using something that could give us a PAC URL
   1115  // (PAC, WPAD or System)
   1116  if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
   1117      mProxyConfig != PROXYCONFIG_SYSTEM) {
   1118    return;
   1119  }
   1120 
   1121  // OK, we need to reload the PAC file if:
   1122  //  1) network.proxy.type changed, or
   1123  //  2) network.proxy.autoconfig_url changed and PAC is configured
   1124 
   1125  if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url"))) reloadPAC = true;
   1126 
   1127  if (reloadPAC) {
   1128    tempString.Truncate();
   1129    if (mProxyConfig == PROXYCONFIG_PAC) {
   1130      prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"), tempString);
   1131      if (mPACMan && !mPACMan->IsPACURI(tempString)) {
   1132        LOG(("PAC Thread URI Changed - Reset Pac Thread"));
   1133        ResetPACThread();
   1134      }
   1135    } else if (mProxyConfig == PROXYCONFIG_WPAD) {
   1136      LOG(("Auto-detecting proxy - Reset Pac Thread"));
   1137      ResetPACThread();
   1138    } else if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM &&
   1139               StaticPrefs::network_proxy_system_wpad()) {
   1140      AsyncConfigureWPADOrFromPAC(false, false, true);
   1141    } else if (mSystemProxySettings) {
   1142      // Get System Proxy settings if available
   1143      AsyncConfigureWPADOrFromPAC(false, false, false);
   1144    }
   1145    if (!tempString.IsEmpty() || mProxyConfig == PROXYCONFIG_WPAD) {
   1146      ConfigureFromPAC(tempString, false);
   1147    }
   1148  }
   1149 }
   1150 
   1151 bool nsProtocolProxyService::CanUseProxy(nsIURI* aURI, int32_t defaultPort) {
   1152  int32_t port;
   1153  nsAutoCString host;
   1154 
   1155  nsresult rv = aURI->GetAsciiHost(host);
   1156  if (NS_FAILED(rv) || host.IsEmpty()) return false;
   1157 
   1158  rv = aURI->GetPort(&port);
   1159  if (NS_FAILED(rv)) return false;
   1160  if (port == -1) port = defaultPort;
   1161 
   1162  PRNetAddr addr;
   1163  bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
   1164 
   1165  PRIPv6Addr ipv6;
   1166  if (is_ipaddr) {
   1167    // convert parsed address to IPv6
   1168    if (addr.raw.family == PR_AF_INET) {
   1169      // convert to IPv4-mapped address
   1170      PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
   1171    } else if (addr.raw.family == PR_AF_INET6) {
   1172      // copy the address
   1173      memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
   1174    } else {
   1175      NS_WARNING("unknown address family");
   1176      return true;  // allow proxying
   1177    }
   1178  }
   1179 
   1180  // Don't use proxy for local hosts (plain hostname, no dots)
   1181  if ((!is_ipaddr && mFilterLocalHosts && !host.Contains('.')) ||
   1182      // This method detects if we have network.proxy.allow_hijacking_localhost
   1183      // pref enabled. If it's true then this method will always return false
   1184      // otherwise it returns true if the host matches an address that's
   1185      // hardcoded to the loopback address.
   1186      (!StaticPrefs::network_proxy_allow_hijacking_localhost() &&
   1187       nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(host))) {
   1188    LOG(("Not using proxy for this local host [%s]!\n", host.get()));
   1189    return false;  // don't allow proxying
   1190  }
   1191 
   1192  int32_t index = -1;
   1193  while (++index < int32_t(mHostFiltersArray.Length())) {
   1194    const auto& hinfo = mHostFiltersArray[index];
   1195 
   1196    if (is_ipaddr != hinfo->is_ipaddr) continue;
   1197    if (hinfo->port && hinfo->port != port) continue;
   1198 
   1199    if (is_ipaddr) {
   1200      // generate masked version of target IPv6 address
   1201      PRIPv6Addr masked;
   1202      memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
   1203      proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
   1204 
   1205      // check for a match
   1206      if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0) {
   1207        return false;  // proxy disallowed
   1208      }
   1209    } else {
   1210      uint32_t host_len = host.Length();
   1211      uint32_t filter_host_len = hinfo->name.host_len;
   1212 
   1213      if (host_len >= filter_host_len) {
   1214        //
   1215        // compare last |filter_host_len| bytes of target hostname.
   1216        //
   1217        const char* host_tail = host.get() + host_len - filter_host_len;
   1218        if (!nsCRT::strncasecmp(host_tail, hinfo->name.host, filter_host_len)) {
   1219          // If the tail of the host string matches the filter
   1220 
   1221          if (filter_host_len > 0 && hinfo->name.host[0] == '.') {
   1222            // If the filter was of the form .foo.bar.tld, all such
   1223            // matches are correct
   1224            return false;  // proxy disallowed
   1225          }
   1226 
   1227          // abc-def.example.org should not match def.example.org
   1228          // however, *.def.example.org should match .def.example.org
   1229          // We check that the filter doesn't start with a `.`. If it does,
   1230          // then the strncasecmp above should suffice. If it doesn't,
   1231          // then we should only consider it a match if the strncasecmp happened
   1232          // at a subdomain boundary
   1233          if (host_len > filter_host_len && *(host_tail - 1) == '.') {
   1234            // If the host was something.foo.bar.tld and the filter
   1235            // was foo.bar.tld, it's still a match.
   1236            // the character right before the tail must be a
   1237            // `.` for this to work
   1238            return false;  // proxy disallowed
   1239          }
   1240 
   1241          if (host_len == filter_host_len) {
   1242            // If the host and filter are of the same length,
   1243            // they should match
   1244            return false;  // proxy disallowed
   1245          }
   1246        }
   1247      }
   1248    }
   1249  }
   1250  return true;
   1251 }
   1252 
   1253 // kProxyType\* may be referred to externally in
   1254 // nsProxyInfo in order to compare by string pointer
   1255 const char kProxyType_HTTP[] = "http";
   1256 const char kProxyType_HTTPS[] = "https";
   1257 const char kProxyType_PROXY[] = "proxy";
   1258 const char kProxyType_SOCKS[] = "socks";
   1259 const char kProxyType_SOCKS4[] = "socks4";
   1260 const char kProxyType_SOCKS5[] = "socks5";
   1261 const char kProxyType_DIRECT[] = "direct";
   1262 const char kProxyType_MASQUE[] = "masque";
   1263 
   1264 const char* nsProtocolProxyService::ExtractProxyInfo(const char* start,
   1265                                                     uint32_t aResolveFlags,
   1266                                                     nsProxyInfo** result) {
   1267  *result = nullptr;
   1268  uint32_t flags = 0;
   1269 
   1270  // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
   1271 
   1272  // find end of proxy info delimiter
   1273  const char* end = start;
   1274  while (*end && *end != ';') ++end;
   1275 
   1276  // find end of proxy type delimiter
   1277  const char* sp = start;
   1278  while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
   1279 
   1280  uint32_t len = sp - start;
   1281  const char* type = nullptr;
   1282  switch (len) {
   1283    case 4:
   1284      if (nsCRT::strncasecmp(start, kProxyType_HTTP, 4) == 0) {
   1285        type = kProxyType_HTTP;
   1286      }
   1287      break;
   1288    case 5:
   1289      if (nsCRT::strncasecmp(start, kProxyType_PROXY, 5) == 0) {
   1290        type = kProxyType_HTTP;
   1291      } else if (nsCRT::strncasecmp(start, kProxyType_SOCKS, 5) == 0) {
   1292        type = kProxyType_SOCKS4;  // assume v4 for 4x compat
   1293        if (StaticPrefs::network_proxy_default_pac_script_socks_version() ==
   1294            5) {
   1295          type = kProxyType_SOCKS;
   1296        }
   1297      } else if (nsCRT::strncasecmp(start, kProxyType_HTTPS, 5) == 0) {
   1298        type = kProxyType_HTTPS;
   1299      }
   1300      break;
   1301    case 6:
   1302      if (nsCRT::strncasecmp(start, kProxyType_DIRECT, 6) == 0) {
   1303        type = kProxyType_DIRECT;
   1304      } else if (nsCRT::strncasecmp(start, kProxyType_SOCKS4, 6) == 0) {
   1305        type = kProxyType_SOCKS4;
   1306      } else if (nsCRT::strncasecmp(start, kProxyType_SOCKS5, 6) == 0) {
   1307        // map "SOCKS5" to "socks" to match contract-id of registered
   1308        // SOCKS-v5 socket provider.
   1309        type = kProxyType_SOCKS;
   1310      }
   1311      break;
   1312  }
   1313  if (type) {
   1314    int32_t port = -1;
   1315 
   1316    // If it's a SOCKS5 proxy, do name resolution on the server side.
   1317    // We could use this with SOCKS4a servers too, but they might not
   1318    // support it.
   1319    if (type == kProxyType_SOCKS || mSOCKS5ProxyRemoteDNS) {
   1320      flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
   1321    }
   1322 
   1323    // extract host:port
   1324    start = sp;
   1325    while ((*start == ' ' || *start == '\t') && start < end) start++;
   1326 
   1327    // port defaults
   1328    if (type == kProxyType_HTTP) {
   1329      port = 80;
   1330    } else if (type == kProxyType_HTTPS) {
   1331      port = 443;
   1332    } else {
   1333      port = 1080;
   1334    }
   1335 
   1336    RefPtr<nsProxyInfo> pi = new nsProxyInfo();
   1337    pi->mType = type;
   1338    pi->mFlags = flags;
   1339    pi->mResolveFlags = aResolveFlags;
   1340    pi->mTimeout = mFailedProxyTimeout;
   1341 
   1342    // www.foo.com:8080 and http://www.foo.com:8080
   1343    nsDependentCSubstring maybeURL(start, end - start);
   1344    nsCOMPtr<nsIURI> pacURI;
   1345 
   1346    nsAutoCString urlHost;
   1347    // First assume the scheme is present, e.g. http://www.example.com:8080
   1348    if (NS_FAILED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) ||
   1349        NS_FAILED(pacURI->GetAsciiHost(urlHost)) || urlHost.IsEmpty()) {
   1350      // It isn't, assume www.example.com:8080
   1351      maybeURL.Insert("http://", 0);
   1352 
   1353      if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL))) {
   1354        pacURI->GetAsciiHost(urlHost);
   1355      }
   1356    }
   1357 
   1358    if (!urlHost.IsEmpty()) {
   1359      pi->mHost = urlHost;
   1360 
   1361      int32_t tPort;
   1362      if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
   1363        port = tPort;
   1364      }
   1365      pi->mPort = port;
   1366    }
   1367 
   1368    pi.forget(result);
   1369  }
   1370 
   1371  while (*end == ';' || *end == ' ' || *end == '\t') ++end;
   1372  return end;
   1373 }
   1374 
   1375 void nsProtocolProxyService::GetProxyKey(nsProxyInfo* pi, nsCString& key) {
   1376  key.AssignASCII(pi->mType);
   1377  if (!pi->mHost.IsEmpty()) {
   1378    key.Append(' ');
   1379    key.Append(pi->mHost);
   1380    key.Append(':');
   1381    key.AppendInt(pi->mPort);
   1382  }
   1383 }
   1384 
   1385 uint32_t nsProtocolProxyService::SecondsSinceSessionStart() {
   1386  PRTime now = PR_Now();
   1387 
   1388  // get time elapsed since session start
   1389  int64_t diff = now - mSessionStart;
   1390 
   1391  // convert microseconds to seconds
   1392  diff /= PR_USEC_PER_SEC;
   1393 
   1394  // return converted 32 bit value
   1395  return uint32_t(diff);
   1396 }
   1397 
   1398 void nsProtocolProxyService::EnableProxy(nsProxyInfo* pi) {
   1399  nsAutoCString key;
   1400  GetProxyKey(pi, key);
   1401  mFailedProxies.Remove(key);
   1402 }
   1403 
   1404 void nsProtocolProxyService::DisableProxy(nsProxyInfo* pi) {
   1405  nsAutoCString key;
   1406  GetProxyKey(pi, key);
   1407 
   1408  uint32_t dsec = SecondsSinceSessionStart();
   1409 
   1410  // Add timeout to interval (this is the time when the proxy can
   1411  // be tried again).
   1412  dsec += pi->mTimeout;
   1413 
   1414  // NOTE: The classic codebase would increase the timeout value
   1415  //       incrementally each time a subsequent failure occurred.
   1416  //       We could do the same, but it would require that we not
   1417  //       remove proxy entries in IsProxyDisabled or otherwise
   1418  //       change the way we are recording disabled proxies.
   1419  //       Simpler is probably better for now, and at least the
   1420  //       user can tune the timeout setting via preferences.
   1421 
   1422  LOG(("DisableProxy %s %d\n", key.get(), dsec));
   1423 
   1424  // If this fails, oh well... means we don't have enough memory
   1425  // to remember the failed proxy.
   1426  mFailedProxies.InsertOrUpdate(key, dsec);
   1427 }
   1428 
   1429 bool nsProtocolProxyService::IsProxyDisabled(nsProxyInfo* pi) {
   1430  nsAutoCString key;
   1431  GetProxyKey(pi, key);
   1432 
   1433  uint32_t val;
   1434  if (!mFailedProxies.Get(key, &val)) return false;
   1435 
   1436  uint32_t dsec = SecondsSinceSessionStart();
   1437 
   1438  // if time passed has exceeded interval, then try proxy again.
   1439  if (dsec > val) {
   1440    mFailedProxies.Remove(key);
   1441    return false;
   1442  }
   1443 
   1444  return true;
   1445 }
   1446 
   1447 nsresult nsProtocolProxyService::SetupPACThread(
   1448    nsISerialEventTarget* mainThreadEventTarget) {
   1449  LOG(("nsProtocolProxyService::SetupPACThread"));
   1450  if (mIsShutdown) {
   1451    return NS_ERROR_FAILURE;
   1452  }
   1453 
   1454  if (mPACMan) return NS_OK;
   1455 
   1456  mPACMan = new nsPACMan(mainThreadEventTarget);
   1457 
   1458  bool mainThreadOnly;
   1459  nsresult rv;
   1460  if (mSystemProxySettings &&
   1461      NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
   1462      !mainThreadOnly) {
   1463    rv = mPACMan->Init(mSystemProxySettings);
   1464  } else {
   1465    rv = mPACMan->Init(nullptr);
   1466  }
   1467  if (NS_FAILED(rv)) {
   1468    mPACMan->Shutdown();
   1469    mPACMan = nullptr;
   1470  }
   1471  return rv;
   1472 }
   1473 
   1474 nsresult nsProtocolProxyService::ResetPACThread() {
   1475  LOG(("nsProtocolProxyService::ResetPACThread"));
   1476  if (!mPACMan) return NS_OK;
   1477 
   1478  mPACMan->Shutdown();
   1479  mPACMan = nullptr;
   1480  return SetupPACThread();
   1481 }
   1482 
   1483 nsresult nsProtocolProxyService::ConfigureFromPAC(const nsCString& spec,
   1484                                                  bool forceReload) {
   1485  nsresult rv = SetupPACThread();
   1486  NS_ENSURE_SUCCESS(rv, rv);
   1487 
   1488  bool autodetect = spec.IsEmpty();
   1489  if (!forceReload && ((!autodetect && mPACMan->IsPACURI(spec)) ||
   1490                       (autodetect && mPACMan->IsUsingWPAD()))) {
   1491    return NS_OK;
   1492  }
   1493 
   1494  mFailedProxies.Clear();
   1495 
   1496  mPACMan->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled);
   1497  return mPACMan->LoadPACFromURI(spec);
   1498 }
   1499 
   1500 void nsProtocolProxyService::ProcessPACString(const nsCString& pacString,
   1501                                              uint32_t aResolveFlags,
   1502                                              nsIProxyInfo** result) {
   1503  if (pacString.IsEmpty()) {
   1504    *result = nullptr;
   1505    return;
   1506  }
   1507 
   1508  const char* proxies = pacString.get();
   1509 
   1510  nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
   1511  while (*proxies) {
   1512    proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
   1513    if (pi && (pi->mType == kProxyType_HTTPS) && !mProxyOverTLS) {
   1514      delete pi;
   1515      pi = nullptr;
   1516    }
   1517 
   1518    if (pi) {
   1519      if (last) {
   1520        NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
   1521        last->mNext = pi;
   1522      } else {
   1523        first = pi;
   1524      }
   1525      last = pi;
   1526    }
   1527  }
   1528  *result = first;
   1529 }
   1530 
   1531 // nsIProtocolProxyService2
   1532 NS_IMETHODIMP
   1533 nsProtocolProxyService::ReloadPAC() {
   1534  LOG(("nsProtocolProxyService::ReloadPAC"));
   1535  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1536  if (!prefs) return NS_OK;
   1537 
   1538  int32_t type;
   1539  nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
   1540  if (NS_FAILED(rv)) return NS_OK;
   1541 
   1542  nsAutoCString pacSpec;
   1543  if (type == PROXYCONFIG_PAC) {
   1544    prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
   1545  } else if (type == PROXYCONFIG_SYSTEM) {
   1546    if (mSystemProxySettings) {
   1547      AsyncConfigureWPADOrFromPAC(true, true,
   1548                                  StaticPrefs::network_proxy_system_wpad());
   1549    } else {
   1550      ResetPACThread();
   1551    }
   1552  }
   1553 
   1554  if (!pacSpec.IsEmpty() || type == PROXYCONFIG_WPAD) {
   1555    ConfigureFromPAC(pacSpec, true);
   1556  }
   1557  return NS_OK;
   1558 }
   1559 
   1560 nsresult nsProtocolProxyService::AsyncResolveInternal(
   1561    nsIChannel* channel, uint32_t flags, nsIProtocolProxyCallback* callback,
   1562    nsICancelable** result, bool isSyncOK,
   1563    nsISerialEventTarget* mainThreadEventTarget) {
   1564  LOG(("nsProtocolProxyService::AsyncResolveInternal"));
   1565  NS_ENSURE_ARG_POINTER(channel);
   1566  NS_ENSURE_ARG_POINTER(callback);
   1567 
   1568  nsCOMPtr<nsIURI> uri;
   1569  nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
   1570  if (NS_FAILED(rv)) return rv;
   1571 
   1572  *result = nullptr;
   1573  RefPtr<nsAsyncResolveRequest> ctx =
   1574      new nsAsyncResolveRequest(this, channel, flags, callback);
   1575 
   1576  nsProtocolInfo info;
   1577  rv = GetProtocolInfo(uri, &info);
   1578  if (NS_FAILED(rv)) return rv;
   1579 
   1580  nsCOMPtr<nsIProxyInfo> pi;
   1581  bool usePACThread;
   1582 
   1583  // adapt to realtime changes in the system proxy service
   1584  if (mProxyConfig == PROXYCONFIG_SYSTEM &&
   1585      !StaticPrefs::network_proxy_system_wpad()) {
   1586    nsCOMPtr<nsISystemProxySettings> sp2 =
   1587        do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
   1588    if (sp2 != mSystemProxySettings) {
   1589      mSystemProxySettings = sp2;
   1590      ResetPACThread();
   1591    }
   1592  }
   1593 
   1594  rv = SetupPACThread(mainThreadEventTarget);
   1595  if (NS_FAILED(rv)) {
   1596    return rv;
   1597  }
   1598 
   1599  // SystemProxySettings and PAC files can block the main thread
   1600  // but if neither of them are in use, we can just do the work
   1601  // right here and directly invoke the callback
   1602 
   1603  rv =
   1604      Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
   1605  if (NS_FAILED(rv)) return rv;
   1606 
   1607  if (!usePACThread || !mPACMan) {
   1608    // we can do it locally
   1609    rv = ctx->ProcessLocally(info, pi, isSyncOK);
   1610    if (NS_SUCCEEDED(rv) && !isSyncOK) {
   1611      ctx.forget(result);
   1612    }
   1613    return rv;
   1614  }
   1615 
   1616  // else kick off a PAC thread query
   1617  rv = mPACMan->AsyncGetProxyForURI(uri, ctx, flags, true);
   1618  if (NS_SUCCEEDED(rv)) ctx.forget(result);
   1619  return rv;
   1620 }
   1621 
   1622 // nsIProtocolProxyService
   1623 NS_IMETHODIMP
   1624 nsProtocolProxyService::AsyncResolve2(
   1625    nsIChannel* channel, uint32_t flags, nsIProtocolProxyCallback* callback,
   1626    nsISerialEventTarget* mainThreadEventTarget, nsICancelable** result) {
   1627  return AsyncResolveInternal(channel, flags, callback, result, true,
   1628                              mainThreadEventTarget);
   1629 }
   1630 
   1631 NS_IMETHODIMP
   1632 nsProtocolProxyService::AsyncResolve(
   1633    nsISupports* channelOrURI, uint32_t flags,
   1634    nsIProtocolProxyCallback* callback,
   1635    nsISerialEventTarget* mainThreadEventTarget, nsICancelable** result) {
   1636  nsresult rv;
   1637  // Check if we got a channel:
   1638  nsCOMPtr<nsIChannel> channel = do_QueryInterface(channelOrURI);
   1639  if (!channel) {
   1640    nsCOMPtr<nsIURI> uri = do_QueryInterface(channelOrURI);
   1641    if (!uri) {
   1642      return NS_ERROR_NO_INTERFACE;
   1643    }
   1644 
   1645    // creating a temporary channel from the URI which is not
   1646    // used to perform any network loads, hence its safe to
   1647    // use systemPrincipal as the loadingPrincipal.
   1648    rv = NS_NewChannel(getter_AddRefs(channel), uri,
   1649                       nsContentUtils::GetSystemPrincipal(),
   1650                       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
   1651                       nsIContentPolicy::TYPE_OTHER);
   1652    NS_ENSURE_SUCCESS(rv, rv);
   1653  }
   1654 
   1655  return AsyncResolveInternal(channel, flags, callback, result, false,
   1656                              mainThreadEventTarget);
   1657 }
   1658 
   1659 NS_IMETHODIMP
   1660 nsProtocolProxyService::NewProxyInfo(
   1661    const nsACString& aType, const nsACString& aHost, int32_t aPort,
   1662    const nsACString& aProxyAuthorizationHeader,
   1663    const nsACString& aConnectionIsolationKey, uint32_t aFlags,
   1664    uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
   1665    nsIProxyInfo** aResult) {
   1666  return NewProxyInfoWithAuth(aType, aHost, aPort, ""_ns, ""_ns,
   1667                              aProxyAuthorizationHeader,
   1668                              aConnectionIsolationKey, aFlags, aFailoverTimeout,
   1669                              aFailoverProxy, aResult);
   1670 }
   1671 
   1672 NS_IMETHODIMP
   1673 nsProtocolProxyService::NewProxyInfoWithAuth(
   1674    const nsACString& aType, const nsACString& aHost, int32_t aPort,
   1675    const nsACString& aUsername, const nsACString& aPassword,
   1676    const nsACString& aProxyAuthorizationHeader,
   1677    const nsACString& aConnectionIsolationKey, uint32_t aFlags,
   1678    uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
   1679    nsIProxyInfo** aResult) {
   1680  static const char* types[] = {kProxyType_HTTP, kProxyType_HTTPS,
   1681                                kProxyType_SOCKS, kProxyType_SOCKS4,
   1682                                kProxyType_DIRECT};
   1683 
   1684  // resolve type; this allows us to avoid copying the type string into each
   1685  // proxy info instance.  we just reference the string literals directly :)
   1686  const char* type = nullptr;
   1687  for (auto& t : types) {
   1688    if (aType.LowerCaseEqualsASCII(t)) {
   1689      type = t;
   1690      break;
   1691    }
   1692  }
   1693  NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
   1694 
   1695  // We have only implemented username/password for SOCKS proxies.
   1696  if ((!aUsername.IsEmpty() || !aPassword.IsEmpty()) &&
   1697      !aType.LowerCaseEqualsASCII(kProxyType_SOCKS) &&
   1698      !aType.LowerCaseEqualsASCII(kProxyType_SOCKS4)) {
   1699    return NS_ERROR_NOT_IMPLEMENTED;
   1700  }
   1701 
   1702  return NewProxyInfo_Internal(type, aHost, aPort, ""_ns, aUsername, aPassword,
   1703                               aProxyAuthorizationHeader,
   1704                               aConnectionIsolationKey, aFlags,
   1705                               aFailoverTimeout, aFailoverProxy, 0, aResult);
   1706 }
   1707 
   1708 NS_IMETHODIMP
   1709 nsProtocolProxyService::NewMASQUEProxyInfo(
   1710    const nsACString& aHost, int32_t aPort, const nsACString& aMasqueTemplate,
   1711    const nsACString& aProxyAuthorizationHeader,
   1712    const nsACString& aConnectionIsolationKey, uint32_t aFlags,
   1713    uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
   1714    nsIProxyInfo** aResult) {
   1715  return NewProxyInfo_Internal(kProxyType_MASQUE, aHost, aPort, aMasqueTemplate,
   1716                               ""_ns, ""_ns, aProxyAuthorizationHeader,
   1717                               aConnectionIsolationKey, aFlags,
   1718                               aFailoverTimeout, aFailoverProxy, 0, aResult);
   1719 }
   1720 
   1721 NS_IMETHODIMP
   1722 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo* aProxy, nsIURI* aURI,
   1723                                            nsresult aStatus,
   1724                                            nsIProxyInfo** aResult) {
   1725  // Failover is supported through a variety of methods including:
   1726  // * PAC scripts (PROXYCONFIG_PAC and PROXYCONFIG_WPAD)
   1727  // * System proxy
   1728  // * Extensions
   1729  // With extensions the mProxyConfig can be any type and the extension
   1730  // is still involved in the proxy filtering.  It may have also supplied
   1731  // any number of failover proxies.  We cannot determine what the mix is
   1732  // here, so we will attempt to get a failover regardless of the config
   1733  // type.  MANUAL configuration will not disable a proxy.
   1734 
   1735  // Verify that |aProxy| is one of our nsProxyInfo objects.
   1736  nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
   1737  NS_ENSURE_ARG(pi);
   1738  // OK, the QI checked out.  We can proceed.
   1739 
   1740  // Remember that this proxy is down.  If the user has manually configured some
   1741  // proxies we do not want to disable them.
   1742  if (mProxyConfig != PROXYCONFIG_MANUAL) {
   1743    DisableProxy(pi);
   1744  }
   1745 
   1746  // NOTE: At this point, we might want to prompt the user if we have
   1747  //       not already tried going DIRECT.  This is something that the
   1748  //       classic codebase supported; however, IE6 does not prompt.
   1749 
   1750  if (!pi->mNext) return NS_ERROR_NOT_AVAILABLE;
   1751 
   1752  LOG(("PAC failover from %s %s:%d to %s %s:%d\n", pi->mType, pi->mHost.get(),
   1753       pi->mPort, pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
   1754 
   1755  *aResult = do_AddRef(pi->mNext).take();
   1756  return NS_OK;
   1757 }
   1758 
   1759 namespace {  // anon
   1760 
   1761 class ProxyFilterPositionComparator {
   1762  using FilterLinkRef = RefPtr<nsProtocolProxyService::FilterLink>;
   1763 
   1764 public:
   1765  bool Equals(const FilterLinkRef& a, const FilterLinkRef& b) const {
   1766    return a->position == b->position;
   1767  }
   1768  bool LessThan(const FilterLinkRef& a, const FilterLinkRef& b) const {
   1769    return a->position < b->position;
   1770  }
   1771 };
   1772 
   1773 class ProxyFilterObjectComparator {
   1774  using FilterLinkRef = RefPtr<nsProtocolProxyService::FilterLink>;
   1775 
   1776 public:
   1777  bool Equals(const FilterLinkRef& link, const nsISupports* obj) const {
   1778    return obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->filter)) ||
   1779           obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->channelFilter));
   1780  }
   1781 };
   1782 
   1783 }  // namespace
   1784 
   1785 nsresult nsProtocolProxyService::InsertFilterLink(RefPtr<FilterLink>&& link) {
   1786  LOG(("nsProtocolProxyService::InsertFilterLink filter=%p", link.get()));
   1787 
   1788  if (mIsShutdown) {
   1789    return NS_ERROR_FAILURE;
   1790  }
   1791 
   1792  // If we add a new element with the same position as an existing one, we want
   1793  // to preserve the insertion order to avoid surprises.
   1794  mFilters.InsertElementSorted(link, ProxyFilterPositionComparator());
   1795 
   1796  NotifyProxyConfigChangedInternal();
   1797 
   1798  return NS_OK;
   1799 }
   1800 
   1801 NS_IMETHODIMP
   1802 nsProtocolProxyService::GetHasProxyFilterRegistered(bool* aResult) {
   1803  *aResult = !mFilters.IsEmpty();
   1804  return NS_OK;
   1805 }
   1806 
   1807 NS_IMETHODIMP
   1808 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter* filter,
   1809                                       uint32_t position) {
   1810  UnregisterFilter(filter);  // remove this filter if we already have it
   1811 
   1812  RefPtr<FilterLink> link = new FilterLink(position, filter);
   1813  return InsertFilterLink(std::move(link));
   1814 }
   1815 
   1816 NS_IMETHODIMP
   1817 nsProtocolProxyService::RegisterChannelFilter(
   1818    nsIProtocolProxyChannelFilter* channelFilter, uint32_t position) {
   1819  UnregisterChannelFilter(
   1820      channelFilter);  // remove this filter if we already have it
   1821 
   1822  RefPtr<FilterLink> link = new FilterLink(position, channelFilter);
   1823  return InsertFilterLink(std::move(link));
   1824 }
   1825 
   1826 nsresult nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject) {
   1827  LOG(("nsProtocolProxyService::RemoveFilterLink target=%p", givenObject));
   1828 
   1829  nsresult rv =
   1830      mFilters.RemoveElement(givenObject, ProxyFilterObjectComparator())
   1831          ? NS_OK
   1832          : NS_ERROR_UNEXPECTED;
   1833  if (NS_SUCCEEDED(rv)) {
   1834    NotifyProxyConfigChangedInternal();
   1835  }
   1836 
   1837  return rv;
   1838 }
   1839 
   1840 NS_IMETHODIMP
   1841 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter* filter) {
   1842  // QI to nsISupports so we can safely test object identity.
   1843  nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
   1844  return RemoveFilterLink(givenObject);
   1845 }
   1846 
   1847 NS_IMETHODIMP
   1848 nsProtocolProxyService::UnregisterChannelFilter(
   1849    nsIProtocolProxyChannelFilter* channelFilter) {
   1850  // QI to nsISupports so we can safely test object identity.
   1851  nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
   1852  return RemoveFilterLink(givenObject);
   1853 }
   1854 
   1855 NS_IMETHODIMP
   1856 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType) {
   1857  *aProxyConfigType = mProxyConfig;
   1858  return NS_OK;
   1859 }
   1860 
   1861 void nsProtocolProxyService::LoadHostFilters(const nsACString& aFilters) {
   1862  if (mIsShutdown) {
   1863    return;
   1864  }
   1865 
   1866  // check to see the owners flag? /!?/ TODO
   1867  if (mHostFiltersArray.Length() > 0) {
   1868    mHostFiltersArray.Clear();
   1869  }
   1870 
   1871  // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref
   1872  // string
   1873  mFilterLocalHosts = false;
   1874 
   1875  if (aFilters.IsEmpty()) {
   1876    return;
   1877  }
   1878 
   1879  //
   1880  // filter  = ( host | domain | ipaddr ["/" mask] ) [":" port]
   1881  // filters = filter *( "," LWS filter)
   1882  //
   1883  mozilla::Tokenizer t(aFilters);
   1884  mozilla::Tokenizer::Token token;
   1885  bool eof = false;
   1886  // while (*filters) {
   1887  while (!eof) {
   1888    // skip over spaces and ,
   1889    t.SkipWhites();
   1890    while (t.CheckChar(',')) {
   1891      t.SkipWhites();
   1892    }
   1893 
   1894    nsAutoCString portStr;
   1895    nsAutoCString hostStr;
   1896    nsAutoCString maskStr;
   1897    t.Record();
   1898 
   1899    bool parsingIPv6 = false;
   1900    bool parsingPort = false;
   1901    bool parsingMask = false;
   1902    while (t.Next(token)) {
   1903      if (token.Equals(mozilla::Tokenizer::Token::EndOfFile())) {
   1904        eof = true;
   1905        break;
   1906      }
   1907      if (token.Equals(mozilla::Tokenizer::Token::Char(',')) ||
   1908          token.Type() == mozilla::Tokenizer::TOKEN_WS) {
   1909        break;
   1910      }
   1911 
   1912      if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
   1913        parsingIPv6 = true;
   1914        continue;
   1915      }
   1916 
   1917      if (!parsingIPv6 && token.Equals(mozilla::Tokenizer::Token::Char(':'))) {
   1918        // Port is starting. Claim the previous as host.
   1919        if (parsingMask) {
   1920          t.Claim(maskStr);
   1921        } else {
   1922          t.Claim(hostStr);
   1923        }
   1924        t.Record();
   1925        parsingPort = true;
   1926        continue;
   1927      }
   1928 
   1929      if (token.Equals(mozilla::Tokenizer::Token::Char('/'))) {
   1930        t.Claim(hostStr);
   1931        t.Record();
   1932        parsingMask = true;
   1933        continue;
   1934      }
   1935 
   1936      if (token.Equals(mozilla::Tokenizer::Token::Char(']'))) {
   1937        parsingIPv6 = false;
   1938        continue;
   1939      }
   1940    }
   1941    if (!parsingPort && !parsingMask) {
   1942      t.Claim(hostStr);
   1943    } else if (parsingPort) {
   1944      t.Claim(portStr);
   1945    } else if (parsingMask) {
   1946      t.Claim(maskStr);
   1947    } else {
   1948      NS_WARNING("Could not parse this rule");
   1949      continue;
   1950    }
   1951 
   1952    if (hostStr.IsEmpty()) {
   1953      continue;
   1954    }
   1955 
   1956    // If the current host filter is "<local>", then all local (i.e.
   1957    // no dots in the hostname) hosts should bypass the proxy
   1958    if (hostStr.EqualsIgnoreCase("<local>")) {
   1959      mFilterLocalHosts = true;
   1960      LOG(
   1961          ("loaded filter for local hosts "
   1962           "(plain host names, no dots)\n"));
   1963      // Continue to next host filter;
   1964      continue;
   1965    }
   1966 
   1967    // For all other host filters, create HostInfo object and add to list
   1968    HostInfo* hinfo = new HostInfo();
   1969    nsresult rv = NS_OK;
   1970 
   1971    int32_t port = portStr.ToInteger(&rv);
   1972    if (NS_FAILED(rv)) {
   1973      port = 0;
   1974    }
   1975    hinfo->port = port;
   1976 
   1977    int32_t maskLen = maskStr.ToInteger(&rv);
   1978    if (NS_FAILED(rv)) {
   1979      maskLen = 128;
   1980    }
   1981 
   1982    // PR_StringToNetAddr can't parse brackets enclosed IPv6
   1983    nsAutoCString addrString = hostStr;
   1984    if (hostStr.First() == '[' && hostStr.Last() == ']') {
   1985      addrString = Substring(hostStr, 1, hostStr.Length() - 2);
   1986    }
   1987 
   1988    PRNetAddr addr;
   1989    if (PR_StringToNetAddr(addrString.get(), &addr) == PR_SUCCESS) {
   1990      hinfo->is_ipaddr = true;
   1991      hinfo->ip.family = PR_AF_INET6;  // we always store address as IPv6
   1992      hinfo->ip.mask_len = maskLen;
   1993 
   1994      if (hinfo->ip.mask_len == 0) {
   1995        NS_WARNING("invalid mask");
   1996        goto loser;
   1997      }
   1998 
   1999      if (addr.raw.family == PR_AF_INET) {
   2000        // convert to IPv4-mapped address
   2001        PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
   2002        // adjust mask_len accordingly
   2003        if (hinfo->ip.mask_len <= 32) hinfo->ip.mask_len += 96;
   2004      } else if (addr.raw.family == PR_AF_INET6) {
   2005        // copy the address
   2006        memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
   2007      } else {
   2008        NS_WARNING("unknown address family");
   2009        goto loser;
   2010      }
   2011 
   2012      // apply mask to IPv6 address
   2013      proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
   2014    } else {
   2015      nsAutoCString host;
   2016      if (hostStr.First() == '*') {
   2017        host = Substring(hostStr, 1);
   2018      } else {
   2019        host = hostStr;
   2020      }
   2021 
   2022      if (host.IsEmpty()) {
   2023        hinfo->name.host = nullptr;
   2024        goto loser;
   2025      }
   2026 
   2027      hinfo->name.host_len = host.Length();
   2028 
   2029      hinfo->is_ipaddr = false;
   2030      hinfo->name.host = ToNewCString(host, mozilla::fallible);
   2031 
   2032      if (!hinfo->name.host) goto loser;
   2033    }
   2034 
   2035 // #define DEBUG_DUMP_FILTERS
   2036 #ifdef DEBUG_DUMP_FILTERS
   2037    printf("loaded filter[%zu]:\n", mHostFiltersArray.Length());
   2038    printf("  is_ipaddr = %u\n", hinfo->is_ipaddr);
   2039    printf("  port = %u\n", hinfo->port);
   2040    printf("  host = %s\n", hostStr.get());
   2041    if (hinfo->is_ipaddr) {
   2042      printf("  ip.family = %x\n", hinfo->ip.family);
   2043      printf("  ip.mask_len = %u\n", hinfo->ip.mask_len);
   2044 
   2045      PRNetAddr netAddr;
   2046      PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
   2047      memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
   2048 
   2049      char buf[256];
   2050      PR_NetAddrToString(&netAddr, buf, sizeof(buf));
   2051 
   2052      printf("  ip.addr = %s\n", buf);
   2053    } else {
   2054      printf("  name.host = %s\n", hinfo->name.host);
   2055    }
   2056 #endif
   2057 
   2058    mHostFiltersArray.AppendElement(hinfo);
   2059    hinfo = nullptr;
   2060  loser:
   2061    delete hinfo;
   2062  }
   2063 }
   2064 
   2065 nsresult nsProtocolProxyService::GetProtocolInfo(nsIURI* uri,
   2066                                                 nsProtocolInfo* info) {
   2067  AssertIsOnMainThread();
   2068  MOZ_ASSERT(uri, "URI is null");
   2069  MOZ_ASSERT(info, "info is null");
   2070 
   2071  nsresult rv;
   2072 
   2073  rv = uri->GetScheme(info->scheme);
   2074  if (NS_FAILED(rv)) return rv;
   2075 
   2076  nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
   2077  if (NS_FAILED(rv)) return rv;
   2078 
   2079  rv = ios->GetDynamicProtocolFlags(uri, &info->flags);
   2080  if (NS_FAILED(rv)) return rv;
   2081 
   2082  rv = ios->GetDefaultPort(info->scheme.get(), &info->defaultPort);
   2083  return rv;
   2084 }
   2085 
   2086 nsresult nsProtocolProxyService::NewProxyInfo_Internal(
   2087    const char* aType, const nsACString& aHost, int32_t aPort,
   2088    const nsACString& aMasqueTemplate, const nsACString& aUsername,
   2089    const nsACString& aPassword, const nsACString& aProxyAuthorizationHeader,
   2090    const nsACString& aConnectionIsolationKey, uint32_t aFlags,
   2091    uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
   2092    uint32_t aResolveFlags, nsIProxyInfo** aResult) {
   2093  if (aPort <= 0) aPort = -1;
   2094 
   2095  nsCOMPtr<nsProxyInfo> failover;
   2096  if (aFailoverProxy) {
   2097    failover = do_QueryInterface(aFailoverProxy);
   2098    NS_ENSURE_ARG(failover);
   2099  }
   2100 
   2101  RefPtr<nsProxyInfo> proxyInfo = new nsProxyInfo();
   2102 
   2103  proxyInfo->mType = aType;
   2104  proxyInfo->mHost = aHost;
   2105  proxyInfo->mPort = aPort;
   2106  proxyInfo->mMasqueTemplate = aMasqueTemplate;
   2107  proxyInfo->mUsername = aUsername;
   2108  proxyInfo->mPassword = aPassword;
   2109  proxyInfo->mFlags = aFlags;
   2110  proxyInfo->mResolveFlags = aResolveFlags;
   2111  proxyInfo->mTimeout =
   2112      aFailoverTimeout == UINT32_MAX ? mFailedProxyTimeout : aFailoverTimeout;
   2113  proxyInfo->mProxyAuthorizationHeader = aProxyAuthorizationHeader;
   2114  proxyInfo->mConnectionIsolationKey = aConnectionIsolationKey;
   2115  failover.swap(proxyInfo->mNext);
   2116 
   2117  proxyInfo.forget(aResult);
   2118  return NS_OK;
   2119 }
   2120 
   2121 const char* nsProtocolProxyService::SOCKSProxyType() {
   2122  if (mSOCKSProxyVersion == nsIProxyInfo::SOCKS_V4) {
   2123    return kProxyType_SOCKS4;
   2124  }
   2125  return kProxyType_SOCKS;
   2126 }
   2127 
   2128 bool nsProtocolProxyService::SOCKSRemoteDNS() {
   2129  return (mSOCKSProxyVersion == nsIProxyInfo::SOCKS_V4 &&
   2130          mSOCKS4ProxyRemoteDNS) ||
   2131         (mSOCKSProxyVersion == nsIProxyInfo::SOCKS_V5 &&
   2132          mSOCKS5ProxyRemoteDNS);
   2133 }
   2134 
   2135 nsresult nsProtocolProxyService::Resolve_Internal(nsIChannel* channel,
   2136                                                  const nsProtocolInfo& info,
   2137                                                  uint32_t flags,
   2138                                                  bool* usePACThread,
   2139                                                  nsIProxyInfo** result) {
   2140  LOG(("nsProtocolProxyService::Resolve_Internal"));
   2141  NS_ENSURE_ARG_POINTER(channel);
   2142 
   2143  *usePACThread = false;
   2144  *result = nullptr;
   2145 
   2146  if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY)) {
   2147    return NS_OK;  // Can't proxy this (filters may not override)
   2148  }
   2149 
   2150  nsCOMPtr<nsIURI> uri;
   2151  nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
   2152  if (NS_FAILED(rv)) return rv;
   2153 
   2154  // See bug #586908.
   2155  // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
   2156  // here means that we will not use a proxy for this connection.
   2157  if (mPACMan && mPACMan->IsPACURI(uri)) return NS_OK;
   2158 
   2159  // if proxies are enabled and this host:port combo is supposed to use a
   2160  // proxy, check for a proxy.
   2161  if ((mProxyConfig == PROXYCONFIG_DIRECT) ||
   2162      !CanUseProxy(uri, info.defaultPort)) {
   2163    return NS_OK;
   2164  }
   2165 
   2166  bool mainThreadOnly;
   2167  if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM &&
   2168      NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
   2169      !mainThreadOnly) {
   2170    *usePACThread = true;
   2171    return NS_OK;
   2172  }
   2173 
   2174  if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) {
   2175    // If the system proxy setting implementation is not threadsafe (e.g
   2176    // linux gconf), we'll do it inline here. Such implementations promise
   2177    // not to block
   2178    // bug 1366133: this block uses GetPACURI & GetProxyForURI, which may
   2179    // hang on Windows platform. Fortunately, current implementation on
   2180    // Windows is not main thread only, so we are safe here.
   2181 
   2182    nsAutoCString PACURI;
   2183    nsAutoCString pacString;
   2184 
   2185    if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
   2186        !PACURI.IsEmpty()) {
   2187      // There is a PAC URI configured. If it is unchanged, then
   2188      // just execute the PAC thread. If it is changed then load
   2189      // the new value
   2190 
   2191      if (mPACMan && mPACMan->IsPACURI(PACURI)) {
   2192        // unchanged
   2193        *usePACThread = true;
   2194        return NS_OK;
   2195      }
   2196 
   2197      ConfigureFromPAC(PACURI, false);
   2198      return NS_OK;
   2199    }
   2200 
   2201    nsAutoCString spec;
   2202    nsAutoCString host;
   2203    nsAutoCString scheme;
   2204    int32_t port = -1;
   2205 
   2206    uri->GetAsciiSpec(spec);
   2207    uri->GetAsciiHost(host);
   2208    uri->GetScheme(scheme);
   2209    uri->GetPort(&port);
   2210 
   2211    if (flags & RESOLVE_PREFER_SOCKS_PROXY) {
   2212      LOG(("Ignoring RESOLVE_PREFER_SOCKS_PROXY for system proxy setting\n"));
   2213    } else if (flags & RESOLVE_PREFER_HTTPS_PROXY) {
   2214      scheme.AssignLiteral("https");
   2215    } else if (flags & RESOLVE_IGNORE_URI_SCHEME) {
   2216      scheme.AssignLiteral("http");
   2217    }
   2218 
   2219    // now try the system proxy settings for this particular url
   2220    if (NS_SUCCEEDED(mSystemProxySettings->GetProxyForURI(spec, scheme, host,
   2221                                                          port, pacString))) {
   2222      nsCOMPtr<nsIProxyInfo> pi;
   2223      ProcessPACString(pacString, 0, getter_AddRefs(pi));
   2224 
   2225      if (flags & RESOLVE_PREFER_SOCKS_PROXY &&
   2226          flags & RESOLVE_PREFER_HTTPS_PROXY) {
   2227        nsAutoCString type;
   2228        pi->GetType(type);
   2229        // DIRECT from ProcessPACString indicates that system proxy settings
   2230        // are not configured to use SOCKS proxy. Try https proxy as a
   2231        // secondary preferrable proxy. This is mainly for websocket whose
   2232        // proxy precedence is SOCKS > HTTPS > DIRECT.
   2233        if (type.EqualsLiteral(kProxyType_DIRECT)) {
   2234          scheme.AssignLiteral(kProxyType_HTTPS);
   2235          if (NS_SUCCEEDED(mSystemProxySettings->GetProxyForURI(
   2236                  spec, scheme, host, port, pacString))) {
   2237            ProcessPACString(pacString, 0, getter_AddRefs(pi));
   2238          }
   2239        }
   2240      }
   2241      pi.forget(result);
   2242      return NS_OK;
   2243    }
   2244  }
   2245 
   2246  // if proxies are enabled and this host:port combo is supposed to use a
   2247  // proxy, check for a proxy.
   2248  if (mProxyConfig == PROXYCONFIG_DIRECT ||
   2249      (mProxyConfig == PROXYCONFIG_MANUAL &&
   2250       !CanUseProxy(uri, info.defaultPort))) {
   2251    return NS_OK;
   2252  }
   2253 
   2254  // Proxy auto config magic...
   2255  if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD ||
   2256      StaticPrefs::network_proxy_system_wpad()) {
   2257    // Do not query PAC now.
   2258    *usePACThread = true;
   2259    return NS_OK;
   2260  }
   2261 
   2262  // If we aren't in manual proxy configuration mode then we don't
   2263  // want to honor any manual specific prefs that might be still set
   2264  if (mProxyConfig != PROXYCONFIG_MANUAL) return NS_OK;
   2265 
   2266  // proxy info values for manual configuration mode
   2267  const char* type = nullptr;
   2268  const nsACString* host = nullptr;
   2269  int32_t port = -1;
   2270 
   2271  uint32_t proxyFlags = 0;
   2272 
   2273  if ((flags & RESOLVE_PREFER_SOCKS_PROXY) && !mSOCKSProxyTarget.IsEmpty() &&
   2274      (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
   2275    host = &mSOCKSProxyTarget;
   2276    type = SOCKSProxyType();
   2277    if (SOCKSRemoteDNS()) {
   2278      proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
   2279    }
   2280    port = mSOCKSProxyPort;
   2281  } else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
   2282             !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
   2283    host = &mHTTPSProxyHost;
   2284    type = kProxyType_HTTP;
   2285    port = mHTTPSProxyPort;
   2286  } else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
   2287             ((flags & RESOLVE_IGNORE_URI_SCHEME) ||
   2288              info.scheme.EqualsLiteral("http"))) {
   2289    host = &mHTTPProxyHost;
   2290    type = kProxyType_HTTP;
   2291    port = mHTTPProxyPort;
   2292  } else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
   2293             !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
   2294             info.scheme.EqualsLiteral("https")) {
   2295    host = &mHTTPSProxyHost;
   2296    type = kProxyType_HTTP;
   2297    port = mHTTPSProxyPort;
   2298  } else if (!mSOCKSProxyTarget.IsEmpty() &&
   2299             (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
   2300    host = &mSOCKSProxyTarget;
   2301    type = SOCKSProxyType();
   2302    if (SOCKSRemoteDNS()) {
   2303      proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
   2304    }
   2305    port = mSOCKSProxyPort;
   2306  }
   2307 
   2308  if (type) {
   2309    rv = NewProxyInfo_Internal(type, *host, port, ""_ns, ""_ns, ""_ns, ""_ns,
   2310                               ""_ns, proxyFlags, UINT32_MAX, nullptr, flags,
   2311                               result);
   2312    if (NS_FAILED(rv)) return rv;
   2313  }
   2314 
   2315  return NS_OK;
   2316 }
   2317 
   2318 void nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo* aProxy) {
   2319  // Disable Prefetch in the DNS service if a proxy is in use.
   2320  if (!aProxy) return;
   2321 
   2322  nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
   2323  if (!pi || !pi->mType || pi->mType == kProxyType_DIRECT) return;
   2324 
   2325  if (StaticPrefs::network_dns_prefetch_via_proxy()) {
   2326    return;
   2327  }
   2328 
   2329  // To avoid getting DNS service recursively, we directly use
   2330  // GetXPCOMSingleton().
   2331  nsCOMPtr<nsIDNSService> dns = nsDNSService::GetXPCOMSingleton();
   2332  if (!dns) return;
   2333  nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
   2334  if (!pdns) return;
   2335 
   2336  // We lose the prefetch optimization for the life of the dns service.
   2337  pdns->SetPrefetchEnabled(false);
   2338 }
   2339 
   2340 void nsProtocolProxyService::CopyFilters(nsTArray<RefPtr<FilterLink>>& aCopy) {
   2341  MOZ_ASSERT(aCopy.Length() == 0);
   2342  aCopy.AppendElements(mFilters);
   2343 }
   2344 
   2345 bool nsProtocolProxyService::ApplyFilter(
   2346    FilterLink const* filterLink, nsIChannel* channel,
   2347    const nsProtocolInfo& info, nsCOMPtr<nsIProxyInfo> list,
   2348    nsIProxyProtocolFilterResult* callback) {
   2349  nsresult rv;
   2350 
   2351  // We prune the proxy list prior to invoking each filter.  This may be
   2352  // somewhat inefficient, but it seems like a good idea since we want each
   2353  // filter to "see" a valid proxy list.
   2354  PruneProxyInfo(info, list);
   2355 
   2356  if (filterLink->filter) {
   2357    nsCOMPtr<nsIURI> uri;
   2358    (void)GetProxyURI(channel, getter_AddRefs(uri));
   2359    if (!uri) {
   2360      return false;
   2361    }
   2362 
   2363    rv = filterLink->filter->ApplyFilter(uri, list, callback);
   2364    return NS_SUCCEEDED(rv);
   2365  }
   2366 
   2367  if (filterLink->channelFilter) {
   2368    rv = filterLink->channelFilter->ApplyFilter(channel, list, callback);
   2369    return NS_SUCCEEDED(rv);
   2370  }
   2371 
   2372  return false;
   2373 }
   2374 
   2375 void nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo& info,
   2376                                            nsIProxyInfo** list) {
   2377  if (!*list) return;
   2378 
   2379  LOG(("nsProtocolProxyService::PruneProxyInfo ENTER list=%p", *list));
   2380 
   2381  nsProxyInfo* head = nullptr;
   2382  CallQueryInterface(*list, &head);
   2383  if (!head) {
   2384    MOZ_ASSERT_UNREACHABLE("nsIProxyInfo must QI to nsProxyInfo");
   2385    return;
   2386  }
   2387  NS_RELEASE(*list);
   2388 
   2389  // Pruning of disabled proxies works like this:
   2390  //   - If all proxies are disabled, return the full list
   2391  //   - Otherwise, remove the disabled proxies.
   2392  //
   2393  // Pruning of disallowed proxies works like this:
   2394  //   - If the protocol handler disallows the proxy, then we disallow it.
   2395 
   2396  // Start by removing all disallowed proxies if required:
   2397  if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
   2398    nsProxyInfo *last = nullptr, *iter = head;
   2399    while (iter) {
   2400      if ((iter->Type() == kProxyType_HTTP) ||
   2401          (iter->Type() == kProxyType_HTTPS)) {
   2402        // reject!
   2403        if (last) {
   2404          last->mNext = iter->mNext;
   2405        } else {
   2406          head = iter->mNext;
   2407        }
   2408        nsProxyInfo* next = iter->mNext;
   2409        iter->mNext = nullptr;
   2410        iter->Release();
   2411        iter = next;
   2412      } else {
   2413        last = iter;
   2414        iter = iter->mNext;
   2415      }
   2416    }
   2417    if (!head) {
   2418      return;
   2419    }
   2420  }
   2421 
   2422  // Scan to see if all remaining non-direct proxies are disabled. If so, then
   2423  // we'll just bail and return them all.  Otherwise, we'll go and prune the
   2424  // disabled ones.
   2425 
   2426  bool allNonDirectProxiesDisabled = true;
   2427 
   2428  nsProxyInfo* iter;
   2429  for (iter = head; iter; iter = iter->mNext) {
   2430    if (!IsProxyDisabled(iter) && iter->mType != kProxyType_DIRECT) {
   2431      allNonDirectProxiesDisabled = false;
   2432      break;
   2433    }
   2434  }
   2435 
   2436  if (allNonDirectProxiesDisabled &&
   2437      StaticPrefs::network_proxy_retry_failed_proxies()) {
   2438    LOG(("All proxies are disabled, so trying all again"));
   2439  } else {
   2440    // remove any disabled proxies.
   2441    nsProxyInfo* last = nullptr;
   2442    for (iter = head; iter;) {
   2443      if (IsProxyDisabled(iter)) {
   2444        // reject!
   2445        nsProxyInfo* reject = iter;
   2446 
   2447        iter = iter->mNext;
   2448        if (last) {
   2449          last->mNext = iter;
   2450        } else {
   2451          head = iter;
   2452        }
   2453 
   2454        reject->mNext = nullptr;
   2455        NS_RELEASE(reject);
   2456        continue;
   2457      }
   2458 
   2459      // since we are about to use this proxy, make sure it is not on
   2460      // the disabled proxy list.  we'll add it back to that list if
   2461      // we have to (in GetFailoverForProxy).
   2462      //
   2463      // XXX(darin): It might be better to do this as a final pass.
   2464      //
   2465      EnableProxy(iter);
   2466 
   2467      last = iter;
   2468      iter = iter->mNext;
   2469    }
   2470  }
   2471 
   2472  // if only DIRECT was specified then return no proxy info, and we're done.
   2473  if (head && !head->mNext && head->mType == kProxyType_DIRECT) {
   2474    NS_RELEASE(head);
   2475  }
   2476 
   2477  *list = head;  // Transfer ownership
   2478 
   2479  LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list));
   2480 }
   2481 
   2482 bool nsProtocolProxyService::GetIsPACLoading() {
   2483  return mPACMan && mPACMan->IsLoading();
   2484 }
   2485 
   2486 NS_IMETHODIMP
   2487 nsProtocolProxyService::AddProxyConfigCallback(
   2488    nsIProxyConfigChangedCallback* aCallback) {
   2489  MOZ_ASSERT(NS_IsMainThread());
   2490  if (!aCallback) {
   2491    return NS_ERROR_INVALID_ARG;
   2492  }
   2493 
   2494  mProxyConfigChangedCallbacks.AppendElement(aCallback);
   2495  return NS_OK;
   2496 }
   2497 
   2498 NS_IMETHODIMP
   2499 nsProtocolProxyService::RemoveProxyConfigCallback(
   2500    nsIProxyConfigChangedCallback* aCallback) {
   2501  MOZ_ASSERT(NS_IsMainThread());
   2502 
   2503  mProxyConfigChangedCallbacks.RemoveElement(aCallback);
   2504  return NS_OK;
   2505 }
   2506 
   2507 NS_IMETHODIMP
   2508 nsProtocolProxyService::NotifyProxyConfigChangedInternal() {
   2509  LOG(("nsProtocolProxyService::NotifyProxyConfigChangedInternal"));
   2510  MOZ_ASSERT(NS_IsMainThread());
   2511 
   2512  for (const auto& callback : mProxyConfigChangedCallbacks) {
   2513    callback->OnProxyConfigChanged();
   2514  }
   2515  return NS_OK;
   2516 }
   2517 
   2518 }  // namespace net
   2519 }  // namespace mozilla