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 "nsIScriptSecurityManager.h"
23 #include "nsIPrefService.h"
24 #include "nsIPrefBranch.h"
25 #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/Logging.h"
42 #include "mozilla/Tokenizer.h"
43 #include "mozilla/Unused.h"
45 //----------------------------------------------------------------------------
50 extern const char kProxyType_HTTP
[];
51 extern const char kProxyType_HTTPS
[];
52 extern const char kProxyType_SOCKS
[];
53 extern const char kProxyType_SOCKS4
[];
54 extern const char kProxyType_SOCKS5
[];
55 extern const char kProxyType_DIRECT
[];
58 #define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
60 //----------------------------------------------------------------------------
62 #define PROXY_PREF_BRANCH "network.proxy"
63 #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
65 //----------------------------------------------------------------------------
67 // This structure is intended to be allocated on the stack
68 struct nsProtocolInfo
{
74 //----------------------------------------------------------------------------
76 // Return the channel's proxy URI, or if it doesn't exist, the
77 // channel's main URI.
78 static nsresult
GetProxyURI(nsIChannel
*channel
, nsIURI
**aOut
) {
80 nsCOMPtr
<nsIURI
> proxyURI
;
81 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel(do_QueryInterface(channel
));
83 rv
= httpChannel
->GetProxyURI(getter_AddRefs(proxyURI
));
86 rv
= channel
->GetURI(getter_AddRefs(proxyURI
));
91 proxyURI
.forget(aOut
);
95 //-----------------------------------------------------------------------------
97 nsProtocolProxyService::FilterLink::FilterLink(uint32_t p
,
98 nsIProtocolProxyFilter
*f
)
99 : position(p
), filter(f
), channelFilter(nullptr) {
100 LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, filter=%p", this,
103 nsProtocolProxyService::FilterLink::FilterLink(
104 uint32_t p
, nsIProtocolProxyChannelFilter
*cf
)
105 : position(p
), filter(nullptr), channelFilter(cf
) {
106 LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, channel-filter=%p",
110 nsProtocolProxyService::FilterLink::~FilterLink() {
111 LOG(("nsProtocolProxyService::FilterLink::~FilterLink %p", this));
114 //-----------------------------------------------------------------------------
116 // The nsPACManCallback portion of this implementation should be run
117 // on the main thread - so call nsPACMan::AsyncGetProxyForURI() with
118 // a true mainThreadResponse parameter.
119 class nsAsyncResolveRequest final
: public nsIRunnable
,
120 public nsPACManCallback
,
121 public nsICancelable
{
123 NS_DECL_THREADSAFE_ISUPPORTS
125 nsAsyncResolveRequest(nsProtocolProxyService
*pps
, nsIChannel
*channel
,
126 uint32_t aResolveFlags
,
127 nsIProtocolProxyCallback
*callback
)
130 mResolveFlags(aResolveFlags
),
134 mCallback(callback
) {
135 NS_ASSERTION(mCallback
, "null callback");
139 ~nsAsyncResolveRequest() {
140 if (!NS_IsMainThread()) {
141 // these xpcom pointers might need to be proxied back to the
142 // main thread to delete safely, but if this request had its
143 // callbacks called normally they will all be null and this is a nop
146 NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mChannel",
151 NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mCallback",
156 NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mProxyInfo",
157 mProxyInfo
.forget());
161 NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mXPComPPS",
167 // Helper class to loop over all registered asynchronous filters.
168 // There is a cycle between nsAsyncResolveRequest and this class that
169 // is broken after the last filter has called back on this object.
170 class AsyncApplyFilters final
: public nsIProxyProtocolFilterResult
,
172 public nsICancelable
{
173 // The reference counter is thread-safe, but the processing logic is
174 // considered single thread only. We want the counter be thread safe,
175 // since this class can be released on a background thread.
176 NS_DECL_THREADSAFE_ISUPPORTS
177 NS_DECL_NSIPROXYPROTOCOLFILTERRESULT
179 NS_DECL_NSICANCELABLE
181 typedef std::function
<nsresult(nsAsyncResolveRequest
*, nsIProxyInfo
*,
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 typedef nsProtocolProxyService::FilterLink 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
<nsIEventTarget
> mProcessingThread
;
235 nsresult
ProcessLocally(nsProtocolInfo
&info
, nsIProxyInfo
*pi
,
237 SetResult(NS_OK
, pi
);
239 auto consumeFiltersResult
= [isSyncOK
](nsAsyncResolveRequest
*ctx
,
241 bool aCalledAsync
) -> nsresult
{
242 ctx
->SetResult(NS_OK
, pi
);
243 if (isSyncOK
|| aCalledAsync
) {
248 return ctx
->DispatchCallback();
251 mAsyncFilterApplier
= new AsyncApplyFilters(info
, consumeFiltersResult
);
252 // may call consumeFiltersResult() directly
253 return mAsyncFilterApplier
->AsyncProcess(this);
256 void SetResult(nsresult status
, nsIProxyInfo
*pi
) {
261 NS_IMETHOD
Run() override
{
262 if (mCallback
) DoCallback();
266 NS_IMETHOD
Cancel(nsresult reason
) override
{
267 NS_ENSURE_ARG(NS_FAILED(reason
));
269 if (mAsyncFilterApplier
) {
270 mAsyncFilterApplier
->Cancel(reason
);
273 // If we've already called DoCallback then, nothing more to do.
274 if (!mCallback
) return NS_OK
;
276 SetResult(reason
, nullptr);
277 return DispatchCallback();
280 nsresult
DispatchCallback() {
281 if (mDispatched
) // Only need to dispatch once
284 nsresult rv
= NS_DispatchToCurrentThread(this);
286 NS_WARNING("unable to dispatch callback event");
292 mCallback
= nullptr; // break possible reference cycle
297 // Called asynchronously, so we do not need to post another PLEvent
298 // before calling DoCallback.
299 void OnQueryComplete(nsresult status
, const nsACString
&pacString
,
300 const nsACString
&newPACURL
) override
{
301 // If we've already called DoCallback then, nothing more to do.
302 if (!mCallback
) return;
304 // Provided we haven't been canceled...
305 if (mStatus
== NS_OK
) {
307 mPACString
= pacString
;
311 // In the cancelation case, we may still have another PLEvent in
312 // the queue that wants to call DoCallback. No need to wait for
313 // it, just run the callback now.
318 bool pacAvailable
= true;
319 if (mStatus
== NS_ERROR_NOT_AVAILABLE
&& !mProxyInfo
) {
320 // If the PAC service is not avail (e.g. failed pac load
321 // or shutdown) then we will be going direct. Make that
322 // mapping now so that any filters are still applied.
323 mPACString
= NS_LITERAL_CSTRING("DIRECT;");
326 LOG(("pac not available, use DIRECT\n"));
327 pacAvailable
= false;
330 // Generate proxy info from the PAC string if appropriate
331 if (NS_SUCCEEDED(mStatus
) && !mProxyInfo
&& !mPACString
.IsEmpty()) {
332 mPPS
->ProcessPACString(mPACString
, mResolveFlags
,
333 getter_AddRefs(mProxyInfo
));
334 nsCOMPtr
<nsIURI
> proxyURI
;
335 GetProxyURI(mChannel
, getter_AddRefs(proxyURI
));
337 // Now apply proxy filters
339 mStatus
= mPPS
->GetProtocolInfo(proxyURI
, &info
);
341 auto consumeFiltersResult
= [pacAvailable
](nsAsyncResolveRequest
*self
,
343 bool async
) -> nsresult
{
344 LOG(("DoCallback::consumeFiltersResult this=%p, pi=%p, async=%d", self
,
347 self
->mProxyInfo
= pi
;
350 // if !pacAvailable, it was already logged above
351 LOG(("pac thread callback %s\n", self
->mPACString
.get()));
354 if (NS_SUCCEEDED(self
->mStatus
)) {
355 self
->mPPS
->MaybeDisableDNSPrefetch(self
->mProxyInfo
);
358 self
->mCallback
->OnProxyAvailable(self
, self
->mChannel
,
359 self
->mProxyInfo
, self
->mStatus
);
364 if (NS_SUCCEEDED(mStatus
)) {
365 mAsyncFilterApplier
= new AsyncApplyFilters(info
, consumeFiltersResult
);
366 // This may call consumeFiltersResult() directly.
367 mAsyncFilterApplier
->AsyncProcess(this);
371 consumeFiltersResult(this, nullptr, false);
372 } else if (NS_SUCCEEDED(mStatus
) && !mPACURL
.IsEmpty()) {
373 LOG(("pac thread callback indicates new pac file load\n"));
375 nsCOMPtr
<nsIURI
> proxyURI
;
376 GetProxyURI(mChannel
, getter_AddRefs(proxyURI
));
378 // trigger load of new pac url
379 nsresult rv
= mPPS
->ConfigureFromPAC(mPACURL
, false);
380 if (NS_SUCCEEDED(rv
)) {
381 // now that the load is triggered, we can resubmit the query
382 RefPtr
<nsAsyncResolveRequest
> newRequest
=
383 new nsAsyncResolveRequest(mPPS
, mChannel
, mResolveFlags
, mCallback
);
384 rv
= mPPS
->mPACMan
->AsyncGetProxyForURI(proxyURI
, newRequest
, true);
388 mCallback
->OnProxyAvailable(this, mChannel
, nullptr, rv
);
390 // do not call onproxyavailable() in SUCCESS case - the newRequest will
393 LOG(("pac thread callback did not provide information %" PRIX32
"\n",
394 static_cast<uint32_t>(mStatus
)));
395 if (NS_SUCCEEDED(mStatus
)) mPPS
->MaybeDisableDNSPrefetch(mProxyInfo
);
396 mCallback
->OnProxyAvailable(this, mChannel
, mProxyInfo
, mStatus
);
399 // We are on the main thread now and don't need these any more so
400 // release them to avoid having to proxy them back to the main thread
402 mCallback
= nullptr; // in case the callback holds an owning ref to us
406 mProxyInfo
= nullptr;
411 nsCString mPACString
;
414 uint32_t mResolveFlags
;
416 nsProtocolProxyService
*mPPS
;
417 nsCOMPtr
<nsIProtocolProxyService
> mXPComPPS
;
418 nsCOMPtr
<nsIChannel
> mChannel
;
419 nsCOMPtr
<nsIProtocolProxyCallback
> mCallback
;
420 nsCOMPtr
<nsIProxyInfo
> mProxyInfo
;
422 RefPtr
<AsyncApplyFilters
> mAsyncFilterApplier
;
425 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest
, nsICancelable
, nsIRunnable
)
427 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest::AsyncApplyFilters
,
428 nsIProxyProtocolFilterResult
, nsICancelable
, nsIRunnable
)
430 nsAsyncResolveRequest::AsyncApplyFilters::AsyncApplyFilters(
431 nsProtocolInfo
&aInfo
, Callback
const &aCallback
)
433 mCallback(aCallback
),
435 mProcessingInLoop(false),
436 mFilterCalledBack(false) {
437 LOG(("AsyncApplyFilters %p", this));
440 nsAsyncResolveRequest::AsyncApplyFilters::~AsyncApplyFilters() {
441 LOG(("~AsyncApplyFilters %p", this));
443 MOZ_ASSERT(!mRequest
);
444 MOZ_ASSERT(!mProxyInfo
);
445 MOZ_ASSERT(!mFiltersCopy
.Length());
448 nsresult
nsAsyncResolveRequest::AsyncApplyFilters::AsyncProcess(
449 nsAsyncResolveRequest
*aRequest
) {
450 LOG(("AsyncApplyFilters::AsyncProcess %p for req %p", this, aRequest
));
452 MOZ_ASSERT(!mRequest
, "AsyncApplyFilters started more than once!");
454 if (!(mInfo
.flags
& nsIProtocolHandler::ALLOWS_PROXY
)) {
455 // Calling the callback directly (not via Finish()) since we
456 // don't want to prune.
457 return mCallback(aRequest
, aRequest
->mProxyInfo
, false);
460 mProcessingThread
= NS_GetCurrentThread();
463 mProxyInfo
= aRequest
->mProxyInfo
;
465 aRequest
->mPPS
->CopyFilters(mFiltersCopy
);
467 // We want to give filters a chance to process in a single loop to prevent
468 // any current-thread dispatch delays when those are not needed.
469 // This code is rather "loopy" than "recursive" to prevent long stack traces.
471 MOZ_ASSERT(!mProcessingInLoop
);
473 mozilla::AutoRestore
<bool> restore(mProcessingInLoop
);
474 mProcessingInLoop
= true;
476 nsresult rv
= ProcessNextFilter();
480 } while (mFilterCalledBack
);
485 nsresult
nsAsyncResolveRequest::AsyncApplyFilters::ProcessNextFilter() {
486 LOG(("AsyncApplyFilters::ProcessNextFilter %p ENTER pi=%p", this,
489 RefPtr
<FilterLink
> filter
;
491 mFilterCalledBack
= false;
496 return NS_OK
; // should we let the consumer know?
499 if (mNextFilterIndex
== mFiltersCopy
.Length()) {
503 filter
= mFiltersCopy
[mNextFilterIndex
++];
505 // Loop until a call to a filter succeeded. Other option is to recurse
506 // but that would waste stack trace when a number of filters gets registered
507 // and all from some reason tend to fail.
508 // The !mFilterCalledBack part of the condition is there to protect us from
509 // calling on another filter when the current one managed to call back and
510 // then threw. We already have the result so take it and use it since
511 // the next filter will be processed by the root loop or a call to
512 // ProcessNextFilter has already been dispatched to this thread.
513 LOG((" calling filter %p pi=%p", filter
.get(), mProxyInfo
.get()));
514 } while (!mRequest
->mPPS
->ApplyFilter(filter
, mRequest
->mChannel
, mInfo
,
518 LOG(("AsyncApplyFilters::ProcessNextFilter %p LEAVE pi=%p", this,
524 nsAsyncResolveRequest::AsyncApplyFilters::OnProxyFilterResult(
525 nsIProxyInfo
*aProxyInfo
) {
526 LOG(("AsyncApplyFilters::OnProxyFilterResult %p pi=%p", this, aProxyInfo
));
528 MOZ_ASSERT(mProcessingThread
&& mProcessingThread
->IsOnCurrentThread());
529 MOZ_ASSERT(!mFilterCalledBack
);
531 if (mFilterCalledBack
) {
532 LOG((" duplicate notification?"));
536 mFilterCalledBack
= true;
537 mProxyInfo
= aProxyInfo
;
539 if (mProcessingInLoop
) {
540 // No need to call/dispatch ProcessNextFilter(), we are in a control
541 // loop that will do this for us and save recursion/dispatching.
542 LOG((" in a root loop"));
552 if (mNextFilterIndex
== mFiltersCopy
.Length()) {
553 // We are done, all filters have been called on!
558 // Redispatch, since we don't want long stacks when filters respond
560 LOG((" redispatching"));
561 NS_DispatchToCurrentThread(this);
566 nsAsyncResolveRequest::AsyncApplyFilters::Run() {
567 LOG(("AsyncApplyFilters::Run %p", this));
569 MOZ_ASSERT(mProcessingThread
&& mProcessingThread
->IsOnCurrentThread());
575 nsresult
nsAsyncResolveRequest::AsyncApplyFilters::Finish() {
576 LOG(("AsyncApplyFilters::Finish %p pi=%p", this, mProxyInfo
.get()));
578 MOZ_ASSERT(mRequest
);
580 mFiltersCopy
.Clear();
582 RefPtr
<nsAsyncResolveRequest
> request
;
583 request
.swap(mRequest
);
585 nsCOMPtr
<nsIProxyInfo
> pi
;
588 request
->mPPS
->PruneProxyInfo(mInfo
, pi
);
589 return mCallback(request
, pi
, !mProcessingInLoop
);
593 nsAsyncResolveRequest::AsyncApplyFilters::Cancel(nsresult reason
) {
594 LOG(("AsyncApplyFilters::Cancel %p", this));
596 MOZ_ASSERT(mProcessingThread
&& mProcessingThread
->IsOnCurrentThread());
598 // This will be called only from inside the request, so don't call
599 // its's callback. Dropping the members means we simply break the cycle.
600 mFiltersCopy
.Clear();
601 mProxyInfo
= nullptr;
607 // Bug 1366133: make GetPACURI off-main-thread since it may hang on Windows
609 class AsyncGetPACURIRequest final
: public nsIRunnable
{
611 NS_DECL_THREADSAFE_ISUPPORTS
613 using CallbackFunc
= nsresult (nsProtocolProxyService::*)(bool, bool,
617 AsyncGetPACURIRequest(nsProtocolProxyService
*aService
,
618 CallbackFunc aCallback
,
619 nsISystemProxySettings
*aSystemProxySettings
,
620 bool aMainThreadOnly
, bool aForceReload
,
621 bool aResetPACThread
)
622 : mIsMainThreadOnly(aMainThreadOnly
),
624 mServiceHolder(do_QueryObject(aService
)),
625 mCallback(aCallback
),
626 mSystemProxySettings(aSystemProxySettings
),
627 mForceReload(aForceReload
),
628 mResetPACThread(aResetPACThread
) {
629 MOZ_ASSERT(NS_IsMainThread());
630 Unused
<< mIsMainThreadOnly
;
633 NS_IMETHOD
Run() override
{
634 MOZ_ASSERT(NS_IsMainThread() == mIsMainThreadOnly
);
637 nsresult rv
= mSystemProxySettings
->GetPACURI(pacUri
);
639 nsCOMPtr
<nsIRunnable
> event
=
640 NewNonOwningCancelableRunnableMethod
<bool, bool, nsresult
, nsCString
>(
641 "AsyncGetPACURIRequestCallback", mService
, mCallback
, mForceReload
,
642 mResetPACThread
, rv
, pacUri
);
644 return NS_DispatchToMainThread(event
);
648 ~AsyncGetPACURIRequest() {
649 NS_ReleaseOnMainThreadSystemGroup("AsyncGetPACURIRequest::mServiceHolder",
650 mServiceHolder
.forget());
653 bool mIsMainThreadOnly
;
655 nsProtocolProxyService
*mService
; // ref-count is hold by mServiceHolder
656 nsCOMPtr
<nsIProtocolProxyService2
> mServiceHolder
;
657 CallbackFunc mCallback
;
658 nsCOMPtr
<nsISystemProxySettings
> mSystemProxySettings
;
661 bool mResetPACThread
;
664 NS_IMPL_ISUPPORTS(AsyncGetPACURIRequest
, nsIRunnable
)
666 //----------------------------------------------------------------------------
669 // apply mask to address (zeros out excluded bits).
671 // NOTE: we do the byte swapping here to minimize overall swapping.
673 static void proxy_MaskIPv6Addr(PRIPv6Addr
&addr
, uint16_t mask_len
) {
674 if (mask_len
== 128) return;
677 addr
.pr_s6_addr32
[3] =
678 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[3]) & (~0L << (128 - mask_len
)));
679 } else if (mask_len
> 64) {
680 addr
.pr_s6_addr32
[3] = 0;
681 addr
.pr_s6_addr32
[2] =
682 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[2]) & (~0L << (96 - mask_len
)));
683 } else if (mask_len
> 32) {
684 addr
.pr_s6_addr32
[3] = 0;
685 addr
.pr_s6_addr32
[2] = 0;
686 addr
.pr_s6_addr32
[1] =
687 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[1]) & (~0L << (64 - mask_len
)));
689 addr
.pr_s6_addr32
[3] = 0;
690 addr
.pr_s6_addr32
[2] = 0;
691 addr
.pr_s6_addr32
[1] = 0;
692 addr
.pr_s6_addr32
[0] =
693 PR_htonl(PR_ntohl(addr
.pr_s6_addr32
[0]) & (~0L << (32 - mask_len
)));
697 static void proxy_GetStringPref(nsIPrefBranch
*aPrefBranch
, const char *aPref
,
698 nsCString
&aResult
) {
700 nsresult rv
= aPrefBranch
->GetCharPref(aPref
, temp
);
704 aResult
.Assign(temp
);
705 // all of our string prefs are hostnames, so we should remove any
706 // whitespace characters that the user might have unknowingly entered.
707 aResult
.StripWhitespace();
711 static void proxy_GetIntPref(nsIPrefBranch
*aPrefBranch
, const char *aPref
,
714 nsresult rv
= aPrefBranch
->GetIntPref(aPref
, &temp
);
721 static void proxy_GetBoolPref(nsIPrefBranch
*aPrefBranch
, const char *aPref
,
724 nsresult rv
= aPrefBranch
->GetBoolPref(aPref
, &temp
);
731 //----------------------------------------------------------------------------
733 static const int32_t PROXYCONFIG_DIRECT4X
= 3;
734 static const int32_t PROXYCONFIG_COUNT
= 6;
736 NS_IMPL_ADDREF(nsProtocolProxyService
)
737 NS_IMPL_RELEASE(nsProtocolProxyService
)
738 NS_IMPL_CLASSINFO(nsProtocolProxyService
, nullptr, nsIClassInfo::SINGLETON
,
739 NS_PROTOCOLPROXYSERVICE_CID
)
741 // NS_IMPL_QUERY_INTERFACE_CI with the nsProtocolProxyService QI change
742 NS_INTERFACE_MAP_BEGIN(nsProtocolProxyService
)
743 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService
)
744 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService2
)
745 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
746 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsProtocolProxyService
)
747 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIProtocolProxyService
)
748 NS_IMPL_QUERY_CLASSINFO(nsProtocolProxyService
)
751 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService
, nsIProtocolProxyService
,
752 nsIProtocolProxyService2
)
754 nsProtocolProxyService::nsProtocolProxyService()
755 : mFilterLocalHosts(false),
756 mProxyConfig(PROXYCONFIG_DIRECT
),
761 mSOCKSProxyVersion(4),
762 mSOCKSProxyRemoteDNS(false),
764 mWPADOverDHCPEnabled(false),
766 mSessionStart(PR_Now()),
767 mFailedProxyTimeout(30 * 60) // 30 minute default
769 mIsShutdown(false) {}
771 nsProtocolProxyService::~nsProtocolProxyService() {
772 // These should have been cleaned up in our Observe method.
773 NS_ASSERTION(mHostFiltersArray
.Length() == 0 && mFilters
.Length() == 0 &&
775 "what happened to xpcom-shutdown?");
778 // nsProtocolProxyService methods
779 nsresult
nsProtocolProxyService::Init() {
780 mProxySettingTarget
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
782 // failure to access prefs is non-fatal
783 nsCOMPtr
<nsIPrefBranch
> prefBranch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
785 // monitor proxy prefs
786 prefBranch
->AddObserver(PROXY_PREF_BRANCH
, this, false);
789 PrefsChanged(prefBranch
, nullptr);
792 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
794 // register for shutdown notification so we can clean ourselves up
796 obs
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
797 obs
->AddObserver(this, NS_NETWORK_LINK_TOPIC
, false);
803 // ReloadNetworkPAC() checks if there's a non-networked PAC in use then avoids
804 // to call ReloadPAC()
805 nsresult
nsProtocolProxyService::ReloadNetworkPAC() {
806 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
812 nsresult rv
= prefs
->GetIntPref(PROXY_PREF("type"), &type
);
817 if (type
== PROXYCONFIG_PAC
) {
818 nsAutoCString pacSpec
;
819 prefs
->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec
);
820 if (!pacSpec
.IsEmpty()) {
821 nsCOMPtr
<nsIURI
> pacURI
;
822 rv
= NS_NewURI(getter_AddRefs(pacURI
), pacSpec
);
823 if (!NS_SUCCEEDED(rv
)) {
828 rv
= GetProtocolInfo(pacURI
, &pac
);
829 if (!NS_SUCCEEDED(rv
)) {
833 if (!pac
.scheme
.EqualsLiteral("file") &&
834 !pac
.scheme
.EqualsLiteral("data")) {
835 LOG((": received network changed event, reload PAC"));
839 } else if ((type
== PROXYCONFIG_WPAD
) || (type
== PROXYCONFIG_SYSTEM
)) {
846 nsresult
nsProtocolProxyService::AsyncConfigureFromPAC(bool aForceReload
,
847 bool aResetPACThread
) {
848 MOZ_ASSERT(NS_IsMainThread());
851 nsresult rv
= mSystemProxySettings
->GetMainThreadOnly(&mainThreadOnly
);
852 if (NS_WARN_IF(NS_FAILED(rv
))) {
856 nsCOMPtr
<nsIRunnable
> req
= new AsyncGetPACURIRequest(
857 this, &nsProtocolProxyService::OnAsyncGetPACURI
, mSystemProxySettings
,
858 mainThreadOnly
, aForceReload
, aResetPACThread
);
860 if (mainThreadOnly
) {
864 if (NS_WARN_IF(!mProxySettingTarget
)) {
865 return NS_ERROR_NOT_INITIALIZED
;
867 return mProxySettingTarget
->Dispatch(req
, nsIEventTarget::DISPATCH_NORMAL
);
870 nsresult
nsProtocolProxyService::OnAsyncGetPACURI(bool aForceReload
,
871 bool aResetPACThread
,
873 const nsACString
&aUri
) {
874 MOZ_ASSERT(NS_IsMainThread());
876 if (aResetPACThread
) {
880 if (NS_SUCCEEDED(aResult
) && !aUri
.IsEmpty()) {
881 ConfigureFromPAC(PromiseFlatCString(aUri
), aForceReload
);
888 nsProtocolProxyService::Observe(nsISupports
*aSubject
, const char *aTopic
,
889 const char16_t
*aData
) {
890 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
893 mHostFiltersArray
.Clear();
901 if (mProxySettingTarget
) {
902 mProxySettingTarget
= nullptr;
905 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
907 obs
->RemoveObserver(this, NS_NETWORK_LINK_TOPIC
);
908 obs
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
911 } else if (strcmp(aTopic
, NS_NETWORK_LINK_TOPIC
) == 0) {
912 nsCString converted
= NS_ConvertUTF16toUTF8(aData
);
913 const char *state
= converted
.get();
914 if (!strcmp(state
, NS_NETWORK_LINK_DATA_CHANGED
)) {
918 NS_ASSERTION(strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
) == 0,
919 "what is this random observer event?");
920 nsCOMPtr
<nsIPrefBranch
> prefs
= do_QueryInterface(aSubject
);
921 if (prefs
) PrefsChanged(prefs
, NS_LossyConvertUTF16toASCII(aData
).get());
926 void nsProtocolProxyService::PrefsChanged(nsIPrefBranch
*prefBranch
,
929 bool reloadPAC
= false;
930 nsAutoCString tempString
;
932 if (!pref
|| !strcmp(pref
, PROXY_PREF("type"))) {
934 rv
= prefBranch
->GetIntPref(PROXY_PREF("type"), &type
);
935 if (NS_SUCCEEDED(rv
)) {
936 // bug 115720 - for ns4.x backwards compatibility
937 if (type
== PROXYCONFIG_DIRECT4X
) {
938 type
= PROXYCONFIG_DIRECT
;
939 // Reset the type so that the dialog looks correct, and we
940 // don't have to handle this case everywhere else
941 // I'm paranoid about a loop of some sort - only do this
942 // if we're enumerating all prefs, and ignore any error
943 if (!pref
) prefBranch
->SetIntPref(PROXY_PREF("type"), type
);
944 } else if (type
>= PROXYCONFIG_COUNT
) {
945 LOG(("unknown proxy type: %" PRId32
"; assuming direct\n", type
));
946 type
= PROXYCONFIG_DIRECT
;
952 if (mProxyConfig
== PROXYCONFIG_SYSTEM
) {
953 mSystemProxySettings
= do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID
);
954 if (!mSystemProxySettings
) mProxyConfig
= PROXYCONFIG_DIRECT
;
957 if (mSystemProxySettings
) {
958 mSystemProxySettings
= nullptr;
964 if (!pref
|| !strcmp(pref
, PROXY_PREF("http")))
965 proxy_GetStringPref(prefBranch
, PROXY_PREF("http"), mHTTPProxyHost
);
967 if (!pref
|| !strcmp(pref
, PROXY_PREF("http_port")))
968 proxy_GetIntPref(prefBranch
, PROXY_PREF("http_port"), mHTTPProxyPort
);
970 if (!pref
|| !strcmp(pref
, PROXY_PREF("ssl")))
971 proxy_GetStringPref(prefBranch
, PROXY_PREF("ssl"), mHTTPSProxyHost
);
973 if (!pref
|| !strcmp(pref
, PROXY_PREF("ssl_port")))
974 proxy_GetIntPref(prefBranch
, PROXY_PREF("ssl_port"), mHTTPSProxyPort
);
976 if (!pref
|| !strcmp(pref
, PROXY_PREF("ftp")))
977 proxy_GetStringPref(prefBranch
, PROXY_PREF("ftp"), mFTPProxyHost
);
979 if (!pref
|| !strcmp(pref
, PROXY_PREF("ftp_port")))
980 proxy_GetIntPref(prefBranch
, PROXY_PREF("ftp_port"), mFTPProxyPort
);
982 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks")))
983 proxy_GetStringPref(prefBranch
, PROXY_PREF("socks"), mSOCKSProxyTarget
);
985 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_port")))
986 proxy_GetIntPref(prefBranch
, PROXY_PREF("socks_port"), mSOCKSProxyPort
);
988 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_version"))) {
990 proxy_GetIntPref(prefBranch
, PROXY_PREF("socks_version"), version
);
991 // make sure this preference value remains sane
993 mSOCKSProxyVersion
= 5;
995 mSOCKSProxyVersion
= 4;
998 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_remote_dns")))
999 proxy_GetBoolPref(prefBranch
, PROXY_PREF("socks_remote_dns"),
1000 mSOCKSProxyRemoteDNS
);
1002 if (!pref
|| !strcmp(pref
, PROXY_PREF("proxy_over_tls"))) {
1003 proxy_GetBoolPref(prefBranch
, PROXY_PREF("proxy_over_tls"), mProxyOverTLS
);
1006 if (!pref
|| !strcmp(pref
, PROXY_PREF("enable_wpad_over_dhcp"))) {
1007 proxy_GetBoolPref(prefBranch
, PROXY_PREF("enable_wpad_over_dhcp"),
1008 mWPADOverDHCPEnabled
);
1009 reloadPAC
= reloadPAC
|| mProxyConfig
== PROXYCONFIG_WPAD
;
1012 if (!pref
|| !strcmp(pref
, PROXY_PREF("failover_timeout")))
1013 proxy_GetIntPref(prefBranch
, PROXY_PREF("failover_timeout"),
1014 mFailedProxyTimeout
);
1016 if (!pref
|| !strcmp(pref
, PROXY_PREF("no_proxies_on"))) {
1017 rv
= prefBranch
->GetCharPref(PROXY_PREF("no_proxies_on"), tempString
);
1018 if (NS_SUCCEEDED(rv
)) LoadHostFilters(tempString
);
1021 // We're done if not using something that could give us a PAC URL
1022 // (PAC, WPAD or System)
1023 if (mProxyConfig
!= PROXYCONFIG_PAC
&& mProxyConfig
!= PROXYCONFIG_WPAD
&&
1024 mProxyConfig
!= PROXYCONFIG_SYSTEM
)
1027 // OK, we need to reload the PAC file if:
1028 // 1) network.proxy.type changed, or
1029 // 2) network.proxy.autoconfig_url changed and PAC is configured
1031 if (!pref
|| !strcmp(pref
, PROXY_PREF("autoconfig_url"))) reloadPAC
= true;
1034 tempString
.Truncate();
1035 if (mProxyConfig
== PROXYCONFIG_PAC
) {
1036 prefBranch
->GetCharPref(PROXY_PREF("autoconfig_url"), tempString
);
1037 if (mPACMan
&& !mPACMan
->IsPACURI(tempString
)) {
1038 LOG(("PAC Thread URI Changed - Reset Pac Thread"));
1041 } else if (mProxyConfig
== PROXYCONFIG_WPAD
) {
1042 LOG(("Auto-detecting proxy - Reset Pac Thread"));
1044 } else if (mSystemProxySettings
) {
1045 // Get System Proxy settings if available
1046 AsyncConfigureFromPAC(false, false);
1048 if (!tempString
.IsEmpty() || mProxyConfig
== PROXYCONFIG_WPAD
) {
1049 ConfigureFromPAC(tempString
, false);
1054 bool nsProtocolProxyService::CanUseProxy(nsIURI
*aURI
, int32_t defaultPort
) {
1055 if (mHostFiltersArray
.Length() == 0 && !mFilterLocalHosts
) return true;
1060 nsresult rv
= aURI
->GetAsciiHost(host
);
1061 if (NS_FAILED(rv
) || host
.IsEmpty()) return false;
1063 rv
= aURI
->GetPort(&port
);
1064 if (NS_FAILED(rv
)) return false;
1065 if (port
== -1) port
= defaultPort
;
1068 bool is_ipaddr
= (PR_StringToNetAddr(host
.get(), &addr
) == PR_SUCCESS
);
1072 // convert parsed address to IPv6
1073 if (addr
.raw
.family
== PR_AF_INET
) {
1074 // convert to IPv4-mapped address
1075 PR_ConvertIPv4AddrToIPv6(addr
.inet
.ip
, &ipv6
);
1076 } else if (addr
.raw
.family
== PR_AF_INET6
) {
1078 memcpy(&ipv6
, &addr
.ipv6
.ip
, sizeof(PRIPv6Addr
));
1080 NS_WARNING("unknown address family");
1081 return true; // allow proxying
1085 // Don't use proxy for local hosts (plain hostname, no dots)
1086 if ((!is_ipaddr
&& mFilterLocalHosts
&& !host
.Contains('.')) ||
1087 host
.EqualsLiteral("127.0.0.1") || host
.EqualsLiteral("::1")) {
1088 LOG(("Not using proxy for this local host [%s]!\n", host
.get()));
1089 return false; // don't allow proxying
1093 while (++index
< int32_t(mHostFiltersArray
.Length())) {
1094 HostInfo
*hinfo
= mHostFiltersArray
[index
];
1096 if (is_ipaddr
!= hinfo
->is_ipaddr
) continue;
1097 if (hinfo
->port
&& hinfo
->port
!= port
) continue;
1100 // generate masked version of target IPv6 address
1102 memcpy(&masked
, &ipv6
, sizeof(PRIPv6Addr
));
1103 proxy_MaskIPv6Addr(masked
, hinfo
->ip
.mask_len
);
1105 // check for a match
1106 if (memcmp(&masked
, &hinfo
->ip
.addr
, sizeof(PRIPv6Addr
)) == 0)
1107 return false; // proxy disallowed
1109 uint32_t host_len
= host
.Length();
1110 uint32_t filter_host_len
= hinfo
->name
.host_len
;
1112 if (host_len
>= filter_host_len
) {
1114 // compare last |filter_host_len| bytes of target hostname.
1116 const char *host_tail
= host
.get() + host_len
- filter_host_len
;
1117 if (!PL_strncasecmp(host_tail
, hinfo
->name
.host
, filter_host_len
)) {
1118 // If the tail of the host string matches the filter
1120 if (filter_host_len
> 0 && hinfo
->name
.host
[0] == '.') {
1121 // If the filter was of the form .foo.bar.tld, all such
1122 // matches are correct
1123 return false; // proxy disallowed
1126 // abc-def.example.org should not match def.example.org
1127 // however, *.def.example.org should match .def.example.org
1128 // We check that the filter doesn't start with a `.`. If it does,
1129 // then the strncasecmp above should suffice. If it doesn't,
1130 // then we should only consider it a match if the strncasecmp happened
1131 // at a subdomain boundary
1132 if (host_len
> filter_host_len
&& *(host_tail
- 1) == '.') {
1133 // If the host was something.foo.bar.tld and the filter
1134 // was foo.bar.tld, it's still a match.
1135 // the character right before the tail must be a
1136 // `.` for this to work
1137 return false; // proxy disallowed
1140 if (host_len
== filter_host_len
) {
1141 // If the host and filter are of the same length,
1142 // they should match
1143 return false; // proxy disallowed
1152 // kProxyType\* may be referred to externally in
1153 // nsProxyInfo in order to compare by string pointer
1154 const char kProxyType_HTTP
[] = "http";
1155 const char kProxyType_HTTPS
[] = "https";
1156 const char kProxyType_PROXY
[] = "proxy";
1157 const char kProxyType_SOCKS
[] = "socks";
1158 const char kProxyType_SOCKS4
[] = "socks4";
1159 const char kProxyType_SOCKS5
[] = "socks5";
1160 const char kProxyType_DIRECT
[] = "direct";
1162 const char *nsProtocolProxyService::ExtractProxyInfo(const char *start
,
1163 uint32_t aResolveFlags
,
1164 nsProxyInfo
**result
) {
1168 // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
1170 // find end of proxy info delimiter
1171 const char *end
= start
;
1172 while (*end
&& *end
!= ';') ++end
;
1174 // find end of proxy type delimiter
1175 const char *sp
= start
;
1176 while (sp
< end
&& *sp
!= ' ' && *sp
!= '\t') ++sp
;
1178 uint32_t len
= sp
- start
;
1179 const char *type
= nullptr;
1182 if (PL_strncasecmp(start
, kProxyType_HTTP
, 5) == 0) {
1183 type
= kProxyType_HTTP
;
1187 if (PL_strncasecmp(start
, kProxyType_PROXY
, 5) == 0) {
1188 type
= kProxyType_HTTP
;
1189 } else if (PL_strncasecmp(start
, kProxyType_SOCKS
, 5) == 0) {
1190 type
= kProxyType_SOCKS4
; // assume v4 for 4x compat
1191 } else if (PL_strncasecmp(start
, kProxyType_HTTPS
, 5) == 0) {
1192 type
= kProxyType_HTTPS
;
1196 if (PL_strncasecmp(start
, kProxyType_DIRECT
, 6) == 0)
1197 type
= kProxyType_DIRECT
;
1198 else if (PL_strncasecmp(start
, kProxyType_SOCKS4
, 6) == 0)
1199 type
= kProxyType_SOCKS4
;
1200 else if (PL_strncasecmp(start
, kProxyType_SOCKS5
, 6) == 0)
1201 // map "SOCKS5" to "socks" to match contract-id of registered
1202 // SOCKS-v5 socket provider.
1203 type
= kProxyType_SOCKS
;
1207 const char *host
= nullptr, *hostEnd
= nullptr;
1210 // If it's a SOCKS5 proxy, do name resolution on the server side.
1211 // We could use this with SOCKS4a servers too, but they might not
1213 if (type
== kProxyType_SOCKS
|| mSOCKSProxyRemoteDNS
)
1214 flags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
1216 // extract host:port
1218 while ((*start
== ' ' || *start
== '\t') && start
< end
) start
++;
1221 if (type
== kProxyType_HTTP
) {
1223 } else if (type
== kProxyType_HTTPS
) {
1229 nsProxyInfo
*pi
= new nsProxyInfo();
1232 pi
->mResolveFlags
= aResolveFlags
;
1233 pi
->mTimeout
= mFailedProxyTimeout
;
1235 // www.foo.com:8080 and http://www.foo.com:8080
1236 nsDependentCSubstring
maybeURL(start
, end
- start
);
1237 nsCOMPtr
<nsIURI
> pacURI
;
1239 nsAutoCString urlHost
;
1240 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI
), maybeURL
)) &&
1241 NS_SUCCEEDED(pacURI
->GetAsciiHost(urlHost
)) && !urlHost
.IsEmpty()) {
1242 // http://www.example.com:8080
1244 pi
->mHost
= urlHost
;
1247 if (NS_SUCCEEDED(pacURI
->GetPort(&tPort
)) && tPort
!= -1) {
1252 // www.example.com:8080
1255 hostEnd
= strchr(host
, ':');
1256 if (!hostEnd
|| hostEnd
> end
) {
1258 // no port, so assume default
1260 port
= atoi(hostEnd
+ 1);
1263 // YES, it is ok to specify a null proxy host.
1265 pi
->mHost
.Assign(host
, hostEnd
- host
);
1269 NS_ADDREF(*result
= pi
);
1272 while (*end
== ';' || *end
== ' ' || *end
== '\t') ++end
;
1276 void nsProtocolProxyService::GetProxyKey(nsProxyInfo
*pi
, nsCString
&key
) {
1277 key
.AssignASCII(pi
->mType
);
1278 if (!pi
->mHost
.IsEmpty()) {
1280 key
.Append(pi
->mHost
);
1282 key
.AppendInt(pi
->mPort
);
1286 uint32_t nsProtocolProxyService::SecondsSinceSessionStart() {
1287 PRTime now
= PR_Now();
1289 // get time elapsed since session start
1290 int64_t diff
= now
- mSessionStart
;
1292 // convert microseconds to seconds
1293 diff
/= PR_USEC_PER_SEC
;
1295 // return converted 32 bit value
1296 return uint32_t(diff
);
1299 void nsProtocolProxyService::EnableProxy(nsProxyInfo
*pi
) {
1301 GetProxyKey(pi
, key
);
1302 mFailedProxies
.Remove(key
);
1305 void nsProtocolProxyService::DisableProxy(nsProxyInfo
*pi
) {
1307 GetProxyKey(pi
, key
);
1309 uint32_t dsec
= SecondsSinceSessionStart();
1311 // Add timeout to interval (this is the time when the proxy can
1313 dsec
+= pi
->mTimeout
;
1315 // NOTE: The classic codebase would increase the timeout value
1316 // incrementally each time a subsequent failure occurred.
1317 // We could do the same, but it would require that we not
1318 // remove proxy entries in IsProxyDisabled or otherwise
1319 // change the way we are recording disabled proxies.
1320 // Simpler is probably better for now, and at least the
1321 // user can tune the timeout setting via preferences.
1323 LOG(("DisableProxy %s %d\n", key
.get(), dsec
));
1325 // If this fails, oh well... means we don't have enough memory
1326 // to remember the failed proxy.
1327 mFailedProxies
.Put(key
, dsec
);
1330 bool nsProtocolProxyService::IsProxyDisabled(nsProxyInfo
*pi
) {
1332 GetProxyKey(pi
, key
);
1335 if (!mFailedProxies
.Get(key
, &val
)) return false;
1337 uint32_t dsec
= SecondsSinceSessionStart();
1339 // if time passed has exceeded interval, then try proxy again.
1341 mFailedProxies
.Remove(key
);
1348 nsresult
nsProtocolProxyService::SetupPACThread(
1349 nsIEventTarget
*mainThreadEventTarget
) {
1351 return NS_ERROR_FAILURE
;
1354 if (mPACMan
) return NS_OK
;
1356 mPACMan
= new nsPACMan(mainThreadEventTarget
);
1358 bool mainThreadOnly
;
1360 if (mSystemProxySettings
&&
1361 NS_SUCCEEDED(mSystemProxySettings
->GetMainThreadOnly(&mainThreadOnly
)) &&
1363 rv
= mPACMan
->Init(mSystemProxySettings
);
1365 rv
= mPACMan
->Init(nullptr);
1367 if (NS_FAILED(rv
)) {
1368 mPACMan
->Shutdown();
1374 nsresult
nsProtocolProxyService::ResetPACThread() {
1375 if (!mPACMan
) return NS_OK
;
1377 mPACMan
->Shutdown();
1379 return SetupPACThread();
1382 nsresult
nsProtocolProxyService::ConfigureFromPAC(const nsCString
&spec
,
1384 nsresult rv
= SetupPACThread();
1385 NS_ENSURE_SUCCESS(rv
, rv
);
1387 bool autodetect
= spec
.IsEmpty();
1388 if (!forceReload
&& ((!autodetect
&& mPACMan
->IsPACURI(spec
)) ||
1389 (autodetect
&& mPACMan
->IsUsingWPAD()))) {
1393 mFailedProxies
.Clear();
1395 mPACMan
->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled
);
1396 return mPACMan
->LoadPACFromURI(spec
);
1399 void nsProtocolProxyService::ProcessPACString(const nsCString
&pacString
,
1400 uint32_t aResolveFlags
,
1401 nsIProxyInfo
**result
) {
1402 if (pacString
.IsEmpty()) {
1407 const char *proxies
= pacString
.get();
1409 nsProxyInfo
*pi
= nullptr, *first
= nullptr, *last
= nullptr;
1411 proxies
= ExtractProxyInfo(proxies
, aResolveFlags
, &pi
);
1412 if (pi
&& (pi
->mType
== kProxyType_HTTPS
) && !mProxyOverTLS
) {
1419 NS_ASSERTION(last
->mNext
== nullptr, "leaking nsProxyInfo");
1429 // nsIProtocolProxyService2
1431 nsProtocolProxyService::ReloadPAC() {
1432 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
1433 if (!prefs
) return NS_OK
;
1436 nsresult rv
= prefs
->GetIntPref(PROXY_PREF("type"), &type
);
1437 if (NS_FAILED(rv
)) return NS_OK
;
1439 nsAutoCString pacSpec
;
1440 if (type
== PROXYCONFIG_PAC
)
1441 prefs
->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec
);
1442 else if (type
== PROXYCONFIG_SYSTEM
) {
1443 if (mSystemProxySettings
) {
1444 AsyncConfigureFromPAC(true, true);
1450 if (!pacSpec
.IsEmpty() || type
== PROXYCONFIG_WPAD
)
1451 ConfigureFromPAC(pacSpec
, true);
1455 // When sync interface is removed this can go away too
1456 // The nsPACManCallback portion of this implementation should be run
1457 // off the main thread, because it uses a condvar for signaling and
1458 // the main thread is blocking on that condvar -
1459 // so call nsPACMan::AsyncGetProxyForURI() with
1460 // a false mainThreadResponse parameter.
1461 class nsAsyncBridgeRequest final
: public nsPACManCallback
{
1462 NS_DECL_THREADSAFE_ISUPPORTS
1464 nsAsyncBridgeRequest()
1465 : mMutex("nsDeprecatedCallback"),
1466 mCondVar(mMutex
, "nsDeprecatedCallback"),
1468 mCompleted(false) {}
1470 void OnQueryComplete(nsresult status
, const nsACString
&pacString
,
1471 const nsACString
&newPACURL
) override
{
1472 MutexAutoLock
lock(mMutex
);
1475 mPACString
= pacString
;
1476 mPACURL
= newPACURL
;
1480 void Lock() { mMutex
.Lock(); }
1481 void Unlock() { mMutex
.Unlock(); }
1482 void Wait() { mCondVar
.Wait(TimeDuration::FromSeconds(3)); }
1485 ~nsAsyncBridgeRequest() = default;
1487 friend class nsProtocolProxyService
;
1493 nsCString mPACString
;
1497 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest
)
1499 nsresult
nsProtocolProxyService::AsyncResolveInternal(
1500 nsIChannel
*channel
, uint32_t flags
, nsIProtocolProxyCallback
*callback
,
1501 nsICancelable
**result
, bool isSyncOK
,
1502 nsIEventTarget
*mainThreadEventTarget
) {
1503 NS_ENSURE_ARG_POINTER(channel
);
1504 NS_ENSURE_ARG_POINTER(callback
);
1506 nsCOMPtr
<nsIURI
> uri
;
1507 nsresult rv
= GetProxyURI(channel
, getter_AddRefs(uri
));
1508 if (NS_FAILED(rv
)) return rv
;
1511 RefPtr
<nsAsyncResolveRequest
> ctx
=
1512 new nsAsyncResolveRequest(this, channel
, flags
, callback
);
1514 nsProtocolInfo info
;
1515 rv
= GetProtocolInfo(uri
, &info
);
1516 if (NS_FAILED(rv
)) return rv
;
1518 nsCOMPtr
<nsIProxyInfo
> pi
;
1521 // adapt to realtime changes in the system proxy service
1522 if (mProxyConfig
== PROXYCONFIG_SYSTEM
) {
1523 nsCOMPtr
<nsISystemProxySettings
> sp2
=
1524 do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID
);
1525 if (sp2
!= mSystemProxySettings
) {
1526 mSystemProxySettings
= sp2
;
1531 rv
= SetupPACThread(mainThreadEventTarget
);
1532 if (NS_FAILED(rv
)) {
1536 // SystemProxySettings and PAC files can block the main thread
1537 // but if neither of them are in use, we can just do the work
1538 // right here and directly invoke the callback
1541 Resolve_Internal(channel
, info
, flags
, &usePACThread
, getter_AddRefs(pi
));
1542 if (NS_FAILED(rv
)) return rv
;
1544 if (!usePACThread
|| !mPACMan
) {
1545 // we can do it locally
1546 rv
= ctx
->ProcessLocally(info
, pi
, isSyncOK
);
1547 if (NS_SUCCEEDED(rv
) && !isSyncOK
) {
1553 // else kick off a PAC thread query
1555 rv
= mPACMan
->AsyncGetProxyForURI(uri
, ctx
, true);
1556 if (NS_SUCCEEDED(rv
)) ctx
.forget(result
);
1560 // nsIProtocolProxyService
1562 nsProtocolProxyService::AsyncResolve2(nsIChannel
*channel
, uint32_t flags
,
1563 nsIProtocolProxyCallback
*callback
,
1564 nsIEventTarget
*mainThreadEventTarget
,
1565 nsICancelable
**result
) {
1566 return AsyncResolveInternal(channel
, flags
, callback
, result
, true,
1567 mainThreadEventTarget
);
1571 nsProtocolProxyService::AsyncResolve(nsISupports
*channelOrURI
, uint32_t flags
,
1572 nsIProtocolProxyCallback
*callback
,
1573 nsIEventTarget
*mainThreadEventTarget
,
1574 nsICancelable
**result
) {
1576 // Check if we got a channel:
1577 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(channelOrURI
);
1579 nsCOMPtr
<nsIURI
> uri
= do_QueryInterface(channelOrURI
);
1581 return NS_ERROR_NO_INTERFACE
;
1584 // creating a temporary channel from the URI which is not
1585 // used to perform any network loads, hence its safe to
1586 // use systemPrincipal as the loadingPrincipal.
1587 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
,
1588 nsContentUtils::GetSystemPrincipal(),
1589 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
,
1590 nsIContentPolicy::TYPE_OTHER
);
1591 NS_ENSURE_SUCCESS(rv
, rv
);
1594 return AsyncResolveInternal(channel
, flags
, callback
, result
, false,
1595 mainThreadEventTarget
);
1599 nsProtocolProxyService::NewProxyInfo(const nsACString
&aType
,
1600 const nsACString
&aHost
, int32_t aPort
,
1601 uint32_t aFlags
, uint32_t aFailoverTimeout
,
1602 nsIProxyInfo
*aFailoverProxy
,
1603 nsIProxyInfo
**aResult
) {
1604 return NewProxyInfoWithAuth(aType
, aHost
, aPort
, EmptyCString(),
1605 EmptyCString(), aFlags
, aFailoverTimeout
,
1606 aFailoverProxy
, aResult
);
1610 nsProtocolProxyService::NewProxyInfoWithAuth(
1611 const nsACString
&aType
, const nsACString
&aHost
, int32_t aPort
,
1612 const nsACString
&aUsername
, const nsACString
&aPassword
, uint32_t aFlags
,
1613 uint32_t aFailoverTimeout
, nsIProxyInfo
*aFailoverProxy
,
1614 nsIProxyInfo
**aResult
) {
1615 static const char *types
[] = {kProxyType_HTTP
, kProxyType_HTTPS
,
1616 kProxyType_SOCKS
, kProxyType_SOCKS4
,
1619 // resolve type; this allows us to avoid copying the type string into each
1620 // proxy info instance. we just reference the string literals directly :)
1621 const char *type
= nullptr;
1622 for (auto &t
: types
) {
1623 if (aType
.LowerCaseEqualsASCII(t
)) {
1628 NS_ENSURE_TRUE(type
, NS_ERROR_INVALID_ARG
);
1630 // We have only implemented username/password for SOCKS proxies.
1631 if ((!aUsername
.IsEmpty() || !aPassword
.IsEmpty()) &&
1632 !aType
.LowerCaseEqualsASCII(kProxyType_SOCKS
) &&
1633 !aType
.LowerCaseEqualsASCII(kProxyType_SOCKS4
)) {
1634 return NS_ERROR_NOT_IMPLEMENTED
;
1637 return NewProxyInfo_Internal(type
, aHost
, aPort
, aUsername
, aPassword
, aFlags
,
1638 aFailoverTimeout
, aFailoverProxy
, 0, aResult
);
1642 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo
*aProxy
, nsIURI
*aURI
,
1644 nsIProxyInfo
**aResult
) {
1645 // We only support failover when a PAC file is configured, either
1646 // directly or via system settings
1647 if (mProxyConfig
!= PROXYCONFIG_PAC
&& mProxyConfig
!= PROXYCONFIG_WPAD
&&
1648 mProxyConfig
!= PROXYCONFIG_SYSTEM
)
1649 return NS_ERROR_NOT_AVAILABLE
;
1651 // Verify that |aProxy| is one of our nsProxyInfo objects.
1652 nsCOMPtr
<nsProxyInfo
> pi
= do_QueryInterface(aProxy
);
1654 // OK, the QI checked out. We can proceed.
1656 // Remember that this proxy is down.
1659 // NOTE: At this point, we might want to prompt the user if we have
1660 // not already tried going DIRECT. This is something that the
1661 // classic codebase supported; however, IE6 does not prompt.
1663 if (!pi
->mNext
) return NS_ERROR_NOT_AVAILABLE
;
1665 LOG(("PAC failover from %s %s:%d to %s %s:%d\n", pi
->mType
, pi
->mHost
.get(),
1666 pi
->mPort
, pi
->mNext
->mType
, pi
->mNext
->mHost
.get(), pi
->mNext
->mPort
));
1668 NS_ADDREF(*aResult
= pi
->mNext
);
1674 class ProxyFilterPositionComparator
{
1675 typedef RefPtr
<nsProtocolProxyService::FilterLink
> FilterLinkRef
;
1678 bool Equals(const FilterLinkRef
&a
, const FilterLinkRef
&b
) const {
1679 return a
->position
== b
->position
;
1681 bool LessThan(const FilterLinkRef
&a
, const FilterLinkRef
&b
) const {
1682 return a
->position
< b
->position
;
1686 class ProxyFilterObjectComparator
{
1687 typedef RefPtr
<nsProtocolProxyService::FilterLink
> FilterLinkRef
;
1690 bool Equals(const FilterLinkRef
&link
, const nsISupports
*obj
) const {
1691 return obj
== nsCOMPtr
<nsISupports
>(do_QueryInterface(link
->filter
)) ||
1692 obj
== nsCOMPtr
<nsISupports
>(do_QueryInterface(link
->channelFilter
));
1698 nsresult
nsProtocolProxyService::InsertFilterLink(RefPtr
<FilterLink
> &&link
) {
1699 LOG(("nsProtocolProxyService::InsertFilterLink filter=%p", link
.get()));
1702 return NS_ERROR_FAILURE
;
1705 mFilters
.AppendElement(link
);
1706 mFilters
.Sort(ProxyFilterPositionComparator());
1711 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter
*filter
,
1712 uint32_t position
) {
1713 UnregisterFilter(filter
); // remove this filter if we already have it
1715 RefPtr
<FilterLink
> link
= new FilterLink(position
, filter
);
1716 return InsertFilterLink(std::move(link
));
1720 nsProtocolProxyService::RegisterChannelFilter(
1721 nsIProtocolProxyChannelFilter
*channelFilter
, uint32_t position
) {
1722 UnregisterChannelFilter(
1723 channelFilter
); // remove this filter if we already have it
1725 RefPtr
<FilterLink
> link
= new FilterLink(position
, channelFilter
);
1726 return InsertFilterLink(std::move(link
));
1729 nsresult
nsProtocolProxyService::RemoveFilterLink(nsISupports
*givenObject
) {
1730 LOG(("nsProtocolProxyService::RemoveFilterLink target=%p", givenObject
));
1732 return mFilters
.RemoveElement(givenObject
, ProxyFilterObjectComparator())
1734 : NS_ERROR_UNEXPECTED
;
1738 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter
*filter
) {
1739 // QI to nsISupports so we can safely test object identity.
1740 nsCOMPtr
<nsISupports
> givenObject
= do_QueryInterface(filter
);
1741 return RemoveFilterLink(givenObject
);
1745 nsProtocolProxyService::UnregisterChannelFilter(
1746 nsIProtocolProxyChannelFilter
*channelFilter
) {
1747 // QI to nsISupports so we can safely test object identity.
1748 nsCOMPtr
<nsISupports
> givenObject
= do_QueryInterface(channelFilter
);
1749 return RemoveFilterLink(givenObject
);
1753 nsProtocolProxyService::GetProxyConfigType(uint32_t *aProxyConfigType
) {
1754 *aProxyConfigType
= mProxyConfig
;
1758 void nsProtocolProxyService::LoadHostFilters(const nsACString
&aFilters
) {
1763 // check to see the owners flag? /!?/ TODO
1764 if (mHostFiltersArray
.Length() > 0) {
1765 mHostFiltersArray
.Clear();
1768 if (aFilters
.IsEmpty()) {
1773 // filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
1774 // filters = filter *( "," LWS filter)
1776 // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref
1778 mFilterLocalHosts
= false;
1780 mozilla::Tokenizer
t(aFilters
);
1781 mozilla::Tokenizer::Token token
;
1783 // while (*filters) {
1785 // skip over spaces and ,
1787 while (t
.CheckChar(',')) {
1791 nsAutoCString portStr
;
1792 nsAutoCString hostStr
;
1793 nsAutoCString maskStr
;
1796 bool parsingIPv6
= false;
1797 bool parsingPort
= false;
1798 bool parsingMask
= false;
1799 while (t
.Next(token
)) {
1800 if (token
.Equals(mozilla::Tokenizer::Token::EndOfFile())) {
1804 if (token
.Equals(mozilla::Tokenizer::Token::Char(',')) ||
1805 token
.Type() == mozilla::Tokenizer::TOKEN_WS
) {
1809 if (token
.Equals(mozilla::Tokenizer::Token::Char('['))) {
1814 if (!parsingIPv6
&& token
.Equals(mozilla::Tokenizer::Token::Char(':'))) {
1815 // Port is starting. Claim the previous as host.
1824 } else if (token
.Equals(mozilla::Tokenizer::Token::Char('/'))) {
1829 } else if (token
.Equals(mozilla::Tokenizer::Token::Char(']'))) {
1830 parsingIPv6
= false;
1834 if (!parsingPort
&& !parsingMask
) {
1836 } else if (parsingPort
) {
1838 } else if (parsingMask
) {
1841 NS_WARNING("Could not parse this rule");
1845 if (hostStr
.IsEmpty()) {
1849 // If the current host filter is "<local>", then all local (i.e.
1850 // no dots in the hostname) hosts should bypass the proxy
1851 if (hostStr
.EqualsIgnoreCase("<local>")) {
1852 mFilterLocalHosts
= true;
1854 ("loaded filter for local hosts "
1855 "(plain host names, no dots)\n"));
1856 // Continue to next host filter;
1860 // For all other host filters, create HostInfo object and add to list
1861 HostInfo
*hinfo
= new HostInfo();
1862 nsresult rv
= NS_OK
;
1864 int32_t port
= portStr
.ToInteger(&rv
);
1865 if (NS_FAILED(rv
)) {
1870 int32_t maskLen
= maskStr
.ToInteger(&rv
);
1871 if (NS_FAILED(rv
)) {
1875 // PR_StringToNetAddr can't parse brackets enclosed IPv6
1876 nsAutoCString addrString
= hostStr
;
1877 if (hostStr
.First() == '[' && hostStr
.Last() == ']') {
1878 addrString
= Substring(hostStr
, 1, hostStr
.Length() - 2);
1882 if (PR_StringToNetAddr(addrString
.get(), &addr
) == PR_SUCCESS
) {
1883 hinfo
->is_ipaddr
= true;
1884 hinfo
->ip
.family
= PR_AF_INET6
; // we always store address as IPv6
1885 hinfo
->ip
.mask_len
= maskLen
;
1887 if (hinfo
->ip
.mask_len
== 0) {
1888 NS_WARNING("invalid mask");
1892 if (addr
.raw
.family
== PR_AF_INET
) {
1893 // convert to IPv4-mapped address
1894 PR_ConvertIPv4AddrToIPv6(addr
.inet
.ip
, &hinfo
->ip
.addr
);
1895 // adjust mask_len accordingly
1896 if (hinfo
->ip
.mask_len
<= 32) hinfo
->ip
.mask_len
+= 96;
1897 } else if (addr
.raw
.family
== PR_AF_INET6
) {
1899 memcpy(&hinfo
->ip
.addr
, &addr
.ipv6
.ip
, sizeof(PRIPv6Addr
));
1901 NS_WARNING("unknown address family");
1905 // apply mask to IPv6 address
1906 proxy_MaskIPv6Addr(hinfo
->ip
.addr
, hinfo
->ip
.mask_len
);
1909 if (hostStr
.First() == '*') {
1910 host
= Substring(hostStr
, 1);
1915 if (host
.IsEmpty()) {
1916 hinfo
->name
.host
= nullptr;
1920 hinfo
->name
.host_len
= host
.Length();
1922 hinfo
->is_ipaddr
= false;
1923 hinfo
->name
.host
= ToNewCString(host
);
1925 if (!hinfo
->name
.host
) goto loser
;
1928 //#define DEBUG_DUMP_FILTERS
1929 #ifdef DEBUG_DUMP_FILTERS
1930 printf("loaded filter[%zu]:\n", mHostFiltersArray
.Length());
1931 printf(" is_ipaddr = %u\n", hinfo
->is_ipaddr
);
1932 printf(" port = %u\n", hinfo
->port
);
1933 printf(" host = %s\n", hostStr
.get());
1934 if (hinfo
->is_ipaddr
) {
1935 printf(" ip.family = %x\n", hinfo
->ip
.family
);
1936 printf(" ip.mask_len = %u\n", hinfo
->ip
.mask_len
);
1939 PR_SetNetAddr(PR_IpAddrNull
, PR_AF_INET6
, 0, &netAddr
);
1940 memcpy(&netAddr
.ipv6
.ip
, &hinfo
->ip
.addr
, sizeof(hinfo
->ip
.addr
));
1943 PR_NetAddrToString(&netAddr
, buf
, sizeof(buf
));
1945 printf(" ip.addr = %s\n", buf
);
1947 printf(" name.host = %s\n", hinfo
->name
.host
);
1951 mHostFiltersArray
.AppendElement(hinfo
);
1958 nsresult
nsProtocolProxyService::GetProtocolInfo(nsIURI
*uri
,
1959 nsProtocolInfo
*info
) {
1960 MOZ_ASSERT(uri
, "URI is null");
1961 MOZ_ASSERT(info
, "info is null");
1965 rv
= uri
->GetScheme(info
->scheme
);
1966 if (NS_FAILED(rv
)) return rv
;
1968 nsCOMPtr
<nsIIOService
> ios
= do_GetIOService(&rv
);
1969 if (NS_FAILED(rv
)) return rv
;
1971 nsCOMPtr
<nsIProtocolHandler
> handler
;
1972 rv
= ios
->GetProtocolHandler(info
->scheme
.get(), getter_AddRefs(handler
));
1973 if (NS_FAILED(rv
)) return rv
;
1975 rv
= handler
->DoGetProtocolFlags(uri
, &info
->flags
);
1976 if (NS_FAILED(rv
)) return rv
;
1978 rv
= handler
->GetDefaultPort(&info
->defaultPort
);
1982 nsresult
nsProtocolProxyService::NewProxyInfo_Internal(
1983 const char *aType
, const nsACString
&aHost
, int32_t aPort
,
1984 const nsACString
&aUsername
, const nsACString
&aPassword
, uint32_t aFlags
,
1985 uint32_t aFailoverTimeout
, nsIProxyInfo
*aFailoverProxy
,
1986 uint32_t aResolveFlags
, nsIProxyInfo
**aResult
) {
1987 if (aPort
<= 0) aPort
= -1;
1989 nsCOMPtr
<nsProxyInfo
> failover
;
1990 if (aFailoverProxy
) {
1991 failover
= do_QueryInterface(aFailoverProxy
);
1992 NS_ENSURE_ARG(failover
);
1995 nsProxyInfo
*proxyInfo
= new nsProxyInfo();
1996 if (!proxyInfo
) return NS_ERROR_OUT_OF_MEMORY
;
1998 proxyInfo
->mType
= aType
;
1999 proxyInfo
->mHost
= aHost
;
2000 proxyInfo
->mPort
= aPort
;
2001 proxyInfo
->mUsername
= aUsername
;
2002 proxyInfo
->mPassword
= aPassword
;
2003 proxyInfo
->mFlags
= aFlags
;
2004 proxyInfo
->mResolveFlags
= aResolveFlags
;
2005 proxyInfo
->mTimeout
=
2006 aFailoverTimeout
== UINT32_MAX
? mFailedProxyTimeout
: aFailoverTimeout
;
2007 failover
.swap(proxyInfo
->mNext
);
2009 NS_ADDREF(*aResult
= proxyInfo
);
2013 nsresult
nsProtocolProxyService::Resolve_Internal(nsIChannel
*channel
,
2014 const nsProtocolInfo
&info
,
2017 nsIProxyInfo
**result
) {
2018 NS_ENSURE_ARG_POINTER(channel
);
2020 *usePACThread
= false;
2023 if (!(info
.flags
& nsIProtocolHandler::ALLOWS_PROXY
))
2024 return NS_OK
; // Can't proxy this (filters may not override)
2026 nsCOMPtr
<nsIURI
> uri
;
2027 nsresult rv
= GetProxyURI(channel
, getter_AddRefs(uri
));
2028 if (NS_FAILED(rv
)) return rv
;
2031 // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
2032 // here means that we will not use a proxy for this connection.
2033 if (mPACMan
&& mPACMan
->IsPACURI(uri
)) return NS_OK
;
2035 // if proxies are enabled and this host:port combo is supposed to use a
2036 // proxy, check for a proxy.
2037 if ((mProxyConfig
== PROXYCONFIG_DIRECT
) ||
2038 !CanUseProxy(uri
, info
.defaultPort
))
2041 bool mainThreadOnly
;
2042 if (mSystemProxySettings
&& mProxyConfig
== PROXYCONFIG_SYSTEM
&&
2043 NS_SUCCEEDED(mSystemProxySettings
->GetMainThreadOnly(&mainThreadOnly
)) &&
2045 *usePACThread
= true;
2049 if (mSystemProxySettings
&& mProxyConfig
== PROXYCONFIG_SYSTEM
) {
2050 // If the system proxy setting implementation is not threadsafe (e.g
2051 // linux gconf), we'll do it inline here. Such implementations promise
2053 // bug 1366133: this block uses GetPACURI & GetProxyForURI, which may
2054 // hang on Windows platform. Fortunately, current implementation on
2055 // Windows is not main thread only, so we are safe here.
2057 nsAutoCString PACURI
;
2058 nsAutoCString pacString
;
2060 if (NS_SUCCEEDED(mSystemProxySettings
->GetPACURI(PACURI
)) &&
2061 !PACURI
.IsEmpty()) {
2062 // There is a PAC URI configured. If it is unchanged, then
2063 // just execute the PAC thread. If it is changed then load
2066 if (mPACMan
&& mPACMan
->IsPACURI(PACURI
)) {
2068 *usePACThread
= true;
2072 ConfigureFromPAC(PACURI
, false);
2078 nsAutoCString scheme
;
2081 uri
->GetAsciiSpec(spec
);
2082 uri
->GetAsciiHost(host
);
2083 uri
->GetScheme(scheme
);
2084 uri
->GetPort(&port
);
2086 if (flags
& RESOLVE_PREFER_SOCKS_PROXY
) {
2087 LOG(("Ignoring RESOLVE_PREFER_SOCKS_PROXY for system proxy setting\n"));
2088 } else if (flags
& RESOLVE_PREFER_HTTPS_PROXY
) {
2089 scheme
.AssignLiteral("https");
2090 } else if (flags
& RESOLVE_IGNORE_URI_SCHEME
) {
2091 scheme
.AssignLiteral("http");
2094 // now try the system proxy settings for this particular url
2095 if (NS_SUCCEEDED(mSystemProxySettings
->GetProxyForURI(spec
, scheme
, host
,
2096 port
, pacString
))) {
2097 ProcessPACString(pacString
, 0, result
);
2102 // if proxies are enabled and this host:port combo is supposed to use a
2103 // proxy, check for a proxy.
2104 if (mProxyConfig
== PROXYCONFIG_DIRECT
||
2105 (mProxyConfig
== PROXYCONFIG_MANUAL
&&
2106 !CanUseProxy(uri
, info
.defaultPort
)))
2109 // Proxy auto config magic...
2110 if (mProxyConfig
== PROXYCONFIG_PAC
|| mProxyConfig
== PROXYCONFIG_WPAD
) {
2111 // Do not query PAC now.
2112 *usePACThread
= true;
2116 // If we aren't in manual proxy configuration mode then we don't
2117 // want to honor any manual specific prefs that might be still set
2118 if (mProxyConfig
!= PROXYCONFIG_MANUAL
) return NS_OK
;
2120 // proxy info values for manual configuration mode
2121 const char *type
= nullptr;
2122 const nsACString
*host
= nullptr;
2125 uint32_t proxyFlags
= 0;
2127 if ((flags
& RESOLVE_PREFER_SOCKS_PROXY
) && !mSOCKSProxyTarget
.IsEmpty() &&
2128 (IsHostLocalTarget(mSOCKSProxyTarget
) || mSOCKSProxyPort
> 0)) {
2129 host
= &mSOCKSProxyTarget
;
2130 if (mSOCKSProxyVersion
== 4)
2131 type
= kProxyType_SOCKS4
;
2133 type
= kProxyType_SOCKS
;
2134 port
= mSOCKSProxyPort
;
2135 if (mSOCKSProxyRemoteDNS
)
2136 proxyFlags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
2137 } else if ((flags
& RESOLVE_PREFER_HTTPS_PROXY
) &&
2138 !mHTTPSProxyHost
.IsEmpty() && mHTTPSProxyPort
> 0) {
2139 host
= &mHTTPSProxyHost
;
2140 type
= kProxyType_HTTP
;
2141 port
= mHTTPSProxyPort
;
2142 } else if (!mHTTPProxyHost
.IsEmpty() && mHTTPProxyPort
> 0 &&
2143 ((flags
& RESOLVE_IGNORE_URI_SCHEME
) ||
2144 info
.scheme
.EqualsLiteral("http"))) {
2145 host
= &mHTTPProxyHost
;
2146 type
= kProxyType_HTTP
;
2147 port
= mHTTPProxyPort
;
2148 } else if (!mHTTPSProxyHost
.IsEmpty() && mHTTPSProxyPort
> 0 &&
2149 !(flags
& RESOLVE_IGNORE_URI_SCHEME
) &&
2150 info
.scheme
.EqualsLiteral("https")) {
2151 host
= &mHTTPSProxyHost
;
2152 type
= kProxyType_HTTP
;
2153 port
= mHTTPSProxyPort
;
2154 } else if (!mFTPProxyHost
.IsEmpty() && mFTPProxyPort
> 0 &&
2155 !(flags
& RESOLVE_IGNORE_URI_SCHEME
) &&
2156 info
.scheme
.EqualsLiteral("ftp")) {
2157 host
= &mFTPProxyHost
;
2158 type
= kProxyType_HTTP
;
2159 port
= mFTPProxyPort
;
2160 } else if (!mSOCKSProxyTarget
.IsEmpty() &&
2161 (IsHostLocalTarget(mSOCKSProxyTarget
) || mSOCKSProxyPort
> 0)) {
2162 host
= &mSOCKSProxyTarget
;
2163 if (mSOCKSProxyVersion
== 4)
2164 type
= kProxyType_SOCKS4
;
2166 type
= kProxyType_SOCKS
;
2167 port
= mSOCKSProxyPort
;
2168 if (mSOCKSProxyRemoteDNS
)
2169 proxyFlags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
2174 NewProxyInfo_Internal(type
, *host
, port
, EmptyCString(), EmptyCString(),
2175 proxyFlags
, UINT32_MAX
, nullptr, flags
, result
);
2176 if (NS_FAILED(rv
)) return rv
;
2182 void nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo
*aProxy
) {
2183 // Disable Prefetch in the DNS service if a proxy is in use.
2184 if (!aProxy
) return;
2186 nsCOMPtr
<nsProxyInfo
> pi
= do_QueryInterface(aProxy
);
2187 if (!pi
|| !pi
->mType
|| pi
->mType
== kProxyType_DIRECT
) return;
2189 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
);
2191 nsCOMPtr
<nsPIDNSService
> pdns
= do_QueryInterface(dns
);
2194 // We lose the prefetch optimization for the life of the dns service.
2195 pdns
->SetPrefetchEnabled(false);
2198 void nsProtocolProxyService::CopyFilters(nsTArray
<RefPtr
<FilterLink
>> &aCopy
) {
2199 MOZ_ASSERT(aCopy
.Length() == 0);
2200 aCopy
.AppendElements(mFilters
);
2203 bool nsProtocolProxyService::ApplyFilter(
2204 FilterLink
const *filterLink
, nsIChannel
*channel
,
2205 const nsProtocolInfo
&info
, nsCOMPtr
<nsIProxyInfo
> list
,
2206 nsIProxyProtocolFilterResult
*callback
) {
2209 // We prune the proxy list prior to invoking each filter. This may be
2210 // somewhat inefficient, but it seems like a good idea since we want each
2211 // filter to "see" a valid proxy list.
2212 PruneProxyInfo(info
, list
);
2214 if (filterLink
->filter
) {
2215 nsCOMPtr
<nsIURI
> uri
;
2216 Unused
<< GetProxyURI(channel
, getter_AddRefs(uri
));
2221 rv
= filterLink
->filter
->ApplyFilter(this, uri
, list
, callback
);
2222 return NS_SUCCEEDED(rv
);
2225 if (filterLink
->channelFilter
) {
2226 rv
= filterLink
->channelFilter
->ApplyFilter(this, channel
, list
, callback
);
2227 return NS_SUCCEEDED(rv
);
2233 void nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo
&info
,
2234 nsIProxyInfo
**list
) {
2237 LOG(("nsProtocolProxyService::PruneProxyInfo ENTER list=%p", *list
));
2239 nsProxyInfo
*head
= nullptr;
2240 CallQueryInterface(*list
, &head
);
2242 MOZ_ASSERT_UNREACHABLE("nsIProxyInfo must QI to nsProxyInfo");
2247 // Pruning of disabled proxies works like this:
2248 // - If all proxies are disabled, return the full list
2249 // - Otherwise, remove the disabled proxies.
2251 // Pruning of disallowed proxies works like this:
2252 // - If the protocol handler disallows the proxy, then we disallow it.
2254 // Start by removing all disallowed proxies if required:
2255 if (!(info
.flags
& nsIProtocolHandler::ALLOWS_PROXY_HTTP
)) {
2256 nsProxyInfo
*last
= nullptr, *iter
= head
;
2258 if ((iter
->Type() == kProxyType_HTTP
) ||
2259 (iter
->Type() == kProxyType_HTTPS
)) {
2262 last
->mNext
= iter
->mNext
;
2265 nsProxyInfo
*next
= iter
->mNext
;
2266 iter
->mNext
= nullptr;
2279 // Scan to see if all remaining non-direct proxies are disabled. If so, then
2280 // we'll just bail and return them all. Otherwise, we'll go and prune the
2283 bool allNonDirectProxiesDisabled
= true;
2286 for (iter
= head
; iter
; iter
= iter
->mNext
) {
2287 if (!IsProxyDisabled(iter
) && iter
->mType
!= kProxyType_DIRECT
) {
2288 allNonDirectProxiesDisabled
= false;
2293 if (allNonDirectProxiesDisabled
) {
2294 LOG(("All proxies are disabled, so trying all again"));
2296 // remove any disabled proxies.
2297 nsProxyInfo
*last
= nullptr;
2298 for (iter
= head
; iter
;) {
2299 if (IsProxyDisabled(iter
)) {
2301 nsProxyInfo
*reject
= iter
;
2309 reject
->mNext
= nullptr;
2314 // since we are about to use this proxy, make sure it is not on
2315 // the disabled proxy list. we'll add it back to that list if
2316 // we have to (in GetFailoverForProxy).
2318 // XXX(darin): It might be better to do this as a final pass.
2327 // if only DIRECT was specified then return no proxy info, and we're done.
2328 if (head
&& !head
->mNext
&& head
->mType
== kProxyType_DIRECT
)
2331 *list
= head
; // Transfer ownership
2333 LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list
));
2336 bool nsProtocolProxyService::GetIsPACLoading() {
2337 return mPACMan
&& mPACMan
->IsLoading();
2341 } // namespace mozilla