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/. */
6 
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/AutoRestore.h"
10 
11 #include "nsProtocolProxyService.h"
12 #include "nsProxyInfo.h"
13 #include "nsIClassInfoImpl.h"
14 #include "nsIIOService.h"
15 #include "nsIObserverService.h"
16 #include "nsIProtocolHandler.h"
17 #include "nsIProtocolProxyCallback.h"
18 #include "nsIChannel.h"
19 #include "nsICancelable.h"
20 #include "nsIDNSService.h"
21 #include "nsPIDNSService.h"
22 #include "nsIPrefBranch.h"
23 #include "nsIPrefService.h"
24 #include "nsContentUtils.h"
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 "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/dom/nsMixedContentBlocker.h"
42 #include "mozilla/Logging.h"
43 #include "mozilla/StaticPrefs_network.h"
44 #include "mozilla/Tokenizer.h"
45 #include "mozilla/Unused.h"
46 #include "mozilla/StaticPrefs_network.h"
47 
48 //----------------------------------------------------------------------------
49 
50 namespace mozilla {
51 namespace net {
52 
53 extern const char kProxyType_HTTP[];
54 extern const char kProxyType_HTTPS[];
55 extern const char kProxyType_SOCKS[];
56 extern const char kProxyType_SOCKS4[];
57 extern const char kProxyType_SOCKS5[];
58 extern const char kProxyType_DIRECT[];
59 
60 #undef LOG
61 #define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
62 
63 //----------------------------------------------------------------------------
64 
65 #define PROXY_PREF_BRANCH "network.proxy"
66 #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
67 
68 //----------------------------------------------------------------------------
69 
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;
75 };
76 
77 //----------------------------------------------------------------------------
78 
79 // Return the channel's proxy URI, or if it doesn't exist, the
80 // channel's main URI.
GetProxyURI(nsIChannel * channel,nsIURI ** aOut)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));
87   }
88   if (!proxyURI) {
89     rv = channel->GetURI(getter_AddRefs(proxyURI));
90   }
91   if (NS_FAILED(rv)) {
92     return rv;
93   }
94   proxyURI.forget(aOut);
95   return NS_OK;
96 }
97 
98 //-----------------------------------------------------------------------------
99 
FilterLink(uint32_t p,nsIProtocolProxyFilter * f)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));
105 }
FilterLink(uint32_t p,nsIProtocolProxyChannelFilter * cf)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));
111 }
112 
~FilterLink()113 nsProtocolProxyService::FilterLink::~FilterLink() {
114   LOG(("nsProtocolProxyService::FilterLink::~FilterLink %p", this));
115 }
116 
117 //-----------------------------------------------------------------------------
118 
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
127 
nsAsyncResolveRequest(nsProtocolProxyService * pps,nsIChannel * channel,uint32_t aResolveFlags,nsIProtocolProxyCallback * callback)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");
137   }
138 
139  private:
~nsAsyncResolveRequest()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
145 
146       if (mChannel) {
147         NS_ReleaseOnMainThread("nsAsyncResolveRequest::mChannel",
148                                mChannel.forget());
149       }
150 
151       if (mCallback) {
152         NS_ReleaseOnMainThread("nsAsyncResolveRequest::mCallback",
153                                mCallback.forget());
154       }
155 
156       if (mProxyInfo) {
157         NS_ReleaseOnMainThread("nsAsyncResolveRequest::mProxyInfo",
158                                mProxyInfo.forget());
159       }
160 
161       if (mXPComPPS) {
162         NS_ReleaseOnMainThread("nsAsyncResolveRequest::mXPComPPS",
163                                mXPComPPS.forget());
164       }
165     }
166   }
167 
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
181 
182     using Callback =
183         std::function<nsresult(nsAsyncResolveRequest*, nsIProxyInfo*, bool)>;
184 
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);
196 
197    private:
198     using FilterLink = nsProtocolProxyService::FilterLink;
199 
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();
207 
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;
217 
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;
225 
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;
229 
230     // The logic is written as non-thread safe, assert single-thread usage.
231     nsCOMPtr<nsISerialEventTarget> mProcessingThread;
232   };
233 
EnsureResolveFlagsMatch()234   void EnsureResolveFlagsMatch() {
235     nsCOMPtr<nsIProxyInfo> proxyInfo = mProxyInfo;
236     while (proxyInfo) {
237       proxyInfo->SetResolveFlags(mResolveFlags);
238       proxyInfo->GetFailoverProxy(getter_AddRefs(proxyInfo));
239     }
240   }
241 
242  public:
ProcessLocally(nsProtocolInfo & info,nsIProxyInfo * pi,bool isSyncOK)243   nsresult ProcessLocally(nsProtocolInfo& info, nsIProxyInfo* pi,
244                           bool isSyncOK) {
245     SetResult(NS_OK, pi);
246 
247     auto consumeFiltersResult = [isSyncOK](nsAsyncResolveRequest* ctx,
248                                            nsIProxyInfo* pi,
249                                            bool aCalledAsync) -> nsresult {
250       ctx->SetResult(NS_OK, pi);
251       if (isSyncOK || aCalledAsync) {
252         ctx->Run();
253         return NS_OK;
254       }
255 
256       return ctx->DispatchCallback();
257     };
258 
259     mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
260     // may call consumeFiltersResult() directly
261     return mAsyncFilterApplier->AsyncProcess(this);
262   }
263 
SetResult(nsresult status,nsIProxyInfo * pi)264   void SetResult(nsresult status, nsIProxyInfo* pi) {
265     mStatus = status;
266     mProxyInfo = pi;
267   }
268 
Run()269   NS_IMETHOD Run() override {
270     if (mCallback) DoCallback();
271     return NS_OK;
272   }
273 
Cancel(nsresult reason)274   NS_IMETHOD Cancel(nsresult reason) override {
275     NS_ENSURE_ARG(NS_FAILED(reason));
276 
277     if (mAsyncFilterApplier) {
278       mAsyncFilterApplier->Cancel(reason);
279     }
280 
281     // If we've already called DoCallback then, nothing more to do.
282     if (!mCallback) return NS_OK;
283 
284     SetResult(reason, nullptr);
285     return DispatchCallback();
286   }
287 
DispatchCallback()288   nsresult DispatchCallback() {
289     if (mDispatched) {  // Only need to dispatch once
290       return NS_OK;
291     }
292 
293     nsresult rv = NS_DispatchToCurrentThread(this);
294     if (NS_FAILED(rv)) {
295       NS_WARNING("unable to dispatch callback event");
296     } else {
297       mDispatched = true;
298       return NS_OK;
299     }
300 
301     mCallback = nullptr;  // break possible reference cycle
302     return rv;
303   }
304 
305  private:
306   // Called asynchronously, so we do not need to post another PLEvent
307   // before calling DoCallback.
OnQueryComplete(nsresult status,const nsACString & pacString,const nsACString & newPACURL)308   void OnQueryComplete(nsresult status, const nsACString& pacString,
309                        const nsACString& newPACURL) override {
310     // If we've already called DoCallback then, nothing more to do.
311     if (!mCallback) return;
312 
313     // Provided we haven't been canceled...
314     if (mStatus == NS_OK) {
315       mStatus = status;
316       mPACString = pacString;
317       mPACURL = newPACURL;
318     }
319 
320     // In the cancelation case, we may still have another PLEvent in
321     // the queue that wants to call DoCallback.  No need to wait for
322     // it, just run the callback now.
323     DoCallback();
324   }
325 
DoCallback()326   void DoCallback() {
327     bool pacAvailable = true;
328     if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) {
329       // If the PAC service is not avail (e.g. failed pac load
330       // or shutdown) then we will be going direct. Make that
331       // mapping now so that any filters are still applied.
332       mPACString = "DIRECT;"_ns;
333       mStatus = NS_OK;
334 
335       LOG(("pac not available, use DIRECT\n"));
336       pacAvailable = false;
337     }
338 
339     // Generate proxy info from the PAC string if appropriate
340     if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
341       mPPS->ProcessPACString(mPACString, mResolveFlags,
342                              getter_AddRefs(mProxyInfo));
343       nsCOMPtr<nsIURI> proxyURI;
344       GetProxyURI(mChannel, getter_AddRefs(proxyURI));
345 
346       // Now apply proxy filters
347       nsProtocolInfo info;
348       mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
349 
350       auto consumeFiltersResult = [pacAvailable](nsAsyncResolveRequest* self,
351                                                  nsIProxyInfo* pi,
352                                                  bool async) -> nsresult {
353         LOG(("DoCallback::consumeFiltersResult this=%p, pi=%p, async=%d", self,
354              pi, async));
355 
356         self->mProxyInfo = pi;
357 
358         if (pacAvailable) {
359           // if !pacAvailable, it was already logged above
360           LOG(("pac thread callback %s\n", self->mPACString.get()));
361         }
362 
363         if (NS_SUCCEEDED(self->mStatus)) {
364           self->mPPS->MaybeDisableDNSPrefetch(self->mProxyInfo);
365         }
366 
367         self->EnsureResolveFlagsMatch();
368         self->mCallback->OnProxyAvailable(self, self->mChannel,
369                                           self->mProxyInfo, self->mStatus);
370 
371         return NS_OK;
372       };
373 
374       if (NS_SUCCEEDED(mStatus)) {
375         mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
376         // This may call consumeFiltersResult() directly.
377         mAsyncFilterApplier->AsyncProcess(this);
378         return;
379       }
380 
381       consumeFiltersResult(this, nullptr, false);
382     } else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
383       LOG(("pac thread callback indicates new pac file load\n"));
384 
385       nsCOMPtr<nsIURI> proxyURI;
386       GetProxyURI(mChannel, getter_AddRefs(proxyURI));
387 
388       // trigger load of new pac url
389       nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
390       if (NS_SUCCEEDED(rv)) {
391         // now that the load is triggered, we can resubmit the query
392         RefPtr<nsAsyncResolveRequest> newRequest =
393             new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback);
394         rv = mPPS->mPACMan->AsyncGetProxyForURI(proxyURI, newRequest,
395                                                 mResolveFlags, true);
396       }
397 
398       if (NS_FAILED(rv)) {
399         mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
400       }
401 
402       // do not call onproxyavailable() in SUCCESS case - the newRequest will
403       // take care of that
404     } else {
405       LOG(("pac thread callback did not provide information %" PRIX32 "\n",
406            static_cast<uint32_t>(mStatus)));
407       if (NS_SUCCEEDED(mStatus)) mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
408       EnsureResolveFlagsMatch();
409       mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
410     }
411 
412     // We are on the main thread now and don't need these any more so
413     // release them to avoid having to proxy them back to the main thread
414     // in the dtor
415     mCallback = nullptr;  // in case the callback holds an owning ref to us
416     mPPS = nullptr;
417     mXPComPPS = nullptr;
418     mChannel = nullptr;
419     mProxyInfo = nullptr;
420   }
421 
422  private:
423   nsresult mStatus{NS_OK};
424   nsCString mPACString;
425   nsCString mPACURL;
426   bool mDispatched{false};
427   uint32_t mResolveFlags;
428 
429   nsProtocolProxyService* mPPS;
430   nsCOMPtr<nsIProtocolProxyService> mXPComPPS;
431   nsCOMPtr<nsIChannel> mChannel;
432   nsCOMPtr<nsIProtocolProxyCallback> mCallback;
433   nsCOMPtr<nsIProxyInfo> mProxyInfo;
434 
435   RefPtr<AsyncApplyFilters> mAsyncFilterApplier;
436 };
437 
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest,nsICancelable,nsIRunnable)438 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
439 
440 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest::AsyncApplyFilters,
441                   nsIProxyProtocolFilterResult, nsICancelable, nsIRunnable)
442 
443 nsAsyncResolveRequest::AsyncApplyFilters::AsyncApplyFilters(
444     nsProtocolInfo& aInfo, Callback const& aCallback)
445     : mInfo(aInfo),
446       mCallback(aCallback),
447       mNextFilterIndex(0),
448       mProcessingInLoop(false),
449       mFilterCalledBack(false) {
450   LOG(("AsyncApplyFilters %p", this));
451 }
452 
~AsyncApplyFilters()453 nsAsyncResolveRequest::AsyncApplyFilters::~AsyncApplyFilters() {
454   LOG(("~AsyncApplyFilters %p", this));
455 
456   MOZ_ASSERT(!mRequest);
457   MOZ_ASSERT(!mProxyInfo);
458   MOZ_ASSERT(!mFiltersCopy.Length());
459 }
460 
AsyncProcess(nsAsyncResolveRequest * aRequest)461 nsresult nsAsyncResolveRequest::AsyncApplyFilters::AsyncProcess(
462     nsAsyncResolveRequest* aRequest) {
463   LOG(("AsyncApplyFilters::AsyncProcess %p for req %p", this, aRequest));
464 
465   MOZ_ASSERT(!mRequest, "AsyncApplyFilters started more than once!");
466 
467   if (!(mInfo.flags & nsIProtocolHandler::ALLOWS_PROXY)) {
468     // Calling the callback directly (not via Finish()) since we
469     // don't want to prune.
470     return mCallback(aRequest, aRequest->mProxyInfo, false);
471   }
472 
473   mProcessingThread = NS_GetCurrentThread();
474 
475   mRequest = aRequest;
476   mProxyInfo = aRequest->mProxyInfo;
477 
478   aRequest->mPPS->CopyFilters(mFiltersCopy);
479 
480   // We want to give filters a chance to process in a single loop to prevent
481   // any current-thread dispatch delays when those are not needed.
482   // This code is rather "loopy" than "recursive" to prevent long stack traces.
483   do {
484     MOZ_ASSERT(!mProcessingInLoop);
485 
486     mozilla::AutoRestore<bool> restore(mProcessingInLoop);
487     mProcessingInLoop = true;
488 
489     nsresult rv = ProcessNextFilter();
490     if (NS_FAILED(rv)) {
491       return rv;
492     }
493   } while (mFilterCalledBack);
494 
495   return NS_OK;
496 }
497 
ProcessNextFilter()498 nsresult nsAsyncResolveRequest::AsyncApplyFilters::ProcessNextFilter() {
499   LOG(("AsyncApplyFilters::ProcessNextFilter %p ENTER pi=%p", this,
500        mProxyInfo.get()));
501 
502   RefPtr<FilterLink> filter;
503   do {
504     mFilterCalledBack = false;
505 
506     if (!mRequest) {
507       // We got canceled
508       LOG(("  canceled"));
509       return NS_OK;  // should we let the consumer know?
510     }
511 
512     if (mNextFilterIndex == mFiltersCopy.Length()) {
513       return Finish();
514     }
515 
516     filter = mFiltersCopy[mNextFilterIndex++];
517 
518     // Loop until a call to a filter succeeded.  Other option is to recurse
519     // but that would waste stack trace when a number of filters gets registered
520     // and all from some reason tend to fail.
521     // The !mFilterCalledBack part of the condition is there to protect us from
522     // calling on another filter when the current one managed to call back and
523     // then threw. We already have the result so take it and use it since
524     // the next filter will be processed by the root loop or a call to
525     // ProcessNextFilter has already been dispatched to this thread.
526     LOG(("  calling filter %p pi=%p", filter.get(), mProxyInfo.get()));
527   } while (!mRequest->mPPS->ApplyFilter(filter, mRequest->mChannel, mInfo,
528                                         mProxyInfo, this) &&
529            !mFilterCalledBack);
530 
531   LOG(("AsyncApplyFilters::ProcessNextFilter %p LEAVE pi=%p", this,
532        mProxyInfo.get()));
533   return NS_OK;
534 }
535 
536 NS_IMETHODIMP
OnProxyFilterResult(nsIProxyInfo * aProxyInfo)537 nsAsyncResolveRequest::AsyncApplyFilters::OnProxyFilterResult(
538     nsIProxyInfo* aProxyInfo) {
539   LOG(("AsyncApplyFilters::OnProxyFilterResult %p pi=%p", this, aProxyInfo));
540 
541   MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
542   MOZ_ASSERT(!mFilterCalledBack);
543 
544   if (mFilterCalledBack) {
545     LOG(("  duplicate notification?"));
546     return NS_OK;
547   }
548 
549   mFilterCalledBack = true;
550 
551   if (!mRequest) {
552     // We got canceled
553     LOG(("  canceled"));
554     return NS_OK;
555   }
556 
557   mProxyInfo = aProxyInfo;
558 
559   if (mProcessingInLoop) {
560     // No need to call/dispatch ProcessNextFilter(), we are in a control
561     // loop that will do this for us and save recursion/dispatching.
562     LOG(("  in a root loop"));
563     return NS_OK;
564   }
565 
566   if (mNextFilterIndex == mFiltersCopy.Length()) {
567     // We are done, all filters have been called on!
568     Finish();
569     return NS_OK;
570   }
571 
572   // Redispatch, since we don't want long stacks when filters respond
573   // synchronously.
574   LOG(("  redispatching"));
575   NS_DispatchToCurrentThread(this);
576   return NS_OK;
577 }
578 
579 NS_IMETHODIMP
Run()580 nsAsyncResolveRequest::AsyncApplyFilters::Run() {
581   LOG(("AsyncApplyFilters::Run %p", this));
582 
583   MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
584 
585   ProcessNextFilter();
586   return NS_OK;
587 }
588 
Finish()589 nsresult nsAsyncResolveRequest::AsyncApplyFilters::Finish() {
590   LOG(("AsyncApplyFilters::Finish %p pi=%p", this, mProxyInfo.get()));
591 
592   MOZ_ASSERT(mRequest);
593 
594   mFiltersCopy.Clear();
595 
596   RefPtr<nsAsyncResolveRequest> request;
597   request.swap(mRequest);
598 
599   nsCOMPtr<nsIProxyInfo> pi;
600   pi.swap(mProxyInfo);
601 
602   request->mPPS->PruneProxyInfo(mInfo, pi);
603   return mCallback(request, pi, !mProcessingInLoop);
604 }
605 
606 NS_IMETHODIMP
Cancel(nsresult reason)607 nsAsyncResolveRequest::AsyncApplyFilters::Cancel(nsresult reason) {
608   LOG(("AsyncApplyFilters::Cancel %p", this));
609 
610   MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
611 
612   // This will be called only from inside the request, so don't call
613   // its's callback.  Dropping the members means we simply break the cycle.
614   mFiltersCopy.Clear();
615   mProxyInfo = nullptr;
616   mRequest = nullptr;
617 
618   return NS_OK;
619 }
620 
621 // Bug 1366133: make GetPACURI off-main-thread since it may hang on Windows
622 // platform
623 class AsyncGetPACURIRequest final : public nsIRunnable {
624  public:
625   NS_DECL_THREADSAFE_ISUPPORTS
626 
627   using CallbackFunc = nsresult (nsProtocolProxyService::*)(bool, bool,
628                                                             nsresult,
629                                                             const nsACString&);
630 
AsyncGetPACURIRequest(nsProtocolProxyService * aService,CallbackFunc aCallback,nsISystemProxySettings * aSystemProxySettings,bool aMainThreadOnly,bool aForceReload,bool aResetPACThread)631   AsyncGetPACURIRequest(nsProtocolProxyService* aService,
632                         CallbackFunc aCallback,
633                         nsISystemProxySettings* aSystemProxySettings,
634                         bool aMainThreadOnly, bool aForceReload,
635                         bool aResetPACThread)
636       : mIsMainThreadOnly(aMainThreadOnly),
637         mService(aService),
638         mServiceHolder(do_QueryObject(aService)),
639         mCallback(aCallback),
640         mSystemProxySettings(aSystemProxySettings),
641         mForceReload(aForceReload),
642         mResetPACThread(aResetPACThread) {
643     MOZ_ASSERT(NS_IsMainThread());
644     Unused << mIsMainThreadOnly;
645   }
646 
Run()647   NS_IMETHOD Run() override {
648     MOZ_ASSERT(NS_IsMainThread() == mIsMainThreadOnly);
649 
650     nsCString pacUri;
651     nsresult rv = mSystemProxySettings->GetPACURI(pacUri);
652 
653     nsCOMPtr<nsIRunnable> event =
654         NewNonOwningCancelableRunnableMethod<bool, bool, nsresult, nsCString>(
655             "AsyncGetPACURIRequestCallback", mService, mCallback, mForceReload,
656             mResetPACThread, rv, pacUri);
657 
658     return NS_DispatchToMainThread(event);
659   }
660 
661  private:
~AsyncGetPACURIRequest()662   ~AsyncGetPACURIRequest() {
663     NS_ReleaseOnMainThread("AsyncGetPACURIRequest::mServiceHolder",
664                            mServiceHolder.forget());
665   }
666 
667   bool mIsMainThreadOnly;
668 
669   nsProtocolProxyService* mService;  // ref-count is hold by mServiceHolder
670   nsCOMPtr<nsIProtocolProxyService2> mServiceHolder;
671   CallbackFunc mCallback;
672   nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
673 
674   bool mForceReload;
675   bool mResetPACThread;
676 };
677 
NS_IMPL_ISUPPORTS(AsyncGetPACURIRequest,nsIRunnable)678 NS_IMPL_ISUPPORTS(AsyncGetPACURIRequest, nsIRunnable)
679 
680 //----------------------------------------------------------------------------
681 
682 //
683 // apply mask to address (zeros out excluded bits).
684 //
685 // NOTE: we do the byte swapping here to minimize overall swapping.
686 //
687 static void proxy_MaskIPv6Addr(PRIPv6Addr& addr, uint16_t mask_len) {
688   if (mask_len == 128) return;
689 
690   if (mask_len > 96) {
691     addr.pr_s6_addr32[3] =
692         PR_htonl(PR_ntohl(addr.pr_s6_addr32[3]) & (~0uL << (128 - mask_len)));
693   } else if (mask_len > 64) {
694     addr.pr_s6_addr32[3] = 0;
695     addr.pr_s6_addr32[2] =
696         PR_htonl(PR_ntohl(addr.pr_s6_addr32[2]) & (~0uL << (96 - mask_len)));
697   } else if (mask_len > 32) {
698     addr.pr_s6_addr32[3] = 0;
699     addr.pr_s6_addr32[2] = 0;
700     addr.pr_s6_addr32[1] =
701         PR_htonl(PR_ntohl(addr.pr_s6_addr32[1]) & (~0uL << (64 - mask_len)));
702   } else {
703     addr.pr_s6_addr32[3] = 0;
704     addr.pr_s6_addr32[2] = 0;
705     addr.pr_s6_addr32[1] = 0;
706     addr.pr_s6_addr32[0] =
707         PR_htonl(PR_ntohl(addr.pr_s6_addr32[0]) & (~0uL << (32 - mask_len)));
708   }
709 }
710 
proxy_GetStringPref(nsIPrefBranch * aPrefBranch,const char * aPref,nsCString & aResult)711 static void proxy_GetStringPref(nsIPrefBranch* aPrefBranch, const char* aPref,
712                                 nsCString& aResult) {
713   nsAutoCString temp;
714   nsresult rv = aPrefBranch->GetCharPref(aPref, temp);
715   if (NS_FAILED(rv)) {
716     aResult.Truncate();
717   } else {
718     aResult.Assign(temp);
719     // all of our string prefs are hostnames, so we should remove any
720     // whitespace characters that the user might have unknowingly entered.
721     aResult.StripWhitespace();
722   }
723 }
724 
proxy_GetIntPref(nsIPrefBranch * aPrefBranch,const char * aPref,int32_t & aResult)725 static void proxy_GetIntPref(nsIPrefBranch* aPrefBranch, const char* aPref,
726                              int32_t& aResult) {
727   int32_t temp;
728   nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
729   if (NS_FAILED(rv)) {
730     aResult = -1;
731   } else {
732     aResult = temp;
733   }
734 }
735 
proxy_GetBoolPref(nsIPrefBranch * aPrefBranch,const char * aPref,bool & aResult)736 static void proxy_GetBoolPref(nsIPrefBranch* aPrefBranch, const char* aPref,
737                               bool& aResult) {
738   bool temp;
739   nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
740   if (NS_FAILED(rv)) {
741     aResult = false;
742   } else {
743     aResult = temp;
744   }
745 }
746 
747 //----------------------------------------------------------------------------
748 
749 static const int32_t PROXYCONFIG_DIRECT4X = 3;
750 static const int32_t PROXYCONFIG_COUNT = 6;
751 
752 NS_IMPL_ADDREF(nsProtocolProxyService)
NS_IMPL_RELEASE(nsProtocolProxyService)753 NS_IMPL_RELEASE(nsProtocolProxyService)
754 NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
755                   NS_PROTOCOLPROXYSERVICE_CID)
756 
757 // NS_IMPL_QUERY_INTERFACE_CI with the nsProtocolProxyService QI change
758 NS_INTERFACE_MAP_BEGIN(nsProtocolProxyService)
759   NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService)
760   NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService2)
761   NS_INTERFACE_MAP_ENTRY(nsIObserver)
762   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
763   NS_INTERFACE_MAP_ENTRY_CONCRETE(nsProtocolProxyService)
764   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolProxyService)
765   NS_IMPL_QUERY_CLASSINFO(nsProtocolProxyService)
766 NS_INTERFACE_MAP_END
767 
768 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService, nsIProtocolProxyService,
769                             nsIProtocolProxyService2)
770 
771 nsProtocolProxyService::nsProtocolProxyService() : mSessionStart(PR_Now()) {}
772 
~nsProtocolProxyService()773 nsProtocolProxyService::~nsProtocolProxyService() {
774   // These should have been cleaned up in our Observe method.
775   NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters.Length() == 0 &&
776                    mPACMan == nullptr,
777                "what happened to xpcom-shutdown?");
778 }
779 
780 // nsProtocolProxyService methods
Init()781 nsresult nsProtocolProxyService::Init() {
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);
787 
788     // read all prefs
789     PrefsChanged(prefBranch, nullptr);
790   }
791 
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);
798   }
799 
800   return NS_OK;
801 }
802 
803 // ReloadNetworkPAC() checks if there's a non-networked PAC in use then avoids
804 // to call ReloadPAC()
ReloadNetworkPAC()805 nsresult nsProtocolProxyService::ReloadNetworkPAC() {
806   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
807   if (!prefs) {
808     return NS_OK;
809   }
810 
811   int32_t type;
812   nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
813   if (NS_FAILED(rv)) {
814     return NS_OK;
815   }
816 
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;
825       }
826 
827       nsProtocolInfo pac;
828       rv = GetProtocolInfo(pacURI, &pac);
829       if (!NS_SUCCEEDED(rv)) {
830         return rv;
831       }
832 
833       if (!pac.scheme.EqualsLiteral("file") &&
834           !pac.scheme.EqualsLiteral("data")) {
835         LOG((": received network changed event, reload PAC"));
836         ReloadPAC();
837       }
838     }
839   } else if ((type == PROXYCONFIG_WPAD) || (type == PROXYCONFIG_SYSTEM)) {
840     ReloadPAC();
841   }
842 
843   return NS_OK;
844 }
845 
AsyncConfigureFromPAC(bool aForceReload,bool aResetPACThread)846 nsresult nsProtocolProxyService::AsyncConfigureFromPAC(bool aForceReload,
847                                                        bool aResetPACThread) {
848   MOZ_ASSERT(NS_IsMainThread());
849 
850   bool mainThreadOnly;
851   nsresult rv = mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly);
852   if (NS_WARN_IF(NS_FAILED(rv))) {
853     return rv;
854   }
855 
856   nsCOMPtr<nsIRunnable> req = new AsyncGetPACURIRequest(
857       this, &nsProtocolProxyService::OnAsyncGetPACURI, mSystemProxySettings,
858       mainThreadOnly, aForceReload, aResetPACThread);
859 
860   if (mainThreadOnly) {
861     return req->Run();
862   }
863 
864   return NS_DispatchBackgroundTask(req.forget(),
865                                    nsIEventTarget::DISPATCH_NORMAL);
866 }
867 
OnAsyncGetPACURI(bool aForceReload,bool aResetPACThread,nsresult aResult,const nsACString & aUri)868 nsresult nsProtocolProxyService::OnAsyncGetPACURI(bool aForceReload,
869                                                   bool aResetPACThread,
870                                                   nsresult aResult,
871                                                   const nsACString& aUri) {
872   MOZ_ASSERT(NS_IsMainThread());
873 
874   if (aResetPACThread) {
875     ResetPACThread();
876   }
877 
878   if (NS_SUCCEEDED(aResult) && !aUri.IsEmpty()) {
879     ConfigureFromPAC(PromiseFlatCString(aUri), aForceReload);
880   }
881 
882   return NS_OK;
883 }
884 
885 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)886 nsProtocolProxyService::Observe(nsISupports* aSubject, const char* aTopic,
887                                 const char16_t* aData) {
888   if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
889     mIsShutdown = true;
890     // cleanup
891     mHostFiltersArray.Clear();
892     mFilters.Clear();
893 
894     if (mPACMan) {
895       mPACMan->Shutdown();
896       mPACMan = nullptr;
897     }
898 
899     if (mReloadPACTimer) {
900       mReloadPACTimer->Cancel();
901       mReloadPACTimer = nullptr;
902     }
903 
904     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
905     if (obs) {
906       obs->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
907       obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
908     }
909 
910   } else if (strcmp(aTopic, NS_NETWORK_LINK_TOPIC) == 0) {
911     nsCString converted = NS_ConvertUTF16toUTF8(aData);
912     const char* state = converted.get();
913     if (!strcmp(state, NS_NETWORK_LINK_DATA_CHANGED)) {
914       uint32_t delay = StaticPrefs::network_proxy_reload_pac_delay();
915       LOG(("nsProtocolProxyService::Observe call ReloadNetworkPAC() delay=%u",
916            delay));
917 
918       if (delay) {
919         if (mReloadPACTimer) {
920           mReloadPACTimer->Cancel();
921           mReloadPACTimer = nullptr;
922         }
923         NS_NewTimerWithCallback(getter_AddRefs(mReloadPACTimer), this, delay,
924                                 nsITimer::TYPE_ONE_SHOT);
925       } else {
926         ReloadNetworkPAC();
927       }
928     }
929   } else {
930     NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
931                  "what is this random observer event?");
932     nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
933     if (prefs) PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
934   }
935   return NS_OK;
936 }
937 
938 NS_IMETHODIMP
Notify(nsITimer * aTimer)939 nsProtocolProxyService::Notify(nsITimer* aTimer) {
940   MOZ_ASSERT(aTimer == mReloadPACTimer);
941   ReloadNetworkPAC();
942   return NS_OK;
943 }
944 
PrefsChanged(nsIPrefBranch * prefBranch,const char * pref)945 void nsProtocolProxyService::PrefsChanged(nsIPrefBranch* prefBranch,
946                                           const char* pref) {
947   nsresult rv = NS_OK;
948   bool reloadPAC = false;
949   nsAutoCString tempString;
950 
951   if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
952     int32_t type = -1;
953     rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
954     if (NS_SUCCEEDED(rv)) {
955       // bug 115720 - for ns4.x backwards compatibility
956       if (type == PROXYCONFIG_DIRECT4X) {
957         type = PROXYCONFIG_DIRECT;
958         // Reset the type so that the dialog looks correct, and we
959         // don't have to handle this case everywhere else
960         // I'm paranoid about a loop of some sort - only do this
961         // if we're enumerating all prefs, and ignore any error
962         if (!pref) prefBranch->SetIntPref(PROXY_PREF("type"), type);
963       } else if (type >= PROXYCONFIG_COUNT) {
964         LOG(("unknown proxy type: %" PRId32 "; assuming direct\n", type));
965         type = PROXYCONFIG_DIRECT;
966       }
967       mProxyConfig = type;
968       reloadPAC = true;
969     }
970 
971     if (mProxyConfig == PROXYCONFIG_SYSTEM) {
972       mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
973       if (!mSystemProxySettings) mProxyConfig = PROXYCONFIG_DIRECT;
974       ResetPACThread();
975     } else {
976       if (mSystemProxySettings) {
977         mSystemProxySettings = nullptr;
978         ResetPACThread();
979       }
980     }
981   }
982 
983   if (!pref || !strcmp(pref, PROXY_PREF("http"))) {
984     proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
985   }
986 
987   if (!pref || !strcmp(pref, PROXY_PREF("http_port"))) {
988     proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
989   }
990 
991   if (!pref || !strcmp(pref, PROXY_PREF("ssl"))) {
992     proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
993   }
994 
995   if (!pref || !strcmp(pref, PROXY_PREF("ssl_port"))) {
996     proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
997   }
998 
999   if (!pref || !strcmp(pref, PROXY_PREF("socks"))) {
1000     proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyTarget);
1001   }
1002 
1003   if (!pref || !strcmp(pref, PROXY_PREF("socks_port"))) {
1004     proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
1005   }
1006 
1007   if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
1008     int32_t version;
1009     proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
1010     // make sure this preference value remains sane
1011     if (version == 5) {
1012       mSOCKSProxyVersion = 5;
1013     } else {
1014       mSOCKSProxyVersion = 4;
1015     }
1016   }
1017 
1018   if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns"))) {
1019     proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
1020                       mSOCKSProxyRemoteDNS);
1021   }
1022 
1023   if (!pref || !strcmp(pref, PROXY_PREF("proxy_over_tls"))) {
1024     proxy_GetBoolPref(prefBranch, PROXY_PREF("proxy_over_tls"), mProxyOverTLS);
1025   }
1026 
1027   if (!pref || !strcmp(pref, PROXY_PREF("enable_wpad_over_dhcp"))) {
1028     proxy_GetBoolPref(prefBranch, PROXY_PREF("enable_wpad_over_dhcp"),
1029                       mWPADOverDHCPEnabled);
1030     reloadPAC = reloadPAC || mProxyConfig == PROXYCONFIG_WPAD;
1031   }
1032 
1033   if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout"))) {
1034     proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
1035                      mFailedProxyTimeout);
1036   }
1037 
1038   if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
1039     rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"), tempString);
1040     if (NS_SUCCEEDED(rv)) LoadHostFilters(tempString);
1041   }
1042 
1043   // We're done if not using something that could give us a PAC URL
1044   // (PAC, WPAD or System)
1045   if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
1046       mProxyConfig != PROXYCONFIG_SYSTEM) {
1047     return;
1048   }
1049 
1050   // OK, we need to reload the PAC file if:
1051   //  1) network.proxy.type changed, or
1052   //  2) network.proxy.autoconfig_url changed and PAC is configured
1053 
1054   if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url"))) reloadPAC = true;
1055 
1056   if (reloadPAC) {
1057     tempString.Truncate();
1058     if (mProxyConfig == PROXYCONFIG_PAC) {
1059       prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"), tempString);
1060       if (mPACMan && !mPACMan->IsPACURI(tempString)) {
1061         LOG(("PAC Thread URI Changed - Reset Pac Thread"));
1062         ResetPACThread();
1063       }
1064     } else if (mProxyConfig == PROXYCONFIG_WPAD) {
1065       LOG(("Auto-detecting proxy - Reset Pac Thread"));
1066       ResetPACThread();
1067     } else if (mSystemProxySettings) {
1068       // Get System Proxy settings if available
1069       AsyncConfigureFromPAC(false, false);
1070     }
1071     if (!tempString.IsEmpty() || mProxyConfig == PROXYCONFIG_WPAD) {
1072       ConfigureFromPAC(tempString, false);
1073     }
1074   }
1075 }
1076 
CanUseProxy(nsIURI * aURI,int32_t defaultPort)1077 bool nsProtocolProxyService::CanUseProxy(nsIURI* aURI, int32_t defaultPort) {
1078   int32_t port;
1079   nsAutoCString host;
1080 
1081   nsresult rv = aURI->GetAsciiHost(host);
1082   if (NS_FAILED(rv) || host.IsEmpty()) return false;
1083 
1084   rv = aURI->GetPort(&port);
1085   if (NS_FAILED(rv)) return false;
1086   if (port == -1) port = defaultPort;
1087 
1088   PRNetAddr addr;
1089   bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
1090 
1091   PRIPv6Addr ipv6;
1092   if (is_ipaddr) {
1093     // convert parsed address to IPv6
1094     if (addr.raw.family == PR_AF_INET) {
1095       // convert to IPv4-mapped address
1096       PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
1097     } else if (addr.raw.family == PR_AF_INET6) {
1098       // copy the address
1099       memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1100     } else {
1101       NS_WARNING("unknown address family");
1102       return true;  // allow proxying
1103     }
1104   }
1105 
1106   // Don't use proxy for local hosts (plain hostname, no dots)
1107   if ((!is_ipaddr && mFilterLocalHosts && !host.Contains('.')) ||
1108       // This method detects if we have network.proxy.allow_hijacking_localhost
1109       // pref enabled. If it's true then this method will always return false
1110       // otherwise it returns true if the host matches an address that's
1111       // hardcoded to the loopback address.
1112       (!StaticPrefs::network_proxy_allow_hijacking_localhost() &&
1113        nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(host))) {
1114     LOG(("Not using proxy for this local host [%s]!\n", host.get()));
1115     return false;  // don't allow proxying
1116   }
1117 
1118   int32_t index = -1;
1119   while (++index < int32_t(mHostFiltersArray.Length())) {
1120     const auto& hinfo = mHostFiltersArray[index];
1121 
1122     if (is_ipaddr != hinfo->is_ipaddr) continue;
1123     if (hinfo->port && hinfo->port != port) continue;
1124 
1125     if (is_ipaddr) {
1126       // generate masked version of target IPv6 address
1127       PRIPv6Addr masked;
1128       memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
1129       proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
1130 
1131       // check for a match
1132       if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0) {
1133         return false;  // proxy disallowed
1134       }
1135     } else {
1136       uint32_t host_len = host.Length();
1137       uint32_t filter_host_len = hinfo->name.host_len;
1138 
1139       if (host_len >= filter_host_len) {
1140         //
1141         // compare last |filter_host_len| bytes of target hostname.
1142         //
1143         const char* host_tail = host.get() + host_len - filter_host_len;
1144         if (!nsCRT::strncasecmp(host_tail, hinfo->name.host, filter_host_len)) {
1145           // If the tail of the host string matches the filter
1146 
1147           if (filter_host_len > 0 && hinfo->name.host[0] == '.') {
1148             // If the filter was of the form .foo.bar.tld, all such
1149             // matches are correct
1150             return false;  // proxy disallowed
1151           }
1152 
1153           // abc-def.example.org should not match def.example.org
1154           // however, *.def.example.org should match .def.example.org
1155           // We check that the filter doesn't start with a `.`. If it does,
1156           // then the strncasecmp above should suffice. If it doesn't,
1157           // then we should only consider it a match if the strncasecmp happened
1158           // at a subdomain boundary
1159           if (host_len > filter_host_len && *(host_tail - 1) == '.') {
1160             // If the host was something.foo.bar.tld and the filter
1161             // was foo.bar.tld, it's still a match.
1162             // the character right before the tail must be a
1163             // `.` for this to work
1164             return false;  // proxy disallowed
1165           }
1166 
1167           if (host_len == filter_host_len) {
1168             // If the host and filter are of the same length,
1169             // they should match
1170             return false;  // proxy disallowed
1171           }
1172         }
1173       }
1174     }
1175   }
1176   return true;
1177 }
1178 
1179 // kProxyType\* may be referred to externally in
1180 // nsProxyInfo in order to compare by string pointer
1181 const char kProxyType_HTTP[] = "http";
1182 const char kProxyType_HTTPS[] = "https";
1183 const char kProxyType_PROXY[] = "proxy";
1184 const char kProxyType_SOCKS[] = "socks";
1185 const char kProxyType_SOCKS4[] = "socks4";
1186 const char kProxyType_SOCKS5[] = "socks5";
1187 const char kProxyType_DIRECT[] = "direct";
1188 
ExtractProxyInfo(const char * start,uint32_t aResolveFlags,nsProxyInfo ** result)1189 const char* nsProtocolProxyService::ExtractProxyInfo(const char* start,
1190                                                      uint32_t aResolveFlags,
1191                                                      nsProxyInfo** result) {
1192   *result = nullptr;
1193   uint32_t flags = 0;
1194 
1195   // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
1196 
1197   // find end of proxy info delimiter
1198   const char* end = start;
1199   while (*end && *end != ';') ++end;
1200 
1201   // find end of proxy type delimiter
1202   const char* sp = start;
1203   while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
1204 
1205   uint32_t len = sp - start;
1206   const char* type = nullptr;
1207   switch (len) {
1208     case 4:
1209       if (nsCRT::strncasecmp(start, kProxyType_HTTP, 4) == 0) {
1210         type = kProxyType_HTTP;
1211       }
1212       break;
1213     case 5:
1214       if (nsCRT::strncasecmp(start, kProxyType_PROXY, 5) == 0) {
1215         type = kProxyType_HTTP;
1216       } else if (nsCRT::strncasecmp(start, kProxyType_SOCKS, 5) == 0) {
1217         type = kProxyType_SOCKS4;  // assume v4 for 4x compat
1218         if (StaticPrefs::network_proxy_default_pac_script_socks_version() ==
1219             5) {
1220           type = kProxyType_SOCKS;
1221         }
1222       } else if (nsCRT::strncasecmp(start, kProxyType_HTTPS, 5) == 0) {
1223         type = kProxyType_HTTPS;
1224       }
1225       break;
1226     case 6:
1227       if (nsCRT::strncasecmp(start, kProxyType_DIRECT, 6) == 0) {
1228         type = kProxyType_DIRECT;
1229       } else if (nsCRT::strncasecmp(start, kProxyType_SOCKS4, 6) == 0) {
1230         type = kProxyType_SOCKS4;
1231       } else if (nsCRT::strncasecmp(start, kProxyType_SOCKS5, 6) == 0) {
1232         // map "SOCKS5" to "socks" to match contract-id of registered
1233         // SOCKS-v5 socket provider.
1234         type = kProxyType_SOCKS;
1235       }
1236       break;
1237   }
1238   if (type) {
1239     int32_t port = -1;
1240 
1241     // If it's a SOCKS5 proxy, do name resolution on the server side.
1242     // We could use this with SOCKS4a servers too, but they might not
1243     // support it.
1244     if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS) {
1245       flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1246     }
1247 
1248     // extract host:port
1249     start = sp;
1250     while ((*start == ' ' || *start == '\t') && start < end) start++;
1251 
1252     // port defaults
1253     if (type == kProxyType_HTTP) {
1254       port = 80;
1255     } else if (type == kProxyType_HTTPS) {
1256       port = 443;
1257     } else {
1258       port = 1080;
1259     }
1260 
1261     RefPtr<nsProxyInfo> pi = new nsProxyInfo();
1262     pi->mType = type;
1263     pi->mFlags = flags;
1264     pi->mResolveFlags = aResolveFlags;
1265     pi->mTimeout = mFailedProxyTimeout;
1266 
1267     // www.foo.com:8080 and http://www.foo.com:8080
1268     nsDependentCSubstring maybeURL(start, end - start);
1269     nsCOMPtr<nsIURI> pacURI;
1270 
1271     nsAutoCString urlHost;
1272     // First assume the scheme is present, e.g. http://www.example.com:8080
1273     if (NS_FAILED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) ||
1274         NS_FAILED(pacURI->GetAsciiHost(urlHost)) || urlHost.IsEmpty()) {
1275       // It isn't, assume www.example.com:8080
1276       maybeURL.Insert("http://", 0);
1277 
1278       if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL))) {
1279         pacURI->GetAsciiHost(urlHost);
1280       }
1281     }
1282 
1283     if (!urlHost.IsEmpty()) {
1284       pi->mHost = urlHost;
1285 
1286       int32_t tPort;
1287       if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
1288         port = tPort;
1289       }
1290       pi->mPort = port;
1291     }
1292 
1293     pi.forget(result);
1294   }
1295 
1296   while (*end == ';' || *end == ' ' || *end == '\t') ++end;
1297   return end;
1298 }
1299 
GetProxyKey(nsProxyInfo * pi,nsCString & key)1300 void nsProtocolProxyService::GetProxyKey(nsProxyInfo* pi, nsCString& key) {
1301   key.AssignASCII(pi->mType);
1302   if (!pi->mHost.IsEmpty()) {
1303     key.Append(' ');
1304     key.Append(pi->mHost);
1305     key.Append(':');
1306     key.AppendInt(pi->mPort);
1307   }
1308 }
1309 
SecondsSinceSessionStart()1310 uint32_t nsProtocolProxyService::SecondsSinceSessionStart() {
1311   PRTime now = PR_Now();
1312 
1313   // get time elapsed since session start
1314   int64_t diff = now - mSessionStart;
1315 
1316   // convert microseconds to seconds
1317   diff /= PR_USEC_PER_SEC;
1318 
1319   // return converted 32 bit value
1320   return uint32_t(diff);
1321 }
1322 
EnableProxy(nsProxyInfo * pi)1323 void nsProtocolProxyService::EnableProxy(nsProxyInfo* pi) {
1324   nsAutoCString key;
1325   GetProxyKey(pi, key);
1326   mFailedProxies.Remove(key);
1327 }
1328 
DisableProxy(nsProxyInfo * pi)1329 void nsProtocolProxyService::DisableProxy(nsProxyInfo* pi) {
1330   nsAutoCString key;
1331   GetProxyKey(pi, key);
1332 
1333   uint32_t dsec = SecondsSinceSessionStart();
1334 
1335   // Add timeout to interval (this is the time when the proxy can
1336   // be tried again).
1337   dsec += pi->mTimeout;
1338 
1339   // NOTE: The classic codebase would increase the timeout value
1340   //       incrementally each time a subsequent failure occurred.
1341   //       We could do the same, but it would require that we not
1342   //       remove proxy entries in IsProxyDisabled or otherwise
1343   //       change the way we are recording disabled proxies.
1344   //       Simpler is probably better for now, and at least the
1345   //       user can tune the timeout setting via preferences.
1346 
1347   LOG(("DisableProxy %s %d\n", key.get(), dsec));
1348 
1349   // If this fails, oh well... means we don't have enough memory
1350   // to remember the failed proxy.
1351   mFailedProxies.InsertOrUpdate(key, dsec);
1352 }
1353 
IsProxyDisabled(nsProxyInfo * pi)1354 bool nsProtocolProxyService::IsProxyDisabled(nsProxyInfo* pi) {
1355   nsAutoCString key;
1356   GetProxyKey(pi, key);
1357 
1358   uint32_t val;
1359   if (!mFailedProxies.Get(key, &val)) return false;
1360 
1361   uint32_t dsec = SecondsSinceSessionStart();
1362 
1363   // if time passed has exceeded interval, then try proxy again.
1364   if (dsec > val) {
1365     mFailedProxies.Remove(key);
1366     return false;
1367   }
1368 
1369   return true;
1370 }
1371 
SetupPACThread(nsISerialEventTarget * mainThreadEventTarget)1372 nsresult nsProtocolProxyService::SetupPACThread(
1373     nsISerialEventTarget* mainThreadEventTarget) {
1374   if (mIsShutdown) {
1375     return NS_ERROR_FAILURE;
1376   }
1377 
1378   if (mPACMan) return NS_OK;
1379 
1380   mPACMan = new nsPACMan(mainThreadEventTarget);
1381 
1382   bool mainThreadOnly;
1383   nsresult rv;
1384   if (mSystemProxySettings &&
1385       NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
1386       !mainThreadOnly) {
1387     rv = mPACMan->Init(mSystemProxySettings);
1388   } else {
1389     rv = mPACMan->Init(nullptr);
1390   }
1391   if (NS_FAILED(rv)) {
1392     mPACMan->Shutdown();
1393     mPACMan = nullptr;
1394   }
1395   return rv;
1396 }
1397 
ResetPACThread()1398 nsresult nsProtocolProxyService::ResetPACThread() {
1399   if (!mPACMan) return NS_OK;
1400 
1401   mPACMan->Shutdown();
1402   mPACMan = nullptr;
1403   return SetupPACThread();
1404 }
1405 
ConfigureFromPAC(const nsCString & spec,bool forceReload)1406 nsresult nsProtocolProxyService::ConfigureFromPAC(const nsCString& spec,
1407                                                   bool forceReload) {
1408   nsresult rv = SetupPACThread();
1409   NS_ENSURE_SUCCESS(rv, rv);
1410 
1411   bool autodetect = spec.IsEmpty();
1412   if (!forceReload && ((!autodetect && mPACMan->IsPACURI(spec)) ||
1413                        (autodetect && mPACMan->IsUsingWPAD()))) {
1414     return NS_OK;
1415   }
1416 
1417   mFailedProxies.Clear();
1418 
1419   mPACMan->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled);
1420   return mPACMan->LoadPACFromURI(spec);
1421 }
1422 
ProcessPACString(const nsCString & pacString,uint32_t aResolveFlags,nsIProxyInfo ** result)1423 void nsProtocolProxyService::ProcessPACString(const nsCString& pacString,
1424                                               uint32_t aResolveFlags,
1425                                               nsIProxyInfo** result) {
1426   if (pacString.IsEmpty()) {
1427     *result = nullptr;
1428     return;
1429   }
1430 
1431   const char* proxies = pacString.get();
1432 
1433   nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
1434   while (*proxies) {
1435     proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
1436     if (pi && (pi->mType == kProxyType_HTTPS) && !mProxyOverTLS) {
1437       delete pi;
1438       pi = nullptr;
1439     }
1440 
1441     if (pi) {
1442       if (last) {
1443         NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
1444         last->mNext = pi;
1445       } else {
1446         first = pi;
1447       }
1448       last = pi;
1449     }
1450   }
1451   *result = first;
1452 }
1453 
1454 // nsIProtocolProxyService2
1455 NS_IMETHODIMP
ReloadPAC()1456 nsProtocolProxyService::ReloadPAC() {
1457   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
1458   if (!prefs) return NS_OK;
1459 
1460   int32_t type;
1461   nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
1462   if (NS_FAILED(rv)) return NS_OK;
1463 
1464   nsAutoCString pacSpec;
1465   if (type == PROXYCONFIG_PAC) {
1466     prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
1467   } else if (type == PROXYCONFIG_SYSTEM) {
1468     if (mSystemProxySettings) {
1469       AsyncConfigureFromPAC(true, true);
1470     } else {
1471       ResetPACThread();
1472     }
1473   }
1474 
1475   if (!pacSpec.IsEmpty() || type == PROXYCONFIG_WPAD) {
1476     ConfigureFromPAC(pacSpec, true);
1477   }
1478   return NS_OK;
1479 }
1480 
1481 // When sync interface is removed this can go away too
1482 // The nsPACManCallback portion of this implementation should be run
1483 // off the main thread, because it uses a condvar for signaling and
1484 // the main thread is blocking on that condvar -
1485 //  so call nsPACMan::AsyncGetProxyForURI() with
1486 // a false mainThreadResponse parameter.
1487 class nsAsyncBridgeRequest final : public nsPACManCallback {
1488   NS_DECL_THREADSAFE_ISUPPORTS
1489 
nsAsyncBridgeRequest()1490   nsAsyncBridgeRequest()
1491       : mMutex("nsDeprecatedCallback"),
1492         mCondVar(mMutex, "nsDeprecatedCallback") {}
1493 
OnQueryComplete(nsresult status,const nsACString & pacString,const nsACString & newPACURL)1494   void OnQueryComplete(nsresult status, const nsACString& pacString,
1495                        const nsACString& newPACURL) override {
1496     MutexAutoLock lock(mMutex);
1497     mCompleted = true;
1498     mStatus = status;
1499     mPACString = pacString;
1500     mPACURL = newPACURL;
1501     mCondVar.Notify();
1502   }
1503 
Lock()1504   void Lock() { mMutex.Lock(); }
Unlock()1505   void Unlock() { mMutex.Unlock(); }
Wait()1506   void Wait() { mCondVar.Wait(TimeDuration::FromSeconds(3)); }
1507 
1508  private:
1509   ~nsAsyncBridgeRequest() = default;
1510 
1511   friend class nsProtocolProxyService;
1512 
1513   Mutex mMutex;
1514   CondVar mCondVar;
1515 
1516   nsresult mStatus{NS_OK};
1517   nsCString mPACString;
1518   nsCString mPACURL;
1519   bool mCompleted{false};
1520 };
NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)1521 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
1522 
1523 nsresult nsProtocolProxyService::AsyncResolveInternal(
1524     nsIChannel* channel, uint32_t flags, nsIProtocolProxyCallback* callback,
1525     nsICancelable** result, bool isSyncOK,
1526     nsISerialEventTarget* mainThreadEventTarget) {
1527   NS_ENSURE_ARG_POINTER(channel);
1528   NS_ENSURE_ARG_POINTER(callback);
1529 
1530   nsCOMPtr<nsIURI> uri;
1531   nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
1532   if (NS_FAILED(rv)) return rv;
1533 
1534   *result = nullptr;
1535   RefPtr<nsAsyncResolveRequest> ctx =
1536       new nsAsyncResolveRequest(this, channel, flags, callback);
1537 
1538   nsProtocolInfo info;
1539   rv = GetProtocolInfo(uri, &info);
1540   if (NS_FAILED(rv)) return rv;
1541 
1542   nsCOMPtr<nsIProxyInfo> pi;
1543   bool usePACThread;
1544 
1545   // adapt to realtime changes in the system proxy service
1546   if (mProxyConfig == PROXYCONFIG_SYSTEM) {
1547     nsCOMPtr<nsISystemProxySettings> sp2 =
1548         do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
1549     if (sp2 != mSystemProxySettings) {
1550       mSystemProxySettings = sp2;
1551       ResetPACThread();
1552     }
1553   }
1554 
1555   rv = SetupPACThread(mainThreadEventTarget);
1556   if (NS_FAILED(rv)) {
1557     return rv;
1558   }
1559 
1560   // SystemProxySettings and PAC files can block the main thread
1561   // but if neither of them are in use, we can just do the work
1562   // right here and directly invoke the callback
1563 
1564   rv =
1565       Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
1566   if (NS_FAILED(rv)) return rv;
1567 
1568   if (!usePACThread || !mPACMan) {
1569     // we can do it locally
1570     rv = ctx->ProcessLocally(info, pi, isSyncOK);
1571     if (NS_SUCCEEDED(rv) && !isSyncOK) {
1572       ctx.forget(result);
1573     }
1574     return rv;
1575   }
1576 
1577   // else kick off a PAC thread query
1578   rv = mPACMan->AsyncGetProxyForURI(uri, ctx, flags, true);
1579   if (NS_SUCCEEDED(rv)) ctx.forget(result);
1580   return rv;
1581 }
1582 
1583 // nsIProtocolProxyService
1584 NS_IMETHODIMP
AsyncResolve2(nsIChannel * channel,uint32_t flags,nsIProtocolProxyCallback * callback,nsISerialEventTarget * mainThreadEventTarget,nsICancelable ** result)1585 nsProtocolProxyService::AsyncResolve2(
1586     nsIChannel* channel, uint32_t flags, nsIProtocolProxyCallback* callback,
1587     nsISerialEventTarget* mainThreadEventTarget, nsICancelable** result) {
1588   return AsyncResolveInternal(channel, flags, callback, result, true,
1589                               mainThreadEventTarget);
1590 }
1591 
1592 NS_IMETHODIMP
AsyncResolve(nsISupports * channelOrURI,uint32_t flags,nsIProtocolProxyCallback * callback,nsISerialEventTarget * mainThreadEventTarget,nsICancelable ** result)1593 nsProtocolProxyService::AsyncResolve(
1594     nsISupports* channelOrURI, uint32_t flags,
1595     nsIProtocolProxyCallback* callback,
1596     nsISerialEventTarget* mainThreadEventTarget, nsICancelable** result) {
1597   nsresult rv;
1598   // Check if we got a channel:
1599   nsCOMPtr<nsIChannel> channel = do_QueryInterface(channelOrURI);
1600   if (!channel) {
1601     nsCOMPtr<nsIURI> uri = do_QueryInterface(channelOrURI);
1602     if (!uri) {
1603       return NS_ERROR_NO_INTERFACE;
1604     }
1605 
1606     // creating a temporary channel from the URI which is not
1607     // used to perform any network loads, hence its safe to
1608     // use systemPrincipal as the loadingPrincipal.
1609     rv = NS_NewChannel(getter_AddRefs(channel), uri,
1610                        nsContentUtils::GetSystemPrincipal(),
1611                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
1612                        nsIContentPolicy::TYPE_OTHER);
1613     NS_ENSURE_SUCCESS(rv, rv);
1614   }
1615 
1616   return AsyncResolveInternal(channel, flags, callback, result, false,
1617                               mainThreadEventTarget);
1618 }
1619 
1620 NS_IMETHODIMP
NewProxyInfo(const nsACString & aType,const nsACString & aHost,int32_t aPort,const nsACString & aProxyAuthorizationHeader,const nsACString & aConnectionIsolationKey,uint32_t aFlags,uint32_t aFailoverTimeout,nsIProxyInfo * aFailoverProxy,nsIProxyInfo ** aResult)1621 nsProtocolProxyService::NewProxyInfo(
1622     const nsACString& aType, const nsACString& aHost, int32_t aPort,
1623     const nsACString& aProxyAuthorizationHeader,
1624     const nsACString& aConnectionIsolationKey, uint32_t aFlags,
1625     uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
1626     nsIProxyInfo** aResult) {
1627   return NewProxyInfoWithAuth(aType, aHost, aPort, ""_ns, ""_ns,
1628                               aProxyAuthorizationHeader,
1629                               aConnectionIsolationKey, aFlags, aFailoverTimeout,
1630                               aFailoverProxy, aResult);
1631 }
1632 
1633 NS_IMETHODIMP
NewProxyInfoWithAuth(const nsACString & aType,const nsACString & aHost,int32_t aPort,const nsACString & aUsername,const nsACString & aPassword,const nsACString & aProxyAuthorizationHeader,const nsACString & aConnectionIsolationKey,uint32_t aFlags,uint32_t aFailoverTimeout,nsIProxyInfo * aFailoverProxy,nsIProxyInfo ** aResult)1634 nsProtocolProxyService::NewProxyInfoWithAuth(
1635     const nsACString& aType, const nsACString& aHost, int32_t aPort,
1636     const nsACString& aUsername, const nsACString& aPassword,
1637     const nsACString& aProxyAuthorizationHeader,
1638     const nsACString& aConnectionIsolationKey, uint32_t aFlags,
1639     uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
1640     nsIProxyInfo** aResult) {
1641   static const char* types[] = {kProxyType_HTTP, kProxyType_HTTPS,
1642                                 kProxyType_SOCKS, kProxyType_SOCKS4,
1643                                 kProxyType_DIRECT};
1644 
1645   // resolve type; this allows us to avoid copying the type string into each
1646   // proxy info instance.  we just reference the string literals directly :)
1647   const char* type = nullptr;
1648   for (auto& t : types) {
1649     if (aType.LowerCaseEqualsASCII(t)) {
1650       type = t;
1651       break;
1652     }
1653   }
1654   NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
1655 
1656   // We have only implemented username/password for SOCKS proxies.
1657   if ((!aUsername.IsEmpty() || !aPassword.IsEmpty()) &&
1658       !aType.LowerCaseEqualsASCII(kProxyType_SOCKS) &&
1659       !aType.LowerCaseEqualsASCII(kProxyType_SOCKS4)) {
1660     return NS_ERROR_NOT_IMPLEMENTED;
1661   }
1662 
1663   return NewProxyInfo_Internal(type, aHost, aPort, aUsername, aPassword,
1664                                aProxyAuthorizationHeader,
1665                                aConnectionIsolationKey, aFlags,
1666                                aFailoverTimeout, aFailoverProxy, 0, aResult);
1667 }
1668 
1669 NS_IMETHODIMP
GetFailoverForProxy(nsIProxyInfo * aProxy,nsIURI * aURI,nsresult aStatus,nsIProxyInfo ** aResult)1670 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo* aProxy, nsIURI* aURI,
1671                                             nsresult aStatus,
1672                                             nsIProxyInfo** aResult) {
1673   // Failover is supported through a variety of methods including:
1674   // * PAC scripts (PROXYCONFIG_PAC and PROXYCONFIG_WPAD)
1675   // * System proxy
1676   // * Extensions
1677   // With extensions the mProxyConfig can be any type and the extension
1678   // is still involved in the proxy filtering.  It may have also supplied
1679   // any number of failover proxies.  We cannot determine what the mix is
1680   // here, so we will attempt to get a failover regardless of the config
1681   // type.  MANUAL configuration will not disable a proxy.
1682 
1683   // Verify that |aProxy| is one of our nsProxyInfo objects.
1684   nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
1685   NS_ENSURE_ARG(pi);
1686   // OK, the QI checked out.  We can proceed.
1687 
1688   // Remember that this proxy is down.  If the user has manually configured some
1689   // proxies we do not want to disable them.
1690   if (mProxyConfig != PROXYCONFIG_MANUAL) {
1691     DisableProxy(pi);
1692   }
1693 
1694   // NOTE: At this point, we might want to prompt the user if we have
1695   //       not already tried going DIRECT.  This is something that the
1696   //       classic codebase supported; however, IE6 does not prompt.
1697 
1698   if (!pi->mNext) return NS_ERROR_NOT_AVAILABLE;
1699 
1700   LOG(("PAC failover from %s %s:%d to %s %s:%d\n", pi->mType, pi->mHost.get(),
1701        pi->mPort, pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
1702 
1703   *aResult = do_AddRef(pi->mNext).take();
1704   return NS_OK;
1705 }
1706 
1707 namespace {  // anon
1708 
1709 class ProxyFilterPositionComparator {
1710   using FilterLinkRef = RefPtr<nsProtocolProxyService::FilterLink>;
1711 
1712  public:
Equals(const FilterLinkRef & a,const FilterLinkRef & b) const1713   bool Equals(const FilterLinkRef& a, const FilterLinkRef& b) const {
1714     return a->position == b->position;
1715   }
LessThan(const FilterLinkRef & a,const FilterLinkRef & b) const1716   bool LessThan(const FilterLinkRef& a, const FilterLinkRef& b) const {
1717     return a->position < b->position;
1718   }
1719 };
1720 
1721 class ProxyFilterObjectComparator {
1722   using FilterLinkRef = RefPtr<nsProtocolProxyService::FilterLink>;
1723 
1724  public:
Equals(const FilterLinkRef & link,const nsISupports * obj) const1725   bool Equals(const FilterLinkRef& link, const nsISupports* obj) const {
1726     return obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->filter)) ||
1727            obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->channelFilter));
1728   }
1729 };
1730 
1731 }  // namespace
1732 
InsertFilterLink(RefPtr<FilterLink> && link)1733 nsresult nsProtocolProxyService::InsertFilterLink(RefPtr<FilterLink>&& link) {
1734   LOG(("nsProtocolProxyService::InsertFilterLink filter=%p", link.get()));
1735 
1736   if (mIsShutdown) {
1737     return NS_ERROR_FAILURE;
1738   }
1739 
1740   mFilters.AppendElement(link);
1741   mFilters.Sort(ProxyFilterPositionComparator());
1742   return NS_OK;
1743 }
1744 
1745 NS_IMETHODIMP
RegisterFilter(nsIProtocolProxyFilter * filter,uint32_t position)1746 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter* filter,
1747                                        uint32_t position) {
1748   UnregisterFilter(filter);  // remove this filter if we already have it
1749 
1750   RefPtr<FilterLink> link = new FilterLink(position, filter);
1751   return InsertFilterLink(std::move(link));
1752 }
1753 
1754 NS_IMETHODIMP
RegisterChannelFilter(nsIProtocolProxyChannelFilter * channelFilter,uint32_t position)1755 nsProtocolProxyService::RegisterChannelFilter(
1756     nsIProtocolProxyChannelFilter* channelFilter, uint32_t position) {
1757   UnregisterChannelFilter(
1758       channelFilter);  // remove this filter if we already have it
1759 
1760   RefPtr<FilterLink> link = new FilterLink(position, channelFilter);
1761   return InsertFilterLink(std::move(link));
1762 }
1763 
RemoveFilterLink(nsISupports * givenObject)1764 nsresult nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject) {
1765   LOG(("nsProtocolProxyService::RemoveFilterLink target=%p", givenObject));
1766 
1767   return mFilters.RemoveElement(givenObject, ProxyFilterObjectComparator())
1768              ? NS_OK
1769              : NS_ERROR_UNEXPECTED;
1770 }
1771 
1772 NS_IMETHODIMP
UnregisterFilter(nsIProtocolProxyFilter * filter)1773 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter* filter) {
1774   // QI to nsISupports so we can safely test object identity.
1775   nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
1776   return RemoveFilterLink(givenObject);
1777 }
1778 
1779 NS_IMETHODIMP
UnregisterChannelFilter(nsIProtocolProxyChannelFilter * channelFilter)1780 nsProtocolProxyService::UnregisterChannelFilter(
1781     nsIProtocolProxyChannelFilter* channelFilter) {
1782   // QI to nsISupports so we can safely test object identity.
1783   nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
1784   return RemoveFilterLink(givenObject);
1785 }
1786 
1787 NS_IMETHODIMP
GetProxyConfigType(uint32_t * aProxyConfigType)1788 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType) {
1789   *aProxyConfigType = mProxyConfig;
1790   return NS_OK;
1791 }
1792 
LoadHostFilters(const nsACString & aFilters)1793 void nsProtocolProxyService::LoadHostFilters(const nsACString& aFilters) {
1794   if (mIsShutdown) {
1795     return;
1796   }
1797 
1798   // check to see the owners flag? /!?/ TODO
1799   if (mHostFiltersArray.Length() > 0) {
1800     mHostFiltersArray.Clear();
1801   }
1802 
1803   // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref
1804   // string
1805   mFilterLocalHosts = false;
1806 
1807   if (aFilters.IsEmpty()) {
1808     return;
1809   }
1810 
1811   //
1812   // filter  = ( host | domain | ipaddr ["/" mask] ) [":" port]
1813   // filters = filter *( "," LWS filter)
1814   //
1815   mozilla::Tokenizer t(aFilters);
1816   mozilla::Tokenizer::Token token;
1817   bool eof = false;
1818   // while (*filters) {
1819   while (!eof) {
1820     // skip over spaces and ,
1821     t.SkipWhites();
1822     while (t.CheckChar(',')) {
1823       t.SkipWhites();
1824     }
1825 
1826     nsAutoCString portStr;
1827     nsAutoCString hostStr;
1828     nsAutoCString maskStr;
1829     t.Record();
1830 
1831     bool parsingIPv6 = false;
1832     bool parsingPort = false;
1833     bool parsingMask = false;
1834     while (t.Next(token)) {
1835       if (token.Equals(mozilla::Tokenizer::Token::EndOfFile())) {
1836         eof = true;
1837         break;
1838       }
1839       if (token.Equals(mozilla::Tokenizer::Token::Char(',')) ||
1840           token.Type() == mozilla::Tokenizer::TOKEN_WS) {
1841         break;
1842       }
1843 
1844       if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
1845         parsingIPv6 = true;
1846         continue;
1847       }
1848 
1849       if (!parsingIPv6 && token.Equals(mozilla::Tokenizer::Token::Char(':'))) {
1850         // Port is starting. Claim the previous as host.
1851         if (parsingMask) {
1852           t.Claim(maskStr);
1853         } else {
1854           t.Claim(hostStr);
1855         }
1856         t.Record();
1857         parsingPort = true;
1858         continue;
1859       }
1860 
1861       if (token.Equals(mozilla::Tokenizer::Token::Char('/'))) {
1862         t.Claim(hostStr);
1863         t.Record();
1864         parsingMask = true;
1865         continue;
1866       }
1867 
1868       if (token.Equals(mozilla::Tokenizer::Token::Char(']'))) {
1869         parsingIPv6 = false;
1870         continue;
1871       }
1872     }
1873     if (!parsingPort && !parsingMask) {
1874       t.Claim(hostStr);
1875     } else if (parsingPort) {
1876       t.Claim(portStr);
1877     } else if (parsingMask) {
1878       t.Claim(maskStr);
1879     } else {
1880       NS_WARNING("Could not parse this rule");
1881       continue;
1882     }
1883 
1884     if (hostStr.IsEmpty()) {
1885       continue;
1886     }
1887 
1888     // If the current host filter is "<local>", then all local (i.e.
1889     // no dots in the hostname) hosts should bypass the proxy
1890     if (hostStr.EqualsIgnoreCase("<local>")) {
1891       mFilterLocalHosts = true;
1892       LOG(
1893           ("loaded filter for local hosts "
1894            "(plain host names, no dots)\n"));
1895       // Continue to next host filter;
1896       continue;
1897     }
1898 
1899     // For all other host filters, create HostInfo object and add to list
1900     HostInfo* hinfo = new HostInfo();
1901     nsresult rv = NS_OK;
1902 
1903     int32_t port = portStr.ToInteger(&rv);
1904     if (NS_FAILED(rv)) {
1905       port = 0;
1906     }
1907     hinfo->port = port;
1908 
1909     int32_t maskLen = maskStr.ToInteger(&rv);
1910     if (NS_FAILED(rv)) {
1911       maskLen = 128;
1912     }
1913 
1914     // PR_StringToNetAddr can't parse brackets enclosed IPv6
1915     nsAutoCString addrString = hostStr;
1916     if (hostStr.First() == '[' && hostStr.Last() == ']') {
1917       addrString = Substring(hostStr, 1, hostStr.Length() - 2);
1918     }
1919 
1920     PRNetAddr addr;
1921     if (PR_StringToNetAddr(addrString.get(), &addr) == PR_SUCCESS) {
1922       hinfo->is_ipaddr = true;
1923       hinfo->ip.family = PR_AF_INET6;  // we always store address as IPv6
1924       hinfo->ip.mask_len = maskLen;
1925 
1926       if (hinfo->ip.mask_len == 0) {
1927         NS_WARNING("invalid mask");
1928         goto loser;
1929       }
1930 
1931       if (addr.raw.family == PR_AF_INET) {
1932         // convert to IPv4-mapped address
1933         PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
1934         // adjust mask_len accordingly
1935         if (hinfo->ip.mask_len <= 32) hinfo->ip.mask_len += 96;
1936       } else if (addr.raw.family == PR_AF_INET6) {
1937         // copy the address
1938         memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1939       } else {
1940         NS_WARNING("unknown address family");
1941         goto loser;
1942       }
1943 
1944       // apply mask to IPv6 address
1945       proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
1946     } else {
1947       nsAutoCString host;
1948       if (hostStr.First() == '*') {
1949         host = Substring(hostStr, 1);
1950       } else {
1951         host = hostStr;
1952       }
1953 
1954       if (host.IsEmpty()) {
1955         hinfo->name.host = nullptr;
1956         goto loser;
1957       }
1958 
1959       hinfo->name.host_len = host.Length();
1960 
1961       hinfo->is_ipaddr = false;
1962       hinfo->name.host = ToNewCString(host, mozilla::fallible);
1963 
1964       if (!hinfo->name.host) goto loser;
1965     }
1966 
1967 //#define DEBUG_DUMP_FILTERS
1968 #ifdef DEBUG_DUMP_FILTERS
1969     printf("loaded filter[%zu]:\n", mHostFiltersArray.Length());
1970     printf("  is_ipaddr = %u\n", hinfo->is_ipaddr);
1971     printf("  port = %u\n", hinfo->port);
1972     printf("  host = %s\n", hostStr.get());
1973     if (hinfo->is_ipaddr) {
1974       printf("  ip.family = %x\n", hinfo->ip.family);
1975       printf("  ip.mask_len = %u\n", hinfo->ip.mask_len);
1976 
1977       PRNetAddr netAddr;
1978       PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
1979       memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
1980 
1981       char buf[256];
1982       PR_NetAddrToString(&netAddr, buf, sizeof(buf));
1983 
1984       printf("  ip.addr = %s\n", buf);
1985     } else {
1986       printf("  name.host = %s\n", hinfo->name.host);
1987     }
1988 #endif
1989 
1990     mHostFiltersArray.AppendElement(hinfo);
1991     hinfo = nullptr;
1992   loser:
1993     delete hinfo;
1994   }
1995 }
1996 
GetProtocolInfo(nsIURI * uri,nsProtocolInfo * info)1997 nsresult nsProtocolProxyService::GetProtocolInfo(nsIURI* uri,
1998                                                  nsProtocolInfo* info) {
1999   MOZ_ASSERT(uri, "URI is null");
2000   MOZ_ASSERT(info, "info is null");
2001 
2002   nsresult rv;
2003 
2004   rv = uri->GetScheme(info->scheme);
2005   if (NS_FAILED(rv)) return rv;
2006 
2007   nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
2008   if (NS_FAILED(rv)) return rv;
2009 
2010   nsCOMPtr<nsIProtocolHandler> handler;
2011   rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
2012   if (NS_FAILED(rv)) return rv;
2013 
2014   rv = handler->DoGetProtocolFlags(uri, &info->flags);
2015   if (NS_FAILED(rv)) return rv;
2016 
2017   rv = handler->GetDefaultPort(&info->defaultPort);
2018   return rv;
2019 }
2020 
NewProxyInfo_Internal(const char * aType,const nsACString & aHost,int32_t aPort,const nsACString & aUsername,const nsACString & aPassword,const nsACString & aProxyAuthorizationHeader,const nsACString & aConnectionIsolationKey,uint32_t aFlags,uint32_t aFailoverTimeout,nsIProxyInfo * aFailoverProxy,uint32_t aResolveFlags,nsIProxyInfo ** aResult)2021 nsresult nsProtocolProxyService::NewProxyInfo_Internal(
2022     const char* aType, const nsACString& aHost, int32_t aPort,
2023     const nsACString& aUsername, const nsACString& aPassword,
2024     const nsACString& aProxyAuthorizationHeader,
2025     const nsACString& aConnectionIsolationKey, uint32_t aFlags,
2026     uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
2027     uint32_t aResolveFlags, nsIProxyInfo** aResult) {
2028   if (aPort <= 0) aPort = -1;
2029 
2030   nsCOMPtr<nsProxyInfo> failover;
2031   if (aFailoverProxy) {
2032     failover = do_QueryInterface(aFailoverProxy);
2033     NS_ENSURE_ARG(failover);
2034   }
2035 
2036   RefPtr<nsProxyInfo> proxyInfo = new nsProxyInfo();
2037 
2038   proxyInfo->mType = aType;
2039   proxyInfo->mHost = aHost;
2040   proxyInfo->mPort = aPort;
2041   proxyInfo->mUsername = aUsername;
2042   proxyInfo->mPassword = aPassword;
2043   proxyInfo->mFlags = aFlags;
2044   proxyInfo->mResolveFlags = aResolveFlags;
2045   proxyInfo->mTimeout =
2046       aFailoverTimeout == UINT32_MAX ? mFailedProxyTimeout : aFailoverTimeout;
2047   proxyInfo->mProxyAuthorizationHeader = aProxyAuthorizationHeader;
2048   proxyInfo->mConnectionIsolationKey = aConnectionIsolationKey;
2049   failover.swap(proxyInfo->mNext);
2050 
2051   proxyInfo.forget(aResult);
2052   return NS_OK;
2053 }
2054 
Resolve_Internal(nsIChannel * channel,const nsProtocolInfo & info,uint32_t flags,bool * usePACThread,nsIProxyInfo ** result)2055 nsresult nsProtocolProxyService::Resolve_Internal(nsIChannel* channel,
2056                                                   const nsProtocolInfo& info,
2057                                                   uint32_t flags,
2058                                                   bool* usePACThread,
2059                                                   nsIProxyInfo** result) {
2060   NS_ENSURE_ARG_POINTER(channel);
2061 
2062   *usePACThread = false;
2063   *result = nullptr;
2064 
2065   if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY)) {
2066     return NS_OK;  // Can't proxy this (filters may not override)
2067   }
2068 
2069   nsCOMPtr<nsIURI> uri;
2070   nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
2071   if (NS_FAILED(rv)) return rv;
2072 
2073   // See bug #586908.
2074   // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
2075   // here means that we will not use a proxy for this connection.
2076   if (mPACMan && mPACMan->IsPACURI(uri)) return NS_OK;
2077 
2078   // if proxies are enabled and this host:port combo is supposed to use a
2079   // proxy, check for a proxy.
2080   if ((mProxyConfig == PROXYCONFIG_DIRECT) ||
2081       !CanUseProxy(uri, info.defaultPort)) {
2082     return NS_OK;
2083   }
2084 
2085   bool mainThreadOnly;
2086   if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM &&
2087       NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
2088       !mainThreadOnly) {
2089     *usePACThread = true;
2090     return NS_OK;
2091   }
2092 
2093   if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) {
2094     // If the system proxy setting implementation is not threadsafe (e.g
2095     // linux gconf), we'll do it inline here. Such implementations promise
2096     // not to block
2097     // bug 1366133: this block uses GetPACURI & GetProxyForURI, which may
2098     // hang on Windows platform. Fortunately, current implementation on
2099     // Windows is not main thread only, so we are safe here.
2100 
2101     nsAutoCString PACURI;
2102     nsAutoCString pacString;
2103 
2104     if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
2105         !PACURI.IsEmpty()) {
2106       // There is a PAC URI configured. If it is unchanged, then
2107       // just execute the PAC thread. If it is changed then load
2108       // the new value
2109 
2110       if (mPACMan && mPACMan->IsPACURI(PACURI)) {
2111         // unchanged
2112         *usePACThread = true;
2113         return NS_OK;
2114       }
2115 
2116       ConfigureFromPAC(PACURI, false);
2117       return NS_OK;
2118     }
2119 
2120     nsAutoCString spec;
2121     nsAutoCString host;
2122     nsAutoCString scheme;
2123     int32_t port = -1;
2124 
2125     uri->GetAsciiSpec(spec);
2126     uri->GetAsciiHost(host);
2127     uri->GetScheme(scheme);
2128     uri->GetPort(&port);
2129 
2130     if (flags & RESOLVE_PREFER_SOCKS_PROXY) {
2131       LOG(("Ignoring RESOLVE_PREFER_SOCKS_PROXY for system proxy setting\n"));
2132     } else if (flags & RESOLVE_PREFER_HTTPS_PROXY) {
2133       scheme.AssignLiteral("https");
2134     } else if (flags & RESOLVE_IGNORE_URI_SCHEME) {
2135       scheme.AssignLiteral("http");
2136     }
2137 
2138     // now try the system proxy settings for this particular url
2139     if (NS_SUCCEEDED(mSystemProxySettings->GetProxyForURI(spec, scheme, host,
2140                                                           port, pacString))) {
2141       nsCOMPtr<nsIProxyInfo> pi;
2142       ProcessPACString(pacString, 0, getter_AddRefs(pi));
2143 
2144       if (flags & RESOLVE_PREFER_SOCKS_PROXY &&
2145           flags & RESOLVE_PREFER_HTTPS_PROXY) {
2146         nsAutoCString type;
2147         pi->GetType(type);
2148         // DIRECT from ProcessPACString indicates that system proxy settings
2149         // are not configured to use SOCKS proxy. Try https proxy as a
2150         // secondary preferrable proxy. This is mainly for websocket whose
2151         // proxy precedence is SOCKS > HTTPS > DIRECT.
2152         if (type.EqualsLiteral(kProxyType_DIRECT)) {
2153           scheme.AssignLiteral(kProxyType_HTTPS);
2154           if (NS_SUCCEEDED(mSystemProxySettings->GetProxyForURI(
2155                   spec, scheme, host, port, pacString))) {
2156             ProcessPACString(pacString, 0, getter_AddRefs(pi));
2157           }
2158         }
2159       }
2160       pi.forget(result);
2161       return NS_OK;
2162     }
2163   }
2164 
2165   // if proxies are enabled and this host:port combo is supposed to use a
2166   // proxy, check for a proxy.
2167   if (mProxyConfig == PROXYCONFIG_DIRECT ||
2168       (mProxyConfig == PROXYCONFIG_MANUAL &&
2169        !CanUseProxy(uri, info.defaultPort))) {
2170     return NS_OK;
2171   }
2172 
2173   // Proxy auto config magic...
2174   if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD) {
2175     // Do not query PAC now.
2176     *usePACThread = true;
2177     return NS_OK;
2178   }
2179 
2180   // If we aren't in manual proxy configuration mode then we don't
2181   // want to honor any manual specific prefs that might be still set
2182   if (mProxyConfig != PROXYCONFIG_MANUAL) return NS_OK;
2183 
2184   // proxy info values for manual configuration mode
2185   const char* type = nullptr;
2186   const nsACString* host = nullptr;
2187   int32_t port = -1;
2188 
2189   uint32_t proxyFlags = 0;
2190 
2191   if ((flags & RESOLVE_PREFER_SOCKS_PROXY) && !mSOCKSProxyTarget.IsEmpty() &&
2192       (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
2193     host = &mSOCKSProxyTarget;
2194     if (mSOCKSProxyVersion == 4) {
2195       type = kProxyType_SOCKS4;
2196     } else {
2197       type = kProxyType_SOCKS;
2198     }
2199     port = mSOCKSProxyPort;
2200     if (mSOCKSProxyRemoteDNS) {
2201       proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
2202     }
2203   } else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
2204              !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
2205     host = &mHTTPSProxyHost;
2206     type = kProxyType_HTTP;
2207     port = mHTTPSProxyPort;
2208   } else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
2209              ((flags & RESOLVE_IGNORE_URI_SCHEME) ||
2210               info.scheme.EqualsLiteral("http"))) {
2211     host = &mHTTPProxyHost;
2212     type = kProxyType_HTTP;
2213     port = mHTTPProxyPort;
2214   } else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
2215              !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
2216              info.scheme.EqualsLiteral("https")) {
2217     host = &mHTTPSProxyHost;
2218     type = kProxyType_HTTP;
2219     port = mHTTPSProxyPort;
2220   } else if (!mSOCKSProxyTarget.IsEmpty() &&
2221              (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
2222     host = &mSOCKSProxyTarget;
2223     if (mSOCKSProxyVersion == 4) {
2224       type = kProxyType_SOCKS4;
2225     } else {
2226       type = kProxyType_SOCKS;
2227     }
2228     port = mSOCKSProxyPort;
2229     if (mSOCKSProxyRemoteDNS) {
2230       proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
2231     }
2232   }
2233 
2234   if (type) {
2235     rv = NewProxyInfo_Internal(type, *host, port, ""_ns, ""_ns, ""_ns, ""_ns,
2236                                proxyFlags, UINT32_MAX, nullptr, flags, result);
2237     if (NS_FAILED(rv)) return rv;
2238   }
2239 
2240   return NS_OK;
2241 }
2242 
MaybeDisableDNSPrefetch(nsIProxyInfo * aProxy)2243 void nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo* aProxy) {
2244   // Disable Prefetch in the DNS service if a proxy is in use.
2245   if (!aProxy) return;
2246 
2247   nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
2248   if (!pi || !pi->mType || pi->mType == kProxyType_DIRECT) return;
2249 
2250   nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
2251   if (!dns) return;
2252   nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
2253   if (!pdns) return;
2254 
2255   // We lose the prefetch optimization for the life of the dns service.
2256   pdns->SetPrefetchEnabled(false);
2257 }
2258 
CopyFilters(nsTArray<RefPtr<FilterLink>> & aCopy)2259 void nsProtocolProxyService::CopyFilters(nsTArray<RefPtr<FilterLink>>& aCopy) {
2260   MOZ_ASSERT(aCopy.Length() == 0);
2261   aCopy.AppendElements(mFilters);
2262 }
2263 
ApplyFilter(FilterLink const * filterLink,nsIChannel * channel,const nsProtocolInfo & info,nsCOMPtr<nsIProxyInfo> list,nsIProxyProtocolFilterResult * callback)2264 bool nsProtocolProxyService::ApplyFilter(
2265     FilterLink const* filterLink, nsIChannel* channel,
2266     const nsProtocolInfo& info, nsCOMPtr<nsIProxyInfo> list,
2267     nsIProxyProtocolFilterResult* callback) {
2268   nsresult rv;
2269 
2270   // We prune the proxy list prior to invoking each filter.  This may be
2271   // somewhat inefficient, but it seems like a good idea since we want each
2272   // filter to "see" a valid proxy list.
2273   PruneProxyInfo(info, list);
2274 
2275   if (filterLink->filter) {
2276     nsCOMPtr<nsIURI> uri;
2277     Unused << GetProxyURI(channel, getter_AddRefs(uri));
2278     if (!uri) {
2279       return false;
2280     }
2281 
2282     rv = filterLink->filter->ApplyFilter(uri, list, callback);
2283     return NS_SUCCEEDED(rv);
2284   }
2285 
2286   if (filterLink->channelFilter) {
2287     rv = filterLink->channelFilter->ApplyFilter(channel, list, callback);
2288     return NS_SUCCEEDED(rv);
2289   }
2290 
2291   return false;
2292 }
2293 
PruneProxyInfo(const nsProtocolInfo & info,nsIProxyInfo ** list)2294 void nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo& info,
2295                                             nsIProxyInfo** list) {
2296   if (!*list) return;
2297 
2298   LOG(("nsProtocolProxyService::PruneProxyInfo ENTER list=%p", *list));
2299 
2300   nsProxyInfo* head = nullptr;
2301   CallQueryInterface(*list, &head);
2302   if (!head) {
2303     MOZ_ASSERT_UNREACHABLE("nsIProxyInfo must QI to nsProxyInfo");
2304     return;
2305   }
2306   NS_RELEASE(*list);
2307 
2308   // Pruning of disabled proxies works like this:
2309   //   - If all proxies are disabled, return the full list
2310   //   - Otherwise, remove the disabled proxies.
2311   //
2312   // Pruning of disallowed proxies works like this:
2313   //   - If the protocol handler disallows the proxy, then we disallow it.
2314 
2315   // Start by removing all disallowed proxies if required:
2316   if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
2317     nsProxyInfo *last = nullptr, *iter = head;
2318     while (iter) {
2319       if ((iter->Type() == kProxyType_HTTP) ||
2320           (iter->Type() == kProxyType_HTTPS)) {
2321         // reject!
2322         if (last) {
2323           last->mNext = iter->mNext;
2324         } else {
2325           head = iter->mNext;
2326         }
2327         nsProxyInfo* next = iter->mNext;
2328         iter->mNext = nullptr;
2329         iter->Release();
2330         iter = next;
2331       } else {
2332         last = iter;
2333         iter = iter->mNext;
2334       }
2335     }
2336     if (!head) {
2337       return;
2338     }
2339   }
2340 
2341   // Scan to see if all remaining non-direct proxies are disabled. If so, then
2342   // we'll just bail and return them all.  Otherwise, we'll go and prune the
2343   // disabled ones.
2344 
2345   bool allNonDirectProxiesDisabled = true;
2346 
2347   nsProxyInfo* iter;
2348   for (iter = head; iter; iter = iter->mNext) {
2349     if (!IsProxyDisabled(iter) && iter->mType != kProxyType_DIRECT) {
2350       allNonDirectProxiesDisabled = false;
2351       break;
2352     }
2353   }
2354 
2355   if (allNonDirectProxiesDisabled) {
2356     LOG(("All proxies are disabled, so trying all again"));
2357   } else {
2358     // remove any disabled proxies.
2359     nsProxyInfo* last = nullptr;
2360     for (iter = head; iter;) {
2361       if (IsProxyDisabled(iter)) {
2362         // reject!
2363         nsProxyInfo* reject = iter;
2364 
2365         iter = iter->mNext;
2366         if (last) {
2367           last->mNext = iter;
2368         } else {
2369           head = iter;
2370         }
2371 
2372         reject->mNext = nullptr;
2373         NS_RELEASE(reject);
2374         continue;
2375       }
2376 
2377       // since we are about to use this proxy, make sure it is not on
2378       // the disabled proxy list.  we'll add it back to that list if
2379       // we have to (in GetFailoverForProxy).
2380       //
2381       // XXX(darin): It might be better to do this as a final pass.
2382       //
2383       EnableProxy(iter);
2384 
2385       last = iter;
2386       iter = iter->mNext;
2387     }
2388   }
2389 
2390   // if only DIRECT was specified then return no proxy info, and we're done.
2391   if (head && !head->mNext && head->mType == kProxyType_DIRECT) {
2392     NS_RELEASE(head);
2393   }
2394 
2395   *list = head;  // Transfer ownership
2396 
2397   LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list));
2398 }
2399 
GetIsPACLoading()2400 bool nsProtocolProxyService::GetIsPACLoading() {
2401   return mPACMan && mPACMan->IsLoading();
2402 }
2403 
2404 }  // namespace net
2405 }  // namespace mozilla
2406