Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set et sw=2 ts=4: */
/* 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/. */
// We define this to make our use of inet_ntoa() pass. The "proper" function
// inet_ntop() doesn't exist on Windows XP.
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <algorithm>
#include <vector>
#include <stdarg.h>
#include <windef.h>
#include <winbase.h>
#include <wingdi.h>
#include <winuser.h>
#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 <netlistmgr.h>
#include <iprtrmib.h>
#include "mozilla/Logging.h"
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsIObserverService.h"
#include "nsIWindowsRegKey.h"
#include "nsServiceManagerUtils.h"
#include "nsNetAddr.h"
#include "nsNotifyAddrListener.h"
#include "nsString.h"
#include "nsPrintfCString.h"
#include "mozilla/Services.h"
#include "nsCRT.h"
#include "nsThreadPool.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/SHA1.h"
#include "mozilla/Base64.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Telemetry.h"
#include "../LinkServiceCommon.h"
#include <iptypes.h>
#include <iphlpapi.h>
using namespace mozilla;
static LazyLogModule gNotifyAddrLog("nsNotifyAddr");
#define LOG(args) MOZ_LOG(gNotifyAddrLog, mozilla::LogLevel::Debug, args)
#define LOG_ENABLED() MOZ_LOG_TEST(gNotifyAddrLog, mozilla::LogLevel::Debug)
// period during which to absorb subsequent network change events, in
// milliseconds
static const unsigned int kNetworkChangeCoalescingPeriod = 1000;
NS_IMPL_ISUPPORTS(nsNotifyAddrListener, nsINetworkLinkService, nsIRunnable,
nsIObserver)
nsNotifyAddrListener::nsNotifyAddrListener()
: mLinkUp(true), // assume true by default
mStatusKnown(false),
mCheckAttempted(false),
mMutex("nsNotifyAddrListener::mMutex"),
mCheckEvent(nullptr),
mShutdown(false),
mPlatformDNSIndications(NONE_DETECTED),
mIPInterfaceChecksum(0),
mCoalescingActive(false) {}
nsNotifyAddrListener::~nsNotifyAddrListener() {
NS_ASSERTION(!mThread, "nsNotifyAddrListener thread shutdown failed");
}
NS_IMETHODIMP
nsNotifyAddrListener::GetIsLinkUp(bool* aIsUp) {
if (!mCheckAttempted && !mStatusKnown) {
mCheckAttempted = true;
CheckLinkStatus();
}
*aIsUp = mLinkUp;
return NS_OK;
}
NS_IMETHODIMP
nsNotifyAddrListener::GetLinkStatusKnown(bool* aIsUp) {
*aIsUp = mStatusKnown;
return NS_OK;
}
NS_IMETHODIMP
nsNotifyAddrListener::GetLinkType(uint32_t* aLinkType) {
NS_ENSURE_ARG_POINTER(aLinkType);
// XXX This function has not yet been implemented for this platform
*aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
return NS_OK;
}
NS_IMETHODIMP
nsNotifyAddrListener::GetNetworkID(nsACString& aNetworkID) {
MutexAutoLock lock(mMutex);
aNetworkID = mNetworkId;
return NS_OK;
}
NS_IMETHODIMP
nsNotifyAddrListener::GetDnsSuffixList(nsTArray<nsCString>& aDnsSuffixList) {
aDnsSuffixList.Clear();
MutexAutoLock lock(mMutex);
aDnsSuffixList.AppendElements(mDnsSuffixList);
return NS_OK;
}
NS_IMETHODIMP
nsNotifyAddrListener::GetResolvers(nsTArray<RefPtr<nsINetAddr>>& aResolvers) {
nsTArray<mozilla::net::NetAddr> addresses;
nsresult rv = GetNativeResolvers(addresses);
if (NS_FAILED(rv)) {
return rv;
}
for (const auto& addr : addresses) {
aResolvers.AppendElement(MakeRefPtr<nsNetAddr>(&addr));
}
return NS_OK;
}
NS_IMETHODIMP
nsNotifyAddrListener::GetNativeResolvers(
nsTArray<mozilla::net::NetAddr>& aResolvers) {
aResolvers.Clear();
MutexAutoLock lock(mMutex);
aResolvers.AppendElements(mDNSResolvers);
return NS_OK;
}
NS_IMETHODIMP
nsNotifyAddrListener::GetPlatformDNSIndications(
uint32_t* aPlatformDNSIndications) {
*aPlatformDNSIndications = mPlatformDNSIndications;
return NS_OK;
}
//
// Hash the sorted network ids
//
void nsNotifyAddrListener::HashSortedNetworkIds(std::vector<GUID> nwGUIDS,
SHA1Sum& sha1) {
std::sort(nwGUIDS.begin(), nwGUIDS.end(), [](const GUID& a, const GUID& b) {
return memcmp(&a, &b, sizeof(GUID)) < 0;
});
for (auto const& nwGUID : nwGUIDS) {
sha1.update(&nwGUID, sizeof(GUID));
if (LOG_ENABLED()) {
nsPrintfCString guid("%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X",
nwGUID.Data1, nwGUID.Data2, nwGUID.Data3,
nwGUID.Data4[0], nwGUID.Data4[1], nwGUID.Data4[2],
nwGUID.Data4[3], nwGUID.Data4[4], nwGUID.Data4[5],
nwGUID.Data4[6], nwGUID.Data4[7]);
LOG(("calculateNetworkId: interface networkID: %s\n", guid.get()));
}
}
}
//
// Figure out the current "network identification" string.
//
void nsNotifyAddrListener::calculateNetworkId(void) {
MOZ_ASSERT(!NS_IsMainThread(), "Must not be called on the main thread");
// No need to recompute the networkId if we're shutting down.
if (mShutdown) {
return;
}
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) {
return;
}
auto unitialize = MakeScopeExit([]() { CoUninitialize(); });
RefPtr<INetworkListManager> nlm;
HRESULT hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_ALL,
IID_INetworkListManager, getter_AddRefs(nlm));
if (NS_WARN_IF(FAILED(hr))) {
LOG(("CoCreateInstance error: %lX", hr));
return;
}
RefPtr<IEnumNetworks> enumNetworks;
hr = nlm->GetNetworks(NLM_ENUM_NETWORK_CONNECTED,
getter_AddRefs(enumNetworks));
if (NS_WARN_IF(FAILED(hr))) {
LOG(("GetNetworks error: %lX", hr));
return;
}
// We will hash the found network ids
// for privacy reasons
SHA1Sum sha1;
// The networks stored in enumNetworks
// are not ordered. We will sort them
// To keep a consistent hash
// regardless of the found networks order.
std::vector<GUID> nwGUIDS;
// Consume the found networks iterator
while (true) {
RefPtr<INetwork> network;
hr = enumNetworks->Next(1, getter_AddRefs(network), nullptr);
if (hr != S_OK) {
break;
}
GUID nwGUID;
hr = network->GetNetworkId(&nwGUID);
if (hr != S_OK) {
continue;
}
nwGUIDS.push_back(nwGUID);
}
if (nwGUIDS.empty()) {
bool idChanged = false;
{
MutexAutoLock lock(mMutex);
if (!mNetworkId.IsEmpty()) {
idChanged = true;
}
mNetworkId.Truncate();
}
LOG(("calculateNetworkId: no network ID - no active networks"));
Telemetry::Accumulate(Telemetry::NETWORK_ID2, 0);
if (idChanged) {
NotifyObservers(NS_NETWORK_ID_CHANGED_TOPIC, nullptr);
}
return;
}
nsAutoCString output;
SHA1Sum::Hash digest;
HashSortedNetworkIds(nwGUIDS, sha1);
SeedNetworkId(sha1);
sha1.finish(digest);
nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
nsresult rv = Base64Encode(newString, output);
if (NS_FAILED(rv)) {
{
MutexAutoLock lock(mMutex);
mNetworkId.Truncate();
}
Telemetry::Accumulate(Telemetry::NETWORK_ID2, 0);
LOG(("calculateNetworkId: no network ID Base64Encode error %X",
uint32_t(rv)));
return;
}
MutexAutoLock lock(mMutex);
if (output != mNetworkId) {
mNetworkId = output;
Telemetry::Accumulate(Telemetry::NETWORK_ID2, 1);
LOG(("calculateNetworkId: new NetworkID: %s", output.get()));
NotifyObservers(NS_NETWORK_ID_CHANGED_TOPIC, nullptr);
} else {
Telemetry::Accumulate(Telemetry::NETWORK_ID2, 2);
LOG(("calculateNetworkId: same NetworkID: %s", output.get()));
}
}
// 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();
}
DWORD
nsNotifyAddrListener::nextCoalesceWaitTime() {
// check if coalescing period should continue
double period = (TimeStamp::Now() - mChangeTime).ToMilliseconds();
if (period >= kNetworkChangeCoalescingPeriod) {
NotifyObservers(NS_NETWORK_LINK_TOPIC, NS_NETWORK_LINK_DATA_CHANGED);
mCoalescingActive = false;
return INFINITE; // return default
} else {
// wait no longer than to the end of the period
return static_cast<DWORD>(kNetworkChangeCoalescingPeriod - period);
}
}
NS_IMETHODIMP
nsNotifyAddrListener::Run() {
mStartTime = TimeStamp::Now();
calculateNetworkId();
DWORD waitTime = INFINITE;
// Windows Vista and newer versions.
HANDLE interfacechange;
// The callback will simply invoke CheckLinkStatus()
DWORD ret = NotifyIpInterfaceChange(
StaticPrefs::network_notify_IPv6() ? AF_UNSPEC
: AF_INET, // IPv4 and IPv6
(PIPINTERFACE_CHANGE_CALLBACK)OnInterfaceChange,
this, // pass to callback
StaticPrefs::network_notify_initial_call(), // initial notification
&interfacechange);
if (ret == NO_ERROR) {
do {
ret = WaitForSingleObject(mCheckEvent, waitTime);
if (!mShutdown) {
waitTime = nextCoalesceWaitTime();
} else {
break;
}
} while (ret != WAIT_FAILED);
CancelMibChangeNotify2(interfacechange);
} else {
LOG(("Link Monitor: NotifyIpInterfaceChange returned %d\n", (int)ret));
}
return NS_OK;
}
NS_IMETHODIMP
nsNotifyAddrListener::Observe(nsISupports* subject, const char* topic,
const char16_t* data) {
if (!strcmp("xpcom-shutdown-threads", topic)) Shutdown();
return NS_OK;
}
nsresult nsNotifyAddrListener::Init(void) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService) return NS_ERROR_FAILURE;
nsresult rv =
observerService->AddObserver(this, "xpcom-shutdown-threads", false);
NS_ENSURE_SUCCESS(rv, rv);
mCheckEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
NS_ENSURE_TRUE(mCheckEvent, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(1));
MOZ_ALWAYS_SUCCEEDS(
threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize));
MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("Link Monitor"_ns));
mThread = threadPool.forget();
return mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
nsresult nsNotifyAddrListener::Shutdown(void) {
// remove xpcom shutdown observer
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService)
observerService->RemoveObserver(this, "xpcom-shutdown-threads");
if (!mCheckEvent) return NS_OK;
mShutdown = true;
SetEvent(mCheckEvent);
nsresult rv = mThread ? mThread->ShutdownWithTimeout(2000) : NS_OK;
// Have to break the cycle here, otherwise nsNotifyAddrListener holds
// onto the thread and the thread holds onto the nsNotifyAddrListener
// via its mRunnable
mThread = nullptr;
CloseHandle(mCheckEvent);
mCheckEvent = nullptr;
return rv;
}
/*
* A network event has been registered. Delay the actual sending of the event
* for a while and absorb subsequent events in the mean time in an effort to
* squash potentially many triggers into a single event.
* Only ever called from the same thread.
*/
nsresult nsNotifyAddrListener::NetworkChanged() {
if (mCoalescingActive) {
LOG(("NetworkChanged: absorbed an event (coalescing active)\n"));
} else {
// A fresh trigger!
mChangeTime = TimeStamp::Now();
mCoalescingActive = true;
SetEvent(mCheckEvent);
LOG(("NetworkChanged: coalescing period started\n"));
}
return NS_OK;
}
/* Sends the given event. Assumes aTopic/aData never goes out of scope (static
* strings are ideal).
*/
nsresult nsNotifyAddrListener::NotifyObservers(const char* aTopic,
const char* aData) {
LOG(("NotifyObservers: %s=%s\n", aTopic, aData));
if (mShutdown) {
LOG(("NotifyObservers call failed when called during shutdown"));
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
auto runnable = [self = RefPtr<nsNotifyAddrListener>(this), aTopic, aData] {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService)
observerService->NotifyObservers(
static_cast<nsINetworkLinkService*>(self.get()), aTopic,
!aData ? nullptr : NS_ConvertASCIItoUTF16(aData).get());
};
nsresult rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
"nsNotifyAddrListener::NotifyObservers", runnable));
if (NS_FAILED(rv)) {
NS_WARNING(
"nsNotifyAddrListener::NotifyObservers Failed to dispatch observer "
"notification");
}
return rv;
}
DWORD
nsNotifyAddrListener::CheckAdaptersAddresses(void) {
MOZ_ASSERT(!NS_IsMainThread(), "Don't call this on the main thread");
ULONG len = 16384;
PIP_ADAPTER_ADDRESSES adapterList = (PIP_ADAPTER_ADDRESSES)moz_xmalloc(len);
ULONG flags = GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST;
if (!StaticPrefs::network_notify_resolvers()) {
flags |= GAA_FLAG_SKIP_DNS_SERVER;
}
DWORD ret =
GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, adapterList, &len);
if (ret == ERROR_BUFFER_OVERFLOW) {
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(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 sumAll = 0;
nsTArray<nsCString> dnsSuffixList;
nsTArray<mozilla::net::NetAddr> resolvers;
uint32_t platformDNSIndications = NONE_DETECTED;
if (ret == ERROR_SUCCESS) {
bool linkUp = false;
ULONG sum = 0;
for (PIP_ADAPTER_ADDRESSES adapter = adapterList; adapter;
adapter = adapter->Next) {
if (adapter->OperStatus != IfOperStatusUp ||
!adapter->FirstUnicastAddress ||
adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) {
continue;
}
LOG(("Adapter %s type: %lu",
NS_ConvertUTF16toUTF8(adapter->FriendlyName).get(),
adapter->IfType));
if (adapter->IfType == IF_TYPE_PPP ||
adapter->IfType == IF_TYPE_PROP_VIRTUAL ||
nsDependentString(adapter->FriendlyName).Find(u"VPN") != kNotFound ||
nsDependentString(adapter->Description).Find(u"VPN") != kNotFound) {
LOG(("VPN connection found"));
platformDNSIndications |= VPN_DETECTED;
}
sum <<= 2;
// Add chars from AdapterName to the checksum.
for (int i = 0; adapter->AdapterName[i]; ++i) {
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];
}
}
for (IP_ADAPTER_DNS_SERVER_ADDRESS* pDnServer =
adapter->FirstDnsServerAddress;
pDnServer; pDnServer = pDnServer->Next) {
mozilla::net::NetAddr addr;
if (pDnServer->Address.lpSockaddr->sa_family == AF_INET) {
const struct sockaddr_in* sin =
(const struct sockaddr_in*)pDnServer->Address.lpSockaddr;
addr.inet.family = AF_INET;
addr.inet.ip = sin->sin_addr.s_addr;
addr.inet.port = sin->sin_port;
} else if (pDnServer->Address.lpSockaddr->sa_family == AF_INET6) {
const struct sockaddr_in6* sin6 =
(const struct sockaddr_in6*)pDnServer->Address.lpSockaddr;
addr.inet6.family = AF_INET6;
memcpy(&addr.inet6.ip.u8, &sin6->sin6_addr, sizeof(addr.inet6.ip.u8));
addr.inet6.port = sin6->sin6_port;
} else {
NS_WARNING("Unexpected addr type");
continue;
}
if (LOG_ENABLED()) {
char buf[100];
addr.ToStringBuffer(buf, 100);
LOG(("Found DNS resolver=%s", buf));
}
resolvers.AppendElement(addr);
}
if (StaticPrefs::network_notify_dnsSuffixList()) {
nsCString suffix = NS_ConvertUTF16toUTF8(adapter->DnsSuffix);
if (!suffix.IsEmpty()) {
LOG((" found DNS suffix=%s\n", suffix.get()));
dnsSuffixList.AppendElement(suffix);
}
}
linkUp = true;
sumAll ^= sum;
}
mLinkUp = linkUp;
mStatusKnown = true;
}
free(adapterList);
if (mLinkUp) {
/* Store the checksum only if one or more interfaces are up */
mIPInterfaceChecksum = sumAll;
}
CoUninitialize();
if (StaticPrefs::network_notify_dnsSuffixList()) {
// It seems that the only way to retrieve non-connection specific DNS
// suffixes is via the Windows registry.
// This function takes a registry path. If aRegPath\\SearchList is
// found and successfully parsed, then it returns true. Otherwise it
// returns false.
auto checkRegistry = [&dnsSuffixList](const nsAString& aRegPath) -> bool {
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
if (NS_FAILED(rv)) {
LOG((" creating nsIWindowsRegKey failed\n"));
return false;
}
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, aRegPath,
nsIWindowsRegKey::ACCESS_READ);
if (NS_FAILED(rv)) {
LOG((" opening registry key failed\n"));
return false;
}
nsAutoString wideSuffixString;
rv = regKey->ReadStringValue(u"SearchList"_ns, wideSuffixString);
if (NS_FAILED(rv)) {
LOG((" reading registry string value failed\n"));
return false;
}
// Normally the key should not contain whitespace, but editing the
// registry manually or through gpedit doesn't alway enforce this.
nsAutoCString list = NS_ConvertUTF16toUTF8(wideSuffixString);
list.StripWhitespace();
for (const nsACString& suffix : list.Split(',')) {
LOG((" appending DNS suffix from registry: %s\n",
suffix.BeginReading()));
if (!suffix.IsEmpty()) {
dnsSuffixList.AppendElement(suffix);
}
}
return true;
};
// The Local group policy overrides the user set suffix list, so we must
// first check the registry key that is sets by gpedit, and if that fails we
// fall back to the one that is set by the user.
if (!checkRegistry(nsLiteralString(
u"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient"))) {
checkRegistry(nsLiteralString(
u"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"));
}
}
auto registryChildCount = [](const nsAString& aRegPath) -> uint32_t {
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
if (NS_FAILED(rv)) {
LOG((" creating nsIWindowsRegKey failed\n"));
return 0;
}
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, aRegPath,
nsIWindowsRegKey::ACCESS_READ);
if (NS_FAILED(rv)) {
LOG((" opening registry key failed\n"));
return 0;
}
uint32_t count = 0;
rv = regKey->GetChildCount(&count);
if (NS_FAILED(rv)) {
return 0;
}
return count;
};
if (StaticPrefs::network_notify_checkForProxies()) {
if (registryChildCount(u"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\"
"Parameters\\DnsConnections"_ns) > 0 ||
registryChildCount(u"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\"
"Parameters\\DnsConnectionsProxies"_ns) > 0) {
platformDNSIndications |= PROXY_DETECTED;
}
}
if (StaticPrefs::network_notify_checkForNRPT()) {
if (registryChildCount(u"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\"
"Parameters\\DnsPolicyConfig"_ns) > 0 ||
registryChildCount(u"SOFTWARE\\Policies\\Microsoft\\Windows NT\\"
"DNSClient\\DnsPolicyConfig"_ns) > 0) {
platformDNSIndications |= NRPT_DETECTED;
}
}
{
MutexAutoLock lock(mMutex);
mDnsSuffixList = std::move(dnsSuffixList);
mDNSResolvers = std::move(resolvers);
mPlatformDNSIndications = platformDNSIndications;
}
NotifyObservers(NS_DNS_SUFFIX_LIST_UPDATED_TOPIC, nullptr);
calculateNetworkId();
return ret;
}
/**
* Checks the status of all network adapters. If one is up and has a valid IP
* address, sets mLinkUp to true. Sets mStatusKnown to true if the link status
* is definitive.
*/
void nsNotifyAddrListener::CheckLinkStatus(void) {
DWORD ret;
const char* event;
bool prevLinkUp = mLinkUp;
ULONG prevCsum = mIPInterfaceChecksum;
LOG(("check status of all network adapters\n"));
// 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) {
NotifyObservers(NS_NETWORK_LINK_TOPIC, event);
}
} else {
ret = CheckAdaptersAddresses();
if (ret != ERROR_SUCCESS) {
mLinkUp = true;
}
if (mLinkUp && (prevCsum != mIPInterfaceChecksum)) {
TimeDuration since = TimeStamp::Now() - mStartTime;
// Network is online. Topology has changed. Always send CHANGED
// before UP - if allowed to and having cooled down.
if (StaticPrefs::network_notify_changed() &&
(since.ToMilliseconds() > 2000)) {
NetworkChanged();
}
}
if (prevLinkUp != mLinkUp) {
// UP/DOWN status changed, send appropriate UP/DOWN event
NotifyObservers(NS_NETWORK_LINK_TOPIC, mLinkUp
? NS_NETWORK_LINK_DATA_UP
: NS_NETWORK_LINK_DATA_DOWN);
}
}
}