mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-16 11:45:31 +00:00
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:
parent
06efc6492d
commit
d590129918
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user