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.

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

mercurial