/* -*- 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/dom/MemoryReportRequest.h" #include "mozilla/ipc/CrashReporterClient.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/FileDescriptorSetChild.h" #include "mozilla/ipc/IPCStreamAlloc.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/TRRServiceChild.h" #include "mozilla/ipc/PChildToParentStreamChild.h" #include "mozilla/ipc/PParentToChildStreamChild.h" #include "mozilla/Preferences.h" #include "mozilla/RemoteLazyInputStreamChild.h" #include "mozilla/Telemetry.h" #include "nsDebugImpl.h" #include "nsHttpConnectionInfo.h" #include "nsHttpHandler.h" #include "nsIDNSService.h" #include "nsIHttpActivityObserver.h" #include "nsNetUtil.h" #include "nsNSSComponent.h" #include "nsSocketTransportService2.h" #include "nsThreadManager.h" #include "ProcessUtils.h" #include "SocketProcessBridgeParent.h" #if defined(XP_WIN) # include #else # include #endif #if defined(XP_LINUX) && defined(MOZ_SANDBOX) # include "mozilla/Sandbox.h" #endif #ifdef MOZ_GECKO_PROFILER # include "ChildProfilerController.h" #endif #ifdef MOZ_WEBRTC # include "mozilla/net/WebrtcTCPSocketChild.h" #endif namespace mozilla { namespace net { using namespace ipc; SocketProcessChild* sSocketProcessChild; SocketProcessChild::SocketProcessChild() : mShuttingDown(false), mMutex("SocketProcessChild::mMutex") { 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(base::ProcessId aParentPid, const char* aParentBuildID, MessageLoop* aIOLoop, UniquePtr aChannel) { if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) { return false; } if (NS_WARN_IF(!Open(std::move(aChannel), aParentPid, aIOLoop))) { 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(); 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; } 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(); } #ifdef MOZ_GECKO_PROFILER if (mProfilerController) { mProfilerController->Shutdown(); mProfilerController = nullptr; } #endif CrashReporterClient::DestroySingleton(); XRE_ShutdownChildProcess(); } void SocketProcessChild::CleanUp() { LOG(("SocketProcessChild::CleanUp\n")); for (auto iter = mSocketProcessBridgeParentMap.Iter(); !iter.Done(); iter.Next()) { if (!iter.Data()->Closed()) { iter.Data()->Close(); } } { MutexAutoLock lock(mMutex); mBackgroundDataBridgeMap.Clear(); } NS_ShutdownXPCOM(nullptr); } mozilla::ipc::IPCResult SocketProcessChild::RecvInit( const SocketPorcessInitAttributes& aAttributes) { Unused << RecvSetOffline(aAttributes.mOffline()); Unused << RecvSetConnectivity(aAttributes.mConnectivity()); if (aAttributes.mInitSandbox()) { Unused << RecvInitLinuxSandbox(aAttributes.mSandboxBroker()); } 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.Get(aContentProcessId, nullptr)); mSocketProcessBridgeParentMap.Put( aContentProcessId, MakeRefPtr( aContentProcessId, std::move(aEndpoint))); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvInitProfiler( Endpoint&& aEndpoint) { #ifdef MOZ_GECKO_PROFILER mProfilerController = mozilla::ChildProfilerController::Create(std::move(aEndpoint)); #endif return IPC_OK(); } 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(); } PFileDescriptorSetChild* SocketProcessChild::AllocPFileDescriptorSetChild( const FileDescriptor& aFD) { return new FileDescriptorSetChild(aFD); } bool SocketProcessChild::DeallocPFileDescriptorSetChild( PFileDescriptorSetChild* aActor) { delete aActor; return true; } PChildToParentStreamChild* SocketProcessChild::AllocPChildToParentStreamChild() { MOZ_CRASH("PChildToParentStreamChild actors should be manually constructed!"); } bool SocketProcessChild::DeallocPChildToParentStreamChild( PChildToParentStreamChild* aActor) { delete aActor; return true; } PParentToChildStreamChild* SocketProcessChild::AllocPParentToChildStreamChild() { return mozilla::ipc::AllocPParentToChildStreamChild(); } bool SocketProcessChild::DeallocPParentToChildStreamChild( PParentToChildStreamChild* aActor) { delete aActor; return true; } PChildToParentStreamChild* SocketProcessChild::SendPChildToParentStreamConstructor( PChildToParentStreamChild* aActor) { MOZ_ASSERT(NS_IsMainThread()); return PSocketProcessChild::SendPChildToParentStreamConstructor(aActor); } PFileDescriptorSetChild* SocketProcessChild::SendPFileDescriptorSetConstructor( const FileDescriptor& aFD) { MOZ_ASSERT(NS_IsMainThread()); return PSocketProcessChild::SendPFileDescriptorSetConstructor(aFD); } 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 nsCString& aModelId) { MOZ_ASSERT(gHttpHandler); gHttpHandler->SetDeviceModelId(aModelId); return IPC_OK(); } mozilla::ipc::IPCResult SocketProcessChild::RecvOnHttpActivityDistributorActivated( const bool& aIsActivated) { if (nsCOMPtr distributor = services::GetHttpActivityDistributor()) { distributor->SetIsActive(aIsActivated); } 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 nsCString& aHost, const nsCString& aTrrServer, const uint16_t& aType, const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) { RefPtr handler = new DNSRequestHandler(); RefPtr actor = new DNSRequestChild(handler); return actor.forget(); } mozilla::ipc::IPCResult SocketProcessChild::RecvPDNSRequestConstructor( PDNSRequestChild* aActor, const nsCString& aHost, const nsCString& aTrrServer, const uint16_t& aType, const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) { RefPtr actor = static_cast(aActor); RefPtr handler = actor->GetDNSRequest()->AsDNSRequestHandler(); handler->DoAsyncResolve(aHost, aTrrServer, aType, aOriginAttributes, aFlags); return IPC_OK(); } void SocketProcessChild::AddDataBridgeToMap( uint64_t aChannelId, BackgroundDataBridgeParent* aActor) { ipc::AssertIsOnBackgroundThread(); MutexAutoLock lock(mMutex); mBackgroundDataBridgeMap.Put(aChannelId, 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.GetAndRemove(aChannelId); } mozilla::ipc::IPCResult SocketProcessChild::RecvClearSessionCache() { if (EnsureNSSInitializedChromeOrContent()) { nsNSSComponent::DoClearSSLExternalAndInternalSessionCache(); } 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 nsCString& aTopic, const nsString& aData) { nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { obs->NotifyObservers(nullptr, aTopic.get(), aData.get()); } return IPC_OK(); } already_AddRefed SocketProcessChild::AllocPRemoteLazyInputStreamChild(const nsID& aID, const uint64_t& aSize) { RefPtr actor = new RemoteLazyInputStreamChild(aID, aSize); return actor.forget(); } 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(); } } // namespace net } // namespace mozilla