Source code

Revision control

Other Tools

1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "nsWifiAccessPoint.h"
6
#include "win_wifiScanner.h"
7
8
// Moz headers (alphabetical)
9
#include "win_wlanLibrary.h"
10
11
#define DOT11_BSS_TYPE_UNUSED static_cast<DOT11_BSS_TYPE>(0)
12
13
class InterfaceScanCallbackData {
14
public:
15
explicit InterfaceScanCallbackData(uint32_t numInterfaces)
16
: mCurrentlyScanningInterfaces(numInterfaces) {
17
mAllInterfacesDoneScanningEvent =
18
::CreateEvent(nullptr, // null security
19
TRUE, // manual reset event
20
FALSE, // initially nonsignaled
21
nullptr); // not named
22
MOZ_ASSERT(NULL != mAllInterfacesDoneScanningEvent);
23
}
24
25
~InterfaceScanCallbackData() {
26
::CloseHandle(mAllInterfacesDoneScanningEvent);
27
}
28
29
void OnInterfaceScanComplete() {
30
uint32_t val = ::InterlockedDecrement(&mCurrentlyScanningInterfaces);
31
if (!val) {
32
::SetEvent(mAllInterfacesDoneScanningEvent);
33
}
34
}
35
36
void WaitForAllInterfacesToFinishScanning(uint32_t msToWait) {
37
::WaitForSingleObject(mAllInterfacesDoneScanningEvent, msToWait);
38
}
39
40
private:
41
volatile uint32_t mCurrentlyScanningInterfaces;
42
HANDLE mAllInterfacesDoneScanningEvent;
43
};
44
45
static void WINAPI OnScanComplete(PWLAN_NOTIFICATION_DATA data, PVOID context) {
46
if (WLAN_NOTIFICATION_SOURCE_ACM != data->NotificationSource) {
47
return;
48
}
49
50
if (wlan_notification_acm_scan_complete != data->NotificationCode &&
51
wlan_notification_acm_scan_fail != data->NotificationCode) {
52
return;
53
}
54
55
InterfaceScanCallbackData* cbData =
56
reinterpret_cast<InterfaceScanCallbackData*>(context);
57
cbData->OnInterfaceScanComplete();
58
}
59
60
WinWifiScanner::WinWifiScanner() {
61
// NOTE: We assume that, if we were unable to load the WLAN library when
62
// we initially tried, we will not be able to load it in the future.
63
// Technically, on Windows XP SP2, a user could install the redistributable
64
// and make our assumption incorrect. We opt to avoid making a bunch of
65
// spurious LoadLibrary calls in the common case rather than load the
66
// WLAN API in the edge case.
67
mWlanLibrary = WinWLANLibrary::Load();
68
if (!mWlanLibrary) {
69
NS_WARNING("Could not initialize Windows Wi-Fi scanner");
70
}
71
}
72
73
WinWifiScanner::~WinWifiScanner() {}
74
75
nsresult WinWifiScanner::GetAccessPointsFromWLAN(
76
nsCOMArray<nsWifiAccessPoint>& accessPoints) {
77
accessPoints.Clear();
78
79
// NOTE: We do not try to load the WLAN library if we previously failed
80
// to load it. See the note in WinWifiScanner constructor
81
if (!mWlanLibrary) {
82
return NS_ERROR_NOT_AVAILABLE;
83
}
84
85
// Get the list of interfaces. WlanEnumInterfaces allocates interface_list.
86
WLAN_INTERFACE_INFO_LIST* interface_list = nullptr;
87
if (ERROR_SUCCESS !=
88
(*mWlanLibrary->GetWlanEnumInterfacesPtr())(mWlanLibrary->GetWLANHandle(),
89
nullptr, &interface_list)) {
90
return NS_ERROR_FAILURE;
91
}
92
93
// This ensures we call WlanFreeMemory on interface_list
94
ScopedWLANObject scopedInterfaceList(mWlanLibrary, interface_list);
95
96
if (!interface_list->dwNumberOfItems) {
97
return NS_OK;
98
}
99
100
InterfaceScanCallbackData cbData(interface_list->dwNumberOfItems);
101
102
DWORD wlanNotifySource;
103
if (ERROR_SUCCESS != (*mWlanLibrary->GetWlanRegisterNotificationPtr())(
104
mWlanLibrary->GetWLANHandle(),
105
WLAN_NOTIFICATION_SOURCE_ACM, TRUE,
106
(WLAN_NOTIFICATION_CALLBACK)OnScanComplete, &cbData,
107
NULL, &wlanNotifySource)) {
108
return NS_ERROR_FAILURE;
109
}
110
111
// Go through the list of interfaces and call `WlanScan` on each
112
for (unsigned int i = 0; i < interface_list->dwNumberOfItems; ++i) {
113
if (ERROR_SUCCESS != (*mWlanLibrary->GetWlanScanPtr())(
114
mWlanLibrary->GetWLANHandle(),
115
&interface_list->InterfaceInfo[i].InterfaceGuid,
116
NULL, NULL, NULL)) {
117
cbData.OnInterfaceScanComplete();
118
}
119
}
120
121
// From the MSDN documentation:
122
// "Wireless network drivers that meet Windows logo requirements are
123
// required to complete a WlanScan function request in 4 seconds"
124
cbData.WaitForAllInterfacesToFinishScanning(5000);
125
126
// Unregister for the notifications. The documentation mentions that,
127
// if a callback is currently running, this will wait for the callback
128
// to complete.
129
(*mWlanLibrary->GetWlanRegisterNotificationPtr())(
130
mWlanLibrary->GetWLANHandle(), WLAN_NOTIFICATION_SOURCE_NONE, TRUE, NULL,
131
NULL, NULL, &wlanNotifySource);
132
133
// Go through the list of interfaces and get the data for each.
134
for (uint32_t i = 0; i < interface_list->dwNumberOfItems; ++i) {
135
WLAN_BSS_LIST* bss_list;
136
if (ERROR_SUCCESS != (*mWlanLibrary->GetWlanGetNetworkBssListPtr())(
137
mWlanLibrary->GetWLANHandle(),
138
&interface_list->InterfaceInfo[i].InterfaceGuid,
139
nullptr, // Use all SSIDs.
140
DOT11_BSS_TYPE_UNUSED,
141
false, // bSecurityEnabled - unused
142
nullptr, // reserved
143
&bss_list)) {
144
continue;
145
}
146
147
// This ensures we call WlanFreeMemory on bss_list
148
ScopedWLANObject scopedBssList(mWlanLibrary, bss_list);
149
150
// Store each discovered access point in our outparam
151
for (int j = 0; j < static_cast<int>(bss_list->dwNumberOfItems); ++j) {
152
nsWifiAccessPoint* ap = new nsWifiAccessPoint();
153
if (!ap) {
154
continue;
155
}
156
157
const WLAN_BSS_ENTRY bss_entry = bss_list->wlanBssEntries[j];
158
ap->setMac(bss_entry.dot11Bssid);
159
ap->setSignal(bss_entry.lRssi);
160
ap->setSSID(reinterpret_cast<char const*>(bss_entry.dot11Ssid.ucSSID),
161
bss_entry.dot11Ssid.uSSIDLength);
162
163
accessPoints.AppendObject(ap);
164
}
165
}
166
167
return NS_OK;
168
}