1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set et sw=4 ts=4: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License
7 * Version 1.1 (the "License"); you may not use this file except in
8 * compliance with the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is written by Juan Lang.
18 * The Initial Developer of the Original Code is
20 * Portions created by the Initial Developer are Copyright (C) 2003,2006
21 * the Initial Developer. All Rights Reserved.
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
49 #include "nsThreadUtils.h"
50 #include "nsIObserverService.h"
51 #include "nsServiceManagerUtils.h"
52 #include "nsNotifyAddrListener.h"
58 typedef DWORD (WINAPI
*GetAdaptersAddressesFunc
)(ULONG
, DWORD
, PVOID
,
59 PIP_ADAPTER_ADDRESSES
,
61 typedef DWORD (WINAPI
*GetAdaptersInfoFunc
)(PIP_ADAPTER_INFO
, PULONG
);
62 typedef DWORD (WINAPI
*GetIfEntryFunc
)(PMIB_IFROW
);
63 typedef DWORD (WINAPI
*GetIpAddrTableFunc
)(PMIB_IPADDRTABLE
, PULONG
, BOOL
);
64 typedef DWORD (WINAPI
*NotifyAddrChangeFunc
)(PHANDLE
, LPOVERLAPPED
);
66 static HMODULE sIPHelper
;
67 static GetAdaptersAddressesFunc sGetAdaptersAddresses
;
68 static GetAdaptersInfoFunc sGetAdaptersInfo
;
69 static GetIfEntryFunc sGetIfEntry
;
70 static GetIpAddrTableFunc sGetIpAddrTable
;
71 static NotifyAddrChangeFunc sNotifyAddrChange
;
73 static void InitIPHelperLibrary(void)
78 sIPHelper
= LoadLibraryW(L
"iphlpapi.dll");
82 sGetAdaptersAddresses
= (GetAdaptersAddressesFunc
)
83 GetProcAddress(sIPHelper
, "GetAdaptersAddresses");
84 sGetAdaptersInfo
= (GetAdaptersInfoFunc
)
85 GetProcAddress(sIPHelper
, "GetAdaptersInfo");
86 sGetIfEntry
= (GetIfEntryFunc
)
87 GetProcAddress(sIPHelper
, "GetIfEntry");
88 sGetIpAddrTable
= (GetIpAddrTableFunc
)
89 GetProcAddress(sIPHelper
, "GetIpAddrTable");
90 sNotifyAddrChange
= (NotifyAddrChangeFunc
)
91 GetProcAddress(sIPHelper
, "NotifyAddrChange");
94 static void FreeIPHelperLibrary(void)
99 sGetAdaptersAddresses
= nsnull
;
100 sGetAdaptersInfo
= nsnull
;
101 sGetIfEntry
= nsnull
;
102 sGetIpAddrTable
= nsnull
;
103 sNotifyAddrChange
= nsnull
;
105 FreeLibrary(sIPHelper
);
109 NS_IMPL_THREADSAFE_ISUPPORTS3(nsNotifyAddrListener
,
110 nsINetworkLinkService
,
114 nsNotifyAddrListener::nsNotifyAddrListener()
115 : mLinkUp(PR_TRUE
) // assume true by default
116 , mStatusKnown(PR_FALSE
)
117 , mShutdownEvent(nsnull
)
119 mOSVerInfo
.dwOSVersionInfoSize
= sizeof(mOSVerInfo
);
120 GetVersionEx(&mOSVerInfo
);
121 InitIPHelperLibrary();
124 nsNotifyAddrListener::~nsNotifyAddrListener()
126 NS_ASSERTION(!mThread
, "nsNotifyAddrListener thread shutdown failed");
127 FreeIPHelperLibrary();
131 nsNotifyAddrListener::GetIsLinkUp(PRBool
*aIsUp
)
138 nsNotifyAddrListener::GetLinkStatusKnown(PRBool
*aIsUp
)
140 *aIsUp
= mStatusKnown
;
145 nsNotifyAddrListener::Run()
147 HANDLE ev
= CreateEvent(nsnull
, FALSE
, FALSE
, nsnull
);
148 NS_ENSURE_TRUE(ev
, NS_ERROR_OUT_OF_MEMORY
);
150 HANDLE handles
[2] = { ev
, mShutdownEvent
};
151 OVERLAPPED overlapped
= { 0 };
152 PRBool shuttingDown
= PR_FALSE
;
154 overlapped
.hEvent
= ev
;
155 while (!shuttingDown
) {
157 DWORD ret
= sNotifyAddrChange(&h
, &overlapped
);
159 if (ret
== ERROR_IO_PENDING
) {
160 ret
= WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
161 if (ret
== WAIT_OBJECT_0
) {
164 shuttingDown
= PR_TRUE
;
167 shuttingDown
= PR_TRUE
;
176 nsNotifyAddrListener::Observe(nsISupports
*subject
,
178 const PRUnichar
*data
)
180 if (!strcmp("xpcom-shutdown-threads", topic
))
187 nsNotifyAddrListener::Init(void)
189 // XXX this call is very expensive (~650 milliseconds), so we
190 // don't want to call it synchronously. Instead, we just
191 // start up assuming we have a network link, but we'll
192 // report that the status isn't known.
194 // CheckLinkStatus();
196 // only start a thread on Windows 2000 or later
197 if (mOSVerInfo
.dwPlatformId
!= VER_PLATFORM_WIN32_NT
||
198 mOSVerInfo
.dwMajorVersion
< 5)
202 nsCOMPtr
<nsIObserverService
> observerService
=
203 do_GetService("@mozilla.org/observer-service;1", &rv
);
204 NS_ENSURE_SUCCESS(rv
, rv
);
206 rv
= observerService
->AddObserver(this, "xpcom-shutdown-threads",
208 NS_ENSURE_SUCCESS(rv
, rv
);
210 mShutdownEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
211 NS_ENSURE_TRUE(mShutdownEvent
, NS_ERROR_OUT_OF_MEMORY
);
213 rv
= NS_NewThread(getter_AddRefs(mThread
), this);
214 NS_ENSURE_SUCCESS(rv
, rv
);
220 nsNotifyAddrListener::Shutdown(void)
222 // remove xpcom shutdown observer
223 nsCOMPtr
<nsIObserverService
> observerService
=
224 do_GetService("@mozilla.org/observer-service;1");
226 observerService
->RemoveObserver(this, "xpcom-shutdown-threads");
231 SetEvent(mShutdownEvent
);
233 nsresult rv
= mThread
->Shutdown();
235 // Have to break the cycle here, otherwise nsNotifyAddrListener holds
236 // onto the thread and the thread holds onto the nsNotifyAddrListener
240 CloseHandle(mShutdownEvent
);
241 mShutdownEvent
= NULL
;
246 /* Sends the given event to the UI thread. Assumes aEventID never goes out
247 * of scope (static strings are ideal).
250 nsNotifyAddrListener::SendEventToUI(const char *aEventID
)
253 return NS_ERROR_NULL_POINTER
;
256 nsCOMPtr
<nsIRunnable
> event
= new ChangeEvent(this, aEventID
);
257 if (NS_FAILED(rv
= NS_DispatchToMainThread(event
)))
258 NS_WARNING("Failed to dispatch ChangeEvent");
263 nsNotifyAddrListener::ChangeEvent::Run()
265 nsCOMPtr
<nsIObserverService
> observerService
=
266 do_GetService("@mozilla.org/observer-service;1");
268 observerService
->NotifyObservers(
269 mService
, NS_NETWORK_LINK_TOPIC
,
270 NS_ConvertASCIItoUTF16(mEventID
).get());
275 nsNotifyAddrListener::GetOperationalStatus(DWORD aAdapterIndex
)
277 DWORD status
= MIB_IF_OPER_STATUS_CONNECTED
;
279 // try to get operational status on WinNT--on Win98, it consistently gives
280 // me the wrong status, dagnabbit
281 if (mOSVerInfo
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
282 // If this fails, assume it's connected. Didn't find a KB, but it
283 // failed for me w/Win2K SP2, and succeeded for me w/Win2K SP3.
287 ifRow
.dwIndex
= aAdapterIndex
;
288 if (sGetIfEntry(&ifRow
) == ERROR_SUCCESS
)
289 status
= ifRow
.dwOperStatus
;
296 * Calls GetIpAddrTable to check whether a link is up. Assumes so if any
297 * adapter has a non-zero IP (v4) address. Sets mLinkUp if GetIpAddrTable
298 * succeeds, but doesn't set mStatusKnown.
299 * Returns ERROR_SUCCESS on success, and a Win32 error code otherwise.
302 nsNotifyAddrListener::CheckIPAddrTable(void)
304 if (!sGetIpAddrTable
)
305 return ERROR_CALL_NOT_IMPLEMENTED
;
308 DWORD ret
= sGetIpAddrTable(nsnull
, &size
, FALSE
);
309 if (ret
== ERROR_INSUFFICIENT_BUFFER
&& size
> 0) {
310 PMIB_IPADDRTABLE table
= (PMIB_IPADDRTABLE
) malloc(size
);
312 return ERROR_OUTOFMEMORY
;
314 ret
= sGetIpAddrTable(table
, &size
, FALSE
);
315 if (ret
== ERROR_SUCCESS
) {
316 PRBool linkUp
= PR_FALSE
;
318 for (DWORD i
= 0; !linkUp
&& i
< table
->dwNumEntries
; i
++) {
319 if (GetOperationalStatus(table
->table
[i
].dwIndex
) >=
320 MIB_IF_OPER_STATUS_CONNECTED
&&
321 table
->table
[i
].dwAddr
!= 0)
332 * Checks whether a link is up by calling GetAdaptersInfo. If any adapter's
333 * operational status is at least MIB_IF_OPER_STATUS_CONNECTED, checks:
334 * 1. If it's configured for DHCP, the link is considered up if the DHCP
335 * server is initialized.
336 * 2. If it's not configured for DHCP, the link is considered up if it has a
337 * nonzero IP address.
338 * Sets mLinkUp and mStatusKnown if GetAdaptersInfo succeeds.
339 * Returns ERROR_SUCCESS on success, and a Win32 error code otherwise. If the
340 * call is not present on the current platform, returns ERROR_NOT_SUPPORTED.
343 nsNotifyAddrListener::CheckAdaptersInfo(void)
345 if (!sGetAdaptersInfo
)
346 return ERROR_NOT_SUPPORTED
;
348 ULONG adaptersLen
= 0;
350 DWORD ret
= sGetAdaptersInfo(0, &adaptersLen
);
351 if (ret
== ERROR_BUFFER_OVERFLOW
&& adaptersLen
> 0) {
352 PIP_ADAPTER_INFO adapters
= (PIP_ADAPTER_INFO
) malloc(adaptersLen
);
353 if (sGetAdaptersInfo(adapters
, &adaptersLen
) == ERROR_SUCCESS
) {
354 PRBool linkUp
= PR_FALSE
;
355 PIP_ADAPTER_INFO ptr
;
357 for (ptr
= adapters
; ptr
&& !linkUp
; ptr
= ptr
->Next
) {
358 if (GetOperationalStatus(ptr
->Index
) >=
359 MIB_IF_OPER_STATUS_CONNECTED
) {
360 if (ptr
->DhcpEnabled
) {
361 if (PL_strcmp(ptr
->DhcpServer
.IpAddress
.String
,
362 "255.255.255.255")) {
363 // it has a DHCP server, therefore it must have
369 PIP_ADDR_STRING ipAddr
;
370 for (ipAddr
= &ptr
->IpAddressList
; ipAddr
&& !linkUp
;
371 ipAddr
= ipAddr
->Next
)
372 if (PL_strcmp(ipAddr
->IpAddress
.String
, "0.0.0.0"))
378 mStatusKnown
= PR_TRUE
;
386 nsNotifyAddrListener::CheckAdaptersAddresses(void)
388 static const DWORD flags
=
389 GAA_FLAG_SKIP_FRIENDLY_NAME
| GAA_FLAG_SKIP_ANYCAST
|
390 GAA_FLAG_SKIP_MULTICAST
| GAA_FLAG_SKIP_DNS_SERVER
;
392 if (!sGetAdaptersAddresses
)
393 return ERROR_NOT_SUPPORTED
;
397 DWORD ret
= sGetAdaptersAddresses(AF_UNSPEC
, 0, NULL
, NULL
, &len
);
398 if (ret
== ERROR_BUFFER_OVERFLOW
) {
399 PIP_ADAPTER_ADDRESSES addresses
= (PIP_ADAPTER_ADDRESSES
) malloc(len
);
401 ret
= sGetAdaptersAddresses(AF_UNSPEC
, 0, NULL
, addresses
, &len
);
402 if (ret
== ERROR_SUCCESS
) {
403 PIP_ADAPTER_ADDRESSES ptr
;
406 for (ptr
= addresses
; !linkUp
&& ptr
; ptr
= ptr
->Next
) {
407 if (ptr
->OperStatus
== IfOperStatusUp
&&
408 ptr
->IfType
!= IF_TYPE_SOFTWARE_LOOPBACK
)
421 * Checks the status of all network adapters. If one is up and has a valid IP
422 * address, sets mLinkUp to true. Sets mStatusKnown to true if the link status
426 nsNotifyAddrListener::CheckLinkStatus(void)
431 ret
= CheckAdaptersAddresses();
432 if (ret
== ERROR_NOT_SUPPORTED
)
433 ret
= CheckAdaptersInfo();
434 if (ret
== ERROR_NOT_SUPPORTED
)
435 ret
= CheckIPAddrTable();
436 if (ret
!= ERROR_SUCCESS
)
437 mLinkUp
= PR_TRUE
; // I can't tell, so assume there's a link
440 event
= mLinkUp
? NS_NETWORK_LINK_DATA_UP
: NS_NETWORK_LINK_DATA_DOWN
;
442 event
= NS_NETWORK_LINK_DATA_UNKNOWN
;
443 SendEventToUI(event
);