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