1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set ts=4 sw=4 sts=4 et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/Attributes.h"
9
10 #include "nsProtocolProxyService.h"
11 #include "nsProxyInfo.h"
12 #include "nsIClassInfoImpl.h"
13 #include "nsIIOService.h"
14 #include "nsIObserverService.h"
15 #include "nsIProtocolHandler.h"
16 #include "nsIProtocolProxyCallback.h"
17 #include "nsIChannel.h"
18 #include "nsICancelable.h"
19 #include "nsIDNSService.h"
20 #include "nsPIDNSService.h"
21 #include "nsIScriptSecurityManager.h"
22 #include "nsIPrefService.h"
23 #include "nsIPrefBranch.h"
24 #include "nsThreadUtils.h"
25 #include "nsSOCKSIOLayer.h"
26 #include "nsString.h"
27 #include "nsNetUtil.h"
28 #include "nsNetCID.h"
29 #include "plstr.h"
30 #include "prnetdb.h"
31 #include "nsPACMan.h"
32 #include "nsProxyRelease.h"
33 #include "mozilla/Mutex.h"
34 #include "mozilla/CondVar.h"
35 #include "nsISystemProxySettings.h"
36 #include "nsINetworkLinkService.h"
37 #include "nsIHttpChannelInternal.h"
38 #include "mozilla/Logging.h"
39 #include "mozilla/Tokenizer.h"
40
41 //----------------------------------------------------------------------------
42
43 namespace mozilla {
44 namespace net {
45
46 extern const char kProxyType_HTTP[];
47 extern const char kProxyType_HTTPS[];
48 extern const char kProxyType_SOCKS[];
49 extern const char kProxyType_SOCKS4[];
50 extern const char kProxyType_SOCKS5[];
51 extern const char kProxyType_DIRECT[];
52
53 #undef LOG
54 #define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
55
56 //----------------------------------------------------------------------------
57
58 #define PROXY_PREF_BRANCH "network.proxy"
59 #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
60
61 #define WPAD_URL "http://wpad/wpad.dat"
62
63 //----------------------------------------------------------------------------
64
65 // This structure is intended to be allocated on the stack
66 struct nsProtocolInfo {
67 nsAutoCString scheme;
68 uint32_t flags;
69 int32_t defaultPort;
70 };
71
72 //----------------------------------------------------------------------------
73
74 // Return the channel's proxy URI, or if it doesn't exist, the
75 // channel's main URI.
76 static nsresult
GetProxyURI(nsIChannel * channel,nsIURI ** aOut)77 GetProxyURI(nsIChannel *channel, nsIURI **aOut)
78 {
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 // The nsPACManCallback portion of this implementation should be run
98 // on the main thread - so call nsPACMan::AsyncGetProxyForURI() with
99 // a true mainThreadResponse parameter.
100 class nsAsyncResolveRequest final : public nsIRunnable
101 , public nsPACManCallback
102 , public nsICancelable
103 {
104 public:
105 NS_DECL_THREADSAFE_ISUPPORTS
106
nsAsyncResolveRequest(nsProtocolProxyService * pps,nsIChannel * channel,uint32_t aResolveFlags,nsIProtocolProxyCallback * callback)107 nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIChannel *channel,
108 uint32_t aResolveFlags,
109 nsIProtocolProxyCallback *callback)
110 : mStatus(NS_OK)
111 , mDispatched(false)
112 , mResolveFlags(aResolveFlags)
113 , mPPS(pps)
114 , mXPComPPS(pps)
115 , mChannel(channel)
116 , mCallback(callback)
117 {
118 NS_ASSERTION(mCallback, "null callback");
119 }
120
121 private:
~nsAsyncResolveRequest()122 ~nsAsyncResolveRequest()
123 {
124 if (!NS_IsMainThread()) {
125 // these xpcom pointers might need to be proxied back to the
126 // main thread to delete safely, but if this request had its
127 // callbacks called normally they will all be null and this is a nop
128
129 if (mChannel) {
130 NS_ReleaseOnMainThread(mChannel.forget());
131 }
132
133 if (mCallback) {
134 NS_ReleaseOnMainThread(mCallback.forget());
135 }
136
137 if (mProxyInfo) {
138 NS_ReleaseOnMainThread(mProxyInfo.forget());
139 }
140
141 if (mXPComPPS) {
142 NS_ReleaseOnMainThread(mXPComPPS.forget());
143 }
144 }
145 }
146
147 public:
SetResult(nsresult status,nsIProxyInfo * pi)148 void SetResult(nsresult status, nsIProxyInfo *pi)
149 {
150 mStatus = status;
151 mProxyInfo = pi;
152 }
153
Run()154 NS_IMETHOD Run() override
155 {
156 if (mCallback)
157 DoCallback();
158 return NS_OK;
159 }
160
Cancel(nsresult reason)161 NS_IMETHOD Cancel(nsresult reason) override
162 {
163 NS_ENSURE_ARG(NS_FAILED(reason));
164
165 // If we've already called DoCallback then, nothing more to do.
166 if (!mCallback)
167 return NS_OK;
168
169 SetResult(reason, nullptr);
170 return DispatchCallback();
171 }
172
DispatchCallback()173 nsresult DispatchCallback()
174 {
175 if (mDispatched) // Only need to dispatch once
176 return NS_OK;
177
178 nsresult rv = NS_DispatchToCurrentThread(this);
179 if (NS_FAILED(rv))
180 NS_WARNING("unable to dispatch callback event");
181 else {
182 mDispatched = true;
183 return NS_OK;
184 }
185
186 mCallback = nullptr; // break possible reference cycle
187 return rv;
188 }
189
190 private:
191
192 // Called asynchronously, so we do not need to post another PLEvent
193 // before calling DoCallback.
OnQueryComplete(nsresult status,const nsCString & pacString,const nsCString & newPACURL)194 void OnQueryComplete(nsresult status,
195 const nsCString &pacString,
196 const nsCString &newPACURL) override
197 {
198 // If we've already called DoCallback then, nothing more to do.
199 if (!mCallback)
200 return;
201
202 // Provided we haven't been canceled...
203 if (mStatus == NS_OK) {
204 mStatus = status;
205 mPACString = pacString;
206 mPACURL = newPACURL;
207 }
208
209 // In the cancelation case, we may still have another PLEvent in
210 // the queue that wants to call DoCallback. No need to wait for
211 // it, just run the callback now.
212 DoCallback();
213 }
214
DoCallback()215 void DoCallback()
216 {
217 bool pacAvailable = true;
218 if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) {
219 // If the PAC service is not avail (e.g. failed pac load
220 // or shutdown) then we will be going direct. Make that
221 // mapping now so that any filters are still applied.
222 mPACString = NS_LITERAL_CSTRING("DIRECT;");
223 mStatus = NS_OK;
224
225 LOG(("pac not available, use DIRECT\n"));
226 pacAvailable = false;
227 }
228
229 // Generate proxy info from the PAC string if appropriate
230 if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
231 mPPS->ProcessPACString(mPACString, mResolveFlags,
232 getter_AddRefs(mProxyInfo));
233 nsCOMPtr<nsIURI> proxyURI;
234 GetProxyURI(mChannel, getter_AddRefs(proxyURI));
235
236 // Now apply proxy filters
237 nsProtocolInfo info;
238 mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
239 if (NS_SUCCEEDED(mStatus))
240 mPPS->ApplyFilters(mChannel, info, mProxyInfo);
241 else
242 mProxyInfo = nullptr;
243
244 if(pacAvailable) {
245 // if !pacAvailable, it was already logged above
246 LOG(("pac thread callback %s\n", mPACString.get()));
247 }
248 if (NS_SUCCEEDED(mStatus))
249 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
250 mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
251 }
252 else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
253 LOG(("pac thread callback indicates new pac file load\n"));
254
255 nsCOMPtr<nsIURI> proxyURI;
256 GetProxyURI(mChannel, getter_AddRefs(proxyURI));
257
258 // trigger load of new pac url
259 nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
260 if (NS_SUCCEEDED(rv)) {
261 // now that the load is triggered, we can resubmit the query
262 RefPtr<nsAsyncResolveRequest> newRequest =
263 new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags,
264 mCallback);
265 rv = mPPS->mPACMan->AsyncGetProxyForURI(proxyURI,
266 newRequest,
267 true);
268 }
269
270 if (NS_FAILED(rv))
271 mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
272
273 // do not call onproxyavailable() in SUCCESS case - the newRequest will
274 // take care of that
275 }
276 else {
277 LOG(("pac thread callback did not provide information %X\n", mStatus));
278 if (NS_SUCCEEDED(mStatus))
279 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
280 mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
281 }
282
283 // We are on the main thread now and don't need these any more so
284 // release them to avoid having to proxy them back to the main thread
285 // in the dtor
286 mCallback = nullptr; // in case the callback holds an owning ref to us
287 mPPS = nullptr;
288 mXPComPPS = nullptr;
289 mChannel = nullptr;
290 mProxyInfo = nullptr;
291 }
292
293 private:
294
295 nsresult mStatus;
296 nsCString mPACString;
297 nsCString mPACURL;
298 bool mDispatched;
299 uint32_t mResolveFlags;
300
301 nsProtocolProxyService *mPPS;
302 nsCOMPtr<nsIProtocolProxyService> mXPComPPS;
303 nsCOMPtr<nsIChannel> mChannel;
304 nsCOMPtr<nsIProtocolProxyCallback> mCallback;
305 nsCOMPtr<nsIProxyInfo> mProxyInfo;
306 };
307
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest,nsICancelable,nsIRunnable)308 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
309
310 //----------------------------------------------------------------------------
311
312 #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t')
313
314 //
315 // apply mask to address (zeros out excluded bits).
316 //
317 // NOTE: we do the byte swapping here to minimize overall swapping.
318 //
319 static void
320 proxy_MaskIPv6Addr(PRIPv6Addr &addr, uint16_t mask_len)
321 {
322 if (mask_len == 128)
323 return;
324
325 if (mask_len > 96) {
326 addr.pr_s6_addr32[3] = PR_htonl(
327 PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len)));
328 }
329 else if (mask_len > 64) {
330 addr.pr_s6_addr32[3] = 0;
331 addr.pr_s6_addr32[2] = PR_htonl(
332 PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len)));
333 }
334 else if (mask_len > 32) {
335 addr.pr_s6_addr32[3] = 0;
336 addr.pr_s6_addr32[2] = 0;
337 addr.pr_s6_addr32[1] = PR_htonl(
338 PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len)));
339 }
340 else {
341 addr.pr_s6_addr32[3] = 0;
342 addr.pr_s6_addr32[2] = 0;
343 addr.pr_s6_addr32[1] = 0;
344 addr.pr_s6_addr32[0] = PR_htonl(
345 PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
346 }
347 }
348
349 static void
proxy_GetStringPref(nsIPrefBranch * aPrefBranch,const char * aPref,nsCString & aResult)350 proxy_GetStringPref(nsIPrefBranch *aPrefBranch,
351 const char *aPref,
352 nsCString &aResult)
353 {
354 nsXPIDLCString temp;
355 nsresult rv = aPrefBranch->GetCharPref(aPref, getter_Copies(temp));
356 if (NS_FAILED(rv))
357 aResult.Truncate();
358 else {
359 aResult.Assign(temp);
360 // all of our string prefs are hostnames, so we should remove any
361 // whitespace characters that the user might have unknowingly entered.
362 aResult.StripWhitespace();
363 }
364 }
365
366 static void
proxy_GetIntPref(nsIPrefBranch * aPrefBranch,const char * aPref,int32_t & aResult)367 proxy_GetIntPref(nsIPrefBranch *aPrefBranch,
368 const char *aPref,
369 int32_t &aResult)
370 {
371 int32_t temp;
372 nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
373 if (NS_FAILED(rv))
374 aResult = -1;
375 else
376 aResult = temp;
377 }
378
379 static void
proxy_GetBoolPref(nsIPrefBranch * aPrefBranch,const char * aPref,bool & aResult)380 proxy_GetBoolPref(nsIPrefBranch *aPrefBranch,
381 const char *aPref,
382 bool &aResult)
383 {
384 bool temp;
385 nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
386 if (NS_FAILED(rv))
387 aResult = false;
388 else
389 aResult = temp;
390 }
391
392 //----------------------------------------------------------------------------
393
394 static const int32_t PROXYCONFIG_DIRECT4X = 3;
395 static const int32_t PROXYCONFIG_COUNT = 6;
396
397 NS_IMPL_ADDREF(nsProtocolProxyService)
398 NS_IMPL_RELEASE(nsProtocolProxyService)
399 NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
400 NS_PROTOCOLPROXYSERVICE_CID)
401
402 // NS_IMPL_QUERY_INTERFACE_CI with the nsProtocolProxyService QI change
403 NS_INTERFACE_MAP_BEGIN(nsProtocolProxyService)
404 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService)
405 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService2)
406 NS_INTERFACE_MAP_ENTRY(nsIObserver)
407 if ( aIID.Equals(NS_GET_IID(nsProtocolProxyService)) ) foundInterface = static_cast<nsIProtocolProxyService2*>(this); else
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,nsIProtocolProxyService)408 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolProxyService)
409 NS_IMPL_QUERY_CLASSINFO(nsProtocolProxyService)
410 NS_INTERFACE_MAP_END
411
412 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService,
413 nsIProtocolProxyService,
414 nsIProtocolProxyService2)
415
416 nsProtocolProxyService::nsProtocolProxyService()
417 : mFilterLocalHosts(false)
418 , mFilters(nullptr)
419 , mProxyConfig(PROXYCONFIG_DIRECT)
420 , mHTTPProxyPort(-1)
421 , mFTPProxyPort(-1)
422 , mHTTPSProxyPort(-1)
423 , mSOCKSProxyPort(-1)
424 , mSOCKSProxyVersion(4)
425 , mSOCKSProxyRemoteDNS(false)
426 , mProxyOverTLS(true)
427 , mPACMan(nullptr)
428 , mSessionStart(PR_Now())
429 , mFailedProxyTimeout(30 * 60) // 30 minute default
430 {
431 }
432
~nsProtocolProxyService()433 nsProtocolProxyService::~nsProtocolProxyService()
434 {
435 // These should have been cleaned up in our Observe method.
436 NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters == nullptr &&
437 mPACMan == nullptr, "what happened to xpcom-shutdown?");
438 }
439
440 // nsProtocolProxyService methods
441 nsresult
Init()442 nsProtocolProxyService::Init()
443 {
444 // failure to access prefs is non-fatal
445 nsCOMPtr<nsIPrefBranch> prefBranch =
446 do_GetService(NS_PREFSERVICE_CONTRACTID);
447 if (prefBranch) {
448 // monitor proxy prefs
449 prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
450
451 // read all prefs
452 PrefsChanged(prefBranch, nullptr);
453 }
454
455 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
456 if (obs) {
457 // register for shutdown notification so we can clean ourselves up
458 // properly.
459 obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
460 obs->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
461 }
462
463 return NS_OK;
464 }
465
466 // ReloadNetworkPAC() checks if there's a non-networked PAC in use then avoids
467 // to call ReloadPAC()
468 nsresult
ReloadNetworkPAC()469 nsProtocolProxyService::ReloadNetworkPAC()
470 {
471 nsCOMPtr<nsIPrefBranch> prefs =
472 do_GetService(NS_PREFSERVICE_CONTRACTID);
473 if (!prefs) {
474 return NS_OK;
475 }
476
477 int32_t type;
478 nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
479 if (NS_FAILED(rv)) {
480 return NS_OK;
481 }
482
483 if (type == PROXYCONFIG_PAC) {
484 nsXPIDLCString pacSpec;
485 prefs->GetCharPref(PROXY_PREF("autoconfig_url"),
486 getter_Copies(pacSpec));
487 if (!pacSpec.IsEmpty()) {
488 nsCOMPtr<nsIURI> pacURI;
489 rv = NS_NewURI(getter_AddRefs(pacURI), pacSpec);
490 if(!NS_SUCCEEDED(rv)) {
491 return rv;
492 }
493
494 nsProtocolInfo pac;
495 rv = GetProtocolInfo(pacURI, &pac);
496 if(!NS_SUCCEEDED(rv)) {
497 return rv;
498 }
499
500 if (!pac.scheme.EqualsLiteral("file") &&
501 !pac.scheme.EqualsLiteral("data")) {
502 LOG((": received network changed event, reload PAC"));
503 ReloadPAC();
504 }
505 }
506 } else if ((type == PROXYCONFIG_WPAD) || (type == PROXYCONFIG_SYSTEM)) {
507 ReloadPAC();
508 }
509
510 return NS_OK;
511 }
512
513
514 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)515 nsProtocolProxyService::Observe(nsISupports *aSubject,
516 const char *aTopic,
517 const char16_t *aData)
518 {
519 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
520 // cleanup
521 if (mHostFiltersArray.Length() > 0) {
522 mHostFiltersArray.Clear();
523 }
524 if (mFilters) {
525 delete mFilters;
526 mFilters = nullptr;
527 }
528 if (mPACMan) {
529 mPACMan->Shutdown();
530 mPACMan = nullptr;
531 }
532
533 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
534 if (obs) {
535 obs->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
536 obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
537 }
538
539 } else if (strcmp(aTopic, NS_NETWORK_LINK_TOPIC) == 0) {
540 nsCString converted = NS_ConvertUTF16toUTF8(aData);
541 const char *state = converted.get();
542 if (!strcmp(state, NS_NETWORK_LINK_DATA_CHANGED)) {
543 ReloadNetworkPAC();
544 }
545 }
546 else {
547 NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
548 "what is this random observer event?");
549 nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
550 if (prefs)
551 PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
552 }
553 return NS_OK;
554 }
555
556 void
PrefsChanged(nsIPrefBranch * prefBranch,const char * pref)557 nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
558 const char *pref)
559 {
560 nsresult rv = NS_OK;
561 bool reloadPAC = false;
562 nsXPIDLCString tempString;
563
564 if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
565 int32_t type = -1;
566 rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
567 if (NS_SUCCEEDED(rv)) {
568 // bug 115720 - for ns4.x backwards compatibility
569 if (type == PROXYCONFIG_DIRECT4X) {
570 type = PROXYCONFIG_DIRECT;
571 // Reset the type so that the dialog looks correct, and we
572 // don't have to handle this case everywhere else
573 // I'm paranoid about a loop of some sort - only do this
574 // if we're enumerating all prefs, and ignore any error
575 if (!pref)
576 prefBranch->SetIntPref(PROXY_PREF("type"), type);
577 } else if (type >= PROXYCONFIG_COUNT) {
578 LOG(("unknown proxy type: %lu; assuming direct\n", type));
579 type = PROXYCONFIG_DIRECT;
580 }
581 mProxyConfig = type;
582 reloadPAC = true;
583 }
584
585 if (mProxyConfig == PROXYCONFIG_SYSTEM) {
586 mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
587 if (!mSystemProxySettings)
588 mProxyConfig = PROXYCONFIG_DIRECT;
589 ResetPACThread();
590 } else {
591 if (mSystemProxySettings) {
592 mSystemProxySettings = nullptr;
593 ResetPACThread();
594 }
595 }
596 }
597
598 if (!pref || !strcmp(pref, PROXY_PREF("http")))
599 proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
600
601 if (!pref || !strcmp(pref, PROXY_PREF("http_port")))
602 proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
603
604 if (!pref || !strcmp(pref, PROXY_PREF("ssl")))
605 proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
606
607 if (!pref || !strcmp(pref, PROXY_PREF("ssl_port")))
608 proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
609
610 if (!pref || !strcmp(pref, PROXY_PREF("ftp")))
611 proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost);
612
613 if (!pref || !strcmp(pref, PROXY_PREF("ftp_port")))
614 proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
615
616 if (!pref || !strcmp(pref, PROXY_PREF("socks")))
617 proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyTarget);
618
619 if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
620 proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
621
622 if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
623 int32_t version;
624 proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
625 // make sure this preference value remains sane
626 if (version == 5)
627 mSOCKSProxyVersion = 5;
628 else
629 mSOCKSProxyVersion = 4;
630 }
631
632 if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns")))
633 proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
634 mSOCKSProxyRemoteDNS);
635
636 if (!pref || !strcmp(pref, PROXY_PREF("proxy_over_tls"))) {
637 proxy_GetBoolPref(prefBranch, PROXY_PREF("proxy_over_tls"),
638 mProxyOverTLS);
639 }
640
641 if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
642 proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
643 mFailedProxyTimeout);
644
645 if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
646 rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
647 getter_Copies(tempString));
648 if (NS_SUCCEEDED(rv))
649 LoadHostFilters(tempString);
650 }
651
652 // We're done if not using something that could give us a PAC URL
653 // (PAC, WPAD or System)
654 if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
655 mProxyConfig != PROXYCONFIG_SYSTEM)
656 return;
657
658 // OK, we need to reload the PAC file if:
659 // 1) network.proxy.type changed, or
660 // 2) network.proxy.autoconfig_url changed and PAC is configured
661
662 if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url")))
663 reloadPAC = true;
664
665 if (reloadPAC) {
666 tempString.Truncate();
667 if (mProxyConfig == PROXYCONFIG_PAC) {
668 prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"),
669 getter_Copies(tempString));
670 if (mPACMan && !mPACMan->IsPACURI(tempString)) {
671 LOG(("PAC Thread URI Changed - Reset Pac Thread"));
672 ResetPACThread();
673 }
674 } else if (mProxyConfig == PROXYCONFIG_WPAD) {
675 // We diverge from the WPAD spec here in that we don't walk the
676 // hosts's FQDN, stripping components until we hit a TLD. Doing so
677 // is dangerous in the face of an incomplete list of TLDs, and TLDs
678 // get added over time. We could consider doing only a single
679 // substitution of the first component, if that proves to help
680 // compatibility.
681 tempString.AssignLiteral(WPAD_URL);
682 } else if (mSystemProxySettings) {
683 // Get System Proxy settings if available
684 mSystemProxySettings->GetPACURI(tempString);
685 }
686 if (!tempString.IsEmpty())
687 ConfigureFromPAC(tempString, false);
688 }
689 }
690
691 bool
CanUseProxy(nsIURI * aURI,int32_t defaultPort)692 nsProtocolProxyService::CanUseProxy(nsIURI *aURI, int32_t defaultPort)
693 {
694 if (mHostFiltersArray.Length() == 0)
695 return true;
696
697 int32_t port;
698 nsAutoCString host;
699
700 nsresult rv = aURI->GetAsciiHost(host);
701 if (NS_FAILED(rv) || host.IsEmpty())
702 return false;
703
704 rv = aURI->GetPort(&port);
705 if (NS_FAILED(rv))
706 return false;
707 if (port == -1)
708 port = defaultPort;
709
710 PRNetAddr addr;
711 bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
712
713 PRIPv6Addr ipv6;
714 if (is_ipaddr) {
715 // convert parsed address to IPv6
716 if (addr.raw.family == PR_AF_INET) {
717 // convert to IPv4-mapped address
718 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
719 }
720 else if (addr.raw.family == PR_AF_INET6) {
721 // copy the address
722 memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
723 }
724 else {
725 NS_WARNING("unknown address family");
726 return true; // allow proxying
727 }
728 }
729
730 // Don't use proxy for local hosts (plain hostname, no dots)
731 if ((!is_ipaddr && mFilterLocalHosts && !host.Contains('.')) ||
732 host.EqualsLiteral("127.0.0.1") ||
733 host.EqualsLiteral("::1")) {
734 LOG(("Not using proxy for this local host [%s]!\n", host.get()));
735 return false; // don't allow proxying
736 }
737
738 int32_t index = -1;
739 while (++index < int32_t(mHostFiltersArray.Length())) {
740 HostInfo *hinfo = mHostFiltersArray[index];
741
742 if (is_ipaddr != hinfo->is_ipaddr)
743 continue;
744 if (hinfo->port && hinfo->port != port)
745 continue;
746
747 if (is_ipaddr) {
748 // generate masked version of target IPv6 address
749 PRIPv6Addr masked;
750 memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
751 proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
752
753 // check for a match
754 if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0)
755 return false; // proxy disallowed
756 }
757 else {
758 uint32_t host_len = host.Length();
759 uint32_t filter_host_len = hinfo->name.host_len;
760
761 if (host_len >= filter_host_len) {
762 //
763 // compare last |filter_host_len| bytes of target hostname.
764 //
765 const char *host_tail = host.get() + host_len - filter_host_len;
766 if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len)) {
767 // If the tail of the host string matches the filter
768
769 if (filter_host_len > 0 && hinfo->name.host[0] == '.') {
770 // If the filter was of the form .foo.bar.tld, all such
771 // matches are correct
772 return false; // proxy disallowed
773 }
774
775 // abc-def.example.org should not match def.example.org
776 // however, *.def.example.org should match .def.example.org
777 // We check that the filter doesn't start with a `.`. If it does,
778 // then the strncasecmp above should suffice. If it doesn't,
779 // then we should only consider it a match if the strncasecmp happened
780 // at a subdomain boundary
781 if (host_len > filter_host_len && *(host_tail - 1) == '.') {
782 // If the host was something.foo.bar.tld and the filter
783 // was foo.bar.tld, it's still a match.
784 // the character right before the tail must be a
785 // `.` for this to work
786 return false; // proxy disallowed
787 }
788
789 if (host_len == filter_host_len) {
790 // If the host and filter are of the same length,
791 // they should match
792 return false; // proxy disallowed
793 }
794 }
795
796 }
797 }
798 }
799 return true;
800 }
801
802 // kProxyType\* may be referred to externally in
803 // nsProxyInfo in order to compare by string pointer
804 const char kProxyType_HTTP[] = "http";
805 const char kProxyType_HTTPS[] = "https";
806 const char kProxyType_PROXY[] = "proxy";
807 const char kProxyType_SOCKS[] = "socks";
808 const char kProxyType_SOCKS4[] = "socks4";
809 const char kProxyType_SOCKS5[] = "socks5";
810 const char kProxyType_DIRECT[] = "direct";
811
812 const char *
ExtractProxyInfo(const char * start,uint32_t aResolveFlags,nsProxyInfo ** result)813 nsProtocolProxyService::ExtractProxyInfo(const char *start,
814 uint32_t aResolveFlags,
815 nsProxyInfo **result)
816 {
817 *result = nullptr;
818 uint32_t flags = 0;
819
820 // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
821
822 // find end of proxy info delimiter
823 const char *end = start;
824 while (*end && *end != ';') ++end;
825
826 // find end of proxy type delimiter
827 const char *sp = start;
828 while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
829
830 uint32_t len = sp - start;
831 const char *type = nullptr;
832 switch (len) {
833 case 4:
834 if (PL_strncasecmp(start, kProxyType_HTTP, 5) == 0) {
835 type = kProxyType_HTTP;
836 }
837 break;
838 case 5:
839 if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0) {
840 type = kProxyType_HTTP;
841 } else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0) {
842 type = kProxyType_SOCKS4; // assume v4 for 4x compat
843 } else if (PL_strncasecmp(start, kProxyType_HTTPS, 5) == 0) {
844 type = kProxyType_HTTPS;
845 }
846 break;
847 case 6:
848 if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0)
849 type = kProxyType_DIRECT;
850 else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0)
851 type = kProxyType_SOCKS4;
852 else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0)
853 // map "SOCKS5" to "socks" to match contract-id of registered
854 // SOCKS-v5 socket provider.
855 type = kProxyType_SOCKS;
856 break;
857 }
858 if (type) {
859 const char *host = nullptr, *hostEnd = nullptr;
860 int32_t port = -1;
861
862 // If it's a SOCKS5 proxy, do name resolution on the server side.
863 // We could use this with SOCKS4a servers too, but they might not
864 // support it.
865 if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS)
866 flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
867
868 // extract host:port
869 start = sp;
870 while ((*start == ' ' || *start == '\t') && start < end)
871 start++;
872
873 // port defaults
874 if (type == kProxyType_HTTP) {
875 port = 80;
876 } else if (type == kProxyType_HTTPS) {
877 port = 443;
878 } else {
879 port = 1080;
880 }
881
882 nsProxyInfo *pi = new nsProxyInfo();
883 pi->mType = type;
884 pi->mFlags = flags;
885 pi->mResolveFlags = aResolveFlags;
886 pi->mTimeout = mFailedProxyTimeout;
887
888 // www.foo.com:8080 and http://www.foo.com:8080
889 nsDependentCSubstring maybeURL(start, end - start);
890 nsCOMPtr<nsIURI> pacURI;
891
892 nsAutoCString urlHost;
893 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) &&
894 NS_SUCCEEDED(pacURI->GetAsciiHost(urlHost)) &&
895 !urlHost.IsEmpty()) {
896 // http://www.example.com:8080
897
898 pi->mHost = urlHost;
899
900 int32_t tPort;
901 if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
902 port = tPort;
903 }
904 pi->mPort = port;
905 }
906 else {
907 // www.example.com:8080
908 if (start < end) {
909 host = start;
910 hostEnd = strchr(host, ':');
911 if (!hostEnd || hostEnd > end) {
912 hostEnd = end;
913 // no port, so assume default
914 }
915 else {
916 port = atoi(hostEnd + 1);
917 }
918 }
919 // YES, it is ok to specify a null proxy host.
920 if (host) {
921 pi->mHost.Assign(host, hostEnd - host);
922 pi->mPort = port;
923 }
924 }
925 NS_ADDREF(*result = pi);
926 }
927
928 while (*end == ';' || *end == ' ' || *end == '\t')
929 ++end;
930 return end;
931 }
932
933 void
GetProxyKey(nsProxyInfo * pi,nsCString & key)934 nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key)
935 {
936 key.AssignASCII(pi->mType);
937 if (!pi->mHost.IsEmpty()) {
938 key.Append(' ');
939 key.Append(pi->mHost);
940 key.Append(':');
941 key.AppendInt(pi->mPort);
942 }
943 }
944
945 uint32_t
SecondsSinceSessionStart()946 nsProtocolProxyService::SecondsSinceSessionStart()
947 {
948 PRTime now = PR_Now();
949
950 // get time elapsed since session start
951 int64_t diff = now - mSessionStart;
952
953 // convert microseconds to seconds
954 diff /= PR_USEC_PER_SEC;
955
956 // return converted 32 bit value
957 return uint32_t(diff);
958 }
959
960 void
EnableProxy(nsProxyInfo * pi)961 nsProtocolProxyService::EnableProxy(nsProxyInfo *pi)
962 {
963 nsAutoCString key;
964 GetProxyKey(pi, key);
965 mFailedProxies.Remove(key);
966 }
967
968 void
DisableProxy(nsProxyInfo * pi)969 nsProtocolProxyService::DisableProxy(nsProxyInfo *pi)
970 {
971 nsAutoCString key;
972 GetProxyKey(pi, key);
973
974 uint32_t dsec = SecondsSinceSessionStart();
975
976 // Add timeout to interval (this is the time when the proxy can
977 // be tried again).
978 dsec += pi->mTimeout;
979
980 // NOTE: The classic codebase would increase the timeout value
981 // incrementally each time a subsequent failure occurred.
982 // We could do the same, but it would require that we not
983 // remove proxy entries in IsProxyDisabled or otherwise
984 // change the way we are recording disabled proxies.
985 // Simpler is probably better for now, and at least the
986 // user can tune the timeout setting via preferences.
987
988 LOG(("DisableProxy %s %d\n", key.get(), dsec));
989
990 // If this fails, oh well... means we don't have enough memory
991 // to remember the failed proxy.
992 mFailedProxies.Put(key, dsec);
993 }
994
995 bool
IsProxyDisabled(nsProxyInfo * pi)996 nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi)
997 {
998 nsAutoCString key;
999 GetProxyKey(pi, key);
1000
1001 uint32_t val;
1002 if (!mFailedProxies.Get(key, &val))
1003 return false;
1004
1005 uint32_t dsec = SecondsSinceSessionStart();
1006
1007 // if time passed has exceeded interval, then try proxy again.
1008 if (dsec > val) {
1009 mFailedProxies.Remove(key);
1010 return false;
1011 }
1012
1013 return true;
1014 }
1015
1016 nsresult
SetupPACThread()1017 nsProtocolProxyService::SetupPACThread()
1018 {
1019 if (mPACMan)
1020 return NS_OK;
1021
1022 mPACMan = new nsPACMan();
1023
1024 bool mainThreadOnly;
1025 nsresult rv;
1026 if (mSystemProxySettings &&
1027 NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
1028 !mainThreadOnly) {
1029 rv = mPACMan->Init(mSystemProxySettings);
1030 }
1031 else {
1032 rv = mPACMan->Init(nullptr);
1033 }
1034
1035 if (NS_FAILED(rv))
1036 mPACMan = nullptr;
1037 return rv;
1038 }
1039
1040 nsresult
ResetPACThread()1041 nsProtocolProxyService::ResetPACThread()
1042 {
1043 if (!mPACMan)
1044 return NS_OK;
1045
1046 mPACMan->Shutdown();
1047 mPACMan = nullptr;
1048 return SetupPACThread();
1049 }
1050
1051 nsresult
ConfigureFromPAC(const nsCString & spec,bool forceReload)1052 nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
1053 bool forceReload)
1054 {
1055 SetupPACThread();
1056
1057 if (mPACMan->IsPACURI(spec) && !forceReload)
1058 return NS_OK;
1059
1060 mFailedProxies.Clear();
1061
1062 return mPACMan->LoadPACFromURI(spec);
1063 }
1064
1065 void
ProcessPACString(const nsCString & pacString,uint32_t aResolveFlags,nsIProxyInfo ** result)1066 nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
1067 uint32_t aResolveFlags,
1068 nsIProxyInfo **result)
1069 {
1070 if (pacString.IsEmpty()) {
1071 *result = nullptr;
1072 return;
1073 }
1074
1075 const char *proxies = pacString.get();
1076
1077 nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
1078 while (*proxies) {
1079 proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
1080 if (pi && (pi->mType == kProxyType_HTTPS) && !mProxyOverTLS) {
1081 delete pi;
1082 pi = nullptr;
1083 }
1084
1085 if (pi) {
1086 if (last) {
1087 NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
1088 last->mNext = pi;
1089 }
1090 else
1091 first = pi;
1092 last = pi;
1093 }
1094 }
1095 *result = first;
1096 }
1097
1098 // nsIProtocolProxyService2
1099 NS_IMETHODIMP
ReloadPAC()1100 nsProtocolProxyService::ReloadPAC()
1101 {
1102 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
1103 if (!prefs)
1104 return NS_OK;
1105
1106 int32_t type;
1107 nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
1108 if (NS_FAILED(rv))
1109 return NS_OK;
1110
1111 nsXPIDLCString pacSpec;
1112 if (type == PROXYCONFIG_PAC)
1113 prefs->GetCharPref(PROXY_PREF("autoconfig_url"), getter_Copies(pacSpec));
1114 else if (type == PROXYCONFIG_WPAD)
1115 pacSpec.AssignLiteral(WPAD_URL);
1116
1117 if (!pacSpec.IsEmpty())
1118 ConfigureFromPAC(pacSpec, true);
1119 return NS_OK;
1120 }
1121
1122 // When sync interface is removed this can go away too
1123 // The nsPACManCallback portion of this implementation should be run
1124 // off the main thread, because it uses a condvar for signaling and
1125 // the main thread is blocking on that condvar -
1126 // so call nsPACMan::AsyncGetProxyForURI() with
1127 // a false mainThreadResponse parameter.
1128 class nsAsyncBridgeRequest final : public nsPACManCallback
1129 {
1130 NS_DECL_THREADSAFE_ISUPPORTS
1131
nsAsyncBridgeRequest()1132 nsAsyncBridgeRequest()
1133 : mMutex("nsDeprecatedCallback")
1134 , mCondVar(mMutex, "nsDeprecatedCallback")
1135 , mStatus(NS_OK)
1136 , mCompleted(false)
1137 {
1138 }
1139
OnQueryComplete(nsresult status,const nsCString & pacString,const nsCString & newPACURL)1140 void OnQueryComplete(nsresult status,
1141 const nsCString &pacString,
1142 const nsCString &newPACURL) override
1143 {
1144 MutexAutoLock lock(mMutex);
1145 mCompleted = true;
1146 mStatus = status;
1147 mPACString = pacString;
1148 mPACURL = newPACURL;
1149 mCondVar.Notify();
1150 }
1151
Lock()1152 void Lock() { mMutex.Lock(); }
Unlock()1153 void Unlock() { mMutex.Unlock(); }
Wait()1154 void Wait() { mCondVar.Wait(PR_SecondsToInterval(3)); }
1155
1156 private:
~nsAsyncBridgeRequest()1157 ~nsAsyncBridgeRequest()
1158 {
1159 }
1160
1161 friend class nsProtocolProxyService;
1162
1163 Mutex mMutex;
1164 CondVar mCondVar;
1165
1166 nsresult mStatus;
1167 nsCString mPACString;
1168 nsCString mPACURL;
1169 bool mCompleted;
1170 };
NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)1171 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
1172
1173 // nsProtocolProxyService
1174 nsresult
1175 nsProtocolProxyService::DeprecatedBlockingResolve(nsIChannel *aChannel,
1176 uint32_t aFlags,
1177 nsIProxyInfo **retval)
1178 {
1179 NS_ENSURE_ARG_POINTER(aChannel);
1180
1181 nsCOMPtr<nsIURI> uri;
1182 nsresult rv = GetProxyURI(aChannel, getter_AddRefs(uri));
1183 if (NS_FAILED(rv)) return rv;
1184
1185 nsProtocolInfo info;
1186 rv = GetProtocolInfo(uri, &info);
1187 if (NS_FAILED(rv))
1188 return rv;
1189
1190 nsCOMPtr<nsIProxyInfo> pi;
1191 bool usePACThread;
1192
1193 // SystemProxySettings and PAC files can block the main thread
1194 // but if neither of them are in use, we can just do the work
1195 // right here and directly invoke the callback
1196
1197 rv = Resolve_Internal(aChannel, info, aFlags,
1198 &usePACThread, getter_AddRefs(pi));
1199 if (NS_FAILED(rv))
1200 return rv;
1201
1202 if (!usePACThread || !mPACMan) {
1203 ApplyFilters(aChannel, info, pi);
1204 pi.forget(retval);
1205 return NS_OK;
1206 }
1207
1208 // Use the PAC thread to do the work, so we don't have to reimplement that
1209 // code, but block this thread on that completion.
1210 RefPtr<nsAsyncBridgeRequest> ctx = new nsAsyncBridgeRequest();
1211 ctx->Lock();
1212 if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForURI(uri, ctx, false))) {
1213 // this can really block the main thread, so cap it at 3 seconds
1214 ctx->Wait();
1215 }
1216 ctx->Unlock();
1217 if (!ctx->mCompleted)
1218 return NS_ERROR_FAILURE;
1219 if (NS_FAILED(ctx->mStatus))
1220 return ctx->mStatus;
1221
1222 // pretty much duplicate real DoCallback logic
1223
1224 // Generate proxy info from the PAC string if appropriate
1225 if (!ctx->mPACString.IsEmpty()) {
1226 LOG(("sync pac thread callback %s\n", ctx->mPACString.get()));
1227 ProcessPACString(ctx->mPACString, 0, getter_AddRefs(pi));
1228 ApplyFilters(aChannel, info, pi);
1229 pi.forget(retval);
1230 return NS_OK;
1231 }
1232
1233 if (!ctx->mPACURL.IsEmpty()) {
1234 NS_WARNING("sync pac thread callback indicates new pac file load\n");
1235 // This is a problem and is one of the reasons this blocking interface
1236 // is deprecated. The main loop needs to spin to make this reload happen. So
1237 // we are going to kick off the reload and return an error - it will work
1238 // next time. Because this sync interface is only used in the java plugin it
1239 // is extremely likely that the pac file has already been loaded anyhow.
1240
1241 rv = ConfigureFromPAC(ctx->mPACURL, false);
1242 if (NS_FAILED(rv))
1243 return rv;
1244 return NS_ERROR_NOT_AVAILABLE;
1245 }
1246
1247 *retval = nullptr;
1248 return NS_OK;
1249 }
1250
1251 nsresult
AsyncResolveInternal(nsIChannel * channel,uint32_t flags,nsIProtocolProxyCallback * callback,nsICancelable ** result,bool isSyncOK)1252 nsProtocolProxyService::AsyncResolveInternal(nsIChannel *channel, uint32_t flags,
1253 nsIProtocolProxyCallback *callback,
1254 nsICancelable **result,
1255 bool isSyncOK)
1256 {
1257 NS_ENSURE_ARG_POINTER(channel);
1258 NS_ENSURE_ARG_POINTER(callback);
1259
1260 nsCOMPtr<nsIURI> uri;
1261 nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
1262 if (NS_FAILED(rv)) return rv;
1263
1264 *result = nullptr;
1265 RefPtr<nsAsyncResolveRequest> ctx =
1266 new nsAsyncResolveRequest(this, channel, flags, callback);
1267
1268 nsProtocolInfo info;
1269 rv = GetProtocolInfo(uri, &info);
1270 if (NS_FAILED(rv))
1271 return rv;
1272
1273 nsCOMPtr<nsIProxyInfo> pi;
1274 bool usePACThread;
1275
1276 // SystemProxySettings and PAC files can block the main thread
1277 // but if neither of them are in use, we can just do the work
1278 // right here and directly invoke the callback
1279
1280 rv = Resolve_Internal(channel, info, flags,
1281 &usePACThread, getter_AddRefs(pi));
1282 if (NS_FAILED(rv))
1283 return rv;
1284
1285 if (!usePACThread || !mPACMan) {
1286 // we can do it locally
1287 ApplyFilters(channel, info, pi);
1288 ctx->SetResult(NS_OK, pi);
1289 if (isSyncOK) {
1290 ctx->Run();
1291 return NS_OK;
1292 }
1293
1294 rv = ctx->DispatchCallback();
1295 if (NS_SUCCEEDED(rv))
1296 ctx.forget(result);
1297 return rv;
1298 }
1299
1300 // else kick off a PAC thread query
1301
1302 rv = mPACMan->AsyncGetProxyForURI(uri, ctx, true);
1303 if (NS_SUCCEEDED(rv))
1304 ctx.forget(result);
1305 return rv;
1306 }
1307
1308 // nsIProtocolProxyService
1309 NS_IMETHODIMP
AsyncResolve2(nsIChannel * channel,uint32_t flags,nsIProtocolProxyCallback * callback,nsICancelable ** result)1310 nsProtocolProxyService::AsyncResolve2(nsIChannel *channel, uint32_t flags,
1311 nsIProtocolProxyCallback *callback,
1312 nsICancelable **result)
1313 {
1314 return AsyncResolveInternal(channel, flags, callback, result, true);
1315 }
1316
1317 NS_IMETHODIMP
AsyncResolve(nsISupports * channelOrURI,uint32_t flags,nsIProtocolProxyCallback * callback,nsICancelable ** result)1318 nsProtocolProxyService::AsyncResolve(nsISupports *channelOrURI, uint32_t flags,
1319 nsIProtocolProxyCallback *callback,
1320 nsICancelable **result)
1321 {
1322
1323 nsresult rv;
1324 // Check if we got a channel:
1325 nsCOMPtr<nsIChannel> channel = do_QueryInterface(channelOrURI);
1326 if (!channel) {
1327 nsCOMPtr<nsIURI> uri = do_QueryInterface(channelOrURI);
1328 if (!uri) {
1329 return NS_ERROR_NO_INTERFACE;
1330 }
1331
1332 nsCOMPtr<nsIScriptSecurityManager> secMan(
1333 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
1334 NS_ENSURE_SUCCESS(rv, rv);
1335 nsCOMPtr<nsIPrincipal> systemPrincipal;
1336 rv = secMan->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
1337 NS_ENSURE_SUCCESS(rv, rv);
1338
1339 // creating a temporary channel from the URI which is not
1340 // used to perform any network loads, hence its safe to
1341 // use systemPrincipal as the loadingPrincipal.
1342 rv = NS_NewChannel(getter_AddRefs(channel),
1343 uri,
1344 systemPrincipal,
1345 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1346 nsIContentPolicy::TYPE_OTHER);
1347 NS_ENSURE_SUCCESS(rv, rv);
1348 }
1349
1350 return AsyncResolveInternal(channel, flags, callback, result, false);
1351 }
1352
1353 NS_IMETHODIMP
NewProxyInfo(const nsACString & aType,const nsACString & aHost,int32_t aPort,uint32_t aFlags,uint32_t aFailoverTimeout,nsIProxyInfo * aFailoverProxy,nsIProxyInfo ** aResult)1354 nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
1355 const nsACString &aHost,
1356 int32_t aPort,
1357 uint32_t aFlags,
1358 uint32_t aFailoverTimeout,
1359 nsIProxyInfo *aFailoverProxy,
1360 nsIProxyInfo **aResult)
1361 {
1362 return NewProxyInfoWithAuth(aType, aHost, aPort,
1363 EmptyCString(), EmptyCString(),
1364 aFlags, aFailoverTimeout,
1365 aFailoverProxy, aResult);
1366 }
1367
1368 NS_IMETHODIMP
NewProxyInfoWithAuth(const nsACString & aType,const nsACString & aHost,int32_t aPort,const nsACString & aUsername,const nsACString & aPassword,uint32_t aFlags,uint32_t aFailoverTimeout,nsIProxyInfo * aFailoverProxy,nsIProxyInfo ** aResult)1369 nsProtocolProxyService::NewProxyInfoWithAuth(const nsACString &aType,
1370 const nsACString &aHost,
1371 int32_t aPort,
1372 const nsACString &aUsername,
1373 const nsACString &aPassword,
1374 uint32_t aFlags,
1375 uint32_t aFailoverTimeout,
1376 nsIProxyInfo *aFailoverProxy,
1377 nsIProxyInfo **aResult)
1378 {
1379 static const char *types[] = {
1380 kProxyType_HTTP,
1381 kProxyType_HTTPS,
1382 kProxyType_SOCKS,
1383 kProxyType_SOCKS4,
1384 kProxyType_DIRECT
1385 };
1386
1387 // resolve type; this allows us to avoid copying the type string into each
1388 // proxy info instance. we just reference the string literals directly :)
1389 const char *type = nullptr;
1390 for (uint32_t i = 0; i < ArrayLength(types); ++i) {
1391 if (aType.LowerCaseEqualsASCII(types[i])) {
1392 type = types[i];
1393 break;
1394 }
1395 }
1396 NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
1397
1398 // We have only implemented username/password for SOCKS proxies.
1399 if ((!aUsername.IsEmpty() || !aPassword.IsEmpty()) &&
1400 !aType.LowerCaseEqualsASCII(kProxyType_SOCKS) &&
1401 !aType.LowerCaseEqualsASCII(kProxyType_SOCKS4)) {
1402 return NS_ERROR_NOT_IMPLEMENTED;
1403 }
1404
1405 return NewProxyInfo_Internal(type, aHost, aPort,
1406 aUsername, aPassword,
1407 aFlags, aFailoverTimeout,
1408 aFailoverProxy, 0, aResult);
1409 }
1410
1411 NS_IMETHODIMP
GetFailoverForProxy(nsIProxyInfo * aProxy,nsIURI * aURI,nsresult aStatus,nsIProxyInfo ** aResult)1412 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo *aProxy,
1413 nsIURI *aURI,
1414 nsresult aStatus,
1415 nsIProxyInfo **aResult)
1416 {
1417 // We only support failover when a PAC file is configured, either
1418 // directly or via system settings
1419 if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
1420 mProxyConfig != PROXYCONFIG_SYSTEM)
1421 return NS_ERROR_NOT_AVAILABLE;
1422
1423 // Verify that |aProxy| is one of our nsProxyInfo objects.
1424 nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
1425 NS_ENSURE_ARG(pi);
1426 // OK, the QI checked out. We can proceed.
1427
1428 // Remember that this proxy is down.
1429 DisableProxy(pi);
1430
1431 // NOTE: At this point, we might want to prompt the user if we have
1432 // not already tried going DIRECT. This is something that the
1433 // classic codebase supported; however, IE6 does not prompt.
1434
1435 if (!pi->mNext)
1436 return NS_ERROR_NOT_AVAILABLE;
1437
1438 LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
1439 pi->mType, pi->mHost.get(), pi->mPort,
1440 pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
1441
1442 NS_ADDREF(*aResult = pi->mNext);
1443 return NS_OK;
1444 }
1445
1446 nsresult
InsertFilterLink(FilterLink * link,uint32_t position)1447 nsProtocolProxyService::InsertFilterLink(FilterLink *link, uint32_t position)
1448 {
1449 if (!mFilters) {
1450 mFilters = link;
1451 return NS_OK;
1452 }
1453
1454 // insert into mFilters in sorted order
1455 FilterLink *last = nullptr;
1456 for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1457 if (position < iter->position) {
1458 if (last) {
1459 link->next = last->next;
1460 last->next = link;
1461 }
1462 else {
1463 link->next = mFilters;
1464 mFilters = link;
1465 }
1466 return NS_OK;
1467 }
1468 last = iter;
1469 }
1470 // our position is equal to or greater than the last link in the list
1471 last->next = link;
1472 return NS_OK;
1473 }
1474
1475 NS_IMETHODIMP
RegisterFilter(nsIProtocolProxyFilter * filter,uint32_t position)1476 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
1477 uint32_t position)
1478 {
1479 UnregisterFilter(filter); // remove this filter if we already have it
1480
1481 FilterLink *link = new FilterLink(position, filter);
1482 if (!link) {
1483 return NS_ERROR_OUT_OF_MEMORY;
1484 }
1485 return InsertFilterLink(link, position);
1486 }
1487
1488 NS_IMETHODIMP
RegisterChannelFilter(nsIProtocolProxyChannelFilter * channelFilter,uint32_t position)1489 nsProtocolProxyService::RegisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter,
1490 uint32_t position)
1491 {
1492 UnregisterChannelFilter(channelFilter); // remove this filter if we already have it
1493
1494 FilterLink *link = new FilterLink(position, channelFilter);
1495 if (!link) {
1496 return NS_ERROR_OUT_OF_MEMORY;
1497 }
1498 return InsertFilterLink(link, position);
1499 }
1500
1501 nsresult
RemoveFilterLink(nsISupports * givenObject)1502 nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject)
1503 {
1504 FilterLink *last = nullptr;
1505 for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1506 nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
1507 nsCOMPtr<nsISupports> object2 = do_QueryInterface(iter->channelFilter);
1508 if (object == givenObject || object2 == givenObject) {
1509 if (last)
1510 last->next = iter->next;
1511 else
1512 mFilters = iter->next;
1513 iter->next = nullptr;
1514 delete iter;
1515 return NS_OK;
1516 }
1517 last = iter;
1518 }
1519
1520 // No need to throw an exception in this case.
1521 return NS_OK;
1522 }
1523
1524 NS_IMETHODIMP
UnregisterFilter(nsIProtocolProxyFilter * filter)1525 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter) {
1526 // QI to nsISupports so we can safely test object identity.
1527 nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
1528 return RemoveFilterLink(givenObject);
1529 }
1530
1531 NS_IMETHODIMP
UnregisterChannelFilter(nsIProtocolProxyChannelFilter * channelFilter)1532 nsProtocolProxyService::UnregisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter) {
1533 // QI to nsISupports so we can safely test object identity.
1534 nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
1535 return RemoveFilterLink(givenObject);
1536 }
1537
1538 NS_IMETHODIMP
GetProxyConfigType(uint32_t * aProxyConfigType)1539 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType)
1540 {
1541 *aProxyConfigType = mProxyConfig;
1542 return NS_OK;
1543 }
1544
1545 void
LoadHostFilters(const nsACString & aFilters)1546 nsProtocolProxyService::LoadHostFilters(const nsACString& aFilters)
1547 {
1548 // check to see the owners flag? /!?/ TODO
1549 if (mHostFiltersArray.Length() > 0) {
1550 mHostFiltersArray.Clear();
1551 }
1552
1553 if (aFilters.IsEmpty()) {
1554 return;
1555 }
1556
1557 //
1558 // filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
1559 // filters = filter *( "," LWS filter)
1560 //
1561 // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref string
1562 mFilterLocalHosts = false;
1563
1564 mozilla::Tokenizer t(aFilters);
1565 mozilla::Tokenizer::Token token;
1566 bool eof = false;
1567 // while (*filters) {
1568 while (!eof) {
1569 // skip over spaces and ,
1570 t.SkipWhites();
1571 while (t.CheckChar(',')) {
1572 t.SkipWhites();
1573 }
1574
1575 nsAutoCString portStr;
1576 nsAutoCString hostStr;
1577 nsAutoCString maskStr;
1578 t.Record();
1579
1580 bool parsingIPv6 = false;
1581 bool parsingPort = false;
1582 bool parsingMask = false;
1583 while (t.Next(token)) {
1584 if (token.Equals(mozilla::Tokenizer::Token::EndOfFile())) {
1585 eof = true;
1586 break;
1587 }
1588 if (token.Equals(mozilla::Tokenizer::Token::Char(',')) ||
1589 token.Type() == mozilla::Tokenizer::TOKEN_WS) {
1590 break;
1591 }
1592
1593 if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
1594 parsingIPv6 = true;
1595 continue;
1596 }
1597
1598 if (!parsingIPv6 && token.Equals(mozilla::Tokenizer::Token::Char(':'))) {
1599 // Port is starting. Claim the previous as host.
1600 if (parsingMask) {
1601 t.Claim(maskStr);
1602 } else {
1603 t.Claim(hostStr);
1604 }
1605 t.Record();
1606 parsingPort = true;
1607 continue;
1608 } else if (token.Equals(mozilla::Tokenizer::Token::Char('/'))) {
1609 t.Claim(hostStr);
1610 t.Record();
1611 parsingMask = true;
1612 continue;
1613 } else if (token.Equals(mozilla::Tokenizer::Token::Char(']'))) {
1614 parsingIPv6 = false;
1615 continue;
1616 }
1617 }
1618 if (!parsingPort && !parsingMask) {
1619 t.Claim(hostStr);
1620 } else if (parsingPort) {
1621 t.Claim(portStr);
1622 } else if (parsingMask) {
1623 t.Claim(maskStr);
1624 } else {
1625 NS_WARNING("Could not parse this rule");
1626 continue;
1627 }
1628
1629 if (hostStr.IsEmpty()) {
1630 continue;
1631 }
1632
1633 // If the current host filter is "<local>", then all local (i.e.
1634 // no dots in the hostname) hosts should bypass the proxy
1635 if (hostStr.EqualsIgnoreCase("<local>")) {
1636 mFilterLocalHosts = true;
1637 LOG(("loaded filter for local hosts "
1638 "(plain host names, no dots)\n"));
1639 // Continue to next host filter;
1640 continue;
1641 }
1642
1643 // For all other host filters, create HostInfo object and add to list
1644 HostInfo *hinfo = new HostInfo();
1645 nsresult rv = NS_OK;
1646
1647 int32_t port = portStr.ToInteger(&rv);
1648 if (NS_FAILED(rv)) {
1649 port = 0;
1650 }
1651 hinfo->port = port;
1652
1653 int32_t maskLen = maskStr.ToInteger(&rv);
1654 if (NS_FAILED(rv)) {
1655 maskLen = 128;
1656 }
1657
1658 // PR_StringToNetAddr can't parse brackets enclosed IPv6
1659 nsAutoCString addrString = hostStr;
1660 if (hostStr.First() == '[' && hostStr.Last() == ']') {
1661 addrString = Substring(hostStr, 1, hostStr.Length() - 2);
1662 }
1663
1664 PRNetAddr addr;
1665 if (PR_StringToNetAddr(addrString.get(), &addr) == PR_SUCCESS) {
1666 hinfo->is_ipaddr = true;
1667 hinfo->ip.family = PR_AF_INET6; // we always store address as IPv6
1668 hinfo->ip.mask_len = maskLen;
1669
1670 if (hinfo->ip.mask_len == 0) {
1671 NS_WARNING("invalid mask");
1672 goto loser;
1673 }
1674
1675 if (addr.raw.family == PR_AF_INET) {
1676 // convert to IPv4-mapped address
1677 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
1678 // adjust mask_len accordingly
1679 if (hinfo->ip.mask_len <= 32)
1680 hinfo->ip.mask_len += 96;
1681 }
1682 else if (addr.raw.family == PR_AF_INET6) {
1683 // copy the address
1684 memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1685 }
1686 else {
1687 NS_WARNING("unknown address family");
1688 goto loser;
1689 }
1690
1691 // apply mask to IPv6 address
1692 proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
1693 }
1694 else {
1695 nsAutoCString host;
1696 if (hostStr.First() == '*') {
1697 host = Substring(hostStr, 1);
1698 } else {
1699 host = hostStr;
1700 }
1701
1702 if (host.IsEmpty()) {
1703 hinfo->name.host = nullptr;
1704 goto loser;
1705 }
1706
1707 hinfo->name.host_len = host.Length();
1708
1709 hinfo->is_ipaddr = false;
1710 hinfo->name.host = ToNewCString(host);
1711
1712 if (!hinfo->name.host)
1713 goto loser;
1714 }
1715
1716 //#define DEBUG_DUMP_FILTERS
1717 #ifdef DEBUG_DUMP_FILTERS
1718 printf("loaded filter[%zu]:\n", mHostFiltersArray.Length());
1719 printf(" is_ipaddr = %u\n", hinfo->is_ipaddr);
1720 printf(" port = %u\n", hinfo->port);
1721 printf(" host = %s\n", hostStr.get());
1722 if (hinfo->is_ipaddr) {
1723 printf(" ip.family = %x\n", hinfo->ip.family);
1724 printf(" ip.mask_len = %u\n", hinfo->ip.mask_len);
1725
1726 PRNetAddr netAddr;
1727 PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
1728 memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
1729
1730 char buf[256];
1731 PR_NetAddrToString(&netAddr, buf, sizeof(buf));
1732
1733 printf(" ip.addr = %s\n", buf);
1734 }
1735 else {
1736 printf(" name.host = %s\n", hinfo->name.host);
1737 }
1738 #endif
1739
1740 mHostFiltersArray.AppendElement(hinfo);
1741 hinfo = nullptr;
1742 loser:
1743 delete hinfo;
1744 }
1745 }
1746
1747 nsresult
GetProtocolInfo(nsIURI * uri,nsProtocolInfo * info)1748 nsProtocolProxyService::GetProtocolInfo(nsIURI *uri, nsProtocolInfo *info)
1749 {
1750 NS_PRECONDITION(uri, "URI is null");
1751 NS_PRECONDITION(info, "info is null");
1752
1753 nsresult rv;
1754
1755 rv = uri->GetScheme(info->scheme);
1756 if (NS_FAILED(rv))
1757 return rv;
1758
1759 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
1760 if (NS_FAILED(rv))
1761 return rv;
1762
1763 nsCOMPtr<nsIProtocolHandler> handler;
1764 rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
1765 if (NS_FAILED(rv))
1766 return rv;
1767
1768 rv = handler->DoGetProtocolFlags(uri, &info->flags);
1769 if (NS_FAILED(rv))
1770 return rv;
1771
1772 rv = handler->GetDefaultPort(&info->defaultPort);
1773 return rv;
1774 }
1775
1776 nsresult
NewProxyInfo_Internal(const char * aType,const nsACString & aHost,int32_t aPort,const nsACString & aUsername,const nsACString & aPassword,uint32_t aFlags,uint32_t aFailoverTimeout,nsIProxyInfo * aFailoverProxy,uint32_t aResolveFlags,nsIProxyInfo ** aResult)1777 nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
1778 const nsACString &aHost,
1779 int32_t aPort,
1780 const nsACString &aUsername,
1781 const nsACString &aPassword,
1782 uint32_t aFlags,
1783 uint32_t aFailoverTimeout,
1784 nsIProxyInfo *aFailoverProxy,
1785 uint32_t aResolveFlags,
1786 nsIProxyInfo **aResult)
1787 {
1788 if (aPort <= 0)
1789 aPort = -1;
1790
1791 nsCOMPtr<nsProxyInfo> failover;
1792 if (aFailoverProxy) {
1793 failover = do_QueryInterface(aFailoverProxy);
1794 NS_ENSURE_ARG(failover);
1795 }
1796
1797 nsProxyInfo *proxyInfo = new nsProxyInfo();
1798 if (!proxyInfo)
1799 return NS_ERROR_OUT_OF_MEMORY;
1800
1801 proxyInfo->mType = aType;
1802 proxyInfo->mHost = aHost;
1803 proxyInfo->mPort = aPort;
1804 proxyInfo->mUsername = aUsername;
1805 proxyInfo->mPassword = aPassword;
1806 proxyInfo->mFlags = aFlags;
1807 proxyInfo->mResolveFlags = aResolveFlags;
1808 proxyInfo->mTimeout = aFailoverTimeout == UINT32_MAX
1809 ? mFailedProxyTimeout : aFailoverTimeout;
1810 failover.swap(proxyInfo->mNext);
1811
1812 NS_ADDREF(*aResult = proxyInfo);
1813 return NS_OK;
1814 }
1815
1816 nsresult
Resolve_Internal(nsIChannel * channel,const nsProtocolInfo & info,uint32_t flags,bool * usePACThread,nsIProxyInfo ** result)1817 nsProtocolProxyService::Resolve_Internal(nsIChannel *channel,
1818 const nsProtocolInfo &info,
1819 uint32_t flags,
1820 bool *usePACThread,
1821 nsIProxyInfo **result)
1822 {
1823 NS_ENSURE_ARG_POINTER(channel);
1824 nsresult rv = SetupPACThread();
1825 if (NS_FAILED(rv))
1826 return rv;
1827
1828 *usePACThread = false;
1829 *result = nullptr;
1830
1831 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
1832 return NS_OK; // Can't proxy this (filters may not override)
1833
1834 nsCOMPtr<nsIURI> uri;
1835 rv = GetProxyURI(channel, getter_AddRefs(uri));
1836 if (NS_FAILED(rv)) return rv;
1837
1838 // See bug #586908.
1839 // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
1840 // here means that we will not use a proxy for this connection.
1841 if (mPACMan && mPACMan->IsPACURI(uri))
1842 return NS_OK;
1843
1844 bool mainThreadOnly;
1845 if (mSystemProxySettings &&
1846 mProxyConfig == PROXYCONFIG_SYSTEM &&
1847 NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
1848 !mainThreadOnly) {
1849 *usePACThread = true;
1850 return NS_OK;
1851 }
1852
1853 if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) {
1854 // If the system proxy setting implementation is not threadsafe (e.g
1855 // linux gconf), we'll do it inline here. Such implementations promise
1856 // not to block
1857
1858 nsAutoCString PACURI;
1859 nsAutoCString pacString;
1860
1861 if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
1862 !PACURI.IsEmpty()) {
1863 // There is a PAC URI configured. If it is unchanged, then
1864 // just execute the PAC thread. If it is changed then load
1865 // the new value
1866
1867 if (mPACMan && mPACMan->IsPACURI(PACURI)) {
1868 // unchanged
1869 *usePACThread = true;
1870 return NS_OK;
1871 }
1872
1873 ConfigureFromPAC(PACURI, false);
1874 return NS_OK;
1875 }
1876
1877 nsAutoCString spec;
1878 nsAutoCString host;
1879 nsAutoCString scheme;
1880 int32_t port = -1;
1881
1882 uri->GetAsciiSpec(spec);
1883 uri->GetAsciiHost(host);
1884 uri->GetScheme(scheme);
1885 uri->GetPort(&port);
1886
1887 // now try the system proxy settings for this particular url
1888 if (NS_SUCCEEDED(mSystemProxySettings->
1889 GetProxyForURI(spec, scheme, host, port,
1890 pacString))) {
1891 ProcessPACString(pacString, 0, result);
1892 return NS_OK;
1893 }
1894 }
1895
1896 // if proxies are enabled and this host:port combo is supposed to use a
1897 // proxy, check for a proxy.
1898 if (mProxyConfig == PROXYCONFIG_DIRECT ||
1899 (mProxyConfig == PROXYCONFIG_MANUAL &&
1900 !CanUseProxy(uri, info.defaultPort)))
1901 return NS_OK;
1902
1903 // Proxy auto config magic...
1904 if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD) {
1905 // Do not query PAC now.
1906 *usePACThread = true;
1907 return NS_OK;
1908 }
1909
1910 // If we aren't in manual proxy configuration mode then we don't
1911 // want to honor any manual specific prefs that might be still set
1912 if (mProxyConfig != PROXYCONFIG_MANUAL)
1913 return NS_OK;
1914
1915 // proxy info values for manual configuration mode
1916 const char *type = nullptr;
1917 const nsACString *host = nullptr;
1918 int32_t port = -1;
1919
1920 uint32_t proxyFlags = 0;
1921
1922 if ((flags & RESOLVE_PREFER_SOCKS_PROXY) &&
1923 !mSOCKSProxyTarget.IsEmpty() &&
1924 (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
1925 host = &mSOCKSProxyTarget;
1926 if (mSOCKSProxyVersion == 4)
1927 type = kProxyType_SOCKS4;
1928 else
1929 type = kProxyType_SOCKS;
1930 port = mSOCKSProxyPort;
1931 if (mSOCKSProxyRemoteDNS)
1932 proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1933 }
1934 else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
1935 !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
1936 host = &mHTTPSProxyHost;
1937 type = kProxyType_HTTP;
1938 port = mHTTPSProxyPort;
1939 }
1940 else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
1941 ((flags & RESOLVE_IGNORE_URI_SCHEME) ||
1942 info.scheme.EqualsLiteral("http"))) {
1943 host = &mHTTPProxyHost;
1944 type = kProxyType_HTTP;
1945 port = mHTTPProxyPort;
1946 }
1947 else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
1948 !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
1949 info.scheme.EqualsLiteral("https")) {
1950 host = &mHTTPSProxyHost;
1951 type = kProxyType_HTTP;
1952 port = mHTTPSProxyPort;
1953 }
1954 else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 &&
1955 !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
1956 info.scheme.EqualsLiteral("ftp")) {
1957 host = &mFTPProxyHost;
1958 type = kProxyType_HTTP;
1959 port = mFTPProxyPort;
1960 }
1961 else if (!mSOCKSProxyTarget.IsEmpty() &&
1962 (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
1963 host = &mSOCKSProxyTarget;
1964 if (mSOCKSProxyVersion == 4)
1965 type = kProxyType_SOCKS4;
1966 else
1967 type = kProxyType_SOCKS;
1968 port = mSOCKSProxyPort;
1969 if (mSOCKSProxyRemoteDNS)
1970 proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1971 }
1972
1973 if (type) {
1974 rv = NewProxyInfo_Internal(type, *host, port,
1975 EmptyCString(), EmptyCString(),
1976 proxyFlags, UINT32_MAX, nullptr, flags,
1977 result);
1978 if (NS_FAILED(rv))
1979 return rv;
1980 }
1981
1982 return NS_OK;
1983 }
1984
1985 void
MaybeDisableDNSPrefetch(nsIProxyInfo * aProxy)1986 nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo *aProxy)
1987 {
1988 // Disable Prefetch in the DNS service if a proxy is in use.
1989 if (!aProxy)
1990 return;
1991
1992 nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
1993 if (!pi ||
1994 !pi->mType ||
1995 pi->mType == kProxyType_DIRECT)
1996 return;
1997
1998 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
1999 if (!dns)
2000 return;
2001 nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
2002 if (!pdns)
2003 return;
2004
2005 // We lose the prefetch optimization for the life of the dns service.
2006 pdns->SetPrefetchEnabled(false);
2007 }
2008
2009 void
ApplyFilters(nsIChannel * channel,const nsProtocolInfo & info,nsIProxyInfo ** list)2010 nsProtocolProxyService::ApplyFilters(nsIChannel *channel,
2011 const nsProtocolInfo &info,
2012 nsIProxyInfo **list)
2013 {
2014 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
2015 return;
2016
2017 // We prune the proxy list prior to invoking each filter. This may be
2018 // somewhat inefficient, but it seems like a good idea since we want each
2019 // filter to "see" a valid proxy list.
2020
2021 nsCOMPtr<nsIProxyInfo> result;
2022
2023 for (FilterLink *iter = mFilters; iter; iter = iter->next) {
2024 PruneProxyInfo(info, list);
2025 nsresult rv = NS_OK;
2026 if (iter->filter) {
2027 nsCOMPtr<nsIURI> uri;
2028 rv = GetProxyURI(channel, getter_AddRefs(uri));
2029 if (uri) {
2030 rv = iter->filter->ApplyFilter(this, uri, *list,
2031 getter_AddRefs(result));
2032 }
2033 } else if (iter->channelFilter) {
2034 rv = iter->channelFilter->ApplyFilter(this, channel, *list,
2035 getter_AddRefs(result));
2036 }
2037 if (NS_FAILED(rv))
2038 continue;
2039 result.swap(*list);
2040 }
2041
2042 PruneProxyInfo(info, list);
2043 }
2044
2045 void
PruneProxyInfo(const nsProtocolInfo & info,nsIProxyInfo ** list)2046 nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
2047 nsIProxyInfo **list)
2048 {
2049 if (!*list)
2050 return;
2051 nsProxyInfo *head = nullptr;
2052 CallQueryInterface(*list, &head);
2053 if (!head) {
2054 NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo");
2055 return;
2056 }
2057 NS_RELEASE(*list);
2058
2059 // Pruning of disabled proxies works like this:
2060 // - If all proxies are disabled, return the full list
2061 // - Otherwise, remove the disabled proxies.
2062 //
2063 // Pruning of disallowed proxies works like this:
2064 // - If the protocol handler disallows the proxy, then we disallow it.
2065
2066 // Start by removing all disallowed proxies if required:
2067 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
2068 nsProxyInfo *last = nullptr, *iter = head;
2069 while (iter) {
2070 if ((iter->Type() == kProxyType_HTTP) ||
2071 (iter->Type() == kProxyType_HTTPS)) {
2072 // reject!
2073 if (last)
2074 last->mNext = iter->mNext;
2075 else
2076 head = iter->mNext;
2077 nsProxyInfo *next = iter->mNext;
2078 iter->mNext = nullptr;
2079 iter->Release();
2080 iter = next;
2081 } else {
2082 last = iter;
2083 iter = iter->mNext;
2084 }
2085 }
2086 if (!head)
2087 return;
2088 }
2089
2090 // Now, scan to see if all remaining proxies are disabled. If so, then
2091 // we'll just bail and return them all. Otherwise, we'll go and prune the
2092 // disabled ones.
2093
2094 bool allDisabled = true;
2095
2096 nsProxyInfo *iter;
2097 for (iter = head; iter; iter = iter->mNext) {
2098 if (!IsProxyDisabled(iter)) {
2099 allDisabled = false;
2100 break;
2101 }
2102 }
2103
2104 if (allDisabled)
2105 LOG(("All proxies are disabled, so trying all again"));
2106 else {
2107 // remove any disabled proxies.
2108 nsProxyInfo *last = nullptr;
2109 for (iter = head; iter; ) {
2110 if (IsProxyDisabled(iter)) {
2111 // reject!
2112 nsProxyInfo *reject = iter;
2113
2114 iter = iter->mNext;
2115 if (last)
2116 last->mNext = iter;
2117 else
2118 head = iter;
2119
2120 reject->mNext = nullptr;
2121 NS_RELEASE(reject);
2122 continue;
2123 }
2124
2125 // since we are about to use this proxy, make sure it is not on
2126 // the disabled proxy list. we'll add it back to that list if
2127 // we have to (in GetFailoverForProxy).
2128 //
2129 // XXX(darin): It might be better to do this as a final pass.
2130 //
2131 EnableProxy(iter);
2132
2133 last = iter;
2134 iter = iter->mNext;
2135 }
2136 }
2137
2138 // if only DIRECT was specified then return no proxy info, and we're done.
2139 if (head && !head->mNext && head->mType == kProxyType_DIRECT)
2140 NS_RELEASE(head);
2141
2142 *list = head; // Transfer ownership
2143 }
2144
2145 } // namespace net
2146 } // namespace mozilla
2147