netwerk/base/src/nsProtocolProxyService.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/base/src/nsProtocolProxyService.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1883 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim:set ts=4 sw=4 sts=4 et: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/ArrayUtils.h"
    1.11 +#include "mozilla/Attributes.h"
    1.12 +
    1.13 +#include "nsProtocolProxyService.h"
    1.14 +#include "nsProxyInfo.h"
    1.15 +#include "nsIClassInfoImpl.h"
    1.16 +#include "nsIIOService.h"
    1.17 +#include "nsIObserverService.h"
    1.18 +#include "nsIProtocolHandler.h"
    1.19 +#include "nsIProtocolProxyCallback.h"
    1.20 +#include "nsIChannel.h"
    1.21 +#include "nsICancelable.h"
    1.22 +#include "nsIDNSService.h"
    1.23 +#include "nsPIDNSService.h"
    1.24 +#include "nsIPrefService.h"
    1.25 +#include "nsIPrefBranch.h"
    1.26 +#include "nsThreadUtils.h"
    1.27 +#include "nsString.h"
    1.28 +#include "nsNetUtil.h"
    1.29 +#include "nsNetCID.h"
    1.30 +#include "prnetdb.h"
    1.31 +#include "nsPACMan.h"
    1.32 +#include "nsProxyRelease.h"
    1.33 +#include "mozilla/Mutex.h"
    1.34 +#include "mozilla/CondVar.h"
    1.35 +#include "nsISystemProxySettings.h"
    1.36 +
    1.37 +//----------------------------------------------------------------------------
    1.38 +
    1.39 +namespace mozilla {
    1.40 +  extern const char kProxyType_HTTP[];
    1.41 +  extern const char kProxyType_SOCKS[];
    1.42 +  extern const char kProxyType_SOCKS4[];
    1.43 +  extern const char kProxyType_SOCKS5[];
    1.44 +  extern const char kProxyType_DIRECT[];
    1.45 +}
    1.46 +
    1.47 +using namespace mozilla;
    1.48 +
    1.49 +#include "prlog.h"
    1.50 +#if defined(PR_LOGGING)
    1.51 +#endif
    1.52 +#undef LOG
    1.53 +#define LOG(args) PR_LOG(net::GetProxyLog(), PR_LOG_DEBUG, args)
    1.54 +
    1.55 +//----------------------------------------------------------------------------
    1.56 +
    1.57 +#define PROXY_PREF_BRANCH  "network.proxy"
    1.58 +#define PROXY_PREF(x)      PROXY_PREF_BRANCH "." x
    1.59 +
    1.60 +#define WPAD_URL "http://wpad/wpad.dat"
    1.61 +
    1.62 +//----------------------------------------------------------------------------
    1.63 +
    1.64 +// This structure is intended to be allocated on the stack
    1.65 +struct nsProtocolInfo {
    1.66 +    nsAutoCString scheme;
    1.67 +    uint32_t flags;
    1.68 +    int32_t defaultPort;
    1.69 +};
    1.70 +
    1.71 +//----------------------------------------------------------------------------
    1.72 +
    1.73 +// The nsPACManCallback portion of this implementation should be run
    1.74 +// on the main thread - so call nsPACMan::AsyncGetProxyForChannel() with
    1.75 +// a true mainThreadResponse parameter.
    1.76 +class nsAsyncResolveRequest MOZ_FINAL : public nsIRunnable
    1.77 +                                      , public nsPACManCallback
    1.78 +                                      , public nsICancelable
    1.79 +{
    1.80 +public:
    1.81 +    NS_DECL_THREADSAFE_ISUPPORTS
    1.82 +
    1.83 +    nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIChannel *channel,
    1.84 +                          uint32_t aResolveFlags,
    1.85 +                          nsIProtocolProxyCallback *callback)
    1.86 +        : mStatus(NS_OK)
    1.87 +        , mDispatched(false)
    1.88 +        , mResolveFlags(aResolveFlags)
    1.89 +        , mPPS(pps)
    1.90 +        , mXPComPPS(pps)
    1.91 +        , mChannel(channel)
    1.92 +        , mCallback(callback)
    1.93 +    {
    1.94 +        NS_ASSERTION(mCallback, "null callback");
    1.95 +    }
    1.96 +
    1.97 +    ~nsAsyncResolveRequest()
    1.98 +    {
    1.99 +        if (!NS_IsMainThread()) {
   1.100 +            // these xpcom pointers might need to be proxied back to the
   1.101 +            // main thread to delete safely, but if this request had its
   1.102 +            // callbacks called normally they will all be null and this is a nop
   1.103 +
   1.104 +            nsCOMPtr<nsIThread> mainThread;
   1.105 +            NS_GetMainThread(getter_AddRefs(mainThread));
   1.106 +
   1.107 +            if (mChannel) {
   1.108 +                nsIChannel *forgettable;
   1.109 +                mChannel.forget(&forgettable);
   1.110 +                NS_ProxyRelease(mainThread, forgettable, false);
   1.111 +            }
   1.112 +
   1.113 +            if (mCallback) {
   1.114 +                nsIProtocolProxyCallback *forgettable;
   1.115 +                mCallback.forget(&forgettable);
   1.116 +                NS_ProxyRelease(mainThread, forgettable, false);
   1.117 +            }
   1.118 +
   1.119 +            if (mProxyInfo) {
   1.120 +                nsIProxyInfo *forgettable;
   1.121 +                mProxyInfo.forget(&forgettable);
   1.122 +                NS_ProxyRelease(mainThread, forgettable, false);
   1.123 +            }
   1.124 +
   1.125 +            if (mXPComPPS) {
   1.126 +                nsIProtocolProxyService *forgettable;
   1.127 +                mXPComPPS.forget(&forgettable);
   1.128 +                NS_ProxyRelease(mainThread, forgettable, false);
   1.129 +            }
   1.130 +        }
   1.131 +    }
   1.132 +
   1.133 +    void SetResult(nsresult status, nsIProxyInfo *pi)
   1.134 +    {
   1.135 +        mStatus = status;
   1.136 +        mProxyInfo = pi;
   1.137 +    }
   1.138 +
   1.139 +    NS_IMETHOD Run()
   1.140 +    {
   1.141 +        if (mCallback)
   1.142 +            DoCallback();
   1.143 +        return NS_OK;
   1.144 +    }
   1.145 +
   1.146 +    NS_IMETHOD Cancel(nsresult reason)
   1.147 +    {
   1.148 +        NS_ENSURE_ARG(NS_FAILED(reason));
   1.149 +
   1.150 +        // If we've already called DoCallback then, nothing more to do.
   1.151 +        if (!mCallback)
   1.152 +            return NS_OK;
   1.153 +
   1.154 +        SetResult(reason, nullptr);
   1.155 +        return DispatchCallback();
   1.156 +    }
   1.157 +
   1.158 +    nsresult DispatchCallback()
   1.159 +    {
   1.160 +        if (mDispatched)  // Only need to dispatch once
   1.161 +            return NS_OK;
   1.162 +
   1.163 +        nsresult rv = NS_DispatchToCurrentThread(this);
   1.164 +        if (NS_FAILED(rv))
   1.165 +            NS_WARNING("unable to dispatch callback event");
   1.166 +        else {
   1.167 +            mDispatched = true;
   1.168 +            return NS_OK;
   1.169 +        }
   1.170 +
   1.171 +        mCallback = nullptr;  // break possible reference cycle
   1.172 +        return rv;
   1.173 +    }
   1.174 +
   1.175 +private:
   1.176 +
   1.177 +    // Called asynchronously, so we do not need to post another PLEvent
   1.178 +    // before calling DoCallback.
   1.179 +    void OnQueryComplete(nsresult status,
   1.180 +                         const nsCString &pacString,
   1.181 +                         const nsCString &newPACURL)
   1.182 +    {
   1.183 +        // If we've already called DoCallback then, nothing more to do.
   1.184 +        if (!mCallback)
   1.185 +            return;
   1.186 +
   1.187 +        // Provided we haven't been canceled...
   1.188 +        if (mStatus == NS_OK) {
   1.189 +            mStatus = status;
   1.190 +            mPACString = pacString;
   1.191 +            mPACURL = newPACURL;
   1.192 +        }
   1.193 +
   1.194 +        // In the cancelation case, we may still have another PLEvent in
   1.195 +        // the queue that wants to call DoCallback.  No need to wait for
   1.196 +        // it, just run the callback now.
   1.197 +        DoCallback();
   1.198 +    }
   1.199 +
   1.200 +    void DoCallback()
   1.201 +    {
   1.202 +        if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) {
   1.203 +            // If the PAC service is not avail (e.g. failed pac load
   1.204 +            // or shutdown) then we will be going direct. Make that
   1.205 +            // mapping now so that any filters are still applied.
   1.206 +            mPACString = NS_LITERAL_CSTRING("DIRECT;");
   1.207 +            mStatus = NS_OK;
   1.208 +        }
   1.209 +
   1.210 +        // Generate proxy info from the PAC string if appropriate
   1.211 +        if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
   1.212 +            mPPS->ProcessPACString(mPACString, mResolveFlags,
   1.213 +                                   getter_AddRefs(mProxyInfo));
   1.214 +            nsCOMPtr<nsIURI> uri;
   1.215 +            mChannel->GetURI(getter_AddRefs(uri));
   1.216 +
   1.217 +            // Now apply proxy filters
   1.218 +            nsProtocolInfo info;
   1.219 +            mStatus = mPPS->GetProtocolInfo(uri, &info);
   1.220 +            if (NS_SUCCEEDED(mStatus))
   1.221 +                mPPS->ApplyFilters(mChannel, info, mProxyInfo);
   1.222 +            else
   1.223 +                mProxyInfo = nullptr;
   1.224 +
   1.225 +            LOG(("pac thread callback %s\n", mPACString.get()));
   1.226 +            if (NS_SUCCEEDED(mStatus))
   1.227 +                mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
   1.228 +            mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
   1.229 +        }
   1.230 +        else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
   1.231 +            LOG(("pac thread callback indicates new pac file load\n"));
   1.232 +
   1.233 +            // trigger load of new pac url
   1.234 +            nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
   1.235 +            if (NS_SUCCEEDED(rv)) {
   1.236 +                // now that the load is triggered, we can resubmit the query
   1.237 +                nsRefPtr<nsAsyncResolveRequest> newRequest =
   1.238 +                    new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback);
   1.239 +                rv = mPPS->mPACMan->AsyncGetProxyForChannel(mChannel, newRequest, true);
   1.240 +            }
   1.241 +
   1.242 +            if (NS_FAILED(rv))
   1.243 +                mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
   1.244 +
   1.245 +            // do not call onproxyavailable() in SUCCESS case - the newRequest will
   1.246 +            // take care of that
   1.247 +        }
   1.248 +        else {
   1.249 +            LOG(("pac thread callback did not provide information %X\n", mStatus));
   1.250 +            if (NS_SUCCEEDED(mStatus))
   1.251 +                mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
   1.252 +            mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
   1.253 +        }
   1.254 +
   1.255 +        // We are on the main thread now and don't need these any more so
   1.256 +        // release them to avoid having to proxy them back to the main thread
   1.257 +        // in the dtor
   1.258 +        mCallback = nullptr;  // in case the callback holds an owning ref to us
   1.259 +        mPPS = nullptr;
   1.260 +        mXPComPPS = nullptr;
   1.261 +        mChannel = nullptr;
   1.262 +        mProxyInfo = nullptr;
   1.263 +    }
   1.264 +
   1.265 +private:
   1.266 +
   1.267 +    nsresult  mStatus;
   1.268 +    nsCString mPACString;
   1.269 +    nsCString mPACURL;
   1.270 +    bool      mDispatched;
   1.271 +    uint32_t  mResolveFlags;
   1.272 +
   1.273 +    nsProtocolProxyService            *mPPS;
   1.274 +    nsCOMPtr<nsIProtocolProxyService>  mXPComPPS;
   1.275 +    nsCOMPtr<nsIChannel>               mChannel;
   1.276 +    nsCOMPtr<nsIProtocolProxyCallback> mCallback;
   1.277 +    nsCOMPtr<nsIProxyInfo>             mProxyInfo;
   1.278 +};
   1.279 +
   1.280 +NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
   1.281 +
   1.282 +//----------------------------------------------------------------------------
   1.283 +
   1.284 +#define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t')
   1.285 +
   1.286 +//
   1.287 +// apply mask to address (zeros out excluded bits).
   1.288 +//
   1.289 +// NOTE: we do the byte swapping here to minimize overall swapping.
   1.290 +//
   1.291 +static void
   1.292 +proxy_MaskIPv6Addr(PRIPv6Addr &addr, uint16_t mask_len)
   1.293 +{
   1.294 +    if (mask_len == 128)
   1.295 +        return;
   1.296 +
   1.297 +    if (mask_len > 96) {
   1.298 +        addr.pr_s6_addr32[3] = PR_htonl(
   1.299 +                PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len)));
   1.300 +    }
   1.301 +    else if (mask_len > 64) {
   1.302 +        addr.pr_s6_addr32[3] = 0;
   1.303 +        addr.pr_s6_addr32[2] = PR_htonl(
   1.304 +                PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len)));
   1.305 +    }
   1.306 +    else if (mask_len > 32) {
   1.307 +        addr.pr_s6_addr32[3] = 0;
   1.308 +        addr.pr_s6_addr32[2] = 0;
   1.309 +        addr.pr_s6_addr32[1] = PR_htonl(
   1.310 +                PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len)));
   1.311 +    }
   1.312 +    else {
   1.313 +        addr.pr_s6_addr32[3] = 0;
   1.314 +        addr.pr_s6_addr32[2] = 0;
   1.315 +        addr.pr_s6_addr32[1] = 0;
   1.316 +        addr.pr_s6_addr32[0] = PR_htonl(
   1.317 +                PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
   1.318 +    }
   1.319 +}
   1.320 +
   1.321 +static void
   1.322 +proxy_GetStringPref(nsIPrefBranch *aPrefBranch,
   1.323 +                    const char    *aPref,
   1.324 +                    nsCString     &aResult)
   1.325 +{
   1.326 +    nsXPIDLCString temp;
   1.327 +    nsresult rv = aPrefBranch->GetCharPref(aPref, getter_Copies(temp));
   1.328 +    if (NS_FAILED(rv))
   1.329 +        aResult.Truncate();
   1.330 +    else {
   1.331 +        aResult.Assign(temp);
   1.332 +        // all of our string prefs are hostnames, so we should remove any
   1.333 +        // whitespace characters that the user might have unknowingly entered.
   1.334 +        aResult.StripWhitespace();
   1.335 +    }
   1.336 +}
   1.337 +
   1.338 +static void
   1.339 +proxy_GetIntPref(nsIPrefBranch *aPrefBranch,
   1.340 +                 const char    *aPref,
   1.341 +                 int32_t       &aResult)
   1.342 +{
   1.343 +    int32_t temp;
   1.344 +    nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
   1.345 +    if (NS_FAILED(rv)) 
   1.346 +        aResult = -1;
   1.347 +    else
   1.348 +        aResult = temp;
   1.349 +}
   1.350 +
   1.351 +static void
   1.352 +proxy_GetBoolPref(nsIPrefBranch *aPrefBranch,
   1.353 +                 const char    *aPref,
   1.354 +                 bool          &aResult)
   1.355 +{
   1.356 +    bool temp;
   1.357 +    nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
   1.358 +    if (NS_FAILED(rv)) 
   1.359 +        aResult = false;
   1.360 +    else
   1.361 +        aResult = temp;
   1.362 +}
   1.363 +
   1.364 +//----------------------------------------------------------------------------
   1.365 +
   1.366 +static const int32_t PROXYCONFIG_DIRECT4X = 3;
   1.367 +static const int32_t PROXYCONFIG_COUNT = 6;
   1.368 +
   1.369 +NS_IMPL_ADDREF(nsProtocolProxyService)
   1.370 +NS_IMPL_RELEASE(nsProtocolProxyService)
   1.371 +NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
   1.372 +                  NS_PROTOCOLPROXYSERVICE_CID)
   1.373 +NS_IMPL_QUERY_INTERFACE_CI(nsProtocolProxyService,
   1.374 +                           nsIProtocolProxyService,
   1.375 +                           nsIProtocolProxyService2,
   1.376 +                           nsIObserver)
   1.377 +NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService,
   1.378 +                            nsIProtocolProxyService,
   1.379 +                            nsIProtocolProxyService2)
   1.380 +
   1.381 +nsProtocolProxyService::nsProtocolProxyService()
   1.382 +    : mFilterLocalHosts(false)
   1.383 +    , mFilters(nullptr)
   1.384 +    , mProxyConfig(PROXYCONFIG_DIRECT)
   1.385 +    , mHTTPProxyPort(-1)
   1.386 +    , mFTPProxyPort(-1)
   1.387 +    , mHTTPSProxyPort(-1)
   1.388 +    , mSOCKSProxyPort(-1)
   1.389 +    , mSOCKSProxyVersion(4)
   1.390 +    , mSOCKSProxyRemoteDNS(false)
   1.391 +    , mPACMan(nullptr)
   1.392 +    , mSessionStart(PR_Now())
   1.393 +    , mFailedProxyTimeout(30 * 60) // 30 minute default
   1.394 +{
   1.395 +}
   1.396 +
   1.397 +nsProtocolProxyService::~nsProtocolProxyService()
   1.398 +{
   1.399 +    // These should have been cleaned up in our Observe method.
   1.400 +    NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters == nullptr &&
   1.401 +                 mPACMan == nullptr, "what happened to xpcom-shutdown?");
   1.402 +}
   1.403 +
   1.404 +// nsProtocolProxyService methods
   1.405 +nsresult
   1.406 +nsProtocolProxyService::Init()
   1.407 +{
   1.408 +    // failure to access prefs is non-fatal
   1.409 +    nsCOMPtr<nsIPrefBranch> prefBranch =
   1.410 +            do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.411 +    if (prefBranch) {
   1.412 +        // monitor proxy prefs
   1.413 +        prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
   1.414 +
   1.415 +        // read all prefs
   1.416 +        PrefsChanged(prefBranch, nullptr);
   1.417 +    }
   1.418 +
   1.419 +    // register for shutdown notification so we can clean ourselves up properly.
   1.420 +    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1.421 +    if (obs)
   1.422 +        obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   1.423 +
   1.424 +    return NS_OK;
   1.425 +}
   1.426 +
   1.427 +NS_IMETHODIMP
   1.428 +nsProtocolProxyService::Observe(nsISupports     *aSubject,
   1.429 +                                const char      *aTopic,
   1.430 +                                const char16_t *aData)
   1.431 +{
   1.432 +    if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
   1.433 +        // cleanup
   1.434 +        if (mHostFiltersArray.Length() > 0) {
   1.435 +            mHostFiltersArray.Clear();
   1.436 +        }
   1.437 +        if (mFilters) {
   1.438 +            delete mFilters;
   1.439 +            mFilters = nullptr;
   1.440 +        }
   1.441 +        if (mPACMan) {
   1.442 +            mPACMan->Shutdown();
   1.443 +            mPACMan = nullptr;
   1.444 +        }
   1.445 +    }
   1.446 +    else {
   1.447 +        NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
   1.448 +                     "what is this random observer event?");
   1.449 +        nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
   1.450 +        if (prefs)
   1.451 +            PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
   1.452 +    }
   1.453 +    return NS_OK;
   1.454 +}
   1.455 +
   1.456 +void
   1.457 +nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
   1.458 +                                     const char    *pref)
   1.459 +{
   1.460 +    nsresult rv = NS_OK;
   1.461 +    bool reloadPAC = false;
   1.462 +    nsXPIDLCString tempString;
   1.463 +
   1.464 +    if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
   1.465 +        int32_t type = -1;
   1.466 +        rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
   1.467 +        if (NS_SUCCEEDED(rv)) {
   1.468 +            // bug 115720 - for ns4.x backwards compatibility
   1.469 +            if (type == PROXYCONFIG_DIRECT4X) {
   1.470 +                type = PROXYCONFIG_DIRECT;
   1.471 +                // Reset the type so that the dialog looks correct, and we
   1.472 +                // don't have to handle this case everywhere else
   1.473 +                // I'm paranoid about a loop of some sort - only do this
   1.474 +                // if we're enumerating all prefs, and ignore any error
   1.475 +                if (!pref)
   1.476 +                    prefBranch->SetIntPref(PROXY_PREF("type"), type);
   1.477 +            } else if (type >= PROXYCONFIG_COUNT) {
   1.478 +                LOG(("unknown proxy type: %lu; assuming direct\n", type));
   1.479 +                type = PROXYCONFIG_DIRECT;
   1.480 +            }
   1.481 +            mProxyConfig = type;
   1.482 +            reloadPAC = true;
   1.483 +        }
   1.484 +
   1.485 +        if (mProxyConfig == PROXYCONFIG_SYSTEM) {
   1.486 +            mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
   1.487 +            if (!mSystemProxySettings)
   1.488 +                mProxyConfig = PROXYCONFIG_DIRECT;
   1.489 +            ResetPACThread();
   1.490 +        } else {
   1.491 +            if (mSystemProxySettings) {
   1.492 +                mSystemProxySettings = nullptr;
   1.493 +                ResetPACThread();
   1.494 +            }
   1.495 +        }
   1.496 +    }
   1.497 +
   1.498 +    if (!pref || !strcmp(pref, PROXY_PREF("http")))
   1.499 +        proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
   1.500 +
   1.501 +    if (!pref || !strcmp(pref, PROXY_PREF("http_port")))
   1.502 +        proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
   1.503 +
   1.504 +    if (!pref || !strcmp(pref, PROXY_PREF("ssl")))
   1.505 +        proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
   1.506 +
   1.507 +    if (!pref || !strcmp(pref, PROXY_PREF("ssl_port")))
   1.508 +        proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
   1.509 +
   1.510 +    if (!pref || !strcmp(pref, PROXY_PREF("ftp")))
   1.511 +        proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost);
   1.512 +
   1.513 +    if (!pref || !strcmp(pref, PROXY_PREF("ftp_port")))
   1.514 +        proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
   1.515 +
   1.516 +    if (!pref || !strcmp(pref, PROXY_PREF("socks")))
   1.517 +        proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyHost);
   1.518 +    
   1.519 +    if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
   1.520 +        proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
   1.521 +
   1.522 +    if (!pref || !strcmp(pref, PROXY_PREF("socks_username")))
   1.523 +        proxy_GetStringPref(prefBranch, PROXY_PREF("socks_username"), mSOCKSProxyUsername);
   1.524 +
   1.525 +    if (!pref || !strcmp(pref, PROXY_PREF("socks_password")))
   1.526 +        proxy_GetStringPref(prefBranch, PROXY_PREF("socks_password"), mSOCKSProxyPassword);
   1.527 +
   1.528 +    if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
   1.529 +        int32_t version;
   1.530 +        proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
   1.531 +        // make sure this preference value remains sane
   1.532 +        if (version == 5)
   1.533 +            mSOCKSProxyVersion = 5;
   1.534 +        else
   1.535 +            mSOCKSProxyVersion = 4;
   1.536 +    }
   1.537 +
   1.538 +    if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns")))
   1.539 +        proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
   1.540 +                          mSOCKSProxyRemoteDNS);
   1.541 +
   1.542 +    if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
   1.543 +        proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
   1.544 +                         mFailedProxyTimeout);
   1.545 +
   1.546 +    if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
   1.547 +        rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
   1.548 +                                     getter_Copies(tempString));
   1.549 +        if (NS_SUCCEEDED(rv))
   1.550 +            LoadHostFilters(tempString.get());
   1.551 +    }
   1.552 +
   1.553 +    // We're done if not using something that could give us a PAC URL
   1.554 +    // (PAC, WPAD or System)
   1.555 +    if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
   1.556 +        mProxyConfig != PROXYCONFIG_SYSTEM)
   1.557 +        return;
   1.558 +
   1.559 +    // OK, we need to reload the PAC file if:
   1.560 +    //  1) network.proxy.type changed, or
   1.561 +    //  2) network.proxy.autoconfig_url changed and PAC is configured
   1.562 +
   1.563 +    if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url")))
   1.564 +        reloadPAC = true;
   1.565 +
   1.566 +    if (reloadPAC) {
   1.567 +        tempString.Truncate();
   1.568 +        if (mProxyConfig == PROXYCONFIG_PAC) {
   1.569 +            prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"),
   1.570 +                                    getter_Copies(tempString));
   1.571 +        } else if (mProxyConfig == PROXYCONFIG_WPAD) {
   1.572 +            // We diverge from the WPAD spec here in that we don't walk the
   1.573 +            // hosts's FQDN, stripping components until we hit a TLD.  Doing so
   1.574 +            // is dangerous in the face of an incomplete list of TLDs, and TLDs
   1.575 +            // get added over time.  We could consider doing only a single
   1.576 +            // substitution of the first component, if that proves to help
   1.577 +            // compatibility.
   1.578 +            tempString.AssignLiteral(WPAD_URL);
   1.579 +        } else if (mSystemProxySettings) {
   1.580 +            // Get System Proxy settings if available
   1.581 +            mSystemProxySettings->GetPACURI(tempString);
   1.582 +        }
   1.583 +        if (!tempString.IsEmpty())
   1.584 +            ConfigureFromPAC(tempString, false);
   1.585 +    }
   1.586 +}
   1.587 +
   1.588 +bool
   1.589 +nsProtocolProxyService::CanUseProxy(nsIURI *aURI, int32_t defaultPort) 
   1.590 +{
   1.591 +    if (mHostFiltersArray.Length() == 0)
   1.592 +        return true;
   1.593 +
   1.594 +    int32_t port;
   1.595 +    nsAutoCString host;
   1.596 + 
   1.597 +    nsresult rv = aURI->GetAsciiHost(host);
   1.598 +    if (NS_FAILED(rv) || host.IsEmpty())
   1.599 +        return false;
   1.600 +
   1.601 +    rv = aURI->GetPort(&port);
   1.602 +    if (NS_FAILED(rv))
   1.603 +        return false;
   1.604 +    if (port == -1)
   1.605 +        port = defaultPort;
   1.606 +
   1.607 +    PRNetAddr addr;
   1.608 +    bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
   1.609 +
   1.610 +    PRIPv6Addr ipv6;
   1.611 +    if (is_ipaddr) {
   1.612 +        // convert parsed address to IPv6
   1.613 +        if (addr.raw.family == PR_AF_INET) {
   1.614 +            // convert to IPv4-mapped address
   1.615 +            PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
   1.616 +        }
   1.617 +        else if (addr.raw.family == PR_AF_INET6) {
   1.618 +            // copy the address
   1.619 +            memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
   1.620 +        }
   1.621 +        else {
   1.622 +            NS_WARNING("unknown address family");
   1.623 +            return true; // allow proxying
   1.624 +        }
   1.625 +    }
   1.626 +    
   1.627 +    // Don't use proxy for local hosts (plain hostname, no dots)
   1.628 +    if (!is_ipaddr && mFilterLocalHosts && (kNotFound == host.FindChar('.'))) {
   1.629 +        LOG(("Not using proxy for this local host [%s]!\n", host.get()));
   1.630 +        return false; // don't allow proxying
   1.631 +    }
   1.632 +
   1.633 +    int32_t index = -1;
   1.634 +    while (++index < int32_t(mHostFiltersArray.Length())) {
   1.635 +        HostInfo *hinfo = mHostFiltersArray[index];
   1.636 +
   1.637 +        if (is_ipaddr != hinfo->is_ipaddr)
   1.638 +            continue;
   1.639 +        if (hinfo->port && hinfo->port != port)
   1.640 +            continue;
   1.641 +
   1.642 +        if (is_ipaddr) {
   1.643 +            // generate masked version of target IPv6 address
   1.644 +            PRIPv6Addr masked;
   1.645 +            memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
   1.646 +            proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
   1.647 +
   1.648 +            // check for a match
   1.649 +            if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0)
   1.650 +                return false; // proxy disallowed
   1.651 +        }
   1.652 +        else {
   1.653 +            uint32_t host_len = host.Length();
   1.654 +            uint32_t filter_host_len = hinfo->name.host_len;
   1.655 +
   1.656 +            if (host_len >= filter_host_len) {
   1.657 +                //
   1.658 +                // compare last |filter_host_len| bytes of target hostname.
   1.659 +                //
   1.660 +                const char *host_tail = host.get() + host_len - filter_host_len;
   1.661 +                if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len))
   1.662 +                    return false; // proxy disallowed
   1.663 +            }
   1.664 +        }
   1.665 +    }
   1.666 +    return true;
   1.667 +}
   1.668 +
   1.669 +// kProxyType\* may be referred to externally in
   1.670 +// nsProxyInfo in order to compare by string pointer
   1.671 +namespace mozilla {
   1.672 +const char kProxyType_HTTP[]    = "http";
   1.673 +const char kProxyType_PROXY[]   = "proxy";
   1.674 +const char kProxyType_SOCKS[]   = "socks";
   1.675 +const char kProxyType_SOCKS4[]  = "socks4";
   1.676 +const char kProxyType_SOCKS5[]  = "socks5";
   1.677 +const char kProxyType_DIRECT[]  = "direct";
   1.678 +const char kProxyType_UNKNOWN[] = "unknown";
   1.679 +}
   1.680 +
   1.681 +const char *
   1.682 +nsProtocolProxyService::ExtractProxyInfo(const char *start,
   1.683 +                                         uint32_t aResolveFlags,
   1.684 +                                         nsProxyInfo **result)
   1.685 +{
   1.686 +    *result = nullptr;
   1.687 +    uint32_t flags = 0;
   1.688 +
   1.689 +    // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
   1.690 +
   1.691 +    // find end of proxy info delimiter
   1.692 +    const char *end = start;
   1.693 +    while (*end && *end != ';') ++end;
   1.694 +
   1.695 +    // find end of proxy type delimiter
   1.696 +    const char *sp = start;
   1.697 +    while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
   1.698 +
   1.699 +    uint32_t len = sp - start;
   1.700 +    const char *type = nullptr;
   1.701 +    switch (len) {
   1.702 +    case 5:
   1.703 +        if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0)
   1.704 +            type = kProxyType_HTTP;
   1.705 +        else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0)
   1.706 +            type = kProxyType_SOCKS4; // assume v4 for 4x compat
   1.707 +        break;
   1.708 +    case 6:
   1.709 +        if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0)
   1.710 +            type = kProxyType_DIRECT;
   1.711 +        else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0)
   1.712 +            type = kProxyType_SOCKS4;
   1.713 +        else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0)
   1.714 +            // map "SOCKS5" to "socks" to match contract-id of registered
   1.715 +            // SOCKS-v5 socket provider.
   1.716 +            type = kProxyType_SOCKS;
   1.717 +        break;
   1.718 +    }
   1.719 +    if (type) {
   1.720 +        const char *host = nullptr, *hostEnd = nullptr;
   1.721 +        int32_t port = -1;
   1.722 +
   1.723 +        // If it's a SOCKS5 proxy, do name resolution on the server side.
   1.724 +        // We could use this with SOCKS4a servers too, but they might not
   1.725 +        // support it.
   1.726 +        if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS)
   1.727 +            flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
   1.728 +
   1.729 +        // extract host:port
   1.730 +        start = sp;
   1.731 +        while ((*start == ' ' || *start == '\t') && start < end)
   1.732 +            start++;
   1.733 +
   1.734 +        // port defaults
   1.735 +        if (type == kProxyType_HTTP)
   1.736 +            port = 80;
   1.737 +        else
   1.738 +            port = 1080;
   1.739 +
   1.740 +        nsProxyInfo *pi = new nsProxyInfo();
   1.741 +        pi->mType = type;
   1.742 +        pi->mFlags = flags;
   1.743 +        pi->mResolveFlags = aResolveFlags;
   1.744 +        pi->mTimeout = mFailedProxyTimeout;
   1.745 +
   1.746 +        // www.foo.com:8080 and http://www.foo.com:8080
   1.747 +        nsDependentCSubstring maybeURL(start, end - start);
   1.748 +        nsCOMPtr<nsIURI> pacURI;
   1.749 +
   1.750 +        nsAutoCString urlHost;
   1.751 +        if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) &&
   1.752 +            NS_SUCCEEDED(pacURI->GetAsciiHost(urlHost)) &&
   1.753 +            !urlHost.IsEmpty()) {
   1.754 +            // http://www.example.com:8080
   1.755 +
   1.756 +            pi->mHost = urlHost;
   1.757 +
   1.758 +            int32_t tPort;
   1.759 +            if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
   1.760 +                port = tPort;
   1.761 +            }
   1.762 +            pi->mPort = port;
   1.763 +        }
   1.764 +        else {
   1.765 +            // www.example.com:8080
   1.766 +            if (start < end) {
   1.767 +                host = start;
   1.768 +                hostEnd = strchr(host, ':');
   1.769 +                if (!hostEnd || hostEnd > end) {
   1.770 +                    hostEnd = end;
   1.771 +                    // no port, so assume default
   1.772 +                }
   1.773 +                else {
   1.774 +                    port = atoi(hostEnd + 1);
   1.775 +                }
   1.776 +            }
   1.777 +            // YES, it is ok to specify a null proxy host.
   1.778 +            if (host) {
   1.779 +                pi->mHost.Assign(host, hostEnd - host);
   1.780 +                pi->mPort = port;
   1.781 +            }
   1.782 +        }
   1.783 +        NS_ADDREF(*result = pi);
   1.784 +    }
   1.785 +
   1.786 +    while (*end == ';' || *end == ' ' || *end == '\t')
   1.787 +        ++end;
   1.788 +    return end;
   1.789 +}
   1.790 +
   1.791 +void
   1.792 +nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key)
   1.793 +{
   1.794 +    key.AssignASCII(pi->mType);
   1.795 +    if (!pi->mHost.IsEmpty()) {
   1.796 +        key.Append(' ');
   1.797 +        key.Append(pi->mHost);
   1.798 +        key.Append(':');
   1.799 +        key.AppendInt(pi->mPort);
   1.800 +    }
   1.801 +} 
   1.802 +
   1.803 +uint32_t
   1.804 +nsProtocolProxyService::SecondsSinceSessionStart()
   1.805 +{
   1.806 +    PRTime now = PR_Now();
   1.807 +
   1.808 +    // get time elapsed since session start
   1.809 +    int64_t diff = now - mSessionStart;
   1.810 +
   1.811 +    // convert microseconds to seconds
   1.812 +    diff /= PR_USEC_PER_SEC;
   1.813 +
   1.814 +    // return converted 32 bit value
   1.815 +    return uint32_t(diff);
   1.816 +}
   1.817 +
   1.818 +void
   1.819 +nsProtocolProxyService::EnableProxy(nsProxyInfo *pi)
   1.820 +{
   1.821 +    nsAutoCString key;
   1.822 +    GetProxyKey(pi, key);
   1.823 +    mFailedProxies.Remove(key);
   1.824 +}
   1.825 +
   1.826 +void
   1.827 +nsProtocolProxyService::DisableProxy(nsProxyInfo *pi)
   1.828 +{
   1.829 +    nsAutoCString key;
   1.830 +    GetProxyKey(pi, key);
   1.831 +
   1.832 +    uint32_t dsec = SecondsSinceSessionStart();
   1.833 +
   1.834 +    // Add timeout to interval (this is the time when the proxy can
   1.835 +    // be tried again).
   1.836 +    dsec += pi->mTimeout;
   1.837 +
   1.838 +    // NOTE: The classic codebase would increase the timeout value
   1.839 +    //       incrementally each time a subsequent failure occurred.
   1.840 +    //       We could do the same, but it would require that we not
   1.841 +    //       remove proxy entries in IsProxyDisabled or otherwise
   1.842 +    //       change the way we are recording disabled proxies.
   1.843 +    //       Simpler is probably better for now, and at least the
   1.844 +    //       user can tune the timeout setting via preferences.
   1.845 +
   1.846 +    LOG(("DisableProxy %s %d\n", key.get(), dsec));
   1.847 +
   1.848 +    // If this fails, oh well... means we don't have enough memory
   1.849 +    // to remember the failed proxy.
   1.850 +    mFailedProxies.Put(key, dsec);
   1.851 +}
   1.852 +
   1.853 +bool
   1.854 +nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi)
   1.855 +{
   1.856 +    nsAutoCString key;
   1.857 +    GetProxyKey(pi, key);
   1.858 +
   1.859 +    uint32_t val;
   1.860 +    if (!mFailedProxies.Get(key, &val))
   1.861 +        return false;
   1.862 +
   1.863 +    uint32_t dsec = SecondsSinceSessionStart();
   1.864 +
   1.865 +    // if time passed has exceeded interval, then try proxy again.
   1.866 +    if (dsec > val) {
   1.867 +        mFailedProxies.Remove(key);
   1.868 +        return false;
   1.869 +    }
   1.870 +
   1.871 +    return true;
   1.872 +}
   1.873 +
   1.874 +nsresult
   1.875 +nsProtocolProxyService::SetupPACThread()
   1.876 +{
   1.877 +    if (mPACMan)
   1.878 +        return NS_OK;
   1.879 +
   1.880 +    mPACMan = new nsPACMan();
   1.881 +
   1.882 +    bool mainThreadOnly;
   1.883 +    nsresult rv;
   1.884 +    if (mSystemProxySettings &&
   1.885 +        NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
   1.886 +        !mainThreadOnly) {
   1.887 +        rv = mPACMan->Init(mSystemProxySettings);
   1.888 +    }
   1.889 +    else {
   1.890 +        rv = mPACMan->Init(nullptr);
   1.891 +    }
   1.892 +
   1.893 +    if (NS_FAILED(rv))
   1.894 +        mPACMan = nullptr;
   1.895 +    return rv;
   1.896 +}
   1.897 +
   1.898 +nsresult
   1.899 +nsProtocolProxyService::ResetPACThread()
   1.900 +{
   1.901 +    if (!mPACMan)
   1.902 +        return NS_OK;
   1.903 +
   1.904 +    mPACMan->Shutdown();
   1.905 +    mPACMan = nullptr;
   1.906 +    return SetupPACThread();
   1.907 +}
   1.908 +
   1.909 +nsresult
   1.910 +nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
   1.911 +                                         bool forceReload)
   1.912 +{
   1.913 +    SetupPACThread();
   1.914 +
   1.915 +    if (mPACMan->IsPACURI(spec) && !forceReload)
   1.916 +        return NS_OK;
   1.917 +
   1.918 +    mFailedProxies.Clear();
   1.919 +
   1.920 +    return mPACMan->LoadPACFromURI(spec);
   1.921 +}
   1.922 +
   1.923 +void
   1.924 +nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
   1.925 +                                         uint32_t aResolveFlags,
   1.926 +                                         nsIProxyInfo **result)
   1.927 +{
   1.928 +    if (pacString.IsEmpty()) {
   1.929 +        *result = nullptr;
   1.930 +        return;
   1.931 +    }
   1.932 +
   1.933 +    const char *proxies = pacString.get();
   1.934 +
   1.935 +    nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
   1.936 +    while (*proxies) {
   1.937 +        proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
   1.938 +        if (pi) {
   1.939 +            if (last) {
   1.940 +                NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
   1.941 +                last->mNext = pi;
   1.942 +            }
   1.943 +            else
   1.944 +                first = pi;
   1.945 +            last = pi;
   1.946 +        }
   1.947 +    }
   1.948 +    *result = first;
   1.949 +}
   1.950 +
   1.951 +// nsIProtocolProxyService2
   1.952 +NS_IMETHODIMP
   1.953 +nsProtocolProxyService::ReloadPAC()
   1.954 +{
   1.955 +    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.956 +    if (!prefs)
   1.957 +        return NS_OK;
   1.958 +
   1.959 +    int32_t type;
   1.960 +    nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
   1.961 +    if (NS_FAILED(rv))
   1.962 +        return NS_OK;
   1.963 +
   1.964 +    nsXPIDLCString pacSpec;
   1.965 +    if (type == PROXYCONFIG_PAC)
   1.966 +        prefs->GetCharPref(PROXY_PREF("autoconfig_url"), getter_Copies(pacSpec));
   1.967 +    else if (type == PROXYCONFIG_WPAD)
   1.968 +        pacSpec.AssignLiteral(WPAD_URL);
   1.969 +
   1.970 +    if (!pacSpec.IsEmpty())
   1.971 +        ConfigureFromPAC(pacSpec, true);
   1.972 +    return NS_OK;
   1.973 +}
   1.974 +
   1.975 +// When sync interface is removed this can go away too
   1.976 +// The nsPACManCallback portion of this implementation should be run
   1.977 +// off the main thread, because it uses a condvar for signaling and
   1.978 +// the main thread is blocking on that condvar -
   1.979 +//  so call nsPACMan::AsyncGetProxyForChannel() with
   1.980 +// a false mainThreadResponse parameter.
   1.981 +class nsAsyncBridgeRequest MOZ_FINAL  : public nsPACManCallback
   1.982 +{
   1.983 +    NS_DECL_THREADSAFE_ISUPPORTS
   1.984 +
   1.985 +     nsAsyncBridgeRequest()
   1.986 +        : mMutex("nsDeprecatedCallback")
   1.987 +        , mCondVar(mMutex, "nsDeprecatedCallback")
   1.988 +        , mCompleted(false)
   1.989 +    {
   1.990 +    }
   1.991 +
   1.992 +    void OnQueryComplete(nsresult status,
   1.993 +                         const nsCString &pacString,
   1.994 +                         const nsCString &newPACURL)
   1.995 +    {
   1.996 +        MutexAutoLock lock(mMutex);
   1.997 +        mCompleted = true;
   1.998 +        mStatus = status;
   1.999 +        mPACString = pacString;
  1.1000 +        mPACURL = newPACURL;
  1.1001 +        mCondVar.Notify();
  1.1002 +    }
  1.1003 +
  1.1004 +    void Lock()   { mMutex.Lock(); }
  1.1005 +    void Unlock() { mMutex.Unlock(); }
  1.1006 +    void Wait()   { mCondVar.Wait(PR_SecondsToInterval(3)); }
  1.1007 +
  1.1008 +private:
  1.1009 +    ~nsAsyncBridgeRequest()
  1.1010 +    {
  1.1011 +    }
  1.1012 +
  1.1013 +    friend class nsProtocolProxyService;
  1.1014 +
  1.1015 +    Mutex    mMutex;
  1.1016 +    CondVar  mCondVar;
  1.1017 +
  1.1018 +    nsresult  mStatus;
  1.1019 +    nsCString mPACString;
  1.1020 +    nsCString mPACURL;
  1.1021 +    bool      mCompleted;
  1.1022 +};
  1.1023 +NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
  1.1024 +
  1.1025 +// nsProtocolProxyService
  1.1026 +nsresult
  1.1027 +nsProtocolProxyService::DeprecatedBlockingResolve(nsIChannel *aChannel,
  1.1028 +                                                  uint32_t aFlags,
  1.1029 +                                                  nsIProxyInfo **retval)
  1.1030 +{
  1.1031 +    NS_ENSURE_ARG_POINTER(aChannel);
  1.1032 +
  1.1033 +    nsCOMPtr<nsIURI> uri;
  1.1034 +    aChannel->GetURI(getter_AddRefs(uri));
  1.1035 +
  1.1036 +    nsProtocolInfo info;
  1.1037 +    nsresult rv = GetProtocolInfo(uri, &info);
  1.1038 +    if (NS_FAILED(rv))
  1.1039 +        return rv;
  1.1040 +
  1.1041 +    nsCOMPtr<nsIProxyInfo> pi;
  1.1042 +    bool usePACThread;
  1.1043 +
  1.1044 +    // SystemProxySettings and PAC files can block the main thread
  1.1045 +    // but if neither of them are in use, we can just do the work
  1.1046 +    // right here and directly invoke the callback
  1.1047 +
  1.1048 +    rv = Resolve_Internal(aChannel, info, aFlags, &usePACThread, getter_AddRefs(pi));
  1.1049 +    if (NS_FAILED(rv))
  1.1050 +        return rv;
  1.1051 +
  1.1052 +    if (!usePACThread || !mPACMan) {
  1.1053 +        ApplyFilters(aChannel, info, pi);
  1.1054 +        pi.forget(retval);
  1.1055 +        return NS_OK;
  1.1056 +    }
  1.1057 +
  1.1058 +    // Use the PAC thread to do the work, so we don't have to reimplement that
  1.1059 +    // code, but block this thread on that completion.
  1.1060 +    nsRefPtr<nsAsyncBridgeRequest> ctx = new nsAsyncBridgeRequest();
  1.1061 +    ctx->Lock();
  1.1062 +    if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForChannel(aChannel, ctx, false))) {
  1.1063 +        // this can really block the main thread, so cap it at 3 seconds
  1.1064 +       ctx->Wait();
  1.1065 +    }
  1.1066 +    ctx->Unlock();
  1.1067 +    if (!ctx->mCompleted)
  1.1068 +        return NS_ERROR_FAILURE;
  1.1069 +    if (NS_FAILED(ctx->mStatus))
  1.1070 +        return ctx->mStatus;
  1.1071 +
  1.1072 +    // pretty much duplicate real DoCallback logic
  1.1073 +
  1.1074 +    // Generate proxy info from the PAC string if appropriate
  1.1075 +    if (!ctx->mPACString.IsEmpty()) {
  1.1076 +        LOG(("sync pac thread callback %s\n", ctx->mPACString.get()));
  1.1077 +        ProcessPACString(ctx->mPACString, 0, getter_AddRefs(pi));
  1.1078 +        ApplyFilters(aChannel, info, pi);
  1.1079 +        pi.forget(retval);
  1.1080 +        return NS_OK;
  1.1081 +    }
  1.1082 +
  1.1083 +    if (!ctx->mPACURL.IsEmpty()) {
  1.1084 +        NS_WARNING("sync pac thread callback indicates new pac file load\n");
  1.1085 +        // This is a problem and is one of the reasons this blocking interface
  1.1086 +        // is deprecated. The main loop needs to spin to make this reload happen. So
  1.1087 +        // we are going to kick off the reload and return an error - it will work
  1.1088 +        // next time. Because this sync interface is only used in the java plugin it
  1.1089 +        // is extremely likely that the pac file has already been loaded anyhow.
  1.1090 +
  1.1091 +        rv = ConfigureFromPAC(ctx->mPACURL, false);
  1.1092 +        if (NS_FAILED(rv))
  1.1093 +            return rv;
  1.1094 +        return NS_ERROR_NOT_AVAILABLE;
  1.1095 +    }
  1.1096 +
  1.1097 +    *retval = nullptr;
  1.1098 +    return NS_OK;
  1.1099 +}
  1.1100 +
  1.1101 +nsresult
  1.1102 +nsProtocolProxyService::AsyncResolveInternal(nsIChannel *channel, uint32_t flags,
  1.1103 +                                             nsIProtocolProxyCallback *callback,
  1.1104 +                                             nsICancelable **result,
  1.1105 +                                             bool isSyncOK)
  1.1106 +{
  1.1107 +    NS_ENSURE_ARG_POINTER(channel);
  1.1108 +    NS_ENSURE_ARG_POINTER(callback);
  1.1109 +
  1.1110 +    nsCOMPtr<nsIURI> uri;
  1.1111 +    channel->GetURI(getter_AddRefs(uri));
  1.1112 +
  1.1113 +    *result = nullptr;
  1.1114 +    nsRefPtr<nsAsyncResolveRequest> ctx =
  1.1115 +        new nsAsyncResolveRequest(this, channel, flags, callback);
  1.1116 +
  1.1117 +    nsProtocolInfo info;
  1.1118 +    nsresult rv = GetProtocolInfo(uri, &info);
  1.1119 +    if (NS_FAILED(rv))
  1.1120 +        return rv;
  1.1121 +
  1.1122 +    nsCOMPtr<nsIProxyInfo> pi;
  1.1123 +    bool usePACThread;
  1.1124 +
  1.1125 +    // SystemProxySettings and PAC files can block the main thread
  1.1126 +    // but if neither of them are in use, we can just do the work
  1.1127 +    // right here and directly invoke the callback
  1.1128 +
  1.1129 +    rv = Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
  1.1130 +    if (NS_FAILED(rv))
  1.1131 +        return rv;
  1.1132 +
  1.1133 +    if (!usePACThread || !mPACMan) {
  1.1134 +        // we can do it locally
  1.1135 +        ApplyFilters(channel, info, pi);
  1.1136 +        ctx->SetResult(NS_OK, pi);
  1.1137 +        if (isSyncOK) {
  1.1138 +            ctx->Run();
  1.1139 +            return NS_OK;
  1.1140 +        }
  1.1141 +
  1.1142 +        rv = ctx->DispatchCallback();
  1.1143 +        if (NS_SUCCEEDED(rv))
  1.1144 +            ctx.forget(result);
  1.1145 +        return rv;
  1.1146 +    }
  1.1147 +
  1.1148 +    // else kick off a PAC thread query
  1.1149 +
  1.1150 +    rv = mPACMan->AsyncGetProxyForChannel(channel, ctx, true);
  1.1151 +    if (NS_SUCCEEDED(rv))
  1.1152 +        ctx.forget(result);
  1.1153 +    return rv;
  1.1154 +}
  1.1155 +
  1.1156 +// nsIProtocolProxyService
  1.1157 +NS_IMETHODIMP
  1.1158 +nsProtocolProxyService::AsyncResolve2(nsIChannel *channel, uint32_t flags,
  1.1159 +                                      nsIProtocolProxyCallback *callback,
  1.1160 +                                      nsICancelable **result)
  1.1161 +{
  1.1162 +    return AsyncResolveInternal(channel, flags, callback, result, true);
  1.1163 +}
  1.1164 +
  1.1165 +NS_IMETHODIMP
  1.1166 +nsProtocolProxyService::AsyncResolve(nsIChannel *channel, uint32_t flags,
  1.1167 +                                     nsIProtocolProxyCallback *callback,
  1.1168 +                                     nsICancelable **result)
  1.1169 +{
  1.1170 +    return AsyncResolveInternal(channel, flags, callback, result, false);
  1.1171 +}
  1.1172 +
  1.1173 +NS_IMETHODIMP
  1.1174 +nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
  1.1175 +                                     const nsACString &aHost,
  1.1176 +                                     int32_t aPort,
  1.1177 +                                     uint32_t aFlags,
  1.1178 +                                     uint32_t aFailoverTimeout,
  1.1179 +                                     nsIProxyInfo *aFailoverProxy,
  1.1180 +                                     nsIProxyInfo **aResult)
  1.1181 +{
  1.1182 +    static const char *types[] = {
  1.1183 +        kProxyType_HTTP,
  1.1184 +        kProxyType_SOCKS,
  1.1185 +        kProxyType_SOCKS4,
  1.1186 +        kProxyType_DIRECT
  1.1187 +    };
  1.1188 +
  1.1189 +    // resolve type; this allows us to avoid copying the type string into each
  1.1190 +    // proxy info instance.  we just reference the string literals directly :)
  1.1191 +    const char *type = nullptr;
  1.1192 +    for (uint32_t i=0; i<ArrayLength(types); ++i) {
  1.1193 +        if (aType.LowerCaseEqualsASCII(types[i])) {
  1.1194 +            type = types[i];
  1.1195 +            break;
  1.1196 +        }
  1.1197 +    }
  1.1198 +    NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
  1.1199 +
  1.1200 +    return NewProxyInfo_Internal(type, aHost, aPort,
  1.1201 +                                 mSOCKSProxyUsername, mSOCKSProxyPassword,
  1.1202 +                                 aFlags, aFailoverTimeout,
  1.1203 +                                 aFailoverProxy, 0, aResult);
  1.1204 +}
  1.1205 +
  1.1206 +NS_IMETHODIMP
  1.1207 +nsProtocolProxyService::NewSOCKSProxyInfo(const nsACString &aHost,
  1.1208 +                                          int32_t aPort,
  1.1209 +                                          const nsACString &aUsername,
  1.1210 +                                          const nsACString &aPassword,
  1.1211 +                                          uint32_t aFlags,
  1.1212 +                                          uint32_t aFailoverTimeout,
  1.1213 +                                          nsIProxyInfo *aFailoverProxy,
  1.1214 +                                          nsIProxyInfo **aResult)
  1.1215 +{
  1.1216 +    return NewProxyInfo_Internal(kProxyType_SOCKS, aHost, aPort,
  1.1217 +                                 aUsername, aPassword,
  1.1218 +                                 aFlags, aFailoverTimeout,
  1.1219 +                                 aFailoverProxy, 0, aResult);
  1.1220 +}
  1.1221 +
  1.1222 +NS_IMETHODIMP
  1.1223 +nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo  *aProxy,
  1.1224 +                                            nsIURI        *aURI,
  1.1225 +                                            nsresult       aStatus,
  1.1226 +                                            nsIProxyInfo **aResult)
  1.1227 +{
  1.1228 +    // We only support failover when a PAC file is configured, either
  1.1229 +    // directly or via system settings
  1.1230 +    if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
  1.1231 +        mProxyConfig != PROXYCONFIG_SYSTEM)
  1.1232 +        return NS_ERROR_NOT_AVAILABLE;
  1.1233 +
  1.1234 +    // Verify that |aProxy| is one of our nsProxyInfo objects.
  1.1235 +    nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
  1.1236 +    NS_ENSURE_ARG(pi);
  1.1237 +    // OK, the QI checked out.  We can proceed.
  1.1238 +
  1.1239 +    // Remember that this proxy is down.
  1.1240 +    DisableProxy(pi);
  1.1241 +
  1.1242 +    // NOTE: At this point, we might want to prompt the user if we have
  1.1243 +    //       not already tried going DIRECT.  This is something that the
  1.1244 +    //       classic codebase supported; however, IE6 does not prompt.
  1.1245 +
  1.1246 +    if (!pi->mNext)
  1.1247 +        return NS_ERROR_NOT_AVAILABLE;
  1.1248 +
  1.1249 +    LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
  1.1250 +        pi->mType, pi->mHost.get(), pi->mPort,
  1.1251 +        pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
  1.1252 +
  1.1253 +    NS_ADDREF(*aResult = pi->mNext);
  1.1254 +    return NS_OK;
  1.1255 +}
  1.1256 +
  1.1257 +nsresult
  1.1258 +nsProtocolProxyService::InsertFilterLink(FilterLink *link, uint32_t position)
  1.1259 +{
  1.1260 +    if (!mFilters) {
  1.1261 +        mFilters = link;
  1.1262 +        return NS_OK;
  1.1263 +    }
  1.1264 +
  1.1265 +    // insert into mFilters in sorted order
  1.1266 +    FilterLink *last = nullptr;
  1.1267 +    for (FilterLink *iter = mFilters; iter; iter = iter->next) {
  1.1268 +        if (position < iter->position) {
  1.1269 +            if (last) {
  1.1270 +                link->next = last->next;
  1.1271 +                last->next = link;
  1.1272 +            }
  1.1273 +            else {
  1.1274 +                link->next = mFilters;
  1.1275 +                mFilters = link;
  1.1276 +            }
  1.1277 +            return NS_OK;
  1.1278 +        }
  1.1279 +        last = iter;
  1.1280 +    }
  1.1281 +    // our position is equal to or greater than the last link in the list
  1.1282 +    last->next = link;
  1.1283 +    return NS_OK;
  1.1284 +}
  1.1285 +
  1.1286 +NS_IMETHODIMP
  1.1287 +nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
  1.1288 +                                       uint32_t position)
  1.1289 +{
  1.1290 +    UnregisterFilter(filter); // remove this filter if we already have it
  1.1291 +
  1.1292 +    FilterLink *link = new FilterLink(position, filter);
  1.1293 +    if (!link)
  1.1294 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1295 +    return InsertFilterLink(link, position);
  1.1296 +}
  1.1297 +
  1.1298 +NS_IMETHODIMP
  1.1299 +nsProtocolProxyService::RegisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter,
  1.1300 +                                              uint32_t position)
  1.1301 +{
  1.1302 +    UnregisterChannelFilter(channelFilter);  // remove this filter if we already have it
  1.1303 +
  1.1304 +    FilterLink *link = new FilterLink(position, channelFilter);
  1.1305 +    if (!link)
  1.1306 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1307 +    return InsertFilterLink(link, position);
  1.1308 +}
  1.1309 +
  1.1310 +nsresult
  1.1311 +nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject)
  1.1312 +{
  1.1313 +    FilterLink *last = nullptr;
  1.1314 +    for (FilterLink *iter = mFilters; iter; iter = iter->next) {
  1.1315 +        nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
  1.1316 +        if (object == givenObject) {
  1.1317 +            if (last)
  1.1318 +                last->next = iter->next;
  1.1319 +            else
  1.1320 +                mFilters = iter->next;
  1.1321 +            iter->next = nullptr;
  1.1322 +            delete iter;
  1.1323 +            return NS_OK;
  1.1324 +        }
  1.1325 +        last = iter;
  1.1326 +    }
  1.1327 +
  1.1328 +    // No need to throw an exception in this case.
  1.1329 +    return NS_OK;
  1.1330 +}
  1.1331 +
  1.1332 +NS_IMETHODIMP
  1.1333 +nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter) {
  1.1334 +    // QI to nsISupports so we can safely test object identity.
  1.1335 +    nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
  1.1336 +    return RemoveFilterLink(givenObject);
  1.1337 +}
  1.1338 +
  1.1339 +NS_IMETHODIMP
  1.1340 +nsProtocolProxyService::UnregisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter) {
  1.1341 +    // QI to nsISupports so we can safely test object identity.
  1.1342 +    nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
  1.1343 +    return RemoveFilterLink(givenObject);
  1.1344 +}
  1.1345 +
  1.1346 +NS_IMETHODIMP
  1.1347 +nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType)
  1.1348 +{
  1.1349 +  *aProxyConfigType = mProxyConfig;
  1.1350 +  return NS_OK;
  1.1351 +}
  1.1352 +
  1.1353 +void
  1.1354 +nsProtocolProxyService::LoadHostFilters(const char *filters)
  1.1355 +{
  1.1356 +    // check to see the owners flag? /!?/ TODO
  1.1357 +    if (mHostFiltersArray.Length() > 0) {
  1.1358 +        mHostFiltersArray.Clear();
  1.1359 +    }
  1.1360 +
  1.1361 +    if (!filters)
  1.1362 +        return; // fail silently...
  1.1363 +
  1.1364 +    //
  1.1365 +    // filter  = ( host | domain | ipaddr ["/" mask] ) [":" port] 
  1.1366 +    // filters = filter *( "," LWS filter)
  1.1367 +    //
  1.1368 +    // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref string
  1.1369 +    mFilterLocalHosts = false;
  1.1370 +    while (*filters) {
  1.1371 +        // skip over spaces and ,
  1.1372 +        while (*filters && (*filters == ',' || IS_ASCII_SPACE(*filters)))
  1.1373 +            filters++;
  1.1374 +
  1.1375 +        const char *starthost = filters;
  1.1376 +        const char *endhost = filters + 1; // at least that...
  1.1377 +        const char *portLocation = 0; 
  1.1378 +        const char *maskLocation = 0;
  1.1379 +
  1.1380 +        while (*endhost && (*endhost != ',' && !IS_ASCII_SPACE(*endhost))) {
  1.1381 +            if (*endhost == ':')
  1.1382 +                portLocation = endhost;
  1.1383 +            else if (*endhost == '/')
  1.1384 +                maskLocation = endhost;
  1.1385 +            else if (*endhost == ']') // IPv6 address literals
  1.1386 +                portLocation = 0;
  1.1387 +            endhost++;
  1.1388 +        }
  1.1389 +
  1.1390 +        filters = endhost; // advance iterator up front
  1.1391 +
  1.1392 +        // locate end of host
  1.1393 +        const char *end = maskLocation ? maskLocation :
  1.1394 +                          portLocation ? portLocation :
  1.1395 +                          endhost;
  1.1396 +
  1.1397 +        nsAutoCString str(starthost, end - starthost);
  1.1398 +
  1.1399 +        // If the current host filter is "<local>", then all local (i.e.
  1.1400 +        // no dots in the hostname) hosts should bypass the proxy
  1.1401 +        if (str.EqualsIgnoreCase("<local>")) {
  1.1402 +            mFilterLocalHosts = true;
  1.1403 +            LOG(("loaded filter for local hosts "
  1.1404 +                 "(plain host names, no dots)\n"));
  1.1405 +            // Continue to next host filter;
  1.1406 +            continue;
  1.1407 +        }
  1.1408 +
  1.1409 +        // For all other host filters, create HostInfo object and add to list
  1.1410 +        HostInfo *hinfo = new HostInfo();
  1.1411 +        hinfo->port = portLocation ? atoi(portLocation + 1) : 0;
  1.1412 +
  1.1413 +        PRNetAddr addr;
  1.1414 +        if (PR_StringToNetAddr(str.get(), &addr) == PR_SUCCESS) {
  1.1415 +            hinfo->is_ipaddr   = true;
  1.1416 +            hinfo->ip.family   = PR_AF_INET6; // we always store address as IPv6
  1.1417 +            hinfo->ip.mask_len = maskLocation ? atoi(maskLocation + 1) : 128;
  1.1418 +
  1.1419 +            if (hinfo->ip.mask_len == 0) {
  1.1420 +                NS_WARNING("invalid mask");
  1.1421 +                goto loser;
  1.1422 +            }
  1.1423 +
  1.1424 +            if (addr.raw.family == PR_AF_INET) {
  1.1425 +                // convert to IPv4-mapped address
  1.1426 +                PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
  1.1427 +                // adjust mask_len accordingly
  1.1428 +                if (hinfo->ip.mask_len <= 32)
  1.1429 +                    hinfo->ip.mask_len += 96;
  1.1430 +            }
  1.1431 +            else if (addr.raw.family == PR_AF_INET6) {
  1.1432 +                // copy the address
  1.1433 +                memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
  1.1434 +            }
  1.1435 +            else {
  1.1436 +                NS_WARNING("unknown address family");
  1.1437 +                goto loser;
  1.1438 +            }
  1.1439 +
  1.1440 +            // apply mask to IPv6 address
  1.1441 +            proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
  1.1442 +        }
  1.1443 +        else {
  1.1444 +            uint32_t startIndex, endIndex;
  1.1445 +            if (str.First() == '*')
  1.1446 +                startIndex = 1; // *.domain -> .domain
  1.1447 +            else
  1.1448 +                startIndex = 0;
  1.1449 +            endIndex = (portLocation ? portLocation : endhost) - starthost;
  1.1450 +
  1.1451 +            hinfo->is_ipaddr = false;
  1.1452 +            hinfo->name.host = ToNewCString(Substring(str, startIndex, endIndex));
  1.1453 +
  1.1454 +            if (!hinfo->name.host)
  1.1455 +                goto loser;
  1.1456 +
  1.1457 +            hinfo->name.host_len = endIndex - startIndex;
  1.1458 +        }
  1.1459 +
  1.1460 +//#define DEBUG_DUMP_FILTERS
  1.1461 +#ifdef DEBUG_DUMP_FILTERS
  1.1462 +        printf("loaded filter[%u]:\n", mHostFiltersArray.Length());
  1.1463 +        printf("  is_ipaddr = %u\n", hinfo->is_ipaddr);
  1.1464 +        printf("  port = %u\n", hinfo->port);
  1.1465 +        if (hinfo->is_ipaddr) {
  1.1466 +            printf("  ip.family = %x\n", hinfo->ip.family);
  1.1467 +            printf("  ip.mask_len = %u\n", hinfo->ip.mask_len);
  1.1468 +
  1.1469 +            PRNetAddr netAddr;
  1.1470 +            PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
  1.1471 +            memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
  1.1472 +
  1.1473 +            char buf[256];
  1.1474 +            PR_NetAddrToString(&netAddr, buf, sizeof(buf));
  1.1475 +
  1.1476 +            printf("  ip.addr = %s\n", buf);
  1.1477 +        }
  1.1478 +        else {
  1.1479 +            printf("  name.host = %s\n", hinfo->name.host);
  1.1480 +        }
  1.1481 +#endif
  1.1482 +
  1.1483 +        mHostFiltersArray.AppendElement(hinfo);
  1.1484 +        hinfo = nullptr;
  1.1485 +loser:
  1.1486 +        delete hinfo;
  1.1487 +    }
  1.1488 +}
  1.1489 +
  1.1490 +nsresult
  1.1491 +nsProtocolProxyService::GetProtocolInfo(nsIURI *uri, nsProtocolInfo *info)
  1.1492 +{
  1.1493 +    NS_PRECONDITION(uri, "URI is null");
  1.1494 +    NS_PRECONDITION(info, "info is null");
  1.1495 +
  1.1496 +    nsresult rv;
  1.1497 +
  1.1498 +    rv = uri->GetScheme(info->scheme);
  1.1499 +    if (NS_FAILED(rv))
  1.1500 +        return rv;
  1.1501 +
  1.1502 +    nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
  1.1503 +    if (NS_FAILED(rv))
  1.1504 +        return rv;
  1.1505 +
  1.1506 +    nsCOMPtr<nsIProtocolHandler> handler;
  1.1507 +    rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
  1.1508 +    if (NS_FAILED(rv))
  1.1509 +        return rv;
  1.1510 +
  1.1511 +    rv = handler->GetProtocolFlags(&info->flags);
  1.1512 +    if (NS_FAILED(rv))
  1.1513 +        return rv;
  1.1514 +
  1.1515 +    rv = handler->GetDefaultPort(&info->defaultPort);
  1.1516 +    return rv;
  1.1517 +}
  1.1518 +
  1.1519 +nsresult
  1.1520 +nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
  1.1521 +                                              const nsACString &aHost,
  1.1522 +                                              int32_t aPort,
  1.1523 +                                              const nsACString &aUsername,
  1.1524 +                                              const nsACString &aPassword,
  1.1525 +                                              uint32_t aFlags,
  1.1526 +                                              uint32_t aFailoverTimeout,
  1.1527 +                                              nsIProxyInfo *aFailoverProxy,
  1.1528 +                                              uint32_t aResolveFlags,
  1.1529 +                                              nsIProxyInfo **aResult)
  1.1530 +{
  1.1531 +    if (aPort <= 0)
  1.1532 +        aPort = -1;
  1.1533 +
  1.1534 +    nsCOMPtr<nsProxyInfo> failover;
  1.1535 +    if (aFailoverProxy) {
  1.1536 +        failover = do_QueryInterface(aFailoverProxy);
  1.1537 +        NS_ENSURE_ARG(failover);
  1.1538 +    }
  1.1539 +
  1.1540 +    nsProxyInfo *proxyInfo = new nsProxyInfo();
  1.1541 +    if (!proxyInfo)
  1.1542 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1543 +
  1.1544 +    proxyInfo->mType = aType;
  1.1545 +    proxyInfo->mHost = aHost;
  1.1546 +    proxyInfo->mPort = aPort;
  1.1547 +    proxyInfo->mUsername = aUsername;
  1.1548 +    proxyInfo->mPassword = aPassword;
  1.1549 +    proxyInfo->mFlags = aFlags;
  1.1550 +    proxyInfo->mResolveFlags = aResolveFlags;
  1.1551 +    proxyInfo->mTimeout = aFailoverTimeout == UINT32_MAX
  1.1552 +        ? mFailedProxyTimeout : aFailoverTimeout;
  1.1553 +    failover.swap(proxyInfo->mNext);
  1.1554 +
  1.1555 +    NS_ADDREF(*aResult = proxyInfo);
  1.1556 +    return NS_OK;
  1.1557 +}
  1.1558 +
  1.1559 +nsresult
  1.1560 +nsProtocolProxyService::Resolve_Internal(nsIChannel *channel,
  1.1561 +                                         const nsProtocolInfo &info,
  1.1562 +                                         uint32_t flags,
  1.1563 +                                         bool *usePACThread,
  1.1564 +                                         nsIProxyInfo **result)
  1.1565 +{
  1.1566 +    NS_ENSURE_ARG_POINTER(channel);
  1.1567 +    nsresult rv = SetupPACThread();
  1.1568 +    if (NS_FAILED(rv))
  1.1569 +        return rv;
  1.1570 +
  1.1571 +    *usePACThread = false;
  1.1572 +    *result = nullptr;
  1.1573 +
  1.1574 +    if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
  1.1575 +        return NS_OK;  // Can't proxy this (filters may not override)
  1.1576 +
  1.1577 +    nsCOMPtr<nsIURI> uri;
  1.1578 +    channel->GetURI(getter_AddRefs(uri));
  1.1579 +
  1.1580 +    // See bug #586908.
  1.1581 +    // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
  1.1582 +    // here means that we will not use a proxy for this connection.
  1.1583 +    if (mPACMan && mPACMan->IsPACURI(uri))
  1.1584 +        return NS_OK;
  1.1585 +
  1.1586 +    bool mainThreadOnly;
  1.1587 +    if (mSystemProxySettings &&
  1.1588 +        mProxyConfig == PROXYCONFIG_SYSTEM &&
  1.1589 +        NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
  1.1590 +        !mainThreadOnly) {
  1.1591 +        *usePACThread = true;
  1.1592 +        return NS_OK;
  1.1593 +    }
  1.1594 +
  1.1595 +    if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) {
  1.1596 +        // If the system proxy setting implementation is not threadsafe (e.g
  1.1597 +        // linux gconf), we'll do it inline here. Such implementations promise
  1.1598 +        // not to block
  1.1599 +
  1.1600 +        nsAutoCString PACURI;
  1.1601 +        nsAutoCString pacString;
  1.1602 +
  1.1603 +        if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
  1.1604 +            !PACURI.IsEmpty()) {
  1.1605 +            // There is a PAC URI configured. If it is unchanged, then
  1.1606 +            // just execute the PAC thread. If it is changed then load
  1.1607 +            // the new value
  1.1608 +
  1.1609 +            if (mPACMan && mPACMan->IsPACURI(PACURI)) {
  1.1610 +                // unchanged
  1.1611 +                *usePACThread = true;
  1.1612 +                return NS_OK;
  1.1613 +            }
  1.1614 +
  1.1615 +            ConfigureFromPAC(PACURI, false);
  1.1616 +            return NS_OK;
  1.1617 +        }
  1.1618 +
  1.1619 +        nsAutoCString spec;
  1.1620 +        nsAutoCString host;
  1.1621 +        nsAutoCString scheme;
  1.1622 +        int32_t port = -1;
  1.1623 +
  1.1624 +        uri->GetAsciiSpec(spec);
  1.1625 +        uri->GetAsciiHost(host);
  1.1626 +        uri->GetScheme(scheme);
  1.1627 +        uri->GetPort(&port);
  1.1628 +
  1.1629 +        // now try the system proxy settings for this particular url
  1.1630 +        if (NS_SUCCEEDED(mSystemProxySettings->
  1.1631 +                         GetProxyForURI(spec, scheme, host, port,
  1.1632 +                                        pacString))) {
  1.1633 +            ProcessPACString(pacString, 0, result);
  1.1634 +            return NS_OK;
  1.1635 +        }
  1.1636 +    }
  1.1637 +
  1.1638 +    // if proxies are enabled and this host:port combo is supposed to use a
  1.1639 +    // proxy, check for a proxy.
  1.1640 +    if (mProxyConfig == PROXYCONFIG_DIRECT ||
  1.1641 +        (mProxyConfig == PROXYCONFIG_MANUAL &&
  1.1642 +         !CanUseProxy(uri, info.defaultPort)))
  1.1643 +        return NS_OK;
  1.1644 +
  1.1645 +    // Proxy auto config magic...
  1.1646 +    if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD) {
  1.1647 +        // Do not query PAC now.
  1.1648 +        *usePACThread = true;
  1.1649 +        return NS_OK;
  1.1650 +    }
  1.1651 +
  1.1652 +    // If we aren't in manual proxy configuration mode then we don't
  1.1653 +    // want to honor any manual specific prefs that might be still set
  1.1654 +    if (mProxyConfig != PROXYCONFIG_MANUAL)
  1.1655 +        return NS_OK;
  1.1656 +
  1.1657 +    // proxy info values for manual configuration mode
  1.1658 +    const char *type = nullptr;
  1.1659 +    const nsACString *host = nullptr;
  1.1660 +    int32_t port = -1;
  1.1661 +
  1.1662 +    uint32_t proxyFlags = 0;
  1.1663 +
  1.1664 +    if ((flags & RESOLVE_PREFER_SOCKS_PROXY) &&
  1.1665 +        !mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
  1.1666 +      host = &mSOCKSProxyHost;
  1.1667 +      if (mSOCKSProxyVersion == 4)
  1.1668 +          type = kProxyType_SOCKS4;
  1.1669 +      else
  1.1670 +          type = kProxyType_SOCKS;
  1.1671 +      port = mSOCKSProxyPort;
  1.1672 +      if (mSOCKSProxyRemoteDNS)
  1.1673 +          proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
  1.1674 +    }
  1.1675 +    else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
  1.1676 +             !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
  1.1677 +        host = &mHTTPSProxyHost;
  1.1678 +        type = kProxyType_HTTP;
  1.1679 +        port = mHTTPSProxyPort;
  1.1680 +    }
  1.1681 +    else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
  1.1682 +             ((flags & RESOLVE_IGNORE_URI_SCHEME) ||
  1.1683 +              info.scheme.EqualsLiteral("http"))) {
  1.1684 +        host = &mHTTPProxyHost;
  1.1685 +        type = kProxyType_HTTP;
  1.1686 +        port = mHTTPProxyPort;
  1.1687 +    }
  1.1688 +    else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
  1.1689 +             !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
  1.1690 +             info.scheme.EqualsLiteral("https")) {
  1.1691 +        host = &mHTTPSProxyHost;
  1.1692 +        type = kProxyType_HTTP;
  1.1693 +        port = mHTTPSProxyPort;
  1.1694 +    }
  1.1695 +    else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 &&
  1.1696 +             !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
  1.1697 +             info.scheme.EqualsLiteral("ftp")) {
  1.1698 +        host = &mFTPProxyHost;
  1.1699 +        type = kProxyType_HTTP;
  1.1700 +        port = mFTPProxyPort;
  1.1701 +    }
  1.1702 +    else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
  1.1703 +        host = &mSOCKSProxyHost;
  1.1704 +        if (mSOCKSProxyVersion == 4)
  1.1705 +            type = kProxyType_SOCKS4;
  1.1706 +        else
  1.1707 +            type = kProxyType_SOCKS;
  1.1708 +        port = mSOCKSProxyPort;
  1.1709 +        if (mSOCKSProxyRemoteDNS)
  1.1710 +            proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
  1.1711 +    }
  1.1712 +
  1.1713 +    if (type) {
  1.1714 +        rv = NewProxyInfo_Internal(type, *host, port,
  1.1715 +                                   mSOCKSProxyUsername, mSOCKSProxyPassword,
  1.1716 +                                   proxyFlags, UINT32_MAX, nullptr, flags,
  1.1717 +                                   result);
  1.1718 +        if (NS_FAILED(rv))
  1.1719 +            return rv;
  1.1720 +    }
  1.1721 +
  1.1722 +    return NS_OK;
  1.1723 +}
  1.1724 +
  1.1725 +void
  1.1726 +nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo *aProxy)
  1.1727 +{
  1.1728 +    // Disable Prefetch in the DNS service if a proxy is in use.
  1.1729 +    if (!aProxy)
  1.1730 +        return;
  1.1731 +
  1.1732 +    nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
  1.1733 +    if (!pi ||
  1.1734 +        !pi->mType ||
  1.1735 +        pi->mType == kProxyType_DIRECT)
  1.1736 +        return;
  1.1737 +
  1.1738 +    nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
  1.1739 +    if (!dns)
  1.1740 +        return;
  1.1741 +    nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
  1.1742 +    if (!pdns)
  1.1743 +        return;
  1.1744 +
  1.1745 +    // We lose the prefetch optimization for the life of the dns service.
  1.1746 +    pdns->SetPrefetchEnabled(false);
  1.1747 +}
  1.1748 +
  1.1749 +void
  1.1750 +nsProtocolProxyService::ApplyFilters(nsIChannel *channel, const nsProtocolInfo &info,
  1.1751 +                                     nsIProxyInfo **list)
  1.1752 +{
  1.1753 +    if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
  1.1754 +        return;
  1.1755 +
  1.1756 +    // We prune the proxy list prior to invoking each filter.  This may be
  1.1757 +    // somewhat inefficient, but it seems like a good idea since we want each
  1.1758 +    // filter to "see" a valid proxy list.
  1.1759 +
  1.1760 +    nsresult rv;
  1.1761 +    nsCOMPtr<nsIProxyInfo> result;
  1.1762 +
  1.1763 +    for (FilterLink *iter = mFilters; iter; iter = iter->next) {
  1.1764 +        PruneProxyInfo(info, list);
  1.1765 +        if (!!iter->filter) {
  1.1766 +          nsCOMPtr<nsIURI> uri;
  1.1767 +          channel->GetURI(getter_AddRefs(uri));
  1.1768 +          if (!!uri) {
  1.1769 +            nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
  1.1770 +            nsCOMPtr<nsIURI> proxyURI = nullptr;
  1.1771 +            if (!!httpChannel) {
  1.1772 +              httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
  1.1773 +            }
  1.1774 +            rv = iter->filter->ApplyFilter(this, proxyURI ? proxyURI : uri, *list,
  1.1775 +                                           getter_AddRefs(result));
  1.1776 +          }
  1.1777 +        } else if (!!iter->channelFilter) {
  1.1778 +          rv = iter->channelFilter->ApplyFilter(this, channel, *list,
  1.1779 +                                                getter_AddRefs(result));
  1.1780 +        }
  1.1781 +        if (NS_FAILED(rv))
  1.1782 +            continue;
  1.1783 +        result.swap(*list);
  1.1784 +    }
  1.1785 +
  1.1786 +    PruneProxyInfo(info, list);
  1.1787 +}
  1.1788 +
  1.1789 +void
  1.1790 +nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
  1.1791 +                                       nsIProxyInfo **list)
  1.1792 +{
  1.1793 +    if (!*list)
  1.1794 +        return;
  1.1795 +    nsProxyInfo *head = nullptr;
  1.1796 +    CallQueryInterface(*list, &head);
  1.1797 +    if (!head) {
  1.1798 +        NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo");
  1.1799 +        return;
  1.1800 +    }
  1.1801 +    NS_RELEASE(*list);
  1.1802 +
  1.1803 +    // Pruning of disabled proxies works like this:
  1.1804 +    //   - If all proxies are disabled, return the full list
  1.1805 +    //   - Otherwise, remove the disabled proxies.
  1.1806 +    //
  1.1807 +    // Pruning of disallowed proxies works like this:
  1.1808 +    //   - If the protocol handler disallows the proxy, then we disallow it.
  1.1809 +
  1.1810 +    // Start by removing all disallowed proxies if required:
  1.1811 +    if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
  1.1812 +        nsProxyInfo *last = nullptr, *iter = head;
  1.1813 +        while (iter) {
  1.1814 +            if (iter->Type() == kProxyType_HTTP) {
  1.1815 +                // reject!
  1.1816 +                if (last)
  1.1817 +                    last->mNext = iter->mNext;
  1.1818 +                else
  1.1819 +                    head = iter->mNext;
  1.1820 +                nsProxyInfo *next = iter->mNext;
  1.1821 +                iter->mNext = nullptr;
  1.1822 +                iter->Release();
  1.1823 +                iter = next;
  1.1824 +            } else {
  1.1825 +                last = iter;
  1.1826 +                iter = iter->mNext;
  1.1827 +            }
  1.1828 +        }
  1.1829 +        if (!head)
  1.1830 +            return;
  1.1831 +    }
  1.1832 +
  1.1833 +    // Now, scan to see if all remaining proxies are disabled.  If so, then
  1.1834 +    // we'll just bail and return them all.  Otherwise, we'll go and prune the
  1.1835 +    // disabled ones.
  1.1836 +
  1.1837 +    bool allDisabled = true;
  1.1838 +
  1.1839 +    nsProxyInfo *iter;
  1.1840 +    for (iter = head; iter; iter = iter->mNext) {
  1.1841 +        if (!IsProxyDisabled(iter)) {
  1.1842 +            allDisabled = false;
  1.1843 +            break;
  1.1844 +        }
  1.1845 +    }
  1.1846 +
  1.1847 +    if (allDisabled)
  1.1848 +        LOG(("All proxies are disabled, so trying all again"));
  1.1849 +    else {
  1.1850 +        // remove any disabled proxies.
  1.1851 +        nsProxyInfo *last = nullptr;
  1.1852 +        for (iter = head; iter; ) {
  1.1853 +            if (IsProxyDisabled(iter)) {
  1.1854 +                // reject!
  1.1855 +                nsProxyInfo *reject = iter;
  1.1856 +
  1.1857 +                iter = iter->mNext;
  1.1858 +                if (last)
  1.1859 +                    last->mNext = iter;
  1.1860 +                else
  1.1861 +                    head = iter;
  1.1862 +
  1.1863 +                reject->mNext = nullptr;
  1.1864 +                NS_RELEASE(reject);
  1.1865 +                continue;
  1.1866 +            }
  1.1867 +
  1.1868 +            // since we are about to use this proxy, make sure it is not on
  1.1869 +            // the disabled proxy list.  we'll add it back to that list if
  1.1870 +            // we have to (in GetFailoverForProxy).
  1.1871 +            //
  1.1872 +            // XXX(darin): It might be better to do this as a final pass.
  1.1873 +            //
  1.1874 +            EnableProxy(iter);
  1.1875 +
  1.1876 +            last = iter;
  1.1877 +            iter = iter->mNext;
  1.1878 +        }
  1.1879 +    }
  1.1880 +
  1.1881 +    // if only DIRECT was specified then return no proxy info, and we're done.
  1.1882 +    if (head && !head->mNext && head->mType == kProxyType_DIRECT)
  1.1883 +        NS_RELEASE(head);
  1.1884 +
  1.1885 +    *list = head;  // Transfer ownership
  1.1886 +}

mercurial