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