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