зеркало из https://github.com/mozilla/gecko-dev.git
Bug 939318 - Detect network interface changes on Windows. r=mcmanus
Now supports IPv6 as well if a new enough windows version is used. Which notification function to use is detect at run-time. Now sends CHANGED event if the online interface(s) are different in any way since it was previously checked and considered UP. CHANGED is sent before UP in case both are detected. It does not send any CHANGED events during the first 2 seconds after startup. nIOService: split up the network event receiver function from the network status init function and have the event receiver act on the incoming event. DNSservice: acts on network changes (flushes the host cache and restarts ongoing name resolves) HttpHandler: acts on network changes
This commit is contained in:
Родитель
2128623a66
Коммит
e3a15e4b5d
|
@ -10,7 +10,7 @@
|
|||
/**
|
||||
* Network link status monitoring service.
|
||||
*/
|
||||
[scriptable, uuid(f7d3be87-7403-4a1e-b89f-2797776e9b08)]
|
||||
[scriptable, uuid(103e5293-77b3-4b70-af59-6e9e4a1f994a)]
|
||||
interface nsINetworkLinkService : nsISupports
|
||||
{
|
||||
/* Link type constants */
|
||||
|
@ -64,6 +64,11 @@ interface nsINetworkLinkService : nsISupports
|
|||
* isLinkUp is now false, linkStatusKnown is true.
|
||||
*/
|
||||
#define NS_NETWORK_LINK_DATA_DOWN "down"
|
||||
/**
|
||||
* isLinkUp is still true, but the network setup is modified.
|
||||
* linkStatusKnown is true.
|
||||
*/
|
||||
#define NS_NETWORK_LINK_DATA_CHANGED "changed"
|
||||
/**
|
||||
* linkStatusKnown is now false.
|
||||
*/
|
||||
|
|
|
@ -60,8 +60,8 @@ using namespace mozilla;
|
|||
nsIOService* gIOService = nullptr;
|
||||
static bool gHasWarnedUploadChannel2;
|
||||
|
||||
// A general port blacklist. Connections to these ports will not be allowed unless
|
||||
// the protocol overrides.
|
||||
// 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
|
||||
|
@ -266,9 +266,8 @@ nsIOService::InitializeNetworkLinkService()
|
|||
mManageOfflineStatus = false;
|
||||
}
|
||||
|
||||
|
||||
if (mManageOfflineStatus)
|
||||
TrackNetworkLinkStatusForOffline();
|
||||
OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN);
|
||||
else
|
||||
SetOffline(false);
|
||||
|
||||
|
@ -922,7 +921,7 @@ nsIOService::Observe(nsISupports *subject,
|
|||
if (mOfflineForProfileChange) {
|
||||
mOfflineForProfileChange = false;
|
||||
if (!mManageOfflineStatus ||
|
||||
NS_FAILED(TrackNetworkLinkStatusForOffline())) {
|
||||
NS_FAILED(OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN))) {
|
||||
SetOffline(false);
|
||||
}
|
||||
}
|
||||
|
@ -950,10 +949,9 @@ nsIOService::Observe(nsISupports *subject,
|
|||
|
||||
// Break circular reference.
|
||||
mProxyService = nullptr;
|
||||
}
|
||||
else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
|
||||
} else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
|
||||
if (!mOfflineForProfileChange && mManageOfflineStatus) {
|
||||
TrackNetworkLinkStatusForOffline();
|
||||
OnNetworkLinkEvent(NS_ConvertUTF16toUTF8(data).get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1054,24 +1052,25 @@ nsIOService::NewSimpleNestedURI(nsIURI* aURI, nsIURI** aResult)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOService::SetManageOfflineStatus(bool aManage) {
|
||||
nsIOService::SetManageOfflineStatus(bool aManage)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// SetManageOfflineStatus must throw when we fail to go from non-managed
|
||||
// to managed. Usually because there is no link monitoring service
|
||||
// available. Failure to do this switch is detected by a failure of
|
||||
// TrackNetworkLinkStatusForOffline(). When there is no network link
|
||||
// available during call to InitializeNetworkLinkService(), application is
|
||||
// put to offline mode. And when we change mMangeOfflineStatus to false
|
||||
// on the next line we get stuck on being offline even though the link
|
||||
// becomes later available.
|
||||
// OnNetworkLinkEvent(). When there is no network link available during
|
||||
// call to InitializeNetworkLinkService(), application is put to offline
|
||||
// mode. And when we change mMangeOfflineStatus to false on the next line
|
||||
// we get stuck on being offline even though the link becomes later
|
||||
// available.
|
||||
bool wasManaged = mManageOfflineStatus;
|
||||
mManageOfflineStatus = aManage;
|
||||
|
||||
InitializeNetworkLinkService();
|
||||
|
||||
if (mManageOfflineStatus && !wasManaged) {
|
||||
rv = TrackNetworkLinkStatusForOffline();
|
||||
rv = OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN);
|
||||
if (NS_FAILED(rv))
|
||||
mManageOfflineStatus = false;
|
||||
}
|
||||
|
@ -1084,41 +1083,57 @@ nsIOService::GetManageOfflineStatus(bool* aManage) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// input argument 'data' is already UTF8'ed
|
||||
nsresult
|
||||
nsIOService::TrackNetworkLinkStatusForOffline()
|
||||
nsIOService::OnNetworkLinkEvent(const char *data)
|
||||
{
|
||||
NS_ASSERTION(mManageOfflineStatus,
|
||||
"Don't call this unless we're managing the offline status");
|
||||
if (!mNetworkLinkService)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (mShutdown)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
if (mManageOfflineStatus)
|
||||
return NS_OK;
|
||||
|
||||
if (!strcmp(data, NS_NETWORK_LINK_DATA_DOWN)) {
|
||||
// check to make sure this won't collide with Autodial
|
||||
if (mSocketTransportService) {
|
||||
bool autodialEnabled = false;
|
||||
mSocketTransportService->GetAutodialEnabled(&autodialEnabled);
|
||||
// If autodialing-on-link-down is enabled, check if the OS auto dial
|
||||
// option is set to always autodial. If so, then we are
|
||||
// If autodialing-on-link-down is enabled, check if the OS auto
|
||||
// dial option is set to always autodial. If so, then we are
|
||||
// always up for the purposes of offline management.
|
||||
if (autodialEnabled) {
|
||||
#if defined(XP_WIN)
|
||||
// On Windows, we should first check with the OS
|
||||
// to see if autodial is enabled. If it is
|
||||
// enabled then we are allowed to manage the
|
||||
// offline state.
|
||||
if(nsNativeConnectionHelper::IsAutodialEnabled())
|
||||
// On Windows, we should first check with the OS to see if
|
||||
// autodial is enabled. If it is enabled then we are allowed
|
||||
// to manage the offline state.
|
||||
if (nsNativeConnectionHelper::IsAutodialEnabled()) {
|
||||
return SetOffline(false);
|
||||
}
|
||||
#else
|
||||
return SetOffline(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isUp;
|
||||
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_CHANGED)) {
|
||||
// CHANGED events are handled by others
|
||||
return NS_OK;
|
||||
} 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 SetOffline(!isUp);
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ private:
|
|||
nsIOService();
|
||||
~nsIOService();
|
||||
|
||||
nsresult TrackNetworkLinkStatusForOffline();
|
||||
nsresult OnNetworkLinkEvent(const char *data);
|
||||
|
||||
nsresult GetCachedProtocolHandler(const char *scheme,
|
||||
nsIProtocolHandler* *hdlrResult,
|
||||
|
|
|
@ -30,12 +30,15 @@
|
|||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsNetAddr.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsINetworkLinkService.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/VisualEventTracer.h"
|
||||
#include "mozilla/net/NeckoCommon.h"
|
||||
#include "mozilla/net/ChildDNSService.h"
|
||||
#include "mozilla/net/DNSListenerProxy.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::net;
|
||||
|
@ -540,12 +543,13 @@ nsDNSService::Init()
|
|||
prefs->AddObserver("network.proxy.type", this, false);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
do_GetService("@mozilla.org/observer-service;1", &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->AddObserver(this, "last-pb-context-exited", false);
|
||||
observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
nsDNSPrefetch::Initialize(this);
|
||||
|
@ -870,11 +874,21 @@ nsDNSService::GetMyHostName(nsACString &result)
|
|||
NS_IMETHODIMP
|
||||
nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *data)
|
||||
{
|
||||
// we are only getting called if a preference has changed.
|
||||
// We are only getting called if a preference has changed or there's a
|
||||
// network link event.
|
||||
NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 ||
|
||||
strcmp(topic, "last-pb-context-exited") == 0,
|
||||
strcmp(topic, "last-pb-context-exited") == 0 ||
|
||||
strcmp(topic, NS_NETWORK_LINK_TOPIC) == 0,
|
||||
"unexpected observe call");
|
||||
|
||||
if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
|
||||
nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
|
||||
if (!strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
|
||||
mResolver->FlushCache();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// Shutdown and this function are both only called on the UI thread, so we don't
|
||||
// have to worry about mResolver being cleared out from under us.
|
||||
|
|
|
@ -289,6 +289,7 @@ nsHostRecord::nsHostRecord(const nsHostKey *key)
|
|||
, mGetTtl(false)
|
||||
#endif
|
||||
, mBlacklistedCount(0)
|
||||
, mResolveAgain(false)
|
||||
{
|
||||
host = ((char *) this) + sizeof(nsHostRecord);
|
||||
memcpy((char *) host, key->host, strlen(key->host) + 1);
|
||||
|
@ -467,6 +468,21 @@ nsHostRecord::GetPriority(uint16_t aFlags)
|
|||
return nsHostRecord::DNS_PRIORITY_LOW;
|
||||
}
|
||||
|
||||
// Returns true if the entry can be removed, or false if it was marked to get
|
||||
// refreshed.
|
||||
bool
|
||||
nsHostRecord::RemoveOrRefresh()
|
||||
{
|
||||
// This condition implies that the request has been passed to the OS
|
||||
// resolver. The resultant DNS record should be considered stale and not
|
||||
// trusted; set a flag to ensure it is called again.
|
||||
if (resolving && !onQueue) {
|
||||
mResolveAgain = true;
|
||||
return false;
|
||||
}
|
||||
return true; // can be removed now
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct nsHostDBEnt : PLDHashEntryHdr
|
||||
|
@ -580,6 +596,21 @@ HostDB_RemoveEntry(PLDHashTable *table,
|
|||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
HostDB_PruneEntry(PLDHashTable *table,
|
||||
PLDHashEntryHdr *hdr,
|
||||
uint32_t number,
|
||||
void *arg)
|
||||
{
|
||||
nsHostDBEnt* ent = static_cast<nsHostDBEnt *>(hdr);
|
||||
|
||||
// Try to remove the record, or mark it for refresh
|
||||
if (ent->rec->RemoveOrRefresh()) {
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#if TTL_AVAILABLE
|
||||
|
@ -746,6 +777,41 @@ nsHostResolver::ClearPendingQueue(PRCList *aPendingQ)
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FlushCache() is what we call when the network has changed. We must not
|
||||
// trust names that were resolved before this change. They may resolve
|
||||
// differently now.
|
||||
//
|
||||
// This function removes all existing resolved host entries from the hash.
|
||||
// Names that are in the pending queues can be left there. Entries in the
|
||||
// cache that have 'Resolve' set true but not 'onQueue' are being resolved
|
||||
// right now, so we need to mark them to get re-resolved on completion!
|
||||
|
||||
void
|
||||
nsHostResolver::FlushCache()
|
||||
{
|
||||
PRCList evictionQ;
|
||||
PR_INIT_CLIST(&evictionQ);
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
MoveCList(mEvictionQ, evictionQ);
|
||||
mEvictionQSize = 0;
|
||||
|
||||
// prune the hash from all hosts already resolved
|
||||
PL_DHashTableEnumerate(&mDB, HostDB_PruneEntry, nullptr);
|
||||
}
|
||||
|
||||
if (!PR_CLIST_IS_EMPTY(&evictionQ)) {
|
||||
PRCList *node = evictionQ.next;
|
||||
while (node != &evictionQ) {
|
||||
nsHostRecord *rec = static_cast<nsHostRecord *>(node);
|
||||
node = node->next;
|
||||
NS_RELEASE(rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHostResolver::Shutdown()
|
||||
{
|
||||
|
@ -1331,7 +1397,12 @@ nsHostResolver::PrepareRecordExpiration(nsHostRecord* rec) const
|
|||
rec->host, lifetime, grace, sDnsVariant));
|
||||
}
|
||||
|
||||
void
|
||||
//
|
||||
// OnLookupComplete() checks if the resolving should be redone and if so it
|
||||
// returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
|
||||
//
|
||||
|
||||
nsHostResolver::LookupStatus
|
||||
nsHostResolver::OnLookupComplete(nsHostRecord* rec, nsresult status, AddrInfo* result)
|
||||
{
|
||||
// get the list of pending callbacks for this lookup, and notify
|
||||
|
@ -1341,6 +1412,11 @@ nsHostResolver::OnLookupComplete(nsHostRecord* rec, nsresult status, AddrInfo* r
|
|||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
if (rec->mResolveAgain && (status != NS_ERROR_ABORT)) {
|
||||
rec->mResolveAgain = false;
|
||||
return LOOKUP_RESOLVEAGAIN;
|
||||
}
|
||||
|
||||
// grab list of callbacks to notify
|
||||
MoveCList(rec->callbacks, cbs);
|
||||
|
||||
|
@ -1419,6 +1495,8 @@ nsHostResolver::OnLookupComplete(nsHostRecord* rec, nsresult status, AddrInfo* r
|
|||
#endif
|
||||
|
||||
NS_RELEASE(rec);
|
||||
|
||||
return LOOKUP_OK;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1502,9 +1580,10 @@ nsHostResolver::ThreadFunc(void *arg)
|
|||
nsResState rs;
|
||||
#endif
|
||||
nsHostResolver *resolver = (nsHostResolver *)arg;
|
||||
nsHostRecord *rec;
|
||||
nsHostRecord *rec = nullptr;
|
||||
AddrInfo *ai = nullptr;
|
||||
while (resolver->GetHostToLookup(&rec)) {
|
||||
|
||||
while (rec || resolver->GetHostToLookup(&rec)) {
|
||||
LOG(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
|
||||
rec->host));
|
||||
|
||||
|
@ -1548,7 +1627,13 @@ nsHostResolver::ThreadFunc(void *arg)
|
|||
// OnLookupComplete may release "rec", long before we lose it.
|
||||
LOG(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
|
||||
rec->host, ai ? "success" : "failure: unknown host"));
|
||||
resolver->OnLookupComplete(rec, status, ai);
|
||||
if (LOOKUP_RESOLVEAGAIN == resolver->OnLookupComplete(rec, status, ai)) {
|
||||
// leave 'rec' assigned and loop to make a renewed host resolve
|
||||
LOG(("DNS lookup thread - Re-resolving host [%s].\n",
|
||||
rec->host));
|
||||
} else {
|
||||
rec = nullptr;
|
||||
}
|
||||
}
|
||||
NS_RELEASE(resolver);
|
||||
LOG(("DNS lookup thread - queue empty, thread finished.\n"));
|
||||
|
|
|
@ -119,6 +119,9 @@ public:
|
|||
};
|
||||
static DnsPriority GetPriority(uint16_t aFlags);
|
||||
|
||||
bool RemoveOrRefresh(); // Returns whether the host record can be removed
|
||||
// or needs to be refreshed
|
||||
|
||||
private:
|
||||
friend class nsHostResolver;
|
||||
|
||||
|
@ -141,6 +144,10 @@ private:
|
|||
// lifetime.
|
||||
uint32_t mBlacklistedCount;
|
||||
|
||||
// when the results from this resolve is returned, it is not to be
|
||||
// trusted, but instead a new resolve must be made!
|
||||
bool mResolveAgain;
|
||||
|
||||
// a list of addresses associated with this record that have been reported
|
||||
// as unusable. the list is kept as a set of strings to make it independent
|
||||
// of gencnt.
|
||||
|
@ -278,6 +285,11 @@ public:
|
|||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
/**
|
||||
* Flush the DNS cache.
|
||||
*/
|
||||
void FlushCache();
|
||||
|
||||
private:
|
||||
explicit nsHostResolver(uint32_t maxCacheEntries,
|
||||
uint32_t defaultCacheEntryLifetime,
|
||||
|
@ -287,7 +299,13 @@ private:
|
|||
nsresult Init();
|
||||
nsresult IssueLookup(nsHostRecord *);
|
||||
bool GetHostToLookup(nsHostRecord **m);
|
||||
void OnLookupComplete(nsHostRecord *, nsresult, mozilla::net::AddrInfo *);
|
||||
|
||||
enum LookupStatus {
|
||||
LOOKUP_OK,
|
||||
LOOKUP_RESOLVEAGAIN,
|
||||
};
|
||||
|
||||
LookupStatus OnLookupComplete(nsHostRecord *, nsresult, mozilla::net::AddrInfo *);
|
||||
void DeQueue(PRCList &aQ, nsHostRecord **aResult);
|
||||
void ClearPendingQueue(PRCList *aPendingQueue);
|
||||
nsresult ConditionallyCreateThread(nsHostRecord *rec);
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "SpdyZlibReporter.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIParentalControlsService.h"
|
||||
#include "nsINetworkLinkService.h"
|
||||
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
@ -348,6 +349,7 @@ nsHttpHandler::Init()
|
|||
mObserverService->AddObserver(this, "net:failed-to-process-uri-content", true);
|
||||
mObserverService->AddObserver(this, "last-pb-context-exited", true);
|
||||
mObserverService->AddObserver(this, "browser:purge-session-history", true);
|
||||
mObserverService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
|
||||
}
|
||||
|
||||
MakeNewRequestTokenBucket();
|
||||
|
@ -1780,13 +1782,12 @@ nsHttpHandler::Observe(nsISupports *subject,
|
|||
{
|
||||
LOG(("nsHttpHandler::Observe [topic=\"%s\"]\n", topic));
|
||||
|
||||
if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
|
||||
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, "profile-change-net-teardown") == 0 ||
|
||||
strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||
} else if (!strcmp(topic, "profile-change-net-teardown") ||
|
||||
!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) ) {
|
||||
|
||||
mHandlerActive = false;
|
||||
|
||||
|
@ -1806,37 +1807,39 @@ nsHttpHandler::Observe(nsISupports *subject,
|
|||
|
||||
if (!mDoNotTrackEnabled) {
|
||||
Telemetry::Accumulate(Telemetry::DNT_USAGE, DONOTTRACK_VALUE_UNSET);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Telemetry::Accumulate(Telemetry::DNT_USAGE, mDoNotTrackValue);
|
||||
}
|
||||
}
|
||||
else if (strcmp(topic, "profile-change-net-restore") == 0) {
|
||||
} else if (!strcmp(topic, "profile-change-net-restore")) {
|
||||
// initialize connection manager
|
||||
InitConnectionMgr();
|
||||
}
|
||||
else if (strcmp(topic, "net:clear-active-logins") == 0) {
|
||||
} else if (!strcmp(topic, "net:clear-active-logins")) {
|
||||
mAuthCache.ClearAll();
|
||||
mPrivateAuthCache.ClearAll();
|
||||
}
|
||||
else if (strcmp(topic, "net:prune-dead-connections") == 0) {
|
||||
} else if (!strcmp(topic, "net:prune-dead-connections")) {
|
||||
if (mConnMgr) {
|
||||
mConnMgr->PruneDeadConnections();
|
||||
}
|
||||
}
|
||||
else if (strcmp(topic, "net:failed-to-process-uri-content") == 0) {
|
||||
} else if (!strcmp(topic, "net:failed-to-process-uri-content")) {
|
||||
nsCOMPtr<nsIURI> uri = do_QueryInterface(subject);
|
||||
if (uri && mConnMgr)
|
||||
if (uri && mConnMgr) {
|
||||
mConnMgr->ReportFailedToProcess(uri);
|
||||
}
|
||||
else if (strcmp(topic, "last-pb-context-exited") == 0) {
|
||||
} else if (!strcmp(topic, "last-pb-context-exited")) {
|
||||
mPrivateAuthCache.ClearAll();
|
||||
} else if (strcmp(topic, "browser:purge-session-history") == 0) {
|
||||
} else if (!strcmp(topic, "browser:purge-session-history")) {
|
||||
if (mConnMgr && gSocketTransportService) {
|
||||
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(mConnMgr,
|
||||
&nsHttpConnectionMgr::ClearConnectionHistory);
|
||||
gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
} else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
|
||||
nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
|
||||
if (!strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
|
||||
if (mConnMgr) {
|
||||
mConnMgr->PruneDeadConnections();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
#include <ole2.h>
|
||||
#include <netcon.h>
|
||||
#include <objbase.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2ipdef.h>
|
||||
#include <tcpmib.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <netioapi.h>
|
||||
#include <iprtrmib.h>
|
||||
#include "plstr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -29,6 +34,26 @@
|
|||
static HMODULE sNetshell;
|
||||
static decltype(NcFreeNetconProperties)* sNcFreeNetconProperties;
|
||||
|
||||
static HMODULE sIphlpapi;
|
||||
static decltype(NotifyIpInterfaceChange)* sNotifyIpInterfaceChange;
|
||||
static decltype(CancelMibChangeNotify2)* sCancelMibChangeNotify2;
|
||||
|
||||
static void InitIphlpapi(void)
|
||||
{
|
||||
if (!sIphlpapi) {
|
||||
sIphlpapi = LoadLibraryW(L"Iphlpapi.dll");
|
||||
if (sIphlpapi) {
|
||||
sNotifyIpInterfaceChange = (decltype(NotifyIpInterfaceChange)*)
|
||||
GetProcAddress(sIphlpapi, "NotifyIpInterfaceChange");
|
||||
sCancelMibChangeNotify2 = (decltype(CancelMibChangeNotify2)*)
|
||||
GetProcAddress(sIphlpapi, "CancelMibChangeNotify2");
|
||||
} else {
|
||||
NS_WARNING("Failed to load Iphlpapi.dll - cannot detect network"
|
||||
" changes!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InitNetshellLibrary(void)
|
||||
{
|
||||
if (!sNetshell) {
|
||||
|
@ -47,6 +72,12 @@ static void FreeDynamicLibraries(void)
|
|||
FreeLibrary(sNetshell);
|
||||
sNetshell = nullptr;
|
||||
}
|
||||
if (sIphlpapi) {
|
||||
sNotifyIpInterfaceChange = nullptr;
|
||||
sCancelMibChangeNotify2 = nullptr;
|
||||
FreeLibrary(sIphlpapi);
|
||||
sIphlpapi = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
|
||||
|
@ -59,7 +90,9 @@ nsNotifyAddrListener::nsNotifyAddrListener()
|
|||
, mStatusKnown(false)
|
||||
, mCheckAttempted(false)
|
||||
, mShutdownEvent(nullptr)
|
||||
, mIPInterfaceChecksum(0)
|
||||
{
|
||||
InitIphlpapi();
|
||||
}
|
||||
|
||||
nsNotifyAddrListener::~nsNotifyAddrListener()
|
||||
|
@ -97,11 +130,25 @@ nsNotifyAddrListener::GetLinkType(uint32_t *aLinkType)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Static Callback function for NotifyIpInterfaceChange API.
|
||||
static void WINAPI OnInterfaceChange(PVOID callerContext,
|
||||
PMIB_IPINTERFACE_ROW row,
|
||||
MIB_NOTIFICATION_TYPE notificationType)
|
||||
{
|
||||
nsNotifyAddrListener *notify = static_cast<nsNotifyAddrListener*>(callerContext);
|
||||
notify->CheckLinkStatus();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNotifyAddrListener::Run()
|
||||
{
|
||||
PR_SetCurrentThreadName("Link Monitor");
|
||||
|
||||
mChangedTime = TimeStamp::Now();
|
||||
|
||||
if (!sNotifyIpInterfaceChange || !sCancelMibChangeNotify2) {
|
||||
// For Windows versions which are older than Vista which lack
|
||||
// NotifyIpInterfaceChange. Note this means no IPv6 support.
|
||||
HANDLE ev = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
NS_ENSURE_TRUE(ev, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
|
@ -126,7 +173,22 @@ nsNotifyAddrListener::Run()
|
|||
}
|
||||
}
|
||||
CloseHandle(ev);
|
||||
} else {
|
||||
// Windows Vista and newer versions.
|
||||
HANDLE interfacechange;
|
||||
// The callback will simply invoke CheckLinkStatus()
|
||||
DWORD ret = sNotifyIpInterfaceChange(
|
||||
AF_UNSPEC, // IPv4 and IPv6
|
||||
(PIPINTERFACE_CHANGE_CALLBACK)OnInterfaceChange,
|
||||
this, // pass to callback
|
||||
false, // no initial notification
|
||||
&interfacechange);
|
||||
|
||||
if (ret == NO_ERROR) {
|
||||
ret = WaitForSingleObject(mShutdownEvent, INFINITE);
|
||||
}
|
||||
sCancelMibChangeNotify2(interfacechange);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -189,11 +251,11 @@ nsNotifyAddrListener::Shutdown(void)
|
|||
return rv;
|
||||
}
|
||||
|
||||
/* Sends the given event to the UI thread. Assumes aEventID never goes out
|
||||
* of scope (static strings are ideal).
|
||||
/* Sends the given event. Assumes aEventID never goes out of scope (static
|
||||
* strings are ideal).
|
||||
*/
|
||||
nsresult
|
||||
nsNotifyAddrListener::SendEventToUI(const char *aEventID)
|
||||
nsNotifyAddrListener::SendEvent(const char *aEventID)
|
||||
{
|
||||
if (!aEventID)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
@ -217,8 +279,12 @@ nsNotifyAddrListener::ChangeEvent::Run()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// Bug 465158 features an explanation for this check. ICS being "Internet
|
||||
// Connection Sharing). The description says it is always IP address
|
||||
// 192.168.0.1 for this case.
|
||||
bool
|
||||
nsNotifyAddrListener::CheckIsGateway(PIP_ADAPTER_ADDRESSES aAdapter)
|
||||
nsNotifyAddrListener::CheckICSGateway(PIP_ADAPTER_ADDRESSES aAdapter)
|
||||
{
|
||||
if (!aAdapter->FirstUnicastAddress)
|
||||
return false;
|
||||
|
@ -320,38 +386,70 @@ nsNotifyAddrListener::CheckAdaptersAddresses(void)
|
|||
{
|
||||
ULONG len = 16384;
|
||||
|
||||
PIP_ADAPTER_ADDRESSES addresses = (PIP_ADAPTER_ADDRESSES) malloc(len);
|
||||
if (!addresses)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
PIP_ADAPTER_ADDRESSES adapterList = (PIP_ADAPTER_ADDRESSES) moz_xmalloc(len);
|
||||
|
||||
DWORD ret = GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, addresses, &len);
|
||||
ULONG flags = GAA_FLAG_SKIP_DNS_SERVER|GAA_FLAG_SKIP_MULTICAST|
|
||||
GAA_FLAG_SKIP_ANYCAST;
|
||||
|
||||
DWORD ret = GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, adapterList, &len);
|
||||
if (ret == ERROR_BUFFER_OVERFLOW) {
|
||||
free(addresses);
|
||||
addresses = (PIP_ADAPTER_ADDRESSES) malloc(len);
|
||||
if (!addresses)
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
ret = GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, addresses, &len);
|
||||
free(adapterList);
|
||||
adapterList = static_cast<PIP_ADAPTER_ADDRESSES> (moz_xmalloc(len));
|
||||
|
||||
ret = GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, adapterList, &len);
|
||||
}
|
||||
|
||||
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) {
|
||||
free(addresses);
|
||||
free(adapterList);
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
//
|
||||
// Since NotifyIpInterfaceChange() signals a change more often than we
|
||||
// think is a worthy change, we checksum the entire state of all interfaces
|
||||
// that are UP. If the checksum is the same as previous check, nothing
|
||||
// of interest changed!
|
||||
//
|
||||
ULONG sum = 0;
|
||||
|
||||
if (ret == ERROR_SUCCESS) {
|
||||
PIP_ADAPTER_ADDRESSES ptr;
|
||||
bool linkUp = false;
|
||||
|
||||
for (ptr = addresses; !linkUp && ptr; ptr = ptr->Next) {
|
||||
if (ptr->OperStatus == IfOperStatusUp &&
|
||||
ptr->IfType != IF_TYPE_SOFTWARE_LOOPBACK &&
|
||||
!CheckIsGateway(ptr))
|
||||
for (PIP_ADAPTER_ADDRESSES adapter = adapterList; adapter;
|
||||
adapter = adapter->Next) {
|
||||
if (adapter->OperStatus != IfOperStatusUp ||
|
||||
!adapter->FirstUnicastAddress ||
|
||||
adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK ||
|
||||
CheckICSGateway(adapter) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add chars from AdapterName to the checksum.
|
||||
for (int i = 0; adapter->AdapterName[i]; ++i) {
|
||||
sum <<= 2;
|
||||
sum += adapter->AdapterName[i];
|
||||
}
|
||||
|
||||
// Add bytes from each socket address to the checksum.
|
||||
for (PIP_ADAPTER_UNICAST_ADDRESS pip = adapter->FirstUnicastAddress;
|
||||
pip; pip = pip->Next) {
|
||||
SOCKET_ADDRESS *sockAddr = &pip->Address;
|
||||
for (int i = 0; i < sockAddr->iSockaddrLength; ++i) {
|
||||
sum += (reinterpret_cast<unsigned char *>
|
||||
(sockAddr->lpSockaddr))[i];
|
||||
}
|
||||
}
|
||||
linkUp = true;
|
||||
}
|
||||
mLinkUp = linkUp;
|
||||
mStatusKnown = true;
|
||||
}
|
||||
free(addresses);
|
||||
free(adapterList);
|
||||
|
||||
if (mLinkUp) {
|
||||
/* Store the checksum only if one or more interfaces are up */
|
||||
mIPInterfaceChecksum = sum;
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
|
@ -368,24 +466,49 @@ nsNotifyAddrListener::CheckLinkStatus(void)
|
|||
{
|
||||
DWORD ret;
|
||||
const char *event;
|
||||
bool prevLinkUp = mLinkUp;
|
||||
ULONG prevCsum = mIPInterfaceChecksum;
|
||||
|
||||
// This call is very expensive (~650 milliseconds), so we don't want to
|
||||
// call it synchronously. Instead, we just start up assuming we have a
|
||||
// network link, but we'll report that the status is unknown.
|
||||
// The CheckAdaptersAddresses call is very expensive (~650 milliseconds),
|
||||
// so we don't want to call it synchronously. Instead, we just start up
|
||||
// assuming we have a network link, but we'll report that the status is
|
||||
// unknown.
|
||||
if (NS_IsMainThread()) {
|
||||
NS_WARNING("CheckLinkStatus called on main thread! No check "
|
||||
"performed. Assuming link is up, status is unknown.");
|
||||
mLinkUp = true;
|
||||
|
||||
if (!mStatusKnown) {
|
||||
event = NS_NETWORK_LINK_DATA_UNKNOWN;
|
||||
} else if (!prevLinkUp) {
|
||||
event = NS_NETWORK_LINK_DATA_UP;
|
||||
} else {
|
||||
// Known status and it was already UP
|
||||
event = nullptr;
|
||||
}
|
||||
|
||||
if (event) {
|
||||
SendEvent(event);
|
||||
}
|
||||
} else {
|
||||
ret = CheckAdaptersAddresses();
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
mLinkUp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mStatusKnown)
|
||||
event = mLinkUp ? NS_NETWORK_LINK_DATA_UP : NS_NETWORK_LINK_DATA_DOWN;
|
||||
else
|
||||
event = NS_NETWORK_LINK_DATA_UNKNOWN;
|
||||
SendEventToUI(event);
|
||||
if (mLinkUp && (prevCsum != mIPInterfaceChecksum)) {
|
||||
TimeDuration since = TimeStamp::Now() - mChangedTime;
|
||||
|
||||
// Network is online. Topology has changed. Always send CHANGED
|
||||
// before UP - after having cooled down.
|
||||
if (since.ToMilliseconds() > 2000) {
|
||||
SendEvent(NS_NETWORK_LINK_DATA_CHANGED);
|
||||
}
|
||||
}
|
||||
if (prevLinkUp != mLinkUp) {
|
||||
// UP/DOWN status changed, send appropriate UP/DOWN event
|
||||
SendEvent(mLinkUp ?
|
||||
NS_NETWORK_LINK_DATA_UP : NS_NETWORK_LINK_DATA_DOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "nsIObserver.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
class nsNotifyAddrListener : public nsINetworkLinkService,
|
||||
public nsIRunnable,
|
||||
|
@ -30,6 +31,7 @@ public:
|
|||
nsNotifyAddrListener();
|
||||
|
||||
nsresult Init(void);
|
||||
void CheckLinkStatus(void);
|
||||
|
||||
protected:
|
||||
class ChangeEvent : public nsRunnable {
|
||||
|
@ -48,16 +50,25 @@ protected:
|
|||
bool mCheckAttempted;
|
||||
|
||||
nsresult Shutdown(void);
|
||||
nsresult SendEventToUI(const char *aEventID);
|
||||
nsresult SendEvent(const char *aEventID);
|
||||
|
||||
DWORD CheckAdaptersAddresses(void);
|
||||
bool CheckIsGateway(PIP_ADAPTER_ADDRESSES aAdapter);
|
||||
|
||||
// Checks for an Internet Connection Sharing (ICS) gateway.
|
||||
bool CheckICSGateway(PIP_ADAPTER_ADDRESSES aAdapter);
|
||||
bool CheckICSStatus(PWCHAR aAdapterName);
|
||||
void CheckLinkStatus(void);
|
||||
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
|
||||
HANDLE mShutdownEvent;
|
||||
|
||||
private:
|
||||
// This is a checksum of various meta data for all network interfaces
|
||||
// considered UP at last check.
|
||||
ULONG mIPInterfaceChecksum;
|
||||
|
||||
// time of the last sent changed event
|
||||
mozilla::TimeStamp mChangedTime;
|
||||
};
|
||||
|
||||
#endif /* NSNOTIFYADDRLISTENER_H_ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче