зеркало из https://github.com/mozilla/gecko-dev.git
2501 строка
78 KiB
C++
2501 строка
78 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=4 sw=2 sts=2 et: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/AutoRestore.h"
|
|
|
|
#include "nsProtocolProxyService.h"
|
|
#include "nsProxyInfo.h"
|
|
#include "nsIClassInfoImpl.h"
|
|
#include "nsIIOService.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIProtocolHandler.h"
|
|
#include "nsIProtocolProxyCallback.h"
|
|
#include "nsIChannel.h"
|
|
#include "nsICancelable.h"
|
|
#include "nsDNSService2.h"
|
|
#include "nsPIDNSService.h"
|
|
#include "nsIPrefBranch.h"
|
|
#include "nsIPrefService.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsCRT.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsQueryObject.h"
|
|
#include "nsSOCKSIOLayer.h"
|
|
#include "nsString.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsNetCID.h"
|
|
#include "prnetdb.h"
|
|
#include "nsPACMan.h"
|
|
#include "nsProxyRelease.h"
|
|
#include "mozilla/Mutex.h"
|
|
#include "mozilla/CondVar.h"
|
|
#include "nsISystemProxySettings.h"
|
|
#include "nsINetworkLinkService.h"
|
|
#include "nsIHttpChannelInternal.h"
|
|
#include "mozilla/dom/nsMixedContentBlocker.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/ScopeExit.h"
|
|
#include "mozilla/StaticPrefs_network.h"
|
|
#include "mozilla/Tokenizer.h"
|
|
#include "mozilla/Unused.h"
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
namespace mozilla {
|
|
namespace net {
|
|
|
|
extern const char kProxyType_HTTP[];
|
|
extern const char kProxyType_HTTPS[];
|
|
extern const char kProxyType_SOCKS[];
|
|
extern const char kProxyType_SOCKS4[];
|
|
extern const char kProxyType_SOCKS5[];
|
|
extern const char kProxyType_DIRECT[];
|
|
extern const char kProxyType_PROXY[];
|
|
|
|
#undef LOG
|
|
#define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define PROXY_PREF_BRANCH "network.proxy"
|
|
#define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// This structure is intended to be allocated on the stack
|
|
struct nsProtocolInfo {
|
|
nsAutoCString scheme;
|
|
uint32_t flags = 0;
|
|
int32_t defaultPort = 0;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Return the channel's proxy URI, or if it doesn't exist, the
|
|
// channel's main URI.
|
|
static nsresult GetProxyURI(nsIChannel* channel, nsIURI** aOut) {
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsIURI> proxyURI;
|
|
nsCOMPtr<nsIHttpChannelInternal> httpChannel(do_QueryInterface(channel));
|
|
if (httpChannel) {
|
|
rv = httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
|
|
}
|
|
if (!proxyURI) {
|
|
rv = channel->GetURI(getter_AddRefs(proxyURI));
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
proxyURI.forget(aOut);
|
|
return NS_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
nsProtocolProxyService::FilterLink::FilterLink(uint32_t p,
|
|
nsIProtocolProxyFilter* f)
|
|
: position(p), filter(f), channelFilter(nullptr) {
|
|
LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, filter=%p", this,
|
|
f));
|
|
}
|
|
nsProtocolProxyService::FilterLink::FilterLink(
|
|
uint32_t p, nsIProtocolProxyChannelFilter* cf)
|
|
: position(p), filter(nullptr), channelFilter(cf) {
|
|
LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, channel-filter=%p",
|
|
this, cf));
|
|
}
|
|
|
|
nsProtocolProxyService::FilterLink::~FilterLink() {
|
|
LOG(("nsProtocolProxyService::FilterLink::~FilterLink %p", this));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// The nsPACManCallback portion of this implementation should be run
|
|
// on the main thread - so call nsPACMan::AsyncGetProxyForURI() with
|
|
// a true mainThreadResponse parameter.
|
|
class nsAsyncResolveRequest final : public nsIRunnable,
|
|
public nsPACManCallback,
|
|
public nsICancelable {
|
|
public:
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
nsAsyncResolveRequest(nsProtocolProxyService* pps, nsIChannel* channel,
|
|
uint32_t aResolveFlags,
|
|
nsIProtocolProxyCallback* callback)
|
|
: mResolveFlags(aResolveFlags),
|
|
mPPS(pps),
|
|
mXPComPPS(pps),
|
|
mChannel(channel),
|
|
mCallback(callback) {
|
|
NS_ASSERTION(mCallback, "null callback");
|
|
}
|
|
|
|
private:
|
|
~nsAsyncResolveRequest() {
|
|
if (!NS_IsMainThread()) {
|
|
// these xpcom pointers might need to be proxied back to the
|
|
// main thread to delete safely, but if this request had its
|
|
// callbacks called normally they will all be null and this is a nop
|
|
|
|
if (mChannel) {
|
|
NS_ReleaseOnMainThread("nsAsyncResolveRequest::mChannel",
|
|
mChannel.forget());
|
|
}
|
|
|
|
if (mCallback) {
|
|
NS_ReleaseOnMainThread("nsAsyncResolveRequest::mCallback",
|
|
mCallback.forget());
|
|
}
|
|
|
|
if (mProxyInfo) {
|
|
NS_ReleaseOnMainThread("nsAsyncResolveRequest::mProxyInfo",
|
|
mProxyInfo.forget());
|
|
}
|
|
|
|
if (mXPComPPS) {
|
|
NS_ReleaseOnMainThread("nsAsyncResolveRequest::mXPComPPS",
|
|
mXPComPPS.forget());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper class to loop over all registered asynchronous filters.
|
|
// There is a cycle between nsAsyncResolveRequest and this class that
|
|
// is broken after the last filter has called back on this object.
|
|
class AsyncApplyFilters final : public nsIProxyProtocolFilterResult,
|
|
public nsIRunnable,
|
|
public nsICancelable {
|
|
// The reference counter is thread-safe, but the processing logic is
|
|
// considered single thread only. We want the counter be thread safe,
|
|
// since this class can be released on a background thread.
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
NS_DECL_NSIPROXYPROTOCOLFILTERRESULT
|
|
NS_DECL_NSIRUNNABLE
|
|
NS_DECL_NSICANCELABLE
|
|
|
|
using Callback =
|
|
std::function<nsresult(nsAsyncResolveRequest*, nsIProxyInfo*, bool)>;
|
|
|
|
explicit AsyncApplyFilters(nsProtocolInfo& aInfo,
|
|
Callback const& aCallback);
|
|
// This method starts the processing or filters. If all of them
|
|
// answer synchronously (call back from within applyFilters) this method
|
|
// will return immediately and the returning result will carry return
|
|
// result of the callback given in constructor.
|
|
// This method is looping the registered filters (that have been copied
|
|
// locally) as long as an answer from a filter is obtained synchronously.
|
|
// Note that filters are processed serially to let them build a list
|
|
// of proxy info.
|
|
nsresult AsyncProcess(nsAsyncResolveRequest* aRequest);
|
|
|
|
private:
|
|
using FilterLink = nsProtocolProxyService::FilterLink;
|
|
|
|
virtual ~AsyncApplyFilters();
|
|
// Processes the next filter and loops until a filter is successfully
|
|
// called on or it has called back to us.
|
|
nsresult ProcessNextFilter();
|
|
// Called after the last filter has been processed (=called back or failed
|
|
// to be called on)
|
|
nsresult Finish();
|
|
|
|
nsProtocolInfo mInfo;
|
|
// This is nullified before we call back on the request or when
|
|
// Cancel() on this object has been called to break the cycle
|
|
// and signal to stop.
|
|
RefPtr<nsAsyncResolveRequest> mRequest;
|
|
Callback mCallback;
|
|
// A shallow snapshot of filters as they were registered at the moment
|
|
// we started to process filters for the given resolve request.
|
|
nsTArray<RefPtr<FilterLink>> mFiltersCopy;
|
|
|
|
nsTArray<RefPtr<FilterLink>>::index_type mNextFilterIndex;
|
|
// true when we are calling ProcessNextFilter() from inside AsyncProcess(),
|
|
// false otherwise.
|
|
bool mProcessingInLoop;
|
|
// true after a filter called back to us with a result, dropped to false
|
|
// just before we call a filter.
|
|
bool mFilterCalledBack;
|
|
|
|
// This keeps the initial value we pass to the first filter in line and also
|
|
// collects the result from each filter call.
|
|
nsCOMPtr<nsIProxyInfo> mProxyInfo;
|
|
|
|
// The logic is written as non-thread safe, assert single-thread usage.
|
|
nsCOMPtr<nsISerialEventTarget> mProcessingThread;
|
|
};
|
|
|
|
void EnsureResolveFlagsMatch() {
|
|
nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(mProxyInfo);
|
|
if (!pi || pi->ResolveFlags() == mResolveFlags) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIProxyInfo> proxyInfo =
|
|
pi->CloneProxyInfoWithNewResolveFlags(mResolveFlags);
|
|
mProxyInfo.swap(proxyInfo);
|
|
}
|
|
|
|
public:
|
|
nsresult ProcessLocally(nsProtocolInfo& info, nsIProxyInfo* pi,
|
|
bool isSyncOK) {
|
|
SetResult(NS_OK, pi);
|
|
|
|
auto consumeFiltersResult = [isSyncOK](nsAsyncResolveRequest* ctx,
|
|
nsIProxyInfo* pi,
|
|
bool aCalledAsync) -> nsresult {
|
|
ctx->SetResult(NS_OK, pi);
|
|
if (isSyncOK || aCalledAsync) {
|
|
ctx->Run();
|
|
return NS_OK;
|
|
}
|
|
|
|
return ctx->DispatchCallback();
|
|
};
|
|
|
|
mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
|
|
// may call consumeFiltersResult() directly
|
|
return mAsyncFilterApplier->AsyncProcess(this);
|
|
}
|
|
|
|
void SetResult(nsresult status, nsIProxyInfo* pi) {
|
|
mStatus = status;
|
|
mProxyInfo = pi;
|
|
}
|
|
|
|
NS_IMETHOD Run() override {
|
|
if (mCallback) DoCallback();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD Cancel(nsresult reason) override {
|
|
NS_ENSURE_ARG(NS_FAILED(reason));
|
|
|
|
if (mAsyncFilterApplier) {
|
|
mAsyncFilterApplier->Cancel(reason);
|
|
}
|
|
|
|
// If we've already called DoCallback then, nothing more to do.
|
|
if (!mCallback) return NS_OK;
|
|
|
|
SetResult(reason, nullptr);
|
|
return DispatchCallback();
|
|
}
|
|
|
|
nsresult DispatchCallback() {
|
|
if (mDispatched) { // Only need to dispatch once
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult rv = NS_DispatchToCurrentThread(this);
|
|
if (NS_FAILED(rv)) {
|
|
NS_WARNING("unable to dispatch callback event");
|
|
} else {
|
|
mDispatched = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
mCallback = nullptr; // break possible reference cycle
|
|
return rv;
|
|
}
|
|
|
|
private:
|
|
// Called asynchronously, so we do not need to post another PLEvent
|
|
// before calling DoCallback.
|
|
void OnQueryComplete(nsresult status, const nsACString& pacString,
|
|
const nsACString& newPACURL) override {
|
|
// If we've already called DoCallback then, nothing more to do.
|
|
if (!mCallback) return;
|
|
|
|
// Provided we haven't been canceled...
|
|
if (mStatus == NS_OK) {
|
|
mStatus = status;
|
|
mPACString = pacString;
|
|
mPACURL = newPACURL;
|
|
}
|
|
|
|
// In the cancelation case, we may still have another PLEvent in
|
|
// the queue that wants to call DoCallback. No need to wait for
|
|
// it, just run the callback now.
|
|
DoCallback();
|
|
}
|
|
|
|
void DoCallback() {
|
|
bool pacAvailable = true;
|
|
if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) {
|
|
// If the PAC service is not avail (e.g. failed pac load
|
|
// or shutdown) then we will be going direct. Make that
|
|
// mapping now so that any filters are still applied.
|
|
mPACString = "DIRECT;"_ns;
|
|
mStatus = NS_OK;
|
|
|
|
LOG(("pac not available, use DIRECT\n"));
|
|
pacAvailable = false;
|
|
}
|
|
|
|
// Generate proxy info from the PAC string if appropriate
|
|
if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
|
|
mPPS->ProcessPACString(mPACString, mResolveFlags,
|
|
getter_AddRefs(mProxyInfo));
|
|
nsCOMPtr<nsIURI> proxyURI;
|
|
GetProxyURI(mChannel, getter_AddRefs(proxyURI));
|
|
|
|
// Now apply proxy filters
|
|
nsProtocolInfo info;
|
|
mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
|
|
|
|
auto consumeFiltersResult = [pacAvailable](nsAsyncResolveRequest* self,
|
|
nsIProxyInfo* pi,
|
|
bool async) -> nsresult {
|
|
LOG(("DoCallback::consumeFiltersResult this=%p, pi=%p, async=%d", self,
|
|
pi, async));
|
|
|
|
self->mProxyInfo = pi;
|
|
|
|
if (pacAvailable) {
|
|
// if !pacAvailable, it was already logged above
|
|
LOG(("pac thread callback %s\n", self->mPACString.get()));
|
|
}
|
|
|
|
if (NS_SUCCEEDED(self->mStatus)) {
|
|
self->mPPS->MaybeDisableDNSPrefetch(self->mProxyInfo);
|
|
}
|
|
|
|
self->EnsureResolveFlagsMatch();
|
|
self->mCallback->OnProxyAvailable(self, self->mChannel,
|
|
self->mProxyInfo, self->mStatus);
|
|
|
|
return NS_OK;
|
|
};
|
|
|
|
if (NS_SUCCEEDED(mStatus)) {
|
|
mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
|
|
// This may call consumeFiltersResult() directly.
|
|
mAsyncFilterApplier->AsyncProcess(this);
|
|
return;
|
|
}
|
|
|
|
consumeFiltersResult(this, nullptr, false);
|
|
} else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
|
|
LOG(("pac thread callback indicates new pac file load\n"));
|
|
|
|
nsCOMPtr<nsIURI> proxyURI;
|
|
GetProxyURI(mChannel, getter_AddRefs(proxyURI));
|
|
|
|
// trigger load of new pac url
|
|
nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// now that the load is triggered, we can resubmit the query
|
|
RefPtr<nsAsyncResolveRequest> newRequest =
|
|
new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback);
|
|
rv = mPPS->mPACMan->AsyncGetProxyForURI(proxyURI, newRequest,
|
|
mResolveFlags, true);
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
|
|
}
|
|
|
|
// do not call onproxyavailable() in SUCCESS case - the newRequest will
|
|
// take care of that
|
|
} else {
|
|
LOG(("pac thread callback did not provide information %" PRIX32 "\n",
|
|
static_cast<uint32_t>(mStatus)));
|
|
if (NS_SUCCEEDED(mStatus)) mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
|
|
EnsureResolveFlagsMatch();
|
|
mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
|
|
}
|
|
|
|
// We are on the main thread now and don't need these any more so
|
|
// release them to avoid having to proxy them back to the main thread
|
|
// in the dtor
|
|
mCallback = nullptr; // in case the callback holds an owning ref to us
|
|
mPPS = nullptr;
|
|
mXPComPPS = nullptr;
|
|
mChannel = nullptr;
|
|
mProxyInfo = nullptr;
|
|
}
|
|
|
|
private:
|
|
nsresult mStatus{NS_OK};
|
|
nsCString mPACString;
|
|
nsCString mPACURL;
|
|
bool mDispatched{false};
|
|
uint32_t mResolveFlags;
|
|
|
|
nsProtocolProxyService* mPPS;
|
|
nsCOMPtr<nsIProtocolProxyService> mXPComPPS;
|
|
nsCOMPtr<nsIChannel> mChannel;
|
|
nsCOMPtr<nsIProtocolProxyCallback> mCallback;
|
|
nsCOMPtr<nsIProxyInfo> mProxyInfo;
|
|
|
|
RefPtr<AsyncApplyFilters> mAsyncFilterApplier;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
|
|
|
|
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest::AsyncApplyFilters,
|
|
nsIProxyProtocolFilterResult, nsICancelable, nsIRunnable)
|
|
|
|
nsAsyncResolveRequest::AsyncApplyFilters::AsyncApplyFilters(
|
|
nsProtocolInfo& aInfo, Callback const& aCallback)
|
|
: mInfo(aInfo),
|
|
mCallback(aCallback),
|
|
mNextFilterIndex(0),
|
|
mProcessingInLoop(false),
|
|
mFilterCalledBack(false) {
|
|
LOG(("AsyncApplyFilters %p", this));
|
|
}
|
|
|
|
nsAsyncResolveRequest::AsyncApplyFilters::~AsyncApplyFilters() {
|
|
LOG(("~AsyncApplyFilters %p", this));
|
|
|
|
MOZ_ASSERT(!mRequest);
|
|
MOZ_ASSERT(!mProxyInfo);
|
|
MOZ_ASSERT(!mFiltersCopy.Length());
|
|
}
|
|
|
|
nsresult nsAsyncResolveRequest::AsyncApplyFilters::AsyncProcess(
|
|
nsAsyncResolveRequest* aRequest) {
|
|
LOG(("AsyncApplyFilters::AsyncProcess %p for req %p", this, aRequest));
|
|
|
|
MOZ_ASSERT(!mRequest, "AsyncApplyFilters started more than once!");
|
|
|
|
if (!(mInfo.flags & nsIProtocolHandler::ALLOWS_PROXY)) {
|
|
// Calling the callback directly (not via Finish()) since we
|
|
// don't want to prune.
|
|
return mCallback(aRequest, aRequest->mProxyInfo, false);
|
|
}
|
|
|
|
mProcessingThread = NS_GetCurrentThread();
|
|
|
|
mRequest = aRequest;
|
|
mProxyInfo = aRequest->mProxyInfo;
|
|
|
|
aRequest->mPPS->CopyFilters(mFiltersCopy);
|
|
|
|
// We want to give filters a chance to process in a single loop to prevent
|
|
// any current-thread dispatch delays when those are not needed.
|
|
// This code is rather "loopy" than "recursive" to prevent long stack traces.
|
|
do {
|
|
MOZ_ASSERT(!mProcessingInLoop);
|
|
|
|
mozilla::AutoRestore<bool> restore(mProcessingInLoop);
|
|
mProcessingInLoop = true;
|
|
|
|
nsresult rv = ProcessNextFilter();
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
} while (mFilterCalledBack);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsAsyncResolveRequest::AsyncApplyFilters::ProcessNextFilter() {
|
|
LOG(("AsyncApplyFilters::ProcessNextFilter %p ENTER pi=%p", this,
|
|
mProxyInfo.get()));
|
|
|
|
RefPtr<FilterLink> filter;
|
|
do {
|
|
mFilterCalledBack = false;
|
|
|
|
if (!mRequest) {
|
|
// We got canceled
|
|
LOG((" canceled"));
|
|
return NS_OK; // should we let the consumer know?
|
|
}
|
|
|
|
if (mNextFilterIndex == mFiltersCopy.Length()) {
|
|
return Finish();
|
|
}
|
|
|
|
filter = mFiltersCopy[mNextFilterIndex++];
|
|
|
|
// Loop until a call to a filter succeeded. Other option is to recurse
|
|
// but that would waste stack trace when a number of filters gets registered
|
|
// and all from some reason tend to fail.
|
|
// The !mFilterCalledBack part of the condition is there to protect us from
|
|
// calling on another filter when the current one managed to call back and
|
|
// then threw. We already have the result so take it and use it since
|
|
// the next filter will be processed by the root loop or a call to
|
|
// ProcessNextFilter has already been dispatched to this thread.
|
|
LOG((" calling filter %p pi=%p", filter.get(), mProxyInfo.get()));
|
|
} while (!mRequest->mPPS->ApplyFilter(filter, mRequest->mChannel, mInfo,
|
|
mProxyInfo, this) &&
|
|
!mFilterCalledBack);
|
|
|
|
LOG(("AsyncApplyFilters::ProcessNextFilter %p LEAVE pi=%p", this,
|
|
mProxyInfo.get()));
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsAsyncResolveRequest::AsyncApplyFilters::OnProxyFilterResult(
|
|
nsIProxyInfo* aProxyInfo) {
|
|
LOG(("AsyncApplyFilters::OnProxyFilterResult %p pi=%p", this, aProxyInfo));
|
|
|
|
MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
|
|
MOZ_ASSERT(!mFilterCalledBack);
|
|
|
|
if (mFilterCalledBack) {
|
|
LOG((" duplicate notification?"));
|
|
return NS_OK;
|
|
}
|
|
|
|
mFilterCalledBack = true;
|
|
|
|
if (!mRequest) {
|
|
// We got canceled
|
|
LOG((" canceled"));
|
|
return NS_OK;
|
|
}
|
|
|
|
mProxyInfo = aProxyInfo;
|
|
|
|
if (mProcessingInLoop) {
|
|
// No need to call/dispatch ProcessNextFilter(), we are in a control
|
|
// loop that will do this for us and save recursion/dispatching.
|
|
LOG((" in a root loop"));
|
|
return NS_OK;
|
|
}
|
|
|
|
if (mNextFilterIndex == mFiltersCopy.Length()) {
|
|
// We are done, all filters have been called on!
|
|
Finish();
|
|
return NS_OK;
|
|
}
|
|
|
|
// Redispatch, since we don't want long stacks when filters respond
|
|
// synchronously.
|
|
LOG((" redispatching"));
|
|
NS_DispatchToCurrentThread(this);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsAsyncResolveRequest::AsyncApplyFilters::Run() {
|
|
LOG(("AsyncApplyFilters::Run %p", this));
|
|
|
|
MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
|
|
|
|
ProcessNextFilter();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsAsyncResolveRequest::AsyncApplyFilters::Finish() {
|
|
LOG(("AsyncApplyFilters::Finish %p pi=%p", this, mProxyInfo.get()));
|
|
|
|
MOZ_ASSERT(mRequest);
|
|
|
|
mFiltersCopy.Clear();
|
|
|
|
RefPtr<nsAsyncResolveRequest> request;
|
|
request.swap(mRequest);
|
|
|
|
nsCOMPtr<nsIProxyInfo> pi;
|
|
pi.swap(mProxyInfo);
|
|
|
|
request->mPPS->PruneProxyInfo(mInfo, pi);
|
|
return mCallback(request, pi, !mProcessingInLoop);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsAsyncResolveRequest::AsyncApplyFilters::Cancel(nsresult reason) {
|
|
LOG(("AsyncApplyFilters::Cancel %p", this));
|
|
|
|
MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
|
|
|
|
// This will be called only from inside the request, so don't call
|
|
// its's callback. Dropping the members means we simply break the cycle.
|
|
mFiltersCopy.Clear();
|
|
mProxyInfo = nullptr;
|
|
mRequest = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// Bug 1366133: make GetPACURI and GetSystemWPADSetting off-main-thread since it
|
|
// may hang on Windows platform
|
|
class AsyncGetPACURIRequestOrSystemWPADSetting final : public nsIRunnable {
|
|
public:
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
using CallbackFunc = nsresult (nsProtocolProxyService::*)(bool, bool,
|
|
nsresult,
|
|
const nsACString&,
|
|
bool);
|
|
|
|
AsyncGetPACURIRequestOrSystemWPADSetting(
|
|
nsProtocolProxyService* aService, CallbackFunc aCallback,
|
|
nsISystemProxySettings* aSystemProxySettings, bool aMainThreadOnly,
|
|
bool aForceReload, bool aResetPACThread, bool aSystemWPADAllowed)
|
|
: mIsMainThreadOnly(aMainThreadOnly),
|
|
mService(aService),
|
|
mServiceHolder(do_QueryObject(aService)),
|
|
mCallback(aCallback),
|
|
mSystemProxySettings(aSystemProxySettings),
|
|
mForceReload(aForceReload),
|
|
mResetPACThread(aResetPACThread),
|
|
mSystemWPADAllowed(aSystemWPADAllowed) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
Unused << mIsMainThreadOnly;
|
|
}
|
|
|
|
NS_IMETHOD Run() override {
|
|
MOZ_ASSERT(NS_IsMainThread() == mIsMainThreadOnly);
|
|
|
|
nsresult rv;
|
|
nsCString pacUri;
|
|
bool systemWPADSetting = false;
|
|
if (mSystemWPADAllowed) {
|
|
mSystemProxySettings->GetSystemWPADSetting(&systemWPADSetting);
|
|
}
|
|
|
|
rv = mSystemProxySettings->GetPACURI(pacUri);
|
|
|
|
nsCOMPtr<nsIRunnable> event =
|
|
NewNonOwningCancelableRunnableMethod<bool, bool, nsresult, nsCString,
|
|
bool>(
|
|
"AsyncGetPACURIRequestOrSystemWPADSettingCallback", mService,
|
|
mCallback, mForceReload, mResetPACThread, rv, pacUri,
|
|
systemWPADSetting);
|
|
|
|
return NS_DispatchToMainThread(event);
|
|
}
|
|
|
|
private:
|
|
~AsyncGetPACURIRequestOrSystemWPADSetting() {
|
|
NS_ReleaseOnMainThread(
|
|
"AsyncGetPACURIRequestOrSystemWPADSetting::mServiceHolder",
|
|
mServiceHolder.forget());
|
|
}
|
|
|
|
bool mIsMainThreadOnly;
|
|
|
|
nsProtocolProxyService* mService; // ref-count is hold by mServiceHolder
|
|
nsCOMPtr<nsIProtocolProxyService2> mServiceHolder;
|
|
CallbackFunc mCallback;
|
|
nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
|
|
|
|
bool mForceReload;
|
|
bool mResetPACThread;
|
|
bool mSystemWPADAllowed;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(AsyncGetPACURIRequestOrSystemWPADSetting, nsIRunnable)
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
// apply mask to address (zeros out excluded bits).
|
|
//
|
|
// NOTE: we do the byte swapping here to minimize overall swapping.
|
|
//
|
|
static void proxy_MaskIPv6Addr(PRIPv6Addr& addr, uint16_t mask_len) {
|
|
if (mask_len == 128) return;
|
|
|
|
if (mask_len > 96) {
|
|
addr.pr_s6_addr32[3] =
|
|
PR_htonl(PR_ntohl(addr.pr_s6_addr32[3]) & (~0uL << (128 - mask_len)));
|
|
} else if (mask_len > 64) {
|
|
addr.pr_s6_addr32[3] = 0;
|
|
addr.pr_s6_addr32[2] =
|
|
PR_htonl(PR_ntohl(addr.pr_s6_addr32[2]) & (~0uL << (96 - mask_len)));
|
|
} else if (mask_len > 32) {
|
|
addr.pr_s6_addr32[3] = 0;
|
|
addr.pr_s6_addr32[2] = 0;
|
|
addr.pr_s6_addr32[1] =
|
|
PR_htonl(PR_ntohl(addr.pr_s6_addr32[1]) & (~0uL << (64 - mask_len)));
|
|
} else {
|
|
addr.pr_s6_addr32[3] = 0;
|
|
addr.pr_s6_addr32[2] = 0;
|
|
addr.pr_s6_addr32[1] = 0;
|
|
addr.pr_s6_addr32[0] =
|
|
PR_htonl(PR_ntohl(addr.pr_s6_addr32[0]) & (~0uL << (32 - mask_len)));
|
|
}
|
|
}
|
|
|
|
static void proxy_GetStringPref(nsIPrefBranch* aPrefBranch, const char* aPref,
|
|
nsCString& aResult) {
|
|
nsAutoCString temp;
|
|
nsresult rv = aPrefBranch->GetCharPref(aPref, temp);
|
|
if (NS_FAILED(rv)) {
|
|
aResult.Truncate();
|
|
} else {
|
|
aResult.Assign(temp);
|
|
// all of our string prefs are hostnames, so we should remove any
|
|
// whitespace characters that the user might have unknowingly entered.
|
|
aResult.StripWhitespace();
|
|
}
|
|
}
|
|
|
|
static void proxy_GetIntPref(nsIPrefBranch* aPrefBranch, const char* aPref,
|
|
int32_t& aResult) {
|
|
int32_t temp;
|
|
nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
|
|
if (NS_FAILED(rv)) {
|
|
aResult = -1;
|
|
} else {
|
|
aResult = temp;
|
|
}
|
|
}
|
|
|
|
static void proxy_GetBoolPref(nsIPrefBranch* aPrefBranch, const char* aPref,
|
|
bool& aResult) {
|
|
bool temp;
|
|
nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
|
|
if (NS_FAILED(rv)) {
|
|
aResult = false;
|
|
} else {
|
|
aResult = temp;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
static const int32_t PROXYCONFIG_DIRECT4X = 3;
|
|
static const int32_t PROXYCONFIG_COUNT = 6;
|
|
|
|
NS_IMPL_ADDREF(nsProtocolProxyService)
|
|
NS_IMPL_RELEASE(nsProtocolProxyService)
|
|
NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
|
|
NS_PROTOCOLPROXYSERVICE_CID)
|
|
|
|
// NS_IMPL_QUERY_INTERFACE_CI with the nsProtocolProxyService QI change
|
|
NS_INTERFACE_MAP_BEGIN(nsProtocolProxyService)
|
|
NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService)
|
|
NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService2)
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
|
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
|
|
NS_INTERFACE_MAP_ENTRY(nsINamed)
|
|
NS_INTERFACE_MAP_ENTRY_CONCRETE(nsProtocolProxyService)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolProxyService)
|
|
NS_IMPL_QUERY_CLASSINFO(nsProtocolProxyService)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService, nsIProtocolProxyService,
|
|
nsIProtocolProxyService2)
|
|
|
|
nsProtocolProxyService::nsProtocolProxyService() : mSessionStart(PR_Now()) {}
|
|
|
|
nsProtocolProxyService::~nsProtocolProxyService() {
|
|
// These should have been cleaned up in our Observe method.
|
|
NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters.Length() == 0 &&
|
|
mPACMan == nullptr,
|
|
"what happened to xpcom-shutdown?");
|
|
}
|
|
|
|
// nsProtocolProxyService methods
|
|
nsresult nsProtocolProxyService::Init() {
|
|
// failure to access prefs is non-fatal
|
|
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
if (prefBranch) {
|
|
// monitor proxy prefs
|
|
prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
|
|
|
|
// read all prefs
|
|
PrefsChanged(prefBranch, nullptr);
|
|
}
|
|
|
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
if (obs) {
|
|
// register for shutdown notification so we can clean ourselves up
|
|
// properly.
|
|
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
|
obs->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// ReloadNetworkPAC() checks if there's a non-networked PAC in use then avoids
|
|
// to call ReloadPAC()
|
|
nsresult nsProtocolProxyService::ReloadNetworkPAC() {
|
|
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
if (!prefs) {
|
|
return NS_OK;
|
|
}
|
|
|
|
int32_t type;
|
|
nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
|
|
if (NS_FAILED(rv)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (type == PROXYCONFIG_PAC) {
|
|
nsAutoCString pacSpec;
|
|
prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
|
|
if (!pacSpec.IsEmpty()) {
|
|
nsCOMPtr<nsIURI> pacURI;
|
|
rv = NS_NewURI(getter_AddRefs(pacURI), pacSpec);
|
|
if (!NS_SUCCEEDED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
nsProtocolInfo pac;
|
|
rv = GetProtocolInfo(pacURI, &pac);
|
|
if (!NS_SUCCEEDED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
if (!pac.scheme.EqualsLiteral("file") &&
|
|
!pac.scheme.EqualsLiteral("data")) {
|
|
LOG((": received network changed event, reload PAC"));
|
|
ReloadPAC();
|
|
}
|
|
}
|
|
} else if ((type == PROXYCONFIG_WPAD) || (type == PROXYCONFIG_SYSTEM)) {
|
|
ReloadPAC();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsProtocolProxyService::AsyncConfigureWPADOrFromPAC(
|
|
bool aForceReload, bool aResetPACThread, bool aSystemWPADAllowed) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
bool mainThreadOnly;
|
|
nsresult rv = mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
nsCOMPtr<nsIRunnable> req = new AsyncGetPACURIRequestOrSystemWPADSetting(
|
|
this, &nsProtocolProxyService::OnAsyncGetPACURIOrSystemWPADSetting,
|
|
mSystemProxySettings, mainThreadOnly, aForceReload, aResetPACThread,
|
|
aSystemWPADAllowed);
|
|
|
|
if (mainThreadOnly) {
|
|
return req->Run();
|
|
}
|
|
|
|
return NS_DispatchBackgroundTask(req.forget(),
|
|
nsIEventTarget::DISPATCH_NORMAL);
|
|
}
|
|
|
|
nsresult nsProtocolProxyService::OnAsyncGetPACURIOrSystemWPADSetting(
|
|
bool aForceReload, bool aResetPACThread, nsresult aResult,
|
|
const nsACString& aUri, bool aSystemWPADSetting) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (aResetPACThread) {
|
|
ResetPACThread();
|
|
}
|
|
|
|
if (aSystemWPADSetting) {
|
|
if (mSystemProxySettings || !mPACMan) {
|
|
mSystemProxySettings = nullptr;
|
|
ResetPACThread();
|
|
}
|
|
|
|
nsAutoCString tempString;
|
|
ConfigureFromPAC(EmptyCString(), false);
|
|
} else if (NS_SUCCEEDED(aResult) && !aUri.IsEmpty()) {
|
|
ConfigureFromPAC(PromiseFlatCString(aUri), aForceReload);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::Observe(nsISupports* aSubject, const char* aTopic,
|
|
const char16_t* aData) {
|
|
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
|
mIsShutdown = true;
|
|
// cleanup
|
|
mHostFiltersArray.Clear();
|
|
mFilters.Clear();
|
|
|
|
if (mPACMan) {
|
|
mPACMan->Shutdown();
|
|
mPACMan = nullptr;
|
|
}
|
|
|
|
if (mReloadPACTimer) {
|
|
mReloadPACTimer->Cancel();
|
|
mReloadPACTimer = nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
if (obs) {
|
|
obs->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
|
|
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
|
}
|
|
|
|
} else if (strcmp(aTopic, NS_NETWORK_LINK_TOPIC) == 0) {
|
|
nsCString converted = NS_ConvertUTF16toUTF8(aData);
|
|
const char* state = converted.get();
|
|
if (!strcmp(state, NS_NETWORK_LINK_DATA_CHANGED)) {
|
|
uint32_t delay = StaticPrefs::network_proxy_reload_pac_delay();
|
|
LOG(("nsProtocolProxyService::Observe call ReloadNetworkPAC() delay=%u",
|
|
delay));
|
|
|
|
if (delay) {
|
|
if (mReloadPACTimer) {
|
|
mReloadPACTimer->Cancel();
|
|
mReloadPACTimer = nullptr;
|
|
}
|
|
NS_NewTimerWithCallback(getter_AddRefs(mReloadPACTimer), this, delay,
|
|
nsITimer::TYPE_ONE_SHOT);
|
|
} else {
|
|
ReloadNetworkPAC();
|
|
}
|
|
}
|
|
} else {
|
|
NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
|
|
"what is this random observer event?");
|
|
nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
|
|
if (prefs) PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::Notify(nsITimer* aTimer) {
|
|
MOZ_ASSERT(aTimer == mReloadPACTimer);
|
|
ReloadNetworkPAC();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::GetName(nsACString& aName) {
|
|
aName.AssignLiteral("nsProtocolProxyService");
|
|
return NS_OK;
|
|
}
|
|
|
|
void nsProtocolProxyService::PrefsChanged(nsIPrefBranch* prefBranch,
|
|
const char* pref) {
|
|
nsresult rv = NS_OK;
|
|
bool reloadPAC = false;
|
|
nsAutoCString tempString;
|
|
auto invokeCallback =
|
|
MakeScopeExit([&] { NotifyProxyConfigChangedInternal(); });
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("type")) ||
|
|
!strcmp(pref, PROXY_PREF("system_wpad"))) {
|
|
int32_t type = -1;
|
|
rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// bug 115720 - for ns4.x backwards compatibility
|
|
if (type == PROXYCONFIG_DIRECT4X) {
|
|
type = PROXYCONFIG_DIRECT;
|
|
// Reset the type so that the dialog looks correct, and we
|
|
// don't have to handle this case everywhere else
|
|
// I'm paranoid about a loop of some sort - only do this
|
|
// if we're enumerating all prefs, and ignore any error
|
|
if (!pref) prefBranch->SetIntPref(PROXY_PREF("type"), type);
|
|
} else if (type >= PROXYCONFIG_COUNT) {
|
|
LOG(("unknown proxy type: %" PRId32 "; assuming direct\n", type));
|
|
type = PROXYCONFIG_DIRECT;
|
|
}
|
|
mProxyConfig = type;
|
|
reloadPAC = true;
|
|
}
|
|
|
|
if (mProxyConfig == PROXYCONFIG_SYSTEM) {
|
|
mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
|
|
if (!mSystemProxySettings) mProxyConfig = PROXYCONFIG_DIRECT;
|
|
ResetPACThread();
|
|
} else {
|
|
if (mSystemProxySettings) {
|
|
mSystemProxySettings = nullptr;
|
|
ResetPACThread();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("http"))) {
|
|
proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("http_port"))) {
|
|
proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("ssl"))) {
|
|
proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("ssl_port"))) {
|
|
proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("socks"))) {
|
|
proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyTarget);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("socks_port"))) {
|
|
proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
|
|
int32_t version;
|
|
proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
|
|
// make sure this preference value remains sane
|
|
if (version == nsIProxyInfo::SOCKS_V5) {
|
|
mSOCKSProxyVersion = nsIProxyInfo::SOCKS_V5;
|
|
} else {
|
|
mSOCKSProxyVersion = nsIProxyInfo::SOCKS_V4;
|
|
}
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns"))) {
|
|
proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
|
|
mSOCKS4ProxyRemoteDNS);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("socks5_remote_dns"))) {
|
|
proxy_GetBoolPref(prefBranch, PROXY_PREF("socks5_remote_dns"),
|
|
mSOCKS5ProxyRemoteDNS);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("proxy_over_tls"))) {
|
|
proxy_GetBoolPref(prefBranch, PROXY_PREF("proxy_over_tls"), mProxyOverTLS);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("enable_wpad_over_dhcp"))) {
|
|
proxy_GetBoolPref(prefBranch, PROXY_PREF("enable_wpad_over_dhcp"),
|
|
mWPADOverDHCPEnabled);
|
|
reloadPAC = reloadPAC || mProxyConfig == PROXYCONFIG_WPAD;
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout"))) {
|
|
proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
|
|
mFailedProxyTimeout);
|
|
}
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
|
|
rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"), tempString);
|
|
if (NS_SUCCEEDED(rv)) LoadHostFilters(tempString);
|
|
}
|
|
|
|
// We're done if not using something that could give us a PAC URL
|
|
// (PAC, WPAD or System)
|
|
if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
|
|
mProxyConfig != PROXYCONFIG_SYSTEM) {
|
|
return;
|
|
}
|
|
|
|
// OK, we need to reload the PAC file if:
|
|
// 1) network.proxy.type changed, or
|
|
// 2) network.proxy.autoconfig_url changed and PAC is configured
|
|
|
|
if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url"))) reloadPAC = true;
|
|
|
|
if (reloadPAC) {
|
|
tempString.Truncate();
|
|
if (mProxyConfig == PROXYCONFIG_PAC) {
|
|
prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"), tempString);
|
|
if (mPACMan && !mPACMan->IsPACURI(tempString)) {
|
|
LOG(("PAC Thread URI Changed - Reset Pac Thread"));
|
|
ResetPACThread();
|
|
}
|
|
} else if (mProxyConfig == PROXYCONFIG_WPAD) {
|
|
LOG(("Auto-detecting proxy - Reset Pac Thread"));
|
|
ResetPACThread();
|
|
} else if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM &&
|
|
StaticPrefs::network_proxy_system_wpad()) {
|
|
AsyncConfigureWPADOrFromPAC(false, false, true);
|
|
} else if (mSystemProxySettings) {
|
|
// Get System Proxy settings if available
|
|
AsyncConfigureWPADOrFromPAC(false, false, false);
|
|
}
|
|
if (!tempString.IsEmpty() || mProxyConfig == PROXYCONFIG_WPAD) {
|
|
ConfigureFromPAC(tempString, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool nsProtocolProxyService::CanUseProxy(nsIURI* aURI, int32_t defaultPort) {
|
|
int32_t port;
|
|
nsAutoCString host;
|
|
|
|
nsresult rv = aURI->GetAsciiHost(host);
|
|
if (NS_FAILED(rv) || host.IsEmpty()) return false;
|
|
|
|
rv = aURI->GetPort(&port);
|
|
if (NS_FAILED(rv)) return false;
|
|
if (port == -1) port = defaultPort;
|
|
|
|
PRNetAddr addr;
|
|
bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
|
|
|
|
PRIPv6Addr ipv6;
|
|
if (is_ipaddr) {
|
|
// convert parsed address to IPv6
|
|
if (addr.raw.family == PR_AF_INET) {
|
|
// convert to IPv4-mapped address
|
|
PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
|
|
} else if (addr.raw.family == PR_AF_INET6) {
|
|
// copy the address
|
|
memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
|
|
} else {
|
|
NS_WARNING("unknown address family");
|
|
return true; // allow proxying
|
|
}
|
|
}
|
|
|
|
// Don't use proxy for local hosts (plain hostname, no dots)
|
|
if ((!is_ipaddr && mFilterLocalHosts && !host.Contains('.')) ||
|
|
// This method detects if we have network.proxy.allow_hijacking_localhost
|
|
// pref enabled. If it's true then this method will always return false
|
|
// otherwise it returns true if the host matches an address that's
|
|
// hardcoded to the loopback address.
|
|
(!StaticPrefs::network_proxy_allow_hijacking_localhost() &&
|
|
nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(host))) {
|
|
LOG(("Not using proxy for this local host [%s]!\n", host.get()));
|
|
return false; // don't allow proxying
|
|
}
|
|
|
|
int32_t index = -1;
|
|
while (++index < int32_t(mHostFiltersArray.Length())) {
|
|
const auto& hinfo = mHostFiltersArray[index];
|
|
|
|
if (is_ipaddr != hinfo->is_ipaddr) continue;
|
|
if (hinfo->port && hinfo->port != port) continue;
|
|
|
|
if (is_ipaddr) {
|
|
// generate masked version of target IPv6 address
|
|
PRIPv6Addr masked;
|
|
memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
|
|
proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
|
|
|
|
// check for a match
|
|
if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0) {
|
|
return false; // proxy disallowed
|
|
}
|
|
} else {
|
|
uint32_t host_len = host.Length();
|
|
uint32_t filter_host_len = hinfo->name.host_len;
|
|
|
|
if (host_len >= filter_host_len) {
|
|
//
|
|
// compare last |filter_host_len| bytes of target hostname.
|
|
//
|
|
const char* host_tail = host.get() + host_len - filter_host_len;
|
|
if (!nsCRT::strncasecmp(host_tail, hinfo->name.host, filter_host_len)) {
|
|
// If the tail of the host string matches the filter
|
|
|
|
if (filter_host_len > 0 && hinfo->name.host[0] == '.') {
|
|
// If the filter was of the form .foo.bar.tld, all such
|
|
// matches are correct
|
|
return false; // proxy disallowed
|
|
}
|
|
|
|
// abc-def.example.org should not match def.example.org
|
|
// however, *.def.example.org should match .def.example.org
|
|
// We check that the filter doesn't start with a `.`. If it does,
|
|
// then the strncasecmp above should suffice. If it doesn't,
|
|
// then we should only consider it a match if the strncasecmp happened
|
|
// at a subdomain boundary
|
|
if (host_len > filter_host_len && *(host_tail - 1) == '.') {
|
|
// If the host was something.foo.bar.tld and the filter
|
|
// was foo.bar.tld, it's still a match.
|
|
// the character right before the tail must be a
|
|
// `.` for this to work
|
|
return false; // proxy disallowed
|
|
}
|
|
|
|
if (host_len == filter_host_len) {
|
|
// If the host and filter are of the same length,
|
|
// they should match
|
|
return false; // proxy disallowed
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// kProxyType\* may be referred to externally in
|
|
// nsProxyInfo in order to compare by string pointer
|
|
const char kProxyType_HTTP[] = "http";
|
|
const char kProxyType_HTTPS[] = "https";
|
|
const char kProxyType_PROXY[] = "proxy";
|
|
const char kProxyType_SOCKS[] = "socks";
|
|
const char kProxyType_SOCKS4[] = "socks4";
|
|
const char kProxyType_SOCKS5[] = "socks5";
|
|
const char kProxyType_DIRECT[] = "direct";
|
|
|
|
const char* nsProtocolProxyService::ExtractProxyInfo(const char* start,
|
|
uint32_t aResolveFlags,
|
|
nsProxyInfo** result) {
|
|
*result = nullptr;
|
|
uint32_t flags = 0;
|
|
|
|
// see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
|
|
|
|
// find end of proxy info delimiter
|
|
const char* end = start;
|
|
while (*end && *end != ';') ++end;
|
|
|
|
// find end of proxy type delimiter
|
|
const char* sp = start;
|
|
while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
|
|
|
|
uint32_t len = sp - start;
|
|
const char* type = nullptr;
|
|
switch (len) {
|
|
case 4:
|
|
if (nsCRT::strncasecmp(start, kProxyType_HTTP, 4) == 0) {
|
|
type = kProxyType_HTTP;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (nsCRT::strncasecmp(start, kProxyType_PROXY, 5) == 0) {
|
|
type = kProxyType_HTTP;
|
|
} else if (nsCRT::strncasecmp(start, kProxyType_SOCKS, 5) == 0) {
|
|
type = kProxyType_SOCKS4; // assume v4 for 4x compat
|
|
if (StaticPrefs::network_proxy_default_pac_script_socks_version() ==
|
|
5) {
|
|
type = kProxyType_SOCKS;
|
|
}
|
|
} else if (nsCRT::strncasecmp(start, kProxyType_HTTPS, 5) == 0) {
|
|
type = kProxyType_HTTPS;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (nsCRT::strncasecmp(start, kProxyType_DIRECT, 6) == 0) {
|
|
type = kProxyType_DIRECT;
|
|
} else if (nsCRT::strncasecmp(start, kProxyType_SOCKS4, 6) == 0) {
|
|
type = kProxyType_SOCKS4;
|
|
} else if (nsCRT::strncasecmp(start, kProxyType_SOCKS5, 6) == 0) {
|
|
// map "SOCKS5" to "socks" to match contract-id of registered
|
|
// SOCKS-v5 socket provider.
|
|
type = kProxyType_SOCKS;
|
|
}
|
|
break;
|
|
}
|
|
if (type) {
|
|
int32_t port = -1;
|
|
|
|
// If it's a SOCKS5 proxy, do name resolution on the server side.
|
|
// We could use this with SOCKS4a servers too, but they might not
|
|
// support it.
|
|
if (type == kProxyType_SOCKS || mSOCKS5ProxyRemoteDNS) {
|
|
flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
|
|
}
|
|
|
|
// extract host:port
|
|
start = sp;
|
|
while ((*start == ' ' || *start == '\t') && start < end) start++;
|
|
|
|
// port defaults
|
|
if (type == kProxyType_HTTP) {
|
|
port = 80;
|
|
} else if (type == kProxyType_HTTPS) {
|
|
port = 443;
|
|
} else {
|
|
port = 1080;
|
|
}
|
|
|
|
RefPtr<nsProxyInfo> pi = new nsProxyInfo();
|
|
pi->mType = type;
|
|
pi->mFlags = flags;
|
|
pi->mResolveFlags = aResolveFlags;
|
|
pi->mTimeout = mFailedProxyTimeout;
|
|
|
|
// www.foo.com:8080 and http://www.foo.com:8080
|
|
nsDependentCSubstring maybeURL(start, end - start);
|
|
nsCOMPtr<nsIURI> pacURI;
|
|
|
|
nsAutoCString urlHost;
|
|
// First assume the scheme is present, e.g. http://www.example.com:8080
|
|
if (NS_FAILED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) ||
|
|
NS_FAILED(pacURI->GetAsciiHost(urlHost)) || urlHost.IsEmpty()) {
|
|
// It isn't, assume www.example.com:8080
|
|
maybeURL.Insert("http://", 0);
|
|
|
|
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL))) {
|
|
pacURI->GetAsciiHost(urlHost);
|
|
}
|
|
}
|
|
|
|
if (!urlHost.IsEmpty()) {
|
|
pi->mHost = urlHost;
|
|
|
|
int32_t tPort;
|
|
if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
|
|
port = tPort;
|
|
}
|
|
pi->mPort = port;
|
|
}
|
|
|
|
pi.forget(result);
|
|
}
|
|
|
|
while (*end == ';' || *end == ' ' || *end == '\t') ++end;
|
|
return end;
|
|
}
|
|
|
|
void nsProtocolProxyService::GetProxyKey(nsProxyInfo* pi, nsCString& key) {
|
|
key.AssignASCII(pi->mType);
|
|
if (!pi->mHost.IsEmpty()) {
|
|
key.Append(' ');
|
|
key.Append(pi->mHost);
|
|
key.Append(':');
|
|
key.AppendInt(pi->mPort);
|
|
}
|
|
}
|
|
|
|
uint32_t nsProtocolProxyService::SecondsSinceSessionStart() {
|
|
PRTime now = PR_Now();
|
|
|
|
// get time elapsed since session start
|
|
int64_t diff = now - mSessionStart;
|
|
|
|
// convert microseconds to seconds
|
|
diff /= PR_USEC_PER_SEC;
|
|
|
|
// return converted 32 bit value
|
|
return uint32_t(diff);
|
|
}
|
|
|
|
void nsProtocolProxyService::EnableProxy(nsProxyInfo* pi) {
|
|
nsAutoCString key;
|
|
GetProxyKey(pi, key);
|
|
mFailedProxies.Remove(key);
|
|
}
|
|
|
|
void nsProtocolProxyService::DisableProxy(nsProxyInfo* pi) {
|
|
nsAutoCString key;
|
|
GetProxyKey(pi, key);
|
|
|
|
uint32_t dsec = SecondsSinceSessionStart();
|
|
|
|
// Add timeout to interval (this is the time when the proxy can
|
|
// be tried again).
|
|
dsec += pi->mTimeout;
|
|
|
|
// NOTE: The classic codebase would increase the timeout value
|
|
// incrementally each time a subsequent failure occurred.
|
|
// We could do the same, but it would require that we not
|
|
// remove proxy entries in IsProxyDisabled or otherwise
|
|
// change the way we are recording disabled proxies.
|
|
// Simpler is probably better for now, and at least the
|
|
// user can tune the timeout setting via preferences.
|
|
|
|
LOG(("DisableProxy %s %d\n", key.get(), dsec));
|
|
|
|
// If this fails, oh well... means we don't have enough memory
|
|
// to remember the failed proxy.
|
|
mFailedProxies.InsertOrUpdate(key, dsec);
|
|
}
|
|
|
|
bool nsProtocolProxyService::IsProxyDisabled(nsProxyInfo* pi) {
|
|
nsAutoCString key;
|
|
GetProxyKey(pi, key);
|
|
|
|
uint32_t val;
|
|
if (!mFailedProxies.Get(key, &val)) return false;
|
|
|
|
uint32_t dsec = SecondsSinceSessionStart();
|
|
|
|
// if time passed has exceeded interval, then try proxy again.
|
|
if (dsec > val) {
|
|
mFailedProxies.Remove(key);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
nsresult nsProtocolProxyService::SetupPACThread(
|
|
nsISerialEventTarget* mainThreadEventTarget) {
|
|
if (mIsShutdown) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (mPACMan) return NS_OK;
|
|
|
|
mPACMan = new nsPACMan(mainThreadEventTarget);
|
|
|
|
bool mainThreadOnly;
|
|
nsresult rv;
|
|
if (mSystemProxySettings &&
|
|
NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
|
|
!mainThreadOnly) {
|
|
rv = mPACMan->Init(mSystemProxySettings);
|
|
} else {
|
|
rv = mPACMan->Init(nullptr);
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
mPACMan->Shutdown();
|
|
mPACMan = nullptr;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsProtocolProxyService::ResetPACThread() {
|
|
if (!mPACMan) return NS_OK;
|
|
|
|
mPACMan->Shutdown();
|
|
mPACMan = nullptr;
|
|
return SetupPACThread();
|
|
}
|
|
|
|
nsresult nsProtocolProxyService::ConfigureFromPAC(const nsCString& spec,
|
|
bool forceReload) {
|
|
nsresult rv = SetupPACThread();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool autodetect = spec.IsEmpty();
|
|
if (!forceReload && ((!autodetect && mPACMan->IsPACURI(spec)) ||
|
|
(autodetect && mPACMan->IsUsingWPAD()))) {
|
|
return NS_OK;
|
|
}
|
|
|
|
mFailedProxies.Clear();
|
|
|
|
mPACMan->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled);
|
|
return mPACMan->LoadPACFromURI(spec);
|
|
}
|
|
|
|
void nsProtocolProxyService::ProcessPACString(const nsCString& pacString,
|
|
uint32_t aResolveFlags,
|
|
nsIProxyInfo** result) {
|
|
if (pacString.IsEmpty()) {
|
|
*result = nullptr;
|
|
return;
|
|
}
|
|
|
|
const char* proxies = pacString.get();
|
|
|
|
nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
|
|
while (*proxies) {
|
|
proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
|
|
if (pi && (pi->mType == kProxyType_HTTPS) && !mProxyOverTLS) {
|
|
delete pi;
|
|
pi = nullptr;
|
|
}
|
|
|
|
if (pi) {
|
|
if (last) {
|
|
NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
|
|
last->mNext = pi;
|
|
} else {
|
|
first = pi;
|
|
}
|
|
last = pi;
|
|
}
|
|
}
|
|
*result = first;
|
|
}
|
|
|
|
// nsIProtocolProxyService2
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::ReloadPAC() {
|
|
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
if (!prefs) return NS_OK;
|
|
|
|
int32_t type;
|
|
nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
|
|
if (NS_FAILED(rv)) return NS_OK;
|
|
|
|
nsAutoCString pacSpec;
|
|
if (type == PROXYCONFIG_PAC) {
|
|
prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
|
|
} else if (type == PROXYCONFIG_SYSTEM) {
|
|
if (mSystemProxySettings) {
|
|
AsyncConfigureWPADOrFromPAC(true, true,
|
|
StaticPrefs::network_proxy_system_wpad());
|
|
} else {
|
|
ResetPACThread();
|
|
}
|
|
}
|
|
|
|
if (!pacSpec.IsEmpty() || type == PROXYCONFIG_WPAD) {
|
|
ConfigureFromPAC(pacSpec, true);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
// When sync interface is removed this can go away too
|
|
// The nsPACManCallback portion of this implementation should be run
|
|
// off the main thread, because it uses a condvar for signaling and
|
|
// the main thread is blocking on that condvar -
|
|
// so call nsPACMan::AsyncGetProxyForURI() with
|
|
// a false mainThreadResponse parameter.
|
|
class nsAsyncBridgeRequest final : public nsPACManCallback {
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
nsAsyncBridgeRequest()
|
|
: mMutex("nsDeprecatedCallback"),
|
|
mCondVar(mMutex, "nsDeprecatedCallback") {}
|
|
|
|
void OnQueryComplete(nsresult status, const nsACString& pacString,
|
|
const nsACString& newPACURL) override {
|
|
MutexAutoLock lock(mMutex);
|
|
mCompleted = true;
|
|
mStatus = status;
|
|
mPACString = pacString;
|
|
mPACURL = newPACURL;
|
|
mCondVar.Notify();
|
|
}
|
|
|
|
void Lock() MOZ_CAPABILITY_ACQUIRE(mMutex) { mMutex.Lock(); }
|
|
void Unlock() MOZ_CAPABILITY_RELEASE(mMutex) { mMutex.Unlock(); }
|
|
void Wait() { mCondVar.Wait(TimeDuration::FromSeconds(3)); }
|
|
|
|
private:
|
|
~nsAsyncBridgeRequest() = default;
|
|
|
|
friend class nsProtocolProxyService;
|
|
|
|
Mutex mMutex;
|
|
CondVar mCondVar;
|
|
|
|
nsresult mStatus MOZ_GUARDED_BY(mMutex){NS_OK};
|
|
nsCString mPACString MOZ_GUARDED_BY(mMutex);
|
|
nsCString mPACURL MOZ_GUARDED_BY(mMutex);
|
|
bool mCompleted MOZ_GUARDED_BY(mMutex){false};
|
|
};
|
|
NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
|
|
|
|
nsresult nsProtocolProxyService::AsyncResolveInternal(
|
|
nsIChannel* channel, uint32_t flags, nsIProtocolProxyCallback* callback,
|
|
nsICancelable** result, bool isSyncOK,
|
|
nsISerialEventTarget* mainThreadEventTarget) {
|
|
NS_ENSURE_ARG_POINTER(channel);
|
|
NS_ENSURE_ARG_POINTER(callback);
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*result = nullptr;
|
|
RefPtr<nsAsyncResolveRequest> ctx =
|
|
new nsAsyncResolveRequest(this, channel, flags, callback);
|
|
|
|
nsProtocolInfo info;
|
|
rv = GetProtocolInfo(uri, &info);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsCOMPtr<nsIProxyInfo> pi;
|
|
bool usePACThread;
|
|
|
|
// adapt to realtime changes in the system proxy service
|
|
if (mProxyConfig == PROXYCONFIG_SYSTEM &&
|
|
!StaticPrefs::network_proxy_system_wpad()) {
|
|
nsCOMPtr<nsISystemProxySettings> sp2 =
|
|
do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
|
|
if (sp2 != mSystemProxySettings) {
|
|
mSystemProxySettings = sp2;
|
|
ResetPACThread();
|
|
}
|
|
}
|
|
|
|
rv = SetupPACThread(mainThreadEventTarget);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// SystemProxySettings and PAC files can block the main thread
|
|
// but if neither of them are in use, we can just do the work
|
|
// right here and directly invoke the callback
|
|
|
|
rv =
|
|
Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!usePACThread || !mPACMan) {
|
|
// we can do it locally
|
|
rv = ctx->ProcessLocally(info, pi, isSyncOK);
|
|
if (NS_SUCCEEDED(rv) && !isSyncOK) {
|
|
ctx.forget(result);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
// else kick off a PAC thread query
|
|
rv = mPACMan->AsyncGetProxyForURI(uri, ctx, flags, true);
|
|
if (NS_SUCCEEDED(rv)) ctx.forget(result);
|
|
return rv;
|
|
}
|
|
|
|
// nsIProtocolProxyService
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::AsyncResolve2(
|
|
nsIChannel* channel, uint32_t flags, nsIProtocolProxyCallback* callback,
|
|
nsISerialEventTarget* mainThreadEventTarget, nsICancelable** result) {
|
|
return AsyncResolveInternal(channel, flags, callback, result, true,
|
|
mainThreadEventTarget);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::AsyncResolve(
|
|
nsISupports* channelOrURI, uint32_t flags,
|
|
nsIProtocolProxyCallback* callback,
|
|
nsISerialEventTarget* mainThreadEventTarget, nsICancelable** result) {
|
|
nsresult rv;
|
|
// Check if we got a channel:
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(channelOrURI);
|
|
if (!channel) {
|
|
nsCOMPtr<nsIURI> uri = do_QueryInterface(channelOrURI);
|
|
if (!uri) {
|
|
return NS_ERROR_NO_INTERFACE;
|
|
}
|
|
|
|
// creating a temporary channel from the URI which is not
|
|
// used to perform any network loads, hence its safe to
|
|
// use systemPrincipal as the loadingPrincipal.
|
|
rv = NS_NewChannel(getter_AddRefs(channel), uri,
|
|
nsContentUtils::GetSystemPrincipal(),
|
|
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
|
|
nsIContentPolicy::TYPE_OTHER);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return AsyncResolveInternal(channel, flags, callback, result, false,
|
|
mainThreadEventTarget);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::NewProxyInfo(
|
|
const nsACString& aType, const nsACString& aHost, int32_t aPort,
|
|
const nsACString& aProxyAuthorizationHeader,
|
|
const nsACString& aConnectionIsolationKey, uint32_t aFlags,
|
|
uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
|
|
nsIProxyInfo** aResult) {
|
|
return NewProxyInfoWithAuth(aType, aHost, aPort, ""_ns, ""_ns,
|
|
aProxyAuthorizationHeader,
|
|
aConnectionIsolationKey, aFlags, aFailoverTimeout,
|
|
aFailoverProxy, aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::NewProxyInfoWithAuth(
|
|
const nsACString& aType, const nsACString& aHost, int32_t aPort,
|
|
const nsACString& aUsername, const nsACString& aPassword,
|
|
const nsACString& aProxyAuthorizationHeader,
|
|
const nsACString& aConnectionIsolationKey, uint32_t aFlags,
|
|
uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
|
|
nsIProxyInfo** aResult) {
|
|
static const char* types[] = {kProxyType_HTTP, kProxyType_HTTPS,
|
|
kProxyType_SOCKS, kProxyType_SOCKS4,
|
|
kProxyType_DIRECT};
|
|
|
|
// resolve type; this allows us to avoid copying the type string into each
|
|
// proxy info instance. we just reference the string literals directly :)
|
|
const char* type = nullptr;
|
|
for (auto& t : types) {
|
|
if (aType.LowerCaseEqualsASCII(t)) {
|
|
type = t;
|
|
break;
|
|
}
|
|
}
|
|
NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
|
|
|
|
// We have only implemented username/password for SOCKS proxies.
|
|
if ((!aUsername.IsEmpty() || !aPassword.IsEmpty()) &&
|
|
!aType.LowerCaseEqualsASCII(kProxyType_SOCKS) &&
|
|
!aType.LowerCaseEqualsASCII(kProxyType_SOCKS4)) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return NewProxyInfo_Internal(type, aHost, aPort, aUsername, aPassword,
|
|
aProxyAuthorizationHeader,
|
|
aConnectionIsolationKey, aFlags,
|
|
aFailoverTimeout, aFailoverProxy, 0, aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo* aProxy, nsIURI* aURI,
|
|
nsresult aStatus,
|
|
nsIProxyInfo** aResult) {
|
|
// Failover is supported through a variety of methods including:
|
|
// * PAC scripts (PROXYCONFIG_PAC and PROXYCONFIG_WPAD)
|
|
// * System proxy
|
|
// * Extensions
|
|
// With extensions the mProxyConfig can be any type and the extension
|
|
// is still involved in the proxy filtering. It may have also supplied
|
|
// any number of failover proxies. We cannot determine what the mix is
|
|
// here, so we will attempt to get a failover regardless of the config
|
|
// type. MANUAL configuration will not disable a proxy.
|
|
|
|
// Verify that |aProxy| is one of our nsProxyInfo objects.
|
|
nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
|
|
NS_ENSURE_ARG(pi);
|
|
// OK, the QI checked out. We can proceed.
|
|
|
|
// Remember that this proxy is down. If the user has manually configured some
|
|
// proxies we do not want to disable them.
|
|
if (mProxyConfig != PROXYCONFIG_MANUAL) {
|
|
DisableProxy(pi);
|
|
}
|
|
|
|
// NOTE: At this point, we might want to prompt the user if we have
|
|
// not already tried going DIRECT. This is something that the
|
|
// classic codebase supported; however, IE6 does not prompt.
|
|
|
|
if (!pi->mNext) return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
LOG(("PAC failover from %s %s:%d to %s %s:%d\n", pi->mType, pi->mHost.get(),
|
|
pi->mPort, pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
|
|
|
|
*aResult = do_AddRef(pi->mNext).take();
|
|
return NS_OK;
|
|
}
|
|
|
|
namespace { // anon
|
|
|
|
class ProxyFilterPositionComparator {
|
|
using FilterLinkRef = RefPtr<nsProtocolProxyService::FilterLink>;
|
|
|
|
public:
|
|
bool Equals(const FilterLinkRef& a, const FilterLinkRef& b) const {
|
|
return a->position == b->position;
|
|
}
|
|
bool LessThan(const FilterLinkRef& a, const FilterLinkRef& b) const {
|
|
return a->position < b->position;
|
|
}
|
|
};
|
|
|
|
class ProxyFilterObjectComparator {
|
|
using FilterLinkRef = RefPtr<nsProtocolProxyService::FilterLink>;
|
|
|
|
public:
|
|
bool Equals(const FilterLinkRef& link, const nsISupports* obj) const {
|
|
return obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->filter)) ||
|
|
obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->channelFilter));
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
nsresult nsProtocolProxyService::InsertFilterLink(RefPtr<FilterLink>&& link) {
|
|
LOG(("nsProtocolProxyService::InsertFilterLink filter=%p", link.get()));
|
|
|
|
if (mIsShutdown) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// If we add a new element with the same position as an existing one, we want
|
|
// to preserve the insertion order to avoid surprises.
|
|
mFilters.InsertElementSorted(link, ProxyFilterPositionComparator());
|
|
|
|
NotifyProxyConfigChangedInternal();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter* filter,
|
|
uint32_t position) {
|
|
UnregisterFilter(filter); // remove this filter if we already have it
|
|
|
|
RefPtr<FilterLink> link = new FilterLink(position, filter);
|
|
return InsertFilterLink(std::move(link));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::RegisterChannelFilter(
|
|
nsIProtocolProxyChannelFilter* channelFilter, uint32_t position) {
|
|
UnregisterChannelFilter(
|
|
channelFilter); // remove this filter if we already have it
|
|
|
|
RefPtr<FilterLink> link = new FilterLink(position, channelFilter);
|
|
return InsertFilterLink(std::move(link));
|
|
}
|
|
|
|
nsresult nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject) {
|
|
LOG(("nsProtocolProxyService::RemoveFilterLink target=%p", givenObject));
|
|
|
|
nsresult rv =
|
|
mFilters.RemoveElement(givenObject, ProxyFilterObjectComparator())
|
|
? NS_OK
|
|
: NS_ERROR_UNEXPECTED;
|
|
if (NS_SUCCEEDED(rv)) {
|
|
NotifyProxyConfigChangedInternal();
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter* filter) {
|
|
// QI to nsISupports so we can safely test object identity.
|
|
nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
|
|
return RemoveFilterLink(givenObject);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::UnregisterChannelFilter(
|
|
nsIProtocolProxyChannelFilter* channelFilter) {
|
|
// QI to nsISupports so we can safely test object identity.
|
|
nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
|
|
return RemoveFilterLink(givenObject);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType) {
|
|
*aProxyConfigType = mProxyConfig;
|
|
return NS_OK;
|
|
}
|
|
|
|
void nsProtocolProxyService::LoadHostFilters(const nsACString& aFilters) {
|
|
if (mIsShutdown) {
|
|
return;
|
|
}
|
|
|
|
// check to see the owners flag? /!?/ TODO
|
|
if (mHostFiltersArray.Length() > 0) {
|
|
mHostFiltersArray.Clear();
|
|
}
|
|
|
|
// Reset mFilterLocalHosts - will be set to true if "<local>" is in pref
|
|
// string
|
|
mFilterLocalHosts = false;
|
|
|
|
if (aFilters.IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
|
|
// filters = filter *( "," LWS filter)
|
|
//
|
|
mozilla::Tokenizer t(aFilters);
|
|
mozilla::Tokenizer::Token token;
|
|
bool eof = false;
|
|
// while (*filters) {
|
|
while (!eof) {
|
|
// skip over spaces and ,
|
|
t.SkipWhites();
|
|
while (t.CheckChar(',')) {
|
|
t.SkipWhites();
|
|
}
|
|
|
|
nsAutoCString portStr;
|
|
nsAutoCString hostStr;
|
|
nsAutoCString maskStr;
|
|
t.Record();
|
|
|
|
bool parsingIPv6 = false;
|
|
bool parsingPort = false;
|
|
bool parsingMask = false;
|
|
while (t.Next(token)) {
|
|
if (token.Equals(mozilla::Tokenizer::Token::EndOfFile())) {
|
|
eof = true;
|
|
break;
|
|
}
|
|
if (token.Equals(mozilla::Tokenizer::Token::Char(',')) ||
|
|
token.Type() == mozilla::Tokenizer::TOKEN_WS) {
|
|
break;
|
|
}
|
|
|
|
if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
|
|
parsingIPv6 = true;
|
|
continue;
|
|
}
|
|
|
|
if (!parsingIPv6 && token.Equals(mozilla::Tokenizer::Token::Char(':'))) {
|
|
// Port is starting. Claim the previous as host.
|
|
if (parsingMask) {
|
|
t.Claim(maskStr);
|
|
} else {
|
|
t.Claim(hostStr);
|
|
}
|
|
t.Record();
|
|
parsingPort = true;
|
|
continue;
|
|
}
|
|
|
|
if (token.Equals(mozilla::Tokenizer::Token::Char('/'))) {
|
|
t.Claim(hostStr);
|
|
t.Record();
|
|
parsingMask = true;
|
|
continue;
|
|
}
|
|
|
|
if (token.Equals(mozilla::Tokenizer::Token::Char(']'))) {
|
|
parsingIPv6 = false;
|
|
continue;
|
|
}
|
|
}
|
|
if (!parsingPort && !parsingMask) {
|
|
t.Claim(hostStr);
|
|
} else if (parsingPort) {
|
|
t.Claim(portStr);
|
|
} else if (parsingMask) {
|
|
t.Claim(maskStr);
|
|
} else {
|
|
NS_WARNING("Could not parse this rule");
|
|
continue;
|
|
}
|
|
|
|
if (hostStr.IsEmpty()) {
|
|
continue;
|
|
}
|
|
|
|
// If the current host filter is "<local>", then all local (i.e.
|
|
// no dots in the hostname) hosts should bypass the proxy
|
|
if (hostStr.EqualsIgnoreCase("<local>")) {
|
|
mFilterLocalHosts = true;
|
|
LOG(
|
|
("loaded filter for local hosts "
|
|
"(plain host names, no dots)\n"));
|
|
// Continue to next host filter;
|
|
continue;
|
|
}
|
|
|
|
// For all other host filters, create HostInfo object and add to list
|
|
HostInfo* hinfo = new HostInfo();
|
|
nsresult rv = NS_OK;
|
|
|
|
int32_t port = portStr.ToInteger(&rv);
|
|
if (NS_FAILED(rv)) {
|
|
port = 0;
|
|
}
|
|
hinfo->port = port;
|
|
|
|
int32_t maskLen = maskStr.ToInteger(&rv);
|
|
if (NS_FAILED(rv)) {
|
|
maskLen = 128;
|
|
}
|
|
|
|
// PR_StringToNetAddr can't parse brackets enclosed IPv6
|
|
nsAutoCString addrString = hostStr;
|
|
if (hostStr.First() == '[' && hostStr.Last() == ']') {
|
|
addrString = Substring(hostStr, 1, hostStr.Length() - 2);
|
|
}
|
|
|
|
PRNetAddr addr;
|
|
if (PR_StringToNetAddr(addrString.get(), &addr) == PR_SUCCESS) {
|
|
hinfo->is_ipaddr = true;
|
|
hinfo->ip.family = PR_AF_INET6; // we always store address as IPv6
|
|
hinfo->ip.mask_len = maskLen;
|
|
|
|
if (hinfo->ip.mask_len == 0) {
|
|
NS_WARNING("invalid mask");
|
|
goto loser;
|
|
}
|
|
|
|
if (addr.raw.family == PR_AF_INET) {
|
|
// convert to IPv4-mapped address
|
|
PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
|
|
// adjust mask_len accordingly
|
|
if (hinfo->ip.mask_len <= 32) hinfo->ip.mask_len += 96;
|
|
} else if (addr.raw.family == PR_AF_INET6) {
|
|
// copy the address
|
|
memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
|
|
} else {
|
|
NS_WARNING("unknown address family");
|
|
goto loser;
|
|
}
|
|
|
|
// apply mask to IPv6 address
|
|
proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
|
|
} else {
|
|
nsAutoCString host;
|
|
if (hostStr.First() == '*') {
|
|
host = Substring(hostStr, 1);
|
|
} else {
|
|
host = hostStr;
|
|
}
|
|
|
|
if (host.IsEmpty()) {
|
|
hinfo->name.host = nullptr;
|
|
goto loser;
|
|
}
|
|
|
|
hinfo->name.host_len = host.Length();
|
|
|
|
hinfo->is_ipaddr = false;
|
|
hinfo->name.host = ToNewCString(host, mozilla::fallible);
|
|
|
|
if (!hinfo->name.host) goto loser;
|
|
}
|
|
|
|
// #define DEBUG_DUMP_FILTERS
|
|
#ifdef DEBUG_DUMP_FILTERS
|
|
printf("loaded filter[%zu]:\n", mHostFiltersArray.Length());
|
|
printf(" is_ipaddr = %u\n", hinfo->is_ipaddr);
|
|
printf(" port = %u\n", hinfo->port);
|
|
printf(" host = %s\n", hostStr.get());
|
|
if (hinfo->is_ipaddr) {
|
|
printf(" ip.family = %x\n", hinfo->ip.family);
|
|
printf(" ip.mask_len = %u\n", hinfo->ip.mask_len);
|
|
|
|
PRNetAddr netAddr;
|
|
PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
|
|
memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
|
|
|
|
char buf[256];
|
|
PR_NetAddrToString(&netAddr, buf, sizeof(buf));
|
|
|
|
printf(" ip.addr = %s\n", buf);
|
|
} else {
|
|
printf(" name.host = %s\n", hinfo->name.host);
|
|
}
|
|
#endif
|
|
|
|
mHostFiltersArray.AppendElement(hinfo);
|
|
hinfo = nullptr;
|
|
loser:
|
|
delete hinfo;
|
|
}
|
|
}
|
|
|
|
nsresult nsProtocolProxyService::GetProtocolInfo(nsIURI* uri,
|
|
nsProtocolInfo* info) {
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(uri, "URI is null");
|
|
MOZ_ASSERT(info, "info is null");
|
|
|
|
nsresult rv;
|
|
|
|
rv = uri->GetScheme(info->scheme);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = ios->GetDynamicProtocolFlags(uri, &info->flags);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = ios->GetDefaultPort(info->scheme.get(), &info->defaultPort);
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsProtocolProxyService::NewProxyInfo_Internal(
|
|
const char* aType, const nsACString& aHost, int32_t aPort,
|
|
const nsACString& aUsername, const nsACString& aPassword,
|
|
const nsACString& aProxyAuthorizationHeader,
|
|
const nsACString& aConnectionIsolationKey, uint32_t aFlags,
|
|
uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
|
|
uint32_t aResolveFlags, nsIProxyInfo** aResult) {
|
|
if (aPort <= 0) aPort = -1;
|
|
|
|
nsCOMPtr<nsProxyInfo> failover;
|
|
if (aFailoverProxy) {
|
|
failover = do_QueryInterface(aFailoverProxy);
|
|
NS_ENSURE_ARG(failover);
|
|
}
|
|
|
|
RefPtr<nsProxyInfo> proxyInfo = new nsProxyInfo();
|
|
|
|
proxyInfo->mType = aType;
|
|
proxyInfo->mHost = aHost;
|
|
proxyInfo->mPort = aPort;
|
|
proxyInfo->mUsername = aUsername;
|
|
proxyInfo->mPassword = aPassword;
|
|
proxyInfo->mFlags = aFlags;
|
|
proxyInfo->mResolveFlags = aResolveFlags;
|
|
proxyInfo->mTimeout =
|
|
aFailoverTimeout == UINT32_MAX ? mFailedProxyTimeout : aFailoverTimeout;
|
|
proxyInfo->mProxyAuthorizationHeader = aProxyAuthorizationHeader;
|
|
proxyInfo->mConnectionIsolationKey = aConnectionIsolationKey;
|
|
failover.swap(proxyInfo->mNext);
|
|
|
|
proxyInfo.forget(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
const char* nsProtocolProxyService::SOCKSProxyType() {
|
|
if (mSOCKSProxyVersion == nsIProxyInfo::SOCKS_V4) {
|
|
return kProxyType_SOCKS4;
|
|
}
|
|
return kProxyType_SOCKS;
|
|
}
|
|
|
|
bool nsProtocolProxyService::SOCKSRemoteDNS() {
|
|
return (mSOCKSProxyVersion == nsIProxyInfo::SOCKS_V4 &&
|
|
mSOCKS4ProxyRemoteDNS) ||
|
|
(mSOCKSProxyVersion == nsIProxyInfo::SOCKS_V5 &&
|
|
mSOCKS5ProxyRemoteDNS);
|
|
}
|
|
|
|
nsresult nsProtocolProxyService::Resolve_Internal(nsIChannel* channel,
|
|
const nsProtocolInfo& info,
|
|
uint32_t flags,
|
|
bool* usePACThread,
|
|
nsIProxyInfo** result) {
|
|
NS_ENSURE_ARG_POINTER(channel);
|
|
|
|
*usePACThread = false;
|
|
*result = nullptr;
|
|
|
|
if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY)) {
|
|
return NS_OK; // Can't proxy this (filters may not override)
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// See bug #586908.
|
|
// Avoid endless loop if |uri| is the current PAC-URI. Returning OK
|
|
// here means that we will not use a proxy for this connection.
|
|
if (mPACMan && mPACMan->IsPACURI(uri)) return NS_OK;
|
|
|
|
// if proxies are enabled and this host:port combo is supposed to use a
|
|
// proxy, check for a proxy.
|
|
if ((mProxyConfig == PROXYCONFIG_DIRECT) ||
|
|
!CanUseProxy(uri, info.defaultPort)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
bool mainThreadOnly;
|
|
if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM &&
|
|
NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
|
|
!mainThreadOnly) {
|
|
*usePACThread = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) {
|
|
// If the system proxy setting implementation is not threadsafe (e.g
|
|
// linux gconf), we'll do it inline here. Such implementations promise
|
|
// not to block
|
|
// bug 1366133: this block uses GetPACURI & GetProxyForURI, which may
|
|
// hang on Windows platform. Fortunately, current implementation on
|
|
// Windows is not main thread only, so we are safe here.
|
|
|
|
nsAutoCString PACURI;
|
|
nsAutoCString pacString;
|
|
|
|
if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
|
|
!PACURI.IsEmpty()) {
|
|
// There is a PAC URI configured. If it is unchanged, then
|
|
// just execute the PAC thread. If it is changed then load
|
|
// the new value
|
|
|
|
if (mPACMan && mPACMan->IsPACURI(PACURI)) {
|
|
// unchanged
|
|
*usePACThread = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
ConfigureFromPAC(PACURI, false);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsAutoCString spec;
|
|
nsAutoCString host;
|
|
nsAutoCString scheme;
|
|
int32_t port = -1;
|
|
|
|
uri->GetAsciiSpec(spec);
|
|
uri->GetAsciiHost(host);
|
|
uri->GetScheme(scheme);
|
|
uri->GetPort(&port);
|
|
|
|
if (flags & RESOLVE_PREFER_SOCKS_PROXY) {
|
|
LOG(("Ignoring RESOLVE_PREFER_SOCKS_PROXY for system proxy setting\n"));
|
|
} else if (flags & RESOLVE_PREFER_HTTPS_PROXY) {
|
|
scheme.AssignLiteral("https");
|
|
} else if (flags & RESOLVE_IGNORE_URI_SCHEME) {
|
|
scheme.AssignLiteral("http");
|
|
}
|
|
|
|
// now try the system proxy settings for this particular url
|
|
if (NS_SUCCEEDED(mSystemProxySettings->GetProxyForURI(spec, scheme, host,
|
|
port, pacString))) {
|
|
nsCOMPtr<nsIProxyInfo> pi;
|
|
ProcessPACString(pacString, 0, getter_AddRefs(pi));
|
|
|
|
if (flags & RESOLVE_PREFER_SOCKS_PROXY &&
|
|
flags & RESOLVE_PREFER_HTTPS_PROXY) {
|
|
nsAutoCString type;
|
|
pi->GetType(type);
|
|
// DIRECT from ProcessPACString indicates that system proxy settings
|
|
// are not configured to use SOCKS proxy. Try https proxy as a
|
|
// secondary preferrable proxy. This is mainly for websocket whose
|
|
// proxy precedence is SOCKS > HTTPS > DIRECT.
|
|
if (type.EqualsLiteral(kProxyType_DIRECT)) {
|
|
scheme.AssignLiteral(kProxyType_HTTPS);
|
|
if (NS_SUCCEEDED(mSystemProxySettings->GetProxyForURI(
|
|
spec, scheme, host, port, pacString))) {
|
|
ProcessPACString(pacString, 0, getter_AddRefs(pi));
|
|
}
|
|
}
|
|
}
|
|
pi.forget(result);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// if proxies are enabled and this host:port combo is supposed to use a
|
|
// proxy, check for a proxy.
|
|
if (mProxyConfig == PROXYCONFIG_DIRECT ||
|
|
(mProxyConfig == PROXYCONFIG_MANUAL &&
|
|
!CanUseProxy(uri, info.defaultPort))) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Proxy auto config magic...
|
|
if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD ||
|
|
StaticPrefs::network_proxy_system_wpad()) {
|
|
// Do not query PAC now.
|
|
*usePACThread = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
// If we aren't in manual proxy configuration mode then we don't
|
|
// want to honor any manual specific prefs that might be still set
|
|
if (mProxyConfig != PROXYCONFIG_MANUAL) return NS_OK;
|
|
|
|
// proxy info values for manual configuration mode
|
|
const char* type = nullptr;
|
|
const nsACString* host = nullptr;
|
|
int32_t port = -1;
|
|
|
|
uint32_t proxyFlags = 0;
|
|
|
|
if ((flags & RESOLVE_PREFER_SOCKS_PROXY) && !mSOCKSProxyTarget.IsEmpty() &&
|
|
(IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
|
|
host = &mSOCKSProxyTarget;
|
|
type = SOCKSProxyType();
|
|
if (SOCKSRemoteDNS()) {
|
|
proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
|
|
}
|
|
port = mSOCKSProxyPort;
|
|
} else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
|
|
!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
|
|
host = &mHTTPSProxyHost;
|
|
type = kProxyType_HTTP;
|
|
port = mHTTPSProxyPort;
|
|
} else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
|
|
((flags & RESOLVE_IGNORE_URI_SCHEME) ||
|
|
info.scheme.EqualsLiteral("http"))) {
|
|
host = &mHTTPProxyHost;
|
|
type = kProxyType_HTTP;
|
|
port = mHTTPProxyPort;
|
|
} else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
|
|
!(flags & RESOLVE_IGNORE_URI_SCHEME) &&
|
|
info.scheme.EqualsLiteral("https")) {
|
|
host = &mHTTPSProxyHost;
|
|
type = kProxyType_HTTP;
|
|
port = mHTTPSProxyPort;
|
|
} else if (!mSOCKSProxyTarget.IsEmpty() &&
|
|
(IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
|
|
host = &mSOCKSProxyTarget;
|
|
type = SOCKSProxyType();
|
|
if (SOCKSRemoteDNS()) {
|
|
proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
|
|
}
|
|
port = mSOCKSProxyPort;
|
|
}
|
|
|
|
if (type) {
|
|
rv = NewProxyInfo_Internal(type, *host, port, ""_ns, ""_ns, ""_ns, ""_ns,
|
|
proxyFlags, UINT32_MAX, nullptr, flags, result);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo* aProxy) {
|
|
// Disable Prefetch in the DNS service if a proxy is in use.
|
|
if (!aProxy) return;
|
|
|
|
nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
|
|
if (!pi || !pi->mType || pi->mType == kProxyType_DIRECT) return;
|
|
|
|
if (StaticPrefs::network_dns_prefetch_via_proxy()) {
|
|
return;
|
|
}
|
|
|
|
// To avoid getting DNS service recursively, we directly use
|
|
// GetXPCOMSingleton().
|
|
nsCOMPtr<nsIDNSService> dns = nsDNSService::GetXPCOMSingleton();
|
|
if (!dns) return;
|
|
nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
|
|
if (!pdns) return;
|
|
|
|
// We lose the prefetch optimization for the life of the dns service.
|
|
pdns->SetPrefetchEnabled(false);
|
|
}
|
|
|
|
void nsProtocolProxyService::CopyFilters(nsTArray<RefPtr<FilterLink>>& aCopy) {
|
|
MOZ_ASSERT(aCopy.Length() == 0);
|
|
aCopy.AppendElements(mFilters);
|
|
}
|
|
|
|
bool nsProtocolProxyService::ApplyFilter(
|
|
FilterLink const* filterLink, nsIChannel* channel,
|
|
const nsProtocolInfo& info, nsCOMPtr<nsIProxyInfo> list,
|
|
nsIProxyProtocolFilterResult* callback) {
|
|
nsresult rv;
|
|
|
|
// We prune the proxy list prior to invoking each filter. This may be
|
|
// somewhat inefficient, but it seems like a good idea since we want each
|
|
// filter to "see" a valid proxy list.
|
|
PruneProxyInfo(info, list);
|
|
|
|
if (filterLink->filter) {
|
|
nsCOMPtr<nsIURI> uri;
|
|
Unused << GetProxyURI(channel, getter_AddRefs(uri));
|
|
if (!uri) {
|
|
return false;
|
|
}
|
|
|
|
rv = filterLink->filter->ApplyFilter(uri, list, callback);
|
|
return NS_SUCCEEDED(rv);
|
|
}
|
|
|
|
if (filterLink->channelFilter) {
|
|
rv = filterLink->channelFilter->ApplyFilter(channel, list, callback);
|
|
return NS_SUCCEEDED(rv);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo& info,
|
|
nsIProxyInfo** list) {
|
|
if (!*list) return;
|
|
|
|
LOG(("nsProtocolProxyService::PruneProxyInfo ENTER list=%p", *list));
|
|
|
|
nsProxyInfo* head = nullptr;
|
|
CallQueryInterface(*list, &head);
|
|
if (!head) {
|
|
MOZ_ASSERT_UNREACHABLE("nsIProxyInfo must QI to nsProxyInfo");
|
|
return;
|
|
}
|
|
NS_RELEASE(*list);
|
|
|
|
// Pruning of disabled proxies works like this:
|
|
// - If all proxies are disabled, return the full list
|
|
// - Otherwise, remove the disabled proxies.
|
|
//
|
|
// Pruning of disallowed proxies works like this:
|
|
// - If the protocol handler disallows the proxy, then we disallow it.
|
|
|
|
// Start by removing all disallowed proxies if required:
|
|
if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
|
|
nsProxyInfo *last = nullptr, *iter = head;
|
|
while (iter) {
|
|
if ((iter->Type() == kProxyType_HTTP) ||
|
|
(iter->Type() == kProxyType_HTTPS)) {
|
|
// reject!
|
|
if (last) {
|
|
last->mNext = iter->mNext;
|
|
} else {
|
|
head = iter->mNext;
|
|
}
|
|
nsProxyInfo* next = iter->mNext;
|
|
iter->mNext = nullptr;
|
|
iter->Release();
|
|
iter = next;
|
|
} else {
|
|
last = iter;
|
|
iter = iter->mNext;
|
|
}
|
|
}
|
|
if (!head) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Scan to see if all remaining non-direct proxies are disabled. If so, then
|
|
// we'll just bail and return them all. Otherwise, we'll go and prune the
|
|
// disabled ones.
|
|
|
|
bool allNonDirectProxiesDisabled = true;
|
|
|
|
nsProxyInfo* iter;
|
|
for (iter = head; iter; iter = iter->mNext) {
|
|
if (!IsProxyDisabled(iter) && iter->mType != kProxyType_DIRECT) {
|
|
allNonDirectProxiesDisabled = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (allNonDirectProxiesDisabled &&
|
|
StaticPrefs::network_proxy_retry_failed_proxies()) {
|
|
LOG(("All proxies are disabled, so trying all again"));
|
|
} else {
|
|
// remove any disabled proxies.
|
|
nsProxyInfo* last = nullptr;
|
|
for (iter = head; iter;) {
|
|
if (IsProxyDisabled(iter)) {
|
|
// reject!
|
|
nsProxyInfo* reject = iter;
|
|
|
|
iter = iter->mNext;
|
|
if (last) {
|
|
last->mNext = iter;
|
|
} else {
|
|
head = iter;
|
|
}
|
|
|
|
reject->mNext = nullptr;
|
|
NS_RELEASE(reject);
|
|
continue;
|
|
}
|
|
|
|
// since we are about to use this proxy, make sure it is not on
|
|
// the disabled proxy list. we'll add it back to that list if
|
|
// we have to (in GetFailoverForProxy).
|
|
//
|
|
// XXX(darin): It might be better to do this as a final pass.
|
|
//
|
|
EnableProxy(iter);
|
|
|
|
last = iter;
|
|
iter = iter->mNext;
|
|
}
|
|
}
|
|
|
|
// if only DIRECT was specified then return no proxy info, and we're done.
|
|
if (head && !head->mNext && head->mType == kProxyType_DIRECT) {
|
|
NS_RELEASE(head);
|
|
}
|
|
|
|
*list = head; // Transfer ownership
|
|
|
|
LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list));
|
|
}
|
|
|
|
bool nsProtocolProxyService::GetIsPACLoading() {
|
|
return mPACMan && mPACMan->IsLoading();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::AddProxyConfigCallback(
|
|
nsIProxyConfigChangedCallback* aCallback) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (!aCallback) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
mProxyConfigChangedCallbacks.AppendElement(aCallback);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::RemoveProxyConfigCallback(
|
|
nsIProxyConfigChangedCallback* aCallback) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mProxyConfigChangedCallbacks.RemoveElement(aCallback);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsProtocolProxyService::NotifyProxyConfigChangedInternal() {
|
|
LOG(("nsProtocolProxyService::NotifyProxyConfigChangedInternal"));
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
for (const auto& callback : mProxyConfigChangedCallbacks) {
|
|
callback->OnProxyConfigChanged();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace mozilla
|