michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim:set ts=4 sw=4 sts=4 et: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "nsProtocolProxyService.h" michael@0: #include "nsProxyInfo.h" michael@0: #include "nsIClassInfoImpl.h" michael@0: #include "nsIIOService.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIProtocolHandler.h" michael@0: #include "nsIProtocolProxyCallback.h" michael@0: #include "nsIChannel.h" michael@0: #include "nsICancelable.h" michael@0: #include "nsIDNSService.h" michael@0: #include "nsPIDNSService.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsString.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsNetCID.h" michael@0: #include "prnetdb.h" michael@0: #include "nsPACMan.h" michael@0: #include "nsProxyRelease.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "mozilla/CondVar.h" michael@0: #include "nsISystemProxySettings.h" michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: namespace mozilla { michael@0: extern const char kProxyType_HTTP[]; michael@0: extern const char kProxyType_SOCKS[]; michael@0: extern const char kProxyType_SOCKS4[]; michael@0: extern const char kProxyType_SOCKS5[]; michael@0: extern const char kProxyType_DIRECT[]; michael@0: } michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #include "prlog.h" michael@0: #if defined(PR_LOGGING) michael@0: #endif michael@0: #undef LOG michael@0: #define LOG(args) PR_LOG(net::GetProxyLog(), PR_LOG_DEBUG, args) michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: #define PROXY_PREF_BRANCH "network.proxy" michael@0: #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x michael@0: michael@0: #define WPAD_URL "http://wpad/wpad.dat" michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: // This structure is intended to be allocated on the stack michael@0: struct nsProtocolInfo { michael@0: nsAutoCString scheme; michael@0: uint32_t flags; michael@0: int32_t defaultPort; michael@0: }; michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: // The nsPACManCallback portion of this implementation should be run michael@0: // on the main thread - so call nsPACMan::AsyncGetProxyForChannel() with michael@0: // a true mainThreadResponse parameter. michael@0: class nsAsyncResolveRequest MOZ_FINAL : public nsIRunnable michael@0: , public nsPACManCallback michael@0: , public nsICancelable michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIChannel *channel, michael@0: uint32_t aResolveFlags, michael@0: nsIProtocolProxyCallback *callback) michael@0: : mStatus(NS_OK) michael@0: , mDispatched(false) michael@0: , mResolveFlags(aResolveFlags) michael@0: , mPPS(pps) michael@0: , mXPComPPS(pps) michael@0: , mChannel(channel) michael@0: , mCallback(callback) michael@0: { michael@0: NS_ASSERTION(mCallback, "null callback"); michael@0: } michael@0: michael@0: ~nsAsyncResolveRequest() michael@0: { michael@0: if (!NS_IsMainThread()) { michael@0: // these xpcom pointers might need to be proxied back to the michael@0: // main thread to delete safely, but if this request had its michael@0: // callbacks called normally they will all be null and this is a nop michael@0: michael@0: nsCOMPtr mainThread; michael@0: NS_GetMainThread(getter_AddRefs(mainThread)); michael@0: michael@0: if (mChannel) { michael@0: nsIChannel *forgettable; michael@0: mChannel.forget(&forgettable); michael@0: NS_ProxyRelease(mainThread, forgettable, false); michael@0: } michael@0: michael@0: if (mCallback) { michael@0: nsIProtocolProxyCallback *forgettable; michael@0: mCallback.forget(&forgettable); michael@0: NS_ProxyRelease(mainThread, forgettable, false); michael@0: } michael@0: michael@0: if (mProxyInfo) { michael@0: nsIProxyInfo *forgettable; michael@0: mProxyInfo.forget(&forgettable); michael@0: NS_ProxyRelease(mainThread, forgettable, false); michael@0: } michael@0: michael@0: if (mXPComPPS) { michael@0: nsIProtocolProxyService *forgettable; michael@0: mXPComPPS.forget(&forgettable); michael@0: NS_ProxyRelease(mainThread, forgettable, false); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void SetResult(nsresult status, nsIProxyInfo *pi) michael@0: { michael@0: mStatus = status; michael@0: mProxyInfo = pi; michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: if (mCallback) michael@0: DoCallback(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD Cancel(nsresult reason) michael@0: { michael@0: NS_ENSURE_ARG(NS_FAILED(reason)); michael@0: michael@0: // If we've already called DoCallback then, nothing more to do. michael@0: if (!mCallback) michael@0: return NS_OK; michael@0: michael@0: SetResult(reason, nullptr); michael@0: return DispatchCallback(); michael@0: } michael@0: michael@0: nsresult DispatchCallback() michael@0: { michael@0: if (mDispatched) // Only need to dispatch once michael@0: return NS_OK; michael@0: michael@0: nsresult rv = NS_DispatchToCurrentThread(this); michael@0: if (NS_FAILED(rv)) michael@0: NS_WARNING("unable to dispatch callback event"); michael@0: else { michael@0: mDispatched = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: mCallback = nullptr; // break possible reference cycle michael@0: return rv; michael@0: } michael@0: michael@0: private: michael@0: michael@0: // Called asynchronously, so we do not need to post another PLEvent michael@0: // before calling DoCallback. michael@0: void OnQueryComplete(nsresult status, michael@0: const nsCString &pacString, michael@0: const nsCString &newPACURL) michael@0: { michael@0: // If we've already called DoCallback then, nothing more to do. michael@0: if (!mCallback) michael@0: return; michael@0: michael@0: // Provided we haven't been canceled... michael@0: if (mStatus == NS_OK) { michael@0: mStatus = status; michael@0: mPACString = pacString; michael@0: mPACURL = newPACURL; michael@0: } michael@0: michael@0: // In the cancelation case, we may still have another PLEvent in michael@0: // the queue that wants to call DoCallback. No need to wait for michael@0: // it, just run the callback now. michael@0: DoCallback(); michael@0: } michael@0: michael@0: void DoCallback() michael@0: { michael@0: if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) { michael@0: // If the PAC service is not avail (e.g. failed pac load michael@0: // or shutdown) then we will be going direct. Make that michael@0: // mapping now so that any filters are still applied. michael@0: mPACString = NS_LITERAL_CSTRING("DIRECT;"); michael@0: mStatus = NS_OK; michael@0: } michael@0: michael@0: // Generate proxy info from the PAC string if appropriate michael@0: if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) { michael@0: mPPS->ProcessPACString(mPACString, mResolveFlags, michael@0: getter_AddRefs(mProxyInfo)); michael@0: nsCOMPtr uri; michael@0: mChannel->GetURI(getter_AddRefs(uri)); michael@0: michael@0: // Now apply proxy filters michael@0: nsProtocolInfo info; michael@0: mStatus = mPPS->GetProtocolInfo(uri, &info); michael@0: if (NS_SUCCEEDED(mStatus)) michael@0: mPPS->ApplyFilters(mChannel, info, mProxyInfo); michael@0: else michael@0: mProxyInfo = nullptr; michael@0: michael@0: LOG(("pac thread callback %s\n", mPACString.get())); michael@0: if (NS_SUCCEEDED(mStatus)) michael@0: mPPS->MaybeDisableDNSPrefetch(mProxyInfo); michael@0: mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus); michael@0: } michael@0: else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) { michael@0: LOG(("pac thread callback indicates new pac file load\n")); michael@0: michael@0: // trigger load of new pac url michael@0: nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // now that the load is triggered, we can resubmit the query michael@0: nsRefPtr newRequest = michael@0: new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback); michael@0: rv = mPPS->mPACMan->AsyncGetProxyForChannel(mChannel, newRequest, true); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) michael@0: mCallback->OnProxyAvailable(this, mChannel, nullptr, rv); michael@0: michael@0: // do not call onproxyavailable() in SUCCESS case - the newRequest will michael@0: // take care of that michael@0: } michael@0: else { michael@0: LOG(("pac thread callback did not provide information %X\n", mStatus)); michael@0: if (NS_SUCCEEDED(mStatus)) michael@0: mPPS->MaybeDisableDNSPrefetch(mProxyInfo); michael@0: mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus); michael@0: } michael@0: michael@0: // We are on the main thread now and don't need these any more so michael@0: // release them to avoid having to proxy them back to the main thread michael@0: // in the dtor michael@0: mCallback = nullptr; // in case the callback holds an owning ref to us michael@0: mPPS = nullptr; michael@0: mXPComPPS = nullptr; michael@0: mChannel = nullptr; michael@0: mProxyInfo = nullptr; michael@0: } michael@0: michael@0: private: michael@0: michael@0: nsresult mStatus; michael@0: nsCString mPACString; michael@0: nsCString mPACURL; michael@0: bool mDispatched; michael@0: uint32_t mResolveFlags; michael@0: michael@0: nsProtocolProxyService *mPPS; michael@0: nsCOMPtr mXPComPPS; michael@0: nsCOMPtr mChannel; michael@0: nsCOMPtr mCallback; michael@0: nsCOMPtr mProxyInfo; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable) michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t') michael@0: michael@0: // michael@0: // apply mask to address (zeros out excluded bits). michael@0: // michael@0: // NOTE: we do the byte swapping here to minimize overall swapping. michael@0: // michael@0: static void michael@0: proxy_MaskIPv6Addr(PRIPv6Addr &addr, uint16_t mask_len) michael@0: { michael@0: if (mask_len == 128) michael@0: return; michael@0: michael@0: if (mask_len > 96) { michael@0: addr.pr_s6_addr32[3] = PR_htonl( michael@0: PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len))); michael@0: } michael@0: else if (mask_len > 64) { michael@0: addr.pr_s6_addr32[3] = 0; michael@0: addr.pr_s6_addr32[2] = PR_htonl( michael@0: PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len))); michael@0: } michael@0: else if (mask_len > 32) { michael@0: addr.pr_s6_addr32[3] = 0; michael@0: addr.pr_s6_addr32[2] = 0; michael@0: addr.pr_s6_addr32[1] = PR_htonl( michael@0: PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len))); michael@0: } michael@0: else { michael@0: addr.pr_s6_addr32[3] = 0; michael@0: addr.pr_s6_addr32[2] = 0; michael@0: addr.pr_s6_addr32[1] = 0; michael@0: addr.pr_s6_addr32[0] = PR_htonl( michael@0: PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len))); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: proxy_GetStringPref(nsIPrefBranch *aPrefBranch, michael@0: const char *aPref, michael@0: nsCString &aResult) michael@0: { michael@0: nsXPIDLCString temp; michael@0: nsresult rv = aPrefBranch->GetCharPref(aPref, getter_Copies(temp)); michael@0: if (NS_FAILED(rv)) michael@0: aResult.Truncate(); michael@0: else { michael@0: aResult.Assign(temp); michael@0: // all of our string prefs are hostnames, so we should remove any michael@0: // whitespace characters that the user might have unknowingly entered. michael@0: aResult.StripWhitespace(); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: proxy_GetIntPref(nsIPrefBranch *aPrefBranch, michael@0: const char *aPref, michael@0: int32_t &aResult) michael@0: { michael@0: int32_t temp; michael@0: nsresult rv = aPrefBranch->GetIntPref(aPref, &temp); michael@0: if (NS_FAILED(rv)) michael@0: aResult = -1; michael@0: else michael@0: aResult = temp; michael@0: } michael@0: michael@0: static void michael@0: proxy_GetBoolPref(nsIPrefBranch *aPrefBranch, michael@0: const char *aPref, michael@0: bool &aResult) michael@0: { michael@0: bool temp; michael@0: nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp); michael@0: if (NS_FAILED(rv)) michael@0: aResult = false; michael@0: else michael@0: aResult = temp; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: static const int32_t PROXYCONFIG_DIRECT4X = 3; michael@0: static const int32_t PROXYCONFIG_COUNT = 6; michael@0: michael@0: NS_IMPL_ADDREF(nsProtocolProxyService) michael@0: NS_IMPL_RELEASE(nsProtocolProxyService) michael@0: NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON, michael@0: NS_PROTOCOLPROXYSERVICE_CID) michael@0: NS_IMPL_QUERY_INTERFACE_CI(nsProtocolProxyService, michael@0: nsIProtocolProxyService, michael@0: nsIProtocolProxyService2, michael@0: nsIObserver) michael@0: NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService, michael@0: nsIProtocolProxyService, michael@0: nsIProtocolProxyService2) michael@0: michael@0: nsProtocolProxyService::nsProtocolProxyService() michael@0: : mFilterLocalHosts(false) michael@0: , mFilters(nullptr) michael@0: , mProxyConfig(PROXYCONFIG_DIRECT) michael@0: , mHTTPProxyPort(-1) michael@0: , mFTPProxyPort(-1) michael@0: , mHTTPSProxyPort(-1) michael@0: , mSOCKSProxyPort(-1) michael@0: , mSOCKSProxyVersion(4) michael@0: , mSOCKSProxyRemoteDNS(false) michael@0: , mPACMan(nullptr) michael@0: , mSessionStart(PR_Now()) michael@0: , mFailedProxyTimeout(30 * 60) // 30 minute default michael@0: { michael@0: } michael@0: michael@0: nsProtocolProxyService::~nsProtocolProxyService() michael@0: { michael@0: // These should have been cleaned up in our Observe method. michael@0: NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters == nullptr && michael@0: mPACMan == nullptr, "what happened to xpcom-shutdown?"); michael@0: } michael@0: michael@0: // nsProtocolProxyService methods michael@0: nsresult michael@0: nsProtocolProxyService::Init() michael@0: { michael@0: // failure to access prefs is non-fatal michael@0: nsCOMPtr prefBranch = michael@0: do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (prefBranch) { michael@0: // monitor proxy prefs michael@0: prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false); michael@0: michael@0: // read all prefs michael@0: PrefsChanged(prefBranch, nullptr); michael@0: } michael@0: michael@0: // register for shutdown notification so we can clean ourselves up properly. michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (obs) michael@0: obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { michael@0: // cleanup michael@0: if (mHostFiltersArray.Length() > 0) { michael@0: mHostFiltersArray.Clear(); michael@0: } michael@0: if (mFilters) { michael@0: delete mFilters; michael@0: mFilters = nullptr; michael@0: } michael@0: if (mPACMan) { michael@0: mPACMan->Shutdown(); michael@0: mPACMan = nullptr; michael@0: } michael@0: } michael@0: else { michael@0: NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0, michael@0: "what is this random observer event?"); michael@0: nsCOMPtr prefs = do_QueryInterface(aSubject); michael@0: if (prefs) michael@0: PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get()); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch, michael@0: const char *pref) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: bool reloadPAC = false; michael@0: nsXPIDLCString tempString; michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("type"))) { michael@0: int32_t type = -1; michael@0: rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // bug 115720 - for ns4.x backwards compatibility michael@0: if (type == PROXYCONFIG_DIRECT4X) { michael@0: type = PROXYCONFIG_DIRECT; michael@0: // Reset the type so that the dialog looks correct, and we michael@0: // don't have to handle this case everywhere else michael@0: // I'm paranoid about a loop of some sort - only do this michael@0: // if we're enumerating all prefs, and ignore any error michael@0: if (!pref) michael@0: prefBranch->SetIntPref(PROXY_PREF("type"), type); michael@0: } else if (type >= PROXYCONFIG_COUNT) { michael@0: LOG(("unknown proxy type: %lu; assuming direct\n", type)); michael@0: type = PROXYCONFIG_DIRECT; michael@0: } michael@0: mProxyConfig = type; michael@0: reloadPAC = true; michael@0: } michael@0: michael@0: if (mProxyConfig == PROXYCONFIG_SYSTEM) { michael@0: mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID); michael@0: if (!mSystemProxySettings) michael@0: mProxyConfig = PROXYCONFIG_DIRECT; michael@0: ResetPACThread(); michael@0: } else { michael@0: if (mSystemProxySettings) { michael@0: mSystemProxySettings = nullptr; michael@0: ResetPACThread(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("http"))) michael@0: proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("http_port"))) michael@0: proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("ssl"))) michael@0: proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("ssl_port"))) michael@0: proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("ftp"))) michael@0: proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("ftp_port"))) michael@0: proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("socks"))) michael@0: proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyHost); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("socks_port"))) michael@0: proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("socks_username"))) michael@0: proxy_GetStringPref(prefBranch, PROXY_PREF("socks_username"), mSOCKSProxyUsername); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("socks_password"))) michael@0: proxy_GetStringPref(prefBranch, PROXY_PREF("socks_password"), mSOCKSProxyPassword); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) { michael@0: int32_t version; michael@0: proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version); michael@0: // make sure this preference value remains sane michael@0: if (version == 5) michael@0: mSOCKSProxyVersion = 5; michael@0: else michael@0: mSOCKSProxyVersion = 4; michael@0: } michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns"))) michael@0: proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"), michael@0: mSOCKSProxyRemoteDNS); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout"))) michael@0: proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"), michael@0: mFailedProxyTimeout); michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) { michael@0: rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"), michael@0: getter_Copies(tempString)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: LoadHostFilters(tempString.get()); michael@0: } michael@0: michael@0: // We're done if not using something that could give us a PAC URL michael@0: // (PAC, WPAD or System) michael@0: if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD && michael@0: mProxyConfig != PROXYCONFIG_SYSTEM) michael@0: return; michael@0: michael@0: // OK, we need to reload the PAC file if: michael@0: // 1) network.proxy.type changed, or michael@0: // 2) network.proxy.autoconfig_url changed and PAC is configured michael@0: michael@0: if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url"))) michael@0: reloadPAC = true; michael@0: michael@0: if (reloadPAC) { michael@0: tempString.Truncate(); michael@0: if (mProxyConfig == PROXYCONFIG_PAC) { michael@0: prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"), michael@0: getter_Copies(tempString)); michael@0: } else if (mProxyConfig == PROXYCONFIG_WPAD) { michael@0: // We diverge from the WPAD spec here in that we don't walk the michael@0: // hosts's FQDN, stripping components until we hit a TLD. Doing so michael@0: // is dangerous in the face of an incomplete list of TLDs, and TLDs michael@0: // get added over time. We could consider doing only a single michael@0: // substitution of the first component, if that proves to help michael@0: // compatibility. michael@0: tempString.AssignLiteral(WPAD_URL); michael@0: } else if (mSystemProxySettings) { michael@0: // Get System Proxy settings if available michael@0: mSystemProxySettings->GetPACURI(tempString); michael@0: } michael@0: if (!tempString.IsEmpty()) michael@0: ConfigureFromPAC(tempString, false); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsProtocolProxyService::CanUseProxy(nsIURI *aURI, int32_t defaultPort) michael@0: { michael@0: if (mHostFiltersArray.Length() == 0) michael@0: return true; michael@0: michael@0: int32_t port; michael@0: nsAutoCString host; michael@0: michael@0: nsresult rv = aURI->GetAsciiHost(host); michael@0: if (NS_FAILED(rv) || host.IsEmpty()) michael@0: return false; michael@0: michael@0: rv = aURI->GetPort(&port); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: if (port == -1) michael@0: port = defaultPort; michael@0: michael@0: PRNetAddr addr; michael@0: bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS); michael@0: michael@0: PRIPv6Addr ipv6; michael@0: if (is_ipaddr) { michael@0: // convert parsed address to IPv6 michael@0: if (addr.raw.family == PR_AF_INET) { michael@0: // convert to IPv4-mapped address michael@0: PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6); michael@0: } michael@0: else if (addr.raw.family == PR_AF_INET6) { michael@0: // copy the address michael@0: memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr)); michael@0: } michael@0: else { michael@0: NS_WARNING("unknown address family"); michael@0: return true; // allow proxying michael@0: } michael@0: } michael@0: michael@0: // Don't use proxy for local hosts (plain hostname, no dots) michael@0: if (!is_ipaddr && mFilterLocalHosts && (kNotFound == host.FindChar('.'))) { michael@0: LOG(("Not using proxy for this local host [%s]!\n", host.get())); michael@0: return false; // don't allow proxying michael@0: } michael@0: michael@0: int32_t index = -1; michael@0: while (++index < int32_t(mHostFiltersArray.Length())) { michael@0: HostInfo *hinfo = mHostFiltersArray[index]; michael@0: michael@0: if (is_ipaddr != hinfo->is_ipaddr) michael@0: continue; michael@0: if (hinfo->port && hinfo->port != port) michael@0: continue; michael@0: michael@0: if (is_ipaddr) { michael@0: // generate masked version of target IPv6 address michael@0: PRIPv6Addr masked; michael@0: memcpy(&masked, &ipv6, sizeof(PRIPv6Addr)); michael@0: proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len); michael@0: michael@0: // check for a match michael@0: if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0) michael@0: return false; // proxy disallowed michael@0: } michael@0: else { michael@0: uint32_t host_len = host.Length(); michael@0: uint32_t filter_host_len = hinfo->name.host_len; michael@0: michael@0: if (host_len >= filter_host_len) { michael@0: // michael@0: // compare last |filter_host_len| bytes of target hostname. michael@0: // michael@0: const char *host_tail = host.get() + host_len - filter_host_len; michael@0: if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len)) michael@0: return false; // proxy disallowed michael@0: } michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // kProxyType\* may be referred to externally in michael@0: // nsProxyInfo in order to compare by string pointer michael@0: namespace mozilla { michael@0: const char kProxyType_HTTP[] = "http"; michael@0: const char kProxyType_PROXY[] = "proxy"; michael@0: const char kProxyType_SOCKS[] = "socks"; michael@0: const char kProxyType_SOCKS4[] = "socks4"; michael@0: const char kProxyType_SOCKS5[] = "socks5"; michael@0: const char kProxyType_DIRECT[] = "direct"; michael@0: const char kProxyType_UNKNOWN[] = "unknown"; michael@0: } michael@0: michael@0: const char * michael@0: nsProtocolProxyService::ExtractProxyInfo(const char *start, michael@0: uint32_t aResolveFlags, michael@0: nsProxyInfo **result) michael@0: { michael@0: *result = nullptr; michael@0: uint32_t flags = 0; michael@0: michael@0: // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl michael@0: michael@0: // find end of proxy info delimiter michael@0: const char *end = start; michael@0: while (*end && *end != ';') ++end; michael@0: michael@0: // find end of proxy type delimiter michael@0: const char *sp = start; michael@0: while (sp < end && *sp != ' ' && *sp != '\t') ++sp; michael@0: michael@0: uint32_t len = sp - start; michael@0: const char *type = nullptr; michael@0: switch (len) { michael@0: case 5: michael@0: if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0) michael@0: type = kProxyType_HTTP; michael@0: else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0) michael@0: type = kProxyType_SOCKS4; // assume v4 for 4x compat michael@0: break; michael@0: case 6: michael@0: if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0) michael@0: type = kProxyType_DIRECT; michael@0: else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0) michael@0: type = kProxyType_SOCKS4; michael@0: else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0) michael@0: // map "SOCKS5" to "socks" to match contract-id of registered michael@0: // SOCKS-v5 socket provider. michael@0: type = kProxyType_SOCKS; michael@0: break; michael@0: } michael@0: if (type) { michael@0: const char *host = nullptr, *hostEnd = nullptr; michael@0: int32_t port = -1; michael@0: michael@0: // If it's a SOCKS5 proxy, do name resolution on the server side. michael@0: // We could use this with SOCKS4a servers too, but they might not michael@0: // support it. michael@0: if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS) michael@0: flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST; michael@0: michael@0: // extract host:port michael@0: start = sp; michael@0: while ((*start == ' ' || *start == '\t') && start < end) michael@0: start++; michael@0: michael@0: // port defaults michael@0: if (type == kProxyType_HTTP) michael@0: port = 80; michael@0: else michael@0: port = 1080; michael@0: michael@0: nsProxyInfo *pi = new nsProxyInfo(); michael@0: pi->mType = type; michael@0: pi->mFlags = flags; michael@0: pi->mResolveFlags = aResolveFlags; michael@0: pi->mTimeout = mFailedProxyTimeout; michael@0: michael@0: // www.foo.com:8080 and http://www.foo.com:8080 michael@0: nsDependentCSubstring maybeURL(start, end - start); michael@0: nsCOMPtr pacURI; michael@0: michael@0: nsAutoCString urlHost; michael@0: if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) && michael@0: NS_SUCCEEDED(pacURI->GetAsciiHost(urlHost)) && michael@0: !urlHost.IsEmpty()) { michael@0: // http://www.example.com:8080 michael@0: michael@0: pi->mHost = urlHost; michael@0: michael@0: int32_t tPort; michael@0: if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) { michael@0: port = tPort; michael@0: } michael@0: pi->mPort = port; michael@0: } michael@0: else { michael@0: // www.example.com:8080 michael@0: if (start < end) { michael@0: host = start; michael@0: hostEnd = strchr(host, ':'); michael@0: if (!hostEnd || hostEnd > end) { michael@0: hostEnd = end; michael@0: // no port, so assume default michael@0: } michael@0: else { michael@0: port = atoi(hostEnd + 1); michael@0: } michael@0: } michael@0: // YES, it is ok to specify a null proxy host. michael@0: if (host) { michael@0: pi->mHost.Assign(host, hostEnd - host); michael@0: pi->mPort = port; michael@0: } michael@0: } michael@0: NS_ADDREF(*result = pi); michael@0: } michael@0: michael@0: while (*end == ';' || *end == ' ' || *end == '\t') michael@0: ++end; michael@0: return end; michael@0: } michael@0: michael@0: void michael@0: nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key) michael@0: { michael@0: key.AssignASCII(pi->mType); michael@0: if (!pi->mHost.IsEmpty()) { michael@0: key.Append(' '); michael@0: key.Append(pi->mHost); michael@0: key.Append(':'); michael@0: key.AppendInt(pi->mPort); michael@0: } michael@0: } michael@0: michael@0: uint32_t michael@0: nsProtocolProxyService::SecondsSinceSessionStart() michael@0: { michael@0: PRTime now = PR_Now(); michael@0: michael@0: // get time elapsed since session start michael@0: int64_t diff = now - mSessionStart; michael@0: michael@0: // convert microseconds to seconds michael@0: diff /= PR_USEC_PER_SEC; michael@0: michael@0: // return converted 32 bit value michael@0: return uint32_t(diff); michael@0: } michael@0: michael@0: void michael@0: nsProtocolProxyService::EnableProxy(nsProxyInfo *pi) michael@0: { michael@0: nsAutoCString key; michael@0: GetProxyKey(pi, key); michael@0: mFailedProxies.Remove(key); michael@0: } michael@0: michael@0: void michael@0: nsProtocolProxyService::DisableProxy(nsProxyInfo *pi) michael@0: { michael@0: nsAutoCString key; michael@0: GetProxyKey(pi, key); michael@0: michael@0: uint32_t dsec = SecondsSinceSessionStart(); michael@0: michael@0: // Add timeout to interval (this is the time when the proxy can michael@0: // be tried again). michael@0: dsec += pi->mTimeout; michael@0: michael@0: // NOTE: The classic codebase would increase the timeout value michael@0: // incrementally each time a subsequent failure occurred. michael@0: // We could do the same, but it would require that we not michael@0: // remove proxy entries in IsProxyDisabled or otherwise michael@0: // change the way we are recording disabled proxies. michael@0: // Simpler is probably better for now, and at least the michael@0: // user can tune the timeout setting via preferences. michael@0: michael@0: LOG(("DisableProxy %s %d\n", key.get(), dsec)); michael@0: michael@0: // If this fails, oh well... means we don't have enough memory michael@0: // to remember the failed proxy. michael@0: mFailedProxies.Put(key, dsec); michael@0: } michael@0: michael@0: bool michael@0: nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi) michael@0: { michael@0: nsAutoCString key; michael@0: GetProxyKey(pi, key); michael@0: michael@0: uint32_t val; michael@0: if (!mFailedProxies.Get(key, &val)) michael@0: return false; michael@0: michael@0: uint32_t dsec = SecondsSinceSessionStart(); michael@0: michael@0: // if time passed has exceeded interval, then try proxy again. michael@0: if (dsec > val) { michael@0: mFailedProxies.Remove(key); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: nsProtocolProxyService::SetupPACThread() michael@0: { michael@0: if (mPACMan) michael@0: return NS_OK; michael@0: michael@0: mPACMan = new nsPACMan(); michael@0: michael@0: bool mainThreadOnly; michael@0: nsresult rv; michael@0: if (mSystemProxySettings && michael@0: NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) && michael@0: !mainThreadOnly) { michael@0: rv = mPACMan->Init(mSystemProxySettings); michael@0: } michael@0: else { michael@0: rv = mPACMan->Init(nullptr); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) michael@0: mPACMan = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsProtocolProxyService::ResetPACThread() michael@0: { michael@0: if (!mPACMan) michael@0: return NS_OK; michael@0: michael@0: mPACMan->Shutdown(); michael@0: mPACMan = nullptr; michael@0: return SetupPACThread(); michael@0: } michael@0: michael@0: nsresult michael@0: nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec, michael@0: bool forceReload) michael@0: { michael@0: SetupPACThread(); michael@0: michael@0: if (mPACMan->IsPACURI(spec) && !forceReload) michael@0: return NS_OK; michael@0: michael@0: mFailedProxies.Clear(); michael@0: michael@0: return mPACMan->LoadPACFromURI(spec); michael@0: } michael@0: michael@0: void michael@0: nsProtocolProxyService::ProcessPACString(const nsCString &pacString, michael@0: uint32_t aResolveFlags, michael@0: nsIProxyInfo **result) michael@0: { michael@0: if (pacString.IsEmpty()) { michael@0: *result = nullptr; michael@0: return; michael@0: } michael@0: michael@0: const char *proxies = pacString.get(); michael@0: michael@0: nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr; michael@0: while (*proxies) { michael@0: proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi); michael@0: if (pi) { michael@0: if (last) { michael@0: NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo"); michael@0: last->mNext = pi; michael@0: } michael@0: else michael@0: first = pi; michael@0: last = pi; michael@0: } michael@0: } michael@0: *result = first; michael@0: } michael@0: michael@0: // nsIProtocolProxyService2 michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::ReloadPAC() michael@0: { michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (!prefs) michael@0: return NS_OK; michael@0: michael@0: int32_t type; michael@0: nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type); michael@0: if (NS_FAILED(rv)) michael@0: return NS_OK; michael@0: michael@0: nsXPIDLCString pacSpec; michael@0: if (type == PROXYCONFIG_PAC) michael@0: prefs->GetCharPref(PROXY_PREF("autoconfig_url"), getter_Copies(pacSpec)); michael@0: else if (type == PROXYCONFIG_WPAD) michael@0: pacSpec.AssignLiteral(WPAD_URL); michael@0: michael@0: if (!pacSpec.IsEmpty()) michael@0: ConfigureFromPAC(pacSpec, true); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // When sync interface is removed this can go away too michael@0: // The nsPACManCallback portion of this implementation should be run michael@0: // off the main thread, because it uses a condvar for signaling and michael@0: // the main thread is blocking on that condvar - michael@0: // so call nsPACMan::AsyncGetProxyForChannel() with michael@0: // a false mainThreadResponse parameter. michael@0: class nsAsyncBridgeRequest MOZ_FINAL : public nsPACManCallback michael@0: { michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: nsAsyncBridgeRequest() michael@0: : mMutex("nsDeprecatedCallback") michael@0: , mCondVar(mMutex, "nsDeprecatedCallback") michael@0: , mCompleted(false) michael@0: { michael@0: } michael@0: michael@0: void OnQueryComplete(nsresult status, michael@0: const nsCString &pacString, michael@0: const nsCString &newPACURL) michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: mCompleted = true; michael@0: mStatus = status; michael@0: mPACString = pacString; michael@0: mPACURL = newPACURL; michael@0: mCondVar.Notify(); michael@0: } michael@0: michael@0: void Lock() { mMutex.Lock(); } michael@0: void Unlock() { mMutex.Unlock(); } michael@0: void Wait() { mCondVar.Wait(PR_SecondsToInterval(3)); } michael@0: michael@0: private: michael@0: ~nsAsyncBridgeRequest() michael@0: { michael@0: } michael@0: michael@0: friend class nsProtocolProxyService; michael@0: michael@0: Mutex mMutex; michael@0: CondVar mCondVar; michael@0: michael@0: nsresult mStatus; michael@0: nsCString mPACString; michael@0: nsCString mPACURL; michael@0: bool mCompleted; michael@0: }; michael@0: NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest) michael@0: michael@0: // nsProtocolProxyService michael@0: nsresult michael@0: nsProtocolProxyService::DeprecatedBlockingResolve(nsIChannel *aChannel, michael@0: uint32_t aFlags, michael@0: nsIProxyInfo **retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aChannel); michael@0: michael@0: nsCOMPtr uri; michael@0: aChannel->GetURI(getter_AddRefs(uri)); michael@0: michael@0: nsProtocolInfo info; michael@0: nsresult rv = GetProtocolInfo(uri, &info); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr pi; michael@0: bool usePACThread; michael@0: michael@0: // SystemProxySettings and PAC files can block the main thread michael@0: // but if neither of them are in use, we can just do the work michael@0: // right here and directly invoke the callback michael@0: michael@0: rv = Resolve_Internal(aChannel, info, aFlags, &usePACThread, getter_AddRefs(pi)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (!usePACThread || !mPACMan) { michael@0: ApplyFilters(aChannel, info, pi); michael@0: pi.forget(retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Use the PAC thread to do the work, so we don't have to reimplement that michael@0: // code, but block this thread on that completion. michael@0: nsRefPtr ctx = new nsAsyncBridgeRequest(); michael@0: ctx->Lock(); michael@0: if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForChannel(aChannel, ctx, false))) { michael@0: // this can really block the main thread, so cap it at 3 seconds michael@0: ctx->Wait(); michael@0: } michael@0: ctx->Unlock(); michael@0: if (!ctx->mCompleted) michael@0: return NS_ERROR_FAILURE; michael@0: if (NS_FAILED(ctx->mStatus)) michael@0: return ctx->mStatus; michael@0: michael@0: // pretty much duplicate real DoCallback logic michael@0: michael@0: // Generate proxy info from the PAC string if appropriate michael@0: if (!ctx->mPACString.IsEmpty()) { michael@0: LOG(("sync pac thread callback %s\n", ctx->mPACString.get())); michael@0: ProcessPACString(ctx->mPACString, 0, getter_AddRefs(pi)); michael@0: ApplyFilters(aChannel, info, pi); michael@0: pi.forget(retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!ctx->mPACURL.IsEmpty()) { michael@0: NS_WARNING("sync pac thread callback indicates new pac file load\n"); michael@0: // This is a problem and is one of the reasons this blocking interface michael@0: // is deprecated. The main loop needs to spin to make this reload happen. So michael@0: // we are going to kick off the reload and return an error - it will work michael@0: // next time. Because this sync interface is only used in the java plugin it michael@0: // is extremely likely that the pac file has already been loaded anyhow. michael@0: michael@0: rv = ConfigureFromPAC(ctx->mPACURL, false); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: *retval = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsProtocolProxyService::AsyncResolveInternal(nsIChannel *channel, uint32_t flags, michael@0: nsIProtocolProxyCallback *callback, michael@0: nsICancelable **result, michael@0: bool isSyncOK) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(channel); michael@0: NS_ENSURE_ARG_POINTER(callback); michael@0: michael@0: nsCOMPtr uri; michael@0: channel->GetURI(getter_AddRefs(uri)); michael@0: michael@0: *result = nullptr; michael@0: nsRefPtr ctx = michael@0: new nsAsyncResolveRequest(this, channel, flags, callback); michael@0: michael@0: nsProtocolInfo info; michael@0: nsresult rv = GetProtocolInfo(uri, &info); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr pi; michael@0: bool usePACThread; michael@0: michael@0: // SystemProxySettings and PAC files can block the main thread michael@0: // but if neither of them are in use, we can just do the work michael@0: // right here and directly invoke the callback michael@0: michael@0: rv = Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (!usePACThread || !mPACMan) { michael@0: // we can do it locally michael@0: ApplyFilters(channel, info, pi); michael@0: ctx->SetResult(NS_OK, pi); michael@0: if (isSyncOK) { michael@0: ctx->Run(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: rv = ctx->DispatchCallback(); michael@0: if (NS_SUCCEEDED(rv)) michael@0: ctx.forget(result); michael@0: return rv; michael@0: } michael@0: michael@0: // else kick off a PAC thread query michael@0: michael@0: rv = mPACMan->AsyncGetProxyForChannel(channel, ctx, true); michael@0: if (NS_SUCCEEDED(rv)) michael@0: ctx.forget(result); michael@0: return rv; michael@0: } michael@0: michael@0: // nsIProtocolProxyService michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::AsyncResolve2(nsIChannel *channel, uint32_t flags, michael@0: nsIProtocolProxyCallback *callback, michael@0: nsICancelable **result) michael@0: { michael@0: return AsyncResolveInternal(channel, flags, callback, result, true); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::AsyncResolve(nsIChannel *channel, uint32_t flags, michael@0: nsIProtocolProxyCallback *callback, michael@0: nsICancelable **result) michael@0: { michael@0: return AsyncResolveInternal(channel, flags, callback, result, false); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::NewProxyInfo(const nsACString &aType, michael@0: const nsACString &aHost, michael@0: int32_t aPort, michael@0: uint32_t aFlags, michael@0: uint32_t aFailoverTimeout, michael@0: nsIProxyInfo *aFailoverProxy, michael@0: nsIProxyInfo **aResult) michael@0: { michael@0: static const char *types[] = { michael@0: kProxyType_HTTP, michael@0: kProxyType_SOCKS, michael@0: kProxyType_SOCKS4, michael@0: kProxyType_DIRECT michael@0: }; michael@0: michael@0: // resolve type; this allows us to avoid copying the type string into each michael@0: // proxy info instance. we just reference the string literals directly :) michael@0: const char *type = nullptr; michael@0: for (uint32_t i=0; i pi = do_QueryInterface(aProxy); michael@0: NS_ENSURE_ARG(pi); michael@0: // OK, the QI checked out. We can proceed. michael@0: michael@0: // Remember that this proxy is down. michael@0: DisableProxy(pi); michael@0: michael@0: // NOTE: At this point, we might want to prompt the user if we have michael@0: // not already tried going DIRECT. This is something that the michael@0: // classic codebase supported; however, IE6 does not prompt. michael@0: michael@0: if (!pi->mNext) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: LOG(("PAC failover from %s %s:%d to %s %s:%d\n", michael@0: pi->mType, pi->mHost.get(), pi->mPort, michael@0: pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort)); michael@0: michael@0: NS_ADDREF(*aResult = pi->mNext); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsProtocolProxyService::InsertFilterLink(FilterLink *link, uint32_t position) michael@0: { michael@0: if (!mFilters) { michael@0: mFilters = link; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // insert into mFilters in sorted order michael@0: FilterLink *last = nullptr; michael@0: for (FilterLink *iter = mFilters; iter; iter = iter->next) { michael@0: if (position < iter->position) { michael@0: if (last) { michael@0: link->next = last->next; michael@0: last->next = link; michael@0: } michael@0: else { michael@0: link->next = mFilters; michael@0: mFilters = link; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: last = iter; michael@0: } michael@0: // our position is equal to or greater than the last link in the list michael@0: last->next = link; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter, michael@0: uint32_t position) michael@0: { michael@0: UnregisterFilter(filter); // remove this filter if we already have it michael@0: michael@0: FilterLink *link = new FilterLink(position, filter); michael@0: if (!link) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: return InsertFilterLink(link, position); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::RegisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter, michael@0: uint32_t position) michael@0: { michael@0: UnregisterChannelFilter(channelFilter); // remove this filter if we already have it michael@0: michael@0: FilterLink *link = new FilterLink(position, channelFilter); michael@0: if (!link) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: return InsertFilterLink(link, position); michael@0: } michael@0: michael@0: nsresult michael@0: nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject) michael@0: { michael@0: FilterLink *last = nullptr; michael@0: for (FilterLink *iter = mFilters; iter; iter = iter->next) { michael@0: nsCOMPtr object = do_QueryInterface(iter->filter); michael@0: if (object == givenObject) { michael@0: if (last) michael@0: last->next = iter->next; michael@0: else michael@0: mFilters = iter->next; michael@0: iter->next = nullptr; michael@0: delete iter; michael@0: return NS_OK; michael@0: } michael@0: last = iter; michael@0: } michael@0: michael@0: // No need to throw an exception in this case. michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter) { michael@0: // QI to nsISupports so we can safely test object identity. michael@0: nsCOMPtr givenObject = do_QueryInterface(filter); michael@0: return RemoveFilterLink(givenObject); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::UnregisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter) { michael@0: // QI to nsISupports so we can safely test object identity. michael@0: nsCOMPtr givenObject = do_QueryInterface(channelFilter); michael@0: return RemoveFilterLink(givenObject); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType) michael@0: { michael@0: *aProxyConfigType = mProxyConfig; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsProtocolProxyService::LoadHostFilters(const char *filters) michael@0: { michael@0: // check to see the owners flag? /!?/ TODO michael@0: if (mHostFiltersArray.Length() > 0) { michael@0: mHostFiltersArray.Clear(); michael@0: } michael@0: michael@0: if (!filters) michael@0: return; // fail silently... michael@0: michael@0: // michael@0: // filter = ( host | domain | ipaddr ["/" mask] ) [":" port] michael@0: // filters = filter *( "," LWS filter) michael@0: // michael@0: // Reset mFilterLocalHosts - will be set to true if "" is in pref string michael@0: mFilterLocalHosts = false; michael@0: while (*filters) { michael@0: // skip over spaces and , michael@0: while (*filters && (*filters == ',' || IS_ASCII_SPACE(*filters))) michael@0: filters++; michael@0: michael@0: const char *starthost = filters; michael@0: const char *endhost = filters + 1; // at least that... michael@0: const char *portLocation = 0; michael@0: const char *maskLocation = 0; michael@0: michael@0: while (*endhost && (*endhost != ',' && !IS_ASCII_SPACE(*endhost))) { michael@0: if (*endhost == ':') michael@0: portLocation = endhost; michael@0: else if (*endhost == '/') michael@0: maskLocation = endhost; michael@0: else if (*endhost == ']') // IPv6 address literals michael@0: portLocation = 0; michael@0: endhost++; michael@0: } michael@0: michael@0: filters = endhost; // advance iterator up front michael@0: michael@0: // locate end of host michael@0: const char *end = maskLocation ? maskLocation : michael@0: portLocation ? portLocation : michael@0: endhost; michael@0: michael@0: nsAutoCString str(starthost, end - starthost); michael@0: michael@0: // If the current host filter is "", then all local (i.e. michael@0: // no dots in the hostname) hosts should bypass the proxy michael@0: if (str.EqualsIgnoreCase("")) { michael@0: mFilterLocalHosts = true; michael@0: LOG(("loaded filter for local hosts " michael@0: "(plain host names, no dots)\n")); michael@0: // Continue to next host filter; michael@0: continue; michael@0: } michael@0: michael@0: // For all other host filters, create HostInfo object and add to list michael@0: HostInfo *hinfo = new HostInfo(); michael@0: hinfo->port = portLocation ? atoi(portLocation + 1) : 0; michael@0: michael@0: PRNetAddr addr; michael@0: if (PR_StringToNetAddr(str.get(), &addr) == PR_SUCCESS) { michael@0: hinfo->is_ipaddr = true; michael@0: hinfo->ip.family = PR_AF_INET6; // we always store address as IPv6 michael@0: hinfo->ip.mask_len = maskLocation ? atoi(maskLocation + 1) : 128; michael@0: michael@0: if (hinfo->ip.mask_len == 0) { michael@0: NS_WARNING("invalid mask"); michael@0: goto loser; michael@0: } michael@0: michael@0: if (addr.raw.family == PR_AF_INET) { michael@0: // convert to IPv4-mapped address michael@0: PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr); michael@0: // adjust mask_len accordingly michael@0: if (hinfo->ip.mask_len <= 32) michael@0: hinfo->ip.mask_len += 96; michael@0: } michael@0: else if (addr.raw.family == PR_AF_INET6) { michael@0: // copy the address michael@0: memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr)); michael@0: } michael@0: else { michael@0: NS_WARNING("unknown address family"); michael@0: goto loser; michael@0: } michael@0: michael@0: // apply mask to IPv6 address michael@0: proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len); michael@0: } michael@0: else { michael@0: uint32_t startIndex, endIndex; michael@0: if (str.First() == '*') michael@0: startIndex = 1; // *.domain -> .domain michael@0: else michael@0: startIndex = 0; michael@0: endIndex = (portLocation ? portLocation : endhost) - starthost; michael@0: michael@0: hinfo->is_ipaddr = false; michael@0: hinfo->name.host = ToNewCString(Substring(str, startIndex, endIndex)); michael@0: michael@0: if (!hinfo->name.host) michael@0: goto loser; michael@0: michael@0: hinfo->name.host_len = endIndex - startIndex; michael@0: } michael@0: michael@0: //#define DEBUG_DUMP_FILTERS michael@0: #ifdef DEBUG_DUMP_FILTERS michael@0: printf("loaded filter[%u]:\n", mHostFiltersArray.Length()); michael@0: printf(" is_ipaddr = %u\n", hinfo->is_ipaddr); michael@0: printf(" port = %u\n", hinfo->port); michael@0: if (hinfo->is_ipaddr) { michael@0: printf(" ip.family = %x\n", hinfo->ip.family); michael@0: printf(" ip.mask_len = %u\n", hinfo->ip.mask_len); michael@0: michael@0: PRNetAddr netAddr; michael@0: PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr); michael@0: memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr)); michael@0: michael@0: char buf[256]; michael@0: PR_NetAddrToString(&netAddr, buf, sizeof(buf)); michael@0: michael@0: printf(" ip.addr = %s\n", buf); michael@0: } michael@0: else { michael@0: printf(" name.host = %s\n", hinfo->name.host); michael@0: } michael@0: #endif michael@0: michael@0: mHostFiltersArray.AppendElement(hinfo); michael@0: hinfo = nullptr; michael@0: loser: michael@0: delete hinfo; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsProtocolProxyService::GetProtocolInfo(nsIURI *uri, nsProtocolInfo *info) michael@0: { michael@0: NS_PRECONDITION(uri, "URI is null"); michael@0: NS_PRECONDITION(info, "info is null"); michael@0: michael@0: nsresult rv; michael@0: michael@0: rv = uri->GetScheme(info->scheme); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr ios = do_GetIOService(&rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr handler; michael@0: rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = handler->GetProtocolFlags(&info->flags); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = handler->GetDefaultPort(&info->defaultPort); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsProtocolProxyService::NewProxyInfo_Internal(const char *aType, michael@0: const nsACString &aHost, michael@0: int32_t aPort, michael@0: const nsACString &aUsername, michael@0: const nsACString &aPassword, michael@0: uint32_t aFlags, michael@0: uint32_t aFailoverTimeout, michael@0: nsIProxyInfo *aFailoverProxy, michael@0: uint32_t aResolveFlags, michael@0: nsIProxyInfo **aResult) michael@0: { michael@0: if (aPort <= 0) michael@0: aPort = -1; michael@0: michael@0: nsCOMPtr failover; michael@0: if (aFailoverProxy) { michael@0: failover = do_QueryInterface(aFailoverProxy); michael@0: NS_ENSURE_ARG(failover); michael@0: } michael@0: michael@0: nsProxyInfo *proxyInfo = new nsProxyInfo(); michael@0: if (!proxyInfo) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: proxyInfo->mType = aType; michael@0: proxyInfo->mHost = aHost; michael@0: proxyInfo->mPort = aPort; michael@0: proxyInfo->mUsername = aUsername; michael@0: proxyInfo->mPassword = aPassword; michael@0: proxyInfo->mFlags = aFlags; michael@0: proxyInfo->mResolveFlags = aResolveFlags; michael@0: proxyInfo->mTimeout = aFailoverTimeout == UINT32_MAX michael@0: ? mFailedProxyTimeout : aFailoverTimeout; michael@0: failover.swap(proxyInfo->mNext); michael@0: michael@0: NS_ADDREF(*aResult = proxyInfo); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsProtocolProxyService::Resolve_Internal(nsIChannel *channel, michael@0: const nsProtocolInfo &info, michael@0: uint32_t flags, michael@0: bool *usePACThread, michael@0: nsIProxyInfo **result) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(channel); michael@0: nsresult rv = SetupPACThread(); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: *usePACThread = false; michael@0: *result = nullptr; michael@0: michael@0: if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY)) michael@0: return NS_OK; // Can't proxy this (filters may not override) michael@0: michael@0: nsCOMPtr uri; michael@0: channel->GetURI(getter_AddRefs(uri)); michael@0: michael@0: // See bug #586908. michael@0: // Avoid endless loop if |uri| is the current PAC-URI. Returning OK michael@0: // here means that we will not use a proxy for this connection. michael@0: if (mPACMan && mPACMan->IsPACURI(uri)) michael@0: return NS_OK; michael@0: michael@0: bool mainThreadOnly; michael@0: if (mSystemProxySettings && michael@0: mProxyConfig == PROXYCONFIG_SYSTEM && michael@0: NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) && michael@0: !mainThreadOnly) { michael@0: *usePACThread = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) { michael@0: // If the system proxy setting implementation is not threadsafe (e.g michael@0: // linux gconf), we'll do it inline here. Such implementations promise michael@0: // not to block michael@0: michael@0: nsAutoCString PACURI; michael@0: nsAutoCString pacString; michael@0: michael@0: if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) && michael@0: !PACURI.IsEmpty()) { michael@0: // There is a PAC URI configured. If it is unchanged, then michael@0: // just execute the PAC thread. If it is changed then load michael@0: // the new value michael@0: michael@0: if (mPACMan && mPACMan->IsPACURI(PACURI)) { michael@0: // unchanged michael@0: *usePACThread = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: ConfigureFromPAC(PACURI, false); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoCString spec; michael@0: nsAutoCString host; michael@0: nsAutoCString scheme; michael@0: int32_t port = -1; michael@0: michael@0: uri->GetAsciiSpec(spec); michael@0: uri->GetAsciiHost(host); michael@0: uri->GetScheme(scheme); michael@0: uri->GetPort(&port); michael@0: michael@0: // now try the system proxy settings for this particular url michael@0: if (NS_SUCCEEDED(mSystemProxySettings-> michael@0: GetProxyForURI(spec, scheme, host, port, michael@0: pacString))) { michael@0: ProcessPACString(pacString, 0, result); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // if proxies are enabled and this host:port combo is supposed to use a michael@0: // proxy, check for a proxy. michael@0: if (mProxyConfig == PROXYCONFIG_DIRECT || michael@0: (mProxyConfig == PROXYCONFIG_MANUAL && michael@0: !CanUseProxy(uri, info.defaultPort))) michael@0: return NS_OK; michael@0: michael@0: // Proxy auto config magic... michael@0: if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD) { michael@0: // Do not query PAC now. michael@0: *usePACThread = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If we aren't in manual proxy configuration mode then we don't michael@0: // want to honor any manual specific prefs that might be still set michael@0: if (mProxyConfig != PROXYCONFIG_MANUAL) michael@0: return NS_OK; michael@0: michael@0: // proxy info values for manual configuration mode michael@0: const char *type = nullptr; michael@0: const nsACString *host = nullptr; michael@0: int32_t port = -1; michael@0: michael@0: uint32_t proxyFlags = 0; michael@0: michael@0: if ((flags & RESOLVE_PREFER_SOCKS_PROXY) && michael@0: !mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) { michael@0: host = &mSOCKSProxyHost; michael@0: if (mSOCKSProxyVersion == 4) michael@0: type = kProxyType_SOCKS4; michael@0: else michael@0: type = kProxyType_SOCKS; michael@0: port = mSOCKSProxyPort; michael@0: if (mSOCKSProxyRemoteDNS) michael@0: proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST; michael@0: } michael@0: else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) && michael@0: !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) { michael@0: host = &mHTTPSProxyHost; michael@0: type = kProxyType_HTTP; michael@0: port = mHTTPSProxyPort; michael@0: } michael@0: else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 && michael@0: ((flags & RESOLVE_IGNORE_URI_SCHEME) || michael@0: info.scheme.EqualsLiteral("http"))) { michael@0: host = &mHTTPProxyHost; michael@0: type = kProxyType_HTTP; michael@0: port = mHTTPProxyPort; michael@0: } michael@0: else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 && michael@0: !(flags & RESOLVE_IGNORE_URI_SCHEME) && michael@0: info.scheme.EqualsLiteral("https")) { michael@0: host = &mHTTPSProxyHost; michael@0: type = kProxyType_HTTP; michael@0: port = mHTTPSProxyPort; michael@0: } michael@0: else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 && michael@0: !(flags & RESOLVE_IGNORE_URI_SCHEME) && michael@0: info.scheme.EqualsLiteral("ftp")) { michael@0: host = &mFTPProxyHost; michael@0: type = kProxyType_HTTP; michael@0: port = mFTPProxyPort; michael@0: } michael@0: else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) { michael@0: host = &mSOCKSProxyHost; michael@0: if (mSOCKSProxyVersion == 4) michael@0: type = kProxyType_SOCKS4; michael@0: else michael@0: type = kProxyType_SOCKS; michael@0: port = mSOCKSProxyPort; michael@0: if (mSOCKSProxyRemoteDNS) michael@0: proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST; michael@0: } michael@0: michael@0: if (type) { michael@0: rv = NewProxyInfo_Internal(type, *host, port, michael@0: mSOCKSProxyUsername, mSOCKSProxyPassword, michael@0: proxyFlags, UINT32_MAX, nullptr, flags, michael@0: result); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo *aProxy) michael@0: { michael@0: // Disable Prefetch in the DNS service if a proxy is in use. michael@0: if (!aProxy) michael@0: return; michael@0: michael@0: nsCOMPtr pi = do_QueryInterface(aProxy); michael@0: if (!pi || michael@0: !pi->mType || michael@0: pi->mType == kProxyType_DIRECT) michael@0: return; michael@0: michael@0: nsCOMPtr dns = do_GetService(NS_DNSSERVICE_CONTRACTID); michael@0: if (!dns) michael@0: return; michael@0: nsCOMPtr pdns = do_QueryInterface(dns); michael@0: if (!pdns) michael@0: return; michael@0: michael@0: // We lose the prefetch optimization for the life of the dns service. michael@0: pdns->SetPrefetchEnabled(false); michael@0: } michael@0: michael@0: void michael@0: nsProtocolProxyService::ApplyFilters(nsIChannel *channel, const nsProtocolInfo &info, michael@0: nsIProxyInfo **list) michael@0: { michael@0: if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY)) michael@0: return; michael@0: michael@0: // We prune the proxy list prior to invoking each filter. This may be michael@0: // somewhat inefficient, but it seems like a good idea since we want each michael@0: // filter to "see" a valid proxy list. michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr result; michael@0: michael@0: for (FilterLink *iter = mFilters; iter; iter = iter->next) { michael@0: PruneProxyInfo(info, list); michael@0: if (!!iter->filter) { michael@0: nsCOMPtr uri; michael@0: channel->GetURI(getter_AddRefs(uri)); michael@0: if (!!uri) { michael@0: nsCOMPtr httpChannel = do_QueryInterface(channel); michael@0: nsCOMPtr proxyURI = nullptr; michael@0: if (!!httpChannel) { michael@0: httpChannel->GetProxyURI(getter_AddRefs(proxyURI)); michael@0: } michael@0: rv = iter->filter->ApplyFilter(this, proxyURI ? proxyURI : uri, *list, michael@0: getter_AddRefs(result)); michael@0: } michael@0: } else if (!!iter->channelFilter) { michael@0: rv = iter->channelFilter->ApplyFilter(this, channel, *list, michael@0: getter_AddRefs(result)); michael@0: } michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: result.swap(*list); michael@0: } michael@0: michael@0: PruneProxyInfo(info, list); michael@0: } michael@0: michael@0: void michael@0: nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info, michael@0: nsIProxyInfo **list) michael@0: { michael@0: if (!*list) michael@0: return; michael@0: nsProxyInfo *head = nullptr; michael@0: CallQueryInterface(*list, &head); michael@0: if (!head) { michael@0: NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo"); michael@0: return; michael@0: } michael@0: NS_RELEASE(*list); michael@0: michael@0: // Pruning of disabled proxies works like this: michael@0: // - If all proxies are disabled, return the full list michael@0: // - Otherwise, remove the disabled proxies. michael@0: // michael@0: // Pruning of disallowed proxies works like this: michael@0: // - If the protocol handler disallows the proxy, then we disallow it. michael@0: michael@0: // Start by removing all disallowed proxies if required: michael@0: if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) { michael@0: nsProxyInfo *last = nullptr, *iter = head; michael@0: while (iter) { michael@0: if (iter->Type() == kProxyType_HTTP) { michael@0: // reject! michael@0: if (last) michael@0: last->mNext = iter->mNext; michael@0: else michael@0: head = iter->mNext; michael@0: nsProxyInfo *next = iter->mNext; michael@0: iter->mNext = nullptr; michael@0: iter->Release(); michael@0: iter = next; michael@0: } else { michael@0: last = iter; michael@0: iter = iter->mNext; michael@0: } michael@0: } michael@0: if (!head) michael@0: return; michael@0: } michael@0: michael@0: // Now, scan to see if all remaining proxies are disabled. If so, then michael@0: // we'll just bail and return them all. Otherwise, we'll go and prune the michael@0: // disabled ones. michael@0: michael@0: bool allDisabled = true; michael@0: michael@0: nsProxyInfo *iter; michael@0: for (iter = head; iter; iter = iter->mNext) { michael@0: if (!IsProxyDisabled(iter)) { michael@0: allDisabled = false; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (allDisabled) michael@0: LOG(("All proxies are disabled, so trying all again")); michael@0: else { michael@0: // remove any disabled proxies. michael@0: nsProxyInfo *last = nullptr; michael@0: for (iter = head; iter; ) { michael@0: if (IsProxyDisabled(iter)) { michael@0: // reject! michael@0: nsProxyInfo *reject = iter; michael@0: michael@0: iter = iter->mNext; michael@0: if (last) michael@0: last->mNext = iter; michael@0: else michael@0: head = iter; michael@0: michael@0: reject->mNext = nullptr; michael@0: NS_RELEASE(reject); michael@0: continue; michael@0: } michael@0: michael@0: // since we are about to use this proxy, make sure it is not on michael@0: // the disabled proxy list. we'll add it back to that list if michael@0: // we have to (in GetFailoverForProxy). michael@0: // michael@0: // XXX(darin): It might be better to do this as a final pass. michael@0: // michael@0: EnableProxy(iter); michael@0: michael@0: last = iter; michael@0: iter = iter->mNext; michael@0: } michael@0: } michael@0: michael@0: // if only DIRECT was specified then return no proxy info, and we're done. michael@0: if (head && !head->mNext && head->mType == kProxyType_DIRECT) michael@0: NS_RELEASE(head); michael@0: michael@0: *list = head; // Transfer ownership michael@0: }