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