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