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