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 "nsDNSService2.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"
34 #include "nsProxyRelease.h"
35 #include "mozilla/Mutex.h"
36 #include "mozilla/CondVar.h"
37 #include "nsISystemProxySettings.h"
38 #include "nsINetworkLinkService.h"
39 #include "nsIHttpChannelInternal.h"
40 #include "mozilla/dom/nsMixedContentBlocker.h"
41 #include "mozilla/Logging.h"
42 #include "mozilla/ScopeExit.h"
43 #include "mozilla/StaticPrefs_network.h"
44 #include "mozilla/Tokenizer.h"
45 #include "mozilla/Unused.h"
47 //----------------------------------------------------------------------------
52 extern const char kProxyType_HTTP
[];
53 extern const char kProxyType_HTTPS
[];
54 extern const char kProxyType_SOCKS
[];
55 extern const char kProxyType_SOCKS4
[];
56 extern const char kProxyType_SOCKS5
[];
57 extern const char kProxyType_DIRECT
[];
58 extern const char kProxyType_PROXY
[];
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 and GetSystemWPADSetting off-main-thread since it
625 // may hang on Windows platform
626 class AsyncGetPACURIRequestOrSystemWPADSetting final
: public nsIRunnable
{
628 NS_DECL_THREADSAFE_ISUPPORTS
630 using CallbackFunc
= nsresult (nsProtocolProxyService::*)(bool, bool,
635 AsyncGetPACURIRequestOrSystemWPADSetting(
636 nsProtocolProxyService
* aService
, CallbackFunc aCallback
,
637 nsISystemProxySettings
* aSystemProxySettings
, bool aMainThreadOnly
,
638 bool aForceReload
, bool aResetPACThread
, bool aSystemWPADAllowed
)
639 : mIsMainThreadOnly(aMainThreadOnly
),
641 mServiceHolder(do_QueryObject(aService
)),
642 mCallback(aCallback
),
643 mSystemProxySettings(aSystemProxySettings
),
644 mForceReload(aForceReload
),
645 mResetPACThread(aResetPACThread
),
646 mSystemWPADAllowed(aSystemWPADAllowed
) {
647 MOZ_ASSERT(NS_IsMainThread());
648 Unused
<< mIsMainThreadOnly
;
651 NS_IMETHOD
Run() override
{
652 MOZ_ASSERT(NS_IsMainThread() == mIsMainThreadOnly
);
656 bool systemWPADSetting
= false;
657 if (mSystemWPADAllowed
) {
658 mSystemProxySettings
->GetSystemWPADSetting(&systemWPADSetting
);
661 rv
= mSystemProxySettings
->GetPACURI(pacUri
);
663 nsCOMPtr
<nsIRunnable
> event
=
664 NewNonOwningCancelableRunnableMethod
<bool, bool, nsresult
, nsCString
,
666 "AsyncGetPACURIRequestOrSystemWPADSettingCallback", mService
,
667 mCallback
, mForceReload
, mResetPACThread
, rv
, pacUri
,
670 return NS_DispatchToMainThread(event
);
674 ~AsyncGetPACURIRequestOrSystemWPADSetting() {
675 NS_ReleaseOnMainThread(
676 "AsyncGetPACURIRequestOrSystemWPADSetting::mServiceHolder",
677 mServiceHolder
.forget());
680 bool mIsMainThreadOnly
;
682 nsProtocolProxyService
* mService
; // ref-count is hold by mServiceHolder
683 nsCOMPtr
<nsIProtocolProxyService2
> mServiceHolder
;
684 CallbackFunc mCallback
;
685 nsCOMPtr
<nsISystemProxySettings
> mSystemProxySettings
;
688 bool mResetPACThread
;
689 bool mSystemWPADAllowed
;
692 NS_IMPL_ISUPPORTS(AsyncGetPACURIRequestOrSystemWPADSetting
, nsIRunnable
)
694 //----------------------------------------------------------------------------
697 // apply mask to address (zeros out excluded bits).
699 // NOTE: we do the byte swapping here to minimize overall swapping.
701 static void proxy_MaskIPv6Addr(PRIPv6Addr
& addr
, uint16_t mask_len
) {
702 if (mask_len
== 128) return;
705 addr
.pr_s6_addr32
[3] =
706 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[3]) & (~0uL << (128 - mask_len
)));
707 } else if (mask_len
> 64) {
708 addr
.pr_s6_addr32
[3] = 0;
709 addr
.pr_s6_addr32
[2] =
710 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[2]) & (~0uL << (96 - mask_len
)));
711 } else if (mask_len
> 32) {
712 addr
.pr_s6_addr32
[3] = 0;
713 addr
.pr_s6_addr32
[2] = 0;
714 addr
.pr_s6_addr32
[1] =
715 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[1]) & (~0uL << (64 - mask_len
)));
717 addr
.pr_s6_addr32
[3] = 0;
718 addr
.pr_s6_addr32
[2] = 0;
719 addr
.pr_s6_addr32
[1] = 0;
720 addr
.pr_s6_addr32
[0] =
721 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[0]) & (~0uL << (32 - mask_len
)));
725 static void proxy_GetStringPref(nsIPrefBranch
* aPrefBranch
, const char* aPref
,
726 nsCString
& aResult
) {
728 nsresult rv
= aPrefBranch
->GetCharPref(aPref
, temp
);
732 aResult
.Assign(temp
);
733 // all of our string prefs are hostnames, so we should remove any
734 // whitespace characters that the user might have unknowingly entered.
735 aResult
.StripWhitespace();
739 static void proxy_GetIntPref(nsIPrefBranch
* aPrefBranch
, const char* aPref
,
742 nsresult rv
= aPrefBranch
->GetIntPref(aPref
, &temp
);
750 static void proxy_GetBoolPref(nsIPrefBranch
* aPrefBranch
, const char* aPref
,
753 nsresult rv
= aPrefBranch
->GetBoolPref(aPref
, &temp
);
761 //----------------------------------------------------------------------------
763 static const int32_t PROXYCONFIG_DIRECT4X
= 3;
764 static const int32_t PROXYCONFIG_COUNT
= 6;
766 NS_IMPL_ADDREF(nsProtocolProxyService
)
767 NS_IMPL_RELEASE(nsProtocolProxyService
)
768 NS_IMPL_CLASSINFO(nsProtocolProxyService
, nullptr, nsIClassInfo::SINGLETON
,
769 NS_PROTOCOLPROXYSERVICE_CID
)
771 // NS_IMPL_QUERY_INTERFACE_CI with the nsProtocolProxyService QI change
772 NS_INTERFACE_MAP_BEGIN(nsProtocolProxyService
)
773 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService
)
774 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService2
)
775 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
776 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
777 NS_INTERFACE_MAP_ENTRY(nsINamed
)
778 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsProtocolProxyService
)
779 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIProtocolProxyService
)
780 NS_IMPL_QUERY_CLASSINFO(nsProtocolProxyService
)
783 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService
, nsIProtocolProxyService
,
784 nsIProtocolProxyService2
)
786 nsProtocolProxyService::nsProtocolProxyService() : mSessionStart(PR_Now()) {}
788 nsProtocolProxyService::~nsProtocolProxyService() {
789 // These should have been cleaned up in our Observe method.
790 NS_ASSERTION(mHostFiltersArray
.Length() == 0 && mFilters
.Length() == 0 &&
792 "what happened to xpcom-shutdown?");
795 // nsProtocolProxyService methods
796 nsresult
nsProtocolProxyService::Init() {
797 // failure to access prefs is non-fatal
798 nsCOMPtr
<nsIPrefBranch
> prefBranch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
800 // monitor proxy prefs
801 prefBranch
->AddObserver(PROXY_PREF_BRANCH
, this, false);
804 PrefsChanged(prefBranch
, nullptr);
807 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
809 // register for shutdown notification so we can clean ourselves up
811 obs
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
812 obs
->AddObserver(this, NS_NETWORK_LINK_TOPIC
, false);
818 // ReloadNetworkPAC() checks if there's a non-networked PAC in use then avoids
819 // to call ReloadPAC()
820 nsresult
nsProtocolProxyService::ReloadNetworkPAC() {
821 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
827 nsresult rv
= prefs
->GetIntPref(PROXY_PREF("type"), &type
);
832 if (type
== PROXYCONFIG_PAC
) {
833 nsAutoCString pacSpec
;
834 prefs
->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec
);
835 if (!pacSpec
.IsEmpty()) {
836 nsCOMPtr
<nsIURI
> pacURI
;
837 rv
= NS_NewURI(getter_AddRefs(pacURI
), pacSpec
);
838 if (!NS_SUCCEEDED(rv
)) {
843 rv
= GetProtocolInfo(pacURI
, &pac
);
844 if (!NS_SUCCEEDED(rv
)) {
848 if (!pac
.scheme
.EqualsLiteral("file") &&
849 !pac
.scheme
.EqualsLiteral("data")) {
850 LOG((": received network changed event, reload PAC"));
854 } else if ((type
== PROXYCONFIG_WPAD
) || (type
== PROXYCONFIG_SYSTEM
)) {
861 nsresult
nsProtocolProxyService::AsyncConfigureWPADOrFromPAC(
862 bool aForceReload
, bool aResetPACThread
, bool aSystemWPADAllowed
) {
863 MOZ_ASSERT(NS_IsMainThread());
866 nsresult rv
= mSystemProxySettings
->GetMainThreadOnly(&mainThreadOnly
);
867 if (NS_WARN_IF(NS_FAILED(rv
))) {
871 nsCOMPtr
<nsIRunnable
> req
= new AsyncGetPACURIRequestOrSystemWPADSetting(
872 this, &nsProtocolProxyService::OnAsyncGetPACURIOrSystemWPADSetting
,
873 mSystemProxySettings
, mainThreadOnly
, aForceReload
, aResetPACThread
,
876 if (mainThreadOnly
) {
880 return NS_DispatchBackgroundTask(req
.forget(),
881 nsIEventTarget::DISPATCH_NORMAL
);
884 nsresult
nsProtocolProxyService::OnAsyncGetPACURIOrSystemWPADSetting(
885 bool aForceReload
, bool aResetPACThread
, nsresult aResult
,
886 const nsACString
& aUri
, bool aSystemWPADSetting
) {
887 MOZ_ASSERT(NS_IsMainThread());
889 if (aResetPACThread
) {
893 if (aSystemWPADSetting
) {
894 if (mSystemProxySettings
|| !mPACMan
) {
895 mSystemProxySettings
= nullptr;
899 nsAutoCString tempString
;
900 ConfigureFromPAC(EmptyCString(), false);
901 } else if (NS_SUCCEEDED(aResult
) && !aUri
.IsEmpty()) {
902 ConfigureFromPAC(PromiseFlatCString(aUri
), aForceReload
);
909 nsProtocolProxyService::Observe(nsISupports
* aSubject
, const char* aTopic
,
910 const char16_t
* aData
) {
911 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
914 mHostFiltersArray
.Clear();
922 if (mReloadPACTimer
) {
923 mReloadPACTimer
->Cancel();
924 mReloadPACTimer
= nullptr;
927 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
929 obs
->RemoveObserver(this, NS_NETWORK_LINK_TOPIC
);
930 obs
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
933 } else if (strcmp(aTopic
, NS_NETWORK_LINK_TOPIC
) == 0) {
934 nsCString converted
= NS_ConvertUTF16toUTF8(aData
);
935 const char* state
= converted
.get();
936 if (!strcmp(state
, NS_NETWORK_LINK_DATA_CHANGED
)) {
937 uint32_t delay
= StaticPrefs::network_proxy_reload_pac_delay();
938 LOG(("nsProtocolProxyService::Observe call ReloadNetworkPAC() delay=%u",
942 if (mReloadPACTimer
) {
943 mReloadPACTimer
->Cancel();
944 mReloadPACTimer
= nullptr;
946 NS_NewTimerWithCallback(getter_AddRefs(mReloadPACTimer
), this, delay
,
947 nsITimer::TYPE_ONE_SHOT
);
953 NS_ASSERTION(strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
) == 0,
954 "what is this random observer event?");
955 nsCOMPtr
<nsIPrefBranch
> prefs
= do_QueryInterface(aSubject
);
956 if (prefs
) PrefsChanged(prefs
, NS_LossyConvertUTF16toASCII(aData
).get());
962 nsProtocolProxyService::Notify(nsITimer
* aTimer
) {
963 MOZ_ASSERT(aTimer
== mReloadPACTimer
);
969 nsProtocolProxyService::GetName(nsACString
& aName
) {
970 aName
.AssignLiteral("nsProtocolProxyService");
974 void nsProtocolProxyService::PrefsChanged(nsIPrefBranch
* prefBranch
,
977 bool reloadPAC
= false;
978 nsAutoCString tempString
;
979 auto invokeCallback
=
980 MakeScopeExit([&] { NotifyProxyConfigChangedInternal(); });
982 if (!pref
|| !strcmp(pref
, PROXY_PREF("type")) ||
983 !strcmp(pref
, PROXY_PREF("system_wpad"))) {
985 rv
= prefBranch
->GetIntPref(PROXY_PREF("type"), &type
);
986 if (NS_SUCCEEDED(rv
)) {
987 // bug 115720 - for ns4.x backwards compatibility
988 if (type
== PROXYCONFIG_DIRECT4X
) {
989 type
= PROXYCONFIG_DIRECT
;
990 // Reset the type so that the dialog looks correct, and we
991 // don't have to handle this case everywhere else
992 // I'm paranoid about a loop of some sort - only do this
993 // if we're enumerating all prefs, and ignore any error
994 if (!pref
) prefBranch
->SetIntPref(PROXY_PREF("type"), type
);
995 } else if (type
>= PROXYCONFIG_COUNT
) {
996 LOG(("unknown proxy type: %" PRId32
"; assuming direct\n", type
));
997 type
= PROXYCONFIG_DIRECT
;
1003 if (mProxyConfig
== PROXYCONFIG_SYSTEM
) {
1004 mSystemProxySettings
= do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID
);
1005 if (!mSystemProxySettings
) mProxyConfig
= PROXYCONFIG_DIRECT
;
1008 if (mSystemProxySettings
) {
1009 mSystemProxySettings
= nullptr;
1015 if (!pref
|| !strcmp(pref
, PROXY_PREF("http"))) {
1016 proxy_GetStringPref(prefBranch
, PROXY_PREF("http"), mHTTPProxyHost
);
1019 if (!pref
|| !strcmp(pref
, PROXY_PREF("http_port"))) {
1020 proxy_GetIntPref(prefBranch
, PROXY_PREF("http_port"), mHTTPProxyPort
);
1023 if (!pref
|| !strcmp(pref
, PROXY_PREF("ssl"))) {
1024 proxy_GetStringPref(prefBranch
, PROXY_PREF("ssl"), mHTTPSProxyHost
);
1027 if (!pref
|| !strcmp(pref
, PROXY_PREF("ssl_port"))) {
1028 proxy_GetIntPref(prefBranch
, PROXY_PREF("ssl_port"), mHTTPSProxyPort
);
1031 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks"))) {
1032 proxy_GetStringPref(prefBranch
, PROXY_PREF("socks"), mSOCKSProxyTarget
);
1035 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_port"))) {
1036 proxy_GetIntPref(prefBranch
, PROXY_PREF("socks_port"), mSOCKSProxyPort
);
1039 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_version"))) {
1041 proxy_GetIntPref(prefBranch
, PROXY_PREF("socks_version"), version
);
1042 // make sure this preference value remains sane
1044 mSOCKSProxyVersion
= 5;
1046 mSOCKSProxyVersion
= 4;
1050 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_remote_dns"))) {
1051 proxy_GetBoolPref(prefBranch
, PROXY_PREF("socks_remote_dns"),
1052 mSOCKSProxyRemoteDNS
);
1055 if (!pref
|| !strcmp(pref
, PROXY_PREF("proxy_over_tls"))) {
1056 proxy_GetBoolPref(prefBranch
, PROXY_PREF("proxy_over_tls"), mProxyOverTLS
);
1059 if (!pref
|| !strcmp(pref
, PROXY_PREF("enable_wpad_over_dhcp"))) {
1060 proxy_GetBoolPref(prefBranch
, PROXY_PREF("enable_wpad_over_dhcp"),
1061 mWPADOverDHCPEnabled
);
1062 reloadPAC
= reloadPAC
|| mProxyConfig
== PROXYCONFIG_WPAD
;
1065 if (!pref
|| !strcmp(pref
, PROXY_PREF("failover_timeout"))) {
1066 proxy_GetIntPref(prefBranch
, PROXY_PREF("failover_timeout"),
1067 mFailedProxyTimeout
);
1070 if (!pref
|| !strcmp(pref
, PROXY_PREF("no_proxies_on"))) {
1071 rv
= prefBranch
->GetCharPref(PROXY_PREF("no_proxies_on"), tempString
);
1072 if (NS_SUCCEEDED(rv
)) LoadHostFilters(tempString
);
1075 // We're done if not using something that could give us a PAC URL
1076 // (PAC, WPAD or System)
1077 if (mProxyConfig
!= PROXYCONFIG_PAC
&& mProxyConfig
!= PROXYCONFIG_WPAD
&&
1078 mProxyConfig
!= PROXYCONFIG_SYSTEM
) {
1082 // OK, we need to reload the PAC file if:
1083 // 1) network.proxy.type changed, or
1084 // 2) network.proxy.autoconfig_url changed and PAC is configured
1086 if (!pref
|| !strcmp(pref
, PROXY_PREF("autoconfig_url"))) reloadPAC
= true;
1089 tempString
.Truncate();
1090 if (mProxyConfig
== PROXYCONFIG_PAC
) {
1091 prefBranch
->GetCharPref(PROXY_PREF("autoconfig_url"), tempString
);
1092 if (mPACMan
&& !mPACMan
->IsPACURI(tempString
)) {
1093 LOG(("PAC Thread URI Changed - Reset Pac Thread"));
1096 } else if (mProxyConfig
== PROXYCONFIG_WPAD
) {
1097 LOG(("Auto-detecting proxy - Reset Pac Thread"));
1099 } else if (mSystemProxySettings
&& mProxyConfig
== PROXYCONFIG_SYSTEM
&&
1100 StaticPrefs::network_proxy_system_wpad()) {
1101 AsyncConfigureWPADOrFromPAC(false, false, true);
1102 } else if (mSystemProxySettings
) {
1103 // Get System Proxy settings if available
1104 AsyncConfigureWPADOrFromPAC(false, false, false);
1106 if (!tempString
.IsEmpty() || mProxyConfig
== PROXYCONFIG_WPAD
) {
1107 ConfigureFromPAC(tempString
, false);
1112 bool nsProtocolProxyService::CanUseProxy(nsIURI
* aURI
, int32_t defaultPort
) {
1116 nsresult rv
= aURI
->GetAsciiHost(host
);
1117 if (NS_FAILED(rv
) || host
.IsEmpty()) return false;
1119 rv
= aURI
->GetPort(&port
);
1120 if (NS_FAILED(rv
)) return false;
1121 if (port
== -1) port
= defaultPort
;
1124 bool is_ipaddr
= (PR_StringToNetAddr(host
.get(), &addr
) == PR_SUCCESS
);
1128 // convert parsed address to IPv6
1129 if (addr
.raw
.family
== PR_AF_INET
) {
1130 // convert to IPv4-mapped address
1131 PR_ConvertIPv4AddrToIPv6(addr
.inet
.ip
, &ipv6
);
1132 } else if (addr
.raw
.family
== PR_AF_INET6
) {
1134 memcpy(&ipv6
, &addr
.ipv6
.ip
, sizeof(PRIPv6Addr
));
1136 NS_WARNING("unknown address family");
1137 return true; // allow proxying
1141 // Don't use proxy for local hosts (plain hostname, no dots)
1142 if ((!is_ipaddr
&& mFilterLocalHosts
&& !host
.Contains('.')) ||
1143 // This method detects if we have network.proxy.allow_hijacking_localhost
1144 // pref enabled. If it's true then this method will always return false
1145 // otherwise it returns true if the host matches an address that's
1146 // hardcoded to the loopback address.
1147 (!StaticPrefs::network_proxy_allow_hijacking_localhost() &&
1148 nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(host
))) {
1149 LOG(("Not using proxy for this local host [%s]!\n", host
.get()));
1150 return false; // don't allow proxying
1154 while (++index
< int32_t(mHostFiltersArray
.Length())) {
1155 const auto& hinfo
= mHostFiltersArray
[index
];
1157 if (is_ipaddr
!= hinfo
->is_ipaddr
) continue;
1158 if (hinfo
->port
&& hinfo
->port
!= port
) continue;
1161 // generate masked version of target IPv6 address
1163 memcpy(&masked
, &ipv6
, sizeof(PRIPv6Addr
));
1164 proxy_MaskIPv6Addr(masked
, hinfo
->ip
.mask_len
);
1166 // check for a match
1167 if (memcmp(&masked
, &hinfo
->ip
.addr
, sizeof(PRIPv6Addr
)) == 0) {
1168 return false; // proxy disallowed
1171 uint32_t host_len
= host
.Length();
1172 uint32_t filter_host_len
= hinfo
->name
.host_len
;
1174 if (host_len
>= filter_host_len
) {
1176 // compare last |filter_host_len| bytes of target hostname.
1178 const char* host_tail
= host
.get() + host_len
- filter_host_len
;
1179 if (!nsCRT::strncasecmp(host_tail
, hinfo
->name
.host
, filter_host_len
)) {
1180 // If the tail of the host string matches the filter
1182 if (filter_host_len
> 0 && hinfo
->name
.host
[0] == '.') {
1183 // If the filter was of the form .foo.bar.tld, all such
1184 // matches are correct
1185 return false; // proxy disallowed
1188 // abc-def.example.org should not match def.example.org
1189 // however, *.def.example.org should match .def.example.org
1190 // We check that the filter doesn't start with a `.`. If it does,
1191 // then the strncasecmp above should suffice. If it doesn't,
1192 // then we should only consider it a match if the strncasecmp happened
1193 // at a subdomain boundary
1194 if (host_len
> filter_host_len
&& *(host_tail
- 1) == '.') {
1195 // If the host was something.foo.bar.tld and the filter
1196 // was foo.bar.tld, it's still a match.
1197 // the character right before the tail must be a
1198 // `.` for this to work
1199 return false; // proxy disallowed
1202 if (host_len
== filter_host_len
) {
1203 // If the host and filter are of the same length,
1204 // they should match
1205 return false; // proxy disallowed
1214 // kProxyType\* may be referred to externally in
1215 // nsProxyInfo in order to compare by string pointer
1216 const char kProxyType_HTTP
[] = "http";
1217 const char kProxyType_HTTPS
[] = "https";
1218 const char kProxyType_PROXY
[] = "proxy";
1219 const char kProxyType_SOCKS
[] = "socks";
1220 const char kProxyType_SOCKS4
[] = "socks4";
1221 const char kProxyType_SOCKS5
[] = "socks5";
1222 const char kProxyType_DIRECT
[] = "direct";
1224 const char* nsProtocolProxyService::ExtractProxyInfo(const char* start
,
1225 uint32_t aResolveFlags
,
1226 nsProxyInfo
** result
) {
1230 // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
1232 // find end of proxy info delimiter
1233 const char* end
= start
;
1234 while (*end
&& *end
!= ';') ++end
;
1236 // find end of proxy type delimiter
1237 const char* sp
= start
;
1238 while (sp
< end
&& *sp
!= ' ' && *sp
!= '\t') ++sp
;
1240 uint32_t len
= sp
- start
;
1241 const char* type
= nullptr;
1244 if (nsCRT::strncasecmp(start
, kProxyType_HTTP
, 4) == 0) {
1245 type
= kProxyType_HTTP
;
1249 if (nsCRT::strncasecmp(start
, kProxyType_PROXY
, 5) == 0) {
1250 type
= kProxyType_HTTP
;
1251 } else if (nsCRT::strncasecmp(start
, kProxyType_SOCKS
, 5) == 0) {
1252 type
= kProxyType_SOCKS4
; // assume v4 for 4x compat
1253 if (StaticPrefs::network_proxy_default_pac_script_socks_version() ==
1255 type
= kProxyType_SOCKS
;
1257 } else if (nsCRT::strncasecmp(start
, kProxyType_HTTPS
, 5) == 0) {
1258 type
= kProxyType_HTTPS
;
1262 if (nsCRT::strncasecmp(start
, kProxyType_DIRECT
, 6) == 0) {
1263 type
= kProxyType_DIRECT
;
1264 } else if (nsCRT::strncasecmp(start
, kProxyType_SOCKS4
, 6) == 0) {
1265 type
= kProxyType_SOCKS4
;
1266 } else if (nsCRT::strncasecmp(start
, kProxyType_SOCKS5
, 6) == 0) {
1267 // map "SOCKS5" to "socks" to match contract-id of registered
1268 // SOCKS-v5 socket provider.
1269 type
= kProxyType_SOCKS
;
1276 // If it's a SOCKS5 proxy, do name resolution on the server side.
1277 // We could use this with SOCKS4a servers too, but they might not
1279 if (type
== kProxyType_SOCKS
|| mSOCKSProxyRemoteDNS
) {
1280 flags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
1283 // extract host:port
1285 while ((*start
== ' ' || *start
== '\t') && start
< end
) start
++;
1288 if (type
== kProxyType_HTTP
) {
1290 } else if (type
== kProxyType_HTTPS
) {
1296 RefPtr
<nsProxyInfo
> pi
= new nsProxyInfo();
1299 pi
->mResolveFlags
= aResolveFlags
;
1300 pi
->mTimeout
= mFailedProxyTimeout
;
1302 // www.foo.com:8080 and http://www.foo.com:8080
1303 nsDependentCSubstring
maybeURL(start
, end
- start
);
1304 nsCOMPtr
<nsIURI
> pacURI
;
1306 nsAutoCString urlHost
;
1307 // First assume the scheme is present, e.g. http://www.example.com:8080
1308 if (NS_FAILED(NS_NewURI(getter_AddRefs(pacURI
), maybeURL
)) ||
1309 NS_FAILED(pacURI
->GetAsciiHost(urlHost
)) || urlHost
.IsEmpty()) {
1310 // It isn't, assume www.example.com:8080
1311 maybeURL
.Insert("http://", 0);
1313 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI
), maybeURL
))) {
1314 pacURI
->GetAsciiHost(urlHost
);
1318 if (!urlHost
.IsEmpty()) {
1319 pi
->mHost
= urlHost
;
1322 if (NS_SUCCEEDED(pacURI
->GetPort(&tPort
)) && tPort
!= -1) {
1331 while (*end
== ';' || *end
== ' ' || *end
== '\t') ++end
;
1335 void nsProtocolProxyService::GetProxyKey(nsProxyInfo
* pi
, nsCString
& key
) {
1336 key
.AssignASCII(pi
->mType
);
1337 if (!pi
->mHost
.IsEmpty()) {
1339 key
.Append(pi
->mHost
);
1341 key
.AppendInt(pi
->mPort
);
1345 uint32_t nsProtocolProxyService::SecondsSinceSessionStart() {
1346 PRTime now
= PR_Now();
1348 // get time elapsed since session start
1349 int64_t diff
= now
- mSessionStart
;
1351 // convert microseconds to seconds
1352 diff
/= PR_USEC_PER_SEC
;
1354 // return converted 32 bit value
1355 return uint32_t(diff
);
1358 void nsProtocolProxyService::EnableProxy(nsProxyInfo
* pi
) {
1360 GetProxyKey(pi
, key
);
1361 mFailedProxies
.Remove(key
);
1364 void nsProtocolProxyService::DisableProxy(nsProxyInfo
* pi
) {
1366 GetProxyKey(pi
, key
);
1368 uint32_t dsec
= SecondsSinceSessionStart();
1370 // Add timeout to interval (this is the time when the proxy can
1372 dsec
+= pi
->mTimeout
;
1374 // NOTE: The classic codebase would increase the timeout value
1375 // incrementally each time a subsequent failure occurred.
1376 // We could do the same, but it would require that we not
1377 // remove proxy entries in IsProxyDisabled or otherwise
1378 // change the way we are recording disabled proxies.
1379 // Simpler is probably better for now, and at least the
1380 // user can tune the timeout setting via preferences.
1382 LOG(("DisableProxy %s %d\n", key
.get(), dsec
));
1384 // If this fails, oh well... means we don't have enough memory
1385 // to remember the failed proxy.
1386 mFailedProxies
.InsertOrUpdate(key
, dsec
);
1389 bool nsProtocolProxyService::IsProxyDisabled(nsProxyInfo
* pi
) {
1391 GetProxyKey(pi
, key
);
1394 if (!mFailedProxies
.Get(key
, &val
)) return false;
1396 uint32_t dsec
= SecondsSinceSessionStart();
1398 // if time passed has exceeded interval, then try proxy again.
1400 mFailedProxies
.Remove(key
);
1407 nsresult
nsProtocolProxyService::SetupPACThread(
1408 nsISerialEventTarget
* mainThreadEventTarget
) {
1410 return NS_ERROR_FAILURE
;
1413 if (mPACMan
) return NS_OK
;
1415 mPACMan
= new nsPACMan(mainThreadEventTarget
);
1417 bool mainThreadOnly
;
1419 if (mSystemProxySettings
&&
1420 NS_SUCCEEDED(mSystemProxySettings
->GetMainThreadOnly(&mainThreadOnly
)) &&
1422 rv
= mPACMan
->Init(mSystemProxySettings
);
1424 rv
= mPACMan
->Init(nullptr);
1426 if (NS_FAILED(rv
)) {
1427 mPACMan
->Shutdown();
1433 nsresult
nsProtocolProxyService::ResetPACThread() {
1434 if (!mPACMan
) return NS_OK
;
1436 mPACMan
->Shutdown();
1438 return SetupPACThread();
1441 nsresult
nsProtocolProxyService::ConfigureFromPAC(const nsCString
& spec
,
1443 nsresult rv
= SetupPACThread();
1444 NS_ENSURE_SUCCESS(rv
, rv
);
1446 bool autodetect
= spec
.IsEmpty();
1447 if (!forceReload
&& ((!autodetect
&& mPACMan
->IsPACURI(spec
)) ||
1448 (autodetect
&& mPACMan
->IsUsingWPAD()))) {
1452 mFailedProxies
.Clear();
1454 mPACMan
->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled
);
1455 return mPACMan
->LoadPACFromURI(spec
);
1458 void nsProtocolProxyService::ProcessPACString(const nsCString
& pacString
,
1459 uint32_t aResolveFlags
,
1460 nsIProxyInfo
** result
) {
1461 if (pacString
.IsEmpty()) {
1466 const char* proxies
= pacString
.get();
1468 nsProxyInfo
*pi
= nullptr, *first
= nullptr, *last
= nullptr;
1470 proxies
= ExtractProxyInfo(proxies
, aResolveFlags
, &pi
);
1471 if (pi
&& (pi
->mType
== kProxyType_HTTPS
) && !mProxyOverTLS
) {
1478 NS_ASSERTION(last
->mNext
== nullptr, "leaking nsProxyInfo");
1489 // nsIProtocolProxyService2
1491 nsProtocolProxyService::ReloadPAC() {
1492 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
1493 if (!prefs
) return NS_OK
;
1496 nsresult rv
= prefs
->GetIntPref(PROXY_PREF("type"), &type
);
1497 if (NS_FAILED(rv
)) return NS_OK
;
1499 nsAutoCString pacSpec
;
1500 if (type
== PROXYCONFIG_PAC
) {
1501 prefs
->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec
);
1502 } else if (type
== PROXYCONFIG_SYSTEM
) {
1503 if (mSystemProxySettings
) {
1504 AsyncConfigureWPADOrFromPAC(true, true,
1505 StaticPrefs::network_proxy_system_wpad());
1511 if (!pacSpec
.IsEmpty() || type
== PROXYCONFIG_WPAD
) {
1512 ConfigureFromPAC(pacSpec
, true);
1517 // When sync interface is removed this can go away too
1518 // The nsPACManCallback portion of this implementation should be run
1519 // off the main thread, because it uses a condvar for signaling and
1520 // the main thread is blocking on that condvar -
1521 // so call nsPACMan::AsyncGetProxyForURI() with
1522 // a false mainThreadResponse parameter.
1523 class nsAsyncBridgeRequest final
: public nsPACManCallback
{
1524 NS_DECL_THREADSAFE_ISUPPORTS
1526 nsAsyncBridgeRequest()
1527 : mMutex("nsDeprecatedCallback"),
1528 mCondVar(mMutex
, "nsDeprecatedCallback") {}
1530 void OnQueryComplete(nsresult status
, const nsACString
& pacString
,
1531 const nsACString
& newPACURL
) override
{
1532 MutexAutoLock
lock(mMutex
);
1535 mPACString
= pacString
;
1536 mPACURL
= newPACURL
;
1540 void Lock() MOZ_CAPABILITY_ACQUIRE(mMutex
) { mMutex
.Lock(); }
1541 void Unlock() MOZ_CAPABILITY_RELEASE(mMutex
) { mMutex
.Unlock(); }
1542 void Wait() { mCondVar
.Wait(TimeDuration::FromSeconds(3)); }
1545 ~nsAsyncBridgeRequest() = default;
1547 friend class nsProtocolProxyService
;
1552 nsresult mStatus
MOZ_GUARDED_BY(mMutex
){NS_OK
};
1553 nsCString mPACString
MOZ_GUARDED_BY(mMutex
);
1554 nsCString mPACURL
MOZ_GUARDED_BY(mMutex
);
1555 bool mCompleted
MOZ_GUARDED_BY(mMutex
){false};
1557 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest
)
1559 nsresult
nsProtocolProxyService::AsyncResolveInternal(
1560 nsIChannel
* channel
, uint32_t flags
, nsIProtocolProxyCallback
* callback
,
1561 nsICancelable
** result
, bool isSyncOK
,
1562 nsISerialEventTarget
* mainThreadEventTarget
) {
1563 NS_ENSURE_ARG_POINTER(channel
);
1564 NS_ENSURE_ARG_POINTER(callback
);
1566 nsCOMPtr
<nsIURI
> uri
;
1567 nsresult rv
= GetProxyURI(channel
, getter_AddRefs(uri
));
1568 if (NS_FAILED(rv
)) return rv
;
1571 RefPtr
<nsAsyncResolveRequest
> ctx
=
1572 new nsAsyncResolveRequest(this, channel
, flags
, callback
);
1574 nsProtocolInfo info
;
1575 rv
= GetProtocolInfo(uri
, &info
);
1576 if (NS_FAILED(rv
)) return rv
;
1578 nsCOMPtr
<nsIProxyInfo
> pi
;
1581 // adapt to realtime changes in the system proxy service
1582 if (mProxyConfig
== PROXYCONFIG_SYSTEM
&&
1583 !StaticPrefs::network_proxy_system_wpad()) {
1584 nsCOMPtr
<nsISystemProxySettings
> sp2
=
1585 do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID
);
1586 if (sp2
!= mSystemProxySettings
) {
1587 mSystemProxySettings
= sp2
;
1592 rv
= SetupPACThread(mainThreadEventTarget
);
1593 if (NS_FAILED(rv
)) {
1597 // SystemProxySettings and PAC files can block the main thread
1598 // but if neither of them are in use, we can just do the work
1599 // right here and directly invoke the callback
1602 Resolve_Internal(channel
, info
, flags
, &usePACThread
, getter_AddRefs(pi
));
1603 if (NS_FAILED(rv
)) return rv
;
1605 if (!usePACThread
|| !mPACMan
) {
1606 // we can do it locally
1607 rv
= ctx
->ProcessLocally(info
, pi
, isSyncOK
);
1608 if (NS_SUCCEEDED(rv
) && !isSyncOK
) {
1614 // else kick off a PAC thread query
1615 rv
= mPACMan
->AsyncGetProxyForURI(uri
, ctx
, flags
, true);
1616 if (NS_SUCCEEDED(rv
)) ctx
.forget(result
);
1620 // nsIProtocolProxyService
1622 nsProtocolProxyService::AsyncResolve2(
1623 nsIChannel
* channel
, uint32_t flags
, nsIProtocolProxyCallback
* callback
,
1624 nsISerialEventTarget
* mainThreadEventTarget
, nsICancelable
** result
) {
1625 return AsyncResolveInternal(channel
, flags
, callback
, result
, true,
1626 mainThreadEventTarget
);
1630 nsProtocolProxyService::AsyncResolve(
1631 nsISupports
* channelOrURI
, uint32_t flags
,
1632 nsIProtocolProxyCallback
* callback
,
1633 nsISerialEventTarget
* mainThreadEventTarget
, nsICancelable
** result
) {
1635 // Check if we got a channel:
1636 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(channelOrURI
);
1638 nsCOMPtr
<nsIURI
> uri
= do_QueryInterface(channelOrURI
);
1640 return NS_ERROR_NO_INTERFACE
;
1643 // creating a temporary channel from the URI which is not
1644 // used to perform any network loads, hence its safe to
1645 // use systemPrincipal as the loadingPrincipal.
1646 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
,
1647 nsContentUtils::GetSystemPrincipal(),
1648 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
1649 nsIContentPolicy::TYPE_OTHER
);
1650 NS_ENSURE_SUCCESS(rv
, rv
);
1653 return AsyncResolveInternal(channel
, flags
, callback
, result
, false,
1654 mainThreadEventTarget
);
1658 nsProtocolProxyService::NewProxyInfo(
1659 const nsACString
& aType
, const nsACString
& aHost
, int32_t aPort
,
1660 const nsACString
& aProxyAuthorizationHeader
,
1661 const nsACString
& aConnectionIsolationKey
, uint32_t aFlags
,
1662 uint32_t aFailoverTimeout
, nsIProxyInfo
* aFailoverProxy
,
1663 nsIProxyInfo
** aResult
) {
1664 return NewProxyInfoWithAuth(aType
, aHost
, aPort
, ""_ns
, ""_ns
,
1665 aProxyAuthorizationHeader
,
1666 aConnectionIsolationKey
, aFlags
, aFailoverTimeout
,
1667 aFailoverProxy
, aResult
);
1671 nsProtocolProxyService::NewProxyInfoWithAuth(
1672 const nsACString
& aType
, const nsACString
& aHost
, int32_t aPort
,
1673 const nsACString
& aUsername
, const nsACString
& aPassword
,
1674 const nsACString
& aProxyAuthorizationHeader
,
1675 const nsACString
& aConnectionIsolationKey
, uint32_t aFlags
,
1676 uint32_t aFailoverTimeout
, nsIProxyInfo
* aFailoverProxy
,
1677 nsIProxyInfo
** aResult
) {
1678 static const char* types
[] = {kProxyType_HTTP
, kProxyType_HTTPS
,
1679 kProxyType_SOCKS
, kProxyType_SOCKS4
,
1682 // resolve type; this allows us to avoid copying the type string into each
1683 // proxy info instance. we just reference the string literals directly :)
1684 const char* type
= nullptr;
1685 for (auto& t
: types
) {
1686 if (aType
.LowerCaseEqualsASCII(t
)) {
1691 NS_ENSURE_TRUE(type
, NS_ERROR_INVALID_ARG
);
1693 // We have only implemented username/password for SOCKS proxies.
1694 if ((!aUsername
.IsEmpty() || !aPassword
.IsEmpty()) &&
1695 !aType
.LowerCaseEqualsASCII(kProxyType_SOCKS
) &&
1696 !aType
.LowerCaseEqualsASCII(kProxyType_SOCKS4
)) {
1697 return NS_ERROR_NOT_IMPLEMENTED
;
1700 return NewProxyInfo_Internal(type
, aHost
, aPort
, aUsername
, aPassword
,
1701 aProxyAuthorizationHeader
,
1702 aConnectionIsolationKey
, aFlags
,
1703 aFailoverTimeout
, aFailoverProxy
, 0, aResult
);
1707 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo
* aProxy
, nsIURI
* aURI
,
1709 nsIProxyInfo
** aResult
) {
1710 // Failover is supported through a variety of methods including:
1711 // * PAC scripts (PROXYCONFIG_PAC and PROXYCONFIG_WPAD)
1714 // With extensions the mProxyConfig can be any type and the extension
1715 // is still involved in the proxy filtering. It may have also supplied
1716 // any number of failover proxies. We cannot determine what the mix is
1717 // here, so we will attempt to get a failover regardless of the config
1718 // type. MANUAL configuration will not disable a proxy.
1720 // Verify that |aProxy| is one of our nsProxyInfo objects.
1721 nsCOMPtr
<nsProxyInfo
> pi
= do_QueryInterface(aProxy
);
1723 // OK, the QI checked out. We can proceed.
1725 // Remember that this proxy is down. If the user has manually configured some
1726 // proxies we do not want to disable them.
1727 if (mProxyConfig
!= PROXYCONFIG_MANUAL
) {
1731 // NOTE: At this point, we might want to prompt the user if we have
1732 // not already tried going DIRECT. This is something that the
1733 // classic codebase supported; however, IE6 does not prompt.
1735 if (!pi
->mNext
) return NS_ERROR_NOT_AVAILABLE
;
1737 LOG(("PAC failover from %s %s:%d to %s %s:%d\n", pi
->mType
, pi
->mHost
.get(),
1738 pi
->mPort
, pi
->mNext
->mType
, pi
->mNext
->mHost
.get(), pi
->mNext
->mPort
));
1740 *aResult
= do_AddRef(pi
->mNext
).take();
1746 class ProxyFilterPositionComparator
{
1747 using FilterLinkRef
= RefPtr
<nsProtocolProxyService::FilterLink
>;
1750 bool Equals(const FilterLinkRef
& a
, const FilterLinkRef
& b
) const {
1751 return a
->position
== b
->position
;
1753 bool LessThan(const FilterLinkRef
& a
, const FilterLinkRef
& b
) const {
1754 return a
->position
< b
->position
;
1758 class ProxyFilterObjectComparator
{
1759 using FilterLinkRef
= RefPtr
<nsProtocolProxyService::FilterLink
>;
1762 bool Equals(const FilterLinkRef
& link
, const nsISupports
* obj
) const {
1763 return obj
== nsCOMPtr
<nsISupports
>(do_QueryInterface(link
->filter
)) ||
1764 obj
== nsCOMPtr
<nsISupports
>(do_QueryInterface(link
->channelFilter
));
1770 nsresult
nsProtocolProxyService::InsertFilterLink(RefPtr
<FilterLink
>&& link
) {
1771 LOG(("nsProtocolProxyService::InsertFilterLink filter=%p", link
.get()));
1774 return NS_ERROR_FAILURE
;
1777 // If we add a new element with the same position as an existing one, we want
1778 // to preserve the insertion order to avoid surprises.
1779 mFilters
.InsertElementSorted(link
, ProxyFilterPositionComparator());
1781 NotifyProxyConfigChangedInternal();
1787 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter
* filter
,
1788 uint32_t position
) {
1789 UnregisterFilter(filter
); // remove this filter if we already have it
1791 RefPtr
<FilterLink
> link
= new FilterLink(position
, filter
);
1792 return InsertFilterLink(std::move(link
));
1796 nsProtocolProxyService::RegisterChannelFilter(
1797 nsIProtocolProxyChannelFilter
* channelFilter
, uint32_t position
) {
1798 UnregisterChannelFilter(
1799 channelFilter
); // remove this filter if we already have it
1801 RefPtr
<FilterLink
> link
= new FilterLink(position
, channelFilter
);
1802 return InsertFilterLink(std::move(link
));
1805 nsresult
nsProtocolProxyService::RemoveFilterLink(nsISupports
* givenObject
) {
1806 LOG(("nsProtocolProxyService::RemoveFilterLink target=%p", givenObject
));
1809 mFilters
.RemoveElement(givenObject
, ProxyFilterObjectComparator())
1811 : NS_ERROR_UNEXPECTED
;
1812 if (NS_SUCCEEDED(rv
)) {
1813 NotifyProxyConfigChangedInternal();
1820 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter
* filter
) {
1821 // QI to nsISupports so we can safely test object identity.
1822 nsCOMPtr
<nsISupports
> givenObject
= do_QueryInterface(filter
);
1823 return RemoveFilterLink(givenObject
);
1827 nsProtocolProxyService::UnregisterChannelFilter(
1828 nsIProtocolProxyChannelFilter
* channelFilter
) {
1829 // QI to nsISupports so we can safely test object identity.
1830 nsCOMPtr
<nsISupports
> givenObject
= do_QueryInterface(channelFilter
);
1831 return RemoveFilterLink(givenObject
);
1835 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType
) {
1836 *aProxyConfigType
= mProxyConfig
;
1840 void nsProtocolProxyService::LoadHostFilters(const nsACString
& aFilters
) {
1845 // check to see the owners flag? /!?/ TODO
1846 if (mHostFiltersArray
.Length() > 0) {
1847 mHostFiltersArray
.Clear();
1850 // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref
1852 mFilterLocalHosts
= false;
1854 if (aFilters
.IsEmpty()) {
1859 // filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
1860 // filters = filter *( "," LWS filter)
1862 mozilla::Tokenizer
t(aFilters
);
1863 mozilla::Tokenizer::Token token
;
1865 // while (*filters) {
1867 // skip over spaces and ,
1869 while (t
.CheckChar(',')) {
1873 nsAutoCString portStr
;
1874 nsAutoCString hostStr
;
1875 nsAutoCString maskStr
;
1878 bool parsingIPv6
= false;
1879 bool parsingPort
= false;
1880 bool parsingMask
= false;
1881 while (t
.Next(token
)) {
1882 if (token
.Equals(mozilla::Tokenizer::Token::EndOfFile())) {
1886 if (token
.Equals(mozilla::Tokenizer::Token::Char(',')) ||
1887 token
.Type() == mozilla::Tokenizer::TOKEN_WS
) {
1891 if (token
.Equals(mozilla::Tokenizer::Token::Char('['))) {
1896 if (!parsingIPv6
&& token
.Equals(mozilla::Tokenizer::Token::Char(':'))) {
1897 // Port is starting. Claim the previous as host.
1908 if (token
.Equals(mozilla::Tokenizer::Token::Char('/'))) {
1915 if (token
.Equals(mozilla::Tokenizer::Token::Char(']'))) {
1916 parsingIPv6
= false;
1920 if (!parsingPort
&& !parsingMask
) {
1922 } else if (parsingPort
) {
1924 } else if (parsingMask
) {
1927 NS_WARNING("Could not parse this rule");
1931 if (hostStr
.IsEmpty()) {
1935 // If the current host filter is "<local>", then all local (i.e.
1936 // no dots in the hostname) hosts should bypass the proxy
1937 if (hostStr
.EqualsIgnoreCase("<local>")) {
1938 mFilterLocalHosts
= true;
1940 ("loaded filter for local hosts "
1941 "(plain host names, no dots)\n"));
1942 // Continue to next host filter;
1946 // For all other host filters, create HostInfo object and add to list
1947 HostInfo
* hinfo
= new HostInfo();
1948 nsresult rv
= NS_OK
;
1950 int32_t port
= portStr
.ToInteger(&rv
);
1951 if (NS_FAILED(rv
)) {
1956 int32_t maskLen
= maskStr
.ToInteger(&rv
);
1957 if (NS_FAILED(rv
)) {
1961 // PR_StringToNetAddr can't parse brackets enclosed IPv6
1962 nsAutoCString addrString
= hostStr
;
1963 if (hostStr
.First() == '[' && hostStr
.Last() == ']') {
1964 addrString
= Substring(hostStr
, 1, hostStr
.Length() - 2);
1968 if (PR_StringToNetAddr(addrString
.get(), &addr
) == PR_SUCCESS
) {
1969 hinfo
->is_ipaddr
= true;
1970 hinfo
->ip
.family
= PR_AF_INET6
; // we always store address as IPv6
1971 hinfo
->ip
.mask_len
= maskLen
;
1973 if (hinfo
->ip
.mask_len
== 0) {
1974 NS_WARNING("invalid mask");
1978 if (addr
.raw
.family
== PR_AF_INET
) {
1979 // convert to IPv4-mapped address
1980 PR_ConvertIPv4AddrToIPv6(addr
.inet
.ip
, &hinfo
->ip
.addr
);
1981 // adjust mask_len accordingly
1982 if (hinfo
->ip
.mask_len
<= 32) hinfo
->ip
.mask_len
+= 96;
1983 } else if (addr
.raw
.family
== PR_AF_INET6
) {
1985 memcpy(&hinfo
->ip
.addr
, &addr
.ipv6
.ip
, sizeof(PRIPv6Addr
));
1987 NS_WARNING("unknown address family");
1991 // apply mask to IPv6 address
1992 proxy_MaskIPv6Addr(hinfo
->ip
.addr
, hinfo
->ip
.mask_len
);
1995 if (hostStr
.First() == '*') {
1996 host
= Substring(hostStr
, 1);
2001 if (host
.IsEmpty()) {
2002 hinfo
->name
.host
= nullptr;
2006 hinfo
->name
.host_len
= host
.Length();
2008 hinfo
->is_ipaddr
= false;
2009 hinfo
->name
.host
= ToNewCString(host
, mozilla::fallible
);
2011 if (!hinfo
->name
.host
) goto loser
;
2014 // #define DEBUG_DUMP_FILTERS
2015 #ifdef DEBUG_DUMP_FILTERS
2016 printf("loaded filter[%zu]:\n", mHostFiltersArray
.Length());
2017 printf(" is_ipaddr = %u\n", hinfo
->is_ipaddr
);
2018 printf(" port = %u\n", hinfo
->port
);
2019 printf(" host = %s\n", hostStr
.get());
2020 if (hinfo
->is_ipaddr
) {
2021 printf(" ip.family = %x\n", hinfo
->ip
.family
);
2022 printf(" ip.mask_len = %u\n", hinfo
->ip
.mask_len
);
2025 PR_SetNetAddr(PR_IpAddrNull
, PR_AF_INET6
, 0, &netAddr
);
2026 memcpy(&netAddr
.ipv6
.ip
, &hinfo
->ip
.addr
, sizeof(hinfo
->ip
.addr
));
2029 PR_NetAddrToString(&netAddr
, buf
, sizeof(buf
));
2031 printf(" ip.addr = %s\n", buf
);
2033 printf(" name.host = %s\n", hinfo
->name
.host
);
2037 mHostFiltersArray
.AppendElement(hinfo
);
2044 nsresult
nsProtocolProxyService::GetProtocolInfo(nsIURI
* uri
,
2045 nsProtocolInfo
* info
) {
2046 AssertIsOnMainThread();
2047 MOZ_ASSERT(uri
, "URI is null");
2048 MOZ_ASSERT(info
, "info is null");
2052 rv
= uri
->GetScheme(info
->scheme
);
2053 if (NS_FAILED(rv
)) return rv
;
2055 nsCOMPtr
<nsIIOService
> ios
= do_GetIOService(&rv
);
2056 if (NS_FAILED(rv
)) return rv
;
2058 rv
= ios
->GetDynamicProtocolFlags(uri
, &info
->flags
);
2059 if (NS_FAILED(rv
)) return rv
;
2061 rv
= ios
->GetDefaultPort(info
->scheme
.get(), &info
->defaultPort
);
2065 nsresult
nsProtocolProxyService::NewProxyInfo_Internal(
2066 const char* aType
, const nsACString
& aHost
, int32_t aPort
,
2067 const nsACString
& aUsername
, const nsACString
& aPassword
,
2068 const nsACString
& aProxyAuthorizationHeader
,
2069 const nsACString
& aConnectionIsolationKey
, uint32_t aFlags
,
2070 uint32_t aFailoverTimeout
, nsIProxyInfo
* aFailoverProxy
,
2071 uint32_t aResolveFlags
, nsIProxyInfo
** aResult
) {
2072 if (aPort
<= 0) aPort
= -1;
2074 nsCOMPtr
<nsProxyInfo
> failover
;
2075 if (aFailoverProxy
) {
2076 failover
= do_QueryInterface(aFailoverProxy
);
2077 NS_ENSURE_ARG(failover
);
2080 RefPtr
<nsProxyInfo
> proxyInfo
= new nsProxyInfo();
2082 proxyInfo
->mType
= aType
;
2083 proxyInfo
->mHost
= aHost
;
2084 proxyInfo
->mPort
= aPort
;
2085 proxyInfo
->mUsername
= aUsername
;
2086 proxyInfo
->mPassword
= aPassword
;
2087 proxyInfo
->mFlags
= aFlags
;
2088 proxyInfo
->mResolveFlags
= aResolveFlags
;
2089 proxyInfo
->mTimeout
=
2090 aFailoverTimeout
== UINT32_MAX
? mFailedProxyTimeout
: aFailoverTimeout
;
2091 proxyInfo
->mProxyAuthorizationHeader
= aProxyAuthorizationHeader
;
2092 proxyInfo
->mConnectionIsolationKey
= aConnectionIsolationKey
;
2093 failover
.swap(proxyInfo
->mNext
);
2095 proxyInfo
.forget(aResult
);
2099 nsresult
nsProtocolProxyService::Resolve_Internal(nsIChannel
* channel
,
2100 const nsProtocolInfo
& info
,
2103 nsIProxyInfo
** result
) {
2104 NS_ENSURE_ARG_POINTER(channel
);
2106 *usePACThread
= false;
2109 if (!(info
.flags
& nsIProtocolHandler::ALLOWS_PROXY
)) {
2110 return NS_OK
; // Can't proxy this (filters may not override)
2113 nsCOMPtr
<nsIURI
> uri
;
2114 nsresult rv
= GetProxyURI(channel
, getter_AddRefs(uri
));
2115 if (NS_FAILED(rv
)) return rv
;
2118 // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
2119 // here means that we will not use a proxy for this connection.
2120 if (mPACMan
&& mPACMan
->IsPACURI(uri
)) return NS_OK
;
2122 // if proxies are enabled and this host:port combo is supposed to use a
2123 // proxy, check for a proxy.
2124 if ((mProxyConfig
== PROXYCONFIG_DIRECT
) ||
2125 !CanUseProxy(uri
, info
.defaultPort
)) {
2129 bool mainThreadOnly
;
2130 if (mSystemProxySettings
&& mProxyConfig
== PROXYCONFIG_SYSTEM
&&
2131 NS_SUCCEEDED(mSystemProxySettings
->GetMainThreadOnly(&mainThreadOnly
)) &&
2133 *usePACThread
= true;
2137 if (mSystemProxySettings
&& mProxyConfig
== PROXYCONFIG_SYSTEM
) {
2138 // If the system proxy setting implementation is not threadsafe (e.g
2139 // linux gconf), we'll do it inline here. Such implementations promise
2141 // bug 1366133: this block uses GetPACURI & GetProxyForURI, which may
2142 // hang on Windows platform. Fortunately, current implementation on
2143 // Windows is not main thread only, so we are safe here.
2145 nsAutoCString PACURI
;
2146 nsAutoCString pacString
;
2148 if (NS_SUCCEEDED(mSystemProxySettings
->GetPACURI(PACURI
)) &&
2149 !PACURI
.IsEmpty()) {
2150 // There is a PAC URI configured. If it is unchanged, then
2151 // just execute the PAC thread. If it is changed then load
2154 if (mPACMan
&& mPACMan
->IsPACURI(PACURI
)) {
2156 *usePACThread
= true;
2160 ConfigureFromPAC(PACURI
, false);
2166 nsAutoCString scheme
;
2169 uri
->GetAsciiSpec(spec
);
2170 uri
->GetAsciiHost(host
);
2171 uri
->GetScheme(scheme
);
2172 uri
->GetPort(&port
);
2174 if (flags
& RESOLVE_PREFER_SOCKS_PROXY
) {
2175 LOG(("Ignoring RESOLVE_PREFER_SOCKS_PROXY for system proxy setting\n"));
2176 } else if (flags
& RESOLVE_PREFER_HTTPS_PROXY
) {
2177 scheme
.AssignLiteral("https");
2178 } else if (flags
& RESOLVE_IGNORE_URI_SCHEME
) {
2179 scheme
.AssignLiteral("http");
2182 // now try the system proxy settings for this particular url
2183 if (NS_SUCCEEDED(mSystemProxySettings
->GetProxyForURI(spec
, scheme
, host
,
2184 port
, pacString
))) {
2185 nsCOMPtr
<nsIProxyInfo
> pi
;
2186 ProcessPACString(pacString
, 0, getter_AddRefs(pi
));
2188 if (flags
& RESOLVE_PREFER_SOCKS_PROXY
&&
2189 flags
& RESOLVE_PREFER_HTTPS_PROXY
) {
2192 // DIRECT from ProcessPACString indicates that system proxy settings
2193 // are not configured to use SOCKS proxy. Try https proxy as a
2194 // secondary preferrable proxy. This is mainly for websocket whose
2195 // proxy precedence is SOCKS > HTTPS > DIRECT.
2196 if (type
.EqualsLiteral(kProxyType_DIRECT
)) {
2197 scheme
.AssignLiteral(kProxyType_HTTPS
);
2198 if (NS_SUCCEEDED(mSystemProxySettings
->GetProxyForURI(
2199 spec
, scheme
, host
, port
, pacString
))) {
2200 ProcessPACString(pacString
, 0, getter_AddRefs(pi
));
2209 // if proxies are enabled and this host:port combo is supposed to use a
2210 // proxy, check for a proxy.
2211 if (mProxyConfig
== PROXYCONFIG_DIRECT
||
2212 (mProxyConfig
== PROXYCONFIG_MANUAL
&&
2213 !CanUseProxy(uri
, info
.defaultPort
))) {
2217 // Proxy auto config magic...
2218 if (mProxyConfig
== PROXYCONFIG_PAC
|| mProxyConfig
== PROXYCONFIG_WPAD
||
2219 StaticPrefs::network_proxy_system_wpad()) {
2220 // Do not query PAC now.
2221 *usePACThread
= true;
2225 // If we aren't in manual proxy configuration mode then we don't
2226 // want to honor any manual specific prefs that might be still set
2227 if (mProxyConfig
!= PROXYCONFIG_MANUAL
) return NS_OK
;
2229 // proxy info values for manual configuration mode
2230 const char* type
= nullptr;
2231 const nsACString
* host
= nullptr;
2234 uint32_t proxyFlags
= 0;
2236 if ((flags
& RESOLVE_PREFER_SOCKS_PROXY
) && !mSOCKSProxyTarget
.IsEmpty() &&
2237 (IsHostLocalTarget(mSOCKSProxyTarget
) || mSOCKSProxyPort
> 0)) {
2238 host
= &mSOCKSProxyTarget
;
2239 if (mSOCKSProxyVersion
== 4) {
2240 type
= kProxyType_SOCKS4
;
2242 type
= kProxyType_SOCKS
;
2244 port
= mSOCKSProxyPort
;
2245 if (mSOCKSProxyRemoteDNS
) {
2246 proxyFlags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
2248 } else if ((flags
& RESOLVE_PREFER_HTTPS_PROXY
) &&
2249 !mHTTPSProxyHost
.IsEmpty() && mHTTPSProxyPort
> 0) {
2250 host
= &mHTTPSProxyHost
;
2251 type
= kProxyType_HTTP
;
2252 port
= mHTTPSProxyPort
;
2253 } else if (!mHTTPProxyHost
.IsEmpty() && mHTTPProxyPort
> 0 &&
2254 ((flags
& RESOLVE_IGNORE_URI_SCHEME
) ||
2255 info
.scheme
.EqualsLiteral("http"))) {
2256 host
= &mHTTPProxyHost
;
2257 type
= kProxyType_HTTP
;
2258 port
= mHTTPProxyPort
;
2259 } else if (!mHTTPSProxyHost
.IsEmpty() && mHTTPSProxyPort
> 0 &&
2260 !(flags
& RESOLVE_IGNORE_URI_SCHEME
) &&
2261 info
.scheme
.EqualsLiteral("https")) {
2262 host
= &mHTTPSProxyHost
;
2263 type
= kProxyType_HTTP
;
2264 port
= mHTTPSProxyPort
;
2265 } else if (!mSOCKSProxyTarget
.IsEmpty() &&
2266 (IsHostLocalTarget(mSOCKSProxyTarget
) || mSOCKSProxyPort
> 0)) {
2267 host
= &mSOCKSProxyTarget
;
2268 if (mSOCKSProxyVersion
== 4) {
2269 type
= kProxyType_SOCKS4
;
2271 type
= kProxyType_SOCKS
;
2273 port
= mSOCKSProxyPort
;
2274 if (mSOCKSProxyRemoteDNS
) {
2275 proxyFlags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
2280 rv
= NewProxyInfo_Internal(type
, *host
, port
, ""_ns
, ""_ns
, ""_ns
, ""_ns
,
2281 proxyFlags
, UINT32_MAX
, nullptr, flags
, result
);
2282 if (NS_FAILED(rv
)) return rv
;
2288 void nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo
* aProxy
) {
2289 // Disable Prefetch in the DNS service if a proxy is in use.
2290 if (!aProxy
) return;
2292 nsCOMPtr
<nsProxyInfo
> pi
= do_QueryInterface(aProxy
);
2293 if (!pi
|| !pi
->mType
|| pi
->mType
== kProxyType_DIRECT
) return;
2295 // To avoid getting DNS service recursively, we directly use
2296 // GetXPCOMSingleton().
2297 nsCOMPtr
<nsIDNSService
> dns
= nsDNSService::GetXPCOMSingleton();
2299 nsCOMPtr
<nsPIDNSService
> pdns
= do_QueryInterface(dns
);
2302 // We lose the prefetch optimization for the life of the dns service.
2303 pdns
->SetPrefetchEnabled(false);
2306 void nsProtocolProxyService::CopyFilters(nsTArray
<RefPtr
<FilterLink
>>& aCopy
) {
2307 MOZ_ASSERT(aCopy
.Length() == 0);
2308 aCopy
.AppendElements(mFilters
);
2311 bool nsProtocolProxyService::ApplyFilter(
2312 FilterLink
const* filterLink
, nsIChannel
* channel
,
2313 const nsProtocolInfo
& info
, nsCOMPtr
<nsIProxyInfo
> list
,
2314 nsIProxyProtocolFilterResult
* callback
) {
2317 // We prune the proxy list prior to invoking each filter. This may be
2318 // somewhat inefficient, but it seems like a good idea since we want each
2319 // filter to "see" a valid proxy list.
2320 PruneProxyInfo(info
, list
);
2322 if (filterLink
->filter
) {
2323 nsCOMPtr
<nsIURI
> uri
;
2324 Unused
<< GetProxyURI(channel
, getter_AddRefs(uri
));
2329 rv
= filterLink
->filter
->ApplyFilter(uri
, list
, callback
);
2330 return NS_SUCCEEDED(rv
);
2333 if (filterLink
->channelFilter
) {
2334 rv
= filterLink
->channelFilter
->ApplyFilter(channel
, list
, callback
);
2335 return NS_SUCCEEDED(rv
);
2341 void nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo
& info
,
2342 nsIProxyInfo
** list
) {
2345 LOG(("nsProtocolProxyService::PruneProxyInfo ENTER list=%p", *list
));
2347 nsProxyInfo
* head
= nullptr;
2348 CallQueryInterface(*list
, &head
);
2350 MOZ_ASSERT_UNREACHABLE("nsIProxyInfo must QI to nsProxyInfo");
2355 // Pruning of disabled proxies works like this:
2356 // - If all proxies are disabled, return the full list
2357 // - Otherwise, remove the disabled proxies.
2359 // Pruning of disallowed proxies works like this:
2360 // - If the protocol handler disallows the proxy, then we disallow it.
2362 // Start by removing all disallowed proxies if required:
2363 if (!(info
.flags
& nsIProtocolHandler::ALLOWS_PROXY_HTTP
)) {
2364 nsProxyInfo
*last
= nullptr, *iter
= head
;
2366 if ((iter
->Type() == kProxyType_HTTP
) ||
2367 (iter
->Type() == kProxyType_HTTPS
)) {
2370 last
->mNext
= iter
->mNext
;
2374 nsProxyInfo
* next
= iter
->mNext
;
2375 iter
->mNext
= nullptr;
2388 // Scan to see if all remaining non-direct proxies are disabled. If so, then
2389 // we'll just bail and return them all. Otherwise, we'll go and prune the
2392 bool allNonDirectProxiesDisabled
= true;
2395 for (iter
= head
; iter
; iter
= iter
->mNext
) {
2396 if (!IsProxyDisabled(iter
) && iter
->mType
!= kProxyType_DIRECT
) {
2397 allNonDirectProxiesDisabled
= false;
2402 if (allNonDirectProxiesDisabled
&&
2403 StaticPrefs::network_proxy_retry_failed_proxies()) {
2404 LOG(("All proxies are disabled, so trying all again"));
2406 // remove any disabled proxies.
2407 nsProxyInfo
* last
= nullptr;
2408 for (iter
= head
; iter
;) {
2409 if (IsProxyDisabled(iter
)) {
2411 nsProxyInfo
* reject
= iter
;
2420 reject
->mNext
= nullptr;
2425 // since we are about to use this proxy, make sure it is not on
2426 // the disabled proxy list. we'll add it back to that list if
2427 // we have to (in GetFailoverForProxy).
2429 // XXX(darin): It might be better to do this as a final pass.
2438 // if only DIRECT was specified then return no proxy info, and we're done.
2439 if (head
&& !head
->mNext
&& head
->mType
== kProxyType_DIRECT
) {
2443 *list
= head
; // Transfer ownership
2445 LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list
));
2448 bool nsProtocolProxyService::GetIsPACLoading() {
2449 return mPACMan
&& mPACMan
->IsLoading();
2453 nsProtocolProxyService::AddProxyConfigCallback(
2454 nsIProxyConfigChangedCallback
* aCallback
) {
2455 MOZ_ASSERT(NS_IsMainThread());
2457 return NS_ERROR_INVALID_ARG
;
2460 mProxyConfigChangedCallbacks
.AppendElement(aCallback
);
2465 nsProtocolProxyService::RemoveProxyConfigCallback(
2466 nsIProxyConfigChangedCallback
* aCallback
) {
2467 MOZ_ASSERT(NS_IsMainThread());
2469 mProxyConfigChangedCallbacks
.RemoveElement(aCallback
);
2474 nsProtocolProxyService::NotifyProxyConfigChangedInternal() {
2475 LOG(("nsProtocolProxyService::NotifyProxyConfigChangedInternal"));
2476 MOZ_ASSERT(NS_IsMainThread());
2478 for (const auto& callback
: mProxyConfigChangedCallbacks
) {
2479 callback
->OnProxyConfigChanged();
2485 } // namespace mozilla