Source code

Revision control

Other Tools

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 "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
//----------------------------------------------------------------------------
66
67
// This structure is intended to be allocated on the stack
68
struct nsProtocolInfo {
69
nsAutoCString scheme;
70
uint32_t flags;
71
int32_t defaultPort;
72
};
73
74
//----------------------------------------------------------------------------
75
76
// Return the channel's proxy URI, or if it doesn't exist, the
77
// channel's main URI.
78
static nsresult GetProxyURI(nsIChannel* channel, nsIURI** aOut) {
79
nsresult rv = NS_OK;
80
nsCOMPtr<nsIURI> proxyURI;
81
nsCOMPtr<nsIHttpChannelInternal> httpChannel(do_QueryInterface(channel));
82
if (httpChannel) {
83
rv = httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
84
}
85
if (!proxyURI) {
86
rv = channel->GetURI(getter_AddRefs(proxyURI));
87
}
88
if (NS_FAILED(rv)) {
89
return rv;
90
}
91
proxyURI.forget(aOut);
92
return NS_OK;
93
}
94
95
//-----------------------------------------------------------------------------
96
97
nsProtocolProxyService::FilterLink::FilterLink(uint32_t p,
98
nsIProtocolProxyFilter* f)
99
: position(p), filter(f), channelFilter(nullptr) {
100
LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, filter=%p", this,
101
f));
102
}
103
nsProtocolProxyService::FilterLink::FilterLink(
104
uint32_t p, nsIProtocolProxyChannelFilter* cf)
105
: position(p), filter(nullptr), channelFilter(cf) {
106
LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, channel-filter=%p",
107
this, cf));
108
}
109
110
nsProtocolProxyService::FilterLink::~FilterLink() {
111
LOG(("nsProtocolProxyService::FilterLink::~FilterLink %p", this));
112
}
113
114
//-----------------------------------------------------------------------------
115
116
// The nsPACManCallback portion of this implementation should be run
117
// on the main thread - so call nsPACMan::AsyncGetProxyForURI() with
118
// a true mainThreadResponse parameter.
119
class nsAsyncResolveRequest final : public nsIRunnable,
120
public nsPACManCallback,
121
public nsICancelable {
122
public:
123
NS_DECL_THREADSAFE_ISUPPORTS
124
125
nsAsyncResolveRequest(nsProtocolProxyService* pps, nsIChannel* channel,
126
uint32_t aResolveFlags,
127
nsIProtocolProxyCallback* callback)
128
: mStatus(NS_OK),
129
mDispatched(false),
130
mResolveFlags(aResolveFlags),
131
mPPS(pps),
132
mXPComPPS(pps),
133
mChannel(channel),
134
mCallback(callback) {
135
NS_ASSERTION(mCallback, "null callback");
136
}
137
138
private:
139
~nsAsyncResolveRequest() {
140
if (!NS_IsMainThread()) {
141
// these xpcom pointers might need to be proxied back to the
142
// main thread to delete safely, but if this request had its
143
// callbacks called normally they will all be null and this is a nop
144
145
if (mChannel) {
146
NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mChannel",
147
mChannel.forget());
148
}
149
150
if (mCallback) {
151
NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mCallback",
152
mCallback.forget());
153
}
154
155
if (mProxyInfo) {
156
NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mProxyInfo",
157
mProxyInfo.forget());
158
}
159
160
if (mXPComPPS) {
161
NS_ReleaseOnMainThreadSystemGroup("nsAsyncResolveRequest::mXPComPPS",
162
mXPComPPS.forget());
163
}
164
}
165
}
166
167
// Helper class to loop over all registered asynchronous filters.
168
// There is a cycle between nsAsyncResolveRequest and this class that
169
// is broken after the last filter has called back on this object.
170
class AsyncApplyFilters final : public nsIProxyProtocolFilterResult,
171
public nsIRunnable,
172
public nsICancelable {
173
// The reference counter is thread-safe, but the processing logic is
174
// considered single thread only. We want the counter be thread safe,
175
// since this class can be released on a background thread.
176
NS_DECL_THREADSAFE_ISUPPORTS
177
NS_DECL_NSIPROXYPROTOCOLFILTERRESULT
178
NS_DECL_NSIRUNNABLE
179
NS_DECL_NSICANCELABLE
180
181
typedef std::function<nsresult(nsAsyncResolveRequest*, nsIProxyInfo*, bool)>
182
Callback;
183
184
explicit AsyncApplyFilters(nsProtocolInfo& aInfo,
185
Callback const& aCallback);
186
// This method starts the processing or filters. If all of them
187
// answer synchronously (call back from within applyFilters) this method
188
// will return immediately and the returning result will carry return
189
// result of the callback given in constructor.
190
// This method is looping the registered filters (that have been copied
191
// locally) as long as an answer from a filter is obtained synchronously.
192
// Note that filters are processed serially to let them build a list
193
// of proxy info.
194
nsresult AsyncProcess(nsAsyncResolveRequest* aRequest);
195
196
private:
197
typedef nsProtocolProxyService::FilterLink FilterLink;
198
199
virtual ~AsyncApplyFilters();
200
// Processes the next filter and loops until a filter is successfully
201
// called on or it has called back to us.
202
nsresult ProcessNextFilter();
203
// Called after the last filter has been processed (=called back or failed
204
// to be called on)
205
nsresult Finish();
206
207
nsProtocolInfo mInfo;
208
// This is nullified before we call back on the request or when
209
// Cancel() on this object has been called to break the cycle
210
// and signal to stop.
211
RefPtr<nsAsyncResolveRequest> mRequest;
212
Callback mCallback;
213
// A shallow snapshot of filters as they were registered at the moment
214
// we started to process filters for the given resolve request.
215
nsTArray<RefPtr<FilterLink>> mFiltersCopy;
216
217
nsTArray<RefPtr<FilterLink>>::index_type mNextFilterIndex;
218
// true when we are calling ProcessNextFilter() from inside AsyncProcess(),
219
// false otherwise.
220
bool mProcessingInLoop;
221
// true after a filter called back to us with a result, dropped to false
222
// just before we call a filter.
223
bool mFilterCalledBack;
224
225
// This keeps the initial value we pass to the first filter in line and also
226
// collects the result from each filter call.
227
nsCOMPtr<nsIProxyInfo> mProxyInfo;
228
229
// The logic is written as non-thread safe, assert single-thread usage.
230
nsCOMPtr<nsIEventTarget> mProcessingThread;
231
};
232
233
void EnsureResolveFlagsMatch() {
234
nsCOMPtr<nsIProxyInfo> proxyInfo = mProxyInfo;
235
while (proxyInfo) {
236
proxyInfo->SetResolveFlags(mResolveFlags);
237
proxyInfo->GetFailoverProxy(getter_AddRefs(proxyInfo));
238
}
239
}
240
241
public:
242
nsresult ProcessLocally(nsProtocolInfo& info, nsIProxyInfo* pi,
243
bool isSyncOK) {
244
SetResult(NS_OK, pi);
245
246
auto consumeFiltersResult = [isSyncOK](nsAsyncResolveRequest* ctx,
247
nsIProxyInfo* pi,
248
bool aCalledAsync) -> nsresult {
249
ctx->SetResult(NS_OK, pi);
250
if (isSyncOK || aCalledAsync) {
251
ctx->Run();
252
return NS_OK;
253
}
254
255
return ctx->DispatchCallback();
256
};
257
258
mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
259
// may call consumeFiltersResult() directly
260
return mAsyncFilterApplier->AsyncProcess(this);
261
}
262
263
void SetResult(nsresult status, nsIProxyInfo* pi) {
264
mStatus = status;
265
mProxyInfo = pi;
266
}
267
268
NS_IMETHOD Run() override {
269
if (mCallback) DoCallback();
270
return NS_OK;
271
}
272
273
NS_IMETHOD Cancel(nsresult reason) override {
274
NS_ENSURE_ARG(NS_FAILED(reason));
275
276
if (mAsyncFilterApplier) {
277
mAsyncFilterApplier->Cancel(reason);
278
}
279
280
// If we've already called DoCallback then, nothing more to do.
281
if (!mCallback) return NS_OK;
282
283
SetResult(reason, nullptr);
284
return DispatchCallback();
285
}
286
287
nsresult DispatchCallback() {
288
if (mDispatched) // Only need to dispatch once
289
return NS_OK;
290
291
nsresult rv = NS_DispatchToCurrentThread(this);
292
if (NS_FAILED(rv))
293
NS_WARNING("unable to dispatch callback event");
294
else {
295
mDispatched = true;
296
return NS_OK;
297
}
298
299
mCallback = nullptr; // break possible reference cycle
300
return rv;
301
}
302
303
private:
304
// Called asynchronously, so we do not need to post another PLEvent
305
// before calling DoCallback.
306
void OnQueryComplete(nsresult status, const nsACString& pacString,
307
const nsACString& newPACURL) override {
308
// If we've already called DoCallback then, nothing more to do.
309
if (!mCallback) return;
310
311
// Provided we haven't been canceled...
312
if (mStatus == NS_OK) {
313
mStatus = status;
314
mPACString = pacString;
315
mPACURL = newPACURL;
316
}
317
318
// In the cancelation case, we may still have another PLEvent in
319
// the queue that wants to call DoCallback. No need to wait for
320
// it, just run the callback now.
321
DoCallback();
322
}
323
324
void DoCallback() {
325
bool pacAvailable = true;
326
if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) {
327
// If the PAC service is not avail (e.g. failed pac load
328
// or shutdown) then we will be going direct. Make that
329
// mapping now so that any filters are still applied.
330
mPACString = NS_LITERAL_CSTRING("DIRECT;");
331
mStatus = NS_OK;
332
333
LOG(("pac not available, use DIRECT\n"));
334
pacAvailable = false;
335
}
336
337
// Generate proxy info from the PAC string if appropriate
338
if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
339
mPPS->ProcessPACString(mPACString, mResolveFlags,
340
getter_AddRefs(mProxyInfo));
341
nsCOMPtr<nsIURI> proxyURI;
342
GetProxyURI(mChannel, getter_AddRefs(proxyURI));
343
344
// Now apply proxy filters
345
nsProtocolInfo info;
346
mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
347
348
auto consumeFiltersResult = [pacAvailable](nsAsyncResolveRequest* self,
349
nsIProxyInfo* pi,
350
bool async) -> nsresult {
351
LOG(("DoCallback::consumeFiltersResult this=%p, pi=%p, async=%d", self,
352
pi, async));
353
354
self->mProxyInfo = pi;
355
356
if (pacAvailable) {
357
// if !pacAvailable, it was already logged above
358
LOG(("pac thread callback %s\n", self->mPACString.get()));
359
}
360
361
if (NS_SUCCEEDED(self->mStatus)) {
362
self->mPPS->MaybeDisableDNSPrefetch(self->mProxyInfo);
363
}
364
365
self->EnsureResolveFlagsMatch();
366
self->mCallback->OnProxyAvailable(self, self->mChannel,
367
self->mProxyInfo, self->mStatus);
368
369
return NS_OK;
370
};
371
372
if (NS_SUCCEEDED(mStatus)) {
373
mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
374
// This may call consumeFiltersResult() directly.
375
mAsyncFilterApplier->AsyncProcess(this);
376
return;
377
}
378
379
consumeFiltersResult(this, nullptr, false);
380
} else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
381
LOG(("pac thread callback indicates new pac file load\n"));
382
383
nsCOMPtr<nsIURI> proxyURI;
384
GetProxyURI(mChannel, getter_AddRefs(proxyURI));
385
386
// trigger load of new pac url
387
nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
388
if (NS_SUCCEEDED(rv)) {
389
// now that the load is triggered, we can resubmit the query
390
RefPtr<nsAsyncResolveRequest> newRequest =
391
new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback);
392
rv = mPPS->mPACMan->AsyncGetProxyForURI(proxyURI, newRequest, true);
393
}
394
395
if (NS_FAILED(rv))
396
mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
397
398
// do not call onproxyavailable() in SUCCESS case - the newRequest will
399
// take care of that
400
} else {
401
LOG(("pac thread callback did not provide information %" PRIX32 "\n",
402
static_cast<uint32_t>(mStatus)));
403
if (NS_SUCCEEDED(mStatus)) mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
404
EnsureResolveFlagsMatch();
405
mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
406
}
407
408
// We are on the main thread now and don't need these any more so
409
// release them to avoid having to proxy them back to the main thread
410
// in the dtor
411
mCallback = nullptr; // in case the callback holds an owning ref to us
412
mPPS = nullptr;
413
mXPComPPS = nullptr;
414
mChannel = nullptr;
415
mProxyInfo = nullptr;
416
}
417
418
private:
419
nsresult mStatus;
420
nsCString mPACString;
421
nsCString mPACURL;
422
bool mDispatched;
423
uint32_t mResolveFlags;
424
425
nsProtocolProxyService* mPPS;
426
nsCOMPtr<nsIProtocolProxyService> mXPComPPS;
427
nsCOMPtr<nsIChannel> mChannel;
428
nsCOMPtr<nsIProtocolProxyCallback> mCallback;
429
nsCOMPtr<nsIProxyInfo> mProxyInfo;
430
431
RefPtr<AsyncApplyFilters> mAsyncFilterApplier;
432
};
433
434
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
435
436
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest::AsyncApplyFilters,
437
nsIProxyProtocolFilterResult, nsICancelable, nsIRunnable)
438
439
nsAsyncResolveRequest::AsyncApplyFilters::AsyncApplyFilters(
440
nsProtocolInfo& aInfo, Callback const& aCallback)
441
: mInfo(aInfo),
442
mCallback(aCallback),
443
mNextFilterIndex(0),
444
mProcessingInLoop(false),
445
mFilterCalledBack(false) {
446
LOG(("AsyncApplyFilters %p", this));
447
}
448
449
nsAsyncResolveRequest::AsyncApplyFilters::~AsyncApplyFilters() {
450
LOG(("~AsyncApplyFilters %p", this));
451
452
MOZ_ASSERT(!mRequest);
453
MOZ_ASSERT(!mProxyInfo);
454
MOZ_ASSERT(!mFiltersCopy.Length());
455
}
456
457
nsresult nsAsyncResolveRequest::AsyncApplyFilters::AsyncProcess(
458
nsAsyncResolveRequest* aRequest) {
459
LOG(("AsyncApplyFilters::AsyncProcess %p for req %p", this, aRequest));
460
461
MOZ_ASSERT(!mRequest, "AsyncApplyFilters started more than once!");
462
463
if (!(mInfo.flags & nsIProtocolHandler::ALLOWS_PROXY)) {
464
// Calling the callback directly (not via Finish()) since we
465
// don't want to prune.
466
return mCallback(aRequest, aRequest->mProxyInfo, false);
467
}
468
469
mProcessingThread = NS_GetCurrentThread();
470
471
mRequest = aRequest;
472
mProxyInfo = aRequest->mProxyInfo;
473
474
aRequest->mPPS->CopyFilters(mFiltersCopy);
475
476
// We want to give filters a chance to process in a single loop to prevent
477
// any current-thread dispatch delays when those are not needed.
478
// This code is rather "loopy" than "recursive" to prevent long stack traces.
479
do {
480
MOZ_ASSERT(!mProcessingInLoop);
481
482
mozilla::AutoRestore<bool> restore(mProcessingInLoop);
483
mProcessingInLoop = true;
484
485
nsresult rv = ProcessNextFilter();
486
if (NS_FAILED(rv)) {
487
return rv;
488
}
489
} while (mFilterCalledBack);
490
491
return NS_OK;
492
}
493
494
nsresult nsAsyncResolveRequest::AsyncApplyFilters::ProcessNextFilter() {
495
LOG(("AsyncApplyFilters::ProcessNextFilter %p ENTER pi=%p", this,
496
mProxyInfo.get()));
497
498
RefPtr<FilterLink> filter;
499
do {
500
mFilterCalledBack = false;
501
502
if (!mRequest) {
503
// We got canceled
504
LOG((" canceled"));
505
return NS_OK; // should we let the consumer know?
506
}
507
508
if (mNextFilterIndex == mFiltersCopy.Length()) {
509
return Finish();
510
}
511
512
filter = mFiltersCopy[mNextFilterIndex++];
513
514
// Loop until a call to a filter succeeded. Other option is to recurse
515
// but that would waste stack trace when a number of filters gets registered
516
// and all from some reason tend to fail.
517
// The !mFilterCalledBack part of the condition is there to protect us from
518
// calling on another filter when the current one managed to call back and
519
// then threw. We already have the result so take it and use it since
520
// the next filter will be processed by the root loop or a call to
521
// ProcessNextFilter has already been dispatched to this thread.
522
LOG((" calling filter %p pi=%p", filter.get(), mProxyInfo.get()));
523
} while (!mRequest->mPPS->ApplyFilter(filter, mRequest->mChannel, mInfo,
524
mProxyInfo, this) &&
525
!mFilterCalledBack);
526
527
LOG(("AsyncApplyFilters::ProcessNextFilter %p LEAVE pi=%p", this,
528
mProxyInfo.get()));
529
return NS_OK;
530
}
531
532
NS_IMETHODIMP
533
nsAsyncResolveRequest::AsyncApplyFilters::OnProxyFilterResult(
534
nsIProxyInfo* aProxyInfo) {
535
LOG(("AsyncApplyFilters::OnProxyFilterResult %p pi=%p", this, aProxyInfo));
536
537
MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
538
MOZ_ASSERT(!mFilterCalledBack);
539
540
if (mFilterCalledBack) {
541
LOG((" duplicate notification?"));
542
return NS_OK;
543
}
544
545
mFilterCalledBack = true;
546
547
if (!mRequest) {
548
// We got canceled
549
LOG((" canceled"));
550
return NS_OK;
551
}
552
553
mProxyInfo = aProxyInfo;
554
555
if (mProcessingInLoop) {
556
// No need to call/dispatch ProcessNextFilter(), we are in a control
557
// loop that will do this for us and save recursion/dispatching.
558
LOG((" in a root loop"));
559
return NS_OK;
560
}
561
562
if (mNextFilterIndex == mFiltersCopy.Length()) {
563
// We are done, all filters have been called on!
564
Finish();
565
return NS_OK;
566
}
567
568
// Redispatch, since we don't want long stacks when filters respond
569
// synchronously.
570
LOG((" redispatching"));
571
NS_DispatchToCurrentThread(this);
572
return NS_OK;
573
}
574
575
NS_IMETHODIMP
576
nsAsyncResolveRequest::AsyncApplyFilters::Run() {
577
LOG(("AsyncApplyFilters::Run %p", this));
578
579
MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
580
581
ProcessNextFilter();
582
return NS_OK;
583
}
584
585
nsresult nsAsyncResolveRequest::AsyncApplyFilters::Finish() {
586
LOG(("AsyncApplyFilters::Finish %p pi=%p", this, mProxyInfo.get()));
587
588
MOZ_ASSERT(mRequest);
589
590
mFiltersCopy.Clear();
591
592
RefPtr<nsAsyncResolveRequest> request;
593
request.swap(mRequest);
594
595
nsCOMPtr<nsIProxyInfo> pi;
596
pi.swap(mProxyInfo);
597
598
request->mPPS->PruneProxyInfo(mInfo, pi);
599
return mCallback(request, pi, !mProcessingInLoop);
600
}
601
602
NS_IMETHODIMP
603
nsAsyncResolveRequest::AsyncApplyFilters::Cancel(nsresult reason) {
604
LOG(("AsyncApplyFilters::Cancel %p", this));
605
606
MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
607
608
// This will be called only from inside the request, so don't call
609
// its's callback. Dropping the members means we simply break the cycle.
610
mFiltersCopy.Clear();
611
mProxyInfo = nullptr;
612
mRequest = nullptr;
613
614
return NS_OK;
615
}
616
617
// Bug 1366133: make GetPACURI off-main-thread since it may hang on Windows
618
// platform
619
class AsyncGetPACURIRequest final : public nsIRunnable {
620
public:
621
NS_DECL_THREADSAFE_ISUPPORTS
622
623
using CallbackFunc = nsresult (nsProtocolProxyService::*)(bool, bool,
624
nsresult,
625
const nsACString&);
626
627
AsyncGetPACURIRequest(nsProtocolProxyService* aService,
628
CallbackFunc aCallback,
629
nsISystemProxySettings* aSystemProxySettings,
630
bool aMainThreadOnly, bool aForceReload,
631
bool aResetPACThread)
632
: mIsMainThreadOnly(aMainThreadOnly),
633
mService(aService),
634
mServiceHolder(do_QueryObject(aService)),
635
mCallback(aCallback),
636
mSystemProxySettings(aSystemProxySettings),
637
mForceReload(aForceReload),
638
mResetPACThread(aResetPACThread) {
639
MOZ_ASSERT(NS_IsMainThread());
640
Unused << mIsMainThreadOnly;
641
}
642
643
NS_IMETHOD Run() override {
644
MOZ_ASSERT(NS_IsMainThread() == mIsMainThreadOnly);
645
646
nsCString pacUri;
647
nsresult rv = mSystemProxySettings->GetPACURI(pacUri);
648
649
nsCOMPtr<nsIRunnable> event =
650
NewNonOwningCancelableRunnableMethod<bool, bool, nsresult, nsCString>(
651
"AsyncGetPACURIRequestCallback", mService, mCallback, mForceReload,
652
mResetPACThread, rv, pacUri);
653
654
return NS_DispatchToMainThread(event);
655
}
656
657
private:
658
~AsyncGetPACURIRequest() {
659
NS_ReleaseOnMainThreadSystemGroup("AsyncGetPACURIRequest::mServiceHolder",
660
mServiceHolder.forget());
661
}
662
663
bool mIsMainThreadOnly;
664
665
nsProtocolProxyService* mService; // ref-count is hold by mServiceHolder
666
nsCOMPtr<nsIProtocolProxyService2> mServiceHolder;
667
CallbackFunc mCallback;
668
nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
669
670
bool mForceReload;
671
bool mResetPACThread;
672
};
673
674
NS_IMPL_ISUPPORTS(AsyncGetPACURIRequest, nsIRunnable)
675
676
//----------------------------------------------------------------------------
677
678
//
679
// apply mask to address (zeros out excluded bits).
680
//
681
// NOTE: we do the byte swapping here to minimize overall swapping.
682
//
683
static void proxy_MaskIPv6Addr(PRIPv6Addr& addr, uint16_t mask_len) {
684
if (mask_len == 128) return;
685
686
if (mask_len > 96) {
687
addr.pr_s6_addr32[3] =
688
PR_htonl(PR_ntohl(addr.pr_s6_addr32[3]) & (~0uL << (128 - mask_len)));
689
} else if (mask_len > 64) {
690
addr.pr_s6_addr32[3] = 0;
691
addr.pr_s6_addr32[2] =
692
PR_htonl(PR_ntohl(addr.pr_s6_addr32[2]) & (~0uL << (96 - mask_len)));
693
} else if (mask_len > 32) {
694
addr.pr_s6_addr32[3] = 0;
695
addr.pr_s6_addr32[2] = 0;
696
addr.pr_s6_addr32[1] =
697
PR_htonl(PR_ntohl(addr.pr_s6_addr32[1]) & (~0uL << (64 - mask_len)));
698
} else {
699
addr.pr_s6_addr32[3] = 0;
700
addr.pr_s6_addr32[2] = 0;
701
addr.pr_s6_addr32[1] = 0;
702
addr.pr_s6_addr32[0] =
703
PR_htonl(PR_ntohl(addr.pr_s6_addr32[0]) & (~0uL << (32 - mask_len)));
704
}
705
}
706
707
static void proxy_GetStringPref(nsIPrefBranch* aPrefBranch, const char* aPref,
708
nsCString& aResult) {
709
nsAutoCString temp;
710
nsresult rv = aPrefBranch->GetCharPref(aPref, temp);
711
if (NS_FAILED(rv))
712
aResult.Truncate();
713
else {
714
aResult.Assign(temp);
715
// all of our string prefs are hostnames, so we should remove any
716
// whitespace characters that the user might have unknowingly entered.
717
aResult.StripWhitespace();
718
}
719
}
720
721
static void proxy_GetIntPref(nsIPrefBranch* aPrefBranch, const char* aPref,
722
int32_t& aResult) {
723
int32_t temp;
724
nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
725
if (NS_FAILED(rv))
726
aResult = -1;
727
else
728
aResult = temp;
729
}
730
731
static void proxy_GetBoolPref(nsIPrefBranch* aPrefBranch, const char* aPref,
732
bool& aResult) {
733
bool temp;
734
nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
735
if (NS_FAILED(rv))
736
aResult = false;
737
else
738
aResult = temp;
739
}
740
741
//----------------------------------------------------------------------------
742
743
static const int32_t PROXYCONFIG_DIRECT4X = 3;
744
static const int32_t PROXYCONFIG_COUNT = 6;
745
746
NS_IMPL_ADDREF(nsProtocolProxyService)
747
NS_IMPL_RELEASE(nsProtocolProxyService)
748
NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
749
NS_PROTOCOLPROXYSERVICE_CID)
750
751
// NS_IMPL_QUERY_INTERFACE_CI with the nsProtocolProxyService QI change
752
NS_INTERFACE_MAP_BEGIN(nsProtocolProxyService)
753
NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService)
754
NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService2)
755
NS_INTERFACE_MAP_ENTRY(nsIObserver)
756
NS_INTERFACE_MAP_ENTRY_CONCRETE(nsProtocolProxyService)
757
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolProxyService)
758
NS_IMPL_QUERY_CLASSINFO(nsProtocolProxyService)
759
NS_INTERFACE_MAP_END
760
761
NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService, nsIProtocolProxyService,
762
nsIProtocolProxyService2)
763
764
nsProtocolProxyService::nsProtocolProxyService()
765
: mFilterLocalHosts(false),
766
mProxyConfig(PROXYCONFIG_DIRECT),
767
mHTTPProxyPort(-1),
768
mFTPProxyPort(-1),
769
mHTTPSProxyPort(-1),
770
mSOCKSProxyPort(-1),
771
mSOCKSProxyVersion(4),
772
mSOCKSProxyRemoteDNS(false),
773
mProxyOverTLS(true),
774
mWPADOverDHCPEnabled(false),
775
mAllowHijackingLocalhost(false),
776
mPACMan(nullptr),
777
mSessionStart(PR_Now()),
778
mFailedProxyTimeout(30 * 60) // 30 minute default
779
,
780
mIsShutdown(false) {}
781
782
nsProtocolProxyService::~nsProtocolProxyService() {
783
// These should have been cleaned up in our Observe method.
784
NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters.Length() == 0 &&
785
mPACMan == nullptr,
786
"what happened to xpcom-shutdown?");
787
}
788
789
// nsProtocolProxyService methods
790
nsresult nsProtocolProxyService::Init() {
791
mProxySettingTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
792
793
// failure to access prefs is non-fatal
794
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
795
if (prefBranch) {
796
// monitor proxy prefs
797
prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
798
799
// read all prefs
800
PrefsChanged(prefBranch, nullptr);
801
}
802
803
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
804
if (obs) {
805
// register for shutdown notification so we can clean ourselves up
806
// properly.
807
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
808
obs->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
809
}
810
811
return NS_OK;
812
}
813
814
// ReloadNetworkPAC() checks if there's a non-networked PAC in use then avoids
815
// to call ReloadPAC()
816
nsresult nsProtocolProxyService::ReloadNetworkPAC() {
817
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
818
if (!prefs) {
819
return NS_OK;
820
}
821
822
int32_t type;
823
nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
824
if (NS_FAILED(rv)) {
825
return NS_OK;
826
}
827
828
if (type == PROXYCONFIG_PAC) {
829
nsAutoCString pacSpec;
830
prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
831
if (!pacSpec.IsEmpty()) {
832
nsCOMPtr<nsIURI> pacURI;
833
rv = NS_NewURI(getter_AddRefs(pacURI), pacSpec);
834
if (!NS_SUCCEEDED(rv)) {
835
return rv;
836
}
837
838
nsProtocolInfo pac;
839
rv = GetProtocolInfo(pacURI, &pac);
840
if (!NS_SUCCEEDED(rv)) {
841
return rv;
842
}
843
844
if (!pac.scheme.EqualsLiteral("file") &&
845
!pac.scheme.EqualsLiteral("data")) {
846
LOG((": received network changed event, reload PAC"));
847
ReloadPAC();
848
}
849
}
850
} else if ((type == PROXYCONFIG_WPAD) || (type == PROXYCONFIG_SYSTEM)) {
851
ReloadPAC();
852
}
853
854
return NS_OK;
855
}
856
857
nsresult nsProtocolProxyService::AsyncConfigureFromPAC(bool aForceReload,
858
bool aResetPACThread) {
859
MOZ_ASSERT(NS_IsMainThread());
860
861
bool mainThreadOnly;
862
nsresult rv = mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly);
863
if (NS_WARN_IF(NS_FAILED(rv))) {
864
return rv;
865
}
866
867
nsCOMPtr<nsIRunnable> req = new AsyncGetPACURIRequest(
868
this, &nsProtocolProxyService::OnAsyncGetPACURI, mSystemProxySettings,
869
mainThreadOnly, aForceReload, aResetPACThread);
870
871
if (mainThreadOnly) {
872
return req->Run();
873
}
874
875
if (NS_WARN_IF(!mProxySettingTarget)) {
876
return NS_ERROR_NOT_INITIALIZED;
877
}
878
return mProxySettingTarget->Dispatch(req, nsIEventTarget::DISPATCH_NORMAL);
879
}
880
881
nsresult nsProtocolProxyService::OnAsyncGetPACURI(bool aForceReload,
882
bool aResetPACThread,
883
nsresult aResult,
884
const nsACString& aUri) {
885
MOZ_ASSERT(NS_IsMainThread());
886
887
if (aResetPACThread) {
888
ResetPACThread();
889
}
890
891
if (NS_SUCCEEDED(aResult) && !aUri.IsEmpty()) {
892
ConfigureFromPAC(PromiseFlatCString(aUri), aForceReload);
893
}
894
895
return NS_OK;
896
}
897
898
NS_IMETHODIMP
899
nsProtocolProxyService::Observe(nsISupports* aSubject, const char* aTopic,
900
const char16_t* aData) {
901
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
902
mIsShutdown = true;
903
// cleanup
904
mHostFiltersArray.Clear();
905
mFilters.Clear();
906
907
if (mPACMan) {
908
mPACMan->Shutdown();
909
mPACMan = nullptr;
910
}
911
912
if (mProxySettingTarget) {
913
mProxySettingTarget = nullptr;
914
}
915
916
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
917
if (obs) {
918
obs->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
919
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
920
}
921
922
} else if (strcmp(aTopic, NS_NETWORK_LINK_TOPIC) == 0) {
923
nsCString converted = NS_ConvertUTF16toUTF8(aData);
924
const char* state = converted.get();
925
if (!strcmp(state, NS_NETWORK_LINK_DATA_CHANGED)) {
926
ReloadNetworkPAC();
927
}
928
} else {
929
NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
930
"what is this random observer event?");
931
nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
932
if (prefs) PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
933
}
934
return NS_OK;
935
}
936
937
void nsProtocolProxyService::PrefsChanged(nsIPrefBranch* prefBranch,
938
const char* pref) {
939
nsresult rv = NS_OK;
940
bool reloadPAC = false;
941
nsAutoCString tempString;
942
943
if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
944
int32_t type = -1;
945
rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
946
if (NS_SUCCEEDED(rv)) {
947
// bug 115720 - for ns4.x backwards compatibility
948
if (type == PROXYCONFIG_DIRECT4X) {
949
type = PROXYCONFIG_DIRECT;
950
// Reset the type so that the dialog looks correct, and we
951
// don't have to handle this case everywhere else
952
// I'm paranoid about a loop of some sort - only do this
953
// if we're enumerating all prefs, and ignore any error
954
if (!pref) prefBranch->SetIntPref(PROXY_PREF("type"), type);
955
} else if (type >= PROXYCONFIG_COUNT) {
956
LOG(("unknown proxy type: %" PRId32 "; assuming direct\n", type));
957
type = PROXYCONFIG_DIRECT;
958
}
959
mProxyConfig = type;
960
reloadPAC = true;
961
}
962
963
if (mProxyConfig == PROXYCONFIG_SYSTEM) {
964
mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
965
if (!mSystemProxySettings) mProxyConfig = PROXYCONFIG_DIRECT;
966
ResetPACThread();
967
} else {
968
if (mSystemProxySettings) {
969
mSystemProxySettings = nullptr;
970
ResetPACThread();
971
}
972
}
973
}
974
975
if (!pref || !strcmp(pref, PROXY_PREF("http")))
976
proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
977
978
if (!pref || !strcmp(pref, PROXY_PREF("http_port")))
979
proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
980
981
if (!pref || !strcmp(pref, PROXY_PREF("ssl")))
982
proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
983
984
if (!pref || !strcmp(pref, PROXY_PREF("ssl_port")))
985
proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
986
987
if (!pref || !strcmp(pref, PROXY_PREF("ftp")))
988
proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost);
989
990
if (!pref || !strcmp(pref, PROXY_PREF("ftp_port")))
991
proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
992
993
if (!pref || !strcmp(pref, PROXY_PREF("socks")))
994
proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyTarget);
995
996
if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
997
proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
998
999
if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
1000
int32_t version;
1001
proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
1002
// make sure this preference value remains sane
1003
if (version == 5)
1004
mSOCKSProxyVersion = 5;
1005
else
1006
mSOCKSProxyVersion = 4;
1007
}
1008
1009
if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns")))
1010
proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
1011
mSOCKSProxyRemoteDNS);
1012
1013
if (!pref || !strcmp(pref, PROXY_PREF("proxy_over_tls"))) {
1014
proxy_GetBoolPref(prefBranch, PROXY_PREF("proxy_over_tls"), mProxyOverTLS);
1015
}
1016
1017
if (!pref || !strcmp(pref, PROXY_PREF("enable_wpad_over_dhcp"))) {
1018
proxy_GetBoolPref(prefBranch, PROXY_PREF("enable_wpad_over_dhcp"),
1019
mWPADOverDHCPEnabled);
1020
reloadPAC = reloadPAC || mProxyConfig == PROXYCONFIG_WPAD;
1021
}
1022
1023
if (!pref || !strcmp(pref, PROXY_PREF("allow_hijacking_localhost"))) {
1024
proxy_GetBoolPref(prefBranch, PROXY_PREF("allow_hijacking_localhost"),
1025
mAllowHijackingLocalhost);
1026
}
1027
1028
if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
1029
proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
1030
mFailedProxyTimeout);
1031
1032
if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
1033
rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"), tempString);
1034
if (NS_SUCCEEDED(rv)) LoadHostFilters(tempString);
1035
}
1036
1037
// We're done if not using something that could give us a PAC URL
1038
// (PAC, WPAD or System)
1039
if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
1040
mProxyConfig != PROXYCONFIG_SYSTEM)
1041
return;
1042
1043
// OK, we need to reload the PAC file if:
1044
// 1) network.proxy.type changed, or
1045
// 2) network.proxy.autoconfig_url changed and PAC is configured
1046
1047
if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url"))) reloadPAC = true;
1048
1049
if (reloadPAC) {
1050
tempString.Truncate();
1051
if (mProxyConfig == PROXYCONFIG_PAC) {
1052
prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"), tempString);
1053
if (mPACMan && !mPACMan->IsPACURI(tempString)) {
1054
LOG(("PAC Thread URI Changed - Reset Pac Thread"));
1055
ResetPACThread();
1056
}
1057
} else if (mProxyConfig == PROXYCONFIG_WPAD) {
1058
LOG(("Auto-detecting proxy - Reset Pac Thread"));
1059
ResetPACThread();
1060
} else if (mSystemProxySettings) {
1061
// Get System Proxy settings if available
1062
AsyncConfigureFromPAC(false, false);
1063
}
1064
if (!tempString.IsEmpty() || mProxyConfig == PROXYCONFIG_WPAD) {
1065
ConfigureFromPAC(tempString, false);
1066
}
1067
}
1068
}
1069
1070
bool nsProtocolProxyService::CanUseProxy(nsIURI* aURI, int32_t defaultPort) {
1071
int32_t port;
1072
nsAutoCString host;
1073
1074
nsresult rv = aURI->GetAsciiHost(host);
1075
if (NS_FAILED(rv) || host.IsEmpty()) return false;
1076
1077
rv = aURI->GetPort(&port);
1078
if (NS_FAILED(rv)) return false;
1079
if (port == -1) port = defaultPort;
1080
1081
PRNetAddr addr;
1082
bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
1083
1084
PRIPv6Addr ipv6;
1085
if (is_ipaddr) {
1086
// convert parsed address to IPv6
1087
if (addr.raw.family == PR_AF_INET) {
1088
// convert to IPv4-mapped address
1089
PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
1090
} else if (addr.raw.family == PR_AF_INET6) {
1091
// copy the address
1092
memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1093
} else {
1094
NS_WARNING("unknown address family");
1095
return true; // allow proxying
1096
}
1097
}
1098
1099
// Don't use proxy for local hosts (plain hostname, no dots)
1100
if ((!is_ipaddr && mFilterLocalHosts && !host.Contains('.')) ||
1101
(!mAllowHijackingLocalhost &&
1102
(host.EqualsLiteral("127.0.0.1") || host.EqualsLiteral("::1") ||
1103
host.EqualsLiteral("localhost")))) {
1104
LOG(("Not using proxy for this local host [%s]!\n", host.get()));
1105
return false; // don't allow proxying
1106
}
1107
1108
int32_t index = -1;
1109
while (++index < int32_t(mHostFiltersArray.Length())) {
1110
HostInfo* hinfo = mHostFiltersArray[index];
1111
1112
if (is_ipaddr != hinfo->is_ipaddr) continue;
1113
if (hinfo->port && hinfo->port != port) continue;
1114
1115
if (is_ipaddr) {
1116
// generate masked version of target IPv6 address
1117
PRIPv6Addr masked;
1118
memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
1119
proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
1120
1121
// check for a match
1122
if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0)
1123
return false; // proxy disallowed
1124
} else {
1125
uint32_t host_len = host.Length();
1126
uint32_t filter_host_len = hinfo->name.host_len;
1127
1128
if (host_len >= filter_host_len) {
1129
//
1130
// compare last |filter_host_len| bytes of target hostname.
1131
//
1132
const char* host_tail = host.get() + host_len - filter_host_len;
1133
if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len)) {
1134
// If the tail of the host string matches the filter
1135
1136
if (filter_host_len > 0 && hinfo->name.host[0] == '.') {
1137
// If the filter was of the form .foo.bar.tld, all such
1138
// matches are correct
1139
return false; // proxy disallowed
1140
}
1141
1142
// abc-def.example.org should not match def.example.org
1143
// however, *.def.example.org should match .def.example.org
1144
// We check that the filter doesn't start with a `.`. If it does,
1145
// then the strncasecmp above should suffice. If it doesn't,
1146
// then we should only consider it a match if the strncasecmp happened
1147
// at a subdomain boundary
1148
if (host_len > filter_host_len && *(host_tail - 1) == '.') {
1149
// If the host was something.foo.bar.tld and the filter
1150
// was foo.bar.tld, it's still a match.
1151
// the character right before the tail must be a
1152
// `.` for this to work
1153
return false; // proxy disallowed
1154
}
1155
1156
if (host_len == filter_host_len) {
1157
// If the host and filter are of the same length,
1158
// they should match
1159
return false; // proxy disallowed
1160
}
1161
}
1162
}
1163
}
1164
}
1165
return true;
1166
}
1167
1168
// kProxyType\* may be referred to externally in
1169
// nsProxyInfo in order to compare by string pointer
1170
const char kProxyType_HTTP[] = "http";
1171
const char kProxyType_HTTPS[] = "https";
1172
const char kProxyType_PROXY[] = "proxy";
1173
const char kProxyType_SOCKS[] = "socks";
1174
const char kProxyType_SOCKS4[] = "socks4";
1175
const char kProxyType_SOCKS5[] = "socks5";
1176
const char kProxyType_DIRECT[] = "direct";
1177
1178
const char* nsProtocolProxyService::ExtractProxyInfo(const char* start,
1179
uint32_t aResolveFlags,
1180
nsProxyInfo** result) {
1181
*result = nullptr;
1182
uint32_t flags = 0;
1183
1184
// see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
1185
1186
// find end of proxy info delimiter
1187
const char* end = start;
1188
while (*end && *end != ';') ++end;
1189
1190
// find end of proxy type delimiter
1191
const char* sp = start;
1192
while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
1193
1194
uint32_t len = sp - start;
1195
const char* type = nullptr;
1196
switch (len) {
1197
case 4:
1198
if (PL_strncasecmp(start, kProxyType_HTTP, 5) == 0) {
1199
type = kProxyType_HTTP;
1200
}
1201
break;
1202
case 5:
1203
if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0) {
1204
type = kProxyType_HTTP;
1205
} else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0) {
1206
type = kProxyType_SOCKS4; // assume v4 for 4x compat
1207
} else if (PL_strncasecmp(start, kProxyType_HTTPS, 5) == 0) {
1208
type = kProxyType_HTTPS;
1209
}
1210
break;
1211
case 6:
1212
if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0)
1213
type = kProxyType_DIRECT;
1214
else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0)
1215
type = kProxyType_SOCKS4;
1216
else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0)
1217
// map "SOCKS5" to "socks" to match contract-id of registered
1218
// SOCKS-v5 socket provider.
1219
type = kProxyType_SOCKS;
1220
break;
1221
}
1222
if (type) {
1223
int32_t port = -1;
1224
1225
// If it's a SOCKS5 proxy, do name resolution on the server side.
1226
// We could use this with SOCKS4a servers too, but they might not
1227
// support it.
1228
if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS)
1229
flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1230
1231
// extract host:port
1232
start = sp;
1233
while ((*start == ' ' || *start == '\t') && start < end) start++;
1234
1235
// port defaults
1236
if (type == kProxyType_HTTP) {
1237
port = 80;
1238
} else if (type == kProxyType_HTTPS) {
1239
port = 443;
1240
} else {
1241
port = 1080;
1242
}
1243
1244
nsProxyInfo* pi = new nsProxyInfo();
1245
pi->mType = type;
1246
pi->mFlags = flags;
1247
pi->mResolveFlags = aResolveFlags;
1248
pi->mTimeout = mFailedProxyTimeout;
1249
1250
// www.foo.com:8080 and http://www.foo.com:8080
1251
nsDependentCSubstring maybeURL(start, end - start);
1252
nsCOMPtr<nsIURI> pacURI;
1253
1254
nsAutoCString urlHost;
1255
// First assume the scheme is present, e.g. http://www.example.com:8080
1256
if (NS_FAILED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) ||
1257
NS_FAILED(pacURI->GetAsciiHost(urlHost)) || urlHost.IsEmpty()) {
1258
// It isn't, assume www.example.com:8080
1259
maybeURL.Insert("http://", 0);
1260
1261
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL))) {
1262
pacURI->GetAsciiHost(urlHost);
1263
}
1264
}
1265
1266
if (!urlHost.IsEmpty()) {
1267
pi->mHost = urlHost;
1268
1269
int32_t tPort;
1270
if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
1271
port = tPort;
1272
}
1273
pi->mPort = port;
1274
}
1275
1276
NS_ADDREF(*result = pi);
1277
}
1278
1279
while (*end == ';' || *end == ' ' || *end == '\t') ++end;
1280
return end;
1281
}
1282
1283
void nsProtocolProxyService::GetProxyKey(nsProxyInfo* pi, nsCString& key) {
1284
key.AssignASCII(pi->mType);
1285
if (!pi->mHost.IsEmpty()) {
1286
key.Append(' ');
1287
key.Append(pi->mHost);
1288
key.Append(':');
1289
key.AppendInt(pi->mPort);
1290
}
1291
}
1292
1293
uint32_t nsProtocolProxyService::SecondsSinceSessionStart() {
1294
PRTime now = PR_Now();
1295
1296
// get time elapsed since session start
1297
int64_t diff = now - mSessionStart;
1298
1299
// convert microseconds to seconds
1300
diff /= PR_USEC_PER_SEC;
1301
1302
// return converted 32 bit value
1303
return uint32_t(diff);
1304
}
1305
1306
void nsProtocolProxyService::EnableProxy(nsProxyInfo* pi) {
1307
nsAutoCString key;
1308
GetProxyKey(pi, key);
1309
mFailedProxies.Remove(key);
1310
}
1311
1312
void nsProtocolProxyService::DisableProxy(nsProxyInfo* pi) {
1313
nsAutoCString key;
1314
GetProxyKey(pi, key);
1315
1316
uint32_t dsec = SecondsSinceSessionStart();
1317
1318
// Add timeout to interval (this is the time when the proxy can
1319
// be tried again).
1320
dsec += pi->mTimeout;
1321
1322
// NOTE: The classic codebase would increase the timeout value
1323
// incrementally each time a subsequent failure occurred.
1324
// We could do the same, but it would require that we not
1325
// remove proxy entries in IsProxyDisabled or otherwise
1326
// change the way we are recording disabled proxies.
1327
// Simpler is probably better for now, and at least the
1328
// user can tune the timeout setting via preferences.
1329
1330
LOG(("DisableProxy %s %d\n", key.get(), dsec));
1331
1332
// If this fails, oh well... means we don't have enough memory
1333
// to remember the failed proxy.
1334
mFailedProxies.Put(key, dsec);
1335
}
1336
1337
bool nsProtocolProxyService::IsProxyDisabled(nsProxyInfo* pi) {
1338
nsAutoCString key;
1339
GetProxyKey(pi, key);
1340
1341
uint32_t val;
1342
if (!mFailedProxies.Get(key, &val)) return false;
1343
1344
uint32_t dsec = SecondsSinceSessionStart();
1345
1346
// if time passed has exceeded interval, then try proxy again.
1347
if (dsec > val) {
1348
mFailedProxies.Remove(key);
1349
return false;
1350
}
1351
1352
return true;
1353
}
1354
1355
nsresult nsProtocolProxyService::SetupPACThread(
1356
nsIEventTarget* mainThreadEventTarget) {
1357
if (mIsShutdown) {
1358
return NS_ERROR_FAILURE;
1359
}
1360
1361
if (mPACMan) return NS_OK;
1362
1363
mPACMan = new nsPACMan(mainThreadEventTarget);
1364
1365
bool mainThreadOnly;
1366
nsresult rv;
1367
if (mSystemProxySettings &&
1368
NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
1369
!mainThreadOnly) {
1370
rv = mPACMan->Init(mSystemProxySettings);
1371
} else {
1372
rv = mPACMan->Init(nullptr);
1373
}
1374
if (NS_FAILED(rv)) {
1375
mPACMan->Shutdown();
1376
mPACMan = nullptr;
1377
}
1378
return rv;
1379
}
1380
1381
nsresult nsProtocolProxyService::ResetPACThread() {
1382
if (!mPACMan) return NS_OK;
1383
1384
mPACMan->Shutdown();
1385
mPACMan = nullptr;
1386
return SetupPACThread();
1387
}
1388
1389
nsresult nsProtocolProxyService::ConfigureFromPAC(const nsCString& spec,
1390
bool forceReload) {
1391
nsresult rv = SetupPACThread();
1392
NS_ENSURE_SUCCESS(rv, rv);
1393
1394
bool autodetect = spec.IsEmpty();
1395
if (!forceReload && ((!autodetect && mPACMan->IsPACURI(spec)) ||
1396
(autodetect && mPACMan->IsUsingWPAD()))) {
1397
return NS_OK;
1398
}
1399
1400
mFailedProxies.Clear();
1401
1402
mPACMan->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled);
1403
return mPACMan->LoadPACFromURI(spec);
1404
}
1405
1406
void nsProtocolProxyService::ProcessPACString(const nsCString& pacString,
1407
uint32_t aResolveFlags,
1408
nsIProxyInfo** result) {
1409
if (pacString.IsEmpty()) {
1410
*result = nullptr;
1411
return;
1412
}
1413
1414
const char* proxies = pacString.get();
1415
1416
nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
1417
while (*proxies) {
1418
proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
1419
if (pi && (pi->mType == kProxyType_HTTPS) && !mProxyOverTLS) {
1420
delete pi;
1421
pi = nullptr;
1422
}
1423
1424
if (pi) {
1425
if (last) {
1426
NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
1427
last->mNext = pi;
1428
} else
1429
first = pi;
1430
last = pi;
1431
}
1432
}
1433
*result = first;
1434
}
1435
1436
// nsIProtocolProxyService2
1437
NS_IMETHODIMP
1438
nsProtocolProxyService::ReloadPAC() {
1439
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
1440
if (!prefs) return NS_OK;
1441
1442
int32_t type;
1443
nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
1444
if (NS_FAILED(rv)) return NS_OK;
1445
1446
nsAutoCString pacSpec;
1447
if (type == PROXYCONFIG_PAC)
1448
prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
1449
else if (type == PROXYCONFIG_SYSTEM) {
1450
if (mSystemProxySettings) {
1451
AsyncConfigureFromPAC(true, true);
1452
} else {
1453
ResetPACThread();
1454
}
1455
}
1456
1457
if (!pacSpec.IsEmpty() || type == PROXYCONFIG_WPAD)
1458
ConfigureFromPAC(pacSpec, true);
1459
return NS_OK;
1460
}
1461
1462
// When sync interface is removed this can go away too
1463
// The nsPACManCallback portion of this implementation should be run
1464
// off the main thread, because it uses a condvar for signaling and
1465
// the main thread is blocking on that condvar -
1466
// so call nsPACMan::AsyncGetProxyForURI() with
1467
// a false mainThreadResponse parameter.
1468
class nsAsyncBridgeRequest final : public nsPACManCallback {
1469
NS_DECL_THREADSAFE_ISUPPORTS
1470
1471
nsAsyncBridgeRequest()
1472
: mMutex("nsDeprecatedCallback"),
1473
mCondVar(mMutex, "nsDeprecatedCallback"),
1474
mStatus(NS_OK),
1475
mCompleted(false) {}
1476
1477
void OnQueryComplete(nsresult status, const nsACString& pacString,
1478
const nsACString& newPACURL) override {
1479
MutexAutoLock lock(mMutex);
1480
mCompleted = true;
1481
mStatus = status;
1482
mPACString = pacString;
1483
mPACURL = newPACURL;
1484
mCondVar.Notify();
1485
}
1486
1487
void Lock() { mMutex.Lock(); }
1488
void Unlock() { mMutex.Unlock(); }
1489
void Wait() { mCondVar.Wait(TimeDuration::FromSeconds(3)); }
1490
1491
private:
1492
~nsAsyncBridgeRequest() = default;
1493
1494
friend class nsProtocolProxyService;
1495
1496
Mutex mMutex;
1497
CondVar mCondVar;
1498
1499
nsresult mStatus;
1500
nsCString mPACString;
1501
nsCString mPACURL;
1502
bool mCompleted;
1503
};
1504
NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
1505
1506
nsresult nsProtocolProxyService::AsyncResolveInternal(
1507
nsIChannel* channel, uint32_t flags, nsIProtocolProxyCallback* callback,
1508
nsICancelable** result, bool isSyncOK,
1509
nsIEventTarget* mainThreadEventTarget) {
1510
NS_ENSURE_ARG_POINTER(channel);
1511
NS_ENSURE_ARG_POINTER(callback);
1512
1513
nsCOMPtr<nsIURI> uri;
1514
nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
1515
if (NS_FAILED(rv)) return rv;
1516
1517
*result = nullptr;
1518
RefPtr<nsAsyncResolveRequest> ctx =
1519
new nsAsyncResolveRequest(this, channel, flags, callback);
1520
1521
nsProtocolInfo info;
1522
rv = GetProtocolInfo(uri, &info);
1523
if (NS_FAILED(rv)) return rv;
1524
1525
nsCOMPtr<nsIProxyInfo> pi;
1526
bool usePACThread;
1527
1528
// adapt to realtime changes in the system proxy service
1529
if (mProxyConfig == PROXYCONFIG_SYSTEM) {
1530
nsCOMPtr<nsISystemProxySettings> sp2 =
1531
do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
1532
if (sp2 != mSystemProxySettings) {
1533
mSystemProxySettings = sp2;
1534
ResetPACThread();
1535
}
1536
}
1537
1538
rv = SetupPACThread(mainThreadEventTarget);
1539
if (NS_FAILED(rv)) {
1540
return rv;
1541
}
1542
1543
// SystemProxySettings and PAC files can block the main thread
1544
// but if neither of them are in use, we can just do the work
1545
// right here and directly invoke the callback
1546
1547
rv =
1548
Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
1549
if (NS_FAILED(rv)) return rv;
1550
1551
if (!usePACThread || !mPACMan) {
1552
// we can do it locally
1553
rv = ctx->ProcessLocally(info, pi, isSyncOK);
1554
if (NS_SUCCEEDED(rv) && !isSyncOK) {
1555
ctx.forget(result);
1556
}
1557
return rv;
1558
}
1559
1560
// else kick off a PAC thread query
1561
1562
rv = mPACMan->AsyncGetProxyForURI(uri, ctx, true);
1563
if (NS_SUCCEEDED(rv)) ctx.forget(result);
1564
return rv;
1565
}
1566
1567
// nsIProtocolProxyService
1568
NS_IMETHODIMP
1569
nsProtocolProxyService::AsyncResolve2(nsIChannel* channel, uint32_t flags,
1570
nsIProtocolProxyCallback* callback,
1571
nsIEventTarget* mainThreadEventTarget,
1572
nsICancelable** result) {
1573
return AsyncResolveInternal(channel, flags, callback, result, true,
1574
mainThreadEventTarget);
1575
}
1576
1577
NS_IMETHODIMP
1578
nsProtocolProxyService::AsyncResolve(nsISupports* channelOrURI, uint32_t flags,
1579
nsIProtocolProxyCallback* callback,
1580
nsIEventTarget* mainThreadEventTarget,
1581
nsICancelable** result) {
1582
nsresult rv;
1583
// Check if we got a channel:
1584
nsCOMPtr<nsIChannel> channel = do_QueryInterface(channelOrURI);
1585
if (!channel) {
1586
nsCOMPtr<nsIURI> uri = do_QueryInterface(channelOrURI);
1587
if (!uri) {
1588
return NS_ERROR_NO_INTERFACE;
1589
}
1590
1591
// creating a temporary channel from the URI which is not
1592
// used to perform any network loads, hence its safe to
1593
// use systemPrincipal as the loadingPrincipal.
1594
rv = NS_NewChannel(getter_AddRefs(channel), uri,
1595
nsContentUtils::GetSystemPrincipal(),
1596
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1597
nsIContentPolicy::TYPE_OTHER);
1598
NS_ENSURE_SUCCESS(rv, rv);
1599
}
1600
1601
return AsyncResolveInternal(channel, flags, callback, result, false,
1602
mainThreadEventTarget);
1603
}
1604
1605
NS_IMETHODIMP
1606
nsProtocolProxyService::NewProxyInfo(
1607
const nsACString& aType, const nsACString& aHost, int32_t aPort,
1608
const nsACString& aProxyAuthorizationHeader,
1609
const nsACString& aConnectionIsolationKey, uint32_t aFlags,
1610
uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
1611
nsIProxyInfo** aResult) {
1612
return NewProxyInfoWithAuth(aType, aHost, aPort, EmptyCString(),
1613
EmptyCString(), aProxyAuthorizationHeader,
1614
aConnectionIsolationKey, aFlags, aFailoverTimeout,
1615
aFailoverProxy, aResult);
1616
}
1617
1618
NS_IMETHODIMP
1619
nsProtocolProxyService::NewProxyInfoWithAuth(
1620
const nsACString& aType, const nsACString& aHost, int32_t aPort,
1621
const nsACString& aUsername, const nsACString& aPassword,
1622
const nsACString& aProxyAuthorizationHeader,
1623
const nsACString& aConnectionIsolationKey, uint32_t aFlags,
1624
uint32_t aFailoverTimeout, nsIProxyInfo* aFailoverProxy,
1625
nsIProxyInfo** aResult) {
1626
static const char* types[] = {kProxyType_HTTP, kProxyType_HTTPS,
1627
kProxyType_SOCKS, kProxyType_SOCKS4,
1628
kProxyType_DIRECT};
1629
1630
// resolve type; this allows us to avoid copying the type string into each
1631
// proxy info instance. we just reference the string literals directly :)
1632
const char* type = nullptr;
1633
for (auto& t : types) {
1634
if (aType.LowerCaseEqualsASCII(t)) {
1635
type = t;
1636
break;
1637
}
1638
}
1639
NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
1640
1641
// We have only implemented username/password for SOCKS proxies.
1642
if ((!aUsername.IsEmpty() || !aPassword.IsEmpty()) &&
1643
!aType.LowerCaseEqualsASCII(kProxyType_SOCKS) &&
1644
!aType.LowerCaseEqualsASCII(kProxyType_SOCKS4)) {
1645
return NS_ERROR_NOT_IMPLEMENTED;
1646
}
1647
1648
return NewProxyInfo_Internal(type, aHost, aPort, aUsername, aPassword,
1649
aProxyAuthorizationHeader,
1650
aConnectionIsolationKey, aFlags,
1651
aFailoverTimeout, aFailoverProxy, 0, aResult);
1652
}
1653
1654
NS_IMETHODIMP
1655
nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo* aProxy, nsIURI* aURI,
1656
nsresult aStatus,
1657
nsIProxyInfo** aResult) {
1658
// We only support failover when a PAC file is configured, either
1659
// directly or via system settings
1660
if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
1661
mProxyConfig != PROXYCONFIG_SYSTEM)
1662
return NS_ERROR_NOT_AVAILABLE;
1663
1664
// Verify that |aProxy| is one of our nsProxyInfo objects.
1665
nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
1666
NS_ENSURE_ARG(pi);
1667
// OK, the QI checked out. We can proceed.
1668
1669
// Remember that this proxy is down.
1670
DisableProxy(pi);
1671
1672
// NOTE: At this point, we might want to prompt the user if we have
1673
// not already tried going DIRECT. This is something that the
1674
// classic codebase supported; however, IE6 does not prompt.
1675
1676
if (!pi->mNext) return NS_ERROR_NOT_AVAILABLE;
1677
1678
LOG(("PAC failover from %s %s:%d to %s %s:%d\n", pi->mType, pi->mHost.get(),
1679
pi->mPort, pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
1680
1681
NS_ADDREF(*aResult = pi->mNext);
1682
return NS_OK;
1683
}
1684
1685
namespace { // anon
1686
1687
class ProxyFilterPositionComparator {
1688
typedef RefPtr<nsProtocolProxyService::FilterLink> FilterLinkRef;
1689
1690
public:
1691
bool Equals(const FilterLinkRef& a, const FilterLinkRef& b) const {
1692
return a->position == b->position;
1693
}
1694
bool LessThan(const FilterLinkRef& a, const FilterLinkRef& b) const {
1695
return a->position < b->position;