Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / netwerk / base / nsIOService.cpp
blobc37b4a4639cce8f6bcd48cdc8a2eb84b1e531276
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=4 sw=2 cindent 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/. */
7 #include "mozilla/DebugOnly.h"
9 #include "nsIOService.h"
10 #include "nsIProtocolHandler.h"
11 #include "nsIFileProtocolHandler.h"
12 #include "nscore.h"
13 #include "nsIURI.h"
14 #include "prprf.h"
15 #include "netCore.h"
16 #include "nsIObserverService.h"
17 #include "nsXPCOM.h"
18 #include "nsIProxiedProtocolHandler.h"
19 #include "nsIProxyInfo.h"
20 #include "nsDNSService2.h"
21 #include "nsEscape.h"
22 #include "nsNetUtil.h"
23 #include "nsNetCID.h"
24 #include "nsCRT.h"
25 #include "nsSimpleNestedURI.h"
26 #include "nsSocketTransport2.h"
27 #include "nsTArray.h"
28 #include "nsIConsoleService.h"
29 #include "nsIUploadChannel2.h"
30 #include "nsXULAppAPI.h"
31 #include "nsIProtocolProxyCallback.h"
32 #include "nsICancelable.h"
33 #include "nsINetworkLinkService.h"
34 #include "nsAsyncRedirectVerifyHelper.h"
35 #include "nsURLHelper.h"
36 #include "nsIProtocolProxyService2.h"
37 #include "MainThreadUtils.h"
38 #include "nsINode.h"
39 #include "nsIWebTransport.h"
40 #include "nsIWidget.h"
41 #include "nsThreadUtils.h"
42 #include "WebTransportSessionProxy.h"
43 #include "mozilla/AppShutdown.h"
44 #include "mozilla/Components.h"
45 #include "mozilla/LoadInfo.h"
46 #include "mozilla/net/NeckoCommon.h"
47 #include "mozilla/Services.h"
48 #include "mozilla/Telemetry.h"
49 #include "mozilla/net/DNS.h"
50 #include "mozilla/ipc/URIUtils.h"
51 #include "mozilla/net/NeckoChild.h"
52 #include "mozilla/net/NeckoParent.h"
53 #include "mozilla/dom/ClientInfo.h"
54 #include "mozilla/dom/ContentParent.h"
55 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
56 #include "mozilla/dom/ServiceWorkerDescriptor.h"
57 #include "mozilla/net/CaptivePortalService.h"
58 #include "mozilla/net/NetworkConnectivityService.h"
59 #include "mozilla/net/SocketProcessHost.h"
60 #include "mozilla/net/SocketProcessParent.h"
61 #include "mozilla/net/SSLTokensCache.h"
62 #include "mozilla/StoragePrincipalHelper.h"
63 #include "mozilla/Unused.h"
64 #include "nsContentSecurityManager.h"
65 #include "nsContentUtils.h"
66 #include "mozilla/StaticPrefs_network.h"
67 #include "mozilla/StaticPrefs_security.h"
68 #include "mozilla/glean/NetwerkMetrics.h"
69 #include "nsNSSComponent.h"
70 #include "IPv4Parser.h"
71 #include "ssl.h"
72 #include "StaticComponents.h"
74 #ifdef MOZ_WIDGET_ANDROID
75 # include <regex>
76 # include "AndroidBridge.h"
77 # include "mozilla/java/GeckoAppShellWrappers.h"
78 # include "mozilla/jni/Utils.h"
79 #endif
81 namespace mozilla {
82 namespace net {
84 using mozilla::Maybe;
85 using mozilla::dom::ClientInfo;
86 using mozilla::dom::ServiceWorkerDescriptor;
88 #define PORT_PREF_PREFIX "network.security.ports."
89 #define PORT_PREF(x) PORT_PREF_PREFIX x
90 #define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status"
92 // Nb: these have been misnomers since bug 715770 removed the buffer cache.
93 // "network.segment.count" and "network.segment.size" would be better names,
94 // but the old names are still used to preserve backward compatibility.
95 #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count"
96 #define NECKO_BUFFER_CACHE_SIZE_PREF "network.buffer.cache.size"
97 #define NETWORK_CAPTIVE_PORTAL_PREF "network.captive-portal-service.enabled"
98 #define WEBRTC_PREF_PREFIX "media.peerconnection."
99 #define NETWORK_DNS_PREF "network.dns."
100 #define FORCE_EXTERNAL_PREF_PREFIX "network.protocol-handler.external."
102 nsIOService* gIOService;
103 static bool gHasWarnedUploadChannel2;
104 static bool gCaptivePortalEnabled = false;
105 static LazyLogModule gIOServiceLog("nsIOService");
106 #undef LOG
107 #define LOG(args) MOZ_LOG(gIOServiceLog, LogLevel::Debug, args)
109 // A general port blacklist. Connections to these ports will not be allowed
110 // unless the protocol overrides.
112 // This list is to be kept in sync with "bad ports" as defined in the
113 // WHATWG Fetch standard at <https://fetch.spec.whatwg.org/#port-blocking>
115 int16_t gBadPortList[] = {
116 1, // tcpmux
117 7, // echo
118 9, // discard
119 11, // systat
120 13, // daytime
121 15, // netstat
122 17, // qotd
123 19, // chargen
124 20, // ftp-data
125 21, // ftp
126 22, // ssh
127 23, // telnet
128 25, // smtp
129 37, // time
130 42, // name
131 43, // nicname
132 53, // domain
133 69, // tftp
134 77, // priv-rjs
135 79, // finger
136 87, // ttylink
137 95, // supdup
138 101, // hostriame
139 102, // iso-tsap
140 103, // gppitnp
141 104, // acr-nema
142 109, // pop2
143 110, // pop3
144 111, // sunrpc
145 113, // auth
146 115, // sftp
147 117, // uucp-path
148 119, // nntp
149 123, // ntp
150 135, // loc-srv / epmap
151 137, // netbios
152 139, // netbios
153 143, // imap2
154 161, // snmp
155 179, // bgp
156 389, // ldap
157 427, // afp (alternate)
158 465, // smtp (alternate)
159 512, // print / exec
160 513, // login
161 514, // shell
162 515, // printer
163 526, // tempo
164 530, // courier
165 531, // chat
166 532, // netnews
167 540, // uucp
168 548, // afp
169 554, // rtsp
170 556, // remotefs
171 563, // nntp+ssl
172 587, // smtp (outgoing)
173 601, // syslog-conn
174 636, // ldap+ssl
175 989, // ftps-data
176 990, // ftps
177 993, // imap+ssl
178 995, // pop3+ssl
179 1719, // h323gatestat
180 1720, // h323hostcall
181 1723, // pptp
182 2049, // nfs
183 3659, // apple-sasl
184 4045, // lockd
185 4190, // sieve
186 5060, // sip
187 5061, // sips
188 6000, // x11
189 6566, // sane-port
190 6665, // irc (alternate)
191 6666, // irc (alternate)
192 6667, // irc (default)
193 6668, // irc (alternate)
194 6669, // irc (alternate)
195 6679, // osaut
196 6697, // irc+tls
197 10080, // amanda
198 0, // Sentinel value: This MUST be zero
201 static const char kProfileChangeNetTeardownTopic[] =
202 "profile-change-net-teardown";
203 static const char kProfileChangeNetRestoreTopic[] =
204 "profile-change-net-restore";
205 static const char kProfileDoChange[] = "profile-do-change";
207 // Necko buffer defaults
208 uint32_t nsIOService::gDefaultSegmentSize = 4096;
209 uint32_t nsIOService::gDefaultSegmentCount = 24;
211 uint32_t nsIOService::sSocketProcessCrashedCount = 0;
213 ////////////////////////////////////////////////////////////////////////////////
215 nsIOService::nsIOService()
216 : mLastOfflineStateChange(PR_IntervalNow()),
217 mLastConnectivityChange(PR_IntervalNow()),
218 mLastNetworkLinkChange(PR_IntervalNow()) {}
220 static const char* gCallbackPrefs[] = {
221 PORT_PREF_PREFIX,
222 MANAGE_OFFLINE_STATUS_PREF,
223 NECKO_BUFFER_CACHE_COUNT_PREF,
224 NECKO_BUFFER_CACHE_SIZE_PREF,
225 NETWORK_CAPTIVE_PORTAL_PREF,
226 FORCE_EXTERNAL_PREF_PREFIX,
227 SIMPLE_URI_SCHEMES_PREF,
228 nullptr,
231 static const char* gCallbackPrefsForSocketProcess[] = {
232 WEBRTC_PREF_PREFIX,
233 NETWORK_DNS_PREF,
234 "network.send_ODA_to_content_directly",
235 "network.trr.",
236 "doh-rollout.",
237 "network.dns.disableIPv6",
238 "network.offline-mirrors-connectivity",
239 "network.disable-localhost-when-offline",
240 "network.proxy.parse_pac_on_socket_process",
241 "network.proxy.allow_hijacking_localhost",
242 "network.connectivity-service.",
243 "network.captive-portal-service.testMode",
244 "network.socket.ip_addr_any.disabled",
245 "network.socket.attach_mock_network_layer",
246 nullptr,
249 static const char* gCallbackSecurityPrefs[] = {
250 // Note the prefs listed below should be in sync with the code in
251 // HandleTLSPrefChange().
252 "security.tls.version.min",
253 "security.tls.version.max",
254 "security.tls.version.enable-deprecated",
255 "security.tls.hello_downgrade_check",
256 "security.ssl.require_safe_negotiation",
257 "security.ssl.enable_false_start",
258 "security.ssl.enable_alpn",
259 "security.tls.enable_0rtt_data",
260 "security.ssl.disable_session_identifiers",
261 "security.tls.enable_post_handshake_auth",
262 "security.tls.enable_delegated_credentials",
263 nullptr,
266 nsresult nsIOService::Init() {
267 SSLTokensCache::Init();
269 InitializeCaptivePortalService();
271 // setup our bad port list stuff
272 for (int i = 0; gBadPortList[i]; i++) {
273 // We can't be accessed by another thread yet
274 MOZ_PUSH_IGNORE_THREAD_SAFETY
275 mRestrictedPortList.AppendElement(gBadPortList[i]);
276 MOZ_POP_THREAD_SAFETY
279 // Further modifications to the port list come from prefs
280 Preferences::RegisterPrefixCallbacks(nsIOService::PrefsChanged,
281 gCallbackPrefs, this);
282 PrefsChanged();
284 mSocketProcessTopicBlockedList.Insert(
285 nsLiteralCString(NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID));
286 mSocketProcessTopicBlockedList.Insert(
287 nsLiteralCString(NS_XPCOM_SHUTDOWN_OBSERVER_ID));
288 mSocketProcessTopicBlockedList.Insert("xpcom-shutdown-threads"_ns);
289 mSocketProcessTopicBlockedList.Insert("profile-do-change"_ns);
290 mSocketProcessTopicBlockedList.Insert("network:socket-process-crashed"_ns);
292 // Register for profile change notifications
293 mObserverService = services::GetObserverService();
294 AddObserver(this, kProfileChangeNetTeardownTopic, true);
295 AddObserver(this, kProfileChangeNetRestoreTopic, true);
296 AddObserver(this, kProfileDoChange, true);
297 AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
298 AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
299 AddObserver(this, NS_NETWORK_ID_CHANGED_TOPIC, true);
300 AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
302 // Register observers for sending notifications to nsSocketTransportService
303 if (XRE_IsParentProcess()) {
304 AddObserver(this, "profile-initial-state", true);
305 AddObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC, true);
308 if (IsSocketProcessChild()) {
309 Preferences::RegisterCallbacks(nsIOService::OnTLSPrefChange,
310 gCallbackSecurityPrefs, this);
313 gIOService = this;
315 InitializeNetworkLinkService();
316 InitializeProtocolProxyService();
318 SetOffline(false);
320 return NS_OK;
323 NS_IMETHODIMP
324 nsIOService::AddObserver(nsIObserver* aObserver, const char* aTopic,
325 bool aOwnsWeak) {
326 if (!mObserverService) {
327 return NS_ERROR_FAILURE;
330 // Register for the origional observer.
331 nsresult rv = mObserverService->AddObserver(aObserver, aTopic, aOwnsWeak);
332 if (NS_FAILED(rv)) {
333 return rv;
336 if (!XRE_IsParentProcess()) {
337 return NS_OK;
340 nsAutoCString topic(aTopic);
341 // This happens when AddObserver() is called by nsIOService::Init(). We don't
342 // want to add nsIOService again.
343 if (SameCOMIdentity(aObserver, static_cast<nsIObserver*>(this))) {
344 mIOServiceTopicList.Insert(topic);
345 return NS_OK;
348 if (!UseSocketProcess()) {
349 return NS_OK;
352 if (mSocketProcessTopicBlockedList.Contains(topic)) {
353 return NS_ERROR_FAILURE;
356 // Avoid registering duplicate topics.
357 if (mObserverTopicForSocketProcess.Contains(topic)) {
358 return NS_ERROR_FAILURE;
361 mObserverTopicForSocketProcess.Insert(topic);
363 // Avoid registering duplicate topics.
364 if (mIOServiceTopicList.Contains(topic)) {
365 return NS_ERROR_FAILURE;
368 return mObserverService->AddObserver(this, aTopic, true);
371 NS_IMETHODIMP
372 nsIOService::RemoveObserver(nsIObserver* aObserver, const char* aTopic) {
373 return NS_ERROR_NOT_IMPLEMENTED;
376 NS_IMETHODIMP
377 nsIOService::EnumerateObservers(const char* aTopic,
378 nsISimpleEnumerator** anEnumerator) {
379 return NS_ERROR_NOT_IMPLEMENTED;
382 NS_IMETHODIMP nsIOService::NotifyObservers(nsISupports* aSubject,
383 const char* aTopic,
384 const char16_t* aSomeData) {
385 return NS_ERROR_NOT_IMPLEMENTED;
388 nsIOService::~nsIOService() {
389 if (gIOService) {
390 MOZ_ASSERT(gIOService == this);
391 gIOService = nullptr;
395 #ifdef MOZ_WIDGET_ANDROID
396 bool nsIOService::ShouldAddAdditionalSearchHeaders(nsIURI* aURI,
397 bool* aHeaderVal) {
398 if (!(mozilla::AndroidBridge::Bridge())) {
399 return false;
402 if (!aURI->SchemeIs("https")) {
403 return false;
406 // We need to improve below logic for matching google domains
407 // See Bug 1894642
408 // Is URI same as google ^https://www\\.google\\..+
409 nsAutoCString host;
410 aURI->GetHost(host);
411 LOG(("nsIOService::ShouldAddAdditionalSearchHeaders() checking host %s\n",
412 PromiseFlatCString(host).get()));
414 std::regex pattern("^www\\.google\\..+");
415 if (std::regex_match(host.get(), pattern)) {
416 LOG(("Google domain detected for host %s\n",
417 PromiseFlatCString(host).get()));
418 static bool ramAboveThreshold =
419 java::GeckoAppShell::IsDeviceRamThresholdOkay();
420 *aHeaderVal = ramAboveThreshold;
421 return true;
424 return false;
426 #endif
428 // static
429 void nsIOService::OnTLSPrefChange(const char* aPref, void* aSelf) {
430 MOZ_ASSERT(IsSocketProcessChild());
432 if (!EnsureNSSInitializedChromeOrContent()) {
433 LOG(("NSS not initialized."));
434 return;
437 nsAutoCString pref(aPref);
438 // The preferences listed in gCallbackSecurityPrefs need to be in sync with
439 // the code in HandleTLSPrefChange().
440 if (HandleTLSPrefChange(pref)) {
441 LOG(("HandleTLSPrefChange done"));
445 nsresult nsIOService::InitializeCaptivePortalService() {
446 if (XRE_GetProcessType() != GeckoProcessType_Default) {
447 // We only initalize a captive portal service in the main process
448 return NS_OK;
451 mCaptivePortalService = mozilla::components::CaptivePortal::Service();
452 if (mCaptivePortalService) {
453 static_cast<CaptivePortalService*>(mCaptivePortalService.get())
454 ->Initialize();
457 // Instantiate and initialize the service
458 RefPtr<NetworkConnectivityService> ncs =
459 NetworkConnectivityService::GetSingleton();
461 return NS_OK;
464 nsresult nsIOService::InitializeSocketTransportService() {
465 nsresult rv = NS_OK;
467 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
468 LOG(
469 ("nsIOService aborting InitializeSocketTransportService because of app "
470 "shutdown"));
471 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
474 if (!mSocketTransportService) {
475 mSocketTransportService =
476 mozilla::components::SocketTransport::Service(&rv);
477 if (NS_FAILED(rv)) {
478 NS_WARNING("failed to get socket transport service");
482 if (mSocketTransportService) {
483 rv = mSocketTransportService->Init();
484 NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
485 mSocketTransportService->SetOffline(false);
488 return rv;
491 nsresult nsIOService::InitializeNetworkLinkService() {
492 nsresult rv = NS_OK;
494 if (mNetworkLinkServiceInitialized) return rv;
496 if (!NS_IsMainThread()) {
497 NS_WARNING("Network link service should be created on main thread");
498 return NS_ERROR_FAILURE;
501 // go into managed mode if we can, and chrome process
502 if (!XRE_IsParentProcess()) {
503 return NS_ERROR_NOT_AVAILABLE;
506 mNetworkLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
508 if (mNetworkLinkService) {
509 mNetworkLinkServiceInitialized = true;
512 // After initializing the networkLinkService, query the connectivity state
513 OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN);
515 return rv;
518 nsresult nsIOService::InitializeProtocolProxyService() {
519 nsresult rv = NS_OK;
521 if (XRE_IsParentProcess()) {
522 // for early-initialization
523 Unused << mozilla::components::ProtocolProxy::Service(&rv);
526 return rv;
529 already_AddRefed<nsIOService> nsIOService::GetInstance() {
530 if (!gIOService) {
531 RefPtr<nsIOService> ios = new nsIOService();
532 if (NS_SUCCEEDED(ios->Init())) {
533 MOZ_ASSERT(gIOService == ios.get());
534 return ios.forget();
537 return do_AddRef(gIOService);
540 class SocketProcessListenerProxy : public SocketProcessHost::Listener {
541 public:
542 SocketProcessListenerProxy() = default;
543 void OnProcessLaunchComplete(SocketProcessHost* aHost, bool aSucceeded) {
544 if (!gIOService) {
545 return;
548 gIOService->OnProcessLaunchComplete(aHost, aSucceeded);
551 void OnProcessUnexpectedShutdown(SocketProcessHost* aHost) {
552 if (!gIOService) {
553 return;
556 gIOService->OnProcessUnexpectedShutdown(aHost);
560 // static
561 bool nsIOService::TooManySocketProcessCrash() {
562 return sSocketProcessCrashedCount >=
563 StaticPrefs::network_max_socket_process_failed_count();
566 // static
567 void nsIOService::IncreaseSocketProcessCrashCount() {
568 MOZ_ASSERT(IsNeckoChild());
569 sSocketProcessCrashedCount++;
572 nsresult nsIOService::LaunchSocketProcess() {
573 MOZ_ASSERT(NS_IsMainThread());
575 if (XRE_GetProcessType() != GeckoProcessType_Default) {
576 return NS_OK;
579 // We shouldn't launch socket prcess when shutdown begins.
580 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
581 return NS_OK;
584 if (mSocketProcess) {
585 return NS_OK;
588 if (PR_GetEnv("MOZ_DISABLE_SOCKET_PROCESS")) {
589 LOG(("nsIOService skipping LaunchSocketProcess because of the env"));
590 return NS_OK;
593 if (!StaticPrefs::network_process_enabled()) {
594 LOG(("nsIOService skipping LaunchSocketProcess because of the pref"));
595 return NS_OK;
598 Preferences::RegisterPrefixCallbacks(
599 nsIOService::NotifySocketProcessPrefsChanged,
600 gCallbackPrefsForSocketProcess, this);
602 // The subprocess is launched asynchronously, so we wait for a callback to
603 // acquire the IPDL actor.
604 mSocketProcess = new SocketProcessHost(new SocketProcessListenerProxy());
605 LOG(("nsIOService::LaunchSocketProcess"));
606 if (!mSocketProcess->Launch()) {
607 NS_WARNING("Failed to launch socket process!!");
608 DestroySocketProcess();
609 return NS_ERROR_FAILURE;
612 return NS_OK;
615 void nsIOService::DestroySocketProcess() {
616 LOG(("nsIOService::DestroySocketProcess"));
617 MOZ_ASSERT(NS_IsMainThread());
619 if (XRE_GetProcessType() != GeckoProcessType_Default || !mSocketProcess) {
620 return;
623 Preferences::UnregisterPrefixCallbacks(
624 nsIOService::NotifySocketProcessPrefsChanged,
625 gCallbackPrefsForSocketProcess, this);
627 mSocketProcess->Shutdown();
628 mSocketProcess = nullptr;
631 bool nsIOService::SocketProcessReady() {
632 return mSocketProcess && mSocketProcess->IsConnected();
635 static bool sUseSocketProcess = false;
636 static bool sUseSocketProcessChecked = false;
638 // static
639 bool nsIOService::UseSocketProcess(bool aCheckAgain) {
640 if (sUseSocketProcessChecked && !aCheckAgain) {
641 return sUseSocketProcess;
644 sUseSocketProcessChecked = true;
645 sUseSocketProcess = false;
647 if (PR_GetEnv("MOZ_DISABLE_SOCKET_PROCESS")) {
648 return sUseSocketProcess;
651 if (TooManySocketProcessCrash()) {
652 LOG(("TooManySocketProcessCrash"));
653 return sUseSocketProcess;
656 if (PR_GetEnv("MOZ_FORCE_USE_SOCKET_PROCESS")) {
657 sUseSocketProcess = true;
658 return sUseSocketProcess;
661 if (StaticPrefs::network_process_enabled()) {
662 sUseSocketProcess =
663 StaticPrefs::network_http_network_access_on_socket_process_enabled();
665 return sUseSocketProcess;
668 // static
669 void nsIOService::NotifySocketProcessPrefsChanged(const char* aName,
670 void* aSelf) {
671 static_cast<nsIOService*>(aSelf)->NotifySocketProcessPrefsChanged(aName);
674 void nsIOService::NotifySocketProcessPrefsChanged(const char* aName) {
675 MOZ_ASSERT(NS_IsMainThread());
677 if (!XRE_IsParentProcess()) {
678 return;
681 if (!StaticPrefs::network_process_enabled()) {
682 return;
685 dom::Pref pref(nsCString(aName), /* isLocked */ false,
686 /* isSanitized */ false, Nothing(), Nothing());
688 Preferences::GetPreference(&pref, GeckoProcessType_Socket,
689 /* remoteType */ ""_ns);
690 auto sendPrefUpdate = [pref]() {
691 Unused << gIOService->mSocketProcess->GetActor()->SendPreferenceUpdate(
692 pref);
694 CallOrWaitForSocketProcess(sendPrefUpdate);
697 void nsIOService::OnProcessLaunchComplete(SocketProcessHost* aHost,
698 bool aSucceeded) {
699 MOZ_ASSERT(NS_IsMainThread());
701 LOG(("nsIOService::OnProcessLaunchComplete aSucceeded=%d\n", aSucceeded));
703 mSocketProcessLaunchComplete = aSucceeded;
705 if (mShutdown || !SocketProcessReady() || !aSucceeded) {
706 mPendingEvents.Clear();
707 return;
710 if (!mPendingEvents.IsEmpty()) {
711 nsTArray<std::function<void()>> pendingEvents = std::move(mPendingEvents);
712 for (auto& func : pendingEvents) {
713 func();
718 void nsIOService::CallOrWaitForSocketProcess(
719 const std::function<void()>& aFunc) {
720 MOZ_ASSERT(NS_IsMainThread());
721 if (IsSocketProcessLaunchComplete() && SocketProcessReady()) {
722 aFunc();
723 } else {
724 mPendingEvents.AppendElement(aFunc); // infallible
725 LaunchSocketProcess();
729 int32_t nsIOService::SocketProcessPid() {
730 if (!mSocketProcess) {
731 return 0;
733 if (SocketProcessParent* actor = mSocketProcess->GetActor()) {
734 return (int32_t)actor->OtherPid();
736 return 0;
739 bool nsIOService::IsSocketProcessLaunchComplete() {
740 MOZ_ASSERT(NS_IsMainThread());
741 return mSocketProcessLaunchComplete;
744 void nsIOService::OnProcessUnexpectedShutdown(SocketProcessHost* aHost) {
745 MOZ_ASSERT(NS_IsMainThread());
747 LOG(("nsIOService::OnProcessUnexpectedShutdown\n"));
748 DestroySocketProcess();
749 mPendingEvents.Clear();
751 // Nothing to do if socket process was not used before.
752 if (!UseSocketProcess()) {
753 return;
756 sSocketProcessCrashedCount++;
757 if (TooManySocketProcessCrash()) {
758 sUseSocketProcessChecked = false;
759 DNSServiceWrapper::SwitchToBackupDNSService();
762 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
763 if (observerService) {
764 (void)observerService->NotifyObservers(
765 nullptr, "network:socket-process-crashed", nullptr);
768 // UseSocketProcess() could return false if we have too many crashes, so we
769 // should call it again.
770 if (UseSocketProcess()) {
771 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
772 NewRunnableMethod("nsIOService::LaunchSocketProcess", this,
773 &nsIOService::LaunchSocketProcess)));
777 RefPtr<MemoryReportingProcess> nsIOService::GetSocketProcessMemoryReporter() {
778 // Check the prefs here again, since we don't want to create
779 // SocketProcessMemoryReporter for some tests.
780 if (!StaticPrefs::network_process_enabled() || !SocketProcessReady()) {
781 return nullptr;
784 return new SocketProcessMemoryReporter();
787 NS_IMETHODIMP
788 nsIOService::SocketProcessTelemetryPing() {
789 CallOrWaitForSocketProcess([]() {
790 Unused << gIOService->mSocketProcess->GetActor()
791 ->SendSocketProcessTelemetryPing();
793 return NS_OK;
796 NS_IMPL_ISUPPORTS(nsIOService, nsIIOService, nsINetUtil, nsISpeculativeConnect,
797 nsIObserver, nsIIOServiceInternal, nsISupportsWeakReference,
798 nsIObserverService)
800 ////////////////////////////////////////////////////////////////////////////////
802 nsresult nsIOService::RecheckCaptivePortal() {
803 MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
804 if (!mCaptivePortalService) {
805 return NS_OK;
807 nsCOMPtr<nsIRunnable> task = NewRunnableMethod(
808 "nsIOService::RecheckCaptivePortal", mCaptivePortalService,
809 &nsICaptivePortalService::RecheckCaptivePortal);
810 return NS_DispatchToMainThread(task);
813 nsresult nsIOService::RecheckCaptivePortalIfLocalRedirect(nsIChannel* newChan) {
814 nsresult rv;
816 if (!mCaptivePortalService) {
817 return NS_OK;
820 nsCOMPtr<nsIURI> uri;
821 rv = newChan->GetURI(getter_AddRefs(uri));
822 if (NS_FAILED(rv)) {
823 return rv;
826 nsCString host;
827 rv = uri->GetHost(host);
828 if (NS_FAILED(rv)) {
829 return rv;
832 NetAddr addr;
833 // If the redirect wasn't to an IP literal, so there's probably no need
834 // to trigger the captive portal detection right now. It can wait.
835 if (NS_SUCCEEDED(addr.InitFromString(host)) && addr.IsIPAddrLocal()) {
836 RecheckCaptivePortal();
839 return NS_OK;
842 nsresult nsIOService::AsyncOnChannelRedirect(
843 nsIChannel* oldChan, nsIChannel* newChan, uint32_t flags,
844 nsAsyncRedirectVerifyHelper* helper) {
845 // If a redirect to a local network address occurs, then chances are we
846 // are in a captive portal, so we trigger a recheck.
847 RecheckCaptivePortalIfLocalRedirect(newChan);
849 // This is silly. I wish there was a simpler way to get at the global
850 // reference of the contentSecurityManager. But it lives in the XPCOM
851 // service registry.
852 nsCOMPtr<nsIChannelEventSink> sink;
853 sink = mozilla::components::ContentSecurityManager::Service();
854 if (sink) {
855 nsresult rv =
856 helper->DelegateOnChannelRedirect(sink, oldChan, newChan, flags);
857 if (NS_FAILED(rv)) return rv;
860 // Finally, our category
861 nsCOMArray<nsIChannelEventSink> entries;
862 mChannelEventSinks.GetEntries(entries);
863 int32_t len = entries.Count();
864 for (int32_t i = 0; i < len; ++i) {
865 nsresult rv =
866 helper->DelegateOnChannelRedirect(entries[i], oldChan, newChan, flags);
867 if (NS_FAILED(rv)) return rv;
870 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(oldChan));
872 // Collect the redirection from HTTP(S) only.
873 if (httpChan) {
874 MOZ_ASSERT(NS_IsMainThread());
875 nsCOMPtr<nsIURI> newURI;
876 newChan->GetURI(getter_AddRefs(newURI));
877 MOZ_ASSERT(newURI);
879 nsAutoCString scheme;
880 newURI->GetScheme(scheme);
881 MOZ_ASSERT(!scheme.IsEmpty());
883 if (oldChan->IsDocument()) {
884 Telemetry::AccumulateCategoricalKeyed(
885 scheme, Telemetry::LABELS_NETWORK_HTTP_REDIRECT_TO_SCHEME::topLevel);
886 #ifndef ANDROID
887 mozilla::glean::networking::http_redirect_to_scheme_top_level.Get(scheme)
888 .Add(1);
889 #endif
890 } else {
891 Telemetry::AccumulateCategoricalKeyed(
892 scheme,
893 Telemetry::LABELS_NETWORK_HTTP_REDIRECT_TO_SCHEME::subresource);
894 #ifndef ANDROID
895 mozilla::glean::networking::http_redirect_to_scheme_subresource
896 .Get(scheme)
897 .Add(1);
898 #endif
901 return NS_OK;
904 bool nsIOService::UsesExternalProtocolHandler(const nsACString& aScheme) {
905 if (aScheme == "file"_ns || aScheme == "chrome"_ns ||
906 aScheme == "resource"_ns) {
907 // Don't allow file:, chrome: or resource: URIs to be handled with
908 // nsExternalProtocolHandler, since internally we rely on being able to
909 // use and read from these URIs.
910 return false;
913 if (aScheme == "place"_ns || aScheme == "fake-favicon-uri"_ns ||
914 aScheme == "favicon"_ns || aScheme == "moz-nullprincipal"_ns) {
915 // Force place: fake-favicon-uri: favicon: and moz-nullprincipal: URIs to be
916 // handled with nsExternalProtocolHandler, and not with a dynamically
917 // registered handler.
918 return true;
921 // If prefs configure the URI to be handled externally, do so.
922 for (const auto& scheme : mForceExternalSchemes) {
923 if (aScheme == scheme) {
924 return true;
927 return false;
930 ProtocolHandlerInfo nsIOService::LookupProtocolHandler(
931 const nsACString& aScheme) {
932 // Look-ups are ASCII-case-insensitive, so lower-case the string before
933 // continuing.
934 nsAutoCString scheme(aScheme);
935 ToLowerCase(scheme);
937 // NOTE: If we could get rid of mForceExternalSchemes (or prevent them from
938 // disabling static protocols), we could avoid locking mLock until we need to
939 // check `mRuntimeProtocolHandlers.
940 AutoReadLock lock(mLock);
941 if (!UsesExternalProtocolHandler(scheme)) {
942 // Try the static protocol handler first - they cannot be overridden by
943 // dynamic protocols.
944 if (const xpcom::StaticProtocolHandler* handler =
945 xpcom::StaticProtocolHandler::Lookup(scheme)) {
946 return ProtocolHandlerInfo(*handler);
948 if (auto handler = mRuntimeProtocolHandlers.Lookup(scheme)) {
949 return ProtocolHandlerInfo(handler.Data());
952 return ProtocolHandlerInfo(xpcom::StaticProtocolHandler::Default());
955 NS_IMETHODIMP
956 nsIOService::GetProtocolHandler(const char* scheme,
957 nsIProtocolHandler** result) {
958 AssertIsOnMainThread();
959 NS_ENSURE_ARG_POINTER(scheme);
961 *result = LookupProtocolHandler(nsDependentCString(scheme)).Handler().take();
962 return *result ? NS_OK : NS_ERROR_UNKNOWN_PROTOCOL;
965 NS_IMETHODIMP
966 nsIOService::ExtractScheme(const nsACString& inURI, nsACString& scheme) {
967 return net_ExtractURLScheme(inURI, scheme);
970 NS_IMETHODIMP
971 nsIOService::HostnameIsLocalIPAddress(nsIURI* aURI, bool* aResult) {
972 NS_ENSURE_ARG_POINTER(aURI);
974 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
975 NS_ENSURE_ARG_POINTER(innerURI);
977 nsAutoCString host;
978 nsresult rv = innerURI->GetAsciiHost(host);
979 if (NS_FAILED(rv)) {
980 return rv;
983 *aResult = false;
985 NetAddr addr;
986 if (NS_SUCCEEDED(addr.InitFromString(host)) && addr.IsIPAddrLocal()) {
987 *aResult = true;
990 return NS_OK;
993 NS_IMETHODIMP
994 nsIOService::HostnameIsIPAddressAny(nsIURI* aURI, bool* aResult) {
995 NS_ENSURE_ARG_POINTER(aURI);
997 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
998 NS_ENSURE_ARG_POINTER(innerURI);
1000 nsAutoCString host;
1001 nsresult rv = innerURI->GetAsciiHost(host);
1002 if (NS_FAILED(rv)) {
1003 return rv;
1006 *aResult = false;
1008 NetAddr addr;
1009 if (NS_SUCCEEDED(addr.InitFromString(host)) && addr.IsIPAddrAny()) {
1010 *aResult = true;
1013 return NS_OK;
1016 NS_IMETHODIMP
1017 nsIOService::HostnameIsSharedIPAddress(nsIURI* aURI, bool* aResult) {
1018 NS_ENSURE_ARG_POINTER(aURI);
1020 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
1021 NS_ENSURE_ARG_POINTER(innerURI);
1023 nsAutoCString host;
1024 nsresult rv = innerURI->GetAsciiHost(host);
1025 if (NS_FAILED(rv)) {
1026 return rv;
1029 *aResult = false;
1031 NetAddr addr;
1032 if (NS_SUCCEEDED(addr.InitFromString(host)) && addr.IsIPAddrShared()) {
1033 *aResult = true;
1036 return NS_OK;
1039 NS_IMETHODIMP
1040 nsIOService::IsValidHostname(const nsACString& inHostname, bool* aResult) {
1041 if (!net_IsValidDNSHost(inHostname)) {
1042 *aResult = false;
1043 return NS_OK;
1046 // hostname ending with a "." delimited octet that is a number
1047 // must be IPv4 or IPv6 dual address
1048 nsAutoCString host(inHostname);
1049 if (IPv4Parser::EndsInANumber(host)) {
1050 // ipv6 dual address; for example "::1.2.3.4"
1051 if (net_IsValidIPv6Addr(host)) {
1052 *aResult = true;
1053 return NS_OK;
1056 nsAutoCString normalized;
1057 nsresult rv = IPv4Parser::NormalizeIPv4(host, normalized);
1058 if (NS_FAILED(rv)) {
1059 *aResult = false;
1060 return NS_OK;
1063 *aResult = true;
1064 return NS_OK;
1067 NS_IMETHODIMP
1068 nsIOService::GetProtocolFlags(const char* scheme, uint32_t* flags) {
1069 NS_ENSURE_ARG_POINTER(scheme);
1071 *flags =
1072 LookupProtocolHandler(nsDependentCString(scheme)).StaticProtocolFlags();
1073 return NS_OK;
1076 NS_IMETHODIMP
1077 nsIOService::GetDynamicProtocolFlags(nsIURI* uri, uint32_t* flags) {
1078 AssertIsOnMainThread();
1079 NS_ENSURE_ARG(uri);
1081 nsAutoCString scheme;
1082 nsresult rv = uri->GetScheme(scheme);
1083 NS_ENSURE_SUCCESS(rv, rv);
1085 return LookupProtocolHandler(scheme).DynamicProtocolFlags(uri, flags);
1088 NS_IMETHODIMP
1089 nsIOService::GetDefaultPort(const char* scheme, int32_t* defaultPort) {
1090 NS_ENSURE_ARG_POINTER(scheme);
1092 *defaultPort =
1093 LookupProtocolHandler(nsDependentCString(scheme)).DefaultPort();
1094 return NS_OK;
1097 nsresult nsIOService::NewURI(const nsACString& aSpec, const char* aCharset,
1098 nsIURI* aBaseURI, nsIURI** result) {
1099 return NS_NewURI(result, aSpec, aCharset, aBaseURI);
1102 NS_IMETHODIMP
1103 nsIOService::NewFileURI(nsIFile* file, nsIURI** result) {
1104 nsresult rv;
1105 NS_ENSURE_ARG_POINTER(file);
1107 nsCOMPtr<nsIProtocolHandler> handler;
1109 rv = GetProtocolHandler("file", getter_AddRefs(handler));
1110 if (NS_FAILED(rv)) return rv;
1112 nsCOMPtr<nsIFileProtocolHandler> fileHandler(do_QueryInterface(handler, &rv));
1113 if (NS_FAILED(rv)) return rv;
1115 return fileHandler->NewFileURI(file, result);
1118 // static
1119 already_AddRefed<nsIURI> nsIOService::CreateExposableURI(nsIURI* aURI) {
1120 MOZ_ASSERT(aURI, "Must have a URI");
1121 nsCOMPtr<nsIURI> uri = aURI;
1122 bool hasUserPass;
1123 if (NS_SUCCEEDED(aURI->GetHasUserPass(&hasUserPass)) && hasUserPass) {
1124 DebugOnly<nsresult> rv = NS_MutateURI(uri).SetUserPass(""_ns).Finalize(uri);
1125 MOZ_ASSERT(NS_SUCCEEDED(rv) && uri, "Mutating URI should never fail");
1127 return uri.forget();
1130 NS_IMETHODIMP
1131 nsIOService::CreateExposableURI(nsIURI* aURI, nsIURI** _result) {
1132 NS_ENSURE_ARG_POINTER(aURI);
1133 NS_ENSURE_ARG_POINTER(_result);
1134 nsCOMPtr<nsIURI> exposableURI = CreateExposableURI(aURI);
1135 exposableURI.forget(_result);
1136 return NS_OK;
1139 NS_IMETHODIMP
1140 nsIOService::NewChannelFromURI(nsIURI* aURI, nsINode* aLoadingNode,
1141 nsIPrincipal* aLoadingPrincipal,
1142 nsIPrincipal* aTriggeringPrincipal,
1143 uint32_t aSecurityFlags,
1144 nsContentPolicyType aContentPolicyType,
1145 nsIChannel** result) {
1146 return NewChannelFromURIWithProxyFlags(aURI,
1147 nullptr, // aProxyURI
1148 0, // aProxyFlags
1149 aLoadingNode, aLoadingPrincipal,
1150 aTriggeringPrincipal, aSecurityFlags,
1151 aContentPolicyType, result);
1153 nsresult nsIOService::NewChannelFromURIWithClientAndController(
1154 nsIURI* aURI, nsINode* aLoadingNode, nsIPrincipal* aLoadingPrincipal,
1155 nsIPrincipal* aTriggeringPrincipal,
1156 const Maybe<ClientInfo>& aLoadingClientInfo,
1157 const Maybe<ServiceWorkerDescriptor>& aController, uint32_t aSecurityFlags,
1158 nsContentPolicyType aContentPolicyType, uint32_t aSandboxFlags,
1159 nsIChannel** aResult) {
1160 return NewChannelFromURIWithProxyFlagsInternal(
1161 aURI,
1162 nullptr, // aProxyURI
1163 0, // aProxyFlags
1164 aLoadingNode, aLoadingPrincipal, aTriggeringPrincipal, aLoadingClientInfo,
1165 aController, aSecurityFlags, aContentPolicyType, aSandboxFlags, aResult);
1168 NS_IMETHODIMP
1169 nsIOService::NewChannelFromURIWithLoadInfo(nsIURI* aURI, nsILoadInfo* aLoadInfo,
1170 nsIChannel** result) {
1171 return NewChannelFromURIWithProxyFlagsInternal(aURI,
1172 nullptr, // aProxyURI
1173 0, // aProxyFlags
1174 aLoadInfo, result);
1177 nsresult nsIOService::NewChannelFromURIWithProxyFlagsInternal(
1178 nsIURI* aURI, nsIURI* aProxyURI, uint32_t aProxyFlags,
1179 nsINode* aLoadingNode, nsIPrincipal* aLoadingPrincipal,
1180 nsIPrincipal* aTriggeringPrincipal,
1181 const Maybe<ClientInfo>& aLoadingClientInfo,
1182 const Maybe<ServiceWorkerDescriptor>& aController, uint32_t aSecurityFlags,
1183 nsContentPolicyType aContentPolicyType, uint32_t aSandboxFlags,
1184 nsIChannel** result) {
1185 nsCOMPtr<nsILoadInfo> loadInfo = new LoadInfo(
1186 aLoadingPrincipal, aTriggeringPrincipal, aLoadingNode, aSecurityFlags,
1187 aContentPolicyType, aLoadingClientInfo, aController, aSandboxFlags);
1188 return NewChannelFromURIWithProxyFlagsInternal(aURI, aProxyURI, aProxyFlags,
1189 loadInfo, result);
1192 nsresult nsIOService::NewChannelFromURIWithProxyFlagsInternal(
1193 nsIURI* aURI, nsIURI* aProxyURI, uint32_t aProxyFlags,
1194 nsILoadInfo* aLoadInfo, nsIChannel** result) {
1195 nsresult rv;
1196 NS_ENSURE_ARG_POINTER(aURI);
1197 // all channel creations must provide a valid loadinfo
1198 MOZ_ASSERT(aLoadInfo, "can not create channel without aLoadInfo");
1199 NS_ENSURE_ARG_POINTER(aLoadInfo);
1201 nsAutoCString scheme;
1202 rv = aURI->GetScheme(scheme);
1203 if (NS_FAILED(rv)) return rv;
1205 nsCOMPtr<nsIProtocolHandler> handler;
1206 rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
1207 if (NS_FAILED(rv)) return rv;
1209 nsCOMPtr<nsIChannel> channel;
1210 nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler);
1211 if (pph) {
1212 rv = pph->NewProxiedChannel(aURI, nullptr, aProxyFlags, aProxyURI,
1213 aLoadInfo, getter_AddRefs(channel));
1214 } else {
1215 rv = handler->NewChannel(aURI, aLoadInfo, getter_AddRefs(channel));
1217 if (NS_FAILED(rv)) return rv;
1219 // Make sure that all the individual protocolhandlers attach a loadInfo.
1220 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1221 if (aLoadInfo != loadInfo) {
1222 MOZ_ASSERT(false, "newly created channel must have a loadinfo attached");
1223 return NS_ERROR_UNEXPECTED;
1226 // If we're sandboxed, make sure to clear any owner the channel
1227 // might already have.
1228 if (loadInfo->GetLoadingSandboxed()) {
1229 channel->SetOwner(nullptr);
1232 // Some extensions override the http protocol handler and provide their own
1233 // implementation. The channels returned from that implementation doesn't
1234 // seem to always implement the nsIUploadChannel2 interface, presumably
1235 // because it's a new interface.
1236 // Eventually we should remove this and simply require that http channels
1237 // implement the new interface.
1238 // See bug 529041
1239 if (!gHasWarnedUploadChannel2 && scheme.EqualsLiteral("http")) {
1240 nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(channel);
1241 if (!uploadChannel2) {
1242 nsCOMPtr<nsIConsoleService> consoleService;
1243 consoleService = mozilla::components::Console::Service();
1244 if (consoleService) {
1245 consoleService->LogStringMessage(
1246 u"Http channel implementation "
1247 "doesn't support nsIUploadChannel2. An extension has "
1248 "supplied a non-functional http protocol handler. This will "
1249 "break behavior and in future releases not work at all.");
1251 gHasWarnedUploadChannel2 = true;
1255 channel.forget(result);
1256 return NS_OK;
1259 NS_IMETHODIMP
1260 nsIOService::NewChannelFromURIWithProxyFlags(
1261 nsIURI* aURI, nsIURI* aProxyURI, uint32_t aProxyFlags,
1262 nsINode* aLoadingNode, nsIPrincipal* aLoadingPrincipal,
1263 nsIPrincipal* aTriggeringPrincipal, uint32_t aSecurityFlags,
1264 nsContentPolicyType aContentPolicyType, nsIChannel** result) {
1265 return NewChannelFromURIWithProxyFlagsInternal(
1266 aURI, aProxyURI, aProxyFlags, aLoadingNode, aLoadingPrincipal,
1267 aTriggeringPrincipal, Maybe<ClientInfo>(),
1268 Maybe<ServiceWorkerDescriptor>(), aSecurityFlags, aContentPolicyType, 0,
1269 result);
1272 NS_IMETHODIMP
1273 nsIOService::NewChannel(const nsACString& aSpec, const char* aCharset,
1274 nsIURI* aBaseURI, nsINode* aLoadingNode,
1275 nsIPrincipal* aLoadingPrincipal,
1276 nsIPrincipal* aTriggeringPrincipal,
1277 uint32_t aSecurityFlags,
1278 nsContentPolicyType aContentPolicyType,
1279 nsIChannel** result) {
1280 nsresult rv;
1281 nsCOMPtr<nsIURI> uri;
1282 rv = NewURI(aSpec, aCharset, aBaseURI, getter_AddRefs(uri));
1283 if (NS_FAILED(rv)) return rv;
1285 return NewChannelFromURI(uri, aLoadingNode, aLoadingPrincipal,
1286 aTriggeringPrincipal, aSecurityFlags,
1287 aContentPolicyType, result);
1290 NS_IMETHODIMP
1291 nsIOService::NewWebTransport(nsIWebTransport** result) {
1292 if (!XRE_IsParentProcess()) {
1293 return NS_ERROR_NOT_AVAILABLE;
1296 nsCOMPtr<nsIWebTransport> webTransport = new WebTransportSessionProxy();
1298 webTransport.forget(result);
1299 return NS_OK;
1302 NS_IMETHODIMP
1303 nsIOService::OriginAttributesForNetworkState(
1304 nsIChannel* aChannel, JSContext* cx, JS::MutableHandle<JS::Value> _retval) {
1305 OriginAttributes attrs;
1306 if (!StoragePrincipalHelper::GetOriginAttributesForNetworkState(aChannel,
1307 attrs)) {
1308 return NS_ERROR_FAILURE;
1311 if (NS_WARN_IF(!mozilla::dom::ToJSValue(cx, attrs, _retval))) {
1312 return NS_ERROR_FAILURE;
1315 return NS_OK;
1318 bool nsIOService::IsLinkUp() {
1319 InitializeNetworkLinkService();
1321 if (!mNetworkLinkService) {
1322 // We cannot decide, assume the link is up
1323 return true;
1326 bool isLinkUp;
1327 nsresult rv;
1328 rv = mNetworkLinkService->GetIsLinkUp(&isLinkUp);
1329 if (NS_FAILED(rv)) {
1330 return true;
1333 return isLinkUp;
1336 NS_IMETHODIMP
1337 nsIOService::GetOffline(bool* offline) {
1338 if (StaticPrefs::network_offline_mirrors_connectivity()) {
1339 *offline = mOffline || !mConnectivity;
1340 } else {
1341 *offline = mOffline;
1343 return NS_OK;
1346 NS_IMETHODIMP
1347 nsIOService::SetOffline(bool offline) { return SetOfflineInternal(offline); }
1349 nsresult nsIOService::SetOfflineInternal(bool offline,
1350 bool notifySocketProcess) {
1351 LOG(("nsIOService::SetOffline offline=%d\n", offline));
1352 // When someone wants to go online (!offline) after we got XPCOM shutdown
1353 // throw ERROR_NOT_AVAILABLE to prevent return to online state.
1354 if ((mShutdown || mOfflineForProfileChange) && !offline) {
1355 return NS_ERROR_NOT_AVAILABLE;
1358 // SetOffline() may re-enter while it's shutting down services.
1359 // If that happens, save the most recent value and it will be
1360 // processed when the first SetOffline() call is done bringing
1361 // down the service.
1362 mSetOfflineValue = offline;
1363 if (mSettingOffline) {
1364 return NS_OK;
1367 mSettingOffline = true;
1369 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
1371 NS_ASSERTION(observerService, "The observer service should not be null");
1373 if (XRE_IsParentProcess()) {
1374 if (observerService) {
1375 (void)observerService->NotifyObservers(nullptr,
1376 NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
1377 offline ? u"true" : u"false");
1379 if (SocketProcessReady() && notifySocketProcess) {
1380 Unused << mSocketProcess->GetActor()->SendSetOffline(offline);
1384 nsIIOService* subject = static_cast<nsIIOService*>(this);
1385 while (mSetOfflineValue != mOffline) {
1386 offline = mSetOfflineValue;
1388 if (offline && !mOffline) {
1389 mOffline = true; // indicate we're trying to shutdown
1391 // don't care if notifications fail
1392 if (observerService) {
1393 observerService->NotifyObservers(subject,
1394 NS_IOSERVICE_GOING_OFFLINE_TOPIC,
1395 u"" NS_IOSERVICE_OFFLINE);
1398 if (mSocketTransportService) mSocketTransportService->SetOffline(true);
1400 mLastOfflineStateChange = PR_IntervalNow();
1401 if (observerService) {
1402 observerService->NotifyObservers(subject,
1403 NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1404 u"" NS_IOSERVICE_OFFLINE);
1406 } else if (!offline && mOffline) {
1407 // go online
1408 InitializeSocketTransportService();
1409 mOffline = false; // indicate success only AFTER we've
1410 // brought up the services
1412 mLastOfflineStateChange = PR_IntervalNow();
1413 // don't care if notification fails
1414 // Only send the ONLINE notification if there is connectivity
1415 if (observerService && mConnectivity) {
1416 observerService->NotifyObservers(subject,
1417 NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1418 (u"" NS_IOSERVICE_ONLINE));
1423 // Don't notify here, as the above notifications (if used) suffice.
1424 if ((mShutdown || mOfflineForProfileChange) && mOffline) {
1425 if (mSocketTransportService) {
1426 DebugOnly<nsresult> rv = mSocketTransportService->Shutdown(mShutdown);
1427 NS_ASSERTION(NS_SUCCEEDED(rv),
1428 "socket transport service shutdown failed");
1432 mSettingOffline = false;
1434 return NS_OK;
1437 NS_IMETHODIMP
1438 nsIOService::GetConnectivity(bool* aConnectivity) {
1439 *aConnectivity = mConnectivity;
1440 return NS_OK;
1443 NS_IMETHODIMP
1444 nsIOService::SetConnectivity(bool aConnectivity) {
1445 LOG(("nsIOService::SetConnectivity aConnectivity=%d\n", aConnectivity));
1446 // This should only be called from ContentChild to pass the connectivity
1447 // value from the chrome process to the content process.
1448 if (XRE_IsParentProcess()) {
1449 return NS_ERROR_NOT_AVAILABLE;
1451 return SetConnectivityInternal(aConnectivity);
1454 nsresult nsIOService::SetConnectivityInternal(bool aConnectivity) {
1455 LOG(("nsIOService::SetConnectivityInternal aConnectivity=%d\n",
1456 aConnectivity));
1457 if (mConnectivity == aConnectivity) {
1458 // Nothing to do here.
1459 return NS_OK;
1461 mConnectivity = aConnectivity;
1463 // This is used for PR_Connect PR_Close telemetry so it is important that
1464 // we have statistic about network change event even if we are offline.
1465 mLastConnectivityChange = PR_IntervalNow();
1467 if (mCaptivePortalService) {
1468 if (aConnectivity && gCaptivePortalEnabled) {
1469 // This will also trigger a captive portal check for the new network
1470 static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Start();
1471 } else {
1472 static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Stop();
1476 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
1477 if (!observerService) {
1478 return NS_OK;
1480 // This notification sends the connectivity to the child processes
1481 if (XRE_IsParentProcess()) {
1482 observerService->NotifyObservers(nullptr,
1483 NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
1484 aConnectivity ? u"true" : u"false");
1485 if (SocketProcessReady()) {
1486 Unused << mSocketProcess->GetActor()->SendSetConnectivity(aConnectivity);
1490 if (mOffline) {
1491 // We don't need to send any notifications if we're offline
1492 return NS_OK;
1495 if (aConnectivity) {
1496 // If we were previously offline due to connectivity=false,
1497 // send the ONLINE notification
1498 observerService->NotifyObservers(static_cast<nsIIOService*>(this),
1499 NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1500 (u"" NS_IOSERVICE_ONLINE));
1501 } else {
1502 // If we were previously online and lost connectivity
1503 // send the OFFLINE notification
1504 observerService->NotifyObservers(static_cast<nsIIOService*>(this),
1505 NS_IOSERVICE_GOING_OFFLINE_TOPIC,
1506 u"" NS_IOSERVICE_OFFLINE);
1507 observerService->NotifyObservers(static_cast<nsIIOService*>(this),
1508 NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1509 u"" NS_IOSERVICE_OFFLINE);
1511 return NS_OK;
1514 NS_IMETHODIMP
1515 nsIOService::AllowPort(int32_t inPort, const char* scheme, bool* _retval) {
1516 int32_t port = inPort;
1517 if (port == -1) {
1518 *_retval = true;
1519 return NS_OK;
1522 if (port <= 0 || port > std::numeric_limits<uint16_t>::max()) {
1523 *_retval = false;
1524 return NS_OK;
1527 nsTArray<int32_t> restrictedPortList;
1529 AutoReadLock lock(mLock);
1530 restrictedPortList.Assign(mRestrictedPortList);
1532 // first check to see if the port is in our blacklist:
1533 int32_t badPortListCnt = restrictedPortList.Length();
1534 for (int i = 0; i < badPortListCnt; i++) {
1535 if (port == restrictedPortList[i]) {
1536 *_retval = false;
1538 // check to see if the protocol wants to override
1539 if (!scheme) return NS_OK;
1541 // We don't support get protocol handler off main thread.
1542 if (!NS_IsMainThread()) {
1543 return NS_OK;
1545 nsCOMPtr<nsIProtocolHandler> handler;
1546 nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
1547 if (NS_FAILED(rv)) return rv;
1549 // let the protocol handler decide
1550 return handler->AllowPort(port, scheme, _retval);
1554 *_retval = true;
1555 return NS_OK;
1558 ////////////////////////////////////////////////////////////////////////////////
1560 // static
1561 void nsIOService::PrefsChanged(const char* pref, void* self) {
1562 static_cast<nsIOService*>(self)->PrefsChanged(pref);
1565 void nsIOService::PrefsChanged(const char* pref) {
1566 // Look for extra ports to block
1567 if (!pref || strcmp(pref, PORT_PREF("banned")) == 0) {
1568 ParsePortList(PORT_PREF("banned"), false);
1571 // ...as well as previous blocks to remove.
1572 if (!pref || strcmp(pref, PORT_PREF("banned.override")) == 0) {
1573 ParsePortList(PORT_PREF("banned.override"), true);
1576 if (!pref || strcmp(pref, MANAGE_OFFLINE_STATUS_PREF) == 0) {
1577 bool manage;
1578 if (mNetworkLinkServiceInitialized &&
1579 NS_SUCCEEDED(
1580 Preferences::GetBool(MANAGE_OFFLINE_STATUS_PREF, &manage))) {
1581 LOG(("nsIOService::PrefsChanged ManageOfflineStatus manage=%d\n",
1582 manage));
1583 SetManageOfflineStatus(manage);
1587 if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_COUNT_PREF) == 0) {
1588 int32_t count;
1589 if (NS_SUCCEEDED(
1590 Preferences::GetInt(NECKO_BUFFER_CACHE_COUNT_PREF, &count))) {
1591 /* check for bogus values and default if we find such a value */
1592 if (count > 0) gDefaultSegmentCount = count;
1596 if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_SIZE_PREF) == 0) {
1597 int32_t size;
1598 if (NS_SUCCEEDED(
1599 Preferences::GetInt(NECKO_BUFFER_CACHE_SIZE_PREF, &size))) {
1600 /* check for bogus values and default if we find such a value
1601 * the upper limit here is arbitrary. having a 1mb segment size
1602 * is pretty crazy. if you remove this, consider adding some
1603 * integer rollover test.
1605 if (size > 0 && size < 1024 * 1024) gDefaultSegmentSize = size;
1607 NS_WARNING_ASSERTION(!(size & (size - 1)),
1608 "network segment size is not a power of 2!");
1611 if (!pref || strcmp(pref, NETWORK_CAPTIVE_PORTAL_PREF) == 0) {
1612 nsresult rv = Preferences::GetBool(NETWORK_CAPTIVE_PORTAL_PREF,
1613 &gCaptivePortalEnabled);
1614 if (NS_SUCCEEDED(rv) && mCaptivePortalService) {
1615 if (gCaptivePortalEnabled) {
1616 static_cast<CaptivePortalService*>(mCaptivePortalService.get())
1617 ->Start();
1618 } else {
1619 static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Stop();
1624 if (!pref || strncmp(pref, FORCE_EXTERNAL_PREF_PREFIX,
1625 strlen(FORCE_EXTERNAL_PREF_PREFIX)) == 0) {
1626 nsTArray<nsCString> prefs;
1627 if (nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch()) {
1628 prefRootBranch->GetChildList(FORCE_EXTERNAL_PREF_PREFIX, prefs);
1630 nsTArray<nsCString> forceExternalSchemes;
1631 for (const auto& pref : prefs) {
1632 if (Preferences::GetBool(pref.get(), false)) {
1633 forceExternalSchemes.AppendElement(
1634 Substring(pref, strlen(FORCE_EXTERNAL_PREF_PREFIX)));
1637 AutoWriteLock lock(mLock);
1638 mForceExternalSchemes = std::move(forceExternalSchemes);
1641 if (!pref || strncmp(pref, SIMPLE_URI_SCHEMES_PREF,
1642 strlen(SIMPLE_URI_SCHEMES_PREF)) == 0) {
1643 LOG(("simple_uri_unknown_schemes pref changed, updating the scheme list"));
1644 mSimpleURIUnknownSchemes.ParseAndMergePrefSchemes();
1645 // runs on parent and child, no need to broadcast
1649 void nsIOService::ParsePortList(const char* pref, bool remove) {
1650 nsAutoCString portList;
1651 nsTArray<int32_t> restrictedPortList;
1653 AutoWriteLock lock(mLock);
1654 restrictedPortList.Assign(std::move(mRestrictedPortList));
1656 // Get a pref string and chop it up into a list of ports.
1657 Preferences::GetCString(pref, portList);
1658 if (!portList.IsVoid()) {
1659 nsTArray<nsCString> portListArray;
1660 ParseString(portList, ',', portListArray);
1661 uint32_t index;
1662 for (index = 0; index < portListArray.Length(); index++) {
1663 portListArray[index].StripWhitespace();
1664 int32_t portBegin, portEnd;
1666 if (PR_sscanf(portListArray[index].get(), "%d-%d", &portBegin,
1667 &portEnd) == 2) {
1668 if ((portBegin < 65536) && (portEnd < 65536)) {
1669 int32_t curPort;
1670 if (remove) {
1671 for (curPort = portBegin; curPort <= portEnd; curPort++) {
1672 restrictedPortList.RemoveElement(curPort);
1674 } else {
1675 for (curPort = portBegin; curPort <= portEnd; curPort++) {
1676 restrictedPortList.AppendElement(curPort);
1680 } else {
1681 nsresult aErrorCode;
1682 int32_t port = portListArray[index].ToInteger(&aErrorCode);
1683 if (NS_SUCCEEDED(aErrorCode) && port < 65536) {
1684 if (remove) {
1685 restrictedPortList.RemoveElement(port);
1686 } else {
1687 restrictedPortList.AppendElement(port);
1694 AutoWriteLock lock(mLock);
1695 mRestrictedPortList.Assign(std::move(restrictedPortList));
1698 class nsWakeupNotifier : public Runnable {
1699 public:
1700 explicit nsWakeupNotifier(nsIIOServiceInternal* ioService)
1701 : Runnable("net::nsWakeupNotifier"), mIOService(ioService) {}
1703 NS_IMETHOD Run() override { return mIOService->NotifyWakeup(); }
1705 private:
1706 virtual ~nsWakeupNotifier() = default;
1707 nsCOMPtr<nsIIOServiceInternal> mIOService;
1710 NS_IMETHODIMP
1711 nsIOService::NotifyWakeup() {
1712 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
1714 NS_ASSERTION(observerService, "The observer service should not be null");
1716 if (observerService && StaticPrefs::network_notify_changed()) {
1717 (void)observerService->NotifyObservers(nullptr, NS_NETWORK_LINK_TOPIC,
1718 (u"" NS_NETWORK_LINK_DATA_CHANGED));
1721 RecheckCaptivePortal();
1723 return NS_OK;
1726 void nsIOService::SetHttpHandlerAlreadyShutingDown() {
1727 if (!mShutdown && !mOfflineForProfileChange) {
1728 mNetTearingDownStarted = PR_IntervalNow();
1729 mHttpHandlerAlreadyShutingDown = true;
1733 // nsIObserver interface
1734 NS_IMETHODIMP
1735 nsIOService::Observe(nsISupports* subject, const char* topic,
1736 const char16_t* data) {
1737 if (UseSocketProcess() && SocketProcessReady() &&
1738 mObserverTopicForSocketProcess.Contains(nsDependentCString(topic))) {
1739 nsCString topicStr(topic);
1740 nsString dataStr(data);
1741 Unused << mSocketProcess->GetActor()->SendNotifyObserver(topicStr, dataStr);
1744 if (!strcmp(topic, kProfileChangeNetTeardownTopic)) {
1745 if (!mHttpHandlerAlreadyShutingDown) {
1746 mNetTearingDownStarted = PR_IntervalNow();
1748 mHttpHandlerAlreadyShutingDown = false;
1749 if (!mOffline) {
1750 mOfflineForProfileChange = true;
1751 SetOfflineInternal(true, false);
1753 } else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) {
1754 if (mOfflineForProfileChange) {
1755 mOfflineForProfileChange = false;
1756 SetOfflineInternal(false, false);
1758 } else if (!strcmp(topic, kProfileDoChange)) {
1759 if (data && u"startup"_ns.Equals(data)) {
1760 // Lazy initialization of network link service (see bug 620472)
1761 InitializeNetworkLinkService();
1762 // Set up the initilization flag regardless the actuall result.
1763 // If we fail here, we will fail always on.
1764 mNetworkLinkServiceInitialized = true;
1766 // And now reflect the preference setting
1767 PrefsChanged(MANAGE_OFFLINE_STATUS_PREF);
1769 // Bug 870460 - Read cookie database at an early-as-possible time
1770 // off main thread. Hence, we have more chance to finish db query
1771 // before something calls into the cookie service.
1772 nsCOMPtr<nsISupports> cookieServ =
1773 do_GetService(NS_COOKIESERVICE_CONTRACTID);
1775 } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
1776 // Remember we passed XPCOM shutdown notification to prevent any
1777 // changes of the offline status from now. We must not allow going
1778 // online after this point.
1779 mShutdown = true;
1781 if (!mHttpHandlerAlreadyShutingDown && !mOfflineForProfileChange) {
1782 mNetTearingDownStarted = PR_IntervalNow();
1784 mHttpHandlerAlreadyShutingDown = false;
1786 SetOfflineInternal(true, false);
1788 if (mCaptivePortalService) {
1789 static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Stop();
1790 mCaptivePortalService = nullptr;
1793 SSLTokensCache::Shutdown();
1795 DestroySocketProcess();
1797 if (IsSocketProcessChild()) {
1798 Preferences::UnregisterCallbacks(nsIOService::OnTLSPrefChange,
1799 gCallbackSecurityPrefs, this);
1800 PrepareForShutdownInSocketProcess();
1803 // We're in XPCOM shutdown now. Unregister any dynamic protocol handlers
1804 // after this point to avoid leaks.
1806 AutoWriteLock lock(mLock);
1807 mRuntimeProtocolHandlers.Clear();
1809 } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
1810 OnNetworkLinkEvent(NS_ConvertUTF16toUTF8(data).get());
1811 } else if (!strcmp(topic, NS_NETWORK_ID_CHANGED_TOPIC)) {
1812 LOG(("nsIOService::OnNetworkLinkEvent Network id changed"));
1813 } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
1814 // coming back alive from sleep
1815 // this indirection brought to you by:
1816 // https://bugzilla.mozilla.org/show_bug.cgi?id=1152048#c19
1817 nsCOMPtr<nsIRunnable> wakeupNotifier = new nsWakeupNotifier(this);
1818 NS_DispatchToMainThread(wakeupNotifier);
1819 mInSleepMode = false;
1820 } else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
1821 mInSleepMode = true;
1824 return NS_OK;
1827 // nsINetUtil interface
1828 NS_IMETHODIMP
1829 nsIOService::ParseRequestContentType(const nsACString& aTypeHeader,
1830 nsACString& aCharset, bool* aHadCharset,
1831 nsACString& aContentType) {
1832 net_ParseRequestContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
1833 return NS_OK;
1836 // nsINetUtil interface
1837 NS_IMETHODIMP
1838 nsIOService::ParseResponseContentType(const nsACString& aTypeHeader,
1839 nsACString& aCharset, bool* aHadCharset,
1840 nsACString& aContentType) {
1841 net_ParseContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
1842 return NS_OK;
1845 NS_IMETHODIMP
1846 nsIOService::ProtocolHasFlags(nsIURI* uri, uint32_t flags, bool* result) {
1847 NS_ENSURE_ARG(uri);
1849 *result = false;
1850 nsAutoCString scheme;
1851 nsresult rv = uri->GetScheme(scheme);
1852 NS_ENSURE_SUCCESS(rv, rv);
1854 auto handler = LookupProtocolHandler(scheme);
1856 uint32_t protocolFlags;
1857 if (flags & nsIProtocolHandler::DYNAMIC_URI_FLAGS) {
1858 AssertIsOnMainThread();
1859 rv = handler.DynamicProtocolFlags(uri, &protocolFlags);
1860 NS_ENSURE_SUCCESS(rv, rv);
1861 } else {
1862 protocolFlags = handler.StaticProtocolFlags();
1865 *result = (protocolFlags & flags) == flags;
1866 return NS_OK;
1869 NS_IMETHODIMP
1870 nsIOService::URIChainHasFlags(nsIURI* uri, uint32_t flags, bool* result) {
1871 nsresult rv = ProtocolHasFlags(uri, flags, result);
1872 NS_ENSURE_SUCCESS(rv, rv);
1874 if (*result) {
1875 return rv;
1878 // Dig deeper into the chain. Note that this is not a do/while loop to
1879 // avoid the extra addref/release on |uri| in the common (non-nested) case.
1880 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
1881 while (nestedURI) {
1882 nsCOMPtr<nsIURI> innerURI;
1883 rv = nestedURI->GetInnerURI(getter_AddRefs(innerURI));
1884 NS_ENSURE_SUCCESS(rv, rv);
1886 rv = ProtocolHasFlags(innerURI, flags, result);
1888 if (*result) {
1889 return rv;
1892 nestedURI = do_QueryInterface(innerURI);
1895 return rv;
1898 NS_IMETHODIMP
1899 nsIOService::SetManageOfflineStatus(bool aManage) {
1900 LOG(("nsIOService::SetManageOfflineStatus aManage=%d\n", aManage));
1901 mManageLinkStatus = aManage;
1903 // When detection is not activated, the default connectivity state is true.
1904 if (!mManageLinkStatus) {
1905 SetConnectivityInternal(true);
1906 return NS_OK;
1909 InitializeNetworkLinkService();
1910 // If the NetworkLinkService is already initialized, it does not call
1911 // OnNetworkLinkEvent. This is needed, when mManageLinkStatus goes from
1912 // false to true.
1913 OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN);
1914 return NS_OK;
1917 NS_IMETHODIMP
1918 nsIOService::GetManageOfflineStatus(bool* aManage) {
1919 *aManage = mManageLinkStatus;
1920 return NS_OK;
1923 // input argument 'data' is already UTF8'ed
1924 nsresult nsIOService::OnNetworkLinkEvent(const char* data) {
1925 if (IsNeckoChild() || IsSocketProcessChild()) {
1926 // There is nothing IO service could do on the child process
1927 // with this at the moment. Feel free to add functionality
1928 // here at will, though.
1929 return NS_OK;
1932 if (mShutdown) {
1933 return NS_ERROR_NOT_AVAILABLE;
1936 nsCString dataAsString(data);
1937 for (auto* cp : mozilla::dom::ContentParent::AllProcesses(
1938 mozilla::dom::ContentParent::eLive)) {
1939 PNeckoParent* neckoParent = SingleManagedOrNull(cp->ManagedPNeckoParent());
1940 if (!neckoParent) {
1941 continue;
1943 Unused << neckoParent->SendNetworkChangeNotification(dataAsString);
1946 LOG(("nsIOService::OnNetworkLinkEvent data:%s\n", data));
1947 if (!mNetworkLinkService) {
1948 return NS_ERROR_FAILURE;
1951 if (!mManageLinkStatus) {
1952 LOG(("nsIOService::OnNetworkLinkEvent mManageLinkStatus=false\n"));
1953 return NS_OK;
1956 bool isUp = true;
1957 if (!strcmp(data, NS_NETWORK_LINK_DATA_CHANGED)) {
1958 mLastNetworkLinkChange = PR_IntervalNow();
1959 // CHANGED means UP/DOWN didn't change
1960 // but the status of the captive portal may have changed.
1961 RecheckCaptivePortal();
1962 return NS_OK;
1964 if (!strcmp(data, NS_NETWORK_LINK_DATA_DOWN)) {
1965 isUp = false;
1966 } else if (!strcmp(data, NS_NETWORK_LINK_DATA_UP)) {
1967 isUp = true;
1968 } else if (!strcmp(data, NS_NETWORK_LINK_DATA_UNKNOWN)) {
1969 nsresult rv = mNetworkLinkService->GetIsLinkUp(&isUp);
1970 NS_ENSURE_SUCCESS(rv, rv);
1971 } else {
1972 NS_WARNING("Unhandled network event!");
1973 return NS_OK;
1976 return SetConnectivityInternal(isUp);
1979 NS_IMETHODIMP
1980 nsIOService::EscapeString(const nsACString& aString, uint32_t aEscapeType,
1981 nsACString& aResult) {
1982 NS_ENSURE_ARG_MAX(aEscapeType, 4);
1984 nsAutoCString stringCopy(aString);
1985 nsCString result;
1987 if (!NS_Escape(stringCopy, result, (nsEscapeMask)aEscapeType)) {
1988 return NS_ERROR_OUT_OF_MEMORY;
1991 aResult.Assign(result);
1993 return NS_OK;
1996 NS_IMETHODIMP
1997 nsIOService::EscapeURL(const nsACString& aStr, uint32_t aFlags,
1998 nsACString& aResult) {
1999 aResult.Truncate();
2000 NS_EscapeURL(aStr.BeginReading(), aStr.Length(), aFlags | esc_AlwaysCopy,
2001 aResult);
2002 return NS_OK;
2005 NS_IMETHODIMP
2006 nsIOService::UnescapeString(const nsACString& aStr, uint32_t aFlags,
2007 nsACString& aResult) {
2008 aResult.Truncate();
2009 NS_UnescapeURL(aStr.BeginReading(), aStr.Length(), aFlags | esc_AlwaysCopy,
2010 aResult);
2011 return NS_OK;
2014 NS_IMETHODIMP
2015 nsIOService::ExtractCharsetFromContentType(const nsACString& aTypeHeader,
2016 nsACString& aCharset,
2017 int32_t* aCharsetStart,
2018 int32_t* aCharsetEnd,
2019 bool* aHadCharset) {
2020 nsAutoCString ignored;
2021 net_ParseContentType(aTypeHeader, ignored, aCharset, aHadCharset,
2022 aCharsetStart, aCharsetEnd);
2023 if (*aHadCharset && *aCharsetStart == *aCharsetEnd) {
2024 *aHadCharset = false;
2026 return NS_OK;
2029 // nsISpeculativeConnect
2030 class IOServiceProxyCallback final : public nsIProtocolProxyCallback {
2031 ~IOServiceProxyCallback() = default;
2033 public:
2034 NS_DECL_ISUPPORTS
2035 NS_DECL_NSIPROTOCOLPROXYCALLBACK
2037 IOServiceProxyCallback(nsIInterfaceRequestor* aCallbacks,
2038 nsIOService* aIOService,
2039 Maybe<OriginAttributes>&& aOriginAttributes)
2040 : mCallbacks(aCallbacks),
2041 mIOService(aIOService),
2042 mOriginAttributes(std::move(aOriginAttributes)) {}
2044 private:
2045 RefPtr<nsIInterfaceRequestor> mCallbacks;
2046 RefPtr<nsIOService> mIOService;
2047 Maybe<OriginAttributes> mOriginAttributes;
2050 NS_IMPL_ISUPPORTS(IOServiceProxyCallback, nsIProtocolProxyCallback)
2052 NS_IMETHODIMP
2053 IOServiceProxyCallback::OnProxyAvailable(nsICancelable* request,
2054 nsIChannel* channel, nsIProxyInfo* pi,
2055 nsresult status) {
2056 // Checking proxy status for speculative connect
2057 nsAutoCString type;
2058 if (NS_SUCCEEDED(status) && pi && NS_SUCCEEDED(pi->GetType(type)) &&
2059 !type.EqualsLiteral("direct")) {
2060 // proxies dont do speculative connect
2061 return NS_OK;
2064 nsCOMPtr<nsIURI> uri;
2065 nsresult rv = channel->GetURI(getter_AddRefs(uri));
2066 if (NS_FAILED(rv)) {
2067 return NS_OK;
2070 nsAutoCString scheme;
2071 rv = uri->GetScheme(scheme);
2072 if (NS_FAILED(rv)) return NS_OK;
2074 nsCOMPtr<nsIProtocolHandler> handler;
2075 rv = mIOService->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
2076 if (NS_FAILED(rv)) return NS_OK;
2078 nsCOMPtr<nsISpeculativeConnect> speculativeHandler =
2079 do_QueryInterface(handler);
2080 if (!speculativeHandler) return NS_OK;
2082 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
2083 nsCOMPtr<nsIPrincipal> principal = loadInfo->GetLoadingPrincipal();
2085 nsLoadFlags loadFlags = 0;
2086 channel->GetLoadFlags(&loadFlags);
2087 bool anonymous = !!(loadFlags & nsIRequest::LOAD_ANONYMOUS);
2088 if (mOriginAttributes) {
2089 speculativeHandler->SpeculativeConnectWithOriginAttributesNative(
2090 uri, std::move(mOriginAttributes.ref()), mCallbacks, anonymous);
2091 } else {
2092 speculativeHandler->SpeculativeConnect(uri, principal, mCallbacks,
2093 anonymous);
2096 return NS_OK;
2099 nsresult nsIOService::SpeculativeConnectInternal(
2100 nsIURI* aURI, nsIPrincipal* aPrincipal,
2101 Maybe<OriginAttributes>&& aOriginAttributes,
2102 nsIInterfaceRequestor* aCallbacks, bool aAnonymous) {
2103 NS_ENSURE_ARG(aURI);
2105 if (!aURI->SchemeIs("http") && !aURI->SchemeIs("https")) {
2106 // We don't speculatively connect to non-HTTP[S] URIs.
2107 return NS_OK;
2110 if (IsNeckoChild()) {
2111 gNeckoChild->SendSpeculativeConnect(
2112 aURI, aPrincipal, std::move(aOriginAttributes), aAnonymous);
2113 return NS_OK;
2116 // Check for proxy information. If there is a proxy configured then a
2117 // speculative connect should not be performed because the potential
2118 // reward is slim with tcp peers closely located to the browser.
2119 nsresult rv;
2120 nsCOMPtr<nsIProtocolProxyService> pps;
2121 pps = mozilla::components::ProtocolProxy::Service(&rv);
2122 NS_ENSURE_SUCCESS(rv, rv);
2124 nsCOMPtr<nsIPrincipal> loadingPrincipal = aPrincipal;
2126 MOZ_ASSERT(aPrincipal || aOriginAttributes,
2127 "We expect passing a principal or OriginAttributes here.");
2129 if (!aPrincipal && !aOriginAttributes) {
2130 return NS_ERROR_INVALID_ARG;
2133 if (aOriginAttributes) {
2134 loadingPrincipal =
2135 BasePrincipal::CreateContentPrincipal(aURI, aOriginAttributes.ref());
2138 // XXX Bug 1724080: Avoid TCP connections on port 80 when https-only
2139 // or https-first is enabled. Let's create a dummy loadinfo which we
2140 // only use to determine whether we need ot upgrade the speculative
2141 // connection from http to https.
2142 nsCOMPtr<nsIURI> httpsURI;
2143 if (aURI->SchemeIs("http")) {
2144 nsCOMPtr<nsILoadInfo> httpsOnlyCheckLoadInfo =
2145 new LoadInfo(loadingPrincipal, loadingPrincipal, nullptr,
2146 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
2147 nsIContentPolicy::TYPE_SPECULATIVE);
2149 // Check if https-only, or https-first would upgrade the request
2150 if (nsHTTPSOnlyUtils::ShouldUpgradeRequest(aURI, httpsOnlyCheckLoadInfo) ||
2151 nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(
2152 aURI, httpsOnlyCheckLoadInfo)) {
2153 rv = NS_GetSecureUpgradedURI(aURI, getter_AddRefs(httpsURI));
2154 NS_ENSURE_SUCCESS(rv, rv);
2155 aURI = httpsURI.get();
2159 // dummy channel used to create a TCP connection.
2160 // we perform security checks on the *real* channel, responsible
2161 // for any network loads. this real channel just checks the TCP
2162 // pool if there is an available connection created by the
2163 // channel we create underneath - hence it's safe to use
2164 // the systemPrincipal as the loadingPrincipal for this channel.
2165 nsCOMPtr<nsIChannel> channel;
2166 rv = NewChannelFromURI(
2167 aURI,
2168 nullptr, // aLoadingNode,
2169 loadingPrincipal,
2170 nullptr, // aTriggeringPrincipal,
2171 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
2172 nsIContentPolicy::TYPE_SPECULATIVE, getter_AddRefs(channel));
2173 NS_ENSURE_SUCCESS(rv, rv);
2175 if (aAnonymous) {
2176 nsLoadFlags loadFlags = 0;
2177 channel->GetLoadFlags(&loadFlags);
2178 loadFlags |= nsIRequest::LOAD_ANONYMOUS;
2179 channel->SetLoadFlags(loadFlags);
2182 nsCOMPtr<nsICancelable> cancelable;
2183 RefPtr<IOServiceProxyCallback> callback = new IOServiceProxyCallback(
2184 aCallbacks, this, std::move(aOriginAttributes));
2185 nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
2186 if (pps2) {
2187 return pps2->AsyncResolve2(channel, 0, callback, nullptr,
2188 getter_AddRefs(cancelable));
2190 return pps->AsyncResolve(channel, 0, callback, nullptr,
2191 getter_AddRefs(cancelable));
2194 NS_IMETHODIMP
2195 nsIOService::SpeculativeConnect(nsIURI* aURI, nsIPrincipal* aPrincipal,
2196 nsIInterfaceRequestor* aCallbacks,
2197 bool aAnonymous) {
2198 return SpeculativeConnectInternal(aURI, aPrincipal, Nothing(), aCallbacks,
2199 aAnonymous);
2202 NS_IMETHODIMP nsIOService::SpeculativeConnectWithOriginAttributes(
2203 nsIURI* aURI, JS::Handle<JS::Value> aOriginAttributes,
2204 nsIInterfaceRequestor* aCallbacks, bool aAnonymous, JSContext* aCx) {
2205 OriginAttributes attrs;
2206 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
2207 return NS_ERROR_INVALID_ARG;
2210 SpeculativeConnectWithOriginAttributesNative(aURI, std::move(attrs),
2211 aCallbacks, aAnonymous);
2212 return NS_OK;
2215 NS_IMETHODIMP_(void)
2216 nsIOService::SpeculativeConnectWithOriginAttributesNative(
2217 nsIURI* aURI, OriginAttributes&& aOriginAttributes,
2218 nsIInterfaceRequestor* aCallbacks, bool aAnonymous) {
2219 Maybe<OriginAttributes> originAttributes;
2220 originAttributes.emplace(aOriginAttributes);
2221 Unused << SpeculativeConnectInternal(
2222 aURI, nullptr, std::move(originAttributes), aCallbacks, aAnonymous);
2225 NS_IMETHODIMP
2226 nsIOService::NotImplemented() { return NS_ERROR_NOT_IMPLEMENTED; }
2228 NS_IMETHODIMP
2229 nsIOService::GetSocketProcessLaunched(bool* aResult) {
2230 NS_ENSURE_ARG_POINTER(aResult);
2232 *aResult = SocketProcessReady();
2233 return NS_OK;
2236 bool nsIOService::HasObservers(const char* aTopic) {
2237 MOZ_ASSERT(false, "Calling this method is unexpected");
2238 return false;
2241 NS_IMETHODIMP
2242 nsIOService::GetSocketProcessId(uint64_t* aPid) {
2243 NS_ENSURE_ARG_POINTER(aPid);
2245 *aPid = 0;
2246 if (!mSocketProcess) {
2247 return NS_OK;
2250 if (SocketProcessParent* actor = mSocketProcess->GetActor()) {
2251 *aPid = (uint64_t)actor->OtherPid();
2254 return NS_OK;
2257 NS_IMETHODIMP
2258 nsIOService::RegisterProtocolHandler(const nsACString& aScheme,
2259 nsIProtocolHandler* aHandler,
2260 uint32_t aProtocolFlags,
2261 int32_t aDefaultPort) {
2262 if (mShutdown) {
2263 return NS_ERROR_NOT_AVAILABLE;
2265 if (aScheme.IsEmpty()) {
2266 return NS_ERROR_INVALID_ARG;
2269 nsAutoCString scheme(aScheme);
2270 ToLowerCase(scheme);
2272 AutoWriteLock lock(mLock);
2273 return mRuntimeProtocolHandlers.WithEntryHandle(scheme, [&](auto&& entry) {
2274 if (entry) {
2275 NS_WARNING("Cannot override an existing dynamic protocol handler");
2276 return NS_ERROR_FACTORY_EXISTS;
2278 if (xpcom::StaticProtocolHandler::Lookup(scheme)) {
2279 NS_WARNING("Cannot override an existing static protocol handler");
2280 return NS_ERROR_FACTORY_EXISTS;
2282 nsMainThreadPtrHandle<nsIProtocolHandler> handler(
2283 new nsMainThreadPtrHolder<nsIProtocolHandler>("RuntimeProtocolHandler",
2284 aHandler));
2285 entry.Insert(RuntimeProtocolHandler{
2286 .mHandler = std::move(handler),
2287 .mProtocolFlags = aProtocolFlags,
2288 .mDefaultPort = aDefaultPort,
2290 return NS_OK;
2294 NS_IMETHODIMP
2295 nsIOService::UnregisterProtocolHandler(const nsACString& aScheme) {
2296 if (mShutdown) {
2297 return NS_OK;
2299 if (aScheme.IsEmpty()) {
2300 return NS_ERROR_INVALID_ARG;
2303 nsAutoCString scheme(aScheme);
2304 ToLowerCase(scheme);
2306 AutoWriteLock lock(mLock);
2307 return mRuntimeProtocolHandlers.Remove(scheme)
2308 ? NS_OK
2309 : NS_ERROR_FACTORY_NOT_REGISTERED;
2312 NS_IMETHODIMP
2313 nsIOService::SetSimpleURIUnknownRemoteSchemes(
2314 const nsTArray<nsCString>& aRemoteSchemes) {
2315 LOG(("nsIOService::SetSimpleUriUnknownRemoteSchemes"));
2316 mSimpleURIUnknownSchemes.SetAndMergeRemoteSchemes(aRemoteSchemes);
2318 if (XRE_IsParentProcess()) {
2319 // since we only expect socket, parent and content processes to create URLs
2320 // that need to check the bypass list
2321 // we only broadcast the list to content processes
2322 // (and leave socket process broadcast as todo if necessary)
2324 // sending only the remote-settings schemes to the content,
2325 // which already has the pref list
2326 for (auto* cp : mozilla::dom::ContentParent::AllProcesses(
2327 mozilla::dom::ContentParent::eLive)) {
2328 Unused << cp->SendSimpleURIUnknownRemoteSchemes(aRemoteSchemes);
2331 return NS_OK;
2334 NS_IMETHODIMP
2335 nsIOService::IsSimpleURIUnknownScheme(const nsACString& aScheme,
2336 bool* _retval) {
2337 *_retval = mSimpleURIUnknownSchemes.IsSimpleURIUnknownScheme(aScheme);
2338 return NS_OK;
2341 NS_IMETHODIMP
2342 nsIOService::GetSimpleURIUnknownRemoteSchemes(nsTArray<nsCString>& _retval) {
2343 mSimpleURIUnknownSchemes.GetRemoteSchemes(_retval);
2344 return NS_OK;
2347 } // namespace net
2348 } // namespace mozilla