/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=4 sw=2 cindent et: */ /* 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/. */
#ifdef MOZ_WIDGET_ANDROID # include <regex> # include "AndroidBridge.h" # include "mozilla/java/GeckoAppShellWrappers.h" # include "mozilla/jni/Utils.h" #endif
namespace mozilla { namespace net {
using mozilla::Maybe; using mozilla::dom::ClientInfo; using mozilla::dom::ServiceWorkerDescriptor;
#define PORT_PREF_PREFIX "network.security.ports." #define PORT_PREF(x) PORT_PREF_PREFIX x #define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status"
// Nb: these have been misnomers since bug 715770 removed the buffer cache. // "network.segment.count" and "network.segment.size" would be better names, // but the old names are still used to preserve backward compatibility. #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count" #define NECKO_BUFFER_CACHE_SIZE_PREF "network.buffer.cache.size" #define NETWORK_CAPTIVE_PORTAL_PREF "network.captive-portal-service.enabled" #define WEBRTC_PREF_PREFIX "media.peerconnection." #define NETWORK_DNS_PREF "network.dns." #define FORCE_EXTERNAL_PREF_PREFIX "network.protocol-handler.external."
// A general port blacklist. Connections to these ports will not be allowed // unless the protocol overrides. // // This list is to be kept in sync with "bad ports" as defined in the // WHATWG Fetch standard at <https://fetch.spec.whatwg.org/#port-blocking>
staticconstchar* gCallbackSecurityPrefs[] = { // Note the prefs listed below should be in sync with the code in // HandleTLSPrefChange(). "security.tls.version.min", "security.tls.version.max", "security.tls.version.enable-deprecated", "security.tls.hello_downgrade_check", "security.ssl.require_safe_negotiation", "security.ssl.enable_false_start", "security.ssl.enable_alpn", "security.tls.enable_0rtt_data", "security.ssl.disable_session_identifiers", "security.tls.enable_post_handshake_auth", "security.tls.enable_delegated_credentials",
nullptr,
};
// setup our bad port list stuff for (int i = 0; gBadPortList[i]; i++) { // We can't be accessed by another thread yet
MOZ_PUSH_IGNORE_THREAD_SAFETY
mRestrictedPortList.AppendElement(gBadPortList[i]);
MOZ_POP_THREAD_SAFETY
}
// Further modifications to the port list come from prefs
Preferences::RegisterPrefixCallbacks(nsIOService::PrefsChanged,
gCallbackPrefs, this);
PrefsChanged();
// Register for the origional observer.
nsresult rv = mObserverService->AddObserver(aObserver, aTopic, aOwnsWeak); if (NS_FAILED(rv)) { return rv;
}
if (!XRE_IsParentProcess()) { return NS_OK;
}
nsAutoCString topic(aTopic); // This happens when AddObserver() is called by nsIOService::Init(). We don't // want to add nsIOService again. if (SameCOMIdentity(aObserver, static_cast<nsIObserver*>(this))) {
mIOServiceTopicList.Insert(topic); return NS_OK;
}
if (!UseSocketProcess()) { return NS_OK;
}
if (mSocketProcessTopicBlockedList.Contains(topic)) { return NS_ERROR_FAILURE;
}
// We need to improve below logic for matching google domains // See Bug 1894642 // Is URI same as google ^https://www\\.google\\..+
nsAutoCString host;
aURI->GetHost(host);
LOG(("nsIOService::ShouldAddAdditionalSearchHeaders() checking host %s\n",
PromiseFlatCString(host).get()));
if (!EnsureNSSInitializedChromeOrContent()) {
LOG(("NSS not initialized.")); return;
}
nsAutoCString pref(aPref); // The preferences listed in gCallbackSecurityPrefs need to be in sync with // the code in HandleTLSPrefChange(). if (HandleTLSPrefChange(pref)) {
LOG(("HandleTLSPrefChange done"));
}
}
nsresult nsIOService::InitializeCaptivePortalService() { if (XRE_GetProcessType() != GeckoProcessType_Default) { // We only initalize a captive portal service in the main process return NS_OK;
}
mCaptivePortalService = mozilla::components::CaptivePortal::Service(); if (mCaptivePortalService) { static_cast<CaptivePortalService*>(mCaptivePortalService.get())
->Initialize();
}
// Instantiate and initialize the service
RefPtr<NetworkConnectivityService> ncs =
NetworkConnectivityService::GetSingleton();
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
LOG(
("nsIOService aborting InitializeSocketTransportService because of app " "shutdown")); return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
if (!mSocketTransportService) {
mSocketTransportService =
mozilla::components::SocketTransport::Service(&rv); if (NS_FAILED(rv)) {
NS_WARNING("failed to get socket transport service");
}
}
if (mSocketTransportService) {
rv = mSocketTransportService->Init();
NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
mSocketTransportService->SetOffline(false);
}
// The subprocess is launched asynchronously, so we wait for a callback to // acquire the IPDL actor.
mSocketProcess = new SocketProcessHost(new SocketProcessListenerProxy());
LOG(("nsIOService::LaunchSocketProcess")); if (!mSocketProcess->Launch()) {
NS_WARNING("Failed to launch socket process!!");
DestroySocketProcess(); return NS_ERROR_FAILURE;
}
// UseSocketProcess() could return false if we have too many crashes, so we // should call it again. if (UseSocketProcess()) {
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
NewRunnableMethod("nsIOService::LaunchSocketProcess", this,
&nsIOService::LaunchSocketProcess)));
}
}
RefPtr<MemoryReportingProcess> nsIOService::GetSocketProcessMemoryReporter() { // Check the prefs here again, since we don't want to create // SocketProcessMemoryReporter for some tests. if (!StaticPrefs::network_process_enabled() || !SocketProcessReady()) { return nullptr;
}
nsresult nsIOService::RecheckCaptivePortal() {
MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread"); if (!mCaptivePortalService) { return NS_OK;
}
nsCOMPtr<nsIRunnable> task = NewRunnableMethod( "nsIOService::RecheckCaptivePortal", mCaptivePortalService,
&nsICaptivePortalService::RecheckCaptivePortal); return NS_DispatchToMainThread(task);
}
NetAddr addr; // If the redirect wasn't to an IP literal, so there's probably no need // to trigger the captive portal detection right now. It can wait. if (NS_SUCCEEDED(addr.InitFromString(host)) && addr.IsIPAddrLocal()) {
RecheckCaptivePortal();
}
return NS_OK;
}
nsresult nsIOService::AsyncOnChannelRedirect(
nsIChannel* oldChan, nsIChannel* newChan, uint32_t flags,
nsAsyncRedirectVerifyHelper* helper) { // If a redirect to a local network address occurs, then chances are we // are in a captive portal, so we trigger a recheck.
RecheckCaptivePortalIfLocalRedirect(newChan);
// This is silly. I wish there was a simpler way to get at the global // reference of the contentSecurityManager. But it lives in the XPCOM // service registry.
nsCOMPtr<nsIChannelEventSink> sink;
sink = mozilla::components::ContentSecurityManager::Service(); if (sink) {
nsresult rv =
helper->DelegateOnChannelRedirect(sink, oldChan, newChan, flags); if (NS_FAILED(rv)) return rv;
}
// Finally, our category
nsCOMArray<nsIChannelEventSink> entries;
mChannelEventSinks.GetEntries(entries);
int32_t len = entries.Count(); for (int32_t i = 0; i < len; ++i) {
nsresult rv =
helper->DelegateOnChannelRedirect(entries[i], oldChan, newChan, flags); if (NS_FAILED(rv)) return rv;
}
// Collect the redirection from HTTP(S) only. if (httpChan) {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIURI> newURI;
newChan->GetURI(getter_AddRefs(newURI));
MOZ_ASSERT(newURI);
bool nsIOService::UsesExternalProtocolHandler(const nsACString& aScheme) { if (aScheme == "file"_ns || aScheme == "chrome"_ns ||
aScheme == "resource"_ns) { // Don't allow file:, chrome: or resource: URIs to be handled with // nsExternalProtocolHandler, since internally we rely on being able to // use and read from these URIs. returnfalse;
}
if (aScheme == "place"_ns || aScheme == "fake-favicon-uri"_ns ||
aScheme == "favicon"_ns || aScheme == "moz-nullprincipal"_ns) { // Force place: fake-favicon-uri: favicon: and moz-nullprincipal: URIs to be // handled with nsExternalProtocolHandler, and not with a dynamically // registered handler. returntrue;
}
// If prefs configure the URI to be handled externally, do so. for (constauto& scheme : mForceExternalSchemes) { if (aScheme == scheme) { returntrue;
}
} returnfalse;
}
ProtocolHandlerInfo nsIOService::LookupProtocolHandler( const nsACString& aScheme) { // Look-ups are ASCII-case-insensitive, so lower-case the string before // continuing.
nsAutoCString scheme(aScheme);
ToLowerCase(scheme);
// NOTE: If we could get rid of mForceExternalSchemes (or prevent them from // disabling static protocols), we could avoid locking mLock until we need to // check `mRuntimeProtocolHandlers.
AutoReadLock lock(mLock); if (!UsesExternalProtocolHandler(scheme)) { // Try the static protocol handler first - they cannot be overridden by // dynamic protocols. if (const xpcom::StaticProtocolHandler* handler =
xpcom::StaticProtocolHandler::Lookup(scheme)) { return ProtocolHandlerInfo(*handler);
} if (auto handler = mRuntimeProtocolHandlers.Lookup(scheme)) { return ProtocolHandlerInfo(handler.Data());
}
} return ProtocolHandlerInfo(xpcom::StaticProtocolHandler::Default());
}
// hostname ending with a "." delimited octet that is a number // must be IPv4 or IPv6 dual address
nsAutoCString host(inHostname); if (IPv4Parser::EndsInANumber(host)) { // ipv6 dual address; for example "::1.2.3.4" if (net_IsValidIPv6Addr(host)) {
*aResult = true; return NS_OK;
}
// Make sure that all the individual protocolhandlers attach a loadInfo.
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); if (aLoadInfo != loadInfo) {
MOZ_ASSERT(false, "newly created channel must have a loadinfo attached"); return NS_ERROR_UNEXPECTED;
}
// If we're sandboxed, make sure to clear any owner the channel // might already have. if (loadInfo->GetLoadingSandboxed()) {
channel->SetOwner(nullptr);
}
// Some extensions override the http protocol handler and provide their own // implementation. The channels returned from that implementation doesn't // seem to always implement the nsIUploadChannel2 interface, presumably // because it's a new interface. // Eventually we should remove this and simply require that http channels // implement the new interface. // See bug 529041 if (!gHasWarnedUploadChannel2 && scheme.EqualsLiteral("http")) {
nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(channel); if (!uploadChannel2) {
nsCOMPtr<nsIConsoleService> consoleService;
consoleService = mozilla::components::Console::Service(); if (consoleService) {
consoleService->LogStringMessage(
u"Http channel implementation " "doesn't support nsIUploadChannel2. An extension has " "supplied a non-functional http protocol handler. This will " "break behavior and in future releases not work at all.");
}
gHasWarnedUploadChannel2 = true;
}
}
nsresult nsIOService::SetOfflineInternal(bool offline, bool notifySocketProcess) {
LOG(("nsIOService::SetOffline offline=%d\n", offline)); // When someone wants to go online (!offline) after we got XPCOM shutdown // throw ERROR_NOT_AVAILABLE to prevent return to online state. if ((mShutdown || mOfflineForProfileChange) && !offline) { return NS_ERROR_NOT_AVAILABLE;
}
// SetOffline() may re-enter while it's shutting down services. // If that happens, save the most recent value and it will be // processed when the first SetOffline() call is done bringing // down the service.
mSetOfflineValue = offline; if (mSettingOffline) { return NS_OK;
}
if (offline && !mOffline) {
mOffline = true; // indicate we're trying to shutdown
// don't care if notifications fail if (observerService) {
observerService->NotifyObservers(subject,
NS_IOSERVICE_GOING_OFFLINE_TOPIC,
u"" NS_IOSERVICE_OFFLINE);
}
if (mSocketTransportService) mSocketTransportService->SetOffline(true);
mLastOfflineStateChange = PR_IntervalNow(); if (observerService) {
observerService->NotifyObservers(subject,
NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
u"" NS_IOSERVICE_OFFLINE);
}
} elseif (!offline && mOffline) { // go online
InitializeSocketTransportService();
mOffline = false; // indicate success only AFTER we've // brought up the services
mLastOfflineStateChange = PR_IntervalNow(); // don't care if notification fails // Only send the ONLINE notification if there is connectivity if (observerService && mConnectivity) {
observerService->NotifyObservers(subject,
NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
(u"" NS_IOSERVICE_ONLINE));
}
}
}
// Don't notify here, as the above notifications (if used) suffice. if ((mShutdown || mOfflineForProfileChange) && mOffline) { if (mSocketTransportService) {
DebugOnly<nsresult> rv = mSocketTransportService->Shutdown(mShutdown);
NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed");
}
}
NS_IMETHODIMP
nsIOService::SetConnectivity(bool aConnectivity) {
LOG(("nsIOService::SetConnectivity aConnectivity=%d\n", aConnectivity)); // This should only be called from ContentChild to pass the connectivity // value from the chrome process to the content process. if (XRE_IsParentProcess()) { return NS_ERROR_NOT_AVAILABLE;
} return SetConnectivityInternal(aConnectivity);
}
nsresult nsIOService::SetConnectivityInternal(bool aConnectivity) {
LOG(("nsIOService::SetConnectivityInternal aConnectivity=%d\n",
aConnectivity)); if (mConnectivity == aConnectivity) { // Nothing to do here. return NS_OK;
}
mConnectivity = aConnectivity;
// This is used for PR_Connect PR_Close telemetry so it is important that // we have statistic about network change event even if we are offline.
mLastConnectivityChange = PR_IntervalNow();
if (mCaptivePortalService) { if (aConnectivity && gCaptivePortalEnabled) { // This will also trigger a captive portal check for the new network static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Start();
} else { static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Stop();
}
}
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); if (!observerService) { return NS_OK;
} // This notification sends the connectivity to the child processes if (XRE_IsParentProcess()) {
observerService->NotifyObservers(nullptr,
NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
aConnectivity ? u"true" : u"false"); if (SocketProcessReady()) {
Unused << mSocketProcess->GetActor()->SendSetConnectivity(aConnectivity);
}
}
if (mOffline) { // We don't need to send any notifications if we're offline return NS_OK;
}
if (aConnectivity) { // If we were previously offline due to connectivity=false, // send the ONLINE notification
observerService->NotifyObservers(static_cast<nsIIOService*>(this),
NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
(u"" NS_IOSERVICE_ONLINE));
} else { // If we were previously online and lost connectivity // send the OFFLINE notification
observerService->NotifyObservers(static_cast<nsIIOService*>(this),
NS_IOSERVICE_GOING_OFFLINE_TOPIC,
u"" NS_IOSERVICE_OFFLINE);
observerService->NotifyObservers(static_cast<nsIIOService*>(this),
NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
u"" NS_IOSERVICE_OFFLINE);
} return NS_OK;
}
if (port <= 0 || port > std::numeric_limits<uint16_t>::max()) {
*_retval = false; return NS_OK;
}
nsTArray<int32_t> restrictedPortList;
{
AutoReadLock lock(mLock);
restrictedPortList.Assign(mRestrictedPortList);
} // first check to see if the port is in our blacklist:
int32_t badPortListCnt = restrictedPortList.Length(); for (int i = 0; i < badPortListCnt; i++) { if (port == restrictedPortList[i]) {
*_retval = false;
// check to see if the protocol wants to override if (!scheme) return NS_OK;
// We don't support get protocol handler off main thread. if (!NS_IsMainThread()) { return NS_OK;
}
nsCOMPtr<nsIProtocolHandler> handler;
nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler)); if (NS_FAILED(rv)) return rv;
// let the protocol handler decide return handler->AllowPort(port, scheme, _retval);
}
}
void nsIOService::PrefsChanged(constchar* pref) { // Look for extra ports to block if (!pref || strcmp(pref, PORT_PREF("banned")) == 0) {
ParsePortList(PORT_PREF("banned"), false);
}
// ...as well as previous blocks to remove. if (!pref || strcmp(pref, PORT_PREF("banned.override")) == 0) {
ParsePortList(PORT_PREF("banned.override"), true);
}
if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_COUNT_PREF) == 0) {
int32_t count; if (NS_SUCCEEDED(
Preferences::GetInt(NECKO_BUFFER_CACHE_COUNT_PREF, &count))) { /* check for bogus values and default if we find such a value */ if (count > 0) gDefaultSegmentCount = count;
}
}
if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_SIZE_PREF) == 0) {
int32_t size; if (NS_SUCCEEDED(
Preferences::GetInt(NECKO_BUFFER_CACHE_SIZE_PREF, &size))) { /* check for bogus values and default if we find such a value * the upper limit here is arbitrary. having a 1mb segment size * is pretty crazy. if you remove this, consider adding some * integer rollover test.
*/ if (size > 0 && size < 1024 * 1024) gDefaultSegmentSize = size;
}
NS_WARNING_ASSERTION(!(size & (size - 1)), "network segment size is not a power of 2!");
}
if (!pref || strcmp(pref, NETWORK_CAPTIVE_PORTAL_PREF) == 0) {
nsresult rv = Preferences::GetBool(NETWORK_CAPTIVE_PORTAL_PREF,
&gCaptivePortalEnabled); if (NS_SUCCEEDED(rv) && mCaptivePortalService) { if (gCaptivePortalEnabled) { static_cast<CaptivePortalService*>(mCaptivePortalService.get())
->Start();
} else { static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Stop();
}
}
}
if (!pref || strncmp(pref, SIMPLE_URI_SCHEMES_PREF,
strlen(SIMPLE_URI_SCHEMES_PREF)) == 0) {
LOG(("simple_uri_unknown_schemes pref changed, updating the scheme list"));
mSimpleURIUnknownSchemes.ParseAndMergePrefSchemes(); // runs on parent and child, no need to broadcast
}
}
void nsIOService::ParsePortList(constchar* pref, bool remove) {
nsAutoCString portList;
nsTArray<int32_t> restrictedPortList;
{
AutoWriteLock lock(mLock);
restrictedPortList.Assign(std::move(mRestrictedPortList));
} // Get a pref string and chop it up into a list of ports.
Preferences::GetCString(pref, portList); if (!portList.IsVoid()) {
nsTArray<nsCString> portListArray;
ParseString(portList, ',', portListArray);
uint32_t index; for (index = 0; index < portListArray.Length(); index++) {
portListArray[index].StripWhitespace();
int32_t portBegin, portEnd;
if (PR_sscanf(portListArray[index].get(), "%d-%d", &portBegin,
&portEnd) == 2) { if ((portBegin < 65536) && (portEnd < 65536)) {
int32_t curPort; if (remove) { for (curPort = portBegin; curPort <= portEnd; curPort++) {
restrictedPortList.RemoveElement(curPort);
}
} else { for (curPort = portBegin; curPort <= portEnd; curPort++) {
restrictedPortList.AppendElement(curPort);
}
}
}
} else {
nsresult aErrorCode;
int32_t port = portListArray[index].ToInteger(&aErrorCode); if (NS_SUCCEEDED(aErrorCode) && port < 65536) { if (remove) {
restrictedPortList.RemoveElement(port);
} else {
restrictedPortList.AppendElement(port);
}
}
}
}
}
if (!strcmp(topic, kProfileChangeNetTeardownTopic)) { if (!mHttpHandlerAlreadyShutingDown) {
mNetTearingDownStarted = PR_IntervalNow();
}
mHttpHandlerAlreadyShutingDown = false; if (!mOffline) {
mOfflineForProfileChange = true;
SetOfflineInternal(true, false);
}
} elseif (!strcmp(topic, kProfileChangeNetRestoreTopic)) { if (mOfflineForProfileChange) {
mOfflineForProfileChange = false;
SetOfflineInternal(false, false);
}
} elseif (!strcmp(topic, kProfileDoChange)) { if (data && u"startup"_ns.Equals(data)) { // Lazy initialization of network link service (see bug 620472)
InitializeNetworkLinkService(); // Set up the initilization flag regardless the actuall result. // If we fail here, we will fail always on.
mNetworkLinkServiceInitialized = true;
// And now reflect the preference setting
PrefsChanged(MANAGE_OFFLINE_STATUS_PREF);
// Bug 870460 - Read cookie database at an early-as-possible time // off main thread. Hence, we have more chance to finish db query // before something calls into the cookie service.
nsCOMPtr<nsISupports> cookieServ =
do_GetService(NS_COOKIESERVICE_CONTRACTID);
}
} elseif (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { // Remember we passed XPCOM shutdown notification to prevent any // changes of the offline status from now. We must not allow going // online after this point.
mShutdown = true;
// Dig deeper into the chain. Note that this is not a do/while loop to // avoid the extra addref/release on |uri| in the common (non-nested) case.
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri); while (nestedURI) {
nsCOMPtr<nsIURI> innerURI;
rv = nestedURI->GetInnerURI(getter_AddRefs(innerURI));
NS_ENSURE_SUCCESS(rv, rv);
// When detection is not activated, the default connectivity state is true. if (!mManageLinkStatus) {
SetConnectivityInternal(true); return NS_OK;
}
InitializeNetworkLinkService(); // If the NetworkLinkService is already initialized, it does not call // OnNetworkLinkEvent. This is needed, when mManageLinkStatus goes from // false to true.
OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN); return NS_OK;
}
// input argument 'data' is already UTF8'ed
nsresult nsIOService::OnNetworkLinkEvent(constchar* data) { if (IsNeckoChild() || IsSocketProcessChild()) { // There is nothing IO service could do on the child process // with this at the moment. Feel free to add functionality // here at will, though. return NS_OK;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.