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:
InterfaceScanCallbackData(uint32_t numInterfaces)15 explicit InterfaceScanCallbackData(uint32_t numInterfaces)
16 : mCurrentlyScanningInterfaces(numInterfaces) {
17 mAllInterfacesDoneScanningEvent =
18 ::CreateEventW(nullptr, // null security
19 TRUE, // manual reset event
20 FALSE, // initially nonsignaled
21 nullptr); // not named
22 MOZ_ASSERT(NULL != mAllInterfacesDoneScanningEvent);
23 }
24
~InterfaceScanCallbackData()25 ~InterfaceScanCallbackData() {
26 ::CloseHandle(mAllInterfacesDoneScanningEvent);
27 }
28
OnInterfaceScanComplete()29 void OnInterfaceScanComplete() {
30 uint32_t val = ::InterlockedDecrement(&mCurrentlyScanningInterfaces);
31 if (!val) {
32 ::SetEvent(mAllInterfacesDoneScanningEvent);
33 }
34 }
35
WaitForAllInterfacesToFinishScanning(uint32_t msToWait)36 void WaitForAllInterfacesToFinishScanning(uint32_t msToWait) {
37 ::WaitForSingleObject(mAllInterfacesDoneScanningEvent, msToWait);
38 }
39
40 private:
41 volatile uint32_t mCurrentlyScanningInterfaces;
42 HANDLE mAllInterfacesDoneScanningEvent;
43 };
44
OnScanComplete(PWLAN_NOTIFICATION_DATA data,PVOID context)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
WinWifiScanner()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.reset(WinWLANLibrary::Load());
68 if (!mWlanLibrary) {
69 NS_WARNING("Could not initialize Windows Wi-Fi scanner");
70 }
71 }
72
~WinWifiScanner()73 WinWifiScanner::~WinWifiScanner() {}
74
GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> & accessPoints)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 !=
104 (*mWlanLibrary
105 ->GetWlanRegisterNotificationPtr())(mWlanLibrary->GetWLANHandle(),
106 WLAN_NOTIFICATION_SOURCE_ACM,
107 TRUE,
108 (WLAN_NOTIFICATION_CALLBACK)
109 OnScanComplete,
110 &cbData, NULL,
111 &wlanNotifySource)) {
112 return NS_ERROR_FAILURE;
113 }
114
115 // Go through the list of interfaces and call `WlanScan` on each
116 for (unsigned int i = 0; i < interface_list->dwNumberOfItems; ++i) {
117 if (ERROR_SUCCESS !=
118 (*mWlanLibrary->GetWlanScanPtr())(mWlanLibrary->GetWLANHandle(),
119 &interface_list->InterfaceInfo[i]
120 .InterfaceGuid,
121 NULL, NULL, NULL)) {
122 cbData.OnInterfaceScanComplete();
123 }
124 }
125
126 // From the MSDN documentation:
127 // "Wireless network drivers that meet Windows logo requirements are
128 // required to complete a WlanScan function request in 4 seconds"
129 cbData.WaitForAllInterfacesToFinishScanning(5000);
130
131 // Unregister for the notifications. The documentation mentions that,
132 // if a callback is currently running, this will wait for the callback
133 // to complete.
134 (*mWlanLibrary->GetWlanRegisterNotificationPtr())(
135 mWlanLibrary->GetWLANHandle(), WLAN_NOTIFICATION_SOURCE_NONE, TRUE, NULL,
136 NULL, NULL, &wlanNotifySource);
137
138 // Go through the list of interfaces and get the data for each.
139 for (uint32_t i = 0; i < interface_list->dwNumberOfItems; ++i) {
140 WLAN_BSS_LIST* bss_list;
141 if (ERROR_SUCCESS !=
142 (*mWlanLibrary
143 ->GetWlanGetNetworkBssListPtr())(mWlanLibrary->GetWLANHandle(),
144 &interface_list->InterfaceInfo[i]
145 .InterfaceGuid,
146 nullptr, // Use all SSIDs.
147 DOT11_BSS_TYPE_UNUSED,
148 false, // bSecurityEnabled -
149 // unused
150 nullptr, // reserved
151 &bss_list)) {
152 continue;
153 }
154
155 // This ensures we call WlanFreeMemory on bss_list
156 ScopedWLANObject scopedBssList(*mWlanLibrary, bss_list);
157
158 // Store each discovered access point in our outparam
159 for (int j = 0; j < static_cast<int>(bss_list->dwNumberOfItems); ++j) {
160 nsWifiAccessPoint* ap = new nsWifiAccessPoint();
161 if (!ap) {
162 continue;
163 }
164
165 const WLAN_BSS_ENTRY bss_entry = bss_list->wlanBssEntries[j];
166 ap->setMac(bss_entry.dot11Bssid);
167 ap->setSignal(bss_entry.lRssi);
168 ap->setSSID(reinterpret_cast<char const*>(bss_entry.dot11Ssid.ucSSID),
169 bss_entry.dot11Ssid.uSSIDLength);
170
171 accessPoints.AppendObject(ap);
172 }
173 }
174
175 return NS_OK;
176 }
177