Files
UXP-Fixed/netwerk/base/nsIOService.cpp
wolfbeast a1e6132772 Issue #1474 - Prevent the cp detection from starting if preffed off.
When setting up connectivity, e.g. with new network detection, the pref
wouldn't be checked (or honored), resulting in spurious startup calls
despite being preffed off. This fixes that.

Resolves #1474
2020-03-07 21:58:27 +01:00

1883 lines
62 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 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/. */
#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "nsIOService.h"
#include "nsIDOMNode.h"
#include "nsIProtocolHandler.h"
#include "nsIFileProtocolHandler.h"
#include "nscore.h"
#include "nsIURI.h"
#include "prprf.h"
#include "nsIErrorService.h"
#include "netCore.h"
#include "nsIObserverService.h"
#include "nsIPrefService.h"
#include "nsXPCOM.h"
#include "nsIProxiedProtocolHandler.h"
#include "nsIProxyInfo.h"
#include "nsEscape.h"
#include "nsNetUtil.h"
#include "nsNetCID.h"
#include "nsCRT.h"
#include "nsSecCheckWrapChannel.h"
#include "nsSimpleNestedURI.h"
#include "nsTArray.h"
#include "nsIConsoleService.h"
#include "nsIUploadChannel2.h"
#include "nsXULAppAPI.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsIProtocolProxyCallback.h"
#include "nsICancelable.h"
#include "nsINetworkLinkService.h"
#include "nsPISocketTransportService.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsURLHelper.h"
#include "nsPIDNSService.h"
#include "nsIProtocolProxyService2.h"
#include "MainThreadUtils.h"
#include "nsINode.h"
#include "nsIWidget.h"
#include "nsThreadUtils.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/net/NeckoCommon.h"
#include "mozilla/Services.h"
#include "mozilla/net/DNS.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/net/CaptivePortalService.h"
#include "ReferrerPolicy.h"
#include "nsContentSecurityManager.h"
#include "nsContentUtils.h"
#include "xpcpublic.h"
namespace mozilla {
namespace net {
#define PORT_PREF_PREFIX "network.security.ports."
#define PORT_PREF(x) PORT_PREF_PREFIX x
#define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status"
#define OFFLINE_MIRRORS_CONNECTIVITY "network.offline-mirrors-connectivity"
// 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_NOTIFY_CHANGED_PREF "network.notify.changed"
#define NETWORK_CAPTIVE_PORTAL_PREF "network.captive-portal-service.enabled"
#define MAX_RECURSION_COUNT 50
nsIOService* gIOService = nullptr;
static bool gHasWarnedUploadChannel2;
static bool gCaptivePortalEnabled = false;
static LazyLogModule gIOServiceLog("nsIOService");
#undef LOG
#define LOG(args) MOZ_LOG(gIOServiceLog, LogLevel::Debug, args)
// A general port blacklist. Connections to these ports will not be allowed
// unless the protocol overrides.
//
// TODO: I am sure that there are more ports to be added.
// This cut is based on the classic mozilla codebase
int16_t gBadPortList[] = {
1, // tcpmux
7, // echo
9, // discard
11, // systat
13, // daytime
15, // netstat
17, // qotd
19, // chargen
20, // ftp-data
21, // ftp-cntl
22, // ssh
23, // telnet
25, // smtp
37, // time
42, // name
43, // nicname
53, // domain
77, // priv-rjs
79, // finger
87, // ttylink
95, // supdup
101, // hostriame
102, // iso-tsap
103, // gppitnp
104, // acr-nema
109, // pop2
110, // pop3
111, // sunrpc
113, // auth
115, // sftp
117, // uucp-path
119, // nntp
123, // NTP
135, // loc-srv / epmap
139, // netbios
143, // imap2
179, // BGP
389, // ldap
465, // smtp+ssl
512, // print / exec
513, // login
514, // shell
515, // printer
526, // tempo
530, // courier
531, // Chat
532, // netnews
540, // uucp
556, // remotefs
563, // nntp+ssl
587, //
601, //
636, // ldap+ssl
993, // imap+ssl
995, // pop3+ssl
2049, // nfs
3659, // apple-sasl / PasswordServer
4045, // lockd
6000, // x11
6665, // Alternate IRC [Apple addition]
6666, // Alternate IRC [Apple addition]
6667, // Standard IRC [Apple addition]
6668, // Alternate IRC [Apple addition]
6669, // Alternate IRC [Apple addition]
0, // This MUST be zero so that we can populating the array
};
static const char kProfileChangeNetTeardownTopic[] = "profile-change-net-teardown";
static const char kProfileChangeNetRestoreTopic[] = "profile-change-net-restore";
static const char kProfileDoChange[] = "profile-do-change";
// Necko buffer defaults
uint32_t nsIOService::gDefaultSegmentSize = 4096;
uint32_t nsIOService::gDefaultSegmentCount = 24;
bool nsIOService::sBlockToplevelDataUriNavigations = false;
bool nsIOService::sBlockFTPSubresources = false;
////////////////////////////////////////////////////////////////////////////////
nsIOService::nsIOService()
: mOffline(true)
, mOfflineForProfileChange(false)
, mManageLinkStatus(false)
, mConnectivity(true)
, mOfflineMirrorsConnectivity(true)
, mSettingOffline(false)
, mSetOfflineValue(false)
, mShutdown(false)
, mHttpHandlerAlreadyShutingDown(false)
, mNetworkLinkServiceInitialized(false)
, mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
, mNetworkNotifyChanged(true)
, mNetTearingDownStarted(0)
{
}
nsresult
nsIOService::Init()
{
nsresult rv;
// We need to get references to the DNS service so that we can shut it
// down later. If we wait until the nsIOService is being shut down,
// GetService will fail at that point.
mDNSService = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
NS_WARNING("failed to get DNS service");
return rv;
}
// XXX hack until xpidl supports error info directly (bug 13423)
nsCOMPtr<nsIErrorService> errorService = do_GetService(NS_ERRORSERVICE_CONTRACTID);
if (errorService) {
errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_NETWORK, NECKO_MSGS_URL);
}
else
NS_WARNING("failed to get error service");
InitializeCaptivePortalService();
// setup our bad port list stuff
for(int i=0; gBadPortList[i]; i++)
mRestrictedPortList.AppendElement(gBadPortList[i]);
// Further modifications to the port list come from prefs
nsCOMPtr<nsIPrefBranch> prefBranch;
GetPrefBranch(getter_AddRefs(prefBranch));
if (prefBranch) {
prefBranch->AddObserver(PORT_PREF_PREFIX, this, true);
prefBranch->AddObserver(MANAGE_OFFLINE_STATUS_PREF, this, true);
prefBranch->AddObserver(NECKO_BUFFER_CACHE_COUNT_PREF, this, true);
prefBranch->AddObserver(NECKO_BUFFER_CACHE_SIZE_PREF, this, true);
prefBranch->AddObserver(NETWORK_NOTIFY_CHANGED_PREF, this, true);
prefBranch->AddObserver(NETWORK_CAPTIVE_PORTAL_PREF, this, true);
PrefsChanged(prefBranch);
}
// Register for profile change notifications
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
if (observerService) {
observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
observerService->AddObserver(this, kProfileChangeNetRestoreTopic, true);
observerService->AddObserver(this, kProfileDoChange, true);
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
observerService->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
}
else
NS_WARNING("failed to get observer service");
Preferences::AddBoolVarCache(&sBlockToplevelDataUriNavigations,
"security.data_uri.block_toplevel_data_uri_navigations", false);
Preferences::AddBoolVarCache(&sBlockFTPSubresources,
"security.block_ftp_subresources", true);
Preferences::AddBoolVarCache(&mOfflineMirrorsConnectivity, OFFLINE_MIRRORS_CONNECTIVITY, true);
gIOService = this;
InitializeNetworkLinkService();
SetOffline(false);
return NS_OK;
}
nsIOService::~nsIOService()
{
gIOService = nullptr;
}
nsresult
nsIOService::InitializeCaptivePortalService()
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
// We only initalize a captive portal service in the main process
return NS_OK;
}
mCaptivePortalService = do_GetService(NS_CAPTIVEPORTAL_CID);
if (mCaptivePortalService) {
return static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Initialize();
}
return NS_OK;
}
nsresult
nsIOService::InitializeSocketTransportService()
{
nsresult rv = NS_OK;
if (!mSocketTransportService) {
mSocketTransportService = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &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);
}
return rv;
}
nsresult
nsIOService::InitializeNetworkLinkService()
{
nsresult rv = NS_OK;
if (mNetworkLinkServiceInitialized)
return rv;
if (!NS_IsMainThread()) {
NS_WARNING("Network link service should be created on main thread");
return NS_ERROR_FAILURE;
}
// go into managed mode if we can, and chrome process
if (XRE_IsParentProcess())
{
mNetworkLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
}
if (mNetworkLinkService) {
mNetworkLinkServiceInitialized = true;
}
// After initializing the networkLinkService, query the connectivity state
OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN);
return rv;
}
nsIOService*
nsIOService::GetInstance() {
if (!gIOService) {
gIOService = new nsIOService();
if (!gIOService)
return nullptr;
NS_ADDREF(gIOService);
nsresult rv = gIOService->Init();
if (NS_FAILED(rv)) {
NS_RELEASE(gIOService);
return nullptr;
}
return gIOService;
}
NS_ADDREF(gIOService);
return gIOService;
}
NS_IMPL_ISUPPORTS(nsIOService,
nsIIOService,
nsIIOService2,
nsINetUtil,
nsISpeculativeConnect,
nsIObserver,
nsIIOServiceInternal,
nsISupportsWeakReference)
////////////////////////////////////////////////////////////////////////////////
nsresult
nsIOService::RecheckCaptivePortal()
{
MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
if (mCaptivePortalService) {
mCaptivePortalService->RecheckCaptivePortal();
}
return NS_OK;
}
nsresult
nsIOService::RecheckCaptivePortalIfLocalRedirect(nsIChannel* newChan)
{
nsresult rv;
if (!mCaptivePortalService) {
return NS_OK;
}
nsCOMPtr<nsIURI> uri;
rv = newChan->GetURI(getter_AddRefs(uri));
if (NS_FAILED(rv)) {
return rv;
}
nsCString host;
rv = uri->GetHost(host);
if (NS_FAILED(rv)) {
return rv;
}
PRNetAddr prAddr;
if (PR_StringToNetAddr(host.BeginReading(), &prAddr) != PR_SUCCESS) {
// 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.
return NS_OK;
}
NetAddr netAddr;
PRNetAddrToNetAddr(&prAddr, &netAddr);
if (IsIPAddrLocal(&netAddr)) {
// Redirects to local IP addresses are probably captive portals
mCaptivePortalService->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 =
do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
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;
}
return NS_OK;
}
nsresult
nsIOService::CacheProtocolHandler(const char *scheme, nsIProtocolHandler *handler)
{
MOZ_ASSERT(NS_IsMainThread());
for (unsigned int i=0; i<NS_N(gScheme); i++)
{
if (!nsCRT::strcasecmp(scheme, gScheme[i]))
{
nsresult rv;
NS_ASSERTION(!mWeakHandler[i], "Protocol handler already cached");
// Make sure the handler supports weak references.
nsCOMPtr<nsISupportsWeakReference> factoryPtr = do_QueryInterface(handler, &rv);
if (!factoryPtr)
{
// Don't cache handlers that don't support weak reference as
// there is real danger of a circular reference.
#ifdef DEBUG_dp
printf("DEBUG: %s protcol handler doesn't support weak ref. Not cached.\n", scheme);
#endif /* DEBUG_dp */
return NS_ERROR_FAILURE;
}
mWeakHandler[i] = do_GetWeakReference(handler);
return NS_OK;
}
}
return NS_ERROR_FAILURE;
}
nsresult
nsIOService::GetCachedProtocolHandler(const char *scheme, nsIProtocolHandler **result, uint32_t start, uint32_t end)
{
MOZ_ASSERT(NS_IsMainThread());
uint32_t len = end - start - 1;
for (unsigned int i=0; i<NS_N(gScheme); i++)
{
if (!mWeakHandler[i])
continue;
// handle unterminated strings
// start is inclusive, end is exclusive, len = end - start - 1
if (end ? (!nsCRT::strncasecmp(scheme + start, gScheme[i], len)
&& gScheme[i][len] == '\0')
: (!nsCRT::strcasecmp(scheme, gScheme[i])))
{
return CallQueryReferent(mWeakHandler[i].get(), result);
}
}
return NS_ERROR_FAILURE;
}
static bool
UsesExternalProtocolHandler(const char* aScheme)
{
if (NS_LITERAL_CSTRING("file").Equals(aScheme) ||
NS_LITERAL_CSTRING("chrome").Equals(aScheme) ||
NS_LITERAL_CSTRING("resource").Equals(aScheme)) {
// 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.
return false;
}
nsAutoCString pref("network.protocol-handler.external.");
pref += aScheme;
return Preferences::GetBool(pref.get(), false);
}
NS_IMETHODIMP
nsIOService::GetProtocolHandler(const char* scheme, nsIProtocolHandler* *result)
{
nsresult rv;
NS_ENSURE_ARG_POINTER(scheme);
// XXX we may want to speed this up by introducing our own protocol
// scheme -> protocol handler mapping, avoiding the string manipulation
// and service manager stuff
rv = GetCachedProtocolHandler(scheme, result);
if (NS_SUCCEEDED(rv))
return rv;
if (!UsesExternalProtocolHandler(scheme)) {
nsAutoCString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
contractID += scheme;
ToLowerCase(contractID);
rv = CallGetService(contractID.get(), result);
if (NS_SUCCEEDED(rv)) {
CacheProtocolHandler(scheme, *result);
return rv;
}
#ifdef MOZ_ENABLE_GIO
// check to see whether GVFS can handle this URI scheme. if it can
// create a nsIURI for the "scheme:", then we assume it has support for
// the requested protocol. otherwise, we failover to using the default
// protocol handler.
rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"moz-gio",
result);
if (NS_SUCCEEDED(rv)) {
nsAutoCString spec(scheme);
spec.Append(':');
nsIURI *uri;
rv = (*result)->NewURI(spec, nullptr, nullptr, &uri);
if (NS_SUCCEEDED(rv)) {
NS_RELEASE(uri);
return rv;
}
NS_RELEASE(*result);
}
#endif
}
// Okay we don't have a protocol handler to handle this url type, so use
// the default protocol handler. This will cause urls to get dispatched
// out to the OS ('cause we can't do anything with them) when we try to
// read from a channel created by the default protocol handler.
rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default",
result);
if (NS_FAILED(rv))
return NS_ERROR_UNKNOWN_PROTOCOL;
return rv;
}
NS_IMETHODIMP
nsIOService::ExtractScheme(const nsACString &inURI, nsACString &scheme)
{
return net_ExtractURLScheme(inURI, scheme);
}
NS_IMETHODIMP
nsIOService::GetProtocolFlags(const char* scheme, uint32_t *flags)
{
nsCOMPtr<nsIProtocolHandler> handler;
nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
if (NS_FAILED(rv)) return rv;
// We can't call DoGetProtocolFlags here because we don't have a URI. This
// API is used by (and only used by) extensions, which is why it's still
// around. Calling this on a scheme with dynamic flags will throw.
rv = handler->GetProtocolFlags(flags);
return rv;
}
class AutoIncrement
{
public:
explicit AutoIncrement(uint32_t *var) : mVar(var)
{
++*var;
}
~AutoIncrement()
{
--*mVar;
}
private:
uint32_t *mVar;
};
nsresult
nsIOService::NewURI(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIURI **result)
{
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
static uint32_t recursionCount = 0;
if (recursionCount >= MAX_RECURSION_COUNT)
return NS_ERROR_MALFORMED_URI;
AutoIncrement inc(&recursionCount);
nsAutoCString scheme;
nsresult rv = ExtractScheme(aSpec, scheme);
if (NS_FAILED(rv)) {
// then aSpec is relative
if (!aBaseURI)
return NS_ERROR_MALFORMED_URI;
if (!aSpec.IsEmpty() && aSpec[0] == '#') {
// Looks like a reference instead of a fully-specified URI.
// --> initialize |uri| as a clone of |aBaseURI|, with ref appended.
return aBaseURI->CloneWithNewRef(aSpec, result);
}
rv = aBaseURI->GetScheme(scheme);
if (NS_FAILED(rv)) return rv;
}
// now get the handler for this scheme
nsCOMPtr<nsIProtocolHandler> handler;
rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
if (NS_FAILED(rv)) return rv;
return handler->NewURI(aSpec, aCharset, aBaseURI, result);
}
NS_IMETHODIMP
nsIOService::NewFileURI(nsIFile *file, nsIURI **result)
{
nsresult rv;
NS_ENSURE_ARG_POINTER(file);
nsCOMPtr<nsIProtocolHandler> handler;
rv = GetProtocolHandler("file", getter_AddRefs(handler));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIFileProtocolHandler> fileHandler( do_QueryInterface(handler, &rv) );
if (NS_FAILED(rv)) return rv;
return fileHandler->NewFileURI(file, result);
}
NS_IMETHODIMP
nsIOService::NewChannelFromURI2(nsIURI* aURI,
nsIDOMNode* aLoadingNode,
nsIPrincipal* aLoadingPrincipal,
nsIPrincipal* aTriggeringPrincipal,
uint32_t aSecurityFlags,
uint32_t aContentPolicyType,
nsIChannel** result)
{
return NewChannelFromURIWithProxyFlags2(aURI,
nullptr, // aProxyURI
0, // aProxyFlags
aLoadingNode,
aLoadingPrincipal,
aTriggeringPrincipal,
aSecurityFlags,
aContentPolicyType,
result);
}
/* ***** DEPRECATED *****
* please use NewChannelFromURI2 providing the right arguments for:
* * aLoadingNode
* * aLoadingPrincipal
* * aTriggeringPrincipal
* * aSecurityFlags
* * aContentPolicyType
*
* See nsIIoService.idl for a detailed description of those arguments
*/
NS_IMETHODIMP
nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result)
{
NS_ASSERTION(false, "Deprecated, use NewChannelFromURI2 providing loadInfo arguments!");
const char16_t* params[] = {
u"nsIOService::NewChannelFromURI()",
u"nsIOService::NewChannelFromURI2()"
};
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Security by Default"),
nullptr, // aDocument
nsContentUtils::eNECKO_PROPERTIES,
"APIDeprecationWarning",
params, ArrayLength(params));
return NewChannelFromURI2(aURI,
nullptr, // aLoadingNode
nsContentUtils::GetSystemPrincipal(),
nullptr, // aTriggeringPrincipal
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER,
result);
}
NS_IMETHODIMP
nsIOService::NewChannelFromURIWithLoadInfo(nsIURI* aURI,
nsILoadInfo* aLoadInfo,
nsIChannel** result)
{
return NewChannelFromURIWithProxyFlagsInternal(aURI,
nullptr, // aProxyURI
0, // aProxyFlags
aLoadInfo,
result);
}
nsresult
nsIOService::NewChannelFromURIWithProxyFlagsInternal(nsIURI* aURI,
nsIURI* aProxyURI,
uint32_t aProxyFlags,
nsILoadInfo* aLoadInfo,
nsIChannel** result)
{
nsresult rv;
NS_ENSURE_ARG_POINTER(aURI);
nsAutoCString scheme;
rv = aURI->GetScheme(scheme);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIProtocolHandler> handler;
rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
if (NS_FAILED(rv))
return rv;
uint32_t protoFlags;
rv = handler->DoGetProtocolFlags(aURI, &protoFlags);
if (NS_FAILED(rv))
return rv;
// Ideally we are creating new channels by calling NewChannel2 (NewProxiedChannel2).
// Keep in mind that Addons can implement their own Protocolhandlers, hence
// NewChannel2() might *not* be implemented.
// We do not want to break those addons, therefore we first try to create a channel
// calling NewChannel2(); if that fails:
// * we fall back to creating a channel by calling NewChannel()
// * wrap the addon channel
// * and attach the loadInfo to the channel wrapper
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler);
if (pph) {
rv = pph->NewProxiedChannel2(aURI, nullptr, aProxyFlags, aProxyURI,
aLoadInfo, getter_AddRefs(channel));
// if calling NewProxiedChannel2() fails we try to fall back to
// creating a new proxied channel by calling NewProxiedChannel().
if (NS_FAILED(rv)) {
rv = pph->NewProxiedChannel(aURI, nullptr, aProxyFlags, aProxyURI,
getter_AddRefs(channel));
NS_ENSURE_SUCCESS(rv, rv);
// The protocol handler does not implement NewProxiedChannel2, so
// maybe we need to wrap the channel (see comment in MaybeWrap
// function).
channel = nsSecCheckWrapChannel::MaybeWrap(channel, aLoadInfo);
}
}
else {
rv = handler->NewChannel2(aURI, aLoadInfo, getter_AddRefs(channel));
// if calling newChannel2() fails we try to fall back to
// creating a new channel by calling NewChannel().
if (NS_FAILED(rv)) {
rv = handler->NewChannel(aURI, getter_AddRefs(channel));
if (NS_FAILED(rv)) {
return rv;
}
// The protocol handler does not implement NewChannel2, so
// maybe we need to wrap the channel (see comment in MaybeWrap
// function).
channel = nsSecCheckWrapChannel::MaybeWrap(channel, aLoadInfo);
}
}
// Make sure that all the individual protocolhandlers attach a loadInfo.
if (aLoadInfo) {
// make sure we have the same instance of loadInfo on the newly created channel
nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
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 =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (consoleService) {
consoleService->LogStringMessage(NS_LITERAL_STRING(
"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."
).get());
}
gHasWarnedUploadChannel2 = true;
}
}
channel.forget(result);
return NS_OK;
}
NS_IMETHODIMP
nsIOService::NewChannelFromURIWithProxyFlags2(nsIURI* aURI,
nsIURI* aProxyURI,
uint32_t aProxyFlags,
nsIDOMNode* aLoadingNode,
nsIPrincipal* aLoadingPrincipal,
nsIPrincipal* aTriggeringPrincipal,
uint32_t aSecurityFlags,
uint32_t aContentPolicyType,
nsIChannel** result)
{
// Ideally all callers of NewChannelFromURIWithProxyFlags2 provide the
// necessary arguments to create a loadinfo. Keep in mind that addons
// might still call NewChannelFromURIWithProxyFlags() which forwards
// its calls to NewChannelFromURIWithProxyFlags2 using *null* values
// as the arguments for aLoadingNode, aLoadingPrincipal, and also
// aTriggeringPrincipal.
// We do not want to break those addons, hence we only create a Loadinfo
// if 'aLoadingNode' or 'aLoadingPrincipal' are provided. Note, that
// either aLoadingNode or aLoadingPrincipal is required to succesfully
// create a LoadInfo object.
// Except in the case of top level TYPE_DOCUMENT loads, where the
// loadingNode and loadingPrincipal are allowed to have null values.
nsCOMPtr<nsILoadInfo> loadInfo;
// TYPE_DOCUMENT loads don't require a loadingNode or principal, but other
// types do.
if (aLoadingNode || aLoadingPrincipal ||
aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
nsCOMPtr<nsINode> loadingNode(do_QueryInterface(aLoadingNode));
loadInfo = new LoadInfo(aLoadingPrincipal,
aTriggeringPrincipal,
loadingNode,
aSecurityFlags,
aContentPolicyType);
}
NS_ASSERTION(loadInfo, "Please pass security info when creating a channel");
return NewChannelFromURIWithProxyFlagsInternal(aURI,
aProxyURI,
aProxyFlags,
loadInfo,
result);
}
/* ***** DEPRECATED *****
* please use NewChannelFromURIWithProxyFlags2 providing the right arguments for:
* * aLoadingNode
* * aLoadingPrincipal
* * aTriggeringPrincipal
* * aSecurityFlags
* * aContentPolicyType
*
* See nsIIoService.idl for a detailed description of those arguments
*/
NS_IMETHODIMP
nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI,
nsIURI *aProxyURI,
uint32_t aProxyFlags,
nsIChannel **result)
{
NS_ASSERTION(false, "Deprecated, use NewChannelFromURIWithProxyFlags2 providing loadInfo arguments!");
const char16_t* params[] = {
u"nsIOService::NewChannelFromURIWithProxyFlags()",
u"nsIOService::NewChannelFromURIWithProxyFlags2()"
};
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Security by Default"),
nullptr, // aDocument
nsContentUtils::eNECKO_PROPERTIES,
"APIDeprecationWarning",
params, ArrayLength(params));
return NewChannelFromURIWithProxyFlags2(aURI,
aProxyURI,
aProxyFlags,
nullptr, // aLoadingNode
nsContentUtils::GetSystemPrincipal(),
nullptr, // aTriggeringPrincipal
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER,
result);
}
NS_IMETHODIMP
nsIOService::NewChannel2(const nsACString& aSpec,
const char* aCharset,
nsIURI* aBaseURI,
nsIDOMNode* aLoadingNode,
nsIPrincipal* aLoadingPrincipal,
nsIPrincipal* aTriggeringPrincipal,
uint32_t aSecurityFlags,
uint32_t aContentPolicyType,
nsIChannel** result)
{
nsresult rv;
nsCOMPtr<nsIURI> uri;
rv = NewURI(aSpec, aCharset, aBaseURI, getter_AddRefs(uri));
if (NS_FAILED(rv)) return rv;
return NewChannelFromURI2(uri,
aLoadingNode,
aLoadingPrincipal,
aTriggeringPrincipal,
aSecurityFlags,
aContentPolicyType,
result);
}
/* ***** DEPRECATED *****
* please use NewChannel2 providing the right arguments for:
* * aLoadingNode
* * aLoadingPrincipal
* * aTriggeringPrincipal
* * aSecurityFlags
* * aContentPolicyType
*
* See nsIIoService.idl for a detailed description of those arguments
*/
NS_IMETHODIMP
nsIOService::NewChannel(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIChannel **result)
{
NS_ASSERTION(false, "Deprecated, use NewChannel2 providing loadInfo arguments!");
const char16_t* params[] = {
u"nsIOService::NewChannel()",
u"nsIOService::NewChannel2()"
};
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Security by Default"),
nullptr, // aDocument
nsContentUtils::eNECKO_PROPERTIES,
"APIDeprecationWarning",
params, ArrayLength(params));
// Call NewChannel2 providing default arguments for the loadInfo.
return NewChannel2(aSpec,
aCharset,
aBaseURI,
nullptr, // aLoadingNode
nsContentUtils::GetSystemPrincipal(), // aLoadingPrincipal
nullptr, // aTriggeringPrincipal
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER,
result);
}
bool
nsIOService::IsLinkUp()
{
InitializeNetworkLinkService();
if (!mNetworkLinkService) {
// We cannot decide, assume the link is up
return true;
}
bool isLinkUp;
nsresult rv;
rv = mNetworkLinkService->GetIsLinkUp(&isLinkUp);
if (NS_FAILED(rv)) {
return true;
}
return isLinkUp;
}
NS_IMETHODIMP
nsIOService::GetOffline(bool *offline)
{
if (mOfflineMirrorsConnectivity) {
*offline = mOffline || !mConnectivity;
} else {
*offline = mOffline;
}
return NS_OK;
}
NS_IMETHODIMP
nsIOService::SetOffline(bool offline)
{
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;
}
mSettingOffline = true;
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
NS_ASSERTION(observerService, "The observer service should not be null");
if (XRE_IsParentProcess()) {
if (observerService) {
(void)observerService->NotifyObservers(nullptr,
NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, offline ?
u"true" :
u"false");
}
}
nsIIOService *subject = static_cast<nsIIOService *>(this);
while (mSetOfflineValue != mOffline) {
offline = mSetOfflineValue;
if (offline && !mOffline) {
NS_NAMED_LITERAL_STRING(offlineString, NS_IOSERVICE_OFFLINE);
mOffline = true; // indicate we're trying to shutdown
// don't care if notifications fail
if (observerService)
observerService->NotifyObservers(subject,
NS_IOSERVICE_GOING_OFFLINE_TOPIC,
offlineString.get());
if (mSocketTransportService)
mSocketTransportService->SetOffline(true);
if (observerService)
observerService->NotifyObservers(subject,
NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
offlineString.get());
}
else if (!offline && mOffline) {
// go online
if (mDNSService) {
DebugOnly<nsresult> rv = mDNSService->Init();
NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed");
}
InitializeSocketTransportService();
mOffline = false; // indicate success only AFTER we've
// brought up the services
// trigger a PAC reload when we come back online
if (mProxyService)
mProxyService->ReloadPAC();
// 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) {
// be sure to try and shutdown both (even if the first fails)...
// shutdown dns service first, because it has callbacks for socket transport
if (mDNSService) {
DebugOnly<nsresult> rv = mDNSService->Shutdown();
NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service shutdown failed");
}
if (mSocketTransportService) {
DebugOnly<nsresult> rv = mSocketTransportService->Shutdown(mShutdown);
NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed");
}
}
mSettingOffline = false;
return NS_OK;
}
NS_IMETHODIMP
nsIOService::GetConnectivity(bool *aConnectivity)
{
*aConnectivity = mConnectivity;
return NS_OK;
}
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;
if (mCaptivePortalService) {
if (aConnectivity && !xpc::AreNonLocalConnectionsDisabled() && 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 (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
const nsLiteralString offlineString(u"" NS_IOSERVICE_OFFLINE);
observerService->NotifyObservers(static_cast<nsIIOService *>(this),
NS_IOSERVICE_GOING_OFFLINE_TOPIC,
offlineString.get());
observerService->NotifyObservers(static_cast<nsIIOService *>(this),
NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
offlineString.get());
}
return NS_OK;
}
NS_IMETHODIMP
nsIOService::AllowPort(int32_t inPort, const char *scheme, bool *_retval)
{
int16_t port = inPort;
if (port == -1) {
*_retval = true;
return NS_OK;
}
if (port == 0) {
*_retval = false;
return NS_OK;
}
// first check to see if the port is in our blacklist:
int32_t badPortListCnt = mRestrictedPortList.Length();
for (int i=0; i<badPortListCnt; i++)
{
if (port == mRestrictedPortList[i])
{
*_retval = false;
// check to see if the protocol wants to override
if (!scheme)
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);
}
}
*_retval = true;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
void
nsIOService::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
{
if (!prefs) return;
// Look for extra ports to block
if (!pref || strcmp(pref, PORT_PREF("banned")) == 0)
ParsePortList(prefs, PORT_PREF("banned"), false);
// ...as well as previous blocks to remove.
if (!pref || strcmp(pref, PORT_PREF("banned.override")) == 0)
ParsePortList(prefs, PORT_PREF("banned.override"), true);
if (!pref || strcmp(pref, MANAGE_OFFLINE_STATUS_PREF) == 0) {
bool manage;
if (mNetworkLinkServiceInitialized &&
NS_SUCCEEDED(prefs->GetBoolPref(MANAGE_OFFLINE_STATUS_PREF,
&manage))) {
LOG(("nsIOService::PrefsChanged ManageOfflineStatus manage=%d\n", manage));
SetManageOfflineStatus(manage);
}
}
if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_COUNT_PREF) == 0) {
int32_t count;
if (NS_SUCCEEDED(prefs->GetIntPref(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(prefs->GetIntPref(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_NOTIFY_CHANGED_PREF) == 0) {
bool allow;
nsresult rv = prefs->GetBoolPref(NETWORK_NOTIFY_CHANGED_PREF, &allow);
if (NS_SUCCEEDED(rv)) {
mNetworkNotifyChanged = allow;
}
}
if (!pref || strcmp(pref, NETWORK_CAPTIVE_PORTAL_PREF) == 0) {
nsresult rv = prefs->GetBoolPref(NETWORK_CAPTIVE_PORTAL_PREF, &gCaptivePortalEnabled);
if (NS_SUCCEEDED(rv) && mCaptivePortalService) {
if (gCaptivePortalEnabled && !xpc::AreNonLocalConnectionsDisabled()) {
static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Start();
} else {
static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Stop();
}
}
}
}
void
nsIOService::ParsePortList(nsIPrefBranch *prefBranch, const char *pref, bool remove)
{
nsXPIDLCString portList;
// Get a pref string and chop it up into a list of ports.
prefBranch->GetCharPref(pref, getter_Copies(portList));
if (portList) {
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++)
mRestrictedPortList.RemoveElement(curPort);
} else {
for (curPort=portBegin; curPort <= portEnd; curPort++)
mRestrictedPortList.AppendElement(curPort);
}
}
} else {
nsresult aErrorCode;
int32_t port = portListArray[index].ToInteger(&aErrorCode);
if (NS_SUCCEEDED(aErrorCode) && port < 65536) {
if (remove)
mRestrictedPortList.RemoveElement(port);
else
mRestrictedPortList.AppendElement(port);
}
}
}
}
}
void
nsIOService::GetPrefBranch(nsIPrefBranch **result)
{
*result = nullptr;
CallGetService(NS_PREFSERVICE_CONTRACTID, result);
}
class nsWakeupNotifier : public Runnable
{
public:
explicit nsWakeupNotifier(nsIIOServiceInternal *ioService)
:mIOService(ioService)
{ }
NS_IMETHOD Run() override
{
return mIOService->NotifyWakeup();
}
private:
virtual ~nsWakeupNotifier() { }
nsCOMPtr<nsIIOServiceInternal> mIOService;
};
NS_IMETHODIMP
nsIOService::NotifyWakeup()
{
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
NS_ASSERTION(observerService, "The observer service should not be null");
if (observerService && mNetworkNotifyChanged) {
(void)observerService->
NotifyObservers(nullptr,
NS_NETWORK_LINK_TOPIC,
(u"" NS_NETWORK_LINK_DATA_CHANGED));
}
RecheckCaptivePortal();
return NS_OK;
}
void
nsIOService::SetHttpHandlerAlreadyShutingDown()
{
if (!mShutdown && !mOfflineForProfileChange) {
mNetTearingDownStarted = PR_IntervalNow();
mHttpHandlerAlreadyShutingDown = true;
}
}
// nsIObserver interface
NS_IMETHODIMP
nsIOService::Observe(nsISupports *subject,
const char *topic,
const char16_t *data)
{
if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
if (prefBranch)
PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
} else if (!strcmp(topic, kProfileChangeNetTeardownTopic)) {
if (!mHttpHandlerAlreadyShutingDown) {
mNetTearingDownStarted = PR_IntervalNow();
}
mHttpHandlerAlreadyShutingDown = false;
if (!mOffline) {
mOfflineForProfileChange = true;
SetOffline(true);
}
} else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) {
if (mOfflineForProfileChange) {
mOfflineForProfileChange = false;
SetOffline(false);
}
} else if (!strcmp(topic, kProfileDoChange)) {
if (data && NS_LITERAL_STRING("startup").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
nsCOMPtr<nsIPrefBranch> prefBranch;
GetPrefBranch(getter_AddRefs(prefBranch));
PrefsChanged(prefBranch, MANAGE_OFFLINE_STATUS_PREF);
}
} else if (!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;
if (!mHttpHandlerAlreadyShutingDown && !mOfflineForProfileChange) {
mNetTearingDownStarted = PR_IntervalNow();
}
mHttpHandlerAlreadyShutingDown = false;
SetOffline(true);
if (mCaptivePortalService) {
static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Stop();
mCaptivePortalService = nullptr;
}
// Break circular reference.
mProxyService = nullptr;
} else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
OnNetworkLinkEvent(NS_ConvertUTF16toUTF8(data).get());
} else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
// coming back alive from sleep
// this indirection brought to you by:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1152048#c19
nsCOMPtr<nsIRunnable> wakeupNotifier = new nsWakeupNotifier(this);
NS_DispatchToMainThread(wakeupNotifier);
}
return NS_OK;
}
// nsINetUtil interface
NS_IMETHODIMP
nsIOService::ParseRequestContentType(const nsACString &aTypeHeader,
nsACString &aCharset,
bool *aHadCharset,
nsACString &aContentType)
{
net_ParseRequestContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
return NS_OK;
}
// nsINetUtil interface
NS_IMETHODIMP
nsIOService::ParseResponseContentType(const nsACString &aTypeHeader,
nsACString &aCharset,
bool *aHadCharset,
nsACString &aContentType)
{
net_ParseContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
return NS_OK;
}
NS_IMETHODIMP
nsIOService::ProtocolHasFlags(nsIURI *uri,
uint32_t flags,
bool *result)
{
NS_ENSURE_ARG(uri);
*result = false;
nsAutoCString scheme;
nsresult rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
// Grab the protocol flags from the URI.
uint32_t protocolFlags;
nsCOMPtr<nsIProtocolHandler> handler;
rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
NS_ENSURE_SUCCESS(rv, rv);
rv = handler->DoGetProtocolFlags(uri, &protocolFlags);
NS_ENSURE_SUCCESS(rv, rv);
*result = (protocolFlags & flags) == flags;
return NS_OK;
}
NS_IMETHODIMP
nsIOService::URIChainHasFlags(nsIURI *uri,
uint32_t flags,
bool *result)
{
nsresult rv = ProtocolHasFlags(uri, flags, result);
NS_ENSURE_SUCCESS(rv, rv);
if (*result) {
return rv;
}
// 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);
rv = ProtocolHasFlags(innerURI, flags, result);
if (*result) {
return rv;
}
nestedURI = do_QueryInterface(innerURI);
}
return rv;
}
NS_IMETHODIMP
nsIOService::ToImmutableURI(nsIURI* uri, nsIURI** result)
{
if (!uri) {
*result = nullptr;
return NS_OK;
}
nsresult rv = NS_EnsureSafeToReturn(uri, result);
NS_ENSURE_SUCCESS(rv, rv);
NS_TryToSetImmutable(*result);
return NS_OK;
}
NS_IMETHODIMP
nsIOService::NewSimpleNestedURI(nsIURI* aURI, nsIURI** aResult)
{
NS_ENSURE_ARG(aURI);
nsCOMPtr<nsIURI> safeURI;
nsresult rv = NS_EnsureSafeToReturn(aURI, getter_AddRefs(safeURI));
NS_ENSURE_SUCCESS(rv, rv);
NS_IF_ADDREF(*aResult = new nsSimpleNestedURI(safeURI));
return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsIOService::SetManageOfflineStatus(bool aManage)
{
LOG(("nsIOService::SetManageOfflineStatus aManage=%d\n", aManage));
mManageLinkStatus = aManage;
// 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;
}
NS_IMETHODIMP
nsIOService::GetManageOfflineStatus(bool* aManage)
{
*aManage = mManageLinkStatus;
return NS_OK;
}
// input argument 'data' is already UTF8'ed
nsresult
nsIOService::OnNetworkLinkEvent(const char *data)
{
LOG(("nsIOService::OnNetworkLinkEvent data:%s\n", data));
if (!mNetworkLinkService)
return NS_ERROR_FAILURE;
if (mShutdown)
return NS_ERROR_NOT_AVAILABLE;
if (!mManageLinkStatus) {
LOG(("nsIOService::OnNetworkLinkEvent mManageLinkStatus=false\n"));
return NS_OK;
}
bool isUp = true;
if (!strcmp(data, NS_NETWORK_LINK_DATA_CHANGED)) {
// CHANGED means UP/DOWN didn't change
// but the status of the captive portal may have changed.
RecheckCaptivePortal();
return NS_OK;
} else if (!strcmp(data, NS_NETWORK_LINK_DATA_DOWN)) {
isUp = false;
} else if (!strcmp(data, NS_NETWORK_LINK_DATA_UP)) {
isUp = true;
} else if (!strcmp(data, NS_NETWORK_LINK_DATA_UNKNOWN)) {
nsresult rv = mNetworkLinkService->GetIsLinkUp(&isUp);
NS_ENSURE_SUCCESS(rv, rv);
} else {
NS_WARNING("Unhandled network event!");
return NS_OK;
}
return SetConnectivityInternal(isUp);
}
NS_IMETHODIMP
nsIOService::EscapeString(const nsACString& aString,
uint32_t aEscapeType,
nsACString& aResult)
{
NS_ENSURE_ARG_MAX(aEscapeType, 4);
nsAutoCString stringCopy(aString);
nsCString result;
if (!NS_Escape(stringCopy, result, (nsEscapeMask) aEscapeType))
return NS_ERROR_OUT_OF_MEMORY;
aResult.Assign(result);
return NS_OK;
}
NS_IMETHODIMP
nsIOService::EscapeURL(const nsACString &aStr,
uint32_t aFlags, nsACString &aResult)
{
aResult.Truncate();
NS_EscapeURL(aStr.BeginReading(), aStr.Length(),
aFlags | esc_AlwaysCopy, aResult);
return NS_OK;
}
NS_IMETHODIMP
nsIOService::UnescapeString(const nsACString &aStr,
uint32_t aFlags, nsACString &aResult)
{
aResult.Truncate();
NS_UnescapeURL(aStr.BeginReading(), aStr.Length(),
aFlags | esc_AlwaysCopy, aResult);
return NS_OK;
}
NS_IMETHODIMP
nsIOService::ExtractCharsetFromContentType(const nsACString &aTypeHeader,
nsACString &aCharset,
int32_t *aCharsetStart,
int32_t *aCharsetEnd,
bool *aHadCharset)
{
nsAutoCString ignored;
net_ParseContentType(aTypeHeader, ignored, aCharset, aHadCharset,
aCharsetStart, aCharsetEnd);
if (*aHadCharset && *aCharsetStart == *aCharsetEnd) {
*aHadCharset = false;
}
return NS_OK;
}
// parse policyString to policy enum value (see ReferrerPolicy.h)
NS_IMETHODIMP
nsIOService::ParseAttributePolicyString(const nsAString& policyString,
uint32_t *outPolicyEnum)
{
NS_ENSURE_ARG(outPolicyEnum);
*outPolicyEnum = (uint32_t)AttributeReferrerPolicyFromString(policyString);
return NS_OK;
}
// nsISpeculativeConnect
class IOServiceProxyCallback final : public nsIProtocolProxyCallback
{
~IOServiceProxyCallback() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROTOCOLPROXYCALLBACK
IOServiceProxyCallback(nsIInterfaceRequestor *aCallbacks,
nsIOService *aIOService)
: mCallbacks(aCallbacks)
, mIOService(aIOService)
{ }
private:
RefPtr<nsIInterfaceRequestor> mCallbacks;
RefPtr<nsIOService> mIOService;
};
NS_IMPL_ISUPPORTS(IOServiceProxyCallback, nsIProtocolProxyCallback)
NS_IMETHODIMP
IOServiceProxyCallback::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
nsIProxyInfo *pi, nsresult status)
{
// Checking proxy status for speculative connect
nsAutoCString type;
if (NS_SUCCEEDED(status) && pi &&
NS_SUCCEEDED(pi->GetType(type)) &&
!type.EqualsLiteral("direct")) {
// proxies dont do speculative connect
return NS_OK;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = channel->GetURI(getter_AddRefs(uri));
if (NS_FAILED(rv)) {
return NS_OK;
}
nsAutoCString scheme;
rv = uri->GetScheme(scheme);
if (NS_FAILED(rv))
return NS_OK;
nsCOMPtr<nsIProtocolHandler> handler;
rv = mIOService->GetProtocolHandler(scheme.get(),
getter_AddRefs(handler));
if (NS_FAILED(rv))
return NS_OK;
nsCOMPtr<nsISpeculativeConnect> speculativeHandler =
do_QueryInterface(handler);
if (!speculativeHandler)
return NS_OK;
nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
nsCOMPtr<nsIPrincipal> principal;
if (loadInfo) {
principal = loadInfo->LoadingPrincipal();
}
nsLoadFlags loadFlags = 0;
channel->GetLoadFlags(&loadFlags);
if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
speculativeHandler->SpeculativeAnonymousConnect2(uri, principal, mCallbacks);
} else {
speculativeHandler->SpeculativeConnect2(uri, principal, mCallbacks);
}
return NS_OK;
}
nsresult
nsIOService::SpeculativeConnectInternal(nsIURI *aURI,
nsIPrincipal *aPrincipal,
nsIInterfaceRequestor *aCallbacks,
bool aAnonymous)
{
if (IsNeckoChild()) {
ipc::URIParams params;
SerializeURI(aURI, params);
gNeckoChild->SendSpeculativeConnect(params,
IPC::Principal(aPrincipal),
aAnonymous);
return NS_OK;
}
// Check for proxy information. If there is a proxy configured then a
// speculative connect should not be performed because the potential
// reward is slim with tcp peers closely located to the browser.
nsresult rv;
nsCOMPtr<nsIProtocolProxyService> pps =
do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> loadingPrincipal = aPrincipal;
// If the principal is given, we use this prinicpal directly. Otherwise,
// we fallback to use the system principal.
if (!aPrincipal) {
nsCOMPtr<nsIScriptSecurityManager> secMan(
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
rv = secMan->GetSystemPrincipal(getter_AddRefs(loadingPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
}
// dummy channel used to create a TCP connection.
// we perform security checks on the *real* channel, responsible
// for any network loads. this real channel just checks the TCP
// pool if there is an available connection created by the
// channel we create underneath - hence it's safe to use
// the systemPrincipal as the loadingPrincipal for this channel.
nsCOMPtr<nsIChannel> channel;
rv = NewChannelFromURI2(aURI,
nullptr, // aLoadingNode,
loadingPrincipal,
nullptr, //aTriggeringPrincipal,
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER,
getter_AddRefs(channel));
NS_ENSURE_SUCCESS(rv, rv);
if (aAnonymous) {
nsLoadFlags loadFlags = 0;
channel->GetLoadFlags(&loadFlags);
loadFlags |= nsIRequest::LOAD_ANONYMOUS;
channel->SetLoadFlags(loadFlags);
}
nsCOMPtr<nsICancelable> cancelable;
RefPtr<IOServiceProxyCallback> callback =
new IOServiceProxyCallback(aCallbacks, this);
nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
if (pps2) {
return pps2->AsyncResolve2(channel, 0, callback, getter_AddRefs(cancelable));
}
return pps->AsyncResolve(channel, 0, callback, getter_AddRefs(cancelable));
}
NS_IMETHODIMP
nsIOService::SpeculativeConnect(nsIURI *aURI,
nsIInterfaceRequestor *aCallbacks)
{
return SpeculativeConnectInternal(aURI, nullptr, aCallbacks, false);
}
NS_IMETHODIMP
nsIOService::SpeculativeConnect2(nsIURI *aURI,
nsIPrincipal *aPrincipal,
nsIInterfaceRequestor *aCallbacks)
{
return SpeculativeConnectInternal(aURI, aPrincipal, aCallbacks, false);
}
NS_IMETHODIMP
nsIOService::SpeculativeAnonymousConnect(nsIURI *aURI,
nsIInterfaceRequestor *aCallbacks)
{
return SpeculativeConnectInternal(aURI, nullptr, aCallbacks, true);
}
NS_IMETHODIMP
nsIOService::SpeculativeAnonymousConnect2(nsIURI *aURI,
nsIPrincipal *aPrincipal,
nsIInterfaceRequestor *aCallbacks)
{
return SpeculativeConnectInternal(aURI, aPrincipal, aCallbacks, true);
}
/*static*/ bool
nsIOService::BlockToplevelDataUriNavigations()
{
return sBlockToplevelDataUriNavigations;
}
/*static*/ bool
nsIOService::BlockFTPSubresources()
{
return sBlockFTPSubresources;
}
} // namespace net
} // namespace mozilla