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