netwerk/base/src/nsProtocolProxyService.cpp

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

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

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

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:set ts=4 sw=4 sts=4 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/. */
     7 #include "mozilla/ArrayUtils.h"
     8 #include "mozilla/Attributes.h"
    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 "nsIDNSService.h"
    20 #include "nsPIDNSService.h"
    21 #include "nsIPrefService.h"
    22 #include "nsIPrefBranch.h"
    23 #include "nsThreadUtils.h"
    24 #include "nsString.h"
    25 #include "nsNetUtil.h"
    26 #include "nsNetCID.h"
    27 #include "prnetdb.h"
    28 #include "nsPACMan.h"
    29 #include "nsProxyRelease.h"
    30 #include "mozilla/Mutex.h"
    31 #include "mozilla/CondVar.h"
    32 #include "nsISystemProxySettings.h"
    34 //----------------------------------------------------------------------------
    36 namespace mozilla {
    37   extern const char kProxyType_HTTP[];
    38   extern const char kProxyType_SOCKS[];
    39   extern const char kProxyType_SOCKS4[];
    40   extern const char kProxyType_SOCKS5[];
    41   extern const char kProxyType_DIRECT[];
    42 }
    44 using namespace mozilla;
    46 #include "prlog.h"
    47 #if defined(PR_LOGGING)
    48 #endif
    49 #undef LOG
    50 #define LOG(args) PR_LOG(net::GetProxyLog(), PR_LOG_DEBUG, args)
    52 //----------------------------------------------------------------------------
    54 #define PROXY_PREF_BRANCH  "network.proxy"
    55 #define PROXY_PREF(x)      PROXY_PREF_BRANCH "." x
    57 #define WPAD_URL "http://wpad/wpad.dat"
    59 //----------------------------------------------------------------------------
    61 // This structure is intended to be allocated on the stack
    62 struct nsProtocolInfo {
    63     nsAutoCString scheme;
    64     uint32_t flags;
    65     int32_t defaultPort;
    66 };
    68 //----------------------------------------------------------------------------
    70 // The nsPACManCallback portion of this implementation should be run
    71 // on the main thread - so call nsPACMan::AsyncGetProxyForChannel() with
    72 // a true mainThreadResponse parameter.
    73 class nsAsyncResolveRequest MOZ_FINAL : public nsIRunnable
    74                                       , public nsPACManCallback
    75                                       , public nsICancelable
    76 {
    77 public:
    78     NS_DECL_THREADSAFE_ISUPPORTS
    80     nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIChannel *channel,
    81                           uint32_t aResolveFlags,
    82                           nsIProtocolProxyCallback *callback)
    83         : mStatus(NS_OK)
    84         , mDispatched(false)
    85         , mResolveFlags(aResolveFlags)
    86         , mPPS(pps)
    87         , mXPComPPS(pps)
    88         , mChannel(channel)
    89         , mCallback(callback)
    90     {
    91         NS_ASSERTION(mCallback, "null callback");
    92     }
    94     ~nsAsyncResolveRequest()
    95     {
    96         if (!NS_IsMainThread()) {
    97             // these xpcom pointers might need to be proxied back to the
    98             // main thread to delete safely, but if this request had its
    99             // callbacks called normally they will all be null and this is a nop
   101             nsCOMPtr<nsIThread> mainThread;
   102             NS_GetMainThread(getter_AddRefs(mainThread));
   104             if (mChannel) {
   105                 nsIChannel *forgettable;
   106                 mChannel.forget(&forgettable);
   107                 NS_ProxyRelease(mainThread, forgettable, false);
   108             }
   110             if (mCallback) {
   111                 nsIProtocolProxyCallback *forgettable;
   112                 mCallback.forget(&forgettable);
   113                 NS_ProxyRelease(mainThread, forgettable, false);
   114             }
   116             if (mProxyInfo) {
   117                 nsIProxyInfo *forgettable;
   118                 mProxyInfo.forget(&forgettable);
   119                 NS_ProxyRelease(mainThread, forgettable, false);
   120             }
   122             if (mXPComPPS) {
   123                 nsIProtocolProxyService *forgettable;
   124                 mXPComPPS.forget(&forgettable);
   125                 NS_ProxyRelease(mainThread, forgettable, false);
   126             }
   127         }
   128     }
   130     void SetResult(nsresult status, nsIProxyInfo *pi)
   131     {
   132         mStatus = status;
   133         mProxyInfo = pi;
   134     }
   136     NS_IMETHOD Run()
   137     {
   138         if (mCallback)
   139             DoCallback();
   140         return NS_OK;
   141     }
   143     NS_IMETHOD Cancel(nsresult reason)
   144     {
   145         NS_ENSURE_ARG(NS_FAILED(reason));
   147         // If we've already called DoCallback then, nothing more to do.
   148         if (!mCallback)
   149             return NS_OK;
   151         SetResult(reason, nullptr);
   152         return DispatchCallback();
   153     }
   155     nsresult DispatchCallback()
   156     {
   157         if (mDispatched)  // Only need to dispatch once
   158             return NS_OK;
   160         nsresult rv = NS_DispatchToCurrentThread(this);
   161         if (NS_FAILED(rv))
   162             NS_WARNING("unable to dispatch callback event");
   163         else {
   164             mDispatched = true;
   165             return NS_OK;
   166         }
   168         mCallback = nullptr;  // break possible reference cycle
   169         return rv;
   170     }
   172 private:
   174     // Called asynchronously, so we do not need to post another PLEvent
   175     // before calling DoCallback.
   176     void OnQueryComplete(nsresult status,
   177                          const nsCString &pacString,
   178                          const nsCString &newPACURL)
   179     {
   180         // If we've already called DoCallback then, nothing more to do.
   181         if (!mCallback)
   182             return;
   184         // Provided we haven't been canceled...
   185         if (mStatus == NS_OK) {
   186             mStatus = status;
   187             mPACString = pacString;
   188             mPACURL = newPACURL;
   189         }
   191         // In the cancelation case, we may still have another PLEvent in
   192         // the queue that wants to call DoCallback.  No need to wait for
   193         // it, just run the callback now.
   194         DoCallback();
   195     }
   197     void DoCallback()
   198     {
   199         if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) {
   200             // If the PAC service is not avail (e.g. failed pac load
   201             // or shutdown) then we will be going direct. Make that
   202             // mapping now so that any filters are still applied.
   203             mPACString = NS_LITERAL_CSTRING("DIRECT;");
   204             mStatus = NS_OK;
   205         }
   207         // Generate proxy info from the PAC string if appropriate
   208         if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
   209             mPPS->ProcessPACString(mPACString, mResolveFlags,
   210                                    getter_AddRefs(mProxyInfo));
   211             nsCOMPtr<nsIURI> uri;
   212             mChannel->GetURI(getter_AddRefs(uri));
   214             // Now apply proxy filters
   215             nsProtocolInfo info;
   216             mStatus = mPPS->GetProtocolInfo(uri, &info);
   217             if (NS_SUCCEEDED(mStatus))
   218                 mPPS->ApplyFilters(mChannel, info, mProxyInfo);
   219             else
   220                 mProxyInfo = nullptr;
   222             LOG(("pac thread callback %s\n", mPACString.get()));
   223             if (NS_SUCCEEDED(mStatus))
   224                 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
   225             mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
   226         }
   227         else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
   228             LOG(("pac thread callback indicates new pac file load\n"));
   230             // trigger load of new pac url
   231             nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
   232             if (NS_SUCCEEDED(rv)) {
   233                 // now that the load is triggered, we can resubmit the query
   234                 nsRefPtr<nsAsyncResolveRequest> newRequest =
   235                     new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback);
   236                 rv = mPPS->mPACMan->AsyncGetProxyForChannel(mChannel, newRequest, true);
   237             }
   239             if (NS_FAILED(rv))
   240                 mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
   242             // do not call onproxyavailable() in SUCCESS case - the newRequest will
   243             // take care of that
   244         }
   245         else {
   246             LOG(("pac thread callback did not provide information %X\n", mStatus));
   247             if (NS_SUCCEEDED(mStatus))
   248                 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
   249             mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
   250         }
   252         // We are on the main thread now and don't need these any more so
   253         // release them to avoid having to proxy them back to the main thread
   254         // in the dtor
   255         mCallback = nullptr;  // in case the callback holds an owning ref to us
   256         mPPS = nullptr;
   257         mXPComPPS = nullptr;
   258         mChannel = nullptr;
   259         mProxyInfo = nullptr;
   260     }
   262 private:
   264     nsresult  mStatus;
   265     nsCString mPACString;
   266     nsCString mPACURL;
   267     bool      mDispatched;
   268     uint32_t  mResolveFlags;
   270     nsProtocolProxyService            *mPPS;
   271     nsCOMPtr<nsIProtocolProxyService>  mXPComPPS;
   272     nsCOMPtr<nsIChannel>               mChannel;
   273     nsCOMPtr<nsIProtocolProxyCallback> mCallback;
   274     nsCOMPtr<nsIProxyInfo>             mProxyInfo;
   275 };
   277 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
   279 //----------------------------------------------------------------------------
   281 #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t')
   283 //
   284 // apply mask to address (zeros out excluded bits).
   285 //
   286 // NOTE: we do the byte swapping here to minimize overall swapping.
   287 //
   288 static void
   289 proxy_MaskIPv6Addr(PRIPv6Addr &addr, uint16_t mask_len)
   290 {
   291     if (mask_len == 128)
   292         return;
   294     if (mask_len > 96) {
   295         addr.pr_s6_addr32[3] = PR_htonl(
   296                 PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len)));
   297     }
   298     else if (mask_len > 64) {
   299         addr.pr_s6_addr32[3] = 0;
   300         addr.pr_s6_addr32[2] = PR_htonl(
   301                 PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len)));
   302     }
   303     else if (mask_len > 32) {
   304         addr.pr_s6_addr32[3] = 0;
   305         addr.pr_s6_addr32[2] = 0;
   306         addr.pr_s6_addr32[1] = PR_htonl(
   307                 PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len)));
   308     }
   309     else {
   310         addr.pr_s6_addr32[3] = 0;
   311         addr.pr_s6_addr32[2] = 0;
   312         addr.pr_s6_addr32[1] = 0;
   313         addr.pr_s6_addr32[0] = PR_htonl(
   314                 PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
   315     }
   316 }
   318 static void
   319 proxy_GetStringPref(nsIPrefBranch *aPrefBranch,
   320                     const char    *aPref,
   321                     nsCString     &aResult)
   322 {
   323     nsXPIDLCString temp;
   324     nsresult rv = aPrefBranch->GetCharPref(aPref, getter_Copies(temp));
   325     if (NS_FAILED(rv))
   326         aResult.Truncate();
   327     else {
   328         aResult.Assign(temp);
   329         // all of our string prefs are hostnames, so we should remove any
   330         // whitespace characters that the user might have unknowingly entered.
   331         aResult.StripWhitespace();
   332     }
   333 }
   335 static void
   336 proxy_GetIntPref(nsIPrefBranch *aPrefBranch,
   337                  const char    *aPref,
   338                  int32_t       &aResult)
   339 {
   340     int32_t temp;
   341     nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
   342     if (NS_FAILED(rv)) 
   343         aResult = -1;
   344     else
   345         aResult = temp;
   346 }
   348 static void
   349 proxy_GetBoolPref(nsIPrefBranch *aPrefBranch,
   350                  const char    *aPref,
   351                  bool          &aResult)
   352 {
   353     bool temp;
   354     nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
   355     if (NS_FAILED(rv)) 
   356         aResult = false;
   357     else
   358         aResult = temp;
   359 }
   361 //----------------------------------------------------------------------------
   363 static const int32_t PROXYCONFIG_DIRECT4X = 3;
   364 static const int32_t PROXYCONFIG_COUNT = 6;
   366 NS_IMPL_ADDREF(nsProtocolProxyService)
   367 NS_IMPL_RELEASE(nsProtocolProxyService)
   368 NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
   369                   NS_PROTOCOLPROXYSERVICE_CID)
   370 NS_IMPL_QUERY_INTERFACE_CI(nsProtocolProxyService,
   371                            nsIProtocolProxyService,
   372                            nsIProtocolProxyService2,
   373                            nsIObserver)
   374 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService,
   375                             nsIProtocolProxyService,
   376                             nsIProtocolProxyService2)
   378 nsProtocolProxyService::nsProtocolProxyService()
   379     : mFilterLocalHosts(false)
   380     , mFilters(nullptr)
   381     , mProxyConfig(PROXYCONFIG_DIRECT)
   382     , mHTTPProxyPort(-1)
   383     , mFTPProxyPort(-1)
   384     , mHTTPSProxyPort(-1)
   385     , mSOCKSProxyPort(-1)
   386     , mSOCKSProxyVersion(4)
   387     , mSOCKSProxyRemoteDNS(false)
   388     , mPACMan(nullptr)
   389     , mSessionStart(PR_Now())
   390     , mFailedProxyTimeout(30 * 60) // 30 minute default
   391 {
   392 }
   394 nsProtocolProxyService::~nsProtocolProxyService()
   395 {
   396     // These should have been cleaned up in our Observe method.
   397     NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters == nullptr &&
   398                  mPACMan == nullptr, "what happened to xpcom-shutdown?");
   399 }
   401 // nsProtocolProxyService methods
   402 nsresult
   403 nsProtocolProxyService::Init()
   404 {
   405     // failure to access prefs is non-fatal
   406     nsCOMPtr<nsIPrefBranch> prefBranch =
   407             do_GetService(NS_PREFSERVICE_CONTRACTID);
   408     if (prefBranch) {
   409         // monitor proxy prefs
   410         prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
   412         // read all prefs
   413         PrefsChanged(prefBranch, nullptr);
   414     }
   416     // register for shutdown notification so we can clean ourselves up properly.
   417     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   418     if (obs)
   419         obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   421     return NS_OK;
   422 }
   424 NS_IMETHODIMP
   425 nsProtocolProxyService::Observe(nsISupports     *aSubject,
   426                                 const char      *aTopic,
   427                                 const char16_t *aData)
   428 {
   429     if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
   430         // cleanup
   431         if (mHostFiltersArray.Length() > 0) {
   432             mHostFiltersArray.Clear();
   433         }
   434         if (mFilters) {
   435             delete mFilters;
   436             mFilters = nullptr;
   437         }
   438         if (mPACMan) {
   439             mPACMan->Shutdown();
   440             mPACMan = nullptr;
   441         }
   442     }
   443     else {
   444         NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
   445                      "what is this random observer event?");
   446         nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
   447         if (prefs)
   448             PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
   449     }
   450     return NS_OK;
   451 }
   453 void
   454 nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
   455                                      const char    *pref)
   456 {
   457     nsresult rv = NS_OK;
   458     bool reloadPAC = false;
   459     nsXPIDLCString tempString;
   461     if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
   462         int32_t type = -1;
   463         rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
   464         if (NS_SUCCEEDED(rv)) {
   465             // bug 115720 - for ns4.x backwards compatibility
   466             if (type == PROXYCONFIG_DIRECT4X) {
   467                 type = PROXYCONFIG_DIRECT;
   468                 // Reset the type so that the dialog looks correct, and we
   469                 // don't have to handle this case everywhere else
   470                 // I'm paranoid about a loop of some sort - only do this
   471                 // if we're enumerating all prefs, and ignore any error
   472                 if (!pref)
   473                     prefBranch->SetIntPref(PROXY_PREF("type"), type);
   474             } else if (type >= PROXYCONFIG_COUNT) {
   475                 LOG(("unknown proxy type: %lu; assuming direct\n", type));
   476                 type = PROXYCONFIG_DIRECT;
   477             }
   478             mProxyConfig = type;
   479             reloadPAC = true;
   480         }
   482         if (mProxyConfig == PROXYCONFIG_SYSTEM) {
   483             mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
   484             if (!mSystemProxySettings)
   485                 mProxyConfig = PROXYCONFIG_DIRECT;
   486             ResetPACThread();
   487         } else {
   488             if (mSystemProxySettings) {
   489                 mSystemProxySettings = nullptr;
   490                 ResetPACThread();
   491             }
   492         }
   493     }
   495     if (!pref || !strcmp(pref, PROXY_PREF("http")))
   496         proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
   498     if (!pref || !strcmp(pref, PROXY_PREF("http_port")))
   499         proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
   501     if (!pref || !strcmp(pref, PROXY_PREF("ssl")))
   502         proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
   504     if (!pref || !strcmp(pref, PROXY_PREF("ssl_port")))
   505         proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
   507     if (!pref || !strcmp(pref, PROXY_PREF("ftp")))
   508         proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost);
   510     if (!pref || !strcmp(pref, PROXY_PREF("ftp_port")))
   511         proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
   513     if (!pref || !strcmp(pref, PROXY_PREF("socks")))
   514         proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyHost);
   516     if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
   517         proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
   519     if (!pref || !strcmp(pref, PROXY_PREF("socks_username")))
   520         proxy_GetStringPref(prefBranch, PROXY_PREF("socks_username"), mSOCKSProxyUsername);
   522     if (!pref || !strcmp(pref, PROXY_PREF("socks_password")))
   523         proxy_GetStringPref(prefBranch, PROXY_PREF("socks_password"), mSOCKSProxyPassword);
   525     if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
   526         int32_t version;
   527         proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
   528         // make sure this preference value remains sane
   529         if (version == 5)
   530             mSOCKSProxyVersion = 5;
   531         else
   532             mSOCKSProxyVersion = 4;
   533     }
   535     if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns")))
   536         proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
   537                           mSOCKSProxyRemoteDNS);
   539     if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
   540         proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
   541                          mFailedProxyTimeout);
   543     if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
   544         rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
   545                                      getter_Copies(tempString));
   546         if (NS_SUCCEEDED(rv))
   547             LoadHostFilters(tempString.get());
   548     }
   550     // We're done if not using something that could give us a PAC URL
   551     // (PAC, WPAD or System)
   552     if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
   553         mProxyConfig != PROXYCONFIG_SYSTEM)
   554         return;
   556     // OK, we need to reload the PAC file if:
   557     //  1) network.proxy.type changed, or
   558     //  2) network.proxy.autoconfig_url changed and PAC is configured
   560     if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url")))
   561         reloadPAC = true;
   563     if (reloadPAC) {
   564         tempString.Truncate();
   565         if (mProxyConfig == PROXYCONFIG_PAC) {
   566             prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"),
   567                                     getter_Copies(tempString));
   568         } else if (mProxyConfig == PROXYCONFIG_WPAD) {
   569             // We diverge from the WPAD spec here in that we don't walk the
   570             // hosts's FQDN, stripping components until we hit a TLD.  Doing so
   571             // is dangerous in the face of an incomplete list of TLDs, and TLDs
   572             // get added over time.  We could consider doing only a single
   573             // substitution of the first component, if that proves to help
   574             // compatibility.
   575             tempString.AssignLiteral(WPAD_URL);
   576         } else if (mSystemProxySettings) {
   577             // Get System Proxy settings if available
   578             mSystemProxySettings->GetPACURI(tempString);
   579         }
   580         if (!tempString.IsEmpty())
   581             ConfigureFromPAC(tempString, false);
   582     }
   583 }
   585 bool
   586 nsProtocolProxyService::CanUseProxy(nsIURI *aURI, int32_t defaultPort) 
   587 {
   588     if (mHostFiltersArray.Length() == 0)
   589         return true;
   591     int32_t port;
   592     nsAutoCString host;
   594     nsresult rv = aURI->GetAsciiHost(host);
   595     if (NS_FAILED(rv) || host.IsEmpty())
   596         return false;
   598     rv = aURI->GetPort(&port);
   599     if (NS_FAILED(rv))
   600         return false;
   601     if (port == -1)
   602         port = defaultPort;
   604     PRNetAddr addr;
   605     bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
   607     PRIPv6Addr ipv6;
   608     if (is_ipaddr) {
   609         // convert parsed address to IPv6
   610         if (addr.raw.family == PR_AF_INET) {
   611             // convert to IPv4-mapped address
   612             PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
   613         }
   614         else if (addr.raw.family == PR_AF_INET6) {
   615             // copy the address
   616             memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
   617         }
   618         else {
   619             NS_WARNING("unknown address family");
   620             return true; // allow proxying
   621         }
   622     }
   624     // Don't use proxy for local hosts (plain hostname, no dots)
   625     if (!is_ipaddr && mFilterLocalHosts && (kNotFound == host.FindChar('.'))) {
   626         LOG(("Not using proxy for this local host [%s]!\n", host.get()));
   627         return false; // don't allow proxying
   628     }
   630     int32_t index = -1;
   631     while (++index < int32_t(mHostFiltersArray.Length())) {
   632         HostInfo *hinfo = mHostFiltersArray[index];
   634         if (is_ipaddr != hinfo->is_ipaddr)
   635             continue;
   636         if (hinfo->port && hinfo->port != port)
   637             continue;
   639         if (is_ipaddr) {
   640             // generate masked version of target IPv6 address
   641             PRIPv6Addr masked;
   642             memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
   643             proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
   645             // check for a match
   646             if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0)
   647                 return false; // proxy disallowed
   648         }
   649         else {
   650             uint32_t host_len = host.Length();
   651             uint32_t filter_host_len = hinfo->name.host_len;
   653             if (host_len >= filter_host_len) {
   654                 //
   655                 // compare last |filter_host_len| bytes of target hostname.
   656                 //
   657                 const char *host_tail = host.get() + host_len - filter_host_len;
   658                 if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len))
   659                     return false; // proxy disallowed
   660             }
   661         }
   662     }
   663     return true;
   664 }
   666 // kProxyType\* may be referred to externally in
   667 // nsProxyInfo in order to compare by string pointer
   668 namespace mozilla {
   669 const char kProxyType_HTTP[]    = "http";
   670 const char kProxyType_PROXY[]   = "proxy";
   671 const char kProxyType_SOCKS[]   = "socks";
   672 const char kProxyType_SOCKS4[]  = "socks4";
   673 const char kProxyType_SOCKS5[]  = "socks5";
   674 const char kProxyType_DIRECT[]  = "direct";
   675 const char kProxyType_UNKNOWN[] = "unknown";
   676 }
   678 const char *
   679 nsProtocolProxyService::ExtractProxyInfo(const char *start,
   680                                          uint32_t aResolveFlags,
   681                                          nsProxyInfo **result)
   682 {
   683     *result = nullptr;
   684     uint32_t flags = 0;
   686     // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
   688     // find end of proxy info delimiter
   689     const char *end = start;
   690     while (*end && *end != ';') ++end;
   692     // find end of proxy type delimiter
   693     const char *sp = start;
   694     while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
   696     uint32_t len = sp - start;
   697     const char *type = nullptr;
   698     switch (len) {
   699     case 5:
   700         if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0)
   701             type = kProxyType_HTTP;
   702         else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0)
   703             type = kProxyType_SOCKS4; // assume v4 for 4x compat
   704         break;
   705     case 6:
   706         if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0)
   707             type = kProxyType_DIRECT;
   708         else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0)
   709             type = kProxyType_SOCKS4;
   710         else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0)
   711             // map "SOCKS5" to "socks" to match contract-id of registered
   712             // SOCKS-v5 socket provider.
   713             type = kProxyType_SOCKS;
   714         break;
   715     }
   716     if (type) {
   717         const char *host = nullptr, *hostEnd = nullptr;
   718         int32_t port = -1;
   720         // If it's a SOCKS5 proxy, do name resolution on the server side.
   721         // We could use this with SOCKS4a servers too, but they might not
   722         // support it.
   723         if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS)
   724             flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
   726         // extract host:port
   727         start = sp;
   728         while ((*start == ' ' || *start == '\t') && start < end)
   729             start++;
   731         // port defaults
   732         if (type == kProxyType_HTTP)
   733             port = 80;
   734         else
   735             port = 1080;
   737         nsProxyInfo *pi = new nsProxyInfo();
   738         pi->mType = type;
   739         pi->mFlags = flags;
   740         pi->mResolveFlags = aResolveFlags;
   741         pi->mTimeout = mFailedProxyTimeout;
   743         // www.foo.com:8080 and http://www.foo.com:8080
   744         nsDependentCSubstring maybeURL(start, end - start);
   745         nsCOMPtr<nsIURI> pacURI;
   747         nsAutoCString urlHost;
   748         if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) &&
   749             NS_SUCCEEDED(pacURI->GetAsciiHost(urlHost)) &&
   750             !urlHost.IsEmpty()) {
   751             // http://www.example.com:8080
   753             pi->mHost = urlHost;
   755             int32_t tPort;
   756             if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
   757                 port = tPort;
   758             }
   759             pi->mPort = port;
   760         }
   761         else {
   762             // www.example.com:8080
   763             if (start < end) {
   764                 host = start;
   765                 hostEnd = strchr(host, ':');
   766                 if (!hostEnd || hostEnd > end) {
   767                     hostEnd = end;
   768                     // no port, so assume default
   769                 }
   770                 else {
   771                     port = atoi(hostEnd + 1);
   772                 }
   773             }
   774             // YES, it is ok to specify a null proxy host.
   775             if (host) {
   776                 pi->mHost.Assign(host, hostEnd - host);
   777                 pi->mPort = port;
   778             }
   779         }
   780         NS_ADDREF(*result = pi);
   781     }
   783     while (*end == ';' || *end == ' ' || *end == '\t')
   784         ++end;
   785     return end;
   786 }
   788 void
   789 nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key)
   790 {
   791     key.AssignASCII(pi->mType);
   792     if (!pi->mHost.IsEmpty()) {
   793         key.Append(' ');
   794         key.Append(pi->mHost);
   795         key.Append(':');
   796         key.AppendInt(pi->mPort);
   797     }
   798 } 
   800 uint32_t
   801 nsProtocolProxyService::SecondsSinceSessionStart()
   802 {
   803     PRTime now = PR_Now();
   805     // get time elapsed since session start
   806     int64_t diff = now - mSessionStart;
   808     // convert microseconds to seconds
   809     diff /= PR_USEC_PER_SEC;
   811     // return converted 32 bit value
   812     return uint32_t(diff);
   813 }
   815 void
   816 nsProtocolProxyService::EnableProxy(nsProxyInfo *pi)
   817 {
   818     nsAutoCString key;
   819     GetProxyKey(pi, key);
   820     mFailedProxies.Remove(key);
   821 }
   823 void
   824 nsProtocolProxyService::DisableProxy(nsProxyInfo *pi)
   825 {
   826     nsAutoCString key;
   827     GetProxyKey(pi, key);
   829     uint32_t dsec = SecondsSinceSessionStart();
   831     // Add timeout to interval (this is the time when the proxy can
   832     // be tried again).
   833     dsec += pi->mTimeout;
   835     // NOTE: The classic codebase would increase the timeout value
   836     //       incrementally each time a subsequent failure occurred.
   837     //       We could do the same, but it would require that we not
   838     //       remove proxy entries in IsProxyDisabled or otherwise
   839     //       change the way we are recording disabled proxies.
   840     //       Simpler is probably better for now, and at least the
   841     //       user can tune the timeout setting via preferences.
   843     LOG(("DisableProxy %s %d\n", key.get(), dsec));
   845     // If this fails, oh well... means we don't have enough memory
   846     // to remember the failed proxy.
   847     mFailedProxies.Put(key, dsec);
   848 }
   850 bool
   851 nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi)
   852 {
   853     nsAutoCString key;
   854     GetProxyKey(pi, key);
   856     uint32_t val;
   857     if (!mFailedProxies.Get(key, &val))
   858         return false;
   860     uint32_t dsec = SecondsSinceSessionStart();
   862     // if time passed has exceeded interval, then try proxy again.
   863     if (dsec > val) {
   864         mFailedProxies.Remove(key);
   865         return false;
   866     }
   868     return true;
   869 }
   871 nsresult
   872 nsProtocolProxyService::SetupPACThread()
   873 {
   874     if (mPACMan)
   875         return NS_OK;
   877     mPACMan = new nsPACMan();
   879     bool mainThreadOnly;
   880     nsresult rv;
   881     if (mSystemProxySettings &&
   882         NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
   883         !mainThreadOnly) {
   884         rv = mPACMan->Init(mSystemProxySettings);
   885     }
   886     else {
   887         rv = mPACMan->Init(nullptr);
   888     }
   890     if (NS_FAILED(rv))
   891         mPACMan = nullptr;
   892     return rv;
   893 }
   895 nsresult
   896 nsProtocolProxyService::ResetPACThread()
   897 {
   898     if (!mPACMan)
   899         return NS_OK;
   901     mPACMan->Shutdown();
   902     mPACMan = nullptr;
   903     return SetupPACThread();
   904 }
   906 nsresult
   907 nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
   908                                          bool forceReload)
   909 {
   910     SetupPACThread();
   912     if (mPACMan->IsPACURI(spec) && !forceReload)
   913         return NS_OK;
   915     mFailedProxies.Clear();
   917     return mPACMan->LoadPACFromURI(spec);
   918 }
   920 void
   921 nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
   922                                          uint32_t aResolveFlags,
   923                                          nsIProxyInfo **result)
   924 {
   925     if (pacString.IsEmpty()) {
   926         *result = nullptr;
   927         return;
   928     }
   930     const char *proxies = pacString.get();
   932     nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
   933     while (*proxies) {
   934         proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
   935         if (pi) {
   936             if (last) {
   937                 NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
   938                 last->mNext = pi;
   939             }
   940             else
   941                 first = pi;
   942             last = pi;
   943         }
   944     }
   945     *result = first;
   946 }
   948 // nsIProtocolProxyService2
   949 NS_IMETHODIMP
   950 nsProtocolProxyService::ReloadPAC()
   951 {
   952     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   953     if (!prefs)
   954         return NS_OK;
   956     int32_t type;
   957     nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
   958     if (NS_FAILED(rv))
   959         return NS_OK;
   961     nsXPIDLCString pacSpec;
   962     if (type == PROXYCONFIG_PAC)
   963         prefs->GetCharPref(PROXY_PREF("autoconfig_url"), getter_Copies(pacSpec));
   964     else if (type == PROXYCONFIG_WPAD)
   965         pacSpec.AssignLiteral(WPAD_URL);
   967     if (!pacSpec.IsEmpty())
   968         ConfigureFromPAC(pacSpec, true);
   969     return NS_OK;
   970 }
   972 // When sync interface is removed this can go away too
   973 // The nsPACManCallback portion of this implementation should be run
   974 // off the main thread, because it uses a condvar for signaling and
   975 // the main thread is blocking on that condvar -
   976 //  so call nsPACMan::AsyncGetProxyForChannel() with
   977 // a false mainThreadResponse parameter.
   978 class nsAsyncBridgeRequest MOZ_FINAL  : public nsPACManCallback
   979 {
   980     NS_DECL_THREADSAFE_ISUPPORTS
   982      nsAsyncBridgeRequest()
   983         : mMutex("nsDeprecatedCallback")
   984         , mCondVar(mMutex, "nsDeprecatedCallback")
   985         , mCompleted(false)
   986     {
   987     }
   989     void OnQueryComplete(nsresult status,
   990                          const nsCString &pacString,
   991                          const nsCString &newPACURL)
   992     {
   993         MutexAutoLock lock(mMutex);
   994         mCompleted = true;
   995         mStatus = status;
   996         mPACString = pacString;
   997         mPACURL = newPACURL;
   998         mCondVar.Notify();
   999     }
  1001     void Lock()   { mMutex.Lock(); }
  1002     void Unlock() { mMutex.Unlock(); }
  1003     void Wait()   { mCondVar.Wait(PR_SecondsToInterval(3)); }
  1005 private:
  1006     ~nsAsyncBridgeRequest()
  1010     friend class nsProtocolProxyService;
  1012     Mutex    mMutex;
  1013     CondVar  mCondVar;
  1015     nsresult  mStatus;
  1016     nsCString mPACString;
  1017     nsCString mPACURL;
  1018     bool      mCompleted;
  1019 };
  1020 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
  1022 // nsProtocolProxyService
  1023 nsresult
  1024 nsProtocolProxyService::DeprecatedBlockingResolve(nsIChannel *aChannel,
  1025                                                   uint32_t aFlags,
  1026                                                   nsIProxyInfo **retval)
  1028     NS_ENSURE_ARG_POINTER(aChannel);
  1030     nsCOMPtr<nsIURI> uri;
  1031     aChannel->GetURI(getter_AddRefs(uri));
  1033     nsProtocolInfo info;
  1034     nsresult rv = GetProtocolInfo(uri, &info);
  1035     if (NS_FAILED(rv))
  1036         return rv;
  1038     nsCOMPtr<nsIProxyInfo> pi;
  1039     bool usePACThread;
  1041     // SystemProxySettings and PAC files can block the main thread
  1042     // but if neither of them are in use, we can just do the work
  1043     // right here and directly invoke the callback
  1045     rv = Resolve_Internal(aChannel, info, aFlags, &usePACThread, getter_AddRefs(pi));
  1046     if (NS_FAILED(rv))
  1047         return rv;
  1049     if (!usePACThread || !mPACMan) {
  1050         ApplyFilters(aChannel, info, pi);
  1051         pi.forget(retval);
  1052         return NS_OK;
  1055     // Use the PAC thread to do the work, so we don't have to reimplement that
  1056     // code, but block this thread on that completion.
  1057     nsRefPtr<nsAsyncBridgeRequest> ctx = new nsAsyncBridgeRequest();
  1058     ctx->Lock();
  1059     if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForChannel(aChannel, ctx, false))) {
  1060         // this can really block the main thread, so cap it at 3 seconds
  1061        ctx->Wait();
  1063     ctx->Unlock();
  1064     if (!ctx->mCompleted)
  1065         return NS_ERROR_FAILURE;
  1066     if (NS_FAILED(ctx->mStatus))
  1067         return ctx->mStatus;
  1069     // pretty much duplicate real DoCallback logic
  1071     // Generate proxy info from the PAC string if appropriate
  1072     if (!ctx->mPACString.IsEmpty()) {
  1073         LOG(("sync pac thread callback %s\n", ctx->mPACString.get()));
  1074         ProcessPACString(ctx->mPACString, 0, getter_AddRefs(pi));
  1075         ApplyFilters(aChannel, info, pi);
  1076         pi.forget(retval);
  1077         return NS_OK;
  1080     if (!ctx->mPACURL.IsEmpty()) {
  1081         NS_WARNING("sync pac thread callback indicates new pac file load\n");
  1082         // This is a problem and is one of the reasons this blocking interface
  1083         // is deprecated. The main loop needs to spin to make this reload happen. So
  1084         // we are going to kick off the reload and return an error - it will work
  1085         // next time. Because this sync interface is only used in the java plugin it
  1086         // is extremely likely that the pac file has already been loaded anyhow.
  1088         rv = ConfigureFromPAC(ctx->mPACURL, false);
  1089         if (NS_FAILED(rv))
  1090             return rv;
  1091         return NS_ERROR_NOT_AVAILABLE;
  1094     *retval = nullptr;
  1095     return NS_OK;
  1098 nsresult
  1099 nsProtocolProxyService::AsyncResolveInternal(nsIChannel *channel, uint32_t flags,
  1100                                              nsIProtocolProxyCallback *callback,
  1101                                              nsICancelable **result,
  1102                                              bool isSyncOK)
  1104     NS_ENSURE_ARG_POINTER(channel);
  1105     NS_ENSURE_ARG_POINTER(callback);
  1107     nsCOMPtr<nsIURI> uri;
  1108     channel->GetURI(getter_AddRefs(uri));
  1110     *result = nullptr;
  1111     nsRefPtr<nsAsyncResolveRequest> ctx =
  1112         new nsAsyncResolveRequest(this, channel, flags, callback);
  1114     nsProtocolInfo info;
  1115     nsresult rv = GetProtocolInfo(uri, &info);
  1116     if (NS_FAILED(rv))
  1117         return rv;
  1119     nsCOMPtr<nsIProxyInfo> pi;
  1120     bool usePACThread;
  1122     // SystemProxySettings and PAC files can block the main thread
  1123     // but if neither of them are in use, we can just do the work
  1124     // right here and directly invoke the callback
  1126     rv = Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
  1127     if (NS_FAILED(rv))
  1128         return rv;
  1130     if (!usePACThread || !mPACMan) {
  1131         // we can do it locally
  1132         ApplyFilters(channel, info, pi);
  1133         ctx->SetResult(NS_OK, pi);
  1134         if (isSyncOK) {
  1135             ctx->Run();
  1136             return NS_OK;
  1139         rv = ctx->DispatchCallback();
  1140         if (NS_SUCCEEDED(rv))
  1141             ctx.forget(result);
  1142         return rv;
  1145     // else kick off a PAC thread query
  1147     rv = mPACMan->AsyncGetProxyForChannel(channel, ctx, true);
  1148     if (NS_SUCCEEDED(rv))
  1149         ctx.forget(result);
  1150     return rv;
  1153 // nsIProtocolProxyService
  1154 NS_IMETHODIMP
  1155 nsProtocolProxyService::AsyncResolve2(nsIChannel *channel, uint32_t flags,
  1156                                       nsIProtocolProxyCallback *callback,
  1157                                       nsICancelable **result)
  1159     return AsyncResolveInternal(channel, flags, callback, result, true);
  1162 NS_IMETHODIMP
  1163 nsProtocolProxyService::AsyncResolve(nsIChannel *channel, uint32_t flags,
  1164                                      nsIProtocolProxyCallback *callback,
  1165                                      nsICancelable **result)
  1167     return AsyncResolveInternal(channel, flags, callback, result, false);
  1170 NS_IMETHODIMP
  1171 nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
  1172                                      const nsACString &aHost,
  1173                                      int32_t aPort,
  1174                                      uint32_t aFlags,
  1175                                      uint32_t aFailoverTimeout,
  1176                                      nsIProxyInfo *aFailoverProxy,
  1177                                      nsIProxyInfo **aResult)
  1179     static const char *types[] = {
  1180         kProxyType_HTTP,
  1181         kProxyType_SOCKS,
  1182         kProxyType_SOCKS4,
  1183         kProxyType_DIRECT
  1184     };
  1186     // resolve type; this allows us to avoid copying the type string into each
  1187     // proxy info instance.  we just reference the string literals directly :)
  1188     const char *type = nullptr;
  1189     for (uint32_t i=0; i<ArrayLength(types); ++i) {
  1190         if (aType.LowerCaseEqualsASCII(types[i])) {
  1191             type = types[i];
  1192             break;
  1195     NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
  1197     return NewProxyInfo_Internal(type, aHost, aPort,
  1198                                  mSOCKSProxyUsername, mSOCKSProxyPassword,
  1199                                  aFlags, aFailoverTimeout,
  1200                                  aFailoverProxy, 0, aResult);
  1203 NS_IMETHODIMP
  1204 nsProtocolProxyService::NewSOCKSProxyInfo(const nsACString &aHost,
  1205                                           int32_t aPort,
  1206                                           const nsACString &aUsername,
  1207                                           const nsACString &aPassword,
  1208                                           uint32_t aFlags,
  1209                                           uint32_t aFailoverTimeout,
  1210                                           nsIProxyInfo *aFailoverProxy,
  1211                                           nsIProxyInfo **aResult)
  1213     return NewProxyInfo_Internal(kProxyType_SOCKS, aHost, aPort,
  1214                                  aUsername, aPassword,
  1215                                  aFlags, aFailoverTimeout,
  1216                                  aFailoverProxy, 0, aResult);
  1219 NS_IMETHODIMP
  1220 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo  *aProxy,
  1221                                             nsIURI        *aURI,
  1222                                             nsresult       aStatus,
  1223                                             nsIProxyInfo **aResult)
  1225     // We only support failover when a PAC file is configured, either
  1226     // directly or via system settings
  1227     if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
  1228         mProxyConfig != PROXYCONFIG_SYSTEM)
  1229         return NS_ERROR_NOT_AVAILABLE;
  1231     // Verify that |aProxy| is one of our nsProxyInfo objects.
  1232     nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
  1233     NS_ENSURE_ARG(pi);
  1234     // OK, the QI checked out.  We can proceed.
  1236     // Remember that this proxy is down.
  1237     DisableProxy(pi);
  1239     // NOTE: At this point, we might want to prompt the user if we have
  1240     //       not already tried going DIRECT.  This is something that the
  1241     //       classic codebase supported; however, IE6 does not prompt.
  1243     if (!pi->mNext)
  1244         return NS_ERROR_NOT_AVAILABLE;
  1246     LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
  1247         pi->mType, pi->mHost.get(), pi->mPort,
  1248         pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
  1250     NS_ADDREF(*aResult = pi->mNext);
  1251     return NS_OK;
  1254 nsresult
  1255 nsProtocolProxyService::InsertFilterLink(FilterLink *link, uint32_t position)
  1257     if (!mFilters) {
  1258         mFilters = link;
  1259         return NS_OK;
  1262     // insert into mFilters in sorted order
  1263     FilterLink *last = nullptr;
  1264     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
  1265         if (position < iter->position) {
  1266             if (last) {
  1267                 link->next = last->next;
  1268                 last->next = link;
  1270             else {
  1271                 link->next = mFilters;
  1272                 mFilters = link;
  1274             return NS_OK;
  1276         last = iter;
  1278     // our position is equal to or greater than the last link in the list
  1279     last->next = link;
  1280     return NS_OK;
  1283 NS_IMETHODIMP
  1284 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
  1285                                        uint32_t position)
  1287     UnregisterFilter(filter); // remove this filter if we already have it
  1289     FilterLink *link = new FilterLink(position, filter);
  1290     if (!link)
  1291         return NS_ERROR_OUT_OF_MEMORY;
  1292     return InsertFilterLink(link, position);
  1295 NS_IMETHODIMP
  1296 nsProtocolProxyService::RegisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter,
  1297                                               uint32_t position)
  1299     UnregisterChannelFilter(channelFilter);  // remove this filter if we already have it
  1301     FilterLink *link = new FilterLink(position, channelFilter);
  1302     if (!link)
  1303         return NS_ERROR_OUT_OF_MEMORY;
  1304     return InsertFilterLink(link, position);
  1307 nsresult
  1308 nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject)
  1310     FilterLink *last = nullptr;
  1311     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
  1312         nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
  1313         if (object == givenObject) {
  1314             if (last)
  1315                 last->next = iter->next;
  1316             else
  1317                 mFilters = iter->next;
  1318             iter->next = nullptr;
  1319             delete iter;
  1320             return NS_OK;
  1322         last = iter;
  1325     // No need to throw an exception in this case.
  1326     return NS_OK;
  1329 NS_IMETHODIMP
  1330 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter) {
  1331     // QI to nsISupports so we can safely test object identity.
  1332     nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
  1333     return RemoveFilterLink(givenObject);
  1336 NS_IMETHODIMP
  1337 nsProtocolProxyService::UnregisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter) {
  1338     // QI to nsISupports so we can safely test object identity.
  1339     nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
  1340     return RemoveFilterLink(givenObject);
  1343 NS_IMETHODIMP
  1344 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType)
  1346   *aProxyConfigType = mProxyConfig;
  1347   return NS_OK;
  1350 void
  1351 nsProtocolProxyService::LoadHostFilters(const char *filters)
  1353     // check to see the owners flag? /!?/ TODO
  1354     if (mHostFiltersArray.Length() > 0) {
  1355         mHostFiltersArray.Clear();
  1358     if (!filters)
  1359         return; // fail silently...
  1361     //
  1362     // filter  = ( host | domain | ipaddr ["/" mask] ) [":" port] 
  1363     // filters = filter *( "," LWS filter)
  1364     //
  1365     // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref string
  1366     mFilterLocalHosts = false;
  1367     while (*filters) {
  1368         // skip over spaces and ,
  1369         while (*filters && (*filters == ',' || IS_ASCII_SPACE(*filters)))
  1370             filters++;
  1372         const char *starthost = filters;
  1373         const char *endhost = filters + 1; // at least that...
  1374         const char *portLocation = 0; 
  1375         const char *maskLocation = 0;
  1377         while (*endhost && (*endhost != ',' && !IS_ASCII_SPACE(*endhost))) {
  1378             if (*endhost == ':')
  1379                 portLocation = endhost;
  1380             else if (*endhost == '/')
  1381                 maskLocation = endhost;
  1382             else if (*endhost == ']') // IPv6 address literals
  1383                 portLocation = 0;
  1384             endhost++;
  1387         filters = endhost; // advance iterator up front
  1389         // locate end of host
  1390         const char *end = maskLocation ? maskLocation :
  1391                           portLocation ? portLocation :
  1392                           endhost;
  1394         nsAutoCString str(starthost, end - starthost);
  1396         // If the current host filter is "<local>", then all local (i.e.
  1397         // no dots in the hostname) hosts should bypass the proxy
  1398         if (str.EqualsIgnoreCase("<local>")) {
  1399             mFilterLocalHosts = true;
  1400             LOG(("loaded filter for local hosts "
  1401                  "(plain host names, no dots)\n"));
  1402             // Continue to next host filter;
  1403             continue;
  1406         // For all other host filters, create HostInfo object and add to list
  1407         HostInfo *hinfo = new HostInfo();
  1408         hinfo->port = portLocation ? atoi(portLocation + 1) : 0;
  1410         PRNetAddr addr;
  1411         if (PR_StringToNetAddr(str.get(), &addr) == PR_SUCCESS) {
  1412             hinfo->is_ipaddr   = true;
  1413             hinfo->ip.family   = PR_AF_INET6; // we always store address as IPv6
  1414             hinfo->ip.mask_len = maskLocation ? atoi(maskLocation + 1) : 128;
  1416             if (hinfo->ip.mask_len == 0) {
  1417                 NS_WARNING("invalid mask");
  1418                 goto loser;
  1421             if (addr.raw.family == PR_AF_INET) {
  1422                 // convert to IPv4-mapped address
  1423                 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
  1424                 // adjust mask_len accordingly
  1425                 if (hinfo->ip.mask_len <= 32)
  1426                     hinfo->ip.mask_len += 96;
  1428             else if (addr.raw.family == PR_AF_INET6) {
  1429                 // copy the address
  1430                 memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
  1432             else {
  1433                 NS_WARNING("unknown address family");
  1434                 goto loser;
  1437             // apply mask to IPv6 address
  1438             proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
  1440         else {
  1441             uint32_t startIndex, endIndex;
  1442             if (str.First() == '*')
  1443                 startIndex = 1; // *.domain -> .domain
  1444             else
  1445                 startIndex = 0;
  1446             endIndex = (portLocation ? portLocation : endhost) - starthost;
  1448             hinfo->is_ipaddr = false;
  1449             hinfo->name.host = ToNewCString(Substring(str, startIndex, endIndex));
  1451             if (!hinfo->name.host)
  1452                 goto loser;
  1454             hinfo->name.host_len = endIndex - startIndex;
  1457 //#define DEBUG_DUMP_FILTERS
  1458 #ifdef DEBUG_DUMP_FILTERS
  1459         printf("loaded filter[%u]:\n", mHostFiltersArray.Length());
  1460         printf("  is_ipaddr = %u\n", hinfo->is_ipaddr);
  1461         printf("  port = %u\n", hinfo->port);
  1462         if (hinfo->is_ipaddr) {
  1463             printf("  ip.family = %x\n", hinfo->ip.family);
  1464             printf("  ip.mask_len = %u\n", hinfo->ip.mask_len);
  1466             PRNetAddr netAddr;
  1467             PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
  1468             memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
  1470             char buf[256];
  1471             PR_NetAddrToString(&netAddr, buf, sizeof(buf));
  1473             printf("  ip.addr = %s\n", buf);
  1475         else {
  1476             printf("  name.host = %s\n", hinfo->name.host);
  1478 #endif
  1480         mHostFiltersArray.AppendElement(hinfo);
  1481         hinfo = nullptr;
  1482 loser:
  1483         delete hinfo;
  1487 nsresult
  1488 nsProtocolProxyService::GetProtocolInfo(nsIURI *uri, nsProtocolInfo *info)
  1490     NS_PRECONDITION(uri, "URI is null");
  1491     NS_PRECONDITION(info, "info is null");
  1493     nsresult rv;
  1495     rv = uri->GetScheme(info->scheme);
  1496     if (NS_FAILED(rv))
  1497         return rv;
  1499     nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
  1500     if (NS_FAILED(rv))
  1501         return rv;
  1503     nsCOMPtr<nsIProtocolHandler> handler;
  1504     rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
  1505     if (NS_FAILED(rv))
  1506         return rv;
  1508     rv = handler->GetProtocolFlags(&info->flags);
  1509     if (NS_FAILED(rv))
  1510         return rv;
  1512     rv = handler->GetDefaultPort(&info->defaultPort);
  1513     return rv;
  1516 nsresult
  1517 nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
  1518                                               const nsACString &aHost,
  1519                                               int32_t aPort,
  1520                                               const nsACString &aUsername,
  1521                                               const nsACString &aPassword,
  1522                                               uint32_t aFlags,
  1523                                               uint32_t aFailoverTimeout,
  1524                                               nsIProxyInfo *aFailoverProxy,
  1525                                               uint32_t aResolveFlags,
  1526                                               nsIProxyInfo **aResult)
  1528     if (aPort <= 0)
  1529         aPort = -1;
  1531     nsCOMPtr<nsProxyInfo> failover;
  1532     if (aFailoverProxy) {
  1533         failover = do_QueryInterface(aFailoverProxy);
  1534         NS_ENSURE_ARG(failover);
  1537     nsProxyInfo *proxyInfo = new nsProxyInfo();
  1538     if (!proxyInfo)
  1539         return NS_ERROR_OUT_OF_MEMORY;
  1541     proxyInfo->mType = aType;
  1542     proxyInfo->mHost = aHost;
  1543     proxyInfo->mPort = aPort;
  1544     proxyInfo->mUsername = aUsername;
  1545     proxyInfo->mPassword = aPassword;
  1546     proxyInfo->mFlags = aFlags;
  1547     proxyInfo->mResolveFlags = aResolveFlags;
  1548     proxyInfo->mTimeout = aFailoverTimeout == UINT32_MAX
  1549         ? mFailedProxyTimeout : aFailoverTimeout;
  1550     failover.swap(proxyInfo->mNext);
  1552     NS_ADDREF(*aResult = proxyInfo);
  1553     return NS_OK;
  1556 nsresult
  1557 nsProtocolProxyService::Resolve_Internal(nsIChannel *channel,
  1558                                          const nsProtocolInfo &info,
  1559                                          uint32_t flags,
  1560                                          bool *usePACThread,
  1561                                          nsIProxyInfo **result)
  1563     NS_ENSURE_ARG_POINTER(channel);
  1564     nsresult rv = SetupPACThread();
  1565     if (NS_FAILED(rv))
  1566         return rv;
  1568     *usePACThread = false;
  1569     *result = nullptr;
  1571     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
  1572         return NS_OK;  // Can't proxy this (filters may not override)
  1574     nsCOMPtr<nsIURI> uri;
  1575     channel->GetURI(getter_AddRefs(uri));
  1577     // See bug #586908.
  1578     // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
  1579     // here means that we will not use a proxy for this connection.
  1580     if (mPACMan && mPACMan->IsPACURI(uri))
  1581         return NS_OK;
  1583     bool mainThreadOnly;
  1584     if (mSystemProxySettings &&
  1585         mProxyConfig == PROXYCONFIG_SYSTEM &&
  1586         NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
  1587         !mainThreadOnly) {
  1588         *usePACThread = true;
  1589         return NS_OK;
  1592     if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) {
  1593         // If the system proxy setting implementation is not threadsafe (e.g
  1594         // linux gconf), we'll do it inline here. Such implementations promise
  1595         // not to block
  1597         nsAutoCString PACURI;
  1598         nsAutoCString pacString;
  1600         if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
  1601             !PACURI.IsEmpty()) {
  1602             // There is a PAC URI configured. If it is unchanged, then
  1603             // just execute the PAC thread. If it is changed then load
  1604             // the new value
  1606             if (mPACMan && mPACMan->IsPACURI(PACURI)) {
  1607                 // unchanged
  1608                 *usePACThread = true;
  1609                 return NS_OK;
  1612             ConfigureFromPAC(PACURI, false);
  1613             return NS_OK;
  1616         nsAutoCString spec;
  1617         nsAutoCString host;
  1618         nsAutoCString scheme;
  1619         int32_t port = -1;
  1621         uri->GetAsciiSpec(spec);
  1622         uri->GetAsciiHost(host);
  1623         uri->GetScheme(scheme);
  1624         uri->GetPort(&port);
  1626         // now try the system proxy settings for this particular url
  1627         if (NS_SUCCEEDED(mSystemProxySettings->
  1628                          GetProxyForURI(spec, scheme, host, port,
  1629                                         pacString))) {
  1630             ProcessPACString(pacString, 0, result);
  1631             return NS_OK;
  1635     // if proxies are enabled and this host:port combo is supposed to use a
  1636     // proxy, check for a proxy.
  1637     if (mProxyConfig == PROXYCONFIG_DIRECT ||
  1638         (mProxyConfig == PROXYCONFIG_MANUAL &&
  1639          !CanUseProxy(uri, info.defaultPort)))
  1640         return NS_OK;
  1642     // Proxy auto config magic...
  1643     if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD) {
  1644         // Do not query PAC now.
  1645         *usePACThread = true;
  1646         return NS_OK;
  1649     // If we aren't in manual proxy configuration mode then we don't
  1650     // want to honor any manual specific prefs that might be still set
  1651     if (mProxyConfig != PROXYCONFIG_MANUAL)
  1652         return NS_OK;
  1654     // proxy info values for manual configuration mode
  1655     const char *type = nullptr;
  1656     const nsACString *host = nullptr;
  1657     int32_t port = -1;
  1659     uint32_t proxyFlags = 0;
  1661     if ((flags & RESOLVE_PREFER_SOCKS_PROXY) &&
  1662         !mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
  1663       host = &mSOCKSProxyHost;
  1664       if (mSOCKSProxyVersion == 4)
  1665           type = kProxyType_SOCKS4;
  1666       else
  1667           type = kProxyType_SOCKS;
  1668       port = mSOCKSProxyPort;
  1669       if (mSOCKSProxyRemoteDNS)
  1670           proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
  1672     else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
  1673              !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
  1674         host = &mHTTPSProxyHost;
  1675         type = kProxyType_HTTP;
  1676         port = mHTTPSProxyPort;
  1678     else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
  1679              ((flags & RESOLVE_IGNORE_URI_SCHEME) ||
  1680               info.scheme.EqualsLiteral("http"))) {
  1681         host = &mHTTPProxyHost;
  1682         type = kProxyType_HTTP;
  1683         port = mHTTPProxyPort;
  1685     else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
  1686              !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
  1687              info.scheme.EqualsLiteral("https")) {
  1688         host = &mHTTPSProxyHost;
  1689         type = kProxyType_HTTP;
  1690         port = mHTTPSProxyPort;
  1692     else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 &&
  1693              !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
  1694              info.scheme.EqualsLiteral("ftp")) {
  1695         host = &mFTPProxyHost;
  1696         type = kProxyType_HTTP;
  1697         port = mFTPProxyPort;
  1699     else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
  1700         host = &mSOCKSProxyHost;
  1701         if (mSOCKSProxyVersion == 4)
  1702             type = kProxyType_SOCKS4;
  1703         else
  1704             type = kProxyType_SOCKS;
  1705         port = mSOCKSProxyPort;
  1706         if (mSOCKSProxyRemoteDNS)
  1707             proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
  1710     if (type) {
  1711         rv = NewProxyInfo_Internal(type, *host, port,
  1712                                    mSOCKSProxyUsername, mSOCKSProxyPassword,
  1713                                    proxyFlags, UINT32_MAX, nullptr, flags,
  1714                                    result);
  1715         if (NS_FAILED(rv))
  1716             return rv;
  1719     return NS_OK;
  1722 void
  1723 nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo *aProxy)
  1725     // Disable Prefetch in the DNS service if a proxy is in use.
  1726     if (!aProxy)
  1727         return;
  1729     nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
  1730     if (!pi ||
  1731         !pi->mType ||
  1732         pi->mType == kProxyType_DIRECT)
  1733         return;
  1735     nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
  1736     if (!dns)
  1737         return;
  1738     nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
  1739     if (!pdns)
  1740         return;
  1742     // We lose the prefetch optimization for the life of the dns service.
  1743     pdns->SetPrefetchEnabled(false);
  1746 void
  1747 nsProtocolProxyService::ApplyFilters(nsIChannel *channel, const nsProtocolInfo &info,
  1748                                      nsIProxyInfo **list)
  1750     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
  1751         return;
  1753     // We prune the proxy list prior to invoking each filter.  This may be
  1754     // somewhat inefficient, but it seems like a good idea since we want each
  1755     // filter to "see" a valid proxy list.
  1757     nsresult rv;
  1758     nsCOMPtr<nsIProxyInfo> result;
  1760     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
  1761         PruneProxyInfo(info, list);
  1762         if (!!iter->filter) {
  1763           nsCOMPtr<nsIURI> uri;
  1764           channel->GetURI(getter_AddRefs(uri));
  1765           if (!!uri) {
  1766             nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
  1767             nsCOMPtr<nsIURI> proxyURI = nullptr;
  1768             if (!!httpChannel) {
  1769               httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
  1771             rv = iter->filter->ApplyFilter(this, proxyURI ? proxyURI : uri, *list,
  1772                                            getter_AddRefs(result));
  1774         } else if (!!iter->channelFilter) {
  1775           rv = iter->channelFilter->ApplyFilter(this, channel, *list,
  1776                                                 getter_AddRefs(result));
  1778         if (NS_FAILED(rv))
  1779             continue;
  1780         result.swap(*list);
  1783     PruneProxyInfo(info, list);
  1786 void
  1787 nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
  1788                                        nsIProxyInfo **list)
  1790     if (!*list)
  1791         return;
  1792     nsProxyInfo *head = nullptr;
  1793     CallQueryInterface(*list, &head);
  1794     if (!head) {
  1795         NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo");
  1796         return;
  1798     NS_RELEASE(*list);
  1800     // Pruning of disabled proxies works like this:
  1801     //   - If all proxies are disabled, return the full list
  1802     //   - Otherwise, remove the disabled proxies.
  1803     //
  1804     // Pruning of disallowed proxies works like this:
  1805     //   - If the protocol handler disallows the proxy, then we disallow it.
  1807     // Start by removing all disallowed proxies if required:
  1808     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
  1809         nsProxyInfo *last = nullptr, *iter = head;
  1810         while (iter) {
  1811             if (iter->Type() == kProxyType_HTTP) {
  1812                 // reject!
  1813                 if (last)
  1814                     last->mNext = iter->mNext;
  1815                 else
  1816                     head = iter->mNext;
  1817                 nsProxyInfo *next = iter->mNext;
  1818                 iter->mNext = nullptr;
  1819                 iter->Release();
  1820                 iter = next;
  1821             } else {
  1822                 last = iter;
  1823                 iter = iter->mNext;
  1826         if (!head)
  1827             return;
  1830     // Now, scan to see if all remaining proxies are disabled.  If so, then
  1831     // we'll just bail and return them all.  Otherwise, we'll go and prune the
  1832     // disabled ones.
  1834     bool allDisabled = true;
  1836     nsProxyInfo *iter;
  1837     for (iter = head; iter; iter = iter->mNext) {
  1838         if (!IsProxyDisabled(iter)) {
  1839             allDisabled = false;
  1840             break;
  1844     if (allDisabled)
  1845         LOG(("All proxies are disabled, so trying all again"));
  1846     else {
  1847         // remove any disabled proxies.
  1848         nsProxyInfo *last = nullptr;
  1849         for (iter = head; iter; ) {
  1850             if (IsProxyDisabled(iter)) {
  1851                 // reject!
  1852                 nsProxyInfo *reject = iter;
  1854                 iter = iter->mNext;
  1855                 if (last)
  1856                     last->mNext = iter;
  1857                 else
  1858                     head = iter;
  1860                 reject->mNext = nullptr;
  1861                 NS_RELEASE(reject);
  1862                 continue;
  1865             // since we are about to use this proxy, make sure it is not on
  1866             // the disabled proxy list.  we'll add it back to that list if
  1867             // we have to (in GetFailoverForProxy).
  1868             //
  1869             // XXX(darin): It might be better to do this as a final pass.
  1870             //
  1871             EnableProxy(iter);
  1873             last = iter;
  1874             iter = iter->mNext;
  1878     // if only DIRECT was specified then return no proxy info, and we're done.
  1879     if (head && !head->mNext && head->mType == kProxyType_DIRECT)
  1880         NS_RELEASE(head);
  1882     *list = head;  // Transfer ownership

mercurial