mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 354493 - Add nsINetworkZonePolicy to protect resources loaded from private IPs r=mcmanus
This commit is contained in:
parent
ed98140f75
commit
2cabfdd5a8
@ -1109,6 +1109,9 @@ pref("network.http.connection-timeout", 90);
|
||||
// when starting a new speculative connection.
|
||||
pref("network.http.speculative-parallel-limit", 6);
|
||||
|
||||
// Allow/Forbid speculative connections on loopback.
|
||||
pref("network.http.speculative.allowLoopback", false);
|
||||
|
||||
// Whether or not to block requests for non head js/css items (e.g. media)
|
||||
// while those elements load.
|
||||
pref("network.http.rendering-critical-requests-prioritization", true);
|
||||
@ -1528,6 +1531,10 @@ pref("network.proxy.autoconfig_retry_interval_max", 300); // 5 minutes
|
||||
// Use the HSTS preload list by default
|
||||
pref("network.stricttransportsecurity.preloadlist", true);
|
||||
|
||||
// Prohibit resource loads from private networks (e.g. RFC1918 like IP
|
||||
// addresses) by documents which were loaded from public networks.
|
||||
pref("network.zonepolicy.enabled", true);
|
||||
|
||||
pref("converter.html2txt.structs", true); // Output structured phrases (strong, em, code, sub, sup, b, i, u)
|
||||
pref("converter.html2txt.header_strategy", 1); // 0 = no indention; 1 = indention, increased with header level; 2 = numbering and slight indention
|
||||
|
||||
|
@ -61,6 +61,7 @@ XPIDL_SOURCES += [
|
||||
'nsINetworkPredictor.idl',
|
||||
'nsINetworkPredictorVerifier.idl',
|
||||
'nsINetworkProperties.idl',
|
||||
'nsINetworkZonePolicy.idl',
|
||||
'nsINSSErrorsService.idl',
|
||||
'nsIParentChannel.idl',
|
||||
'nsIParentRedirectingChannel.idl',
|
||||
|
@ -10,7 +10,7 @@
|
||||
/**
|
||||
* nsIIOService2 extends nsIIOService
|
||||
*/
|
||||
[scriptable, uuid(9a7dc724-0b5c-4b78-9722-1037074c02de)]
|
||||
[scriptable, uuid(b2344926-d194-41d5-b749-e6591a257428)]
|
||||
interface nsIIOService2 : nsIIOService
|
||||
{
|
||||
/**
|
||||
@ -40,4 +40,10 @@ interface nsIIOService2 : nsIIOService
|
||||
nsIChannel newChannelFromURIWithProxyFlags(in nsIURI aURI,
|
||||
in nsIURI aProxyURI,
|
||||
in unsigned long aProxyFlags);
|
||||
|
||||
/**
|
||||
* Returns a string ID for the current network. This ID should be
|
||||
* refreshed when the network link changes.
|
||||
*/
|
||||
readonly attribute ACString networkLinkID;
|
||||
};
|
||||
|
@ -15,7 +15,7 @@ typedef unsigned long nsLoadFlags;
|
||||
/**
|
||||
* A load group maintains a collection of nsIRequest objects.
|
||||
*/
|
||||
[scriptable, uuid(afb57ac2-bce5-4ee3-bb34-385089a9ba5c)]
|
||||
[scriptable, uuid(8531ccd9-f12f-4674-ae07-05b625cebf8b)]
|
||||
interface nsILoadGroup : nsIRequest
|
||||
{
|
||||
/**
|
||||
@ -93,6 +93,12 @@ interface nsILoadGroup : nsIRequest
|
||||
* the docShell has created the default request.)
|
||||
*/
|
||||
attribute nsLoadFlags defaultLoadFlags;
|
||||
|
||||
/**
|
||||
* Allow/Deny this loadgroup to load resources from private, RFC1918-like
|
||||
* addresses. See nsINetworkZonePolicy for more information.
|
||||
*/
|
||||
attribute bool allowLoadsFromPrivateNetworks;
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
53
netwerk/base/public/nsINetworkZonePolicy.idl
Normal file
53
netwerk/base/public/nsINetworkZonePolicy.idl
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "nsISupports.idl"
|
||||
|
||||
interface nsIRequest;
|
||||
|
||||
/**
|
||||
* NetworkZonePolicy is used to permit or deny nsIRequests loading from private
|
||||
* IP addresses. Private in this case means RFC1918-like addresses and
|
||||
* localhost. Primarily, this is to protect resources held on private
|
||||
* networks from being loaded by documents which were loaded from the public
|
||||
* internet.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(639a0797-7870-4a37-9110-57ea9f14783f)]
|
||||
interface nsINetworkZonePolicy : nsISupports
|
||||
{
|
||||
/**
|
||||
* Returns true if aRequest can connect to private, RFC1918-like addresses.
|
||||
* Implementing classes should query the loadgroup of the request, along with
|
||||
* the loadgroup's owning/parent hierarchy, to determine if permission is
|
||||
* granted. This should result in requests generated by a public document
|
||||
* being restricted to public loads only.
|
||||
*
|
||||
* @param aRequest The nsIRequest which is trying to load from a private IP
|
||||
* address.
|
||||
* @returns True if aRequest has permission to load from a private IP
|
||||
* address; false if it does not.
|
||||
*/
|
||||
bool checkPrivateNetworkPermission(in nsIRequest aRequest);
|
||||
|
||||
/**
|
||||
* Tries to set permission in the loadgroup of aRequest with respect to
|
||||
* loading from private IP addresses. Permission will only be set if the
|
||||
* loadgroup's ancestors (parent-, owning- and docshell parent loadgroups) all
|
||||
* allow private network loads. If permission cannot be set, the function
|
||||
* returns silently.
|
||||
* Note: only documents should be allowed to set permission for other
|
||||
* resource loads in the loadgroup, so aRequest should represent a document
|
||||
* load. Non document loads will be ignored and the function will return
|
||||
* silently.
|
||||
*
|
||||
* @param aRequest The request which is trying to set private load
|
||||
* permission for its loadgroup.
|
||||
* @param aAllowed True if permission is to be granted; false if not.
|
||||
*/
|
||||
void setPrivateNetworkPermission(in nsIRequest aRequest,
|
||||
in bool aAllowed);
|
||||
};
|
@ -182,6 +182,12 @@ interface nsISocketTransport : nsITransport
|
||||
*/
|
||||
const unsigned long DISABLE_RFC1918 = (1 << 5);
|
||||
|
||||
/**
|
||||
* If set, indicates that the socket should not connect if the hostname
|
||||
* resolves to a loopback address.
|
||||
*/
|
||||
const unsigned long DISABLE_LOOPBACK = (1 << 6);
|
||||
|
||||
/**
|
||||
* Socket QoS/ToS markings. Valid values are IPTOS_DSCP_AFxx or
|
||||
* IPTOS_CLASS_CSx (or IPTOS_DSCP_EF, but currently no supported
|
||||
|
@ -2399,4 +2399,26 @@ NS_IsSrcdocChannel(nsIChannel *aChannel)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides 32 bits of PRNG; workaround for platform variances of RAND_MAX.
|
||||
*/
|
||||
inline uint32_t
|
||||
NS_Get32BitsOfPseudoRandom()
|
||||
{
|
||||
// rand() provides different amounts of PRNG on different platforms.
|
||||
// 15 or 31 bits are common amounts.
|
||||
|
||||
PR_STATIC_ASSERT(RAND_MAX >= 0xfff);
|
||||
|
||||
#if RAND_MAX < 0xffffU
|
||||
return ((uint16_t) rand() << 20) |
|
||||
(((uint16_t) rand() & 0xfff) << 8) |
|
||||
((uint16_t) rand() & 0xff);
|
||||
#elif RAND_MAX < 0xffffffffU
|
||||
return ((uint16_t) rand() << 16) | ((uint16_t) rand() & 0xffff);
|
||||
#else
|
||||
return (uint32_t) rand();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // !nsNetUtil_h__
|
||||
|
@ -47,6 +47,7 @@ UNIFIED_SOURCES += [
|
||||
'nsMIMEInputStream.cpp',
|
||||
'nsNetAddr.cpp',
|
||||
'nsNetStrings.cpp',
|
||||
'nsNetworkZonePolicy.cpp',
|
||||
'nsPACMan.cpp',
|
||||
'nsPreloadedStream.cpp',
|
||||
'nsProtocolProxyService.cpp',
|
||||
|
@ -149,6 +149,15 @@ nsIOService::nsIOService()
|
||||
, mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
|
||||
, mAutoDialEnabled(false)
|
||||
{
|
||||
// XXX May need to remove this once Bug 939319 and associated bugs for
|
||||
// NS_NETWORK_LINK_DATA_CHANGED are complete on all supported platforms.
|
||||
|
||||
// Subfields of unions cannot be targeted in an initializer list
|
||||
mNetworkLinkSelfAddr.raw.family = PR_AF_UNSPEC;
|
||||
|
||||
// Right now, the network link ID is just 64bits of pseudo-randonmess.
|
||||
mNetworkLinkID = NS_Get32BitsOfPseudoRandom();
|
||||
mNetworkLinkID = (mNetworkLinkID << 32) | NS_Get32BitsOfPseudoRandom();
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -1084,6 +1093,38 @@ nsIOService::GetManageOfflineStatus(bool* aManage) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
nsIOService::GetNetworkLinkID() const
|
||||
{
|
||||
return mNetworkLinkID;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOService::GetNetworkLinkID(nsACString &aNetworkLinkID)
|
||||
{
|
||||
char temp[21];
|
||||
PR_snprintf(temp, sizeof(temp), "%llu", mNetworkLinkID);
|
||||
aNetworkLinkID.Append(temp);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// XXX Remove this once Bug 939319 and associated bugs for
|
||||
// NS_NETWORK_LINK_DATA_CHANGED are complete on all supported platforms.
|
||||
// Right now, the network link ID is just 64bits of pseudo-randonmess.
|
||||
void
|
||||
nsIOService::UpdateNetworkLinkID(const mozilla::net::NetAddr aCurrentSelfAddr)
|
||||
{
|
||||
if (IsLoopBackAddress(&aCurrentSelfAddr) ||
|
||||
mNetworkLinkSelfAddr.EqualsIP(aCurrentSelfAddr)) {
|
||||
return;
|
||||
}
|
||||
mNetworkLinkSelfAddr = aCurrentSelfAddr;
|
||||
|
||||
mNetworkLinkID = NS_Get32BitsOfPseudoRandom();
|
||||
mNetworkLinkID = (mNetworkLinkID << 32) | NS_Get32BitsOfPseudoRandom();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIOService::TrackNetworkLinkStatusForOffline()
|
||||
{
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "nsCategoryCache.h"
|
||||
#include "nsISpeculativeConnect.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
|
||||
#define NS_N(x) (sizeof(x)/sizeof(*x))
|
||||
|
||||
@ -74,6 +75,15 @@ public:
|
||||
return mOffline && mSettingOffline && !mSetOfflineValue;
|
||||
}
|
||||
|
||||
// Returns the NetworkLinkID.
|
||||
uint64_t GetNetworkLinkID() const;
|
||||
|
||||
// XXX Remove this once Bug 939319 and associated bugs for
|
||||
// NS_NETWORK_LINK_DATA_CHANGED are complete on all supported
|
||||
// platforms.
|
||||
// Primes the network link ID with the current self address of this host.
|
||||
void UpdateNetworkLinkID(const mozilla::net::NetAddr aCurrentSelfAddr);
|
||||
|
||||
private:
|
||||
// These shouldn't be called directly:
|
||||
// - construct using GetInstance
|
||||
@ -133,6 +143,16 @@ public:
|
||||
// Used for all default buffer sizes that necko allocates.
|
||||
static uint32_t gDefaultSegmentSize;
|
||||
static uint32_t gDefaultSegmentCount;
|
||||
|
||||
private:
|
||||
// XXX Maybe remove these once Bug 939319 and associated bugs for
|
||||
// NS_NETWORK_LINK_DATA_CHANGED are complete on all supported platforms.
|
||||
// Right now, the network link ID is just 64bits of pseudo-randonmess.
|
||||
//
|
||||
// ID of the current network link.
|
||||
uint64_t mNetworkLinkID;
|
||||
// IP address of this host for the current network link.
|
||||
mozilla::net::NetAddr mNetworkLinkSelfAddr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -118,6 +118,7 @@ nsLoadGroup::nsLoadGroup(nsISupports* outer)
|
||||
, mStatus(NS_OK)
|
||||
, mPriority(PRIORITY_NORMAL)
|
||||
, mIsCanceling(false)
|
||||
, mAllowLoadsFromPrivateNetworks(true)
|
||||
, mDefaultLoadIsTimed(false)
|
||||
, mTimedRequests(0)
|
||||
, mCachedRequests(0)
|
||||
@ -1094,6 +1095,20 @@ nsresult nsLoadGroup::MergeLoadFlags(nsIRequest *aRequest, nsLoadFlags& outFlags
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLoadGroup::GetAllowLoadsFromPrivateNetworks(bool *aAllowed)
|
||||
{
|
||||
*aAllowed = mAllowLoadsFromPrivateNetworks;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLoadGroup::SetAllowLoadsFromPrivateNetworks(bool aAllowed)
|
||||
{
|
||||
mAllowLoadsFromPrivateNetworks = aAllowed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsLoadGroupConnectionInfo
|
||||
|
||||
class nsLoadGroupConnectionInfo MOZ_FINAL : public nsILoadGroupConnectionInfo
|
||||
|
@ -82,6 +82,9 @@ protected:
|
||||
int32_t mPriority;
|
||||
bool mIsCanceling;
|
||||
|
||||
// Set if this loadgroup allows loads from private networks (RFC1918 etc).
|
||||
bool mAllowLoadsFromPrivateNetworks;
|
||||
|
||||
/* Telemetry */
|
||||
mozilla::TimeStamp mDefaultRequestCreationTime;
|
||||
bool mDefaultLoadIsTimed;
|
||||
|
361
netwerk/base/src/nsNetworkZonePolicy.cpp
Normal file
361
netwerk/base/src/nsNetworkZonePolicy.cpp
Normal file
@ -0,0 +1,361 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "prlog.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsNetworkZonePolicy.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDocumentLoader.h"
|
||||
#include "nsILoadGroup.h"
|
||||
#include "nsILoadGroupChild.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIRequestObserver.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
#ifdef PR_LOGGING
|
||||
#include "nsString.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
static PRLogModuleInfo *gNZPLog;
|
||||
#define NZPLOG(msg, ...) \
|
||||
PR_LOG(gNZPLog, PR_LOG_DEBUG, ("[NZP] %p: " msg, this, ##__VA_ARGS__))
|
||||
#else
|
||||
#define NZPLOG(msg, ...)
|
||||
#endif
|
||||
|
||||
/* Keeps track of whether or not NZP is enabled */
|
||||
bool nsNetworkZonePolicy::sNZPEnabled = true;
|
||||
|
||||
/* True if shutdown notification has been received. */
|
||||
bool nsNetworkZonePolicy::sShutdown = false;
|
||||
|
||||
/* Singleton pointer. */
|
||||
StaticRefPtr<nsNetworkZonePolicy> nsNetworkZonePolicy::sSingleton;
|
||||
|
||||
nsNetworkZonePolicy::nsNetworkZonePolicy()
|
||||
{
|
||||
Preferences::AddBoolVarCache(&sNZPEnabled, "network.zonepolicy.enabled");
|
||||
|
||||
// Register for shutdown notification.
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
} else {
|
||||
NS_WARNING("failed to get observer service");
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
if (!gNZPLog) {
|
||||
gNZPLog = PR_NewLogModule("NZP");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
already_AddRefed<nsNetworkZonePolicy>
|
||||
nsNetworkZonePolicy::GetSingleton()
|
||||
{
|
||||
if (sShutdown) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!sSingleton) {
|
||||
sSingleton = new nsNetworkZonePolicy();
|
||||
}
|
||||
|
||||
// Return a ref ptr to the singleton.
|
||||
nsRefPtr<nsNetworkZonePolicy> nzp = sSingleton.get();
|
||||
return nzp.forget();
|
||||
}
|
||||
|
||||
nsNetworkZonePolicy::~nsNetworkZonePolicy() {}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsNetworkZonePolicy,
|
||||
nsINetworkZonePolicy,
|
||||
nsIObserver)
|
||||
|
||||
// nsIObserver interface
|
||||
NS_IMETHODIMP
|
||||
nsNetworkZonePolicy::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const char16_t *aData)
|
||||
{
|
||||
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||
sShutdown = true;
|
||||
sSingleton = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsILoadGroup>
|
||||
nsNetworkZonePolicy::GetLoadGroupParent(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
if (NS_WARN_IF(!aLoadGroup)) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(aLoadGroup);
|
||||
|
||||
DebugOnly<nsresult> rv = NS_OK;
|
||||
nsCOMPtr<nsILoadGroup> parent;
|
||||
nsCOMPtr<nsILoadGroupChild> loadGroupAsChild = do_QueryInterface(aLoadGroup);
|
||||
if (!loadGroupAsChild) {
|
||||
return nullptr;
|
||||
}
|
||||
rv = loadGroupAsChild->GetParentLoadGroup(getter_AddRefs(parent));
|
||||
if (!parent) {
|
||||
return nullptr;
|
||||
}
|
||||
NZPLOG("loadgroup %p getting parent loadgroup %p", aLoadGroup,
|
||||
parent.get());
|
||||
return parent.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsILoadGroup>
|
||||
nsNetworkZonePolicy::GetOwningLoadGroup(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
if (NS_WARN_IF(!aLoadGroup)) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(aLoadGroup);
|
||||
|
||||
DebugOnly<nsresult> rv = NS_OK;
|
||||
nsCOMPtr<nsILoadGroup> owner;
|
||||
rv = aLoadGroup->GetLoadGroup(getter_AddRefs(owner));
|
||||
if (!owner) {
|
||||
return nullptr;
|
||||
}
|
||||
NZPLOG("loadgroup %p getting owning loadgroup %p", aLoadGroup, owner.get());
|
||||
return owner.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsILoadGroup>
|
||||
nsNetworkZonePolicy::GetParentDocShellsLoadGroup(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
if (NS_WARN_IF(!aLoadGroup)) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(aLoadGroup);
|
||||
|
||||
DebugOnly<nsresult> rv = NS_OK;
|
||||
nsCOMPtr<nsIRequestObserver> observer;
|
||||
rv = aLoadGroup->GetGroupObserver(getter_AddRefs(observer));
|
||||
if (!observer) {
|
||||
return nullptr;
|
||||
}
|
||||
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(observer);
|
||||
if (!docShell) {
|
||||
return nullptr;
|
||||
}
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem;
|
||||
docShell->GetSameTypeParent(getter_AddRefs(parentAsTreeItem));
|
||||
if (!parentAsTreeItem) {
|
||||
return nullptr;
|
||||
}
|
||||
nsCOMPtr<nsIDocumentLoader> parentAsDocLoader =
|
||||
do_QueryInterface(parentAsTreeItem);
|
||||
if (!parentAsDocLoader) {
|
||||
return nullptr;
|
||||
}
|
||||
nsCOMPtr<nsILoadGroup> dsParent;
|
||||
rv = parentAsDocLoader->GetLoadGroup(getter_AddRefs(dsParent));
|
||||
if (!dsParent) {
|
||||
return nullptr;
|
||||
}
|
||||
NZPLOG("loadgroup %p getting docshell parent's loadgroup %p",
|
||||
aLoadGroup, dsParent.get());
|
||||
return dsParent.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
nsNetworkZonePolicy::CheckLoadGroupAncestorHierarchies(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
if (NS_WARN_IF(!aLoadGroup)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(aLoadGroup);
|
||||
|
||||
// Check the hierarchies of appropriate ancestors.
|
||||
// 1. Parent loadgroup.
|
||||
nsCOMPtr<nsILoadGroup> ancestor = GetLoadGroupParent(aLoadGroup);
|
||||
if (ancestor) {
|
||||
bool ancestorAllows = CheckLoadGroupHierarchy(ancestor);
|
||||
|
||||
NZPLOG("Loadgroup %p's parent loadgroup hierarchy %s private loads.",
|
||||
aLoadGroup, ancestorAllows ? "allows" : "forbids");
|
||||
if (!ancestorAllows) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Owning loadgroup.
|
||||
ancestor = GetOwningLoadGroup(aLoadGroup);
|
||||
if (ancestor) {
|
||||
bool ancestorAllows = CheckLoadGroupHierarchy(ancestor);
|
||||
|
||||
NZPLOG("Loadgroup %p's owning loadgroup hierarchy %s private loads.",
|
||||
aLoadGroup, ancestorAllows ? "allows" : "forbids");
|
||||
if (!ancestorAllows) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Parent docshell's loadgroup.
|
||||
ancestor = GetParentDocShellsLoadGroup(aLoadGroup);
|
||||
if (ancestor) {
|
||||
bool ancestorAllows = CheckLoadGroupHierarchy(ancestor);
|
||||
|
||||
NZPLOG("Loadgroup %p's parent docshell's loadgroup hierarchy %s private "
|
||||
"loads.",
|
||||
aLoadGroup, ancestorAllows ? "allows" : "forbids");
|
||||
if (!ancestorAllows) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no ancestor or they all have permission to load from private
|
||||
// networks, return true.
|
||||
NZPLOG("Loadgroup %p: no ancestor forbids loads from private networks.",
|
||||
aLoadGroup);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsNetworkZonePolicy::CheckLoadGroupHierarchy(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
if (NS_WARN_IF(!aLoadGroup)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(aLoadGroup);
|
||||
|
||||
// Recurse until root load group of same type, or until ancestor forbids
|
||||
// private loads.
|
||||
|
||||
// If current loadgroup does not allow private loads, just return.
|
||||
bool allowed = false;
|
||||
aLoadGroup->GetAllowLoadsFromPrivateNetworks(&allowed);
|
||||
if (!allowed) {
|
||||
NZPLOG("Loadgroup %p forbids loads from private networks.", aLoadGroup);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Else, check the hierarchies of appropriate ancestors.
|
||||
return CheckLoadGroupAncestorHierarchies(aLoadGroup);
|
||||
}
|
||||
|
||||
/*
|
||||
* nsNetworkZonePolicy : nsINetworkZonePolicy
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsNetworkZonePolicy::CheckPrivateNetworkPermission(nsIRequest *aRequest,
|
||||
bool *aAllowed)
|
||||
{
|
||||
if (NS_WARN_IF(!aRequest)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
if (NS_WARN_IF(!aAllowed)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!aRequest || !aAllowed)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
if (!sNZPEnabled) {
|
||||
*aAllowed = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoCString nameStr;
|
||||
aRequest->GetName(nameStr);
|
||||
#endif
|
||||
NZPLOG("CheckPrivateNetworkPermission for request %p [%s].", aRequest,
|
||||
nameStr.get());
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
if (!loadGroup) {
|
||||
NZPLOG("No loadgroup for request %p [%s]; private networks allowed.",
|
||||
aRequest, nameStr.get());
|
||||
*aAllowed = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Find out if this loadgroup's hierarchy allows private loads.
|
||||
bool hierarchyAllows = CheckLoadGroupHierarchy(loadGroup);
|
||||
|
||||
NZPLOG("LoadGroup %p for request %p [%s] is %s private loads.",
|
||||
loadGroup.get(), aRequest, nameStr.get(),
|
||||
hierarchyAllows ? "allowed" : "forbidden");
|
||||
|
||||
*aAllowed = hierarchyAllows;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNetworkZonePolicy::SetPrivateNetworkPermission(nsIRequest *aRequest,
|
||||
bool aAllowed)
|
||||
{
|
||||
if (NS_WARN_IF(!aRequest)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
if (!sNZPEnabled) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
|
||||
if (!loadGroup || NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoCString nameStr;
|
||||
aRequest->GetName(nameStr);
|
||||
#endif
|
||||
NZPLOG("SetPrivateNetworkPermission: try to %s for loadgroup %p of request "
|
||||
"%p [%s].",
|
||||
aAllowed ? "allow" : "forbid", loadGroup.get(), aRequest,
|
||||
nameStr.get());
|
||||
|
||||
// Only allow a document request to set its loadgroup's permissions.
|
||||
nsLoadFlags flags;
|
||||
aRequest->GetLoadFlags(&flags);
|
||||
|
||||
if (!(flags & nsIChannel::LOAD_DOCUMENT_URI)) {
|
||||
NZPLOG("Skipping request %p [%s] - not a document load", aRequest,
|
||||
nameStr.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Do NOT allow a document load to override the loadgroup's hierarchy.
|
||||
bool ancestorsAllow = CheckLoadGroupAncestorHierarchies(loadGroup);
|
||||
|
||||
NZPLOG("LoadGroup %p ancestors for request %p [%s] %s private loads.",
|
||||
loadGroup.get(), aRequest, nameStr.get(),
|
||||
ancestorsAllow ? "allows" : "forbids");
|
||||
|
||||
if (!ancestorsAllow) {
|
||||
NZPLOG("Request %p [%s] can't override hierarchy.", aRequest,
|
||||
nameStr.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return loadGroup->SetAllowLoadsFromPrivateNetworks(aAllowed);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
80
netwerk/base/src/nsNetworkZonePolicy.h
Normal file
80
netwerk/base/src/nsNetworkZonePolicy.h
Normal file
@ -0,0 +1,80 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __nznetworkzonepolicy_h__
|
||||
#define __nznetworkzonepolicy_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsINetworkZonePolicy.h"
|
||||
#include "nsIObserver.h"
|
||||
|
||||
class nsILoadGroup;
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
|
||||
/**
|
||||
* class nsNetworkZonePolicy
|
||||
*
|
||||
* Implements nsINetworkZonePolicy: used by nsIRequest objects to check if
|
||||
* they have permission to load from private, RFC1918-like IP addresses.
|
||||
* See nsINetworkZonePolicy for more info.
|
||||
*/
|
||||
class nsNetworkZonePolicy : public nsINetworkZonePolicy
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSINETWORKZONEPOLICY
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static already_AddRefed<nsNetworkZonePolicy> GetSingleton();
|
||||
|
||||
private:
|
||||
nsNetworkZonePolicy();
|
||||
virtual ~nsNetworkZonePolicy();
|
||||
|
||||
// Returns the parent loadgroup of aLoadGroup, or nullptr if none exists.
|
||||
already_AddRefed<nsILoadGroup> GetLoadGroupParent(nsILoadGroup *aLoadGroup);
|
||||
|
||||
// Returns the owning loadgroup of aLoadGroup, or nullptr if none exists.
|
||||
already_AddRefed<nsILoadGroup> GetOwningLoadGroup(nsILoadGroup *aLoadGroup);
|
||||
|
||||
// Returns the loadgroup of the parent docshell of aLoadGroup's docshell, or
|
||||
// nullptr if none exists.
|
||||
already_AddRefed<nsILoadGroup>
|
||||
GetParentDocShellsLoadGroup(nsILoadGroup *aLoadGroup);
|
||||
|
||||
// Checks aLoadGroup and its ancestors (parent-, owning- and docshell
|
||||
// parent's loadgroups) to check for permission to load from private IP
|
||||
// addresses. The function follows the ancestor hierarchy to the root
|
||||
// loadgroup, or until a loadgroup is forbidden to load from private
|
||||
// networks. In this way, the loadgroup and all of its ancestor loadgroups
|
||||
// must have permission for this function to return true.
|
||||
bool CheckLoadGroupHierarchy(nsILoadGroup *aLoadGroup);
|
||||
|
||||
// Similar to CheckLoadGroupHierarchy, except this function checks the
|
||||
// ancestor hierarchy only for permission to load from private networks;
|
||||
// aLoadGroup is not checked.
|
||||
bool CheckLoadGroupAncestorHierarchies(nsILoadGroup *aLoadGroup);
|
||||
|
||||
// Keeps track of whether or not NZP is enabled.
|
||||
static bool sNZPEnabled;
|
||||
|
||||
// True if shutdown notification has been received.
|
||||
static bool sShutdown;
|
||||
|
||||
// Singleton pointer.
|
||||
static StaticRefPtr<nsNetworkZonePolicy> sSingleton;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* __nznetworkzonepolicy_h__ */
|
@ -1030,6 +1030,10 @@ nsSocketTransport::ResolveHost()
|
||||
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
|
||||
if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4)
|
||||
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
|
||||
if (mConnectionFlags & nsSocketTransport::DISABLE_RFC1918)
|
||||
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_RFC1918;
|
||||
if (mConnectionFlags & nsSocketTransport::DISABLE_LOOPBACK)
|
||||
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_LOOPBACK;
|
||||
|
||||
NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
|
||||
!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
|
||||
@ -1199,10 +1203,12 @@ nsSocketTransport::InitiateSocket()
|
||||
}
|
||||
}
|
||||
|
||||
// Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
|
||||
// connected - Bug 853423.
|
||||
if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 &&
|
||||
IsIPAddrLocal(&mNetAddr)) {
|
||||
// Ensure that we're not using a private IP if such addresses are disabled.
|
||||
bool disableRFC1918 = mConnectionFlags & nsISocketTransport::DISABLE_RFC1918;
|
||||
bool disableLoopback = mConnectionFlags & nsISocketTransport::DISABLE_LOOPBACK;
|
||||
|
||||
if ((disableRFC1918 && IsIPAddrLocal(&mNetAddr)) ||
|
||||
(disableLoopback && IsLoopBackAddress(&mNetAddr))) {
|
||||
#ifdef PR_LOGGING
|
||||
if (SOCKET_LOG_ENABLED()) {
|
||||
nsAutoCString netAddrCString;
|
||||
@ -1211,13 +1217,16 @@ nsSocketTransport::InitiateSocket()
|
||||
netAddrCString.BeginWriting(),
|
||||
kIPv6CStrBufSize))
|
||||
netAddrCString = NS_LITERAL_CSTRING("<IP-to-string failed>");
|
||||
SOCKET_LOG(("nsSocketTransport::InitiateSocket skipping "
|
||||
"speculative connection for host [%s:%d] proxy "
|
||||
"[%s:%d] with Local IP address [%s]",
|
||||
SOCKET_LOG(("nsSocketTransport::InitiateSocket refusing to "
|
||||
"connect to %s host [%s:%d] proxy [%s:%d] with IP "
|
||||
"address [%s]",
|
||||
IsIPAddrLocal(&mNetAddr) ? "private" : "loopback",
|
||||
mHost.get(), mPort, mProxyHost.get(), mProxyPort,
|
||||
netAddrCString.get()));
|
||||
}
|
||||
#endif
|
||||
MOZ_ASSERT(false,
|
||||
"Local or Loopback IP addresses disabled for this socket!");
|
||||
return NS_ERROR_CONNECTION_REFUSED;
|
||||
}
|
||||
|
||||
@ -1699,8 +1708,15 @@ nsSocketTransport::OnSocketEvent(uint32_t type, nsresult status, nsISupports *pa
|
||||
SOCKET_LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
|
||||
mDNSRequest = 0;
|
||||
if (param) {
|
||||
MOZ_ASSERT(NS_SUCCEEDED(status),
|
||||
"Shouldn't have DNS record if request failed.");
|
||||
mDNSRecord = static_cast<nsIDNSRecord *>(param);
|
||||
mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
|
||||
nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Unable to get address after name resolution "
|
||||
"succeeded.");
|
||||
status = rv;
|
||||
}
|
||||
}
|
||||
// status contains DNS lookup status
|
||||
if (NS_FAILED(status)) {
|
||||
|
@ -457,6 +457,16 @@
|
||||
{ 0xae, 0xcf, 0x05, 0xf8, 0xfa, 0xf0, 0x0c, 0x9b } \
|
||||
}
|
||||
|
||||
// service implementing nsINetworkZonePolicy
|
||||
#define NS_NETWORKZONEPOLICY_CONTRACTID "@mozilla.org/network/networkzonepolicy;1"
|
||||
#define NS_NETWORKZONEPOLICY_CID \
|
||||
{ /* {afcabf86-d401-41f0-a511-7a444ce31c71} */ \
|
||||
0xafcabf86, \
|
||||
0xd401, \
|
||||
0x41f0, \
|
||||
{ 0xa5, 0x11, 0x7a, 0x44, 0x4c, 0xe3, 0x1c, 0x71 } \
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* netwerk/cache/ classes
|
||||
*/
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "nsCategoryCache.h"
|
||||
#include "nsIContentSniffer.h"
|
||||
#include "Predictor.h"
|
||||
#include "nsNetworkZonePolicy.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
@ -446,6 +447,10 @@ static const mozilla::Module::CategoryEntry kNeckoCategories[] = {
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinHexDecoder)
|
||||
#endif
|
||||
|
||||
typedef mozilla::net::nsNetworkZonePolicy nsNetworkZonePolicy;
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsNetworkZonePolicy,
|
||||
nsNetworkZonePolicy::GetSingleton)
|
||||
|
||||
static nsresult
|
||||
CreateNewStreamConvServiceFactory(nsISupports* aOuter, REFNSIID aIID, void **aResult)
|
||||
{
|
||||
@ -798,6 +803,7 @@ NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_CACHE_STORAGE_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_NETWORKPREDICTOR_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_NETWORKZONEPOLICY_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
||||
{ &kNS_IOSERVICE_CID, false, nullptr, nsIOServiceConstructor },
|
||||
@ -941,6 +947,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
||||
{ &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor },
|
||||
{ &kNS_CACHE_STORAGE_SERVICE_CID, false, nullptr, CacheStorageServiceConstructor },
|
||||
{ &kNS_NETWORKPREDICTOR_CID, false, nullptr, mozilla::net::Predictor::Create },
|
||||
{ &kNS_NETWORKZONEPOLICY_CID, false, nullptr, nsNetworkZonePolicyConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
@ -1088,6 +1095,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
|
||||
{ NS_CACHE_STORAGE_SERVICE_CONTRACTID, &kNS_CACHE_STORAGE_SERVICE_CID },
|
||||
{ NS_CACHE_STORAGE_SERVICE_CONTRACTID2, &kNS_CACHE_STORAGE_SERVICE_CID },
|
||||
{ NS_NETWORKPREDICTOR_CONTRACTID, &kNS_NETWORKPREDICTOR_CID },
|
||||
{ NS_NETWORKZONEPOLICY_CONTRACTID, &kNS_NETWORKZONEPOLICY_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
|
2
netwerk/cache/nsDiskCache.h
vendored
2
netwerk/cache/nsDiskCache.h
vendored
@ -11,7 +11,7 @@
|
||||
#include "nsCacheEntry.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <winsock.h> // for htonl/ntohl
|
||||
#include <winsock2.h> // for htonl/ntohl
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -204,6 +204,11 @@ bool IsIPAddrLocal(const NetAddr *addr)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsIPAddrPrivate(const NetAddr *addr)
|
||||
{
|
||||
return IsIPAddrLocal(addr) || IsLoopBackAddress(addr);
|
||||
}
|
||||
|
||||
bool
|
||||
NetAddr::operator == (const NetAddr& other) const
|
||||
{
|
||||
@ -227,7 +232,19 @@ NetAddr::operator == (const NetAddr& other) const
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
NetAddr::EqualsIP(const NetAddr& other) const
|
||||
{
|
||||
if (this->raw.family != other.raw.family) {
|
||||
return false;
|
||||
} else if (this->raw.family == AF_INET) {
|
||||
return (this->inet.ip == other.inet.ip);
|
||||
} else if (this->raw.family == AF_INET6) {
|
||||
return (memcmp(&this->inet6.ip, &other.inet6.ip,
|
||||
sizeof(this->inet6.ip)) == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr)
|
||||
{
|
||||
|
@ -111,6 +111,8 @@ union NetAddr {
|
||||
#endif
|
||||
// introduced to support nsTArray<NetAddr> (for DNSRequestParent.cpp)
|
||||
bool operator == (const NetAddr& other) const;
|
||||
// Compares ip fields only; excludes port and other data; IPv4 and v6 only.
|
||||
bool EqualsIP(const NetAddr& other) const;
|
||||
};
|
||||
|
||||
// This class wraps a NetAddr union to provide C++ linked list
|
||||
@ -164,6 +166,8 @@ bool IsIPAddrAny(const NetAddr *addr);
|
||||
|
||||
bool IsIPAddrV4Mapped(const NetAddr *addr);
|
||||
|
||||
bool IsIPAddrPrivate(const NetAddr *addr);
|
||||
|
||||
bool IsIPAddrLocal(const NetAddr *addr);
|
||||
|
||||
} // namespace net
|
||||
|
@ -61,8 +61,19 @@ public:
|
||||
: mHostRecord(hostRecord)
|
||||
, mIter(nullptr)
|
||||
, mIterGenCnt(-1)
|
||||
, mHideLocalIPAddresses(false)
|
||||
, mHideLoopbackIPAddresses(false)
|
||||
, mDone(false) {}
|
||||
|
||||
// Do not return private, RFC1918-like IP addresses.
|
||||
void HideLocalIPAddresses();
|
||||
|
||||
// Do not return loopback addresses.
|
||||
void HideLoopbackIPAddresses();
|
||||
|
||||
// Convenience function for nsIDNSRecord.hasMore().
|
||||
bool HasMore();
|
||||
|
||||
private:
|
||||
virtual ~nsDNSRecord() {}
|
||||
|
||||
@ -71,11 +82,27 @@ private:
|
||||
int mIterGenCnt; // the generation count of
|
||||
// mHostRecord->addr_info when we
|
||||
// start iterating
|
||||
// True if private, RFC1918-like IP addresses should be hidden.
|
||||
bool mHideLocalIPAddresses;
|
||||
// True if loopback addresses should be hidden.
|
||||
bool mHideLoopbackIPAddresses;
|
||||
bool mDone;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
|
||||
|
||||
void
|
||||
nsDNSRecord::HideLocalIPAddresses()
|
||||
{
|
||||
mHideLocalIPAddresses = true;
|
||||
}
|
||||
|
||||
void
|
||||
nsDNSRecord::HideLoopbackIPAddresses()
|
||||
{
|
||||
mHideLoopbackIPAddresses = true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSRecord::GetCanonicalName(nsACString &result)
|
||||
{
|
||||
@ -122,8 +149,11 @@ nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
|
||||
} else {
|
||||
mIter = mIter->getNext();
|
||||
}
|
||||
}
|
||||
while (mIter && mHostRecord->Blacklisted(&mIter->mAddress));
|
||||
} while (mIter && (mHostRecord->Blacklisted(&mIter->mAddress) ||
|
||||
(mHideLocalIPAddresses &&
|
||||
IsIPAddrLocal(&mIter->mAddress)) ||
|
||||
(mHideLoopbackIPAddresses &&
|
||||
IsLoopBackAddress(&mIter->mAddress))));
|
||||
|
||||
if (!mIter && startedFresh) {
|
||||
// If everything was blacklisted we want to reset the blacklist (and
|
||||
@ -131,6 +161,14 @@ nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
|
||||
// than nothing.
|
||||
mHostRecord->ResetBlacklist();
|
||||
mIter = mHostRecord->addr_info->mAddresses.getFirst();
|
||||
|
||||
// If Private IPs are hidden, return the first public address.
|
||||
while (mIter && ((mHideLocalIPAddresses &&
|
||||
IsIPAddrLocal(&mIter->mAddress)) ||
|
||||
(mHideLoopbackIPAddresses &&
|
||||
IsLoopBackAddress(&mIter->mAddress)))) {
|
||||
mIter = mIter->getNext();
|
||||
}
|
||||
}
|
||||
|
||||
if (mIter) {
|
||||
@ -147,7 +185,9 @@ nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
|
||||
else {
|
||||
mHostRecord->addr_info_lock.Unlock();
|
||||
|
||||
if (!mHostRecord->addr) {
|
||||
if (!mHostRecord->addr ||
|
||||
(mHideLocalIPAddresses && IsIPAddrLocal(mHostRecord->addr)) ||
|
||||
(mHideLoopbackIPAddresses && IsLoopBackAddress(mHostRecord->addr))) {
|
||||
// Both mHostRecord->addr_info and mHostRecord->addr are null.
|
||||
// This can happen if mHostRecord->addr_info expired and the
|
||||
// attempt to reresolve it failed.
|
||||
@ -216,6 +256,15 @@ nsDNSRecord::HasMore(bool *result)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsDNSRecord::HasMore()
|
||||
{
|
||||
bool more;
|
||||
DebugOnly<nsresult> rv = HasMore(&more);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
return more;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSRecord::Rewind()
|
||||
{
|
||||
@ -293,9 +342,18 @@ nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
|
||||
nsCOMPtr<nsIDNSRecord> rec;
|
||||
if (NS_SUCCEEDED(status)) {
|
||||
NS_ASSERTION(hostRecord, "no host record");
|
||||
rec = new nsDNSRecord(hostRecord);
|
||||
if (!rec)
|
||||
status = NS_ERROR_OUT_OF_MEMORY;
|
||||
nsRefPtr<nsDNSRecord> recImpl = new nsDNSRecord(hostRecord);
|
||||
if (mFlags & nsIDNSService::RESOLVE_DISABLE_RFC1918) {
|
||||
recImpl->HideLocalIPAddresses();
|
||||
}
|
||||
if (mFlags & nsIDNSService::RESOLVE_DISABLE_LOOPBACK) {
|
||||
recImpl->HideLoopbackIPAddresses();
|
||||
}
|
||||
if (!recImpl->HasMore()) {
|
||||
status = NS_ERROR_UNKNOWN_HOST;
|
||||
recImpl = nullptr;
|
||||
}
|
||||
rec = recImpl.forget();
|
||||
}
|
||||
|
||||
MOZ_EVENT_TRACER_DONE(this, "net::dns::lookup");
|
||||
@ -837,11 +895,18 @@ nsDNSService::Resolve(const nsACString &hostname,
|
||||
rv = syncReq.mStatus;
|
||||
else {
|
||||
NS_ASSERTION(syncReq.mHostRecord, "no host record");
|
||||
nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
|
||||
if (!rec)
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
else
|
||||
NS_ADDREF(*result = rec);
|
||||
nsRefPtr<nsDNSRecord> rec = new nsDNSRecord(syncReq.mHostRecord);
|
||||
if (flags & nsIDNSService::RESOLVE_DISABLE_RFC1918) {
|
||||
rec->HideLocalIPAddresses();
|
||||
}
|
||||
if (flags & nsIDNSService::RESOLVE_DISABLE_LOOPBACK) {
|
||||
rec->HideLoopbackIPAddresses();
|
||||
}
|
||||
if (!rec->HasMore()) {
|
||||
rv = NS_ERROR_UNKNOWN_HOST;
|
||||
rec = nullptr;
|
||||
}
|
||||
rec.forget(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,4 +138,16 @@ interface nsIDNSService : nsISupports
|
||||
* If set, only IPv6 addresses will be returned from resolve/asyncResolve.
|
||||
*/
|
||||
const unsigned long RESOLVE_DISABLE_IPV4 = (1 << 7);
|
||||
|
||||
/**
|
||||
* If set, local (RFC1918) addresses will NOT be returned from
|
||||
* resolve/asyncResolve.
|
||||
*/
|
||||
const unsigned long RESOLVE_DISABLE_RFC1918 = (1 << 8);
|
||||
|
||||
/**
|
||||
* If set, loopback addresses will NOT be returned from
|
||||
* resolve/asyncResolve.
|
||||
*/
|
||||
const unsigned long RESOLVE_DISABLE_LOOPBACK = (1 << 9);
|
||||
};
|
||||
|
@ -76,7 +76,9 @@ HttpBaseChannel::HttpBaseChannel()
|
||||
|
||||
// Subfields of unions cannot be targeted in an initializer list
|
||||
mSelfAddr.raw.family = PR_AF_UNSPEC;
|
||||
memset(&mSelfAddr, 0, sizeof(mSelfAddr.raw.data));
|
||||
mPeerAddr.raw.family = PR_AF_UNSPEC;
|
||||
memset(&mPeerAddr, 0, sizeof(mPeerAddr.raw.data));
|
||||
}
|
||||
|
||||
HttpBaseChannel::~HttpBaseChannel()
|
||||
|
@ -140,6 +140,9 @@ public:
|
||||
// Update the callbacks used to provide security info. May be called on
|
||||
// any thread.
|
||||
virtual void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks) = 0;
|
||||
|
||||
// Returns true if the socket peer has a private (RFC1918-like) address.
|
||||
virtual bool PeerHasPrivateIP() = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpConnection, NS_AHTTPCONNECTION_IID)
|
||||
@ -230,6 +233,10 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpConnection, NS_AHTTPCONNECTION_IID)
|
||||
{ \
|
||||
if (fwdObject) \
|
||||
(fwdObject)->SetSecurityCallbacks(aCallbacks); \
|
||||
} \
|
||||
bool PeerHasPrivateIP() \
|
||||
{ \
|
||||
return fwdObject ? (fwdObject)->PeerHasPrivateIP() : false; \
|
||||
}
|
||||
|
||||
}} // namespace mozilla::net
|
||||
|
@ -83,6 +83,9 @@ typedef uint8_t nsHttpVersion;
|
||||
// weaker security profiles based on past history
|
||||
#define NS_HTTP_ALLOW_RSA_FALSESTART (1<<9)
|
||||
|
||||
// Allows a transaction to use a connection to a private, RFC1918-like address.
|
||||
#define NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES (1<<10)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// some default values
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "nsIStreamListenerTee.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsILoadGroupChild.h"
|
||||
#include "nsINetworkZonePolicy.h"
|
||||
#include "nsIProtocolProxyService2.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "nsNetUtil.h"
|
||||
@ -2707,6 +2708,34 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC
|
||||
}
|
||||
buf.Adopt(0);
|
||||
|
||||
// If the entry was loaded from a private/RFC1918 network, verify that
|
||||
// private loads are allowed for this loadgroup and that the network
|
||||
// link ID matches.
|
||||
rv = entry->GetMetaDataElement("loaded-from-private-network",
|
||||
getter_Copies(buf));
|
||||
if (NS_SUCCEEDED(rv) && !buf.IsEmpty()) {
|
||||
bool privateIPAddrOK = true;
|
||||
nsCString currentNetworkIDString;
|
||||
rv = gIOService->GetNetworkLinkID(currentNetworkIDString);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
if (!buf.Equals(currentNetworkIDString)) {
|
||||
LOG(("nsHttpChannel::OnCacheEntryCheck %p private entry "
|
||||
"does not match network link ID - not wanted.", this));
|
||||
privateIPAddrOK = false;
|
||||
} else {
|
||||
privateIPAddrOK = mCaps & NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES;
|
||||
LOG(("nsHttpChannel::OnCacheEntryCheck %p private entry %s.",
|
||||
this, privateIPAddrOK ? "allowed" : "forbidden"));
|
||||
}
|
||||
if (!privateIPAddrOK) {
|
||||
*aResult = ENTRY_NOT_WANTED;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
buf.Adopt(0);
|
||||
|
||||
// We'll need this value in later computations...
|
||||
uint32_t lastModifiedTime;
|
||||
rv = entry->GetLastModified(&lastModifiedTime);
|
||||
@ -3123,6 +3152,30 @@ nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry *aEntry,
|
||||
Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
|
||||
false);
|
||||
}
|
||||
|
||||
// For cached doument loads, we must set Private/Public Network load
|
||||
// permissions for the document's sub-resources.
|
||||
// -- Forbid private loads if the doc was loaded on a public network.
|
||||
// -- Allow private loads if the doc was loaded from a private IP.
|
||||
// Note: Private docs should only be loaded from the same network.
|
||||
// This check should already have been done in OnCacheEntryCheck.
|
||||
if (mNZP && !aNew && mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
|
||||
nsXPIDLCString buf;
|
||||
nsresult rv =
|
||||
mCacheEntry->GetMetaDataElement("loaded-from-private-network",
|
||||
getter_Copies(buf));
|
||||
bool privateIPAddrOK = NS_SUCCEEDED(rv) && !buf.IsEmpty();
|
||||
|
||||
LOG(("nsHttpChannel::OnNormalCacheEntryAvailable %p document "
|
||||
"load: %s sub-resource loads from private networks.",
|
||||
this, privateIPAddrOK ? "allows" : "forbids"));
|
||||
|
||||
rv = mNZP->SetPrivateNetworkPermission(this, privateIPAddrOK);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("nsHttpChannel::OnNormalCacheEntryAvailable %p failed "
|
||||
"SetPrivateNetworkPermission rv=0x%x", this, rv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -3867,6 +3920,23 @@ nsHttpChannel::AddCacheEntryHeaders(nsICacheEntry *entry)
|
||||
rv = entry->SetMetaDataElement("response-head", head.get());
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// If the response was loaded from a private/RFC1918 network, store
|
||||
// the networkLinkID in a metadata header.
|
||||
if (IsIPAddrPrivate(&mPeerAddr)) {
|
||||
char privateNetworkIDString[21];
|
||||
PR_snprintf(privateNetworkIDString, sizeof(privateNetworkIDString),
|
||||
"%llu", mPrivateNetworkID);
|
||||
MOZ_ASSERT(strlen(privateNetworkIDString) > 0);
|
||||
|
||||
LOG(("nsHttpChannel::AddCacheEntryHeaders %p setting loaded-from-"
|
||||
"private-network=%s", this, privateNetworkIDString));
|
||||
rv = entry->SetMetaDataElement("loaded-from-private-network",
|
||||
privateNetworkIDString);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate we have successfully finished setting metadata on the cache entry.
|
||||
rv = entry->MetaDataReady();
|
||||
|
||||
@ -4473,8 +4543,32 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
|
||||
|
||||
// add ourselves to the load group. from this point forward, we'll report
|
||||
// all failures asynchronously.
|
||||
if (mLoadGroup)
|
||||
if (mLoadGroup) {
|
||||
mLoadGroup->AddRequest(this, nullptr);
|
||||
}
|
||||
|
||||
// Check if the channel is allowed to load from private addresses.
|
||||
mNZP = do_GetService(NS_NETWORKZONEPOLICY_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv) && mNZP) {
|
||||
bool privateIPAddrOK = false;
|
||||
rv = mNZP->CheckPrivateNetworkPermission(this, &privateIPAddrOK);
|
||||
if (NS_SUCCEEDED(rv) && privateIPAddrOK) {
|
||||
mCaps |= NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES;
|
||||
} else if (NS_FAILED(rv)) {
|
||||
LOG(("nsHttpChannel::AsyncOpen %p CheckPrivateNetworkPermission "
|
||||
"failed with rv=0x%x", this, rv));
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoCString host;
|
||||
rv = mURI->GetAsciiHost(host);
|
||||
LOG(("nsHttpChannel::AsyncOpen %p private addresses %s for "
|
||||
"%s", this, privateIPAddrOK ? "allowed" : "forbidden",
|
||||
host.get()));
|
||||
#endif
|
||||
} else {
|
||||
LOG(("nsHttpChannel::AsyncOpen %p No NetworkZonePolicy object rv=0x%x",
|
||||
this, rv));
|
||||
}
|
||||
|
||||
// record asyncopen time unconditionally and clear it if we
|
||||
// don't want it after OnModifyRequest() weighs in. But waiting for
|
||||
@ -5421,8 +5515,45 @@ nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
|
||||
nsCOMPtr<nsISocketTransport> socketTransport =
|
||||
do_QueryInterface(trans);
|
||||
if (socketTransport) {
|
||||
socketTransport->GetSelfAddr(&mSelfAddr);
|
||||
socketTransport->GetPeerAddr(&mPeerAddr);
|
||||
nsresult selfRv = socketTransport->GetSelfAddr(&mSelfAddr);
|
||||
nsresult peerRv = socketTransport->GetPeerAddr(&mPeerAddr);
|
||||
|
||||
// XXX Remove this call to UpdateNetworkLinkID once Bug 939319 and
|
||||
// associated bugs for NS_NETWORK_LINK_DATA_CHANGED are complete on
|
||||
// all supported platforms. For now, update the network link ID with
|
||||
// mSelfAddr, assuming we were able to get it successfully.
|
||||
if (NS_SUCCEEDED(selfRv)) {
|
||||
gIOService->UpdateNetworkLinkID(mSelfAddr);
|
||||
}
|
||||
|
||||
// If the peer is private, store the network link ID now to be
|
||||
// saved in the cache headers later. Also check permissions.
|
||||
bool peerHasPrivateAddr = NS_SUCCEEDED(peerRv) &&
|
||||
IsIPAddrPrivate(&mPeerAddr);
|
||||
if (peerHasPrivateAddr) {
|
||||
mPrivateNetworkID = gIOService->GetNetworkLinkID();
|
||||
|
||||
if (!(mCaps & NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES)) {
|
||||
LOG(("nsHttpChannel::OnTransportStatus %p not permitted "
|
||||
"to load from private IP address! Canceling.", this));
|
||||
return Cancel(NS_ERROR_CONNECTION_REFUSED);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a document load, set permissions for the rest of the
|
||||
// loadgroup's requests.
|
||||
if (mNZP && NS_SUCCEEDED(peerRv) &&
|
||||
mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
|
||||
LOG(("nsHttpChannel::OnTransportStatus %p document load: "
|
||||
"%s sub-resource loads from private networks.",
|
||||
this, peerHasPrivateAddr ? "allows" : "forbids"));
|
||||
nsresult rv =
|
||||
mNZP->SetPrivateNetworkPermission(this, peerHasPrivateAddr);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("nsHttpChannel::OnTransportStatus %p failed "
|
||||
"SetPrivateNetworkPermission rv=0x%x", this, rv));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsIThreadRetargetableRequest.h"
|
||||
#include "nsIThreadRetargetableStreamListener.h"
|
||||
#include "nsINetworkZonePolicy.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "TimingStruct.h"
|
||||
#include "AutoClose.h"
|
||||
@ -422,6 +423,12 @@ protected:
|
||||
|
||||
private: // cache telemetry
|
||||
bool mDidReval;
|
||||
|
||||
// The network link ID generated by nsIOService at the time of connection.
|
||||
uint64_t mPrivateNetworkID;
|
||||
|
||||
// Set during AsyncOpen
|
||||
nsCOMPtr<nsINetworkZonePolicy> mNZP;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::net
|
||||
|
@ -1712,6 +1712,23 @@ nsHttpConnection::OnSocketReadable()
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpConnection::PeerHasPrivateIP()
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
if (!mSocketTransport) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NetAddr peerAddr;
|
||||
nsresult rv = mSocketTransport->GetPeerAddr(&peerAddr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsIPAddrPrivate(&peerAddr);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnection::SetupSecondaryTLS()
|
||||
{
|
||||
|
@ -200,6 +200,9 @@ public:
|
||||
void SetupSecondaryTLS();
|
||||
void SetInSpdyTunnel(bool arg);
|
||||
|
||||
// Returns true if the socket peer has a private (RFC1918-like) address.
|
||||
bool PeerHasPrivateIP();
|
||||
|
||||
private:
|
||||
// Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
|
||||
enum TCPKeepaliveConfig {
|
||||
|
@ -2938,10 +2938,20 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
|
||||
tmpFlags |= nsISocketTransport::DISABLE_IPV6;
|
||||
}
|
||||
|
||||
if (IsSpeculative()) {
|
||||
// Allow speculative connections for loopback so we can run tests.
|
||||
if (IsSpeculative() && gHttpHandler->AllowSpeculativeConnectOnLoopback()) {
|
||||
tmpFlags |= nsISocketTransport::DISABLE_RFC1918;
|
||||
LOG(("nsHalfOpenSocket::SetupStreams %p Disable private IPs for "
|
||||
"speculative connections", this));
|
||||
} else if (IsSpeculative() ||
|
||||
!(mCaps & NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES)) {
|
||||
tmpFlags |= nsISocketTransport::DISABLE_LOOPBACK |
|
||||
nsISocketTransport::DISABLE_RFC1918;
|
||||
LOG(("nsHalfOpenSocket::SetupStreams %p Disable loopback and private "
|
||||
"IPs", this));
|
||||
}
|
||||
|
||||
|
||||
socketTransport->SetConnectionFlags(tmpFlags);
|
||||
|
||||
socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
|
||||
|
@ -196,6 +196,7 @@ nsHttpHandler::nsHttpHandler()
|
||||
, mConnectTimeout(90000)
|
||||
, mBypassCacheLockThreshold(250.0)
|
||||
, mParallelSpeculativeConnectLimit(6)
|
||||
, mAllowSpeculativeConnectOnLoopback(false)
|
||||
, mRequestTokenBucketEnabled(true)
|
||||
, mRequestTokenBucketMinParallelism(6)
|
||||
, mRequestTokenBucketHz(100)
|
||||
@ -520,28 +521,6 @@ nsHttpHandler::GetIOService(nsIIOService** result)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsHttpHandler::Get32BitsOfPseudoRandom()
|
||||
{
|
||||
// only confirm rand seeding on socket thread
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
|
||||
// rand() provides different amounts of PRNG on different platforms.
|
||||
// 15 or 31 bits are common amounts.
|
||||
|
||||
PR_STATIC_ASSERT(RAND_MAX >= 0xfff);
|
||||
|
||||
#if RAND_MAX < 0xffffU
|
||||
return ((uint16_t) rand() << 20) |
|
||||
(((uint16_t) rand() & 0xfff) << 8) |
|
||||
((uint16_t) rand() & 0xff);
|
||||
#elif RAND_MAX < 0xffffffffU
|
||||
return ((uint16_t) rand() << 16) | ((uint16_t) rand() & 0xffff);
|
||||
#else
|
||||
return (uint32_t) rand();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpHandler::NotifyObservers(nsIHttpChannel *chan, const char *event)
|
||||
{
|
||||
@ -1256,6 +1235,13 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
||||
mParallelSpeculativeConnectLimit = (uint32_t) clamped(val, 0, 1024);
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("speculative.allowLoopback"))) {
|
||||
rv = prefs->GetBoolPref(HTTP_PREF("speculative.allowLoopback"), &cVar);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mAllowSpeculativeConnectOnLoopback = cVar;
|
||||
}
|
||||
}
|
||||
|
||||
// Whether or not to block requests for non head js/css items (e.g. media)
|
||||
// while those elements load.
|
||||
if (PREF_CHANGED(HTTP_PREF("rendering-critical-requests-prioritization"))) {
|
||||
|
@ -109,6 +109,7 @@ public:
|
||||
bool AllowPush() { return mAllowPush; }
|
||||
uint32_t ConnectTimeout() { return mConnectTimeout; }
|
||||
uint32_t ParallelSpeculativeConnectLimit() { return mParallelSpeculativeConnectLimit; }
|
||||
bool AllowSpeculativeConnectOnLoopback() { return mAllowSpeculativeConnectOnLoopback; }
|
||||
bool CriticalRequestPrioritization() { return mCriticalRequestPrioritization; }
|
||||
double BypassCacheLockThreshold() { return mBypassCacheLockThreshold; }
|
||||
|
||||
@ -228,9 +229,6 @@ public:
|
||||
nsICookieService * GetCookieService(); // not addrefed
|
||||
nsISiteSecurityService * GetSSService();
|
||||
|
||||
// callable from socket thread only
|
||||
uint32_t Get32BitsOfPseudoRandom();
|
||||
|
||||
// Called by the channel synchronously during asyncOpen
|
||||
void OnOpeningRequest(nsIHttpChannel *chan)
|
||||
{
|
||||
@ -482,6 +480,9 @@ private:
|
||||
// when starting a new speculative connection.
|
||||
uint32_t mParallelSpeculativeConnectLimit;
|
||||
|
||||
// Allow speculative connections on loopback. Primarily for testing.
|
||||
bool mAllowSpeculativeConnectOnLoopback;
|
||||
|
||||
// For Rate Pacing of HTTP/1 requests through a netwerk/base/src/EventTokenBucket
|
||||
// Active requests <= *MinParallelism are not subject to the rate pacing
|
||||
bool mRequestTokenBucketEnabled;
|
||||
|
@ -632,6 +632,15 @@ nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
|
||||
mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
|
||||
}
|
||||
|
||||
// Verify permission to load from private (RFC1918-like) addresses.
|
||||
if (!(mCaps & NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES) &&
|
||||
mConnection->PeerHasPrivateIP()) {
|
||||
LOG(("nsHttpTransaction::ReadSegments %p private IPs forbidden; "
|
||||
"closing transaction.", this));
|
||||
Close(NS_ERROR_CONNECTION_REFUSED);
|
||||
return NS_ERROR_CONNECTION_REFUSED;
|
||||
}
|
||||
|
||||
mReader = reader;
|
||||
|
||||
nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
|
||||
|
@ -43,10 +43,6 @@ TimeoutListener.prototype = {
|
||||
},
|
||||
};
|
||||
|
||||
function serverStopListener() {
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
function testTimeout(timeoutEnabled, expectResponse) {
|
||||
// Set timeout pref.
|
||||
if (timeoutEnabled) {
|
||||
@ -137,8 +133,11 @@ function setup_tests() {
|
||||
function setup_http_server() {
|
||||
// Start server; will be stopped at test cleanup time.
|
||||
server.start(-1);
|
||||
baseURL = "http://localhost:" + server.identity.primaryPort + "/";
|
||||
baseURL = server.identity.primaryScheme + "://" +
|
||||
server.identity.primaryHost + ":" +
|
||||
server.identity.primaryPort + "/";
|
||||
do_print("Using baseURL: " + baseURL);
|
||||
|
||||
server.registerPathHandler('/', function(metadata, response) {
|
||||
// Wait until the timeout should have passed, then respond.
|
||||
response.processAsync();
|
||||
@ -150,7 +149,7 @@ function setup_http_server() {
|
||||
});
|
||||
});
|
||||
do_register_cleanup(function() {
|
||||
server.stop(serverStopListener);
|
||||
server.stop(function() {});
|
||||
});
|
||||
}
|
||||
|
||||
|
613
netwerk/test/unit/test_networkZonePolicy.js
Normal file
613
netwerk/test/unit/test_networkZonePolicy.js
Normal file
@ -0,0 +1,613 @@
|
||||
/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
var nzp = Cc["@mozilla.org/network/networkzonepolicy;1"]
|
||||
.createInstance(Ci.nsINetworkZonePolicy);
|
||||
// HTTP Server for 'network' requests.
|
||||
var httpServ;
|
||||
// URI Base for requests.
|
||||
var uriBase;
|
||||
|
||||
// Listener implements nsIStreamListener.
|
||||
//
|
||||
// @param expectSuccess If true, Listener will check for request success.
|
||||
// If false, Listener will check for failure and ensure
|
||||
// no onDataAvailable calls are made.
|
||||
// @param loadGroupAllows
|
||||
// Indicates if loadGroup should allow or forbid private
|
||||
// loads AFTER the response is received. This may be
|
||||
// changed by the channel based on the type of channel
|
||||
// load.
|
||||
function Listener(expectSuccess, loadGroupAllows) {
|
||||
this._expectSuccess = expectSuccess;
|
||||
this._loadGroupAllows = loadGroupAllows;
|
||||
}
|
||||
|
||||
Listener.prototype = {
|
||||
_expectSuccess: false,
|
||||
_loadGroupAllows: true,
|
||||
_buffer: null,
|
||||
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(Components.interfaces.nsIStreamListener) ||
|
||||
iid.equals(Components.interfaces.nsIRequestObserver) ||
|
||||
iid.equals(Components.interfaces.nsISupports))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
onStartRequest: function(request, ctx) {
|
||||
do_check_true(request instanceof Ci.nsIHttpChannel);
|
||||
if (this._expectSuccess) {
|
||||
do_check_true(Components.isSuccessCode(request.status));
|
||||
do_check_eq(request.requestSucceeded, true);
|
||||
do_check_eq(request.responseStatus, 200);
|
||||
request.visitResponseHeaders({ visitHeader: function(aHeader, aValue) {
|
||||
do_print(aHeader + ": " + aValue);
|
||||
}});
|
||||
do_print(request.responseStatus + ": " + request.responseStatusText);
|
||||
this._buffer = "";
|
||||
} else {
|
||||
do_check_false(Components.isSuccessCode(request.status));
|
||||
}
|
||||
},
|
||||
|
||||
onDataAvailable: function(request, ctx, stream, off, cnt) {
|
||||
do_check_true(request instanceof Ci.nsIHttpChannel);
|
||||
if (!this._expectSuccess) {
|
||||
do_throw("Should not get data; private load forbidden!");
|
||||
}
|
||||
this._buffer = this._buffer.concat(read_stream(stream, cnt));
|
||||
},
|
||||
|
||||
onStopRequest: function(request, ctx, status) {
|
||||
do_check_true(request instanceof Ci.nsIHttpChannel);
|
||||
// Check loadgroup permission has not changed.
|
||||
do_check_eq(request.loadGroup.allowLoadsFromPrivateNetworks,
|
||||
this._loadGroupAllows);
|
||||
|
||||
if (this._expectSuccess) {
|
||||
do_check_true(Components.isSuccessCode(status));
|
||||
} else {
|
||||
do_check_false(Components.isSuccessCode(status));
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Test Functions
|
||||
//
|
||||
|
||||
// Ensure that the pref enables and disables private load restrictions.
|
||||
function test_basic_NetworkZonePolicy_pref() {
|
||||
// Create loadgroup and channel for non-doc load.
|
||||
var loadGroup = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
var chan = ios.newChannel("http://localhost/failme/", null, null)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.loadGroup = loadGroup;
|
||||
|
||||
// Verify that we're starting with the pref enabled. This should have been set
|
||||
// during this scripts setup phase.
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefBranch);
|
||||
var nzpEnabled = prefs.getBoolPref("network.zonepolicy.enabled");
|
||||
do_check_true(nzpEnabled);
|
||||
|
||||
// Verify defaults
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
|
||||
|
||||
// Set permission for doc load; verify permission changed.
|
||||
chan.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI;
|
||||
|
||||
nzp.setPrivateNetworkPermission(chan, false);
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
|
||||
|
||||
// Disable pref; ensure restrictions lifted.
|
||||
prefs.setBoolPref("network.zonepolicy.enabled", false);
|
||||
// Loadgroup permission will still be "forbid private loads".
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
|
||||
// NZP will report that private loads are ok.
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
|
||||
|
||||
// Enable pref again; ensure restrictions are again in play.
|
||||
prefs.setBoolPref("network.zonepolicy.enabled", true);
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
|
||||
|
||||
// Leaving pref on for the remainder of the tests.
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Ensure that NetworkZonePolicy can manage permissions for a channel's
|
||||
// loadgroup; no loadgroup ancestors.
|
||||
function test_basic_NetworkZonePolicy_and_loadGroup() {
|
||||
// Create loadgroup and channel for non-doc load.
|
||||
var loadGroup = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
var chan = ios.newChannel("http://localhost/failme/", null, null)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.loadGroup = loadGroup;
|
||||
|
||||
// Verify defaults
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
|
||||
|
||||
// Set permission for non-doc load; verify no changes.
|
||||
nzp.setPrivateNetworkPermission(chan, false);
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
|
||||
|
||||
// Set permission for doc load; verify permission changed.
|
||||
chan.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI;
|
||||
|
||||
nzp.setPrivateNetworkPermission(chan, false);
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Ensure that NetworkZonePolicy can manage permissions for a channel's
|
||||
// loadgroup and one of its ancestors. Ancestor is specified by calling
|
||||
// function.
|
||||
function test_loadGroup_and_ancestor(loadGroup, ancestor, chan) {
|
||||
// Verify permission defaults
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(ancestor.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
|
||||
|
||||
// Set permission for non-doc load; verify no changes.
|
||||
nzp.setPrivateNetworkPermission(chan, false);
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(ancestor.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
|
||||
|
||||
// Set permission for doc load; verify permission changed for loadgroup, but
|
||||
// not for ancestor loadgroup.
|
||||
chan.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI;
|
||||
|
||||
nzp.setPrivateNetworkPermission(chan, false);
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
|
||||
do_check_eq(ancestor.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
|
||||
|
||||
// Verify we can set permission allowed again.
|
||||
nzp.setPrivateNetworkPermission(chan, true);
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(ancestor.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
|
||||
|
||||
// Set ancestor to forbid private loads; verify chan permission forbidden.
|
||||
ancestor.allowLoadsFromPrivateNetworks = false;
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
|
||||
// ... loadgroup's own persmission should still be allow.
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
|
||||
// ... nzp should not be able to set permission to true.
|
||||
nzp.setPrivateNetworkPermission(chan, true);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
|
||||
|
||||
// Reset ancestor and verify.
|
||||
ancestor.allowLoadsFromPrivateNetworks = true;
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
|
||||
nzp.setPrivateNetworkPermission(chan, false);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
|
||||
}
|
||||
|
||||
// Ensure that NetworkZonePolicy can manage permissions for a channel's
|
||||
// loadgroup; loadgroup has a parent loadgroup.
|
||||
function test_basic_NetworkZonePolicy_loadGroup_and_parent() {
|
||||
// Create loadgroup, parent loadgroup and channel for non-doc load.
|
||||
var loadGroup = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
var loadGroupAsChild = loadGroup.QueryInterface(Ci.nsILoadGroupChild)
|
||||
var chan = ios.newChannel("http://localhost/failme/", null, null)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.loadGroup = loadGroup;
|
||||
|
||||
var parent = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
loadGroupAsChild.parentLoadGroup = parent;
|
||||
do_check_eq(parent, loadGroupAsChild.parentLoadGroup);
|
||||
|
||||
test_loadGroup_and_ancestor(loadGroup, parent, chan);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Ensure that NetworkZonePolicy can manage permissions for a channel's
|
||||
// loadgroup; loadgroup is member of another loadgroup.
|
||||
function test_basic_NetworkZonePolicy_loadGroup_and_owner() {
|
||||
// Create loadgroup, parent loadgroup and channel for non-doc load.
|
||||
var loadGroup = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
var chan = ios.newChannel("http://localhost/failme/", null, null)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.loadGroup = loadGroup;
|
||||
|
||||
var owner = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
loadGroup.loadGroup = owner;
|
||||
do_check_eq(owner, loadGroup.loadGroup);
|
||||
|
||||
test_loadGroup_and_ancestor(loadGroup, owner, chan);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Ensure that NetworkZonePolicy can manage permissions for a channel's
|
||||
// loadgroup; loadgroup is a docshell loadgroup that has a parent docshell.
|
||||
function test_basic_NetworkZonePolicy_loadGroup_and_docshell() {
|
||||
// Create docshell and docshell parent, and get their loadgroups.
|
||||
var docShell = Cc["@mozilla.org/docshell;1"].createInstance(Ci.nsIDocShell);
|
||||
|
||||
var docShellParent = Cc["@mozilla.org/docshell;1"]
|
||||
.createInstance(Ci.nsIDocShell);
|
||||
docShellParent.addChild(docShell);
|
||||
|
||||
var loadGroup = docShell.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
|
||||
var dsParent = docShellParent.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
|
||||
|
||||
// Create a channel for non-doc load.
|
||||
var chan = ios.newChannel("http://localhost/failme/", null, null)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.loadGroup = loadGroup;
|
||||
|
||||
test_loadGroup_and_ancestor(loadGroup, dsParent, chan);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Ensure that a loadgroup's immediate ancestors dictate its private load
|
||||
// permissions.
|
||||
function test_loadGroup_immediate_ancestors() {
|
||||
// Create docshell and docshell parent, and get their loadgroups.
|
||||
var docShell = Cc["@mozilla.org/docshell;1"].createInstance(Ci.nsIDocShell);
|
||||
var docShellParent = Cc["@mozilla.org/docshell;1"]
|
||||
.createInstance(Ci.nsIDocShell);
|
||||
docShellParent.addChild(docShell);
|
||||
|
||||
var loadGroup = docShell.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
|
||||
var dsParent = docShellParent.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
|
||||
|
||||
// Add owning loadgroup.
|
||||
var owner = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
loadGroup.loadGroup = owner;
|
||||
do_check_eq(owner, loadGroup.loadGroup);
|
||||
|
||||
// Add parent loadgroup.
|
||||
var loadGroupAsChild = loadGroup.QueryInterface(Ci.nsILoadGroupChild)
|
||||
var parent = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
loadGroupAsChild.parentLoadGroup = parent;
|
||||
do_check_eq(parent, loadGroupAsChild.parentLoadGroup);
|
||||
|
||||
// Create a channel for non-doc load.
|
||||
var chan = ios.newChannel("http://localhost/failme/", null, null)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.loadGroup = loadGroup;
|
||||
|
||||
// Verify permission defaults
|
||||
do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(dsParent.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(owner.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(parent.allowLoadsFromPrivateNetworks, true);
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
|
||||
|
||||
// Set ancestors to forbid.
|
||||
for (var i = 0; i < 8; i++) {
|
||||
dsParent.allowLoadsFromPrivateNetworks = !!(i & 1);
|
||||
owner.allowLoadsFromPrivateNetworks = !!(i & 2);
|
||||
parent.allowLoadsFromPrivateNetworks = !!(i & 4);
|
||||
// Permission allowed only when all ancestors allow private loads.
|
||||
do_check_eq(nzp.checkPrivateNetworkPermission(chan), (i == 7));
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Checks a channel load based on its loadgroup private load permissions.
|
||||
//
|
||||
// @param allowPrivateLoads Indicates if private loads should be allowed on
|
||||
// the channel's loadgroup or not.
|
||||
// @param expectSuccessfulResponse
|
||||
// Indicates if the nsIStreamListener for the channel
|
||||
// load should expect a response with success or
|
||||
// failure.
|
||||
// @param urlStr The url that should be loaded by the channel.
|
||||
//
|
||||
function test_single_loadGroup(allowPrivateLoads,
|
||||
expectSuccessfulResponse,
|
||||
urlStr) {
|
||||
// Create loadgroup and channel for non-doc load.
|
||||
var loadGroup = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
var chan = ios.newChannel(urlStr, null, null)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.loadGroup = loadGroup;
|
||||
|
||||
do_print("Setting loadgroup permission: " +
|
||||
(allowPrivateLoads ? "Allowing" : "Forbidding") +
|
||||
" private loads for " + urlStr + ".");
|
||||
loadGroup.allowLoadsFromPrivateNetworks = allowPrivateLoads;
|
||||
|
||||
// Try to load channel with IP literal. For non-doc loads like this, load
|
||||
// group permissions should not change after the response is received.
|
||||
var listener = new Listener(expectSuccessfulResponse, allowPrivateLoads);
|
||||
chan.asyncOpen(listener, null);
|
||||
}
|
||||
|
||||
// Same as test_single_loadGroup but for a document load.
|
||||
function test_single_loadGroup_doc_load(allowPrivateLoads,
|
||||
expectSuccessfulResponse,
|
||||
loadGroupAllowsAfter,
|
||||
urlStr) {
|
||||
// Create loadgroup and channel for document load.
|
||||
var loadGroup = Cc["@mozilla.org/network/load-group;1"]
|
||||
.createInstance(Ci.nsILoadGroup);
|
||||
var chan = ios.newChannel(urlStr, null, null)
|
||||
.QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.loadGroup = loadGroup;
|
||||
chan.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI;
|
||||
|
||||
do_print("Setting loadgroup permission: " +
|
||||
(allowPrivateLoads ? "Allowing" : "Forbidding") +
|
||||
" private loads for doc load " + urlStr + ".");
|
||||
loadGroup.allowLoadsFromPrivateNetworks = allowPrivateLoads;
|
||||
|
||||
// Try to load channel with IP literal.
|
||||
var listener = new Listener(expectSuccessfulResponse, loadGroupAllowsAfter);
|
||||
chan.asyncOpen(listener, null);
|
||||
}
|
||||
|
||||
function prime_cache_entry(uri, isPrivate, networkID, onEntryPrimed) {
|
||||
do_print("Priming cache with a " + (isPrivate ? "private" : "public") +
|
||||
" entry.");
|
||||
|
||||
var fakeResponseHead = "HTTP/1.1 200 OK\r\n" +
|
||||
"Content-Type: text/plain\r\n" +
|
||||
"Server: httpd.js\r\n" +
|
||||
"Date: " + (new Date()).toString() + "\r\n";
|
||||
|
||||
asyncOpenCacheEntry(uri, "disk", Ci.nsICacheStorage.OPEN_TRUNCATE, null,
|
||||
new OpenCallback(NEW, "a1m", "a1d", function(entry) {
|
||||
do_print("Created " + (isPrivate ? "private" : "public") + " entry");
|
||||
entry.setMetaDataElement("request-method", "GET");
|
||||
entry.setMetaDataElement("response-head", fakeResponseHead);
|
||||
if (isPrivate) {
|
||||
entry.setMetaDataElement("loaded-from-private-network", networkID);
|
||||
}
|
||||
do_print("Added metadata");
|
||||
asyncOpenCacheEntry(uri, "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
|
||||
new OpenCallback(NORMAL, "a1m", "a1d", function(entry) {
|
||||
do_print("Verifying " + (isPrivate ? "private" : "public") +
|
||||
" entry created");
|
||||
if (isPrivate) {
|
||||
do_check_eq(entry.getMetaDataElement("loaded-from-private-network"),
|
||||
networkID);
|
||||
}
|
||||
|
||||
do_print((isPrivate ? "Private" : "Public") + " cache entry primed.");
|
||||
onEntryPrimed();
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Create a private cache entry; if private loads are allowed, the entry will
|
||||
// be accepted. If not, the entry will be rejected, but since the server is
|
||||
// localhost the network load should also be rejected.
|
||||
function test_private_cached_entry_same_network(privateLoadAllowed) {
|
||||
var uri = uriBase + "/failme/";
|
||||
prime_cache_entry(uri, true, ios.networkLinkID, function() {
|
||||
test_single_loadGroup(privateLoadAllowed, privateLoadAllowed, uri);
|
||||
});
|
||||
}
|
||||
|
||||
// Create a private cache entry; if private loads are allowed, the entry will
|
||||
// be accepted. If not, the entry will be rejected, but since the server is
|
||||
// localhost the network load should also be rejected.
|
||||
// LoadGroup permissions should not change after response.
|
||||
function test_private_cached_entry_same_network_doc_load(privateLoadAllowed) {
|
||||
var uri = uriBase + "/failme/";
|
||||
prime_cache_entry(uri, true, ios.networkLinkID, function() {
|
||||
test_single_loadGroup_doc_load(privateLoadAllowed,
|
||||
privateLoadAllowed,
|
||||
privateLoadAllowed,
|
||||
uri);
|
||||
});
|
||||
}
|
||||
|
||||
// UUID to fake a load on a different private network.
|
||||
var fakeNetworkID = "{86437A10-658B-4637-8D41-9B3693F72762}";
|
||||
|
||||
// Ensure that privately cached entries from different networks than the current
|
||||
// one are not loaded.
|
||||
function test_private_cached_entry_diff_network(privateLoadAllowed) {
|
||||
// We should bypass the cache entry since it was created on a different
|
||||
// network. As such, use /passme for private loads allowed, and /failme for
|
||||
// private loads forbidden.
|
||||
var uri = uriBase + (privateLoadAllowed ? "/passme/" : "/failme/");
|
||||
prime_cache_entry(uri, true, fakeNetworkID, function() {
|
||||
test_single_loadGroup(privateLoadAllowed, privateLoadAllowed, uri);
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure that privately cached entries from different networks than the current
|
||||
// one are not loaded; doc load.
|
||||
function test_private_cached_entry_diff_network_doc_load(privateLoadAllowed) {
|
||||
// We should bypass the cache entry since it was created on a different
|
||||
// network. As such, use /passme for private loads allowed, and /failme for
|
||||
// private loads forbidden.
|
||||
// LoadGroup permissions should not change after response.
|
||||
var uri = uriBase + (privateLoadAllowed ? "/passme/" : "/failme/");
|
||||
prime_cache_entry(uri, true, fakeNetworkID, function() {
|
||||
test_single_loadGroup_doc_load(privateLoadAllowed,
|
||||
privateLoadAllowed,
|
||||
privateLoadAllowed,
|
||||
uri);
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure that publicly cached entries are always loaded.
|
||||
function test_public_cached_entry(privateCacheEntryAllowed) {
|
||||
var uri = uriBase + "/failme/";
|
||||
prime_cache_entry(uri, false, null, function() {
|
||||
test_single_loadGroup(privateCacheEntryAllowed, true, uri);
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure that publicly cached entries are always loaded; doc loads.
|
||||
function test_public_cached_entry_doc_load(privateCacheEntryAllowed) {
|
||||
// Create a public cache entry for /failme; the entry should be accepted and
|
||||
// we should not go to the network.
|
||||
// LoadGroup permissions should forbid private loads after response.
|
||||
var uri = uriBase + "/failme/";
|
||||
prime_cache_entry(uri, false, null, function() {
|
||||
test_single_loadGroup_doc_load(privateCacheEntryAllowed, true, false, uri);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Initialization
|
||||
//
|
||||
|
||||
// Setup HTTP server to handle for http://localhost/passme/ and
|
||||
// http://localhost/failme/. Requests received by /failme/ will cause the test
|
||||
// script to fail. Thus, it is used to verify that specific request do not go
|
||||
// to the network.
|
||||
function setup_http_server() {
|
||||
httpServ = new HttpServer();
|
||||
|
||||
httpServ.registerPathHandler("/passme/", function (metadata, response) {
|
||||
do_print("Received request on http://localhost/passme/");
|
||||
var httpbody = "0123456789";
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.bodyOutputStream.write(httpbody, httpbody.length);
|
||||
});
|
||||
|
||||
httpServ.registerPathHandler("/failme/", function (metadata, response) {
|
||||
do_throw("http://localhost/failme/ should not not receive requests!");
|
||||
});
|
||||
httpServ.start(-1);
|
||||
|
||||
do_register_cleanup(function() {
|
||||
httpServ.stop(function() {});
|
||||
});
|
||||
|
||||
do_print("Started HTTP Server on " +
|
||||
httpServ.identity.primaryScheme + "://" +
|
||||
httpServ.identity.primaryHost + ":" +
|
||||
httpServ.identity.primaryPort);
|
||||
}
|
||||
|
||||
function setup_and_add_tests() {
|
||||
// Get profile for disk caching.
|
||||
do_get_profile();
|
||||
|
||||
// Run tests with NetworkZonePolicy enabled; set pref back to default after
|
||||
// tests complete.
|
||||
{
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefBranch);
|
||||
if (!prefs.getBoolPref("network.zonepolicy.enabled")) {
|
||||
prefs.setBoolPref("network.zonepolicy.enabled", true);
|
||||
do_register_cleanup(function() {
|
||||
prefs.setBoolPref("network.zonepolicy.enabled", false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
uriBase = httpServ.identity.primaryScheme + "://" +
|
||||
httpServ.identity.primaryHost + ":" +
|
||||
httpServ.identity.primaryPort;
|
||||
|
||||
var tests = [
|
||||
// Basic pref test.
|
||||
test_basic_NetworkZonePolicy_pref,
|
||||
|
||||
// Verify NetworkZonePolicy can manage loadgroup permisssions.
|
||||
test_basic_NetworkZonePolicy_and_loadGroup,
|
||||
test_basic_NetworkZonePolicy_loadGroup_and_parent,
|
||||
test_basic_NetworkZonePolicy_loadGroup_and_owner,
|
||||
test_basic_NetworkZonePolicy_loadGroup_and_docshell,
|
||||
test_loadGroup_immediate_ancestors,
|
||||
|
||||
// Private (localhost) network requests.
|
||||
function test_network_private_allowed() {
|
||||
test_single_loadGroup(true, true, uriBase + "/passme/"); },
|
||||
function test_network_private_forbidden() {
|
||||
test_single_loadGroup(false, false, uriBase + "/failme/"); },
|
||||
|
||||
// Private (localhost) network requests for document loads.
|
||||
function test_network_private_allowed_doc_load() {
|
||||
test_single_loadGroup_doc_load(true, true, true, uriBase + "/passme/"); },
|
||||
function test_network_private_forbidden_doc_load() {
|
||||
test_single_loadGroup_doc_load(false, false, false, uriBase + "/failme/"); },
|
||||
|
||||
// Private cache entries; same network.
|
||||
function test_private_cache_same_network_private_allowed() {
|
||||
test_private_cached_entry_same_network(true); },
|
||||
function test_private_cache_same_network_private_forbidden() {
|
||||
test_private_cached_entry_same_network(false); },
|
||||
|
||||
// Private cache entries; same network; doc load.
|
||||
function test_private_cache_same_network_private_allowed_doc_load() {
|
||||
test_private_cached_entry_same_network_doc_load(true); },
|
||||
function test_private_cache_same_network_private_forbidden_doc_load() {
|
||||
test_private_cached_entry_same_network_doc_load(false); },
|
||||
|
||||
// Private cache entries, different network.
|
||||
function test_private_cache_diff_network_private_allowed() {
|
||||
test_private_cached_entry_diff_network(true); },
|
||||
function test_private_cache_diff_network_private_forbidden() {
|
||||
test_private_cached_entry_diff_network(false); },
|
||||
|
||||
// Private cache entries, different network; doc load.
|
||||
function test_private_cache_diff_network_private_allowed_doc_load() {
|
||||
test_private_cached_entry_diff_network_doc_load(true); },
|
||||
function test_private_cache_diff_network_private_forbidden_doc_load() {
|
||||
test_private_cached_entry_diff_network_doc_load(false); },
|
||||
|
||||
// Public cache entries.
|
||||
function test_public_cache_private_allowed() {
|
||||
test_public_cached_entry(true); },
|
||||
function test_public_cache_private_forbidden() {
|
||||
test_public_cached_entry(false); },
|
||||
|
||||
// Public cache entries for document loads.
|
||||
function test_public_cache_private_allowed_doc_load() {
|
||||
test_public_cached_entry_doc_load(true); },
|
||||
function test_public_cache_private_forbidden_doc_load() {
|
||||
test_public_cached_entry_doc_load(false); }
|
||||
];
|
||||
|
||||
tests.forEach(function(test) {
|
||||
add_test(test);
|
||||
});
|
||||
|
||||
do_print("Added tests.");
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
setup_http_server();
|
||||
|
||||
setup_and_add_tests();
|
||||
|
||||
run_next_test();
|
||||
}
|
@ -119,7 +119,9 @@ TestOutputStreamCallback.prototype = {
|
||||
}
|
||||
} else {
|
||||
// A refusal to connect speculatively should throw an error.
|
||||
do_check_eq(e.result, Cr.NS_ERROR_CONNECTION_REFUSED);
|
||||
do_check_true(e.result == Cr.NS_ERROR_UNKNOWN_HOST ||
|
||||
e.result == Cr.NS_ERROR_UNKNOWN_PROXY_HOST ||
|
||||
e.result == Cr.NS_ERROR_CONNECTION_REFUSED);
|
||||
}
|
||||
this.transport.close(Cr.NS_BINDING_ABORTED);
|
||||
this.next();
|
||||
@ -321,6 +323,18 @@ function next_test() {
|
||||
* Main entry function for test execution.
|
||||
*/
|
||||
function run_test() {
|
||||
// Enable speculative connects on loopback for testing.
|
||||
{
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefBranch);
|
||||
if (!prefs.getBoolPref("network.http.speculative.allowLoopback")) {
|
||||
prefs.setBoolPref("network.http.speculative.allowLoopback", true);
|
||||
do_register_cleanup(function() {
|
||||
prefs.setBoolPref("network.http.speculative.allowLoopback", false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ios = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
|
||||
|
@ -236,6 +236,8 @@ skip-if = os == "android"
|
||||
[test_httpauth.js]
|
||||
[test_httpcancel.js]
|
||||
[test_httpResponseTimeout.js]
|
||||
# Bug 354493: fails on Android, but feature disabled by default.
|
||||
skip-if = os == "android"
|
||||
[test_httpsuspend.js]
|
||||
[test_idnservice.js]
|
||||
[test_idn_urls.js]
|
||||
@ -251,6 +253,7 @@ skip-if = os == "android"
|
||||
[test_net_addr.js]
|
||||
# Bug 732363: test fails on windows for unknown reasons.
|
||||
skip-if = os == "win"
|
||||
[test_networkZonePolicy.js]
|
||||
[test_nojsredir.js]
|
||||
[test_offline_status.js]
|
||||
[test_parse_content_type.js]
|
||||
|
Loading…
Reference in New Issue
Block a user