|
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 } |