Bug 1875768 - Call the appropriate postfork handler on MacOS r=glandium
[gecko.git] / netwerk / base / nsProtocolProxyService.cpp
blob1f88a0158cf9d4ae92f3682902c8e501d3ee4dbb
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"
25 #include "nsCRT.h"
26 #include "nsThreadUtils.h"
27 #include "nsQueryObject.h"
28 #include "nsSOCKSIOLayer.h"
29 #include "nsString.h"
30 #include "nsNetUtil.h"
31 #include "nsNetCID.h"
32 #include "prnetdb.h"
33 #include "nsPACMan.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 //----------------------------------------------------------------------------
49 namespace mozilla {
50 namespace net {
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[];
60 #undef LOG
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 {
72 nsAutoCString scheme;
73 uint32_t flags = 0;
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) {
82 nsresult rv = NS_OK;
83 nsCOMPtr<nsIURI> proxyURI;
84 nsCOMPtr<nsIHttpChannelInternal> httpChannel(do_QueryInterface(channel));
85 if (httpChannel) {
86 rv = httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
88 if (!proxyURI) {
89 rv = channel->GetURI(getter_AddRefs(proxyURI));
91 if (NS_FAILED(rv)) {
92 return rv;
94 proxyURI.forget(aOut);
95 return NS_OK;
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,
104 f));
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",
110 this, cf));
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 {
125 public:
126 NS_DECL_THREADSAFE_ISUPPORTS
128 nsAsyncResolveRequest(nsProtocolProxyService* pps, nsIChannel* channel,
129 uint32_t aResolveFlags,
130 nsIProtocolProxyCallback* callback)
131 : mResolveFlags(aResolveFlags),
132 mPPS(pps),
133 mXPComPPS(pps),
134 mChannel(channel),
135 mCallback(callback) {
136 NS_ASSERTION(mCallback, "null callback");
139 private:
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
146 if (mChannel) {
147 NS_ReleaseOnMainThread("nsAsyncResolveRequest::mChannel",
148 mChannel.forget());
151 if (mCallback) {
152 NS_ReleaseOnMainThread("nsAsyncResolveRequest::mCallback",
153 mCallback.forget());
156 if (mProxyInfo) {
157 NS_ReleaseOnMainThread("nsAsyncResolveRequest::mProxyInfo",
158 mProxyInfo.forget());
161 if (mXPComPPS) {
162 NS_ReleaseOnMainThread("nsAsyncResolveRequest::mXPComPPS",
163 mXPComPPS.forget());
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,
172 public nsIRunnable,
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
179 NS_DECL_NSIRUNNABLE
180 NS_DECL_NSICANCELABLE
182 using Callback =
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
194 // of proxy info.
195 nsresult AsyncProcess(nsAsyncResolveRequest* aRequest);
197 private:
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
205 // to be called on)
206 nsresult Finish();
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;
213 Callback mCallback;
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(),
220 // false otherwise.
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) {
237 return;
240 nsCOMPtr<nsIProxyInfo> proxyInfo =
241 pi->CloneProxyInfoWithNewResolveFlags(mResolveFlags);
242 mProxyInfo.swap(proxyInfo);
245 public:
246 nsresult ProcessLocally(nsProtocolInfo& info, nsIProxyInfo* pi,
247 bool isSyncOK) {
248 SetResult(NS_OK, pi);
250 auto consumeFiltersResult = [isSyncOK](nsAsyncResolveRequest* ctx,
251 nsIProxyInfo* pi,
252 bool aCalledAsync) -> nsresult {
253 ctx->SetResult(NS_OK, pi);
254 if (isSyncOK || aCalledAsync) {
255 ctx->Run();
256 return NS_OK;
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) {
268 mStatus = status;
269 mProxyInfo = pi;
272 NS_IMETHOD Run() override {
273 if (mCallback) DoCallback();
274 return NS_OK;
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
293 return NS_OK;
296 nsresult rv = NS_DispatchToCurrentThread(this);
297 if (NS_FAILED(rv)) {
298 NS_WARNING("unable to dispatch callback event");
299 } else {
300 mDispatched = true;
301 return NS_OK;
304 mCallback = nullptr; // break possible reference cycle
305 return rv;
308 private:
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) {
318 mStatus = status;
319 mPACString = pacString;
320 mPACURL = newPACURL;
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.
326 DoCallback();
329 void DoCallback() {
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;
336 mStatus = NS_OK;
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
350 nsProtocolInfo info;
351 mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
353 auto consumeFiltersResult = [pacAvailable](nsAsyncResolveRequest* self,
354 nsIProxyInfo* pi,
355 bool async) -> nsresult {
356 LOG(("DoCallback::consumeFiltersResult this=%p, pi=%p, async=%d", self,
357 pi, async));
359 self->mProxyInfo = pi;
361 if (pacAvailable) {
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);
374 return NS_OK;
377 if (NS_SUCCEEDED(mStatus)) {
378 mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
379 // This may call consumeFiltersResult() directly.
380 mAsyncFilterApplier->AsyncProcess(this);
381 return;
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);
401 if (NS_FAILED(rv)) {
402 mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
405 // do not call onproxyavailable() in SUCCESS case - the newRequest will
406 // take care of that
407 } else {
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
417 // in the dtor
418 mCallback = nullptr; // in case the callback holds an owning ref to us
419 mPPS = nullptr;
420 mXPComPPS = nullptr;
421 mChannel = nullptr;
422 mProxyInfo = nullptr;
425 private:
426 nsresult mStatus{NS_OK};
427 nsCString mPACString;
428 nsCString mPACURL;
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)
448 : mInfo(aInfo),
449 mCallback(aCallback),
450 mNextFilterIndex(0),
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();
478 mRequest = aRequest;
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.
486 do {
487 MOZ_ASSERT(!mProcessingInLoop);
489 mozilla::AutoRestore<bool> restore(mProcessingInLoop);
490 mProcessingInLoop = true;
492 nsresult rv = ProcessNextFilter();
493 if (NS_FAILED(rv)) {
494 return rv;
496 } while (mFilterCalledBack);
498 return NS_OK;
501 nsresult nsAsyncResolveRequest::AsyncApplyFilters::ProcessNextFilter() {
502 LOG(("AsyncApplyFilters::ProcessNextFilter %p ENTER pi=%p", this,
503 mProxyInfo.get()));
505 RefPtr<FilterLink> filter;
506 do {
507 mFilterCalledBack = false;
509 if (!mRequest) {
510 // We got canceled
511 LOG((" canceled"));
512 return NS_OK; // should we let the consumer know?
515 if (mNextFilterIndex == mFiltersCopy.Length()) {
516 return Finish();
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,
531 mProxyInfo, this) &&
532 !mFilterCalledBack);
534 LOG(("AsyncApplyFilters::ProcessNextFilter %p LEAVE pi=%p", this,
535 mProxyInfo.get()));
536 return NS_OK;
539 NS_IMETHODIMP
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?"));
549 return NS_OK;
552 mFilterCalledBack = true;
554 if (!mRequest) {
555 // We got canceled
556 LOG((" canceled"));
557 return NS_OK;
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"));
566 return NS_OK;
569 if (mNextFilterIndex == mFiltersCopy.Length()) {
570 // We are done, all filters have been called on!
571 Finish();
572 return NS_OK;
575 // Redispatch, since we don't want long stacks when filters respond
576 // synchronously.
577 LOG((" redispatching"));
578 NS_DispatchToCurrentThread(this);
579 return NS_OK;
582 NS_IMETHODIMP
583 nsAsyncResolveRequest::AsyncApplyFilters::Run() {
584 LOG(("AsyncApplyFilters::Run %p", this));
586 MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
588 ProcessNextFilter();
589 return NS_OK;
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;
603 pi.swap(mProxyInfo);
605 request->mPPS->PruneProxyInfo(mInfo, pi);
606 return mCallback(request, pi, !mProcessingInLoop);
609 NS_IMETHODIMP
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;
619 mRequest = nullptr;
621 return NS_OK;
624 // Bug 1366133: make GetPACURI and GetSystemWPADSetting off-main-thread since it
625 // may hang on Windows platform
626 class AsyncGetPACURIRequestOrSystemWPADSetting final : public nsIRunnable {
627 public:
628 NS_DECL_THREADSAFE_ISUPPORTS
630 using CallbackFunc = nsresult (nsProtocolProxyService::*)(bool, bool,
631 nsresult,
632 const nsACString&,
633 bool);
635 AsyncGetPACURIRequestOrSystemWPADSetting(
636 nsProtocolProxyService* aService, CallbackFunc aCallback,
637 nsISystemProxySettings* aSystemProxySettings, bool aMainThreadOnly,
638 bool aForceReload, bool aResetPACThread, bool aSystemWPADAllowed)
639 : mIsMainThreadOnly(aMainThreadOnly),
640 mService(aService),
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);
654 nsresult rv;
655 nsCString pacUri;
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,
665 bool>(
666 "AsyncGetPACURIRequestOrSystemWPADSettingCallback", mService,
667 mCallback, mForceReload, mResetPACThread, rv, pacUri,
668 systemWPADSetting);
670 return NS_DispatchToMainThread(event);
673 private:
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;
687 bool mForceReload;
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;
704 if (mask_len > 96) {
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)));
716 } else {
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) {
727 nsAutoCString temp;
728 nsresult rv = aPrefBranch->GetCharPref(aPref, temp);
729 if (NS_FAILED(rv)) {
730 aResult.Truncate();
731 } else {
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,
740 int32_t& aResult) {
741 int32_t temp;
742 nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
743 if (NS_FAILED(rv)) {
744 aResult = -1;
745 } else {
746 aResult = temp;
750 static void proxy_GetBoolPref(nsIPrefBranch* aPrefBranch, const char* aPref,
751 bool& aResult) {
752 bool temp;
753 nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
754 if (NS_FAILED(rv)) {
755 aResult = false;
756 } else {
757 aResult = 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)
781 NS_INTERFACE_MAP_END
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 &&
791 mPACMan == nullptr,
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);
799 if (prefBranch) {
800 // monitor proxy prefs
801 prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
803 // read all prefs
804 PrefsChanged(prefBranch, nullptr);
807 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
808 if (obs) {
809 // register for shutdown notification so we can clean ourselves up
810 // properly.
811 obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
812 obs->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
815 return NS_OK;
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);
822 if (!prefs) {
823 return NS_OK;
826 int32_t type;
827 nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
828 if (NS_FAILED(rv)) {
829 return NS_OK;
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)) {
839 return rv;
842 nsProtocolInfo pac;
843 rv = GetProtocolInfo(pacURI, &pac);
844 if (!NS_SUCCEEDED(rv)) {
845 return rv;
848 if (!pac.scheme.EqualsLiteral("file") &&
849 !pac.scheme.EqualsLiteral("data")) {
850 LOG((": received network changed event, reload PAC"));
851 ReloadPAC();
854 } else if ((type == PROXYCONFIG_WPAD) || (type == PROXYCONFIG_SYSTEM)) {
855 ReloadPAC();
858 return NS_OK;
861 nsresult nsProtocolProxyService::AsyncConfigureWPADOrFromPAC(
862 bool aForceReload, bool aResetPACThread, bool aSystemWPADAllowed) {
863 MOZ_ASSERT(NS_IsMainThread());
865 bool mainThreadOnly;
866 nsresult rv = mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly);
867 if (NS_WARN_IF(NS_FAILED(rv))) {
868 return rv;
871 nsCOMPtr<nsIRunnable> req = new AsyncGetPACURIRequestOrSystemWPADSetting(
872 this, &nsProtocolProxyService::OnAsyncGetPACURIOrSystemWPADSetting,
873 mSystemProxySettings, mainThreadOnly, aForceReload, aResetPACThread,
874 aSystemWPADAllowed);
876 if (mainThreadOnly) {
877 return req->Run();
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) {
890 ResetPACThread();
893 if (aSystemWPADSetting) {
894 if (mSystemProxySettings || !mPACMan) {
895 mSystemProxySettings = nullptr;
896 ResetPACThread();
899 nsAutoCString tempString;
900 ConfigureFromPAC(EmptyCString(), false);
901 } else if (NS_SUCCEEDED(aResult) && !aUri.IsEmpty()) {
902 ConfigureFromPAC(PromiseFlatCString(aUri), aForceReload);
905 return NS_OK;
908 NS_IMETHODIMP
909 nsProtocolProxyService::Observe(nsISupports* aSubject, const char* aTopic,
910 const char16_t* aData) {
911 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
912 mIsShutdown = true;
913 // cleanup
914 mHostFiltersArray.Clear();
915 mFilters.Clear();
917 if (mPACMan) {
918 mPACMan->Shutdown();
919 mPACMan = nullptr;
922 if (mReloadPACTimer) {
923 mReloadPACTimer->Cancel();
924 mReloadPACTimer = nullptr;
927 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
928 if (obs) {
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",
939 delay));
941 if (delay) {
942 if (mReloadPACTimer) {
943 mReloadPACTimer->Cancel();
944 mReloadPACTimer = nullptr;
946 NS_NewTimerWithCallback(getter_AddRefs(mReloadPACTimer), this, delay,
947 nsITimer::TYPE_ONE_SHOT);
948 } else {
949 ReloadNetworkPAC();
952 } else {
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());
958 return NS_OK;
961 NS_IMETHODIMP
962 nsProtocolProxyService::Notify(nsITimer* aTimer) {
963 MOZ_ASSERT(aTimer == mReloadPACTimer);
964 ReloadNetworkPAC();
965 return NS_OK;
968 NS_IMETHODIMP
969 nsProtocolProxyService::GetName(nsACString& aName) {
970 aName.AssignLiteral("nsProtocolProxyService");
971 return NS_OK;
974 void nsProtocolProxyService::PrefsChanged(nsIPrefBranch* prefBranch,
975 const char* pref) {
976 nsresult rv = NS_OK;
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"))) {
984 int32_t type = -1;
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;
999 mProxyConfig = type;
1000 reloadPAC = true;
1003 if (mProxyConfig == PROXYCONFIG_SYSTEM) {
1004 mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
1005 if (!mSystemProxySettings) mProxyConfig = PROXYCONFIG_DIRECT;
1006 ResetPACThread();
1007 } else {
1008 if (mSystemProxySettings) {
1009 mSystemProxySettings = nullptr;
1010 ResetPACThread();
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"))) {
1040 int32_t version;
1041 proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
1042 // make sure this preference value remains sane
1043 if (version == nsIProxyInfo::SOCKS_V5) {
1044 mSOCKSProxyVersion = nsIProxyInfo::SOCKS_V5;
1045 } else {
1046 mSOCKSProxyVersion = nsIProxyInfo::SOCKS_V4;
1050 if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns"))) {
1051 proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
1052 mSOCKS4ProxyRemoteDNS);
1055 if (!pref || !strcmp(pref, PROXY_PREF("socks5_remote_dns"))) {
1056 proxy_GetBoolPref(prefBranch, PROXY_PREF("socks5_remote_dns"),
1057 mSOCKS5ProxyRemoteDNS);
1060 if (!pref || !strcmp(pref, PROXY_PREF("proxy_over_tls"))) {
1061 proxy_GetBoolPref(prefBranch, PROXY_PREF("proxy_over_tls"), mProxyOverTLS);
1064 if (!pref || !strcmp(pref, PROXY_PREF("enable_wpad_over_dhcp"))) {
1065 proxy_GetBoolPref(prefBranch, PROXY_PREF("enable_wpad_over_dhcp"),
1066 mWPADOverDHCPEnabled);
1067 reloadPAC = reloadPAC || mProxyConfig == PROXYCONFIG_WPAD;
1070 if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout"))) {
1071 proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
1072 mFailedProxyTimeout);
1075 if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
1076 rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"), tempString);
1077 if (NS_SUCCEEDED(rv)) LoadHostFilters(tempString);
1080 // We're done if not using something that could give us a PAC URL
1081 // (PAC, WPAD or System)
1082 if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
1083 mProxyConfig != PROXYCONFIG_SYSTEM) {
1084 return;
1087 // OK, we need to reload the PAC file if:
1088 // 1) network.proxy.type changed, or
1089 // 2) network.proxy.autoconfig_url changed and PAC is configured
1091 if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url"))) reloadPAC = true;
1093 if (reloadPAC) {
1094 tempString.Truncate();
1095 if (mProxyConfig == PROXYCONFIG_PAC) {
1096 prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"), tempString);
1097 if (mPACMan && !mPACMan->IsPACURI(tempString)) {
1098 LOG(("PAC Thread URI Changed - Reset Pac Thread"));
1099 ResetPACThread();
1101 } else if (mProxyConfig == PROXYCONFIG_WPAD) {
1102 LOG(("Auto-detecting proxy - Reset Pac Thread"));
1103 ResetPACThread();
1104 } else if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM &&
1105 StaticPrefs::network_proxy_system_wpad()) {
1106 AsyncConfigureWPADOrFromPAC(false, false, true);
1107 } else if (mSystemProxySettings) {
1108 // Get System Proxy settings if available
1109 AsyncConfigureWPADOrFromPAC(false, false, false);
1111 if (!tempString.IsEmpty() || mProxyConfig == PROXYCONFIG_WPAD) {
1112 ConfigureFromPAC(tempString, false);
1117 bool nsProtocolProxyService::CanUseProxy(nsIURI* aURI, int32_t defaultPort) {
1118 int32_t port;
1119 nsAutoCString host;
1121 nsresult rv = aURI->GetAsciiHost(host);
1122 if (NS_FAILED(rv) || host.IsEmpty()) return false;
1124 rv = aURI->GetPort(&port);
1125 if (NS_FAILED(rv)) return false;
1126 if (port == -1) port = defaultPort;
1128 PRNetAddr addr;
1129 bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
1131 PRIPv6Addr ipv6;
1132 if (is_ipaddr) {
1133 // convert parsed address to IPv6
1134 if (addr.raw.family == PR_AF_INET) {
1135 // convert to IPv4-mapped address
1136 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
1137 } else if (addr.raw.family == PR_AF_INET6) {
1138 // copy the address
1139 memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1140 } else {
1141 NS_WARNING("unknown address family");
1142 return true; // allow proxying
1146 // Don't use proxy for local hosts (plain hostname, no dots)
1147 if ((!is_ipaddr && mFilterLocalHosts && !host.Contains('.')) ||
1148 // This method detects if we have network.proxy.allow_hijacking_localhost
1149 // pref enabled. If it's true then this method will always return false
1150 // otherwise it returns true if the host matches an address that's
1151 // hardcoded to the loopback address.
1152 (!StaticPrefs::network_proxy_allow_hijacking_localhost() &&
1153 nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(host))) {
1154 LOG(("Not using proxy for this local host [%s]!\n", host.get()));
1155 return false; // don't allow proxying
1158 int32_t index = -1;
1159 while (++index < int32_t(mHostFiltersArray.Length())) {
1160 const auto& hinfo = mHostFiltersArray[index];
1162 if (is_ipaddr != hinfo->is_ipaddr) continue;
1163 if (hinfo->port && hinfo->port != port) continue;
1165 if (is_ipaddr) {
1166 // generate masked version of target IPv6 address
1167 PRIPv6Addr masked;
1168 memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
1169 proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
1171 // check for a match
1172 if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0) {
1173 return false; // proxy disallowed
1175 } else {
1176 uint32_t host_len = host.Length();
1177 uint32_t filter_host_len = hinfo->name.host_len;
1179 if (host_len >= filter_host_len) {
1181 // compare last |filter_host_len| bytes of target hostname.
1183 const char* host_tail = host.get() + host_len - filter_host_len;
1184 if (!nsCRT::strncasecmp(host_tail, hinfo->name.host, filter_host_len)) {
1185 // If the tail of the host string matches the filter
1187 if (filter_host_len > 0 && hinfo->name.host[0] == '.') {
1188 // If the filter was of the form .foo.bar.tld, all such
1189 // matches are correct
1190 return false; // proxy disallowed
1193 // abc-def.example.org should not match def.example.org
1194 // however, *.def.example.org should match .def.example.org
1195 // We check that the filter doesn't start with a `.`. If it does,
1196 // then the strncasecmp above should suffice. If it doesn't,
1197 // then we should only consider it a match if the strncasecmp happened
1198 // at a subdomain boundary
1199 if (host_len > filter_host_len && *(host_tail - 1) == '.') {
1200 // If the host was something.foo.bar.tld and the filter
1201 // was foo.bar.tld, it's still a match.
1202 // the character right before the tail must be a
1203 // `.` for this to work
1204 return false; // proxy disallowed
1207 if (host_len == filter_host_len) {
1208 // If the host and filter are of the same length,
1209 // they should match
1210 return false; // proxy disallowed
1216 return true;
1219 // kProxyType\* may be referred to externally in
1220 // nsProxyInfo in order to compare by string pointer
1221 const char kProxyType_HTTP[] = "http";
1222 const char kProxyType_HTTPS[] = "https";
1223 const char kProxyType_PROXY[] = "proxy";
1224 const char kProxyType_SOCKS[] = "socks";
1225 const char kProxyType_SOCKS4[] = "socks4";
1226 const char kProxyType_SOCKS5[] = "socks5";
1227 const char kProxyType_DIRECT[] = "direct";
1229 const char* nsProtocolProxyService::ExtractProxyInfo(const char* start,
1230 uint32_t aResolveFlags,
1231 nsProxyInfo** result) {
1232 *result = nullptr;
1233 uint32_t flags = 0;
1235 // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
1237 // find end of proxy info delimiter
1238 const char* end = start;
1239 while (*end && *end != ';') ++end;
1241 // find end of proxy type delimiter
1242 const char* sp = start;
1243 while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
1245 uint32_t len = sp - start;
1246 const char* type = nullptr;
1247 switch (len) {
1248 case 4:
1249 if (nsCRT::strncasecmp(start, kProxyType_HTTP, 4) == 0) {
1250 type = kProxyType_HTTP;
1252 break;
1253 case 5:
1254 if (nsCRT::strncasecmp(start, kProxyType_PROXY, 5) == 0) {
1255 type = kProxyType_HTTP;
1256 } else if (nsCRT::strncasecmp(start, kProxyType_SOCKS, 5) == 0) {
1257 type = kProxyType_SOCKS4; // assume v4 for 4x compat
1258 if (StaticPrefs::network_proxy_default_pac_script_socks_version() ==
1259 5) {
1260 type = kProxyType_SOCKS;
1262 } else if (nsCRT::strncasecmp(start, kProxyType_HTTPS, 5) == 0) {
1263 type = kProxyType_HTTPS;
1265 break;
1266 case 6:
1267 if (nsCRT::strncasecmp(start, kProxyType_DIRECT, 6) == 0) {
1268 type = kProxyType_DIRECT;
1269 } else if (nsCRT::strncasecmp(start, kProxyType_SOCKS4, 6) == 0) {
1270 type = kProxyType_SOCKS4;
1271 } else if (nsCRT::strncasecmp(start, kProxyType_SOCKS5, 6) == 0) {
1272 // map "SOCKS5" to "socks" to match contract-id of registered
1273 // SOCKS-v5 socket provider.
1274 type = kProxyType_SOCKS;
1276 break;
1278 if (type) {
1279 int32_t port = -1;
1281 // If it's a SOCKS5 proxy, do name resolution on the server side.
1282 // We could use this with SOCKS4a servers too, but they might not
1283 // support it.
1284 if (type == kProxyType_SOCKS || mSOCKS5ProxyRemoteDNS) {
1285 flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1288 // extract host:port
1289 start = sp;
1290 while ((*start == ' ' || *start == '\t') && start < end) start++;
1292 // port defaults
1293 if (type == kProxyType_HTTP) {
1294 port = 80;
1295 } else if (type == kProxyType_HTTPS) {
1296 port = 443;
1297 } else {
1298 port = 1080;
1301 RefPtr<nsProxyInfo> pi = new nsProxyInfo();
1302 pi->mType = type;
1303 pi->mFlags = flags;
1304 pi->mResolveFlags = aResolveFlags;
1305 pi->mTimeout = mFailedProxyTimeout;
1307 // www.foo.com:8080 and http://www.foo.com:8080
1308 nsDependentCSubstring maybeURL(start, end - start);
1309 nsCOMPtr<nsIURI> pacURI;
1311 nsAutoCString urlHost;
1312 // First assume the scheme is present, e.g. http://www.example.com:8080
1313 if (NS_FAILED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) ||
1314 NS_FAILED(pacURI->GetAsciiHost(urlHost)) || urlHost.IsEmpty()) {
1315 // It isn't, assume www.example.com:8080
1316 maybeURL.Insert("http://", 0);
1318 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL))) {
1319 pacURI->GetAsciiHost(urlHost);
1323 if (!urlHost.IsEmpty()) {
1324 pi->mHost = urlHost;
1326 int32_t tPort;
1327 if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
1328 port = tPort;
1330 pi->mPort = port;
1333 pi.forget(result);
1336 while (*end == ';' || *end == ' ' || *end == '\t') ++end;
1337 return end;
1340 void nsProtocolProxyService::GetProxyKey(nsProxyInfo* pi, nsCString& key) {
1341 key.AssignASCII(pi->mType);
1342 if (!pi->mHost.IsEmpty()) {
1343 key.Append(' ');
1344 key.Append(pi->mHost);
1345 key.Append(':');
1346 key.AppendInt(pi->mPort);
1350 uint32_t nsProtocolProxyService::SecondsSinceSessionStart() {
1351 PRTime now = PR_Now();
1353 // get time elapsed since session start
1354 int64_t diff = now - mSessionStart;
1356 // convert microseconds to seconds
1357 diff /= PR_USEC_PER_SEC;
1359 // return converted 32 bit value
1360 return uint32_t(diff);
1363 void nsProtocolProxyService::EnableProxy(nsProxyInfo* pi) {
1364 nsAutoCString key;
1365 GetProxyKey(pi, key);
1366 mFailedProxies.Remove(key);
1369 void nsProtocolProxyService::DisableProxy(nsProxyInfo* pi) {
1370 nsAutoCString key;
1371 GetProxyKey(pi, key);
1373 uint32_t dsec = SecondsSinceSessionStart();
1375 // Add timeout to interval (this is the time when the proxy can
1376 // be tried again).
1377 dsec += pi->mTimeout;
1379 // NOTE: The classic codebase would increase the timeout value
1380 // incrementally each time a subsequent failure occurred.
1381 // We could do the same, but it would require that we not
1382 // remove proxy entries in IsProxyDisabled or otherwise
1383 // change the way we are recording disabled proxies.
1384 // Simpler is probably better for now, and at least the
1385 // user can tune the timeout setting via preferences.
1387 LOG(("DisableProxy %s %d\n", key.get(), dsec));
1389 // If this fails, oh well... means we don't have enough memory
1390 // to remember the failed proxy.
1391 mFailedProxies.InsertOrUpdate(key, dsec);
1394 bool nsProtocolProxyService::IsProxyDisabled(nsProxyInfo* pi) {
1395 nsAutoCString key;
1396 GetProxyKey(pi, key);
1398 uint32_t val;
1399 if (!mFailedProxies.Get(key, &val)) return false;
1401 uint32_t dsec = SecondsSinceSessionStart();
1403 // if time passed has exceeded interval, then try proxy again.
1404 if (dsec > val) {
1405 mFailedProxies.Remove(key);
1406 return false;
1409 return true;
1412 nsresult nsProtocolProxyService::SetupPACThread(
1413 nsISerialEventTarget* mainThreadEventTarget) {
1414 if (mIsShutdown) {
1415 return NS_ERROR_FAILURE;
1418 if (mPACMan) return NS_OK;
1420 mPACMan = new nsPACMan(mainThreadEventTarget);
1422 bool mainThreadOnly;
1423 nsresult rv;
1424 if (mSystemProxySettings &&
1425 NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
1426 !mainThreadOnly) {
1427 rv = mPACMan->Init(mSystemProxySettings);
1428 } else {
1429 rv = mPACMan->Init(nullptr);
1431 if (NS_FAILED(rv)) {
1432 mPACMan->Shutdown();
1433 mPACMan = nullptr;
1435 return rv;
1438 nsresult nsProtocolProxyService::ResetPACThread() {
1439 if (!mPACMan) return NS_OK;
1441 mPACMan->Shutdown();
1442 mPACMan = nullptr;
1443 return SetupPACThread();
1446 nsresult nsProtocolProxyService::ConfigureFromPAC(const nsCString& spec,
1447 bool forceReload) {
1448 nsresult rv = SetupPACThread();
1449 NS_ENSURE_SUCCESS(rv, rv);
1451 bool autodetect = spec.IsEmpty();
1452 if (!forceReload && ((!autodetect && mPACMan->IsPACURI(spec)) ||
1453 (autodetect && mPACMan->IsUsingWPAD()))) {
1454 return NS_OK;
1457 mFailedProxies.Clear();
1459 mPACMan->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled);
1460 return mPACMan->LoadPACFromURI(spec);
1463 void nsProtocolProxyService::ProcessPACString(const nsCString& pacString,
1464 uint32_t aResolveFlags,
1465 nsIProxyInfo** result) {
1466 if (pacString.IsEmpty()) {
1467 *result = nullptr;
1468 return;
1471 const char* proxies = pacString.get();
1473 nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
1474 while (*proxies) {
1475 proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
1476 if (pi && (pi->mType == kProxyType_HTTPS) && !mProxyOverTLS) {
1477 delete pi;
1478 pi = nullptr;
1481 if (pi) {
1482 if (last) {
1483 NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
1484 last->mNext = pi;
1485 } else {
1486 first = pi;
1488 last = pi;
1491 *result = first;
1494 // nsIProtocolProxyService2
1495 NS_IMETHODIMP
1496 nsProtocolProxyService::ReloadPAC() {
1497 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
1498 if (!prefs) return NS_OK;
1500 int32_t type;
1501 nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
1502 if (NS_FAILED(rv)) return NS_OK;
1504 nsAutoCString pacSpec;
1505 if (type == PROXYCONFIG_PAC) {
1506 prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
1507 } else if (type == PROXYCONFIG_SYSTEM) {
1508 if (mSystemProxySettings) {
1509 AsyncConfigureWPADOrFromPAC(true, true,
1510 StaticPrefs::network_proxy_system_wpad());
1511 } else {
1512 ResetPACThread();
1516 if (!pacSpec.IsEmpty() || type == PROXYCONFIG_WPAD) {
1517 ConfigureFromPAC(pacSpec, true);
1519 return NS_OK;
1522 // When sync interface is removed this can go away too
1523 // The nsPACManCallback portion of this implementation should be run
1524 // off the main thread, because it uses a condvar for signaling and
1525 // the main thread is blocking on that condvar -
1526 // so call nsPACMan::AsyncGetProxyForURI() with
1527 // a false mainThreadResponse parameter.
1528 class nsAsyncBridgeRequest final : public nsPACManCallback {
1529 NS_DECL_THREADSAFE_ISUPPORTS
1531 nsAsyncBridgeRequest()
1532 : mMutex("nsDeprecatedCallback"),
1533 mCondVar(mMutex, "nsDeprecatedCallback") {}
1535 void OnQueryComplete(nsresult status, const nsACString& pacString,
1536 const nsACString& newPACURL) override {
1537 MutexAutoLock lock(mMutex);
1538 mCompleted = true;
1539 mStatus = status;
1540 mPACString = pacString;
1541 mPACURL = newPACURL;
1542 mCondVar.Notify();
1545 void Lock() MOZ_CAPABILITY_ACQUIRE(mMutex) { mMutex.Lock(); }
1546 void Unlock() MOZ_CAPABILITY_RELEASE(mMutex) { mMutex.Unlock(); }
1547 void Wait() { mCondVar.Wait(TimeDuration::FromSeconds(3)); }
1549 private:
1550 ~nsAsyncBridgeRequest() = default;
1552 friend class nsProtocolProxyService;
1554 Mutex mMutex;
1555 CondVar mCondVar;
1557 nsresult mStatus MOZ_GUARDED_BY(mMutex){NS_OK};
1558 nsCString mPACString MOZ_GUARDED_BY(mMutex);
1559 nsCString mPACURL MOZ_GUARDED_BY(mMutex);
1560 bool mCompleted MOZ_GUARDED_BY(mMutex){false};
1562 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
1564 nsresult nsProtocolProxyService::AsyncResolveInternal(
1565 nsIChannel* channel, uint32_t flags, nsIProtocolProxyCallback* callback,
1566 nsICancelable** result, bool isSyncOK,
1567 nsISerialEventTarget* mainThreadEventTarget) {
1568 NS_ENSURE_ARG_POINTER(channel);
1569 NS_ENSURE_ARG_POINTER(callback);
1571 nsCOMPtr<nsIURI> uri;
1572 nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
1573 if (NS_FAILED(rv)) return rv;
1575 *result = nullptr;
1576 RefPtr<nsAsyncResolveRequest> ctx =
1577 new nsAsyncResolveRequest(this, channel, flags, callback);
1579 nsProtocolInfo info;
1580 rv = GetProtocolInfo(uri, &info);
1581 if (NS_FAILED(rv)) return rv;
1583 nsCOMPtr<nsIProxyInfo> pi;
1584 bool usePACThread;
1586 // adapt to realtime changes in the system proxy service
1587 if (mProxyConfig == PROXYCONFIG_SYSTEM &&
1588 !StaticPrefs::network_proxy_system_wpad()) {
1589 nsCOMPtr<nsISystemProxySettings> sp2 =
1590 do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
1591 if (sp2 != mSystemProxySettings) {
1592 mSystemProxySettings = sp2;
1593 ResetPACThread();
1597 rv = SetupPACThread(mainThreadEventTarget);
1598 if (NS_FAILED(rv)) {
1599 return rv;
1602 // SystemProxySettings and PAC files can block the main thread
1603 // but if neither of them are in use, we can just do the work
1604 // right here and directly invoke the callback
1606 rv =
1607 Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
1608 if (NS_FAILED(rv)) return rv;
1610 if (!usePACThread || !mPACMan) {
1611 // we can do it locally
1612 rv = ctx->ProcessLocally(info, pi, isSyncOK);
1613 if (NS_SUCCEEDED(rv) && !isSyncOK) {
1614 ctx.forget(result);
1616 return rv;
1619 // else kick off a PAC thread query
1620 rv = mPACMan->AsyncGetProxyForURI(uri, ctx, flags, true);
1621 if (NS_SUCCEEDED(rv)) ctx.forget(result);
1622 return rv;
1625 // nsIProtocolProxyService
1626 NS_IMETHODIMP
1627 nsProtocolProxyService::AsyncResolve2(
1628 nsIChannel* channel, uint32_t flags, nsIProtocolProxyCallback* callback,
1629 nsISerialEventTarget* mainThreadEventTarget, nsICancelable** result) {
1630 return AsyncResolveInternal(channel, flags, callback, result, true,
1631 mainThreadEventTarget);
1634 NS_IMETHODIMP
1635 nsProtocolProxyService::AsyncResolve(
1636 nsISupports* channelOrURI, uint32_t flags,
1637 nsIProtocolProxyCallback* callback,
1638 nsISerialEventTarget* mainThreadEventTarget, nsICancelable** result) {
1639 nsresult rv;
1640 // Check if we got a channel:
1641 nsCOMPtr<nsIChannel> channel = do_QueryInterface(channelOrURI);
1642 if (!channel) {
1643 nsCOMPtr<nsIURI> uri = do_QueryInterface(channelOrURI);
1644 if (!uri) {
1645 return NS_ERROR_NO_INTERFACE;
1648 // creating a temporary channel from the URI which is not
1649 // used to perform any network loads, hence its safe to
1650 // use systemPrincipal as the loadingPrincipal.
1651 rv = NS_NewChannel(getter_AddRefs(channel), uri,
1652 nsContentUtils::GetSystemPrincipal(),
1653 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
1654 nsIContentPolicy::TYPE_OTHER);
1655 NS_ENSURE_SUCCESS(rv, rv);
1658 return AsyncResolveInternal(channel, flags, callback, result, false,
1659 mainThreadEventTarget);
1662 NS_IMETHODIMP
1663 nsProtocolProxyService::NewProxyInfo(
1664 const nsACString& aType, const nsACString& aHost, int32_t aPort,
1665 const nsACString& aProxyAuthorizationHeader,
1666 const nsACString& aConnectionIsolationKey, uint32_t aFlags,
1667 uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
1668 nsIProxyInfo** aResult) {
1669 return NewProxyInfoWithAuth(aType, aHost, aPort, ""_ns, ""_ns,
1670 aProxyAuthorizationHeader,
1671 aConnectionIsolationKey, aFlags, aFailoverTimeout,
1672 aFailoverProxy, aResult);
1675 NS_IMETHODIMP
1676 nsProtocolProxyService::NewProxyInfoWithAuth(
1677 const nsACString& aType, const nsACString& aHost, int32_t aPort,
1678 const nsACString& aUsername, const nsACString& aPassword,
1679 const nsACString& aProxyAuthorizationHeader,
1680 const nsACString& aConnectionIsolationKey, uint32_t aFlags,
1681 uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
1682 nsIProxyInfo** aResult) {
1683 static const char* types[] = {kProxyType_HTTP, kProxyType_HTTPS,
1684 kProxyType_SOCKS, kProxyType_SOCKS4,
1685 kProxyType_DIRECT};
1687 // resolve type; this allows us to avoid copying the type string into each
1688 // proxy info instance. we just reference the string literals directly :)
1689 const char* type = nullptr;
1690 for (auto& t : types) {
1691 if (aType.LowerCaseEqualsASCII(t)) {
1692 type = t;
1693 break;
1696 NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
1698 // We have only implemented username/password for SOCKS proxies.
1699 if ((!aUsername.IsEmpty() || !aPassword.IsEmpty()) &&
1700 !aType.LowerCaseEqualsASCII(kProxyType_SOCKS) &&
1701 !aType.LowerCaseEqualsASCII(kProxyType_SOCKS4)) {
1702 return NS_ERROR_NOT_IMPLEMENTED;
1705 return NewProxyInfo_Internal(type, aHost, aPort, aUsername, aPassword,
1706 aProxyAuthorizationHeader,
1707 aConnectionIsolationKey, aFlags,
1708 aFailoverTimeout, aFailoverProxy, 0, aResult);
1711 NS_IMETHODIMP
1712 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo* aProxy, nsIURI* aURI,
1713 nsresult aStatus,
1714 nsIProxyInfo** aResult) {
1715 // Failover is supported through a variety of methods including:
1716 // * PAC scripts (PROXYCONFIG_PAC and PROXYCONFIG_WPAD)
1717 // * System proxy
1718 // * Extensions
1719 // With extensions the mProxyConfig can be any type and the extension
1720 // is still involved in the proxy filtering. It may have also supplied
1721 // any number of failover proxies. We cannot determine what the mix is
1722 // here, so we will attempt to get a failover regardless of the config
1723 // type. MANUAL configuration will not disable a proxy.
1725 // Verify that |aProxy| is one of our nsProxyInfo objects.
1726 nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
1727 NS_ENSURE_ARG(pi);
1728 // OK, the QI checked out. We can proceed.
1730 // Remember that this proxy is down. If the user has manually configured some
1731 // proxies we do not want to disable them.
1732 if (mProxyConfig != PROXYCONFIG_MANUAL) {
1733 DisableProxy(pi);
1736 // NOTE: At this point, we might want to prompt the user if we have
1737 // not already tried going DIRECT. This is something that the
1738 // classic codebase supported; however, IE6 does not prompt.
1740 if (!pi->mNext) return NS_ERROR_NOT_AVAILABLE;
1742 LOG(("PAC failover from %s %s:%d to %s %s:%d\n", pi->mType, pi->mHost.get(),
1743 pi->mPort, pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
1745 *aResult = do_AddRef(pi->mNext).take();
1746 return NS_OK;
1749 namespace { // anon
1751 class ProxyFilterPositionComparator {
1752 using FilterLinkRef = RefPtr<nsProtocolProxyService::FilterLink>;
1754 public:
1755 bool Equals(const FilterLinkRef& a, const FilterLinkRef& b) const {
1756 return a->position == b->position;
1758 bool LessThan(const FilterLinkRef& a, const FilterLinkRef& b) const {
1759 return a->position < b->position;
1763 class ProxyFilterObjectComparator {
1764 using FilterLinkRef = RefPtr<nsProtocolProxyService::FilterLink>;
1766 public:
1767 bool Equals(const FilterLinkRef& link, const nsISupports* obj) const {
1768 return obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->filter)) ||
1769 obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->channelFilter));
1773 } // namespace
1775 nsresult nsProtocolProxyService::InsertFilterLink(RefPtr<FilterLink>&& link) {
1776 LOG(("nsProtocolProxyService::InsertFilterLink filter=%p", link.get()));
1778 if (mIsShutdown) {
1779 return NS_ERROR_FAILURE;
1782 // If we add a new element with the same position as an existing one, we want
1783 // to preserve the insertion order to avoid surprises.
1784 mFilters.InsertElementSorted(link, ProxyFilterPositionComparator());
1786 NotifyProxyConfigChangedInternal();
1788 return NS_OK;
1791 NS_IMETHODIMP
1792 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter* filter,
1793 uint32_t position) {
1794 UnregisterFilter(filter); // remove this filter if we already have it
1796 RefPtr<FilterLink> link = new FilterLink(position, filter);
1797 return InsertFilterLink(std::move(link));
1800 NS_IMETHODIMP
1801 nsProtocolProxyService::RegisterChannelFilter(
1802 nsIProtocolProxyChannelFilter* channelFilter, uint32_t position) {
1803 UnregisterChannelFilter(
1804 channelFilter); // remove this filter if we already have it
1806 RefPtr<FilterLink> link = new FilterLink(position, channelFilter);
1807 return InsertFilterLink(std::move(link));
1810 nsresult nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject) {
1811 LOG(("nsProtocolProxyService::RemoveFilterLink target=%p", givenObject));
1813 nsresult rv =
1814 mFilters.RemoveElement(givenObject, ProxyFilterObjectComparator())
1815 ? NS_OK
1816 : NS_ERROR_UNEXPECTED;
1817 if (NS_SUCCEEDED(rv)) {
1818 NotifyProxyConfigChangedInternal();
1821 return rv;
1824 NS_IMETHODIMP
1825 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter* filter) {
1826 // QI to nsISupports so we can safely test object identity.
1827 nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
1828 return RemoveFilterLink(givenObject);
1831 NS_IMETHODIMP
1832 nsProtocolProxyService::UnregisterChannelFilter(
1833 nsIProtocolProxyChannelFilter* channelFilter) {
1834 // QI to nsISupports so we can safely test object identity.
1835 nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
1836 return RemoveFilterLink(givenObject);
1839 NS_IMETHODIMP
1840 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType) {
1841 *aProxyConfigType = mProxyConfig;
1842 return NS_OK;
1845 void nsProtocolProxyService::LoadHostFilters(const nsACString& aFilters) {
1846 if (mIsShutdown) {
1847 return;
1850 // check to see the owners flag? /!?/ TODO
1851 if (mHostFiltersArray.Length() > 0) {
1852 mHostFiltersArray.Clear();
1855 // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref
1856 // string
1857 mFilterLocalHosts = false;
1859 if (aFilters.IsEmpty()) {
1860 return;
1864 // filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
1865 // filters = filter *( "," LWS filter)
1867 mozilla::Tokenizer t(aFilters);
1868 mozilla::Tokenizer::Token token;
1869 bool eof = false;
1870 // while (*filters) {
1871 while (!eof) {
1872 // skip over spaces and ,
1873 t.SkipWhites();
1874 while (t.CheckChar(',')) {
1875 t.SkipWhites();
1878 nsAutoCString portStr;
1879 nsAutoCString hostStr;
1880 nsAutoCString maskStr;
1881 t.Record();
1883 bool parsingIPv6 = false;
1884 bool parsingPort = false;
1885 bool parsingMask = false;
1886 while (t.Next(token)) {
1887 if (token.Equals(mozilla::Tokenizer::Token::EndOfFile())) {
1888 eof = true;
1889 break;
1891 if (token.Equals(mozilla::Tokenizer::Token::Char(',')) ||
1892 token.Type() == mozilla::Tokenizer::TOKEN_WS) {
1893 break;
1896 if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
1897 parsingIPv6 = true;
1898 continue;
1901 if (!parsingIPv6 && token.Equals(mozilla::Tokenizer::Token::Char(':'))) {
1902 // Port is starting. Claim the previous as host.
1903 if (parsingMask) {
1904 t.Claim(maskStr);
1905 } else {
1906 t.Claim(hostStr);
1908 t.Record();
1909 parsingPort = true;
1910 continue;
1913 if (token.Equals(mozilla::Tokenizer::Token::Char('/'))) {
1914 t.Claim(hostStr);
1915 t.Record();
1916 parsingMask = true;
1917 continue;
1920 if (token.Equals(mozilla::Tokenizer::Token::Char(']'))) {
1921 parsingIPv6 = false;
1922 continue;
1925 if (!parsingPort && !parsingMask) {
1926 t.Claim(hostStr);
1927 } else if (parsingPort) {
1928 t.Claim(portStr);
1929 } else if (parsingMask) {
1930 t.Claim(maskStr);
1931 } else {
1932 NS_WARNING("Could not parse this rule");
1933 continue;
1936 if (hostStr.IsEmpty()) {
1937 continue;
1940 // If the current host filter is "<local>", then all local (i.e.
1941 // no dots in the hostname) hosts should bypass the proxy
1942 if (hostStr.EqualsIgnoreCase("<local>")) {
1943 mFilterLocalHosts = true;
1944 LOG(
1945 ("loaded filter for local hosts "
1946 "(plain host names, no dots)\n"));
1947 // Continue to next host filter;
1948 continue;
1951 // For all other host filters, create HostInfo object and add to list
1952 HostInfo* hinfo = new HostInfo();
1953 nsresult rv = NS_OK;
1955 int32_t port = portStr.ToInteger(&rv);
1956 if (NS_FAILED(rv)) {
1957 port = 0;
1959 hinfo->port = port;
1961 int32_t maskLen = maskStr.ToInteger(&rv);
1962 if (NS_FAILED(rv)) {
1963 maskLen = 128;
1966 // PR_StringToNetAddr can't parse brackets enclosed IPv6
1967 nsAutoCString addrString = hostStr;
1968 if (hostStr.First() == '[' && hostStr.Last() == ']') {
1969 addrString = Substring(hostStr, 1, hostStr.Length() - 2);
1972 PRNetAddr addr;
1973 if (PR_StringToNetAddr(addrString.get(), &addr) == PR_SUCCESS) {
1974 hinfo->is_ipaddr = true;
1975 hinfo->ip.family = PR_AF_INET6; // we always store address as IPv6
1976 hinfo->ip.mask_len = maskLen;
1978 if (hinfo->ip.mask_len == 0) {
1979 NS_WARNING("invalid mask");
1980 goto loser;
1983 if (addr.raw.family == PR_AF_INET) {
1984 // convert to IPv4-mapped address
1985 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
1986 // adjust mask_len accordingly
1987 if (hinfo->ip.mask_len <= 32) hinfo->ip.mask_len += 96;
1988 } else if (addr.raw.family == PR_AF_INET6) {
1989 // copy the address
1990 memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1991 } else {
1992 NS_WARNING("unknown address family");
1993 goto loser;
1996 // apply mask to IPv6 address
1997 proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
1998 } else {
1999 nsAutoCString host;
2000 if (hostStr.First() == '*') {
2001 host = Substring(hostStr, 1);
2002 } else {
2003 host = hostStr;
2006 if (host.IsEmpty()) {
2007 hinfo->name.host = nullptr;
2008 goto loser;
2011 hinfo->name.host_len = host.Length();
2013 hinfo->is_ipaddr = false;
2014 hinfo->name.host = ToNewCString(host, mozilla::fallible);
2016 if (!hinfo->name.host) goto loser;
2019 // #define DEBUG_DUMP_FILTERS
2020 #ifdef DEBUG_DUMP_FILTERS
2021 printf("loaded filter[%zu]:\n", mHostFiltersArray.Length());
2022 printf(" is_ipaddr = %u\n", hinfo->is_ipaddr);
2023 printf(" port = %u\n", hinfo->port);
2024 printf(" host = %s\n", hostStr.get());
2025 if (hinfo->is_ipaddr) {
2026 printf(" ip.family = %x\n", hinfo->ip.family);
2027 printf(" ip.mask_len = %u\n", hinfo->ip.mask_len);
2029 PRNetAddr netAddr;
2030 PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
2031 memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
2033 char buf[256];
2034 PR_NetAddrToString(&netAddr, buf, sizeof(buf));
2036 printf(" ip.addr = %s\n", buf);
2037 } else {
2038 printf(" name.host = %s\n", hinfo->name.host);
2040 #endif
2042 mHostFiltersArray.AppendElement(hinfo);
2043 hinfo = nullptr;
2044 loser:
2045 delete hinfo;
2049 nsresult nsProtocolProxyService::GetProtocolInfo(nsIURI* uri,
2050 nsProtocolInfo* info) {
2051 AssertIsOnMainThread();
2052 MOZ_ASSERT(uri, "URI is null");
2053 MOZ_ASSERT(info, "info is null");
2055 nsresult rv;
2057 rv = uri->GetScheme(info->scheme);
2058 if (NS_FAILED(rv)) return rv;
2060 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
2061 if (NS_FAILED(rv)) return rv;
2063 rv = ios->GetDynamicProtocolFlags(uri, &info->flags);
2064 if (NS_FAILED(rv)) return rv;
2066 rv = ios->GetDefaultPort(info->scheme.get(), &info->defaultPort);
2067 return rv;
2070 nsresult nsProtocolProxyService::NewProxyInfo_Internal(
2071 const char* aType, const nsACString& aHost, int32_t aPort,
2072 const nsACString& aUsername, const nsACString& aPassword,
2073 const nsACString& aProxyAuthorizationHeader,
2074 const nsACString& aConnectionIsolationKey, uint32_t aFlags,
2075 uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
2076 uint32_t aResolveFlags, nsIProxyInfo** aResult) {
2077 if (aPort <= 0) aPort = -1;
2079 nsCOMPtr<nsProxyInfo> failover;
2080 if (aFailoverProxy) {
2081 failover = do_QueryInterface(aFailoverProxy);
2082 NS_ENSURE_ARG(failover);
2085 RefPtr<nsProxyInfo> proxyInfo = new nsProxyInfo();
2087 proxyInfo->mType = aType;
2088 proxyInfo->mHost = aHost;
2089 proxyInfo->mPort = aPort;
2090 proxyInfo->mUsername = aUsername;
2091 proxyInfo->mPassword = aPassword;
2092 proxyInfo->mFlags = aFlags;
2093 proxyInfo->mResolveFlags = aResolveFlags;
2094 proxyInfo->mTimeout =
2095 aFailoverTimeout == UINT32_MAX ? mFailedProxyTimeout : aFailoverTimeout;
2096 proxyInfo->mProxyAuthorizationHeader = aProxyAuthorizationHeader;
2097 proxyInfo->mConnectionIsolationKey = aConnectionIsolationKey;
2098 failover.swap(proxyInfo->mNext);
2100 proxyInfo.forget(aResult);
2101 return NS_OK;
2104 const char* nsProtocolProxyService::SOCKSProxyType() {
2105 if (mSOCKSProxyVersion == nsIProxyInfo::SOCKS_V4) {
2106 return kProxyType_SOCKS4;
2108 return kProxyType_SOCKS;
2111 bool nsProtocolProxyService::SOCKSRemoteDNS() {
2112 return (mSOCKSProxyVersion == nsIProxyInfo::SOCKS_V4 &&
2113 mSOCKS4ProxyRemoteDNS) ||
2114 (mSOCKSProxyVersion == nsIProxyInfo::SOCKS_V5 &&
2115 mSOCKS5ProxyRemoteDNS);
2118 nsresult nsProtocolProxyService::Resolve_Internal(nsIChannel* channel,
2119 const nsProtocolInfo& info,
2120 uint32_t flags,
2121 bool* usePACThread,
2122 nsIProxyInfo** result) {
2123 NS_ENSURE_ARG_POINTER(channel);
2125 *usePACThread = false;
2126 *result = nullptr;
2128 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY)) {
2129 return NS_OK; // Can't proxy this (filters may not override)
2132 nsCOMPtr<nsIURI> uri;
2133 nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
2134 if (NS_FAILED(rv)) return rv;
2136 // See bug #586908.
2137 // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
2138 // here means that we will not use a proxy for this connection.
2139 if (mPACMan && mPACMan->IsPACURI(uri)) return NS_OK;
2141 // if proxies are enabled and this host:port combo is supposed to use a
2142 // proxy, check for a proxy.
2143 if ((mProxyConfig == PROXYCONFIG_DIRECT) ||
2144 !CanUseProxy(uri, info.defaultPort)) {
2145 return NS_OK;
2148 bool mainThreadOnly;
2149 if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM &&
2150 NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
2151 !mainThreadOnly) {
2152 *usePACThread = true;
2153 return NS_OK;
2156 if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) {
2157 // If the system proxy setting implementation is not threadsafe (e.g
2158 // linux gconf), we'll do it inline here. Such implementations promise
2159 // not to block
2160 // bug 1366133: this block uses GetPACURI & GetProxyForURI, which may
2161 // hang on Windows platform. Fortunately, current implementation on
2162 // Windows is not main thread only, so we are safe here.
2164 nsAutoCString PACURI;
2165 nsAutoCString pacString;
2167 if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
2168 !PACURI.IsEmpty()) {
2169 // There is a PAC URI configured. If it is unchanged, then
2170 // just execute the PAC thread. If it is changed then load
2171 // the new value
2173 if (mPACMan && mPACMan->IsPACURI(PACURI)) {
2174 // unchanged
2175 *usePACThread = true;
2176 return NS_OK;
2179 ConfigureFromPAC(PACURI, false);
2180 return NS_OK;
2183 nsAutoCString spec;
2184 nsAutoCString host;
2185 nsAutoCString scheme;
2186 int32_t port = -1;
2188 uri->GetAsciiSpec(spec);
2189 uri->GetAsciiHost(host);
2190 uri->GetScheme(scheme);
2191 uri->GetPort(&port);
2193 if (flags & RESOLVE_PREFER_SOCKS_PROXY) {
2194 LOG(("Ignoring RESOLVE_PREFER_SOCKS_PROXY for system proxy setting\n"));
2195 } else if (flags & RESOLVE_PREFER_HTTPS_PROXY) {
2196 scheme.AssignLiteral("https");
2197 } else if (flags & RESOLVE_IGNORE_URI_SCHEME) {
2198 scheme.AssignLiteral("http");
2201 // now try the system proxy settings for this particular url
2202 if (NS_SUCCEEDED(mSystemProxySettings->GetProxyForURI(spec, scheme, host,
2203 port, pacString))) {
2204 nsCOMPtr<nsIProxyInfo> pi;
2205 ProcessPACString(pacString, 0, getter_AddRefs(pi));
2207 if (flags & RESOLVE_PREFER_SOCKS_PROXY &&
2208 flags & RESOLVE_PREFER_HTTPS_PROXY) {
2209 nsAutoCString type;
2210 pi->GetType(type);
2211 // DIRECT from ProcessPACString indicates that system proxy settings
2212 // are not configured to use SOCKS proxy. Try https proxy as a
2213 // secondary preferrable proxy. This is mainly for websocket whose
2214 // proxy precedence is SOCKS > HTTPS > DIRECT.
2215 if (type.EqualsLiteral(kProxyType_DIRECT)) {
2216 scheme.AssignLiteral(kProxyType_HTTPS);
2217 if (NS_SUCCEEDED(mSystemProxySettings->GetProxyForURI(
2218 spec, scheme, host, port, pacString))) {
2219 ProcessPACString(pacString, 0, getter_AddRefs(pi));
2223 pi.forget(result);
2224 return NS_OK;
2228 // if proxies are enabled and this host:port combo is supposed to use a
2229 // proxy, check for a proxy.
2230 if (mProxyConfig == PROXYCONFIG_DIRECT ||
2231 (mProxyConfig == PROXYCONFIG_MANUAL &&
2232 !CanUseProxy(uri, info.defaultPort))) {
2233 return NS_OK;
2236 // Proxy auto config magic...
2237 if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD ||
2238 StaticPrefs::network_proxy_system_wpad()) {
2239 // Do not query PAC now.
2240 *usePACThread = true;
2241 return NS_OK;
2244 // If we aren't in manual proxy configuration mode then we don't
2245 // want to honor any manual specific prefs that might be still set
2246 if (mProxyConfig != PROXYCONFIG_MANUAL) return NS_OK;
2248 // proxy info values for manual configuration mode
2249 const char* type = nullptr;
2250 const nsACString* host = nullptr;
2251 int32_t port = -1;
2253 uint32_t proxyFlags = 0;
2255 if ((flags & RESOLVE_PREFER_SOCKS_PROXY) && !mSOCKSProxyTarget.IsEmpty() &&
2256 (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
2257 host = &mSOCKSProxyTarget;
2258 type = SOCKSProxyType();
2259 if (SOCKSRemoteDNS()) {
2260 proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
2262 port = mSOCKSProxyPort;
2263 } else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
2264 !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
2265 host = &mHTTPSProxyHost;
2266 type = kProxyType_HTTP;
2267 port = mHTTPSProxyPort;
2268 } else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
2269 ((flags & RESOLVE_IGNORE_URI_SCHEME) ||
2270 info.scheme.EqualsLiteral("http"))) {
2271 host = &mHTTPProxyHost;
2272 type = kProxyType_HTTP;
2273 port = mHTTPProxyPort;
2274 } else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
2275 !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
2276 info.scheme.EqualsLiteral("https")) {
2277 host = &mHTTPSProxyHost;
2278 type = kProxyType_HTTP;
2279 port = mHTTPSProxyPort;
2280 } else if (!mSOCKSProxyTarget.IsEmpty() &&
2281 (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
2282 host = &mSOCKSProxyTarget;
2283 type = SOCKSProxyType();
2284 if (SOCKSRemoteDNS()) {
2285 proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
2287 port = mSOCKSProxyPort;
2290 if (type) {
2291 rv = NewProxyInfo_Internal(type, *host, port, ""_ns, ""_ns, ""_ns, ""_ns,
2292 proxyFlags, UINT32_MAX, nullptr, flags, result);
2293 if (NS_FAILED(rv)) return rv;
2296 return NS_OK;
2299 void nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo* aProxy) {
2300 // Disable Prefetch in the DNS service if a proxy is in use.
2301 if (!aProxy) return;
2303 nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
2304 if (!pi || !pi->mType || pi->mType == kProxyType_DIRECT) return;
2306 if (StaticPrefs::network_dns_prefetch_via_proxy()) {
2307 return;
2310 // To avoid getting DNS service recursively, we directly use
2311 // GetXPCOMSingleton().
2312 nsCOMPtr<nsIDNSService> dns = nsDNSService::GetXPCOMSingleton();
2313 if (!dns) return;
2314 nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
2315 if (!pdns) return;
2317 // We lose the prefetch optimization for the life of the dns service.
2318 pdns->SetPrefetchEnabled(false);
2321 void nsProtocolProxyService::CopyFilters(nsTArray<RefPtr<FilterLink>>& aCopy) {
2322 MOZ_ASSERT(aCopy.Length() == 0);
2323 aCopy.AppendElements(mFilters);
2326 bool nsProtocolProxyService::ApplyFilter(
2327 FilterLink const* filterLink, nsIChannel* channel,
2328 const nsProtocolInfo& info, nsCOMPtr<nsIProxyInfo> list,
2329 nsIProxyProtocolFilterResult* callback) {
2330 nsresult rv;
2332 // We prune the proxy list prior to invoking each filter. This may be
2333 // somewhat inefficient, but it seems like a good idea since we want each
2334 // filter to "see" a valid proxy list.
2335 PruneProxyInfo(info, list);
2337 if (filterLink->filter) {
2338 nsCOMPtr<nsIURI> uri;
2339 Unused << GetProxyURI(channel, getter_AddRefs(uri));
2340 if (!uri) {
2341 return false;
2344 rv = filterLink->filter->ApplyFilter(uri, list, callback);
2345 return NS_SUCCEEDED(rv);
2348 if (filterLink->channelFilter) {
2349 rv = filterLink->channelFilter->ApplyFilter(channel, list, callback);
2350 return NS_SUCCEEDED(rv);
2353 return false;
2356 void nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo& info,
2357 nsIProxyInfo** list) {
2358 if (!*list) return;
2360 LOG(("nsProtocolProxyService::PruneProxyInfo ENTER list=%p", *list));
2362 nsProxyInfo* head = nullptr;
2363 CallQueryInterface(*list, &head);
2364 if (!head) {
2365 MOZ_ASSERT_UNREACHABLE("nsIProxyInfo must QI to nsProxyInfo");
2366 return;
2368 NS_RELEASE(*list);
2370 // Pruning of disabled proxies works like this:
2371 // - If all proxies are disabled, return the full list
2372 // - Otherwise, remove the disabled proxies.
2374 // Pruning of disallowed proxies works like this:
2375 // - If the protocol handler disallows the proxy, then we disallow it.
2377 // Start by removing all disallowed proxies if required:
2378 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
2379 nsProxyInfo *last = nullptr, *iter = head;
2380 while (iter) {
2381 if ((iter->Type() == kProxyType_HTTP) ||
2382 (iter->Type() == kProxyType_HTTPS)) {
2383 // reject!
2384 if (last) {
2385 last->mNext = iter->mNext;
2386 } else {
2387 head = iter->mNext;
2389 nsProxyInfo* next = iter->mNext;
2390 iter->mNext = nullptr;
2391 iter->Release();
2392 iter = next;
2393 } else {
2394 last = iter;
2395 iter = iter->mNext;
2398 if (!head) {
2399 return;
2403 // Scan to see if all remaining non-direct proxies are disabled. If so, then
2404 // we'll just bail and return them all. Otherwise, we'll go and prune the
2405 // disabled ones.
2407 bool allNonDirectProxiesDisabled = true;
2409 nsProxyInfo* iter;
2410 for (iter = head; iter; iter = iter->mNext) {
2411 if (!IsProxyDisabled(iter) && iter->mType != kProxyType_DIRECT) {
2412 allNonDirectProxiesDisabled = false;
2413 break;
2417 if (allNonDirectProxiesDisabled &&
2418 StaticPrefs::network_proxy_retry_failed_proxies()) {
2419 LOG(("All proxies are disabled, so trying all again"));
2420 } else {
2421 // remove any disabled proxies.
2422 nsProxyInfo* last = nullptr;
2423 for (iter = head; iter;) {
2424 if (IsProxyDisabled(iter)) {
2425 // reject!
2426 nsProxyInfo* reject = iter;
2428 iter = iter->mNext;
2429 if (last) {
2430 last->mNext = iter;
2431 } else {
2432 head = iter;
2435 reject->mNext = nullptr;
2436 NS_RELEASE(reject);
2437 continue;
2440 // since we are about to use this proxy, make sure it is not on
2441 // the disabled proxy list. we'll add it back to that list if
2442 // we have to (in GetFailoverForProxy).
2444 // XXX(darin): It might be better to do this as a final pass.
2446 EnableProxy(iter);
2448 last = iter;
2449 iter = iter->mNext;
2453 // if only DIRECT was specified then return no proxy info, and we're done.
2454 if (head && !head->mNext && head->mType == kProxyType_DIRECT) {
2455 NS_RELEASE(head);
2458 *list = head; // Transfer ownership
2460 LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list));
2463 bool nsProtocolProxyService::GetIsPACLoading() {
2464 return mPACMan && mPACMan->IsLoading();
2467 NS_IMETHODIMP
2468 nsProtocolProxyService::AddProxyConfigCallback(
2469 nsIProxyConfigChangedCallback* aCallback) {
2470 MOZ_ASSERT(NS_IsMainThread());
2471 if (!aCallback) {
2472 return NS_ERROR_INVALID_ARG;
2475 mProxyConfigChangedCallbacks.AppendElement(aCallback);
2476 return NS_OK;
2479 NS_IMETHODIMP
2480 nsProtocolProxyService::RemoveProxyConfigCallback(
2481 nsIProxyConfigChangedCallback* aCallback) {
2482 MOZ_ASSERT(NS_IsMainThread());
2484 mProxyConfigChangedCallbacks.RemoveElement(aCallback);
2485 return NS_OK;
2488 NS_IMETHODIMP
2489 nsProtocolProxyService::NotifyProxyConfigChangedInternal() {
2490 LOG(("nsProtocolProxyService::NotifyProxyConfigChangedInternal"));
2491 MOZ_ASSERT(NS_IsMainThread());
2493 for (const auto& callback : mProxyConfigChangedCallbacks) {
2494 callback->OnProxyConfigChanged();
2496 return NS_OK;
2499 } // namespace net
2500 } // namespace mozilla