1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=4 sw=2 sts=2 et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/AutoRestore.h"
11 #include "nsProtocolProxyService.h"
12 #include "nsProxyInfo.h"
13 #include "nsIClassInfoImpl.h"
14 #include "nsIIOService.h"
15 #include "nsIObserverService.h"
16 #include "nsIProtocolHandler.h"
17 #include "nsIProtocolProxyCallback.h"
18 #include "nsIChannel.h"
19 #include "nsICancelable.h"
20 #include "nsIDNSService.h"
21 #include "nsPIDNSService.h"
22 #include "nsIPrefBranch.h"
23 #include "nsIPrefService.h"
24 #include "nsContentUtils.h"
26 #include "nsThreadUtils.h"
27 #include "nsQueryObject.h"
28 #include "nsSOCKSIOLayer.h"
30 #include "nsNetUtil.h"
35 #include "nsProxyRelease.h"
36 #include "mozilla/Mutex.h"
37 #include "mozilla/CondVar.h"
38 #include "nsISystemProxySettings.h"
39 #include "nsINetworkLinkService.h"
40 #include "nsIHttpChannelInternal.h"
41 #include "mozilla/dom/nsMixedContentBlocker.h"
42 #include "mozilla/Logging.h"
43 #include "mozilla/StaticPrefs_network.h"
44 #include "mozilla/Tokenizer.h"
45 #include "mozilla/Unused.h"
46 #include "mozilla/StaticPrefs_network.h"
48 //----------------------------------------------------------------------------
53 extern const char kProxyType_HTTP
[];
54 extern const char kProxyType_HTTPS
[];
55 extern const char kProxyType_SOCKS
[];
56 extern const char kProxyType_SOCKS4
[];
57 extern const char kProxyType_SOCKS5
[];
58 extern const char kProxyType_DIRECT
[];
61 #define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
63 //----------------------------------------------------------------------------
65 #define PROXY_PREF_BRANCH "network.proxy"
66 #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
68 //----------------------------------------------------------------------------
70 // This structure is intended to be allocated on the stack
71 struct nsProtocolInfo
{
74 int32_t defaultPort
= 0;
77 //----------------------------------------------------------------------------
79 // Return the channel's proxy URI, or if it doesn't exist, the
80 // channel's main URI.
81 static nsresult
GetProxyURI(nsIChannel
* channel
, nsIURI
** aOut
) {
83 nsCOMPtr
<nsIURI
> proxyURI
;
84 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel(do_QueryInterface(channel
));
86 rv
= httpChannel
->GetProxyURI(getter_AddRefs(proxyURI
));
89 rv
= channel
->GetURI(getter_AddRefs(proxyURI
));
94 proxyURI
.forget(aOut
);
98 //-----------------------------------------------------------------------------
100 nsProtocolProxyService::FilterLink::FilterLink(uint32_t p
,
101 nsIProtocolProxyFilter
* f
)
102 : position(p
), filter(f
), channelFilter(nullptr) {
103 LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, filter=%p", this,
106 nsProtocolProxyService::FilterLink::FilterLink(
107 uint32_t p
, nsIProtocolProxyChannelFilter
* cf
)
108 : position(p
), filter(nullptr), channelFilter(cf
) {
109 LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, channel-filter=%p",
113 nsProtocolProxyService::FilterLink::~FilterLink() {
114 LOG(("nsProtocolProxyService::FilterLink::~FilterLink %p", this));
117 //-----------------------------------------------------------------------------
119 // The nsPACManCallback portion of this implementation should be run
120 // on the main thread - so call nsPACMan::AsyncGetProxyForURI() with
121 // a true mainThreadResponse parameter.
122 class nsAsyncResolveRequest final
: public nsIRunnable
,
123 public nsPACManCallback
,
124 public nsICancelable
{
126 NS_DECL_THREADSAFE_ISUPPORTS
128 nsAsyncResolveRequest(nsProtocolProxyService
* pps
, nsIChannel
* channel
,
129 uint32_t aResolveFlags
,
130 nsIProtocolProxyCallback
* callback
)
131 : mResolveFlags(aResolveFlags
),
135 mCallback(callback
) {
136 NS_ASSERTION(mCallback
, "null callback");
140 ~nsAsyncResolveRequest() {
141 if (!NS_IsMainThread()) {
142 // these xpcom pointers might need to be proxied back to the
143 // main thread to delete safely, but if this request had its
144 // callbacks called normally they will all be null and this is a nop
147 NS_ReleaseOnMainThread("nsAsyncResolveRequest::mChannel",
152 NS_ReleaseOnMainThread("nsAsyncResolveRequest::mCallback",
157 NS_ReleaseOnMainThread("nsAsyncResolveRequest::mProxyInfo",
158 mProxyInfo
.forget());
162 NS_ReleaseOnMainThread("nsAsyncResolveRequest::mXPComPPS",
168 // Helper class to loop over all registered asynchronous filters.
169 // There is a cycle between nsAsyncResolveRequest and this class that
170 // is broken after the last filter has called back on this object.
171 class AsyncApplyFilters final
: public nsIProxyProtocolFilterResult
,
173 public nsICancelable
{
174 // The reference counter is thread-safe, but the processing logic is
175 // considered single thread only. We want the counter be thread safe,
176 // since this class can be released on a background thread.
177 NS_DECL_THREADSAFE_ISUPPORTS
178 NS_DECL_NSIPROXYPROTOCOLFILTERRESULT
180 NS_DECL_NSICANCELABLE
183 std::function
<nsresult(nsAsyncResolveRequest
*, nsIProxyInfo
*, bool)>;
185 explicit AsyncApplyFilters(nsProtocolInfo
& aInfo
,
186 Callback
const& aCallback
);
187 // This method starts the processing or filters. If all of them
188 // answer synchronously (call back from within applyFilters) this method
189 // will return immediately and the returning result will carry return
190 // result of the callback given in constructor.
191 // This method is looping the registered filters (that have been copied
192 // locally) as long as an answer from a filter is obtained synchronously.
193 // Note that filters are processed serially to let them build a list
195 nsresult
AsyncProcess(nsAsyncResolveRequest
* aRequest
);
198 using FilterLink
= nsProtocolProxyService::FilterLink
;
200 virtual ~AsyncApplyFilters();
201 // Processes the next filter and loops until a filter is successfully
202 // called on or it has called back to us.
203 nsresult
ProcessNextFilter();
204 // Called after the last filter has been processed (=called back or failed
208 nsProtocolInfo mInfo
;
209 // This is nullified before we call back on the request or when
210 // Cancel() on this object has been called to break the cycle
211 // and signal to stop.
212 RefPtr
<nsAsyncResolveRequest
> mRequest
;
214 // A shallow snapshot of filters as they were registered at the moment
215 // we started to process filters for the given resolve request.
216 nsTArray
<RefPtr
<FilterLink
>> mFiltersCopy
;
218 nsTArray
<RefPtr
<FilterLink
>>::index_type mNextFilterIndex
;
219 // true when we are calling ProcessNextFilter() from inside AsyncProcess(),
221 bool mProcessingInLoop
;
222 // true after a filter called back to us with a result, dropped to false
223 // just before we call a filter.
224 bool mFilterCalledBack
;
226 // This keeps the initial value we pass to the first filter in line and also
227 // collects the result from each filter call.
228 nsCOMPtr
<nsIProxyInfo
> mProxyInfo
;
230 // The logic is written as non-thread safe, assert single-thread usage.
231 nsCOMPtr
<nsISerialEventTarget
> mProcessingThread
;
234 void EnsureResolveFlagsMatch() {
235 nsCOMPtr
<nsProxyInfo
> pi
= do_QueryInterface(mProxyInfo
);
236 if (!pi
|| pi
->ResolveFlags() == mResolveFlags
) {
240 nsCOMPtr
<nsIProxyInfo
> proxyInfo
=
241 pi
->CloneProxyInfoWithNewResolveFlags(mResolveFlags
);
242 mProxyInfo
.swap(proxyInfo
);
246 nsresult
ProcessLocally(nsProtocolInfo
& info
, nsIProxyInfo
* pi
,
248 SetResult(NS_OK
, pi
);
250 auto consumeFiltersResult
= [isSyncOK
](nsAsyncResolveRequest
* ctx
,
252 bool aCalledAsync
) -> nsresult
{
253 ctx
->SetResult(NS_OK
, pi
);
254 if (isSyncOK
|| aCalledAsync
) {
259 return ctx
->DispatchCallback();
262 mAsyncFilterApplier
= new AsyncApplyFilters(info
, consumeFiltersResult
);
263 // may call consumeFiltersResult() directly
264 return mAsyncFilterApplier
->AsyncProcess(this);
267 void SetResult(nsresult status
, nsIProxyInfo
* pi
) {
272 NS_IMETHOD
Run() override
{
273 if (mCallback
) DoCallback();
277 NS_IMETHOD
Cancel(nsresult reason
) override
{
278 NS_ENSURE_ARG(NS_FAILED(reason
));
280 if (mAsyncFilterApplier
) {
281 mAsyncFilterApplier
->Cancel(reason
);
284 // If we've already called DoCallback then, nothing more to do.
285 if (!mCallback
) return NS_OK
;
287 SetResult(reason
, nullptr);
288 return DispatchCallback();
291 nsresult
DispatchCallback() {
292 if (mDispatched
) { // Only need to dispatch once
296 nsresult rv
= NS_DispatchToCurrentThread(this);
298 NS_WARNING("unable to dispatch callback event");
304 mCallback
= nullptr; // break possible reference cycle
309 // Called asynchronously, so we do not need to post another PLEvent
310 // before calling DoCallback.
311 void OnQueryComplete(nsresult status
, const nsACString
& pacString
,
312 const nsACString
& newPACURL
) override
{
313 // If we've already called DoCallback then, nothing more to do.
314 if (!mCallback
) return;
316 // Provided we haven't been canceled...
317 if (mStatus
== NS_OK
) {
319 mPACString
= pacString
;
323 // In the cancelation case, we may still have another PLEvent in
324 // the queue that wants to call DoCallback. No need to wait for
325 // it, just run the callback now.
330 bool pacAvailable
= true;
331 if (mStatus
== NS_ERROR_NOT_AVAILABLE
&& !mProxyInfo
) {
332 // If the PAC service is not avail (e.g. failed pac load
333 // or shutdown) then we will be going direct. Make that
334 // mapping now so that any filters are still applied.
335 mPACString
= "DIRECT;"_ns
;
338 LOG(("pac not available, use DIRECT\n"));
339 pacAvailable
= false;
342 // Generate proxy info from the PAC string if appropriate
343 if (NS_SUCCEEDED(mStatus
) && !mProxyInfo
&& !mPACString
.IsEmpty()) {
344 mPPS
->ProcessPACString(mPACString
, mResolveFlags
,
345 getter_AddRefs(mProxyInfo
));
346 nsCOMPtr
<nsIURI
> proxyURI
;
347 GetProxyURI(mChannel
, getter_AddRefs(proxyURI
));
349 // Now apply proxy filters
351 mStatus
= mPPS
->GetProtocolInfo(proxyURI
, &info
);
353 auto consumeFiltersResult
= [pacAvailable
](nsAsyncResolveRequest
* self
,
355 bool async
) -> nsresult
{
356 LOG(("DoCallback::consumeFiltersResult this=%p, pi=%p, async=%d", self
,
359 self
->mProxyInfo
= pi
;
362 // if !pacAvailable, it was already logged above
363 LOG(("pac thread callback %s\n", self
->mPACString
.get()));
366 if (NS_SUCCEEDED(self
->mStatus
)) {
367 self
->mPPS
->MaybeDisableDNSPrefetch(self
->mProxyInfo
);
370 self
->EnsureResolveFlagsMatch();
371 self
->mCallback
->OnProxyAvailable(self
, self
->mChannel
,
372 self
->mProxyInfo
, self
->mStatus
);
377 if (NS_SUCCEEDED(mStatus
)) {
378 mAsyncFilterApplier
= new AsyncApplyFilters(info
, consumeFiltersResult
);
379 // This may call consumeFiltersResult() directly.
380 mAsyncFilterApplier
->AsyncProcess(this);
384 consumeFiltersResult(this, nullptr, false);
385 } else if (NS_SUCCEEDED(mStatus
) && !mPACURL
.IsEmpty()) {
386 LOG(("pac thread callback indicates new pac file load\n"));
388 nsCOMPtr
<nsIURI
> proxyURI
;
389 GetProxyURI(mChannel
, getter_AddRefs(proxyURI
));
391 // trigger load of new pac url
392 nsresult rv
= mPPS
->ConfigureFromPAC(mPACURL
, false);
393 if (NS_SUCCEEDED(rv
)) {
394 // now that the load is triggered, we can resubmit the query
395 RefPtr
<nsAsyncResolveRequest
> newRequest
=
396 new nsAsyncResolveRequest(mPPS
, mChannel
, mResolveFlags
, mCallback
);
397 rv
= mPPS
->mPACMan
->AsyncGetProxyForURI(proxyURI
, newRequest
,
398 mResolveFlags
, true);
402 mCallback
->OnProxyAvailable(this, mChannel
, nullptr, rv
);
405 // do not call onproxyavailable() in SUCCESS case - the newRequest will
408 LOG(("pac thread callback did not provide information %" PRIX32
"\n",
409 static_cast<uint32_t>(mStatus
)));
410 if (NS_SUCCEEDED(mStatus
)) mPPS
->MaybeDisableDNSPrefetch(mProxyInfo
);
411 EnsureResolveFlagsMatch();
412 mCallback
->OnProxyAvailable(this, mChannel
, mProxyInfo
, mStatus
);
415 // We are on the main thread now and don't need these any more so
416 // release them to avoid having to proxy them back to the main thread
418 mCallback
= nullptr; // in case the callback holds an owning ref to us
422 mProxyInfo
= nullptr;
426 nsresult mStatus
{NS_OK
};
427 nsCString mPACString
;
429 bool mDispatched
{false};
430 uint32_t mResolveFlags
;
432 nsProtocolProxyService
* mPPS
;
433 nsCOMPtr
<nsIProtocolProxyService
> mXPComPPS
;
434 nsCOMPtr
<nsIChannel
> mChannel
;
435 nsCOMPtr
<nsIProtocolProxyCallback
> mCallback
;
436 nsCOMPtr
<nsIProxyInfo
> mProxyInfo
;
438 RefPtr
<AsyncApplyFilters
> mAsyncFilterApplier
;
441 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest
, nsICancelable
, nsIRunnable
)
443 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest::AsyncApplyFilters
,
444 nsIProxyProtocolFilterResult
, nsICancelable
, nsIRunnable
)
446 nsAsyncResolveRequest::AsyncApplyFilters::AsyncApplyFilters(
447 nsProtocolInfo
& aInfo
, Callback
const& aCallback
)
449 mCallback(aCallback
),
451 mProcessingInLoop(false),
452 mFilterCalledBack(false) {
453 LOG(("AsyncApplyFilters %p", this));
456 nsAsyncResolveRequest::AsyncApplyFilters::~AsyncApplyFilters() {
457 LOG(("~AsyncApplyFilters %p", this));
459 MOZ_ASSERT(!mRequest
);
460 MOZ_ASSERT(!mProxyInfo
);
461 MOZ_ASSERT(!mFiltersCopy
.Length());
464 nsresult
nsAsyncResolveRequest::AsyncApplyFilters::AsyncProcess(
465 nsAsyncResolveRequest
* aRequest
) {
466 LOG(("AsyncApplyFilters::AsyncProcess %p for req %p", this, aRequest
));
468 MOZ_ASSERT(!mRequest
, "AsyncApplyFilters started more than once!");
470 if (!(mInfo
.flags
& nsIProtocolHandler::ALLOWS_PROXY
)) {
471 // Calling the callback directly (not via Finish()) since we
472 // don't want to prune.
473 return mCallback(aRequest
, aRequest
->mProxyInfo
, false);
476 mProcessingThread
= NS_GetCurrentThread();
479 mProxyInfo
= aRequest
->mProxyInfo
;
481 aRequest
->mPPS
->CopyFilters(mFiltersCopy
);
483 // We want to give filters a chance to process in a single loop to prevent
484 // any current-thread dispatch delays when those are not needed.
485 // This code is rather "loopy" than "recursive" to prevent long stack traces.
487 MOZ_ASSERT(!mProcessingInLoop
);
489 mozilla::AutoRestore
<bool> restore(mProcessingInLoop
);
490 mProcessingInLoop
= true;
492 nsresult rv
= ProcessNextFilter();
496 } while (mFilterCalledBack
);
501 nsresult
nsAsyncResolveRequest::AsyncApplyFilters::ProcessNextFilter() {
502 LOG(("AsyncApplyFilters::ProcessNextFilter %p ENTER pi=%p", this,
505 RefPtr
<FilterLink
> filter
;
507 mFilterCalledBack
= false;
512 return NS_OK
; // should we let the consumer know?
515 if (mNextFilterIndex
== mFiltersCopy
.Length()) {
519 filter
= mFiltersCopy
[mNextFilterIndex
++];
521 // Loop until a call to a filter succeeded. Other option is to recurse
522 // but that would waste stack trace when a number of filters gets registered
523 // and all from some reason tend to fail.
524 // The !mFilterCalledBack part of the condition is there to protect us from
525 // calling on another filter when the current one managed to call back and
526 // then threw. We already have the result so take it and use it since
527 // the next filter will be processed by the root loop or a call to
528 // ProcessNextFilter has already been dispatched to this thread.
529 LOG((" calling filter %p pi=%p", filter
.get(), mProxyInfo
.get()));
530 } while (!mRequest
->mPPS
->ApplyFilter(filter
, mRequest
->mChannel
, mInfo
,
534 LOG(("AsyncApplyFilters::ProcessNextFilter %p LEAVE pi=%p", this,
540 nsAsyncResolveRequest::AsyncApplyFilters::OnProxyFilterResult(
541 nsIProxyInfo
* aProxyInfo
) {
542 LOG(("AsyncApplyFilters::OnProxyFilterResult %p pi=%p", this, aProxyInfo
));
544 MOZ_ASSERT(mProcessingThread
&& mProcessingThread
->IsOnCurrentThread());
545 MOZ_ASSERT(!mFilterCalledBack
);
547 if (mFilterCalledBack
) {
548 LOG((" duplicate notification?"));
552 mFilterCalledBack
= true;
560 mProxyInfo
= aProxyInfo
;
562 if (mProcessingInLoop
) {
563 // No need to call/dispatch ProcessNextFilter(), we are in a control
564 // loop that will do this for us and save recursion/dispatching.
565 LOG((" in a root loop"));
569 if (mNextFilterIndex
== mFiltersCopy
.Length()) {
570 // We are done, all filters have been called on!
575 // Redispatch, since we don't want long stacks when filters respond
577 LOG((" redispatching"));
578 NS_DispatchToCurrentThread(this);
583 nsAsyncResolveRequest::AsyncApplyFilters::Run() {
584 LOG(("AsyncApplyFilters::Run %p", this));
586 MOZ_ASSERT(mProcessingThread
&& mProcessingThread
->IsOnCurrentThread());
592 nsresult
nsAsyncResolveRequest::AsyncApplyFilters::Finish() {
593 LOG(("AsyncApplyFilters::Finish %p pi=%p", this, mProxyInfo
.get()));
595 MOZ_ASSERT(mRequest
);
597 mFiltersCopy
.Clear();
599 RefPtr
<nsAsyncResolveRequest
> request
;
600 request
.swap(mRequest
);
602 nsCOMPtr
<nsIProxyInfo
> pi
;
605 request
->mPPS
->PruneProxyInfo(mInfo
, pi
);
606 return mCallback(request
, pi
, !mProcessingInLoop
);
610 nsAsyncResolveRequest::AsyncApplyFilters::Cancel(nsresult reason
) {
611 LOG(("AsyncApplyFilters::Cancel %p", this));
613 MOZ_ASSERT(mProcessingThread
&& mProcessingThread
->IsOnCurrentThread());
615 // This will be called only from inside the request, so don't call
616 // its's callback. Dropping the members means we simply break the cycle.
617 mFiltersCopy
.Clear();
618 mProxyInfo
= nullptr;
624 // Bug 1366133: make GetPACURI off-main-thread since it may hang on Windows
626 class AsyncGetPACURIRequest final
: public nsIRunnable
{
628 NS_DECL_THREADSAFE_ISUPPORTS
630 using CallbackFunc
= nsresult (nsProtocolProxyService::*)(bool, bool,
634 AsyncGetPACURIRequest(nsProtocolProxyService
* aService
,
635 CallbackFunc aCallback
,
636 nsISystemProxySettings
* aSystemProxySettings
,
637 bool aMainThreadOnly
, bool aForceReload
,
638 bool aResetPACThread
)
639 : mIsMainThreadOnly(aMainThreadOnly
),
641 mServiceHolder(do_QueryObject(aService
)),
642 mCallback(aCallback
),
643 mSystemProxySettings(aSystemProxySettings
),
644 mForceReload(aForceReload
),
645 mResetPACThread(aResetPACThread
) {
646 MOZ_ASSERT(NS_IsMainThread());
647 Unused
<< mIsMainThreadOnly
;
650 NS_IMETHOD
Run() override
{
651 MOZ_ASSERT(NS_IsMainThread() == mIsMainThreadOnly
);
654 nsresult rv
= mSystemProxySettings
->GetPACURI(pacUri
);
656 nsCOMPtr
<nsIRunnable
> event
=
657 NewNonOwningCancelableRunnableMethod
<bool, bool, nsresult
, nsCString
>(
658 "AsyncGetPACURIRequestCallback", mService
, mCallback
, mForceReload
,
659 mResetPACThread
, rv
, pacUri
);
661 return NS_DispatchToMainThread(event
);
665 ~AsyncGetPACURIRequest() {
666 NS_ReleaseOnMainThread("AsyncGetPACURIRequest::mServiceHolder",
667 mServiceHolder
.forget());
670 bool mIsMainThreadOnly
;
672 nsProtocolProxyService
* mService
; // ref-count is hold by mServiceHolder
673 nsCOMPtr
<nsIProtocolProxyService2
> mServiceHolder
;
674 CallbackFunc mCallback
;
675 nsCOMPtr
<nsISystemProxySettings
> mSystemProxySettings
;
678 bool mResetPACThread
;
681 NS_IMPL_ISUPPORTS(AsyncGetPACURIRequest
, nsIRunnable
)
683 //----------------------------------------------------------------------------
686 // apply mask to address (zeros out excluded bits).
688 // NOTE: we do the byte swapping here to minimize overall swapping.
690 static void proxy_MaskIPv6Addr(PRIPv6Addr
& addr
, uint16_t mask_len
) {
691 if (mask_len
== 128) return;
694 addr
.pr_s6_addr32
[3] =
695 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[3]) & (~0uL << (128 - mask_len
)));
696 } else if (mask_len
> 64) {
697 addr
.pr_s6_addr32
[3] = 0;
698 addr
.pr_s6_addr32
[2] =
699 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[2]) & (~0uL << (96 - mask_len
)));
700 } else if (mask_len
> 32) {
701 addr
.pr_s6_addr32
[3] = 0;
702 addr
.pr_s6_addr32
[2] = 0;
703 addr
.pr_s6_addr32
[1] =
704 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[1]) & (~0uL << (64 - mask_len
)));
706 addr
.pr_s6_addr32
[3] = 0;
707 addr
.pr_s6_addr32
[2] = 0;
708 addr
.pr_s6_addr32
[1] = 0;
709 addr
.pr_s6_addr32
[0] =
710 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[0]) & (~0uL << (32 - mask_len
)));
714 static void proxy_GetStringPref(nsIPrefBranch
* aPrefBranch
, const char* aPref
,
715 nsCString
& aResult
) {
717 nsresult rv
= aPrefBranch
->GetCharPref(aPref
, temp
);
721 aResult
.Assign(temp
);
722 // all of our string prefs are hostnames, so we should remove any
723 // whitespace characters that the user might have unknowingly entered.
724 aResult
.StripWhitespace();
728 static void proxy_GetIntPref(nsIPrefBranch
* aPrefBranch
, const char* aPref
,
731 nsresult rv
= aPrefBranch
->GetIntPref(aPref
, &temp
);
739 static void proxy_GetBoolPref(nsIPrefBranch
* aPrefBranch
, const char* aPref
,
742 nsresult rv
= aPrefBranch
->GetBoolPref(aPref
, &temp
);
750 //----------------------------------------------------------------------------
752 static const int32_t PROXYCONFIG_DIRECT4X
= 3;
753 static const int32_t PROXYCONFIG_COUNT
= 6;
755 NS_IMPL_ADDREF(nsProtocolProxyService
)
756 NS_IMPL_RELEASE(nsProtocolProxyService
)
757 NS_IMPL_CLASSINFO(nsProtocolProxyService
, nullptr, nsIClassInfo::SINGLETON
,
758 NS_PROTOCOLPROXYSERVICE_CID
)
760 // NS_IMPL_QUERY_INTERFACE_CI with the nsProtocolProxyService QI change
761 NS_INTERFACE_MAP_BEGIN(nsProtocolProxyService
)
762 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService
)
763 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService2
)
764 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
765 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
766 NS_INTERFACE_MAP_ENTRY(nsINamed
)
767 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsProtocolProxyService
)
768 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIProtocolProxyService
)
769 NS_IMPL_QUERY_CLASSINFO(nsProtocolProxyService
)
772 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService
, nsIProtocolProxyService
,
773 nsIProtocolProxyService2
)
775 nsProtocolProxyService::nsProtocolProxyService() : mSessionStart(PR_Now()) {}
777 nsProtocolProxyService::~nsProtocolProxyService() {
778 // These should have been cleaned up in our Observe method.
779 NS_ASSERTION(mHostFiltersArray
.Length() == 0 && mFilters
.Length() == 0 &&
781 "what happened to xpcom-shutdown?");
784 // nsProtocolProxyService methods
785 nsresult
nsProtocolProxyService::Init() {
786 // failure to access prefs is non-fatal
787 nsCOMPtr
<nsIPrefBranch
> prefBranch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
789 // monitor proxy prefs
790 prefBranch
->AddObserver(PROXY_PREF_BRANCH
, this, false);
793 PrefsChanged(prefBranch
, nullptr);
796 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
798 // register for shutdown notification so we can clean ourselves up
800 obs
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
801 obs
->AddObserver(this, NS_NETWORK_LINK_TOPIC
, false);
807 // ReloadNetworkPAC() checks if there's a non-networked PAC in use then avoids
808 // to call ReloadPAC()
809 nsresult
nsProtocolProxyService::ReloadNetworkPAC() {
810 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
816 nsresult rv
= prefs
->GetIntPref(PROXY_PREF("type"), &type
);
821 if (type
== PROXYCONFIG_PAC
) {
822 nsAutoCString pacSpec
;
823 prefs
->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec
);
824 if (!pacSpec
.IsEmpty()) {
825 nsCOMPtr
<nsIURI
> pacURI
;
826 rv
= NS_NewURI(getter_AddRefs(pacURI
), pacSpec
);
827 if (!NS_SUCCEEDED(rv
)) {
832 rv
= GetProtocolInfo(pacURI
, &pac
);
833 if (!NS_SUCCEEDED(rv
)) {
837 if (!pac
.scheme
.EqualsLiteral("file") &&
838 !pac
.scheme
.EqualsLiteral("data")) {
839 LOG((": received network changed event, reload PAC"));
843 } else if ((type
== PROXYCONFIG_WPAD
) || (type
== PROXYCONFIG_SYSTEM
)) {
850 nsresult
nsProtocolProxyService::AsyncConfigureFromPAC(bool aForceReload
,
851 bool aResetPACThread
) {
852 MOZ_ASSERT(NS_IsMainThread());
855 nsresult rv
= mSystemProxySettings
->GetMainThreadOnly(&mainThreadOnly
);
856 if (NS_WARN_IF(NS_FAILED(rv
))) {
860 nsCOMPtr
<nsIRunnable
> req
= new AsyncGetPACURIRequest(
861 this, &nsProtocolProxyService::OnAsyncGetPACURI
, mSystemProxySettings
,
862 mainThreadOnly
, aForceReload
, aResetPACThread
);
864 if (mainThreadOnly
) {
868 return NS_DispatchBackgroundTask(req
.forget(),
869 nsIEventTarget::DISPATCH_NORMAL
);
872 nsresult
nsProtocolProxyService::OnAsyncGetPACURI(bool aForceReload
,
873 bool aResetPACThread
,
875 const nsACString
& aUri
) {
876 MOZ_ASSERT(NS_IsMainThread());
878 if (aResetPACThread
) {
882 if (NS_SUCCEEDED(aResult
) && !aUri
.IsEmpty()) {
883 ConfigureFromPAC(PromiseFlatCString(aUri
), aForceReload
);
890 nsProtocolProxyService::Observe(nsISupports
* aSubject
, const char* aTopic
,
891 const char16_t
* aData
) {
892 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
895 mHostFiltersArray
.Clear();
903 if (mReloadPACTimer
) {
904 mReloadPACTimer
->Cancel();
905 mReloadPACTimer
= nullptr;
908 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
910 obs
->RemoveObserver(this, NS_NETWORK_LINK_TOPIC
);
911 obs
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
914 } else if (strcmp(aTopic
, NS_NETWORK_LINK_TOPIC
) == 0) {
915 nsCString converted
= NS_ConvertUTF16toUTF8(aData
);
916 const char* state
= converted
.get();
917 if (!strcmp(state
, NS_NETWORK_LINK_DATA_CHANGED
)) {
918 uint32_t delay
= StaticPrefs::network_proxy_reload_pac_delay();
919 LOG(("nsProtocolProxyService::Observe call ReloadNetworkPAC() delay=%u",
923 if (mReloadPACTimer
) {
924 mReloadPACTimer
->Cancel();
925 mReloadPACTimer
= nullptr;
927 NS_NewTimerWithCallback(getter_AddRefs(mReloadPACTimer
), this, delay
,
928 nsITimer::TYPE_ONE_SHOT
);
934 NS_ASSERTION(strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
) == 0,
935 "what is this random observer event?");
936 nsCOMPtr
<nsIPrefBranch
> prefs
= do_QueryInterface(aSubject
);
937 if (prefs
) PrefsChanged(prefs
, NS_LossyConvertUTF16toASCII(aData
).get());
943 nsProtocolProxyService::Notify(nsITimer
* aTimer
) {
944 MOZ_ASSERT(aTimer
== mReloadPACTimer
);
950 nsProtocolProxyService::GetName(nsACString
& aName
) {
951 aName
.AssignLiteral("nsProtocolProxyService");
955 void nsProtocolProxyService::PrefsChanged(nsIPrefBranch
* prefBranch
,
958 bool reloadPAC
= false;
959 nsAutoCString tempString
;
961 if (!pref
|| !strcmp(pref
, PROXY_PREF("type"))) {
963 rv
= prefBranch
->GetIntPref(PROXY_PREF("type"), &type
);
964 if (NS_SUCCEEDED(rv
)) {
965 // bug 115720 - for ns4.x backwards compatibility
966 if (type
== PROXYCONFIG_DIRECT4X
) {
967 type
= PROXYCONFIG_DIRECT
;
968 // Reset the type so that the dialog looks correct, and we
969 // don't have to handle this case everywhere else
970 // I'm paranoid about a loop of some sort - only do this
971 // if we're enumerating all prefs, and ignore any error
972 if (!pref
) prefBranch
->SetIntPref(PROXY_PREF("type"), type
);
973 } else if (type
>= PROXYCONFIG_COUNT
) {
974 LOG(("unknown proxy type: %" PRId32
"; assuming direct\n", type
));
975 type
= PROXYCONFIG_DIRECT
;
981 if (mProxyConfig
== PROXYCONFIG_SYSTEM
) {
982 mSystemProxySettings
= do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID
);
983 if (!mSystemProxySettings
) mProxyConfig
= PROXYCONFIG_DIRECT
;
986 if (mSystemProxySettings
) {
987 mSystemProxySettings
= nullptr;
993 if (!pref
|| !strcmp(pref
, PROXY_PREF("http"))) {
994 proxy_GetStringPref(prefBranch
, PROXY_PREF("http"), mHTTPProxyHost
);
997 if (!pref
|| !strcmp(pref
, PROXY_PREF("http_port"))) {
998 proxy_GetIntPref(prefBranch
, PROXY_PREF("http_port"), mHTTPProxyPort
);
1001 if (!pref
|| !strcmp(pref
, PROXY_PREF("ssl"))) {
1002 proxy_GetStringPref(prefBranch
, PROXY_PREF("ssl"), mHTTPSProxyHost
);
1005 if (!pref
|| !strcmp(pref
, PROXY_PREF("ssl_port"))) {
1006 proxy_GetIntPref(prefBranch
, PROXY_PREF("ssl_port"), mHTTPSProxyPort
);
1009 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks"))) {
1010 proxy_GetStringPref(prefBranch
, PROXY_PREF("socks"), mSOCKSProxyTarget
);
1013 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_port"))) {
1014 proxy_GetIntPref(prefBranch
, PROXY_PREF("socks_port"), mSOCKSProxyPort
);
1017 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_version"))) {
1019 proxy_GetIntPref(prefBranch
, PROXY_PREF("socks_version"), version
);
1020 // make sure this preference value remains sane
1022 mSOCKSProxyVersion
= 5;
1024 mSOCKSProxyVersion
= 4;
1028 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_remote_dns"))) {
1029 proxy_GetBoolPref(prefBranch
, PROXY_PREF("socks_remote_dns"),
1030 mSOCKSProxyRemoteDNS
);
1033 if (!pref
|| !strcmp(pref
, PROXY_PREF("proxy_over_tls"))) {
1034 proxy_GetBoolPref(prefBranch
, PROXY_PREF("proxy_over_tls"), mProxyOverTLS
);
1037 if (!pref
|| !strcmp(pref
, PROXY_PREF("enable_wpad_over_dhcp"))) {
1038 proxy_GetBoolPref(prefBranch
, PROXY_PREF("enable_wpad_over_dhcp"),
1039 mWPADOverDHCPEnabled
);
1040 reloadPAC
= reloadPAC
|| mProxyConfig
== PROXYCONFIG_WPAD
;
1043 if (!pref
|| !strcmp(pref
, PROXY_PREF("failover_timeout"))) {
1044 proxy_GetIntPref(prefBranch
, PROXY_PREF("failover_timeout"),
1045 mFailedProxyTimeout
);
1048 if (!pref
|| !strcmp(pref
, PROXY_PREF("no_proxies_on"))) {
1049 rv
= prefBranch
->GetCharPref(PROXY_PREF("no_proxies_on"), tempString
);
1050 if (NS_SUCCEEDED(rv
)) LoadHostFilters(tempString
);
1053 // We're done if not using something that could give us a PAC URL
1054 // (PAC, WPAD or System)
1055 if (mProxyConfig
!= PROXYCONFIG_PAC
&& mProxyConfig
!= PROXYCONFIG_WPAD
&&
1056 mProxyConfig
!= PROXYCONFIG_SYSTEM
) {
1060 // OK, we need to reload the PAC file if:
1061 // 1) network.proxy.type changed, or
1062 // 2) network.proxy.autoconfig_url changed and PAC is configured
1064 if (!pref
|| !strcmp(pref
, PROXY_PREF("autoconfig_url"))) reloadPAC
= true;
1067 tempString
.Truncate();
1068 if (mProxyConfig
== PROXYCONFIG_PAC
) {
1069 prefBranch
->GetCharPref(PROXY_PREF("autoconfig_url"), tempString
);
1070 if (mPACMan
&& !mPACMan
->IsPACURI(tempString
)) {
1071 LOG(("PAC Thread URI Changed - Reset Pac Thread"));
1074 } else if (mProxyConfig
== PROXYCONFIG_WPAD
) {
1075 LOG(("Auto-detecting proxy - Reset Pac Thread"));
1077 } else if (mSystemProxySettings
) {
1078 // Get System Proxy settings if available
1079 AsyncConfigureFromPAC(false, false);
1081 if (!tempString
.IsEmpty() || mProxyConfig
== PROXYCONFIG_WPAD
) {
1082 ConfigureFromPAC(tempString
, false);
1087 bool nsProtocolProxyService::CanUseProxy(nsIURI
* aURI
, int32_t defaultPort
) {
1091 nsresult rv
= aURI
->GetAsciiHost(host
);
1092 if (NS_FAILED(rv
) || host
.IsEmpty()) return false;
1094 rv
= aURI
->GetPort(&port
);
1095 if (NS_FAILED(rv
)) return false;
1096 if (port
== -1) port
= defaultPort
;
1099 bool is_ipaddr
= (PR_StringToNetAddr(host
.get(), &addr
) == PR_SUCCESS
);
1103 // convert parsed address to IPv6
1104 if (addr
.raw
.family
== PR_AF_INET
) {
1105 // convert to IPv4-mapped address
1106 PR_ConvertIPv4AddrToIPv6(addr
.inet
.ip
, &ipv6
);
1107 } else if (addr
.raw
.family
== PR_AF_INET6
) {
1109 memcpy(&ipv6
, &addr
.ipv6
.ip
, sizeof(PRIPv6Addr
));
1111 NS_WARNING("unknown address family");
1112 return true; // allow proxying
1116 // Don't use proxy for local hosts (plain hostname, no dots)
1117 if ((!is_ipaddr
&& mFilterLocalHosts
&& !host
.Contains('.')) ||
1118 // This method detects if we have network.proxy.allow_hijacking_localhost
1119 // pref enabled. If it's true then this method will always return false
1120 // otherwise it returns true if the host matches an address that's
1121 // hardcoded to the loopback address.
1122 (!StaticPrefs::network_proxy_allow_hijacking_localhost() &&
1123 nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(host
))) {
1124 LOG(("Not using proxy for this local host [%s]!\n", host
.get()));
1125 return false; // don't allow proxying
1129 while (++index
< int32_t(mHostFiltersArray
.Length())) {
1130 const auto& hinfo
= mHostFiltersArray
[index
];
1132 if (is_ipaddr
!= hinfo
->is_ipaddr
) continue;
1133 if (hinfo
->port
&& hinfo
->port
!= port
) continue;
1136 // generate masked version of target IPv6 address
1138 memcpy(&masked
, &ipv6
, sizeof(PRIPv6Addr
));
1139 proxy_MaskIPv6Addr(masked
, hinfo
->ip
.mask_len
);
1141 // check for a match
1142 if (memcmp(&masked
, &hinfo
->ip
.addr
, sizeof(PRIPv6Addr
)) == 0) {
1143 return false; // proxy disallowed
1146 uint32_t host_len
= host
.Length();
1147 uint32_t filter_host_len
= hinfo
->name
.host_len
;
1149 if (host_len
>= filter_host_len
) {
1151 // compare last |filter_host_len| bytes of target hostname.
1153 const char* host_tail
= host
.get() + host_len
- filter_host_len
;
1154 if (!nsCRT::strncasecmp(host_tail
, hinfo
->name
.host
, filter_host_len
)) {
1155 // If the tail of the host string matches the filter
1157 if (filter_host_len
> 0 && hinfo
->name
.host
[0] == '.') {
1158 // If the filter was of the form .foo.bar.tld, all such
1159 // matches are correct
1160 return false; // proxy disallowed
1163 // abc-def.example.org should not match def.example.org
1164 // however, *.def.example.org should match .def.example.org
1165 // We check that the filter doesn't start with a `.`. If it does,
1166 // then the strncasecmp above should suffice. If it doesn't,
1167 // then we should only consider it a match if the strncasecmp happened
1168 // at a subdomain boundary
1169 if (host_len
> filter_host_len
&& *(host_tail
- 1) == '.') {
1170 // If the host was something.foo.bar.tld and the filter
1171 // was foo.bar.tld, it's still a match.
1172 // the character right before the tail must be a
1173 // `.` for this to work
1174 return false; // proxy disallowed
1177 if (host_len
== filter_host_len
) {
1178 // If the host and filter are of the same length,
1179 // they should match
1180 return false; // proxy disallowed
1189 // kProxyType\* may be referred to externally in
1190 // nsProxyInfo in order to compare by string pointer
1191 const char kProxyType_HTTP
[] = "http";
1192 const char kProxyType_HTTPS
[] = "https";
1193 const char kProxyType_PROXY
[] = "proxy";
1194 const char kProxyType_SOCKS
[] = "socks";
1195 const char kProxyType_SOCKS4
[] = "socks4";
1196 const char kProxyType_SOCKS5
[] = "socks5";
1197 const char kProxyType_DIRECT
[] = "direct";
1199 const char* nsProtocolProxyService::ExtractProxyInfo(const char* start
,
1200 uint32_t aResolveFlags
,
1201 nsProxyInfo
** result
) {
1205 // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
1207 // find end of proxy info delimiter
1208 const char* end
= start
;
1209 while (*end
&& *end
!= ';') ++end
;
1211 // find end of proxy type delimiter
1212 const char* sp
= start
;
1213 while (sp
< end
&& *sp
!= ' ' && *sp
!= '\t') ++sp
;
1215 uint32_t len
= sp
- start
;
1216 const char* type
= nullptr;
1219 if (nsCRT::strncasecmp(start
, kProxyType_HTTP
, 4) == 0) {
1220 type
= kProxyType_HTTP
;
1224 if (nsCRT::strncasecmp(start
, kProxyType_PROXY
, 5) == 0) {
1225 type
= kProxyType_HTTP
;
1226 } else if (nsCRT::strncasecmp(start
, kProxyType_SOCKS
, 5) == 0) {
1227 type
= kProxyType_SOCKS4
; // assume v4 for 4x compat
1228 if (StaticPrefs::network_proxy_default_pac_script_socks_version() ==
1230 type
= kProxyType_SOCKS
;
1232 } else if (nsCRT::strncasecmp(start
, kProxyType_HTTPS
, 5) == 0) {
1233 type
= kProxyType_HTTPS
;
1237 if (nsCRT::strncasecmp(start
, kProxyType_DIRECT
, 6) == 0) {
1238 type
= kProxyType_DIRECT
;
1239 } else if (nsCRT::strncasecmp(start
, kProxyType_SOCKS4
, 6) == 0) {
1240 type
= kProxyType_SOCKS4
;
1241 } else if (nsCRT::strncasecmp(start
, kProxyType_SOCKS5
, 6) == 0) {
1242 // map "SOCKS5" to "socks" to match contract-id of registered
1243 // SOCKS-v5 socket provider.
1244 type
= kProxyType_SOCKS
;
1251 // If it's a SOCKS5 proxy, do name resolution on the server side.
1252 // We could use this with SOCKS4a servers too, but they might not
1254 if (type
== kProxyType_SOCKS
|| mSOCKSProxyRemoteDNS
) {
1255 flags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
1258 // extract host:port
1260 while ((*start
== ' ' || *start
== '\t') && start
< end
) start
++;
1263 if (type
== kProxyType_HTTP
) {
1265 } else if (type
== kProxyType_HTTPS
) {
1271 RefPtr
<nsProxyInfo
> pi
= new nsProxyInfo();
1274 pi
->mResolveFlags
= aResolveFlags
;
1275 pi
->mTimeout
= mFailedProxyTimeout
;
1277 // www.foo.com:8080 and http://www.foo.com:8080
1278 nsDependentCSubstring
maybeURL(start
, end
- start
);
1279 nsCOMPtr
<nsIURI
> pacURI
;
1281 nsAutoCString urlHost
;
1282 // First assume the scheme is present, e.g. http://www.example.com:8080
1283 if (NS_FAILED(NS_NewURI(getter_AddRefs(pacURI
), maybeURL
)) ||
1284 NS_FAILED(pacURI
->GetAsciiHost(urlHost
)) || urlHost
.IsEmpty()) {
1285 // It isn't, assume www.example.com:8080
1286 maybeURL
.Insert("http://", 0);
1288 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI
), maybeURL
))) {
1289 pacURI
->GetAsciiHost(urlHost
);
1293 if (!urlHost
.IsEmpty()) {
1294 pi
->mHost
= urlHost
;
1297 if (NS_SUCCEEDED(pacURI
->GetPort(&tPort
)) && tPort
!= -1) {
1306 while (*end
== ';' || *end
== ' ' || *end
== '\t') ++end
;
1310 void nsProtocolProxyService::GetProxyKey(nsProxyInfo
* pi
, nsCString
& key
) {
1311 key
.AssignASCII(pi
->mType
);
1312 if (!pi
->mHost
.IsEmpty()) {
1314 key
.Append(pi
->mHost
);
1316 key
.AppendInt(pi
->mPort
);
1320 uint32_t nsProtocolProxyService::SecondsSinceSessionStart() {
1321 PRTime now
= PR_Now();
1323 // get time elapsed since session start
1324 int64_t diff
= now
- mSessionStart
;
1326 // convert microseconds to seconds
1327 diff
/= PR_USEC_PER_SEC
;
1329 // return converted 32 bit value
1330 return uint32_t(diff
);
1333 void nsProtocolProxyService::EnableProxy(nsProxyInfo
* pi
) {
1335 GetProxyKey(pi
, key
);
1336 mFailedProxies
.Remove(key
);
1339 void nsProtocolProxyService::DisableProxy(nsProxyInfo
* pi
) {
1341 GetProxyKey(pi
, key
);
1343 uint32_t dsec
= SecondsSinceSessionStart();
1345 // Add timeout to interval (this is the time when the proxy can
1347 dsec
+= pi
->mTimeout
;
1349 // NOTE: The classic codebase would increase the timeout value
1350 // incrementally each time a subsequent failure occurred.
1351 // We could do the same, but it would require that we not
1352 // remove proxy entries in IsProxyDisabled or otherwise
1353 // change the way we are recording disabled proxies.
1354 // Simpler is probably better for now, and at least the
1355 // user can tune the timeout setting via preferences.
1357 LOG(("DisableProxy %s %d\n", key
.get(), dsec
));
1359 // If this fails, oh well... means we don't have enough memory
1360 // to remember the failed proxy.
1361 mFailedProxies
.InsertOrUpdate(key
, dsec
);
1364 bool nsProtocolProxyService::IsProxyDisabled(nsProxyInfo
* pi
) {
1366 GetProxyKey(pi
, key
);
1369 if (!mFailedProxies
.Get(key
, &val
)) return false;
1371 uint32_t dsec
= SecondsSinceSessionStart();
1373 // if time passed has exceeded interval, then try proxy again.
1375 mFailedProxies
.Remove(key
);
1382 nsresult
nsProtocolProxyService::SetupPACThread(
1383 nsISerialEventTarget
* mainThreadEventTarget
) {
1385 return NS_ERROR_FAILURE
;
1388 if (mPACMan
) return NS_OK
;
1390 mPACMan
= new nsPACMan(mainThreadEventTarget
);
1392 bool mainThreadOnly
;
1394 if (mSystemProxySettings
&&
1395 NS_SUCCEEDED(mSystemProxySettings
->GetMainThreadOnly(&mainThreadOnly
)) &&
1397 rv
= mPACMan
->Init(mSystemProxySettings
);
1399 rv
= mPACMan
->Init(nullptr);
1401 if (NS_FAILED(rv
)) {
1402 mPACMan
->Shutdown();
1408 nsresult
nsProtocolProxyService::ResetPACThread() {
1409 if (!mPACMan
) return NS_OK
;
1411 mPACMan
->Shutdown();
1413 return SetupPACThread();
1416 nsresult
nsProtocolProxyService::ConfigureFromPAC(const nsCString
& spec
,
1418 nsresult rv
= SetupPACThread();
1419 NS_ENSURE_SUCCESS(rv
, rv
);
1421 bool autodetect
= spec
.IsEmpty();
1422 if (!forceReload
&& ((!autodetect
&& mPACMan
->IsPACURI(spec
)) ||
1423 (autodetect
&& mPACMan
->IsUsingWPAD()))) {
1427 mFailedProxies
.Clear();
1429 mPACMan
->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled
);
1430 return mPACMan
->LoadPACFromURI(spec
);
1433 void nsProtocolProxyService::ProcessPACString(const nsCString
& pacString
,
1434 uint32_t aResolveFlags
,
1435 nsIProxyInfo
** result
) {
1436 if (pacString
.IsEmpty()) {
1441 const char* proxies
= pacString
.get();
1443 nsProxyInfo
*pi
= nullptr, *first
= nullptr, *last
= nullptr;
1445 proxies
= ExtractProxyInfo(proxies
, aResolveFlags
, &pi
);
1446 if (pi
&& (pi
->mType
== kProxyType_HTTPS
) && !mProxyOverTLS
) {
1453 NS_ASSERTION(last
->mNext
== nullptr, "leaking nsProxyInfo");
1464 // nsIProtocolProxyService2
1466 nsProtocolProxyService::ReloadPAC() {
1467 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
1468 if (!prefs
) return NS_OK
;
1471 nsresult rv
= prefs
->GetIntPref(PROXY_PREF("type"), &type
);
1472 if (NS_FAILED(rv
)) return NS_OK
;
1474 nsAutoCString pacSpec
;
1475 if (type
== PROXYCONFIG_PAC
) {
1476 prefs
->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec
);
1477 } else if (type
== PROXYCONFIG_SYSTEM
) {
1478 if (mSystemProxySettings
) {
1479 AsyncConfigureFromPAC(true, true);
1485 if (!pacSpec
.IsEmpty() || type
== PROXYCONFIG_WPAD
) {
1486 ConfigureFromPAC(pacSpec
, true);
1491 // When sync interface is removed this can go away too
1492 // The nsPACManCallback portion of this implementation should be run
1493 // off the main thread, because it uses a condvar for signaling and
1494 // the main thread is blocking on that condvar -
1495 // so call nsPACMan::AsyncGetProxyForURI() with
1496 // a false mainThreadResponse parameter.
1497 class nsAsyncBridgeRequest final
: public nsPACManCallback
{
1498 NS_DECL_THREADSAFE_ISUPPORTS
1500 nsAsyncBridgeRequest()
1501 : mMutex("nsDeprecatedCallback"),
1502 mCondVar(mMutex
, "nsDeprecatedCallback") {}
1504 void OnQueryComplete(nsresult status
, const nsACString
& pacString
,
1505 const nsACString
& newPACURL
) override
{
1506 MutexAutoLock
lock(mMutex
);
1509 mPACString
= pacString
;
1510 mPACURL
= newPACURL
;
1514 void Lock() { mMutex
.Lock(); }
1515 void Unlock() { mMutex
.Unlock(); }
1516 void Wait() { mCondVar
.Wait(TimeDuration::FromSeconds(3)); }
1519 ~nsAsyncBridgeRequest() = default;
1521 friend class nsProtocolProxyService
;
1526 nsresult mStatus
{NS_OK
};
1527 nsCString mPACString
;
1529 bool mCompleted
{false};
1531 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest
)
1533 nsresult
nsProtocolProxyService::AsyncResolveInternal(
1534 nsIChannel
* channel
, uint32_t flags
, nsIProtocolProxyCallback
* callback
,
1535 nsICancelable
** result
, bool isSyncOK
,
1536 nsISerialEventTarget
* mainThreadEventTarget
) {
1537 NS_ENSURE_ARG_POINTER(channel
);
1538 NS_ENSURE_ARG_POINTER(callback
);
1540 nsCOMPtr
<nsIURI
> uri
;
1541 nsresult rv
= GetProxyURI(channel
, getter_AddRefs(uri
));
1542 if (NS_FAILED(rv
)) return rv
;
1545 RefPtr
<nsAsyncResolveRequest
> ctx
=
1546 new nsAsyncResolveRequest(this, channel
, flags
, callback
);
1548 nsProtocolInfo info
;
1549 rv
= GetProtocolInfo(uri
, &info
);
1550 if (NS_FAILED(rv
)) return rv
;
1552 nsCOMPtr
<nsIProxyInfo
> pi
;
1555 // adapt to realtime changes in the system proxy service
1556 if (mProxyConfig
== PROXYCONFIG_SYSTEM
) {
1557 nsCOMPtr
<nsISystemProxySettings
> sp2
=
1558 do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID
);
1559 if (sp2
!= mSystemProxySettings
) {
1560 mSystemProxySettings
= sp2
;
1565 rv
= SetupPACThread(mainThreadEventTarget
);
1566 if (NS_FAILED(rv
)) {
1570 // SystemProxySettings and PAC files can block the main thread
1571 // but if neither of them are in use, we can just do the work
1572 // right here and directly invoke the callback
1575 Resolve_Internal(channel
, info
, flags
, &usePACThread
, getter_AddRefs(pi
));
1576 if (NS_FAILED(rv
)) return rv
;
1578 if (!usePACThread
|| !mPACMan
) {
1579 // we can do it locally
1580 rv
= ctx
->ProcessLocally(info
, pi
, isSyncOK
);
1581 if (NS_SUCCEEDED(rv
) && !isSyncOK
) {
1587 // else kick off a PAC thread query
1588 rv
= mPACMan
->AsyncGetProxyForURI(uri
, ctx
, flags
, true);
1589 if (NS_SUCCEEDED(rv
)) ctx
.forget(result
);
1593 // nsIProtocolProxyService
1595 nsProtocolProxyService::AsyncResolve2(
1596 nsIChannel
* channel
, uint32_t flags
, nsIProtocolProxyCallback
* callback
,
1597 nsISerialEventTarget
* mainThreadEventTarget
, nsICancelable
** result
) {
1598 return AsyncResolveInternal(channel
, flags
, callback
, result
, true,
1599 mainThreadEventTarget
);
1603 nsProtocolProxyService::AsyncResolve(
1604 nsISupports
* channelOrURI
, uint32_t flags
,
1605 nsIProtocolProxyCallback
* callback
,
1606 nsISerialEventTarget
* mainThreadEventTarget
, nsICancelable
** result
) {
1608 // Check if we got a channel:
1609 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(channelOrURI
);
1611 nsCOMPtr
<nsIURI
> uri
= do_QueryInterface(channelOrURI
);
1613 return NS_ERROR_NO_INTERFACE
;
1616 // creating a temporary channel from the URI which is not
1617 // used to perform any network loads, hence its safe to
1618 // use systemPrincipal as the loadingPrincipal.
1619 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
,
1620 nsContentUtils::GetSystemPrincipal(),
1621 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
1622 nsIContentPolicy::TYPE_OTHER
);
1623 NS_ENSURE_SUCCESS(rv
, rv
);
1626 return AsyncResolveInternal(channel
, flags
, callback
, result
, false,
1627 mainThreadEventTarget
);
1631 nsProtocolProxyService::NewProxyInfo(
1632 const nsACString
& aType
, const nsACString
& aHost
, int32_t aPort
,
1633 const nsACString
& aProxyAuthorizationHeader
,
1634 const nsACString
& aConnectionIsolationKey
, uint32_t aFlags
,
1635 uint32_t aFailoverTimeout
, nsIProxyInfo
* aFailoverProxy
,
1636 nsIProxyInfo
** aResult
) {
1637 return NewProxyInfoWithAuth(aType
, aHost
, aPort
, ""_ns
, ""_ns
,
1638 aProxyAuthorizationHeader
,
1639 aConnectionIsolationKey
, aFlags
, aFailoverTimeout
,
1640 aFailoverProxy
, aResult
);
1644 nsProtocolProxyService::NewProxyInfoWithAuth(
1645 const nsACString
& aType
, const nsACString
& aHost
, int32_t aPort
,
1646 const nsACString
& aUsername
, const nsACString
& aPassword
,
1647 const nsACString
& aProxyAuthorizationHeader
,
1648 const nsACString
& aConnectionIsolationKey
, uint32_t aFlags
,
1649 uint32_t aFailoverTimeout
, nsIProxyInfo
* aFailoverProxy
,
1650 nsIProxyInfo
** aResult
) {
1651 static const char* types
[] = {kProxyType_HTTP
, kProxyType_HTTPS
,
1652 kProxyType_SOCKS
, kProxyType_SOCKS4
,
1655 // resolve type; this allows us to avoid copying the type string into each
1656 // proxy info instance. we just reference the string literals directly :)
1657 const char* type
= nullptr;
1658 for (auto& t
: types
) {
1659 if (aType
.LowerCaseEqualsASCII(t
)) {
1664 NS_ENSURE_TRUE(type
, NS_ERROR_INVALID_ARG
);
1666 // We have only implemented username/password for SOCKS proxies.
1667 if ((!aUsername
.IsEmpty() || !aPassword
.IsEmpty()) &&
1668 !aType
.LowerCaseEqualsASCII(kProxyType_SOCKS
) &&
1669 !aType
.LowerCaseEqualsASCII(kProxyType_SOCKS4
)) {
1670 return NS_ERROR_NOT_IMPLEMENTED
;
1673 return NewProxyInfo_Internal(type
, aHost
, aPort
, aUsername
, aPassword
,
1674 aProxyAuthorizationHeader
,
1675 aConnectionIsolationKey
, aFlags
,
1676 aFailoverTimeout
, aFailoverProxy
, 0, aResult
);
1680 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo
* aProxy
, nsIURI
* aURI
,
1682 nsIProxyInfo
** aResult
) {
1683 // Failover is supported through a variety of methods including:
1684 // * PAC scripts (PROXYCONFIG_PAC and PROXYCONFIG_WPAD)
1687 // With extensions the mProxyConfig can be any type and the extension
1688 // is still involved in the proxy filtering. It may have also supplied
1689 // any number of failover proxies. We cannot determine what the mix is
1690 // here, so we will attempt to get a failover regardless of the config
1691 // type. MANUAL configuration will not disable a proxy.
1693 // Verify that |aProxy| is one of our nsProxyInfo objects.
1694 nsCOMPtr
<nsProxyInfo
> pi
= do_QueryInterface(aProxy
);
1696 // OK, the QI checked out. We can proceed.
1698 // Remember that this proxy is down. If the user has manually configured some
1699 // proxies we do not want to disable them.
1700 if (mProxyConfig
!= PROXYCONFIG_MANUAL
) {
1704 // NOTE: At this point, we might want to prompt the user if we have
1705 // not already tried going DIRECT. This is something that the
1706 // classic codebase supported; however, IE6 does not prompt.
1708 if (!pi
->mNext
) return NS_ERROR_NOT_AVAILABLE
;
1710 LOG(("PAC failover from %s %s:%d to %s %s:%d\n", pi
->mType
, pi
->mHost
.get(),
1711 pi
->mPort
, pi
->mNext
->mType
, pi
->mNext
->mHost
.get(), pi
->mNext
->mPort
));
1713 *aResult
= do_AddRef(pi
->mNext
).take();
1719 class ProxyFilterPositionComparator
{
1720 using FilterLinkRef
= RefPtr
<nsProtocolProxyService::FilterLink
>;
1723 bool Equals(const FilterLinkRef
& a
, const FilterLinkRef
& b
) const {
1724 return a
->position
== b
->position
;
1726 bool LessThan(const FilterLinkRef
& a
, const FilterLinkRef
& b
) const {
1727 return a
->position
< b
->position
;
1731 class ProxyFilterObjectComparator
{
1732 using FilterLinkRef
= RefPtr
<nsProtocolProxyService::FilterLink
>;
1735 bool Equals(const FilterLinkRef
& link
, const nsISupports
* obj
) const {
1736 return obj
== nsCOMPtr
<nsISupports
>(do_QueryInterface(link
->filter
)) ||
1737 obj
== nsCOMPtr
<nsISupports
>(do_QueryInterface(link
->channelFilter
));
1743 nsresult
nsProtocolProxyService::InsertFilterLink(RefPtr
<FilterLink
>&& link
) {
1744 LOG(("nsProtocolProxyService::InsertFilterLink filter=%p", link
.get()));
1747 return NS_ERROR_FAILURE
;
1750 mFilters
.AppendElement(link
);
1751 mFilters
.Sort(ProxyFilterPositionComparator());
1756 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter
* filter
,
1757 uint32_t position
) {
1758 UnregisterFilter(filter
); // remove this filter if we already have it
1760 RefPtr
<FilterLink
> link
= new FilterLink(position
, filter
);
1761 return InsertFilterLink(std::move(link
));
1765 nsProtocolProxyService::RegisterChannelFilter(
1766 nsIProtocolProxyChannelFilter
* channelFilter
, uint32_t position
) {
1767 UnregisterChannelFilter(
1768 channelFilter
); // remove this filter if we already have it
1770 RefPtr
<FilterLink
> link
= new FilterLink(position
, channelFilter
);
1771 return InsertFilterLink(std::move(link
));
1774 nsresult
nsProtocolProxyService::RemoveFilterLink(nsISupports
* givenObject
) {
1775 LOG(("nsProtocolProxyService::RemoveFilterLink target=%p", givenObject
));
1777 return mFilters
.RemoveElement(givenObject
, ProxyFilterObjectComparator())
1779 : NS_ERROR_UNEXPECTED
;
1783 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter
* filter
) {
1784 // QI to nsISupports so we can safely test object identity.
1785 nsCOMPtr
<nsISupports
> givenObject
= do_QueryInterface(filter
);
1786 return RemoveFilterLink(givenObject
);
1790 nsProtocolProxyService::UnregisterChannelFilter(
1791 nsIProtocolProxyChannelFilter
* channelFilter
) {
1792 // QI to nsISupports so we can safely test object identity.
1793 nsCOMPtr
<nsISupports
> givenObject
= do_QueryInterface(channelFilter
);
1794 return RemoveFilterLink(givenObject
);
1798 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType
) {
1799 *aProxyConfigType
= mProxyConfig
;
1803 void nsProtocolProxyService::LoadHostFilters(const nsACString
& aFilters
) {
1808 // check to see the owners flag? /!?/ TODO
1809 if (mHostFiltersArray
.Length() > 0) {
1810 mHostFiltersArray
.Clear();
1813 // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref
1815 mFilterLocalHosts
= false;
1817 if (aFilters
.IsEmpty()) {
1822 // filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
1823 // filters = filter *( "," LWS filter)
1825 mozilla::Tokenizer
t(aFilters
);
1826 mozilla::Tokenizer::Token token
;
1828 // while (*filters) {
1830 // skip over spaces and ,
1832 while (t
.CheckChar(',')) {
1836 nsAutoCString portStr
;
1837 nsAutoCString hostStr
;
1838 nsAutoCString maskStr
;
1841 bool parsingIPv6
= false;
1842 bool parsingPort
= false;
1843 bool parsingMask
= false;
1844 while (t
.Next(token
)) {
1845 if (token
.Equals(mozilla::Tokenizer::Token::EndOfFile())) {
1849 if (token
.Equals(mozilla::Tokenizer::Token::Char(',')) ||
1850 token
.Type() == mozilla::Tokenizer::TOKEN_WS
) {
1854 if (token
.Equals(mozilla::Tokenizer::Token::Char('['))) {
1859 if (!parsingIPv6
&& token
.Equals(mozilla::Tokenizer::Token::Char(':'))) {
1860 // Port is starting. Claim the previous as host.
1871 if (token
.Equals(mozilla::Tokenizer::Token::Char('/'))) {
1878 if (token
.Equals(mozilla::Tokenizer::Token::Char(']'))) {
1879 parsingIPv6
= false;
1883 if (!parsingPort
&& !parsingMask
) {
1885 } else if (parsingPort
) {
1887 } else if (parsingMask
) {
1890 NS_WARNING("Could not parse this rule");
1894 if (hostStr
.IsEmpty()) {
1898 // If the current host filter is "<local>", then all local (i.e.
1899 // no dots in the hostname) hosts should bypass the proxy
1900 if (hostStr
.EqualsIgnoreCase("<local>")) {
1901 mFilterLocalHosts
= true;
1903 ("loaded filter for local hosts "
1904 "(plain host names, no dots)\n"));
1905 // Continue to next host filter;
1909 // For all other host filters, create HostInfo object and add to list
1910 HostInfo
* hinfo
= new HostInfo();
1911 nsresult rv
= NS_OK
;
1913 int32_t port
= portStr
.ToInteger(&rv
);
1914 if (NS_FAILED(rv
)) {
1919 int32_t maskLen
= maskStr
.ToInteger(&rv
);
1920 if (NS_FAILED(rv
)) {
1924 // PR_StringToNetAddr can't parse brackets enclosed IPv6
1925 nsAutoCString addrString
= hostStr
;
1926 if (hostStr
.First() == '[' && hostStr
.Last() == ']') {
1927 addrString
= Substring(hostStr
, 1, hostStr
.Length() - 2);
1931 if (PR_StringToNetAddr(addrString
.get(), &addr
) == PR_SUCCESS
) {
1932 hinfo
->is_ipaddr
= true;
1933 hinfo
->ip
.family
= PR_AF_INET6
; // we always store address as IPv6
1934 hinfo
->ip
.mask_len
= maskLen
;
1936 if (hinfo
->ip
.mask_len
== 0) {
1937 NS_WARNING("invalid mask");
1941 if (addr
.raw
.family
== PR_AF_INET
) {
1942 // convert to IPv4-mapped address
1943 PR_ConvertIPv4AddrToIPv6(addr
.inet
.ip
, &hinfo
->ip
.addr
);
1944 // adjust mask_len accordingly
1945 if (hinfo
->ip
.mask_len
<= 32) hinfo
->ip
.mask_len
+= 96;
1946 } else if (addr
.raw
.family
== PR_AF_INET6
) {
1948 memcpy(&hinfo
->ip
.addr
, &addr
.ipv6
.ip
, sizeof(PRIPv6Addr
));
1950 NS_WARNING("unknown address family");
1954 // apply mask to IPv6 address
1955 proxy_MaskIPv6Addr(hinfo
->ip
.addr
, hinfo
->ip
.mask_len
);
1958 if (hostStr
.First() == '*') {
1959 host
= Substring(hostStr
, 1);
1964 if (host
.IsEmpty()) {
1965 hinfo
->name
.host
= nullptr;
1969 hinfo
->name
.host_len
= host
.Length();
1971 hinfo
->is_ipaddr
= false;
1972 hinfo
->name
.host
= ToNewCString(host
, mozilla::fallible
);
1974 if (!hinfo
->name
.host
) goto loser
;
1977 //#define DEBUG_DUMP_FILTERS
1978 #ifdef DEBUG_DUMP_FILTERS
1979 printf("loaded filter[%zu]:\n", mHostFiltersArray
.Length());
1980 printf(" is_ipaddr = %u\n", hinfo
->is_ipaddr
);
1981 printf(" port = %u\n", hinfo
->port
);
1982 printf(" host = %s\n", hostStr
.get());
1983 if (hinfo
->is_ipaddr
) {
1984 printf(" ip.family = %x\n", hinfo
->ip
.family
);
1985 printf(" ip.mask_len = %u\n", hinfo
->ip
.mask_len
);
1988 PR_SetNetAddr(PR_IpAddrNull
, PR_AF_INET6
, 0, &netAddr
);
1989 memcpy(&netAddr
.ipv6
.ip
, &hinfo
->ip
.addr
, sizeof(hinfo
->ip
.addr
));
1992 PR_NetAddrToString(&netAddr
, buf
, sizeof(buf
));
1994 printf(" ip.addr = %s\n", buf
);
1996 printf(" name.host = %s\n", hinfo
->name
.host
);
2000 mHostFiltersArray
.AppendElement(hinfo
);
2007 nsresult
nsProtocolProxyService::GetProtocolInfo(nsIURI
* uri
,
2008 nsProtocolInfo
* info
) {
2009 MOZ_ASSERT(uri
, "URI is null");
2010 MOZ_ASSERT(info
, "info is null");
2014 rv
= uri
->GetScheme(info
->scheme
);
2015 if (NS_FAILED(rv
)) return rv
;
2017 nsCOMPtr
<nsIIOService
> ios
= do_GetIOService(&rv
);
2018 if (NS_FAILED(rv
)) return rv
;
2020 nsCOMPtr
<nsIProtocolHandler
> handler
;
2021 rv
= ios
->GetProtocolHandler(info
->scheme
.get(), getter_AddRefs(handler
));
2022 if (NS_FAILED(rv
)) return rv
;
2024 rv
= handler
->DoGetProtocolFlags(uri
, &info
->flags
);
2025 if (NS_FAILED(rv
)) return rv
;
2027 rv
= handler
->GetDefaultPort(&info
->defaultPort
);
2031 nsresult
nsProtocolProxyService::NewProxyInfo_Internal(
2032 const char* aType
, const nsACString
& aHost
, int32_t aPort
,
2033 const nsACString
& aUsername
, const nsACString
& aPassword
,
2034 const nsACString
& aProxyAuthorizationHeader
,
2035 const nsACString
& aConnectionIsolationKey
, uint32_t aFlags
,
2036 uint32_t aFailoverTimeout
, nsIProxyInfo
* aFailoverProxy
,
2037 uint32_t aResolveFlags
, nsIProxyInfo
** aResult
) {
2038 if (aPort
<= 0) aPort
= -1;
2040 nsCOMPtr
<nsProxyInfo
> failover
;
2041 if (aFailoverProxy
) {
2042 failover
= do_QueryInterface(aFailoverProxy
);
2043 NS_ENSURE_ARG(failover
);
2046 RefPtr
<nsProxyInfo
> proxyInfo
= new nsProxyInfo();
2048 proxyInfo
->mType
= aType
;
2049 proxyInfo
->mHost
= aHost
;
2050 proxyInfo
->mPort
= aPort
;
2051 proxyInfo
->mUsername
= aUsername
;
2052 proxyInfo
->mPassword
= aPassword
;
2053 proxyInfo
->mFlags
= aFlags
;
2054 proxyInfo
->mResolveFlags
= aResolveFlags
;
2055 proxyInfo
->mTimeout
=
2056 aFailoverTimeout
== UINT32_MAX
? mFailedProxyTimeout
: aFailoverTimeout
;
2057 proxyInfo
->mProxyAuthorizationHeader
= aProxyAuthorizationHeader
;
2058 proxyInfo
->mConnectionIsolationKey
= aConnectionIsolationKey
;
2059 failover
.swap(proxyInfo
->mNext
);
2061 proxyInfo
.forget(aResult
);
2065 nsresult
nsProtocolProxyService::Resolve_Internal(nsIChannel
* channel
,
2066 const nsProtocolInfo
& info
,
2069 nsIProxyInfo
** result
) {
2070 NS_ENSURE_ARG_POINTER(channel
);
2072 *usePACThread
= false;
2075 if (!(info
.flags
& nsIProtocolHandler::ALLOWS_PROXY
)) {
2076 return NS_OK
; // Can't proxy this (filters may not override)
2079 nsCOMPtr
<nsIURI
> uri
;
2080 nsresult rv
= GetProxyURI(channel
, getter_AddRefs(uri
));
2081 if (NS_FAILED(rv
)) return rv
;
2084 // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
2085 // here means that we will not use a proxy for this connection.
2086 if (mPACMan
&& mPACMan
->IsPACURI(uri
)) return NS_OK
;
2088 // if proxies are enabled and this host:port combo is supposed to use a
2089 // proxy, check for a proxy.
2090 if ((mProxyConfig
== PROXYCONFIG_DIRECT
) ||
2091 !CanUseProxy(uri
, info
.defaultPort
)) {
2095 bool mainThreadOnly
;
2096 if (mSystemProxySettings
&& mProxyConfig
== PROXYCONFIG_SYSTEM
&&
2097 NS_SUCCEEDED(mSystemProxySettings
->GetMainThreadOnly(&mainThreadOnly
)) &&
2099 *usePACThread
= true;
2103 if (mSystemProxySettings
&& mProxyConfig
== PROXYCONFIG_SYSTEM
) {
2104 // If the system proxy setting implementation is not threadsafe (e.g
2105 // linux gconf), we'll do it inline here. Such implementations promise
2107 // bug 1366133: this block uses GetPACURI & GetProxyForURI, which may
2108 // hang on Windows platform. Fortunately, current implementation on
2109 // Windows is not main thread only, so we are safe here.
2111 nsAutoCString PACURI
;
2112 nsAutoCString pacString
;
2114 if (NS_SUCCEEDED(mSystemProxySettings
->GetPACURI(PACURI
)) &&
2115 !PACURI
.IsEmpty()) {
2116 // There is a PAC URI configured. If it is unchanged, then
2117 // just execute the PAC thread. If it is changed then load
2120 if (mPACMan
&& mPACMan
->IsPACURI(PACURI
)) {
2122 *usePACThread
= true;
2126 ConfigureFromPAC(PACURI
, false);
2132 nsAutoCString scheme
;
2135 uri
->GetAsciiSpec(spec
);
2136 uri
->GetAsciiHost(host
);
2137 uri
->GetScheme(scheme
);
2138 uri
->GetPort(&port
);
2140 if (flags
& RESOLVE_PREFER_SOCKS_PROXY
) {
2141 LOG(("Ignoring RESOLVE_PREFER_SOCKS_PROXY for system proxy setting\n"));
2142 } else if (flags
& RESOLVE_PREFER_HTTPS_PROXY
) {
2143 scheme
.AssignLiteral("https");
2144 } else if (flags
& RESOLVE_IGNORE_URI_SCHEME
) {
2145 scheme
.AssignLiteral("http");
2148 // now try the system proxy settings for this particular url
2149 if (NS_SUCCEEDED(mSystemProxySettings
->GetProxyForURI(spec
, scheme
, host
,
2150 port
, pacString
))) {
2151 nsCOMPtr
<nsIProxyInfo
> pi
;
2152 ProcessPACString(pacString
, 0, getter_AddRefs(pi
));
2154 if (flags
& RESOLVE_PREFER_SOCKS_PROXY
&&
2155 flags
& RESOLVE_PREFER_HTTPS_PROXY
) {
2158 // DIRECT from ProcessPACString indicates that system proxy settings
2159 // are not configured to use SOCKS proxy. Try https proxy as a
2160 // secondary preferrable proxy. This is mainly for websocket whose
2161 // proxy precedence is SOCKS > HTTPS > DIRECT.
2162 if (type
.EqualsLiteral(kProxyType_DIRECT
)) {
2163 scheme
.AssignLiteral(kProxyType_HTTPS
);
2164 if (NS_SUCCEEDED(mSystemProxySettings
->GetProxyForURI(
2165 spec
, scheme
, host
, port
, pacString
))) {
2166 ProcessPACString(pacString
, 0, getter_AddRefs(pi
));
2175 // if proxies are enabled and this host:port combo is supposed to use a
2176 // proxy, check for a proxy.
2177 if (mProxyConfig
== PROXYCONFIG_DIRECT
||
2178 (mProxyConfig
== PROXYCONFIG_MANUAL
&&
2179 !CanUseProxy(uri
, info
.defaultPort
))) {
2183 // Proxy auto config magic...
2184 if (mProxyConfig
== PROXYCONFIG_PAC
|| mProxyConfig
== PROXYCONFIG_WPAD
) {
2185 // Do not query PAC now.
2186 *usePACThread
= true;
2190 // If we aren't in manual proxy configuration mode then we don't
2191 // want to honor any manual specific prefs that might be still set
2192 if (mProxyConfig
!= PROXYCONFIG_MANUAL
) return NS_OK
;
2194 // proxy info values for manual configuration mode
2195 const char* type
= nullptr;
2196 const nsACString
* host
= nullptr;
2199 uint32_t proxyFlags
= 0;
2201 if ((flags
& RESOLVE_PREFER_SOCKS_PROXY
) && !mSOCKSProxyTarget
.IsEmpty() &&
2202 (IsHostLocalTarget(mSOCKSProxyTarget
) || mSOCKSProxyPort
> 0)) {
2203 host
= &mSOCKSProxyTarget
;
2204 if (mSOCKSProxyVersion
== 4) {
2205 type
= kProxyType_SOCKS4
;
2207 type
= kProxyType_SOCKS
;
2209 port
= mSOCKSProxyPort
;
2210 if (mSOCKSProxyRemoteDNS
) {
2211 proxyFlags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
2213 } else if ((flags
& RESOLVE_PREFER_HTTPS_PROXY
) &&
2214 !mHTTPSProxyHost
.IsEmpty() && mHTTPSProxyPort
> 0) {
2215 host
= &mHTTPSProxyHost
;
2216 type
= kProxyType_HTTP
;
2217 port
= mHTTPSProxyPort
;
2218 } else if (!mHTTPProxyHost
.IsEmpty() && mHTTPProxyPort
> 0 &&
2219 ((flags
& RESOLVE_IGNORE_URI_SCHEME
) ||
2220 info
.scheme
.EqualsLiteral("http"))) {
2221 host
= &mHTTPProxyHost
;
2222 type
= kProxyType_HTTP
;
2223 port
= mHTTPProxyPort
;
2224 } else if (!mHTTPSProxyHost
.IsEmpty() && mHTTPSProxyPort
> 0 &&
2225 !(flags
& RESOLVE_IGNORE_URI_SCHEME
) &&
2226 info
.scheme
.EqualsLiteral("https")) {
2227 host
= &mHTTPSProxyHost
;
2228 type
= kProxyType_HTTP
;
2229 port
= mHTTPSProxyPort
;
2230 } else if (!mSOCKSProxyTarget
.IsEmpty() &&
2231 (IsHostLocalTarget(mSOCKSProxyTarget
) || mSOCKSProxyPort
> 0)) {
2232 host
= &mSOCKSProxyTarget
;
2233 if (mSOCKSProxyVersion
== 4) {
2234 type
= kProxyType_SOCKS4
;
2236 type
= kProxyType_SOCKS
;
2238 port
= mSOCKSProxyPort
;
2239 if (mSOCKSProxyRemoteDNS
) {
2240 proxyFlags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
2245 rv
= NewProxyInfo_Internal(type
, *host
, port
, ""_ns
, ""_ns
, ""_ns
, ""_ns
,
2246 proxyFlags
, UINT32_MAX
, nullptr, flags
, result
);
2247 if (NS_FAILED(rv
)) return rv
;
2253 void nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo
* aProxy
) {
2254 // Disable Prefetch in the DNS service if a proxy is in use.
2255 if (!aProxy
) return;
2257 nsCOMPtr
<nsProxyInfo
> pi
= do_QueryInterface(aProxy
);
2258 if (!pi
|| !pi
->mType
|| pi
->mType
== kProxyType_DIRECT
) return;
2260 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
);
2262 nsCOMPtr
<nsPIDNSService
> pdns
= do_QueryInterface(dns
);
2265 // We lose the prefetch optimization for the life of the dns service.
2266 pdns
->SetPrefetchEnabled(false);
2269 void nsProtocolProxyService::CopyFilters(nsTArray
<RefPtr
<FilterLink
>>& aCopy
) {
2270 MOZ_ASSERT(aCopy
.Length() == 0);
2271 aCopy
.AppendElements(mFilters
);
2274 bool nsProtocolProxyService::ApplyFilter(
2275 FilterLink
const* filterLink
, nsIChannel
* channel
,
2276 const nsProtocolInfo
& info
, nsCOMPtr
<nsIProxyInfo
> list
,
2277 nsIProxyProtocolFilterResult
* callback
) {
2280 // We prune the proxy list prior to invoking each filter. This may be
2281 // somewhat inefficient, but it seems like a good idea since we want each
2282 // filter to "see" a valid proxy list.
2283 PruneProxyInfo(info
, list
);
2285 if (filterLink
->filter
) {
2286 nsCOMPtr
<nsIURI
> uri
;
2287 Unused
<< GetProxyURI(channel
, getter_AddRefs(uri
));
2292 rv
= filterLink
->filter
->ApplyFilter(uri
, list
, callback
);
2293 return NS_SUCCEEDED(rv
);
2296 if (filterLink
->channelFilter
) {
2297 rv
= filterLink
->channelFilter
->ApplyFilter(channel
, list
, callback
);
2298 return NS_SUCCEEDED(rv
);
2304 void nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo
& info
,
2305 nsIProxyInfo
** list
) {
2308 LOG(("nsProtocolProxyService::PruneProxyInfo ENTER list=%p", *list
));
2310 nsProxyInfo
* head
= nullptr;
2311 CallQueryInterface(*list
, &head
);
2313 MOZ_ASSERT_UNREACHABLE("nsIProxyInfo must QI to nsProxyInfo");
2318 // Pruning of disabled proxies works like this:
2319 // - If all proxies are disabled, return the full list
2320 // - Otherwise, remove the disabled proxies.
2322 // Pruning of disallowed proxies works like this:
2323 // - If the protocol handler disallows the proxy, then we disallow it.
2325 // Start by removing all disallowed proxies if required:
2326 if (!(info
.flags
& nsIProtocolHandler::ALLOWS_PROXY_HTTP
)) {
2327 nsProxyInfo
*last
= nullptr, *iter
= head
;
2329 if ((iter
->Type() == kProxyType_HTTP
) ||
2330 (iter
->Type() == kProxyType_HTTPS
)) {
2333 last
->mNext
= iter
->mNext
;
2337 nsProxyInfo
* next
= iter
->mNext
;
2338 iter
->mNext
= nullptr;
2351 // Scan to see if all remaining non-direct proxies are disabled. If so, then
2352 // we'll just bail and return them all. Otherwise, we'll go and prune the
2355 bool allNonDirectProxiesDisabled
= true;
2358 for (iter
= head
; iter
; iter
= iter
->mNext
) {
2359 if (!IsProxyDisabled(iter
) && iter
->mType
!= kProxyType_DIRECT
) {
2360 allNonDirectProxiesDisabled
= false;
2365 if (allNonDirectProxiesDisabled
) {
2366 LOG(("All proxies are disabled, so trying all again"));
2368 // remove any disabled proxies.
2369 nsProxyInfo
* last
= nullptr;
2370 for (iter
= head
; iter
;) {
2371 if (IsProxyDisabled(iter
)) {
2373 nsProxyInfo
* reject
= iter
;
2382 reject
->mNext
= nullptr;
2387 // since we are about to use this proxy, make sure it is not on
2388 // the disabled proxy list. we'll add it back to that list if
2389 // we have to (in GetFailoverForProxy).
2391 // XXX(darin): It might be better to do this as a final pass.
2400 // if only DIRECT was specified then return no proxy info, and we're done.
2401 if (head
&& !head
->mNext
&& head
->mType
== kProxyType_DIRECT
) {
2405 *list
= head
; // Transfer ownership
2407 LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list
));
2410 bool nsProtocolProxyService::GetIsPACLoading() {
2411 return mPACMan
&& mPACMan
->IsLoading();
2415 } // namespace mozilla