netwerk/system/win32/nsNotifyAddrListener.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:bb89457fa64d
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/. */
6
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"
25
26 #include <iptypes.h>
27 #include <iphlpapi.h>
28
29 static HMODULE sNetshell;
30 static decltype(NcFreeNetconProperties)* sNcFreeNetconProperties;
31
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 }
42
43 static void FreeDynamicLibraries(void)
44 {
45 if (sNetshell) {
46 sNcFreeNetconProperties = nullptr;
47 FreeLibrary(sNetshell);
48 sNetshell = nullptr;
49 }
50 }
51
52 NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
53 nsINetworkLinkService,
54 nsIRunnable,
55 nsIObserver)
56
57 nsNotifyAddrListener::nsNotifyAddrListener()
58 : mLinkUp(true) // assume true by default
59 , mStatusKnown(false)
60 , mCheckAttempted(false)
61 , mShutdownEvent(nullptr)
62 {
63 }
64
65 nsNotifyAddrListener::~nsNotifyAddrListener()
66 {
67 NS_ASSERTION(!mThread, "nsNotifyAddrListener thread shutdown failed");
68 FreeDynamicLibraries();
69 }
70
71 NS_IMETHODIMP
72 nsNotifyAddrListener::GetIsLinkUp(bool *aIsUp)
73 {
74 if (!mCheckAttempted && !mStatusKnown) {
75 mCheckAttempted = true;
76 CheckLinkStatus();
77 }
78
79 *aIsUp = mLinkUp;
80 return NS_OK;
81 }
82
83 NS_IMETHODIMP
84 nsNotifyAddrListener::GetLinkStatusKnown(bool *aIsUp)
85 {
86 *aIsUp = mStatusKnown;
87 return NS_OK;
88 }
89
90 NS_IMETHODIMP
91 nsNotifyAddrListener::GetLinkType(uint32_t *aLinkType)
92 {
93 NS_ENSURE_ARG_POINTER(aLinkType);
94
95 // XXX This function has not yet been implemented for this platform
96 *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
97 return NS_OK;
98 }
99
100 NS_IMETHODIMP
101 nsNotifyAddrListener::Run()
102 {
103 PR_SetCurrentThreadName("Link Monitor");
104
105 HANDLE ev = CreateEvent(nullptr, FALSE, FALSE, nullptr);
106 NS_ENSURE_TRUE(ev, NS_ERROR_OUT_OF_MEMORY);
107
108 HANDLE handles[2] = { ev, mShutdownEvent };
109 OVERLAPPED overlapped = { 0 };
110 bool shuttingDown = false;
111
112 overlapped.hEvent = ev;
113 while (!shuttingDown) {
114 HANDLE h;
115 DWORD ret = NotifyAddrChange(&h, &overlapped);
116
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);
129
130 return NS_OK;
131 }
132
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();
140
141 return NS_OK;
142 }
143
144 nsresult
145 nsNotifyAddrListener::Init(void)
146 {
147 nsCOMPtr<nsIObserverService> observerService =
148 mozilla::services::GetObserverService();
149 if (!observerService)
150 return NS_ERROR_FAILURE;
151
152 nsresult rv = observerService->AddObserver(this, "xpcom-shutdown-threads",
153 false);
154 NS_ENSURE_SUCCESS(rv, rv);
155
156 mShutdownEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
157 NS_ENSURE_TRUE(mShutdownEvent, NS_ERROR_OUT_OF_MEMORY);
158
159 rv = NS_NewThread(getter_AddRefs(mThread), this);
160 NS_ENSURE_SUCCESS(rv, rv);
161
162 return NS_OK;
163 }
164
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");
173
174 if (!mShutdownEvent)
175 return NS_OK;
176
177 SetEvent(mShutdownEvent);
178
179 nsresult rv = mThread->Shutdown();
180
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;
185
186 CloseHandle(mShutdownEvent);
187 mShutdownEvent = nullptr;
188
189 return rv;
190 }
191
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;
200
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 }
207
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 }
219
220 bool
221 nsNotifyAddrListener::CheckIsGateway(PIP_ADAPTER_ADDRESSES aAdapter)
222 {
223 if (!aAdapter->FirstUnicastAddress)
224 return false;
225
226 LPSOCKADDR aAddress = aAdapter->FirstUnicastAddress->Address.lpSockaddr;
227 if (!aAddress)
228 return false;
229
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);
236
237 if (isGateway)
238 isGateway = CheckICSStatus(aAdapter->FriendlyName);
239
240 return isGateway;
241 }
242
243 bool
244 nsNotifyAddrListener::CheckICSStatus(PWCHAR aAdapterName)
245 {
246 InitNetshellLibrary();
247
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;
252
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));
261
262 nsRefPtr<INetSharingPrivateConnectionCollection> privateCollection;
263 if (SUCCEEDED(hr)) {
264 hr = netSharingManager->get_EnumPrivateConnections(
265 ICSSC_DEFAULT,
266 getter_AddRefs(privateCollection));
267 }
268
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 }
279
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 }
296
297 nsRefPtr<INetConnection> connection;
298 if (SUCCEEDED(connectionVariant.punkVal->QueryInterface(
299 IID_INetConnection,
300 getter_AddRefs(connection)))) {
301 connectionVariant.punkVal->Release();
302
303 NETCON_PROPERTIES *properties;
304 if (SUCCEEDED(connection->GetProperties(&properties))) {
305 if (!wcscmp(properties->pszwName, aAdapterName))
306 isICSGatewayAdapter = true;
307
308 if (sNcFreeNetconProperties)
309 sNcFreeNetconProperties(properties);
310 }
311 }
312 }
313 }
314
315 return isICSGatewayAdapter;
316 }
317
318 DWORD
319 nsNotifyAddrListener::CheckAdaptersAddresses(void)
320 {
321 ULONG len = 16384;
322
323 PIP_ADAPTER_ADDRESSES addresses = (PIP_ADAPTER_ADDRESSES) malloc(len);
324 if (!addresses)
325 return ERROR_OUTOFMEMORY;
326
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 }
335
336 if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) {
337 free(addresses);
338 return ERROR_NOT_SUPPORTED;
339 }
340
341 if (ret == ERROR_SUCCESS) {
342 PIP_ADAPTER_ADDRESSES ptr;
343 bool linkUp = false;
344
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);
355
356 CoUninitialize();
357
358 return ret;
359 }
360
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;
371
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 }
385
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