netwerk/system/win32/nsNotifyAddrListener.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim:set et sw=4 ts=4: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include <stdarg.h>
michael@0 8 #include <windef.h>
michael@0 9 #include <winbase.h>
michael@0 10 #include <wingdi.h>
michael@0 11 #include <winuser.h>
michael@0 12 #include <ole2.h>
michael@0 13 #include <netcon.h>
michael@0 14 #include <objbase.h>
michael@0 15 #include <iprtrmib.h>
michael@0 16 #include "plstr.h"
michael@0 17 #include "nsThreadUtils.h"
michael@0 18 #include "nsIObserverService.h"
michael@0 19 #include "nsServiceManagerUtils.h"
michael@0 20 #include "nsNotifyAddrListener.h"
michael@0 21 #include "nsString.h"
michael@0 22 #include "nsAutoPtr.h"
michael@0 23 #include "mozilla/Services.h"
michael@0 24 #include "nsCRT.h"
michael@0 25
michael@0 26 #include <iptypes.h>
michael@0 27 #include <iphlpapi.h>
michael@0 28
michael@0 29 static HMODULE sNetshell;
michael@0 30 static decltype(NcFreeNetconProperties)* sNcFreeNetconProperties;
michael@0 31
michael@0 32 static void InitNetshellLibrary(void)
michael@0 33 {
michael@0 34 if (!sNetshell) {
michael@0 35 sNetshell = LoadLibraryW(L"Netshell.dll");
michael@0 36 if (sNetshell) {
michael@0 37 sNcFreeNetconProperties = (decltype(NcFreeNetconProperties)*)
michael@0 38 GetProcAddress(sNetshell, "NcFreeNetconProperties");
michael@0 39 }
michael@0 40 }
michael@0 41 }
michael@0 42
michael@0 43 static void FreeDynamicLibraries(void)
michael@0 44 {
michael@0 45 if (sNetshell) {
michael@0 46 sNcFreeNetconProperties = nullptr;
michael@0 47 FreeLibrary(sNetshell);
michael@0 48 sNetshell = nullptr;
michael@0 49 }
michael@0 50 }
michael@0 51
michael@0 52 NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
michael@0 53 nsINetworkLinkService,
michael@0 54 nsIRunnable,
michael@0 55 nsIObserver)
michael@0 56
michael@0 57 nsNotifyAddrListener::nsNotifyAddrListener()
michael@0 58 : mLinkUp(true) // assume true by default
michael@0 59 , mStatusKnown(false)
michael@0 60 , mCheckAttempted(false)
michael@0 61 , mShutdownEvent(nullptr)
michael@0 62 {
michael@0 63 }
michael@0 64
michael@0 65 nsNotifyAddrListener::~nsNotifyAddrListener()
michael@0 66 {
michael@0 67 NS_ASSERTION(!mThread, "nsNotifyAddrListener thread shutdown failed");
michael@0 68 FreeDynamicLibraries();
michael@0 69 }
michael@0 70
michael@0 71 NS_IMETHODIMP
michael@0 72 nsNotifyAddrListener::GetIsLinkUp(bool *aIsUp)
michael@0 73 {
michael@0 74 if (!mCheckAttempted && !mStatusKnown) {
michael@0 75 mCheckAttempted = true;
michael@0 76 CheckLinkStatus();
michael@0 77 }
michael@0 78
michael@0 79 *aIsUp = mLinkUp;
michael@0 80 return NS_OK;
michael@0 81 }
michael@0 82
michael@0 83 NS_IMETHODIMP
michael@0 84 nsNotifyAddrListener::GetLinkStatusKnown(bool *aIsUp)
michael@0 85 {
michael@0 86 *aIsUp = mStatusKnown;
michael@0 87 return NS_OK;
michael@0 88 }
michael@0 89
michael@0 90 NS_IMETHODIMP
michael@0 91 nsNotifyAddrListener::GetLinkType(uint32_t *aLinkType)
michael@0 92 {
michael@0 93 NS_ENSURE_ARG_POINTER(aLinkType);
michael@0 94
michael@0 95 // XXX This function has not yet been implemented for this platform
michael@0 96 *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
michael@0 97 return NS_OK;
michael@0 98 }
michael@0 99
michael@0 100 NS_IMETHODIMP
michael@0 101 nsNotifyAddrListener::Run()
michael@0 102 {
michael@0 103 PR_SetCurrentThreadName("Link Monitor");
michael@0 104
michael@0 105 HANDLE ev = CreateEvent(nullptr, FALSE, FALSE, nullptr);
michael@0 106 NS_ENSURE_TRUE(ev, NS_ERROR_OUT_OF_MEMORY);
michael@0 107
michael@0 108 HANDLE handles[2] = { ev, mShutdownEvent };
michael@0 109 OVERLAPPED overlapped = { 0 };
michael@0 110 bool shuttingDown = false;
michael@0 111
michael@0 112 overlapped.hEvent = ev;
michael@0 113 while (!shuttingDown) {
michael@0 114 HANDLE h;
michael@0 115 DWORD ret = NotifyAddrChange(&h, &overlapped);
michael@0 116
michael@0 117 if (ret == ERROR_IO_PENDING) {
michael@0 118 ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
michael@0 119 if (ret == WAIT_OBJECT_0) {
michael@0 120 CheckLinkStatus();
michael@0 121 } else {
michael@0 122 shuttingDown = true;
michael@0 123 }
michael@0 124 } else {
michael@0 125 shuttingDown = true;
michael@0 126 }
michael@0 127 }
michael@0 128 CloseHandle(ev);
michael@0 129
michael@0 130 return NS_OK;
michael@0 131 }
michael@0 132
michael@0 133 NS_IMETHODIMP
michael@0 134 nsNotifyAddrListener::Observe(nsISupports *subject,
michael@0 135 const char *topic,
michael@0 136 const char16_t *data)
michael@0 137 {
michael@0 138 if (!strcmp("xpcom-shutdown-threads", topic))
michael@0 139 Shutdown();
michael@0 140
michael@0 141 return NS_OK;
michael@0 142 }
michael@0 143
michael@0 144 nsresult
michael@0 145 nsNotifyAddrListener::Init(void)
michael@0 146 {
michael@0 147 nsCOMPtr<nsIObserverService> observerService =
michael@0 148 mozilla::services::GetObserverService();
michael@0 149 if (!observerService)
michael@0 150 return NS_ERROR_FAILURE;
michael@0 151
michael@0 152 nsresult rv = observerService->AddObserver(this, "xpcom-shutdown-threads",
michael@0 153 false);
michael@0 154 NS_ENSURE_SUCCESS(rv, rv);
michael@0 155
michael@0 156 mShutdownEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
michael@0 157 NS_ENSURE_TRUE(mShutdownEvent, NS_ERROR_OUT_OF_MEMORY);
michael@0 158
michael@0 159 rv = NS_NewThread(getter_AddRefs(mThread), this);
michael@0 160 NS_ENSURE_SUCCESS(rv, rv);
michael@0 161
michael@0 162 return NS_OK;
michael@0 163 }
michael@0 164
michael@0 165 nsresult
michael@0 166 nsNotifyAddrListener::Shutdown(void)
michael@0 167 {
michael@0 168 // remove xpcom shutdown observer
michael@0 169 nsCOMPtr<nsIObserverService> observerService =
michael@0 170 mozilla::services::GetObserverService();
michael@0 171 if (observerService)
michael@0 172 observerService->RemoveObserver(this, "xpcom-shutdown-threads");
michael@0 173
michael@0 174 if (!mShutdownEvent)
michael@0 175 return NS_OK;
michael@0 176
michael@0 177 SetEvent(mShutdownEvent);
michael@0 178
michael@0 179 nsresult rv = mThread->Shutdown();
michael@0 180
michael@0 181 // Have to break the cycle here, otherwise nsNotifyAddrListener holds
michael@0 182 // onto the thread and the thread holds onto the nsNotifyAddrListener
michael@0 183 // via its mRunnable
michael@0 184 mThread = nullptr;
michael@0 185
michael@0 186 CloseHandle(mShutdownEvent);
michael@0 187 mShutdownEvent = nullptr;
michael@0 188
michael@0 189 return rv;
michael@0 190 }
michael@0 191
michael@0 192 /* Sends the given event to the UI thread. Assumes aEventID never goes out
michael@0 193 * of scope (static strings are ideal).
michael@0 194 */
michael@0 195 nsresult
michael@0 196 nsNotifyAddrListener::SendEventToUI(const char *aEventID)
michael@0 197 {
michael@0 198 if (!aEventID)
michael@0 199 return NS_ERROR_NULL_POINTER;
michael@0 200
michael@0 201 nsresult rv;
michael@0 202 nsCOMPtr<nsIRunnable> event = new ChangeEvent(this, aEventID);
michael@0 203 if (NS_FAILED(rv = NS_DispatchToMainThread(event)))
michael@0 204 NS_WARNING("Failed to dispatch ChangeEvent");
michael@0 205 return rv;
michael@0 206 }
michael@0 207
michael@0 208 NS_IMETHODIMP
michael@0 209 nsNotifyAddrListener::ChangeEvent::Run()
michael@0 210 {
michael@0 211 nsCOMPtr<nsIObserverService> observerService =
michael@0 212 mozilla::services::GetObserverService();
michael@0 213 if (observerService)
michael@0 214 observerService->NotifyObservers(
michael@0 215 mService, NS_NETWORK_LINK_TOPIC,
michael@0 216 NS_ConvertASCIItoUTF16(mEventID).get());
michael@0 217 return NS_OK;
michael@0 218 }
michael@0 219
michael@0 220 bool
michael@0 221 nsNotifyAddrListener::CheckIsGateway(PIP_ADAPTER_ADDRESSES aAdapter)
michael@0 222 {
michael@0 223 if (!aAdapter->FirstUnicastAddress)
michael@0 224 return false;
michael@0 225
michael@0 226 LPSOCKADDR aAddress = aAdapter->FirstUnicastAddress->Address.lpSockaddr;
michael@0 227 if (!aAddress)
michael@0 228 return false;
michael@0 229
michael@0 230 PSOCKADDR_IN in_addr = (PSOCKADDR_IN)aAddress;
michael@0 231 bool isGateway = (aAddress->sa_family == AF_INET &&
michael@0 232 in_addr->sin_addr.S_un.S_un_b.s_b1 == 192 &&
michael@0 233 in_addr->sin_addr.S_un.S_un_b.s_b2 == 168 &&
michael@0 234 in_addr->sin_addr.S_un.S_un_b.s_b3 == 0 &&
michael@0 235 in_addr->sin_addr.S_un.S_un_b.s_b4 == 1);
michael@0 236
michael@0 237 if (isGateway)
michael@0 238 isGateway = CheckICSStatus(aAdapter->FriendlyName);
michael@0 239
michael@0 240 return isGateway;
michael@0 241 }
michael@0 242
michael@0 243 bool
michael@0 244 nsNotifyAddrListener::CheckICSStatus(PWCHAR aAdapterName)
michael@0 245 {
michael@0 246 InitNetshellLibrary();
michael@0 247
michael@0 248 // This method enumerates all privately shared connections and checks if some
michael@0 249 // of them has the same name as the one provided in aAdapterName. If such
michael@0 250 // connection is found in the collection the adapter is used as ICS gateway
michael@0 251 bool isICSGatewayAdapter = false;
michael@0 252
michael@0 253 HRESULT hr;
michael@0 254 nsRefPtr<INetSharingManager> netSharingManager;
michael@0 255 hr = CoCreateInstance(
michael@0 256 CLSID_NetSharingManager,
michael@0 257 nullptr,
michael@0 258 CLSCTX_INPROC_SERVER,
michael@0 259 IID_INetSharingManager,
michael@0 260 getter_AddRefs(netSharingManager));
michael@0 261
michael@0 262 nsRefPtr<INetSharingPrivateConnectionCollection> privateCollection;
michael@0 263 if (SUCCEEDED(hr)) {
michael@0 264 hr = netSharingManager->get_EnumPrivateConnections(
michael@0 265 ICSSC_DEFAULT,
michael@0 266 getter_AddRefs(privateCollection));
michael@0 267 }
michael@0 268
michael@0 269 nsRefPtr<IEnumNetSharingPrivateConnection> privateEnum;
michael@0 270 if (SUCCEEDED(hr)) {
michael@0 271 nsRefPtr<IUnknown> privateEnumUnknown;
michael@0 272 hr = privateCollection->get__NewEnum(getter_AddRefs(privateEnumUnknown));
michael@0 273 if (SUCCEEDED(hr)) {
michael@0 274 hr = privateEnumUnknown->QueryInterface(
michael@0 275 IID_IEnumNetSharingPrivateConnection,
michael@0 276 getter_AddRefs(privateEnum));
michael@0 277 }
michael@0 278 }
michael@0 279
michael@0 280 if (SUCCEEDED(hr)) {
michael@0 281 ULONG fetched;
michael@0 282 VARIANT connectionVariant;
michael@0 283 while (!isICSGatewayAdapter &&
michael@0 284 SUCCEEDED(hr = privateEnum->Next(1, &connectionVariant,
michael@0 285 &fetched)) &&
michael@0 286 fetched) {
michael@0 287 if (connectionVariant.vt != VT_UNKNOWN) {
michael@0 288 // We should call VariantClear here but it needs to link
michael@0 289 // with oleaut32.lib that produces a Ts incrase about 10ms
michael@0 290 // that is undesired. As it is quit unlikely the result would
michael@0 291 // be of a different type anyway, let's pass the variant
michael@0 292 // unfreed here.
michael@0 293 NS_ERROR("Variant of unexpected type, expecting VT_UNKNOWN, we probably leak it!");
michael@0 294 continue;
michael@0 295 }
michael@0 296
michael@0 297 nsRefPtr<INetConnection> connection;
michael@0 298 if (SUCCEEDED(connectionVariant.punkVal->QueryInterface(
michael@0 299 IID_INetConnection,
michael@0 300 getter_AddRefs(connection)))) {
michael@0 301 connectionVariant.punkVal->Release();
michael@0 302
michael@0 303 NETCON_PROPERTIES *properties;
michael@0 304 if (SUCCEEDED(connection->GetProperties(&properties))) {
michael@0 305 if (!wcscmp(properties->pszwName, aAdapterName))
michael@0 306 isICSGatewayAdapter = true;
michael@0 307
michael@0 308 if (sNcFreeNetconProperties)
michael@0 309 sNcFreeNetconProperties(properties);
michael@0 310 }
michael@0 311 }
michael@0 312 }
michael@0 313 }
michael@0 314
michael@0 315 return isICSGatewayAdapter;
michael@0 316 }
michael@0 317
michael@0 318 DWORD
michael@0 319 nsNotifyAddrListener::CheckAdaptersAddresses(void)
michael@0 320 {
michael@0 321 ULONG len = 16384;
michael@0 322
michael@0 323 PIP_ADAPTER_ADDRESSES addresses = (PIP_ADAPTER_ADDRESSES) malloc(len);
michael@0 324 if (!addresses)
michael@0 325 return ERROR_OUTOFMEMORY;
michael@0 326
michael@0 327 DWORD ret = GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, addresses, &len);
michael@0 328 if (ret == ERROR_BUFFER_OVERFLOW) {
michael@0 329 free(addresses);
michael@0 330 addresses = (PIP_ADAPTER_ADDRESSES) malloc(len);
michael@0 331 if (!addresses)
michael@0 332 return ERROR_BUFFER_OVERFLOW;
michael@0 333 ret = GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, addresses, &len);
michael@0 334 }
michael@0 335
michael@0 336 if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) {
michael@0 337 free(addresses);
michael@0 338 return ERROR_NOT_SUPPORTED;
michael@0 339 }
michael@0 340
michael@0 341 if (ret == ERROR_SUCCESS) {
michael@0 342 PIP_ADAPTER_ADDRESSES ptr;
michael@0 343 bool linkUp = false;
michael@0 344
michael@0 345 for (ptr = addresses; !linkUp && ptr; ptr = ptr->Next) {
michael@0 346 if (ptr->OperStatus == IfOperStatusUp &&
michael@0 347 ptr->IfType != IF_TYPE_SOFTWARE_LOOPBACK &&
michael@0 348 !CheckIsGateway(ptr))
michael@0 349 linkUp = true;
michael@0 350 }
michael@0 351 mLinkUp = linkUp;
michael@0 352 mStatusKnown = true;
michael@0 353 }
michael@0 354 free(addresses);
michael@0 355
michael@0 356 CoUninitialize();
michael@0 357
michael@0 358 return ret;
michael@0 359 }
michael@0 360
michael@0 361 /**
michael@0 362 * Checks the status of all network adapters. If one is up and has a valid IP
michael@0 363 * address, sets mLinkUp to true. Sets mStatusKnown to true if the link status
michael@0 364 * is definitive.
michael@0 365 */
michael@0 366 void
michael@0 367 nsNotifyAddrListener::CheckLinkStatus(void)
michael@0 368 {
michael@0 369 DWORD ret;
michael@0 370 const char *event;
michael@0 371
michael@0 372 // This call is very expensive (~650 milliseconds), so we don't want to
michael@0 373 // call it synchronously. Instead, we just start up assuming we have a
michael@0 374 // network link, but we'll report that the status is unknown.
michael@0 375 if (NS_IsMainThread()) {
michael@0 376 NS_WARNING("CheckLinkStatus called on main thread! No check "
michael@0 377 "performed. Assuming link is up, status is unknown.");
michael@0 378 mLinkUp = true;
michael@0 379 } else {
michael@0 380 ret = CheckAdaptersAddresses();
michael@0 381 if (ret != ERROR_SUCCESS) {
michael@0 382 mLinkUp = true;
michael@0 383 }
michael@0 384 }
michael@0 385
michael@0 386 if (mStatusKnown)
michael@0 387 event = mLinkUp ? NS_NETWORK_LINK_DATA_UP : NS_NETWORK_LINK_DATA_DOWN;
michael@0 388 else
michael@0 389 event = NS_NETWORK_LINK_DATA_UNKNOWN;
michael@0 390 SendEventToUI(event);
michael@0 391 }

mercurial