Bug 1526591 - Remove devtools.inspector.shapesHighlighter.enabled pref. r=rcaliman
[gecko.git] / netwerk / base / nsProtocolProxyService.cpp
blobe74aeb89e7e244b718993fff8e77de4ca05fa9c9
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"
29 #include "nsString.h"
30 #include "nsNetUtil.h"
31 #include "nsNetCID.h"
32 #include "plstr.h"
33 #include "prnetdb.h"
34 #include "nsPACMan.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 //----------------------------------------------------------------------------
47 namespace mozilla {
48 namespace net {
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[];
57 #undef LOG
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 {
69 nsAutoCString scheme;
70 uint32_t flags;
71 int32_t defaultPort;
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) {
79 nsresult rv = NS_OK;
80 nsCOMPtr<nsIURI> proxyURI;
81 nsCOMPtr<nsIHttpChannelInternal> httpChannel(do_QueryInterface(channel));
82 if (httpChannel) {
83 rv = httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
85 if (!proxyURI) {
86 rv = channel->GetURI(getter_AddRefs(proxyURI));
88 if (NS_FAILED(rv)) {
89 return rv;
91 proxyURI.forget(aOut);
92 return NS_OK;
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,
101 f));
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",
107 this, cf));
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 {
122 public:
123 NS_DECL_THREADSAFE_ISUPPORTS
125 nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIChannel *channel,
126 uint32_t aResolveFlags,
127 nsIProtocolProxyCallback *callback)
128 : mStatus(NS_OK),
129 mDispatched(false),
130 mResolveFlags(aResolveFlags),
131 mPPS(pps),
132 mXPComPPS(pps),
133 mChannel(channel),
134 mCallback(callback) {
135 NS_ASSERTION(mCallback, "null callback");
138 private:
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
145 if (mChannel) {
146 NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mChannel",
147 mChannel.forget());
150 if (mCallback) {
151 NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mCallback",
152 mCallback.forget());
155 if (mProxyInfo) {
156 NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mProxyInfo",
157 mProxyInfo.forget());
160 if (mXPComPPS) {
161 NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mXPComPPS",
162 mXPComPPS.forget());
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,
171 public nsIRunnable,
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
178 NS_DECL_NSIRUNNABLE
179 NS_DECL_NSICANCELABLE
181 typedef std::function<nsresult(nsAsyncResolveRequest *, nsIProxyInfo *,
182 bool)>
183 Callback;
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 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
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<nsIEventTarget> mProcessingThread;
234 public:
235 nsresult ProcessLocally(nsProtocolInfo &info, nsIProxyInfo *pi,
236 bool isSyncOK) {
237 SetResult(NS_OK, pi);
239 auto consumeFiltersResult = [isSyncOK](nsAsyncResolveRequest *ctx,
240 nsIProxyInfo *pi,
241 bool aCalledAsync) -> nsresult {
242 ctx->SetResult(NS_OK, pi);
243 if (isSyncOK || aCalledAsync) {
244 ctx->Run();
245 return NS_OK;
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) {
257 mStatus = status;
258 mProxyInfo = pi;
261 NS_IMETHOD Run() override {
262 if (mCallback) DoCallback();
263 return NS_OK;
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
282 return NS_OK;
284 nsresult rv = NS_DispatchToCurrentThread(this);
285 if (NS_FAILED(rv))
286 NS_WARNING("unable to dispatch callback event");
287 else {
288 mDispatched = true;
289 return NS_OK;
292 mCallback = nullptr; // break possible reference cycle
293 return rv;
296 private:
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) {
306 mStatus = status;
307 mPACString = pacString;
308 mPACURL = newPACURL;
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.
314 DoCallback();
317 void DoCallback() {
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;");
324 mStatus = NS_OK;
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
338 nsProtocolInfo info;
339 mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
341 auto consumeFiltersResult = [pacAvailable](nsAsyncResolveRequest *self,
342 nsIProxyInfo *pi,
343 bool async) -> nsresult {
344 LOG(("DoCallback::consumeFiltersResult this=%p, pi=%p, async=%d", self,
345 pi, async));
347 self->mProxyInfo = pi;
349 if (pacAvailable) {
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);
361 return NS_OK;
364 if (NS_SUCCEEDED(mStatus)) {
365 mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
366 // This may call consumeFiltersResult() directly.
367 mAsyncFilterApplier->AsyncProcess(this);
368 return;
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);
387 if (NS_FAILED(rv))
388 mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
390 // do not call onproxyavailable() in SUCCESS case - the newRequest will
391 // take care of that
392 } else {
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
401 // in the dtor
402 mCallback = nullptr; // in case the callback holds an owning ref to us
403 mPPS = nullptr;
404 mXPComPPS = nullptr;
405 mChannel = nullptr;
406 mProxyInfo = nullptr;
409 private:
410 nsresult mStatus;
411 nsCString mPACString;
412 nsCString mPACURL;
413 bool mDispatched;
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)
432 : mInfo(aInfo),
433 mCallback(aCallback),
434 mNextFilterIndex(0),
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();
462 mRequest = aRequest;
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.
470 do {
471 MOZ_ASSERT(!mProcessingInLoop);
473 mozilla::AutoRestore<bool> restore(mProcessingInLoop);
474 mProcessingInLoop = true;
476 nsresult rv = ProcessNextFilter();
477 if (NS_FAILED(rv)) {
478 return rv;
480 } while (mFilterCalledBack);
482 return NS_OK;
485 nsresult nsAsyncResolveRequest::AsyncApplyFilters::ProcessNextFilter() {
486 LOG(("AsyncApplyFilters::ProcessNextFilter %p ENTER pi=%p", this,
487 mProxyInfo.get()));
489 RefPtr<FilterLink> filter;
490 do {
491 mFilterCalledBack = false;
493 if (!mRequest) {
494 // We got canceled
495 LOG((" canceled"));
496 return NS_OK; // should we let the consumer know?
499 if (mNextFilterIndex == mFiltersCopy.Length()) {
500 return Finish();
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,
515 mProxyInfo, this) &&
516 !mFilterCalledBack);
518 LOG(("AsyncApplyFilters::ProcessNextFilter %p LEAVE pi=%p", this,
519 mProxyInfo.get()));
520 return NS_OK;
523 NS_IMETHODIMP
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?"));
533 return NS_OK;
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"));
543 return NS_OK;
546 if (!mRequest) {
547 // We got canceled
548 LOG((" canceled"));
549 return NS_OK;
552 if (mNextFilterIndex == mFiltersCopy.Length()) {
553 // We are done, all filters have been called on!
554 Finish();
555 return NS_OK;
558 // Redispatch, since we don't want long stacks when filters respond
559 // synchronously.
560 LOG((" redispatching"));
561 NS_DispatchToCurrentThread(this);
562 return NS_OK;
565 NS_IMETHODIMP
566 nsAsyncResolveRequest::AsyncApplyFilters::Run() {
567 LOG(("AsyncApplyFilters::Run %p", this));
569 MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
571 ProcessNextFilter();
572 return NS_OK;
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;
586 pi.swap(mProxyInfo);
588 request->mPPS->PruneProxyInfo(mInfo, pi);
589 return mCallback(request, pi, !mProcessingInLoop);
592 NS_IMETHODIMP
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;
602 mRequest = nullptr;
604 return NS_OK;
607 // Bug 1366133: make GetPACURI off-main-thread since it may hang on Windows
608 // platform
609 class AsyncGetPACURIRequest final : public nsIRunnable {
610 public:
611 NS_DECL_THREADSAFE_ISUPPORTS
613 using CallbackFunc = nsresult (nsProtocolProxyService::*)(bool, bool,
614 nsresult,
615 const nsACString &);
617 AsyncGetPACURIRequest(nsProtocolProxyService *aService,
618 CallbackFunc aCallback,
619 nsISystemProxySettings *aSystemProxySettings,
620 bool aMainThreadOnly, bool aForceReload,
621 bool aResetPACThread)
622 : mIsMainThreadOnly(aMainThreadOnly),
623 mService(aService),
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);
636 nsCString pacUri;
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);
647 private:
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;
660 bool mForceReload;
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;
676 if (mask_len > 96) {
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)));
688 } else {
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) {
699 nsAutoCString temp;
700 nsresult rv = aPrefBranch->GetCharPref(aPref, temp);
701 if (NS_FAILED(rv))
702 aResult.Truncate();
703 else {
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,
712 int32_t &aResult) {
713 int32_t temp;
714 nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
715 if (NS_FAILED(rv))
716 aResult = -1;
717 else
718 aResult = temp;
721 static void proxy_GetBoolPref(nsIPrefBranch *aPrefBranch, const char *aPref,
722 bool &aResult) {
723 bool temp;
724 nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
725 if (NS_FAILED(rv))
726 aResult = false;
727 else
728 aResult = 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)
749 NS_INTERFACE_MAP_END
751 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService, nsIProtocolProxyService,
752 nsIProtocolProxyService2)
754 nsProtocolProxyService::nsProtocolProxyService()
755 : mFilterLocalHosts(false),
756 mProxyConfig(PROXYCONFIG_DIRECT),
757 mHTTPProxyPort(-1),
758 mFTPProxyPort(-1),
759 mHTTPSProxyPort(-1),
760 mSOCKSProxyPort(-1),
761 mSOCKSProxyVersion(4),
762 mSOCKSProxyRemoteDNS(false),
763 mProxyOverTLS(true),
764 mWPADOverDHCPEnabled(false),
765 mPACMan(nullptr),
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 &&
774 mPACMan == nullptr,
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);
784 if (prefBranch) {
785 // monitor proxy prefs
786 prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
788 // read all prefs
789 PrefsChanged(prefBranch, nullptr);
792 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
793 if (obs) {
794 // register for shutdown notification so we can clean ourselves up
795 // properly.
796 obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
797 obs->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
800 return NS_OK;
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);
807 if (!prefs) {
808 return NS_OK;
811 int32_t type;
812 nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
813 if (NS_FAILED(rv)) {
814 return NS_OK;
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)) {
824 return rv;
827 nsProtocolInfo pac;
828 rv = GetProtocolInfo(pacURI, &pac);
829 if (!NS_SUCCEEDED(rv)) {
830 return rv;
833 if (!pac.scheme.EqualsLiteral("file") &&
834 !pac.scheme.EqualsLiteral("data")) {
835 LOG((": received network changed event, reload PAC"));
836 ReloadPAC();
839 } else if ((type == PROXYCONFIG_WPAD) || (type == PROXYCONFIG_SYSTEM)) {
840 ReloadPAC();
843 return NS_OK;
846 nsresult nsProtocolProxyService::AsyncConfigureFromPAC(bool aForceReload,
847 bool aResetPACThread) {
848 MOZ_ASSERT(NS_IsMainThread());
850 bool mainThreadOnly;
851 nsresult rv = mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly);
852 if (NS_WARN_IF(NS_FAILED(rv))) {
853 return rv;
856 nsCOMPtr<nsIRunnable> req = new AsyncGetPACURIRequest(
857 this, &nsProtocolProxyService::OnAsyncGetPACURI, mSystemProxySettings,
858 mainThreadOnly, aForceReload, aResetPACThread);
860 if (mainThreadOnly) {
861 return req->Run();
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,
872 nsresult aResult,
873 const nsACString &aUri) {
874 MOZ_ASSERT(NS_IsMainThread());
876 if (aResetPACThread) {
877 ResetPACThread();
880 if (NS_SUCCEEDED(aResult) && !aUri.IsEmpty()) {
881 ConfigureFromPAC(PromiseFlatCString(aUri), aForceReload);
884 return NS_OK;
887 NS_IMETHODIMP
888 nsProtocolProxyService::Observe(nsISupports *aSubject, const char *aTopic,
889 const char16_t *aData) {
890 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
891 mIsShutdown = true;
892 // cleanup
893 mHostFiltersArray.Clear();
894 mFilters.Clear();
896 if (mPACMan) {
897 mPACMan->Shutdown();
898 mPACMan = nullptr;
901 if (mProxySettingTarget) {
902 mProxySettingTarget = nullptr;
905 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
906 if (obs) {
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)) {
915 ReloadNetworkPAC();
917 } else {
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());
923 return NS_OK;
926 void nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
927 const char *pref) {
928 nsresult rv = NS_OK;
929 bool reloadPAC = false;
930 nsAutoCString tempString;
932 if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
933 int32_t type = -1;
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;
948 mProxyConfig = type;
949 reloadPAC = true;
952 if (mProxyConfig == PROXYCONFIG_SYSTEM) {
953 mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
954 if (!mSystemProxySettings) mProxyConfig = PROXYCONFIG_DIRECT;
955 ResetPACThread();
956 } else {
957 if (mSystemProxySettings) {
958 mSystemProxySettings = nullptr;
959 ResetPACThread();
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"))) {
989 int32_t version;
990 proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
991 // make sure this preference value remains sane
992 if (version == 5)
993 mSOCKSProxyVersion = 5;
994 else
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)
1025 return;
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;
1033 if (reloadPAC) {
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"));
1039 ResetPACThread();
1041 } else if (mProxyConfig == PROXYCONFIG_WPAD) {
1042 LOG(("Auto-detecting proxy - Reset Pac Thread"));
1043 ResetPACThread();
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;
1057 int32_t port;
1058 nsAutoCString host;
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;
1067 PRNetAddr addr;
1068 bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
1070 PRIPv6Addr ipv6;
1071 if (is_ipaddr) {
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) {
1077 // copy the address
1078 memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1079 } else {
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
1092 int32_t index = -1;
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;
1099 if (is_ipaddr) {
1100 // generate masked version of target IPv6 address
1101 PRIPv6Addr masked;
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
1108 } else {
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
1149 return true;
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) {
1165 *result = nullptr;
1166 uint32_t flags = 0;
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;
1180 switch (len) {
1181 case 4:
1182 if (PL_strncasecmp(start, kProxyType_HTTP, 5) == 0) {
1183 type = kProxyType_HTTP;
1185 break;
1186 case 5:
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;
1194 break;
1195 case 6:
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;
1204 break;
1206 if (type) {
1207 const char *host = nullptr, *hostEnd = nullptr;
1208 int32_t port = -1;
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
1212 // support it.
1213 if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS)
1214 flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1216 // extract host:port
1217 start = sp;
1218 while ((*start == ' ' || *start == '\t') && start < end) start++;
1220 // port defaults
1221 if (type == kProxyType_HTTP) {
1222 port = 80;
1223 } else if (type == kProxyType_HTTPS) {
1224 port = 443;
1225 } else {
1226 port = 1080;
1229 nsProxyInfo *pi = new nsProxyInfo();
1230 pi->mType = type;
1231 pi->mFlags = flags;
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;
1246 int32_t tPort;
1247 if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
1248 port = tPort;
1250 pi->mPort = port;
1251 } else {
1252 // www.example.com:8080
1253 if (start < end) {
1254 host = start;
1255 hostEnd = strchr(host, ':');
1256 if (!hostEnd || hostEnd > end) {
1257 hostEnd = end;
1258 // no port, so assume default
1259 } else {
1260 port = atoi(hostEnd + 1);
1263 // YES, it is ok to specify a null proxy host.
1264 if (host) {
1265 pi->mHost.Assign(host, hostEnd - host);
1266 pi->mPort = port;
1269 NS_ADDREF(*result = pi);
1272 while (*end == ';' || *end == ' ' || *end == '\t') ++end;
1273 return end;
1276 void nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key) {
1277 key.AssignASCII(pi->mType);
1278 if (!pi->mHost.IsEmpty()) {
1279 key.Append(' ');
1280 key.Append(pi->mHost);
1281 key.Append(':');
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) {
1300 nsAutoCString key;
1301 GetProxyKey(pi, key);
1302 mFailedProxies.Remove(key);
1305 void nsProtocolProxyService::DisableProxy(nsProxyInfo *pi) {
1306 nsAutoCString key;
1307 GetProxyKey(pi, key);
1309 uint32_t dsec = SecondsSinceSessionStart();
1311 // Add timeout to interval (this is the time when the proxy can
1312 // be tried again).
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) {
1331 nsAutoCString key;
1332 GetProxyKey(pi, key);
1334 uint32_t val;
1335 if (!mFailedProxies.Get(key, &val)) return false;
1337 uint32_t dsec = SecondsSinceSessionStart();
1339 // if time passed has exceeded interval, then try proxy again.
1340 if (dsec > val) {
1341 mFailedProxies.Remove(key);
1342 return false;
1345 return true;
1348 nsresult nsProtocolProxyService::SetupPACThread(
1349 nsIEventTarget *mainThreadEventTarget) {
1350 if (mIsShutdown) {
1351 return NS_ERROR_FAILURE;
1354 if (mPACMan) return NS_OK;
1356 mPACMan = new nsPACMan(mainThreadEventTarget);
1358 bool mainThreadOnly;
1359 nsresult rv;
1360 if (mSystemProxySettings &&
1361 NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
1362 !mainThreadOnly) {
1363 rv = mPACMan->Init(mSystemProxySettings);
1364 } else {
1365 rv = mPACMan->Init(nullptr);
1367 if (NS_FAILED(rv)) {
1368 mPACMan->Shutdown();
1369 mPACMan = nullptr;
1371 return rv;
1374 nsresult nsProtocolProxyService::ResetPACThread() {
1375 if (!mPACMan) return NS_OK;
1377 mPACMan->Shutdown();
1378 mPACMan = nullptr;
1379 return SetupPACThread();
1382 nsresult nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
1383 bool forceReload) {
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()))) {
1390 return NS_OK;
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()) {
1403 *result = nullptr;
1404 return;
1407 const char *proxies = pacString.get();
1409 nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
1410 while (*proxies) {
1411 proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
1412 if (pi && (pi->mType == kProxyType_HTTPS) && !mProxyOverTLS) {
1413 delete pi;
1414 pi = nullptr;
1417 if (pi) {
1418 if (last) {
1419 NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
1420 last->mNext = pi;
1421 } else
1422 first = pi;
1423 last = pi;
1426 *result = first;
1429 // nsIProtocolProxyService2
1430 NS_IMETHODIMP
1431 nsProtocolProxyService::ReloadPAC() {
1432 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
1433 if (!prefs) return NS_OK;
1435 int32_t type;
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);
1445 } else {
1446 ResetPACThread();
1450 if (!pacSpec.IsEmpty() || type == PROXYCONFIG_WPAD)
1451 ConfigureFromPAC(pacSpec, true);
1452 return NS_OK;
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"),
1467 mStatus(NS_OK),
1468 mCompleted(false) {}
1470 void OnQueryComplete(nsresult status, const nsACString &pacString,
1471 const nsACString &newPACURL) override {
1472 MutexAutoLock lock(mMutex);
1473 mCompleted = true;
1474 mStatus = status;
1475 mPACString = pacString;
1476 mPACURL = newPACURL;
1477 mCondVar.Notify();
1480 void Lock() { mMutex.Lock(); }
1481 void Unlock() { mMutex.Unlock(); }
1482 void Wait() { mCondVar.Wait(TimeDuration::FromSeconds(3)); }
1484 private:
1485 ~nsAsyncBridgeRequest() = default;
1487 friend class nsProtocolProxyService;
1489 Mutex mMutex;
1490 CondVar mCondVar;
1492 nsresult mStatus;
1493 nsCString mPACString;
1494 nsCString mPACURL;
1495 bool mCompleted;
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;
1510 *result = nullptr;
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;
1519 bool usePACThread;
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;
1527 ResetPACThread();
1531 rv = SetupPACThread(mainThreadEventTarget);
1532 if (NS_FAILED(rv)) {
1533 return 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
1540 rv =
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) {
1548 ctx.forget(result);
1550 return rv;
1553 // else kick off a PAC thread query
1555 rv = mPACMan->AsyncGetProxyForURI(uri, ctx, true);
1556 if (NS_SUCCEEDED(rv)) ctx.forget(result);
1557 return rv;
1560 // nsIProtocolProxyService
1561 NS_IMETHODIMP
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);
1570 NS_IMETHODIMP
1571 nsProtocolProxyService::AsyncResolve(nsISupports *channelOrURI, uint32_t flags,
1572 nsIProtocolProxyCallback *callback,
1573 nsIEventTarget *mainThreadEventTarget,
1574 nsICancelable **result) {
1575 nsresult rv;
1576 // Check if we got a channel:
1577 nsCOMPtr<nsIChannel> channel = do_QueryInterface(channelOrURI);
1578 if (!channel) {
1579 nsCOMPtr<nsIURI> uri = do_QueryInterface(channelOrURI);
1580 if (!uri) {
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);
1598 NS_IMETHODIMP
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);
1609 NS_IMETHODIMP
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,
1617 kProxyType_DIRECT};
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)) {
1624 type = t;
1625 break;
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);
1641 NS_IMETHODIMP
1642 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo *aProxy, nsIURI *aURI,
1643 nsresult aStatus,
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);
1653 NS_ENSURE_ARG(pi);
1654 // OK, the QI checked out. We can proceed.
1656 // Remember that this proxy is down.
1657 DisableProxy(pi);
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);
1669 return NS_OK;
1672 namespace { // anon
1674 class ProxyFilterPositionComparator {
1675 typedef RefPtr<nsProtocolProxyService::FilterLink> FilterLinkRef;
1677 public:
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;
1689 public:
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));
1696 } // namespace
1698 nsresult nsProtocolProxyService::InsertFilterLink(RefPtr<FilterLink> &&link) {
1699 LOG(("nsProtocolProxyService::InsertFilterLink filter=%p", link.get()));
1701 if (mIsShutdown) {
1702 return NS_ERROR_FAILURE;
1705 mFilters.AppendElement(link);
1706 mFilters.Sort(ProxyFilterPositionComparator());
1707 return NS_OK;
1710 NS_IMETHODIMP
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));
1719 NS_IMETHODIMP
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())
1733 ? NS_OK
1734 : NS_ERROR_UNEXPECTED;
1737 NS_IMETHODIMP
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);
1744 NS_IMETHODIMP
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);
1752 NS_IMETHODIMP
1753 nsProtocolProxyService::GetProxyConfigType(uint32_t *aProxyConfigType) {
1754 *aProxyConfigType = mProxyConfig;
1755 return NS_OK;
1758 void nsProtocolProxyService::LoadHostFilters(const nsACString &aFilters) {
1759 if (mIsShutdown) {
1760 return;
1763 // check to see the owners flag? /!?/ TODO
1764 if (mHostFiltersArray.Length() > 0) {
1765 mHostFiltersArray.Clear();
1768 if (aFilters.IsEmpty()) {
1769 return;
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
1777 // string
1778 mFilterLocalHosts = false;
1780 mozilla::Tokenizer t(aFilters);
1781 mozilla::Tokenizer::Token token;
1782 bool eof = false;
1783 // while (*filters) {
1784 while (!eof) {
1785 // skip over spaces and ,
1786 t.SkipWhites();
1787 while (t.CheckChar(',')) {
1788 t.SkipWhites();
1791 nsAutoCString portStr;
1792 nsAutoCString hostStr;
1793 nsAutoCString maskStr;
1794 t.Record();
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())) {
1801 eof = true;
1802 break;
1804 if (token.Equals(mozilla::Tokenizer::Token::Char(',')) ||
1805 token.Type() == mozilla::Tokenizer::TOKEN_WS) {
1806 break;
1809 if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
1810 parsingIPv6 = true;
1811 continue;
1814 if (!parsingIPv6 && token.Equals(mozilla::Tokenizer::Token::Char(':'))) {
1815 // Port is starting. Claim the previous as host.
1816 if (parsingMask) {
1817 t.Claim(maskStr);
1818 } else {
1819 t.Claim(hostStr);
1821 t.Record();
1822 parsingPort = true;
1823 continue;
1824 } else if (token.Equals(mozilla::Tokenizer::Token::Char('/'))) {
1825 t.Claim(hostStr);
1826 t.Record();
1827 parsingMask = true;
1828 continue;
1829 } else if (token.Equals(mozilla::Tokenizer::Token::Char(']'))) {
1830 parsingIPv6 = false;
1831 continue;
1834 if (!parsingPort && !parsingMask) {
1835 t.Claim(hostStr);
1836 } else if (parsingPort) {
1837 t.Claim(portStr);
1838 } else if (parsingMask) {
1839 t.Claim(maskStr);
1840 } else {
1841 NS_WARNING("Could not parse this rule");
1842 continue;
1845 if (hostStr.IsEmpty()) {
1846 continue;
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;
1853 LOG(
1854 ("loaded filter for local hosts "
1855 "(plain host names, no dots)\n"));
1856 // Continue to next host filter;
1857 continue;
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)) {
1866 port = 0;
1868 hinfo->port = port;
1870 int32_t maskLen = maskStr.ToInteger(&rv);
1871 if (NS_FAILED(rv)) {
1872 maskLen = 128;
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);
1881 PRNetAddr addr;
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");
1889 goto loser;
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) {
1898 // copy the address
1899 memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1900 } else {
1901 NS_WARNING("unknown address family");
1902 goto loser;
1905 // apply mask to IPv6 address
1906 proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
1907 } else {
1908 nsAutoCString host;
1909 if (hostStr.First() == '*') {
1910 host = Substring(hostStr, 1);
1911 } else {
1912 host = hostStr;
1915 if (host.IsEmpty()) {
1916 hinfo->name.host = nullptr;
1917 goto loser;
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);
1938 PRNetAddr netAddr;
1939 PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
1940 memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
1942 char buf[256];
1943 PR_NetAddrToString(&netAddr, buf, sizeof(buf));
1945 printf(" ip.addr = %s\n", buf);
1946 } else {
1947 printf(" name.host = %s\n", hinfo->name.host);
1949 #endif
1951 mHostFiltersArray.AppendElement(hinfo);
1952 hinfo = nullptr;
1953 loser:
1954 delete hinfo;
1958 nsresult nsProtocolProxyService::GetProtocolInfo(nsIURI *uri,
1959 nsProtocolInfo *info) {
1960 MOZ_ASSERT(uri, "URI is null");
1961 MOZ_ASSERT(info, "info is null");
1963 nsresult rv;
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);
1979 return rv;
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);
2010 return NS_OK;
2013 nsresult nsProtocolProxyService::Resolve_Internal(nsIChannel *channel,
2014 const nsProtocolInfo &info,
2015 uint32_t flags,
2016 bool *usePACThread,
2017 nsIProxyInfo **result) {
2018 NS_ENSURE_ARG_POINTER(channel);
2020 *usePACThread = false;
2021 *result = nullptr;
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;
2030 // See bug #586908.
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))
2039 return NS_OK;
2041 bool mainThreadOnly;
2042 if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM &&
2043 NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
2044 !mainThreadOnly) {
2045 *usePACThread = true;
2046 return NS_OK;
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
2052 // not to block
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
2064 // the new value
2066 if (mPACMan && mPACMan->IsPACURI(PACURI)) {
2067 // unchanged
2068 *usePACThread = true;
2069 return NS_OK;
2072 ConfigureFromPAC(PACURI, false);
2073 return NS_OK;
2076 nsAutoCString spec;
2077 nsAutoCString host;
2078 nsAutoCString scheme;
2079 int32_t port = -1;
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);
2098 return NS_OK;
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)))
2107 return NS_OK;
2109 // Proxy auto config magic...
2110 if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD) {
2111 // Do not query PAC now.
2112 *usePACThread = true;
2113 return NS_OK;
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;
2123 int32_t port = -1;
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;
2132 else
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;
2165 else
2166 type = kProxyType_SOCKS;
2167 port = mSOCKSProxyPort;
2168 if (mSOCKSProxyRemoteDNS)
2169 proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
2172 if (type) {
2173 rv =
2174 NewProxyInfo_Internal(type, *host, port, EmptyCString(), EmptyCString(),
2175 proxyFlags, UINT32_MAX, nullptr, flags, result);
2176 if (NS_FAILED(rv)) return rv;
2179 return NS_OK;
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);
2190 if (!dns) return;
2191 nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
2192 if (!pdns) return;
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) {
2207 nsresult rv;
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));
2217 if (!uri) {
2218 return false;
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);
2230 return false;
2233 void nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
2234 nsIProxyInfo **list) {
2235 if (!*list) return;
2237 LOG(("nsProtocolProxyService::PruneProxyInfo ENTER list=%p", *list));
2239 nsProxyInfo *head = nullptr;
2240 CallQueryInterface(*list, &head);
2241 if (!head) {
2242 MOZ_ASSERT_UNREACHABLE("nsIProxyInfo must QI to nsProxyInfo");
2243 return;
2245 NS_RELEASE(*list);
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;
2257 while (iter) {
2258 if ((iter->Type() == kProxyType_HTTP) ||
2259 (iter->Type() == kProxyType_HTTPS)) {
2260 // reject!
2261 if (last)
2262 last->mNext = iter->mNext;
2263 else
2264 head = iter->mNext;
2265 nsProxyInfo *next = iter->mNext;
2266 iter->mNext = nullptr;
2267 iter->Release();
2268 iter = next;
2269 } else {
2270 last = iter;
2271 iter = iter->mNext;
2274 if (!head) {
2275 return;
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
2281 // disabled ones.
2283 bool allNonDirectProxiesDisabled = true;
2285 nsProxyInfo *iter;
2286 for (iter = head; iter; iter = iter->mNext) {
2287 if (!IsProxyDisabled(iter) && iter->mType != kProxyType_DIRECT) {
2288 allNonDirectProxiesDisabled = false;
2289 break;
2293 if (allNonDirectProxiesDisabled) {
2294 LOG(("All proxies are disabled, so trying all again"));
2295 } else {
2296 // remove any disabled proxies.
2297 nsProxyInfo *last = nullptr;
2298 for (iter = head; iter;) {
2299 if (IsProxyDisabled(iter)) {
2300 // reject!
2301 nsProxyInfo *reject = iter;
2303 iter = iter->mNext;
2304 if (last)
2305 last->mNext = iter;
2306 else
2307 head = iter;
2309 reject->mNext = nullptr;
2310 NS_RELEASE(reject);
2311 continue;
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.
2320 EnableProxy(iter);
2322 last = iter;
2323 iter = iter->mNext;
2327 // if only DIRECT was specified then return no proxy info, and we're done.
2328 if (head && !head->mNext && head->mType == kProxyType_DIRECT)
2329 NS_RELEASE(head);
2331 *list = head; // Transfer ownership
2333 LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list));
2336 bool nsProtocolProxyService::GetIsPACLoading() {
2337 return mPACMan && mPACMan->IsLoading();
2340 } // namespace net
2341 } // namespace mozilla