/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SocketProcessChild.h" #include "SocketProcessLogging.h" #include "base/task.h" #include "InputChannelThrottleQueueChild.h" #include "HttpInfo.h" #include "HttpTransactionChild.h" #include "HttpConnectionMgrChild.h" #include "mozilla/Assertions.h" #include "mozilla/Atomics.h" #include "mozilla/Components.h" #include "mozilla/dom/MemoryReportRequest.h" #include "mozilla/FOGIPC.h" #include "mozilla/glean/GleanMetrics.h" #include "mozilla/ipc/CrashReporterClient.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/ProcessChild.h" #include "mozilla/net/AltSvcTransactionChild.h" #include "mozilla/net/BackgroundDataBridgeParent.h" #include "mozilla/net/DNSRequestChild.h" #include "mozilla/net/DNSRequestParent.h" #include "mozilla/net/NativeDNSResolverOverrideChild.h" #include "mozilla/net/ProxyAutoConfigChild.h" #include "mozilla/net/TRRServiceChild.h" #include "mozilla/ipc/ProcessUtils.h" #include "mozilla/Preferences.h" #include "mozilla/RemoteLazyInputStreamChild.h" #include "mozilla/StaticPrefs_network.h" #include "mozilla/Telemetry.h" #include "NetworkConnectivityService.h" #include "nsDebugImpl.h" #include "nsHttpConnectionInfo.h" #include "nsHttpHandler.h" #include "nsIDNSService.h" #include "nsIHttpActivityObserver.h" #include "nsIXULRuntime.h" #include "nsNetUtil.h" #include "nsNSSComponent.h" #include "nsSocketTransportService2.h" #include "nsThreadManager.h" #include "SocketProcessBridgeParent.h" #include "jsapi.h" #include "js/Initialization.h" #include "XPCSelfHostedShmem.h" #if defined(XP_WIN) # include # include "mozilla/WinDllServices.h" #else # include #endif #if defined(XP_LINUX) && defined(MOZ_SANDBOX) # include "mozilla/Sandbox.h" #endif #include "ChildProfilerController.h" #ifdef MOZ_WEBRTC # include "mozilla/net/WebrtcTCPSocketChild.h" #endif #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) # include "mozilla/SandboxTestingChild.h" #endif namespace mozilla { namespace net { using namespace ipc; static bool sInitializedJS = false; static Atomic sSocketProcessChild; SocketProcessChild::SocketProcessChild() { LOG(("CONSTRUCT SocketProcessChild::SocketProcessChild\n")); nsDebugImpl::SetMultiprocessMode("Socket"); MOZ_COUNT_CTOR(SocketProcessChild); sSocketProcessChild = this; } SocketProcessChild::~SocketProcessChild() { LOG(("DESTRUCT SocketProcessChild::SocketProcessChild\n")); MOZ_COUNT_DTOR(SocketProcessChild); sSocketProcessChild = nullptr; } /* static */ SocketProcessChild* SocketProcessChild::GetSingleton() { return sSocketProcessChild; } #if defined(XP_MACOSX) extern "C" { void CGSShutdownServerConnections(); }; #endif bool SocketProcessChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint, const char* aParentBuildID) { if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) { return false; } if (NS_WARN_IF(!aEndpoint.Bind(this))) { return false; } // This must be sent before any IPDL message, which may hit sentinel // errors due to parent and content processes having different // versions. MessageChannel* channel = GetIPCChannel(); if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) { // We need to quit this process if the buildID doesn't match the parent's. // This can occur when an update occurred in the background. ProcessChild::QuickExit(); } // Init crash reporter support. CrashReporterClient::InitSingleton(this); if (NS_FAILED(NS_InitMinimalXPCOM())) { return false; } BackgroundChild::Startup(); BackgroundChild::InitSocketStarter(this); SetThisProcessName("Socket Process"); #if defined(XP_MACOSX) // Close all current connections to the WindowServer. This ensures that the // Activity Monitor will not label the socket process as "Not responding" // because it's not running a native event loop. See bug 1384336. CGSShutdownServerConnections(); #endif // XP_MACOSX nsresult rv; nsCOMPtr ios = do_GetIOService(&rv); if (NS_FAILED(rv)) { return false; } nsCOMPtr handler; rv = ios->GetProtocolHandler("http", getter_AddRefs(handler)); if (NS_FAILED(rv)) { return false; } // Initialize DNS Service here, since it needs to be done in main thread. nsCOMPtr dns = do_GetService("@mozilla.org/network/dns-service;1", &rv); if (NS_FAILED(rv)) { return false; } if (!EnsureNSSInitializedChromeOrContent()) { return false; } return true; } void SocketProcessChild::ActorDestroy(ActorDestroyReason aWhy) { LOG(("SocketProcessChild::ActorDestroy\n")); mShuttingDown = true; if (AbnormalShutdown == aWhy) { NS_WARNING("Shutting down Socket process early due to a crash!"); ProcessChild::QuickExit(); } // Send the last bits of Glean data over to the main process. glean::FlushFOGData( [](ByteBuf&& aBuf) { glean::SendFOGData(std::move(aBuf)); }); if (mProfilerController) { mProfilerController->Shutdown(); mProfilerController = nullptr; } CrashReporterClient::DestroySingleton(); XRE_ShutdownChildProcess(); } void SocketProcessChild::CleanUp() { LOG(("SocketProcessChild::CleanUp\n")); for (const auto& parent : mSocketProcessBridgeParentMap.Values()) { if (!parent->Closed()) { parent->Close(); } } { MutexAutoLock lock(mMutex); mBackgroundDataBridgeMap.Clear(); } // Normally, the IPC channel should be already closed at this point, but // sometimes it's not (bug 1788860). When the channel is closed, calling // Close() again is harmless. Close(); NS_ShutdownXPCOM(nullptr); if (sInitializedJS) { JS_ShutDown(); } } mozilla::ipc::IPCResult SocketProcessChild::RecvInit( const SocketPorcessInitAttributes& aAttributes) { Unused << RecvSetOffline(aAttributes.mOffline()); Unused << RecvSetConnectivity(aAttributes.mConnectivity()); if (aAttributes.mInitSandbox()) { Unused << RecvInitLinuxSandbox(aAttributes.mSandboxBroker()); } #if defined(XP_WIN) RefPtr dllSvc(DllServices::Get()); dllSvc->StartUntrustedModulesProcessor( aAttributes.mIsReadyForBackgroundProcessing()); #endif // defined(XP_WIN) return IPC_OK(); } IPCResult SocketProcessChild::RecvPreferenceUpdate(const Pref& aPref) { Preferences::SetPreference(aPref); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvRequestMemoryReport( const uint32_t& aGeneration, const bool& aAnonymize, const bool& aMinimizeMemoryUsage, const Maybe& aDMDFile, const RequestMemoryReportResolver& aResolver) { nsPrintfCString processName("Socket (pid %u)", (unsigned)getpid()); mozilla::dom::MemoryReportRequestClient::Start( aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName, [&](const MemoryReport& aReport) { Unused << GetSingleton()->SendAddMemoryReport(aReport); }, aResolver); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvSetOffline( const bool& aOffline) { LOG(("SocketProcessChild::RecvSetOffline aOffline=%d\n", aOffline)); nsCOMPtr io(do_GetIOService()); NS_ASSERTION(io, "IO Service can not be null"); io->SetOffline(aOffline); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvSetConnectivity( const bool& aConnectivity) { nsCOMPtr io(do_GetIOService()); nsCOMPtr ioInternal(do_QueryInterface(io)); NS_ASSERTION(ioInternal, "IO Service can not be null"); ioInternal->SetConnectivity(aConnectivity); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvInitLinuxSandbox( const Maybe& aBrokerFd) { #if defined(XP_LINUX) && defined(MOZ_SANDBOX) int fd = -1; if (aBrokerFd.isSome()) { fd = aBrokerFd.value().ClonePlatformHandle().release(); } SetSocketProcessSandbox(fd); #endif // XP_LINUX && MOZ_SANDBOX return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvInitSocketProcessBridgeParent( const ProcessId& aContentProcessId, Endpoint&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mSocketProcessBridgeParentMap.Contains(aContentProcessId)); if (NS_WARN_IF(!aEndpoint.IsValid())) { return IPC_FAIL(this, "invalid endpoint"); } auto bridge = MakeRefPtr(aContentProcessId); MOZ_ALWAYS_TRUE(aEndpoint.Bind(bridge)); mSocketProcessBridgeParentMap.InsertOrUpdate(aContentProcessId, std::move(bridge)); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvInitProfiler( Endpoint&& aEndpoint) { mProfilerController = mozilla::ChildProfilerController::Create(std::move(aEndpoint)); return IPC_OK(); } #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) mozilla::ipc::IPCResult SocketProcessChild::RecvInitSandboxTesting( Endpoint&& aEndpoint) { if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) { return IPC_FAIL( this, "InitSandboxTesting failed to initialise the child process."); } return IPC_OK(); } #endif mozilla::ipc::IPCResult SocketProcessChild::RecvSocketProcessTelemetryPing() { const uint32_t kExpectedUintValue = 42; Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_SOCKET_ONLY_UINT, kExpectedUintValue); return IPC_OK(); } void SocketProcessChild::DestroySocketProcessBridgeParent(ProcessId aId) { MOZ_ASSERT(NS_IsMainThread()); mSocketProcessBridgeParentMap.Remove(aId); } PWebrtcTCPSocketChild* SocketProcessChild::AllocPWebrtcTCPSocketChild( const Maybe& tabId) { // We don't allocate here: instead we always use IPDL constructor that takes // an existing object MOZ_ASSERT_UNREACHABLE( "AllocPWebrtcTCPSocketChild should not be called on" " socket child"); return nullptr; } bool SocketProcessChild::DeallocPWebrtcTCPSocketChild( PWebrtcTCPSocketChild* aActor) { #ifdef MOZ_WEBRTC WebrtcTCPSocketChild* child = static_cast(aActor); child->ReleaseIPDLReference(); #endif return true; } already_AddRefed SocketProcessChild::AllocPHttpTransactionChild() { RefPtr actor = new HttpTransactionChild(); return actor.forget(); } already_AddRefed SocketProcessChild::AllocPHttpConnectionMgrChild( const HttpHandlerInitArgs& aArgs) { LOG(("SocketProcessChild::AllocPHttpConnectionMgrChild \n")); MOZ_ASSERT(gHttpHandler); gHttpHandler->SetHttpHandlerInitArgs(aArgs); RefPtr actor = new HttpConnectionMgrChild(); return actor.forget(); } mozilla::ipc::IPCResult SocketProcessChild::RecvUpdateDeviceModelId( const nsACString& aModelId) { MOZ_ASSERT(gHttpHandler); gHttpHandler->SetDeviceModelId(aModelId); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvOnHttpActivityDistributorActivated( const bool& aIsActivated) { if (nsCOMPtr distributor = components::HttpActivityDistributor::Service()) { distributor->SetIsActive(aIsActivated); } return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvOnHttpActivityDistributorObserveProxyResponse( const bool& aIsEnabled) { nsCOMPtr distributor = do_GetService("@mozilla.org/network/http-activity-distributor;1"); if (distributor) { Unused << distributor->SetObserveProxyResponse(aIsEnabled); } return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvOnHttpActivityDistributorObserveConnection( const bool& aIsEnabled) { nsCOMPtr distributor = do_GetService("@mozilla.org/network/http-activity-distributor;1"); if (distributor) { Unused << distributor->SetObserveConnection(aIsEnabled); } return IPC_OK(); } already_AddRefed SocketProcessChild::AllocPInputChannelThrottleQueueChild( const uint32_t& aMeanBytesPerSecond, const uint32_t& aMaxBytesPerSecond) { RefPtr p = new InputChannelThrottleQueueChild(); p->Init(aMeanBytesPerSecond, aMaxBytesPerSecond); return p.forget(); } already_AddRefed SocketProcessChild::AllocPAltSvcTransactionChild( const HttpConnectionInfoCloneArgs& aConnInfo, const uint32_t& aCaps) { RefPtr cinfo = nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(aConnInfo); RefPtr child = new AltSvcTransactionChild(cinfo, aCaps); return child.forget(); } already_AddRefed SocketProcessChild::AllocPDNSRequestChild( const nsACString& aHost, const nsACString& aTrrServer, const int32_t& aPort, const uint16_t& aType, const OriginAttributes& aOriginAttributes, const nsIDNSService::DNSFlags& aFlags) { RefPtr handler = new DNSRequestHandler(); RefPtr actor = new DNSRequestChild(handler); return actor.forget(); } mozilla::ipc::IPCResult SocketProcessChild::RecvPDNSRequestConstructor( PDNSRequestChild* aActor, const nsACString& aHost, const nsACString& aTrrServer, const int32_t& aPort, const uint16_t& aType, const OriginAttributes& aOriginAttributes, const nsIDNSService::DNSFlags& aFlags) { RefPtr actor = static_cast(aActor); RefPtr handler = actor->GetDNSRequest()->AsDNSRequestHandler(); handler->DoAsyncResolve(aHost, aTrrServer, aPort, aType, aOriginAttributes, aFlags); return IPC_OK(); } void SocketProcessChild::AddDataBridgeToMap( uint64_t aChannelId, BackgroundDataBridgeParent* aActor) { ipc::AssertIsOnBackgroundThread(); MutexAutoLock lock(mMutex); mBackgroundDataBridgeMap.InsertOrUpdate(aChannelId, RefPtr{aActor}); } void SocketProcessChild::RemoveDataBridgeFromMap(uint64_t aChannelId) { ipc::AssertIsOnBackgroundThread(); MutexAutoLock lock(mMutex); mBackgroundDataBridgeMap.Remove(aChannelId); } Maybe> SocketProcessChild::GetAndRemoveDataBridge(uint64_t aChannelId) { MutexAutoLock lock(mMutex); return mBackgroundDataBridgeMap.Extract(aChannelId); } mozilla::ipc::IPCResult SocketProcessChild::RecvClearSessionCache( ClearSessionCacheResolver&& aResolve) { nsNSSComponent::DoClearSSLExternalAndInternalSessionCache(); aResolve(void_t{}); return IPC_OK(); } already_AddRefed SocketProcessChild::AllocPTRRServiceChild( const bool& aCaptiveIsPassed, const bool& aParentalControlEnabled, const nsTArray& aDNSSuffixList) { RefPtr actor = new TRRServiceChild(); return actor.forget(); } mozilla::ipc::IPCResult SocketProcessChild::RecvPTRRServiceConstructor( PTRRServiceChild* aActor, const bool& aCaptiveIsPassed, const bool& aParentalControlEnabled, nsTArray&& aDNSSuffixList) { static_cast(aActor)->Init( aCaptiveIsPassed, aParentalControlEnabled, std::move(aDNSSuffixList)); return IPC_OK(); } already_AddRefed SocketProcessChild::AllocPNativeDNSResolverOverrideChild() { RefPtr actor = new NativeDNSResolverOverrideChild(); return actor.forget(); } mozilla::ipc::IPCResult SocketProcessChild::RecvPNativeDNSResolverOverrideConstructor( PNativeDNSResolverOverrideChild* aActor) { return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvNotifyObserver( const nsACString& aTopic, const nsAString& aData) { if (nsCOMPtr obs = mozilla::services::GetObserverService()) { obs->NotifyObservers(nullptr, PromiseFlatCString(aTopic).get(), PromiseFlatString(aData).get()); } return IPC_OK(); } namespace { class DataResolverBase { public: // This type is threadsafe-refcounted, as it's referenced on the socket // thread, but must be destroyed on the main thread. NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD( DataResolverBase) DataResolverBase() = default; protected: virtual ~DataResolverBase() = default; }; template class DataResolver final : public DataResolverBase { public: explicit DataResolver(ResolverType&& aResolve) : mResolve(std::move(aResolve)) {} void OnResolve(DataType&& aData) { MOZ_ASSERT(OnSocketThread()); RefPtr> self = this; mData = std::move(aData); NS_DispatchToMainThread(NS_NewRunnableFunction( "net::DataResolver::OnResolve", [self{std::move(self)}]() { self->mResolve(std::move(self->mData)); })); } private: virtual ~DataResolver() = default; ResolverType mResolve; DataType mData; }; } // anonymous namespace mozilla::ipc::IPCResult SocketProcessChild::RecvGetSocketData( GetSocketDataResolver&& aResolve) { if (!gSocketTransportService) { aResolve(SocketDataArgs()); return IPC_OK(); } RefPtr< DataResolver> resolver = new DataResolver( std::move(aResolve)); gSocketTransportService->Dispatch( NS_NewRunnableFunction( "net::SocketProcessChild::RecvGetSocketData", [resolver{std::move(resolver)}]() { SocketDataArgs args; gSocketTransportService->GetSocketConnections(&args.info()); args.totalSent() = gSocketTransportService->GetSentBytes(); args.totalRecv() = gSocketTransportService->GetReceivedBytes(); resolver->OnResolve(std::move(args)); }), NS_DISPATCH_NORMAL); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvGetDNSCacheEntries( GetDNSCacheEntriesResolver&& aResolve) { nsresult rv = NS_OK; nsCOMPtr dns = do_GetService("@mozilla.org/network/dns-service;1", &rv); if (NS_FAILED(rv)) { aResolve(nsTArray()); return IPC_OK(); } RefPtr, SocketProcessChild::GetDNSCacheEntriesResolver>> resolver = new DataResolver, SocketProcessChild::GetDNSCacheEntriesResolver>( std::move(aResolve)); gSocketTransportService->Dispatch( NS_NewRunnableFunction( "net::SocketProcessChild::RecvGetDNSCacheEntries", [resolver{std::move(resolver)}, dns{std::move(dns)}]() { nsTArray entries; dns->GetDNSCacheEntries(&entries); resolver->OnResolve(std::move(entries)); }), NS_DISPATCH_NORMAL); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvGetHttpConnectionData( GetHttpConnectionDataResolver&& aResolve) { if (!gSocketTransportService) { aResolve(nsTArray()); return IPC_OK(); } RefPtr, SocketProcessChild::GetHttpConnectionDataResolver>> resolver = new DataResolver, SocketProcessChild::GetHttpConnectionDataResolver>( std::move(aResolve)); gSocketTransportService->Dispatch( NS_NewRunnableFunction( "net::SocketProcessChild::RecvGetHttpConnectionData", [resolver{std::move(resolver)}]() { nsTArray data; HttpInfo::GetHttpConnectionData(&data); resolver->OnResolve(std::move(data)); }), NS_DISPATCH_NORMAL); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvInitProxyAutoConfigChild( Endpoint&& aEndpoint) { // For parsing PAC. if (!sInitializedJS) { JS::DisableJitBackend(); const char* jsInitFailureReason = JS_InitWithFailureDiagnostic(); if (jsInitFailureReason) { MOZ_CRASH_UNSAFE(jsInitFailureReason); } sInitializedJS = true; xpc::SelfHostedShmem::GetSingleton(); } Unused << ProxyAutoConfigChild::Create(std::move(aEndpoint)); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvRecheckIPConnectivity() { RefPtr ncs = NetworkConnectivityService::GetSingleton(); if (ncs) { ncs->RecheckIPConnectivity(); } return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvRecheckDNS() { RefPtr ncs = NetworkConnectivityService::GetSingleton(); if (ncs) { ncs->RecheckDNS(); } return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvFlushFOGData( FlushFOGDataResolver&& aResolver) { glean::FlushFOGData(std::move(aResolver)); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvTestTriggerMetrics( TestTriggerMetricsResolver&& aResolve) { mozilla::glean::test_only_ipc::a_counter.Add( nsIXULRuntime::PROCESS_TYPE_SOCKET); aResolve(true); return IPC_OK(); } #if defined(XP_WIN) mozilla::ipc::IPCResult SocketProcessChild::RecvGetUntrustedModulesData( GetUntrustedModulesDataResolver&& aResolver) { RefPtr dllSvc(DllServices::Get()); dllSvc->GetUntrustedModulesData()->Then( GetMainThreadSerialEventTarget(), __func__, [aResolver](Maybe&& aData) { aResolver(std::move(aData)); }, [aResolver](nsresult aReason) { aResolver(Nothing()); }); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvUnblockUntrustedModulesThread() { if (nsCOMPtr obs = mozilla::services::GetObserverService()) { obs->NotifyObservers(nullptr, "unblock-untrusted-modules-thread", nullptr); } return IPC_OK(); } #endif // defined(XP_WIN) } // namespace net } // namespace mozilla