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 ::CreateEvent(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 != (*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 }
169