Bug 939318 - detect network interface changes on windows properly. 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.

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)

HttpHandler: acts on network changes
This commit is contained in:
Daniel Stenberg 2014-08-29 01:18:00 +02:00
parent 06efc6492d
commit d590129918
9 changed files with 296 additions and 122 deletions

View File

@ -10,7 +10,7 @@
/**
* Network link status monitoring service.
*/
[scriptable, uuid(f7d3be87-7403-4a1e-b89f-2797776e9b08)]
[scriptable, uuid(a8770c10-2f54-11e4-8c21-0800200c9a66)]
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.
*/

View File

@ -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
@ -265,10 +265,9 @@ nsIOService::InitializeNetworkLinkService()
// so let's cross our fingers!
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);
}
}
@ -952,11 +951,11 @@ nsIOService::Observe(nsISupports *subject,
mProxyService = nullptr;
}
else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
if (!mOfflineForProfileChange && mManageOfflineStatus) {
TrackNetworkLinkStatusForOffline();
if (!mOfflineForProfileChange) {
OnNetworkLinkEvent(NS_ConvertUTF16toUTF8(data).get());
}
}
return NS_OK;
}
@ -1054,24 +1053,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.
// to managed. Usually because there is no link monitoring service
// available. Failure to do this switch is detected by a failure of
// 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 +1084,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;
// 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
// always up for the purposes of offline management.
if (autodialEnabled) {
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
// 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())
return SetOffline(false);
// 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);
return SetOffline(false);
#endif
}
}
}
bool isUp;
nsresult rv = mNetworkLinkService->GetIsLinkUp(&isUp);
NS_ENSURE_SUCCESS(rv, rv);
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);
}

View File

@ -81,7 +81,7 @@ private:
nsIOService();
~nsIOService();
nsresult TrackNetworkLinkStatusForOffline();
nsresult OnNetworkLinkEvent(const char *data);
nsresult GetCachedProtocolHandler(const char *scheme,
nsIProtocolHandler* *hdlrResult,

View File

@ -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,10 +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,
"unexpected observe call");
strcmp(topic, "last-pb-context-exited") == 0 ||
strcmp(topic, NS_NETWORK_LINK_TOPIC) == 0,
"unexpected observe call");
if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
nsCString converted = NS_ConvertUTF16toUTF8(data);
const char *state = converted.get();
if (!strcmp(state, 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

View File

@ -478,10 +478,8 @@ nsHostResolver::ClearPendingQueue(PRCList *aPendingQ)
}
void
nsHostResolver::Shutdown()
nsHostResolver::FlushCache()
{
LOG(("Shutting down host resolver.\n"));
PRCList pendingQHigh, pendingQMed, pendingQLow, evictionQ;
PR_INIT_CLIST(&pendingQHigh);
PR_INIT_CLIST(&pendingQMed);
@ -490,23 +488,20 @@ nsHostResolver::Shutdown()
{
MutexAutoLock lock(mLock);
mShutdown = true;
MoveCList(mHighQ, pendingQHigh);
MoveCList(mMediumQ, pendingQMed);
MoveCList(mLowQ, pendingQLow);
MoveCList(mEvictionQ, evictionQ);
mEvictionQSize = 0;
mPendingCount = 0;
if (mNumIdleThreads)
mIdleThreadCV.NotifyAll();
// empty host database
PL_DHashTableEnumerate(&mDB, HostDB_RemoveEntry, nullptr);
}
ClearPendingQueue(&pendingQHigh);
ClearPendingQueue(&pendingQMed);
ClearPendingQueue(&pendingQLow);
@ -519,6 +514,18 @@ nsHostResolver::Shutdown()
NS_RELEASE(rec);
}
}
}
void
nsHostResolver::Shutdown()
{
LOG(("Shutting down host resolver.\n"));
{
MutexAutoLock lock(mLock);
mShutdown = true;
}
FlushCache();
#ifdef NS_BUILD_REFCNT_LOGGING

View File

@ -238,6 +238,11 @@ public:
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
/**
* Flush the DNS cache.
*/
void FlushCache();
private:
explicit nsHostResolver(uint32_t maxCacheEntries = 50, uint32_t maxCacheLifetime = 60,
uint32_t lifetimeGracePeriod = 0);

View File

@ -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"
@ -347,6 +348,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();
@ -1777,9 +1779,8 @@ nsHttpHandler::Observe(nsISupports *subject,
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") == 0 ||
strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
mHandlerActive = false;
@ -1799,30 +1800,25 @@ 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") == 0) {
// initialize connection manager
InitConnectionMgr();
}
else if (strcmp(topic, "net:clear-active-logins") == 0) {
} else if (strcmp(topic, "net:clear-active-logins") == 0) {
mAuthCache.ClearAll();
mPrivateAuthCache.ClearAll();
}
else if (strcmp(topic, "net:prune-dead-connections") == 0) {
} else if (strcmp(topic, "net:prune-dead-connections") == 0) {
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") == 0) {
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") == 0) {
mPrivateAuthCache.ClearAll();
} else if (strcmp(topic, "browser:purge-session-history") == 0) {
if (mConnMgr && gSocketTransportService) {
@ -1830,6 +1826,14 @@ nsHttpHandler::Observe(nsISupports *subject,
&nsHttpConnectionMgr::ClearConnectionHistory);
gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
}
} else if (strcmp(topic, NS_NETWORK_LINK_TOPIC) == 0) {
nsCString converted = NS_ConvertUTF16toUTF8(data);
const char *state = converted.get();
if (strcmp(state, NS_NETWORK_LINK_DATA_CHANGED) == 0) {
if (mConnMgr) {
mConnMgr->PruneDeadConnections();
}
}
}
return NS_OK;

View File

@ -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,
@ -60,6 +91,7 @@ nsNotifyAddrListener::nsNotifyAddrListener()
, mCheckAttempted(false)
, mShutdownEvent(nullptr)
{
InitIphlpapi();
}
nsNotifyAddrListener::~nsNotifyAddrListener()
@ -97,36 +129,62 @@ 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");
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);
HANDLE ev = CreateEvent(nullptr, FALSE, FALSE, nullptr);
NS_ENSURE_TRUE(ev, NS_ERROR_OUT_OF_MEMORY);
HANDLE handles[2] = { ev, mShutdownEvent };
OVERLAPPED overlapped = { 0 };
bool shuttingDown = false;
HANDLE handles[2] = { ev, mShutdownEvent };
OVERLAPPED overlapped = { 0 };
bool shuttingDown = false;
overlapped.hEvent = ev;
while (!shuttingDown) {
HANDLE h;
DWORD ret = NotifyAddrChange(&h, &overlapped);
overlapped.hEvent = ev;
while (!shuttingDown) {
HANDLE h;
DWORD ret = NotifyAddrChange(&h, &overlapped);
if (ret == ERROR_IO_PENDING) {
ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
if (ret == WAIT_OBJECT_0) {
CheckLinkStatus();
if (ret == ERROR_IO_PENDING) {
ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
if (ret == WAIT_OBJECT_0) {
CheckLinkStatus();
} else {
shuttingDown = true;
}
} else {
shuttingDown = true;
}
} else {
shuttingDown = true;
}
}
CloseHandle(ev);
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 +247,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 +275,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 +382,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))
linkUp = true;
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 +462,45 @@ 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)) {
// Network is online. Topology has changed. Always send CHANGED
// before UP.
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);
}
}
}

View File

@ -30,6 +30,7 @@ public:
nsNotifyAddrListener();
nsresult Init(void);
void CheckLinkStatus(void);
protected:
class ChangeEvent : public nsRunnable {
@ -48,16 +49,22 @@ 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;
};
#endif /* NSNOTIFYADDRLISTENER_H_ */