WinWifiScanner.cpp (7638B)
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 "WinWifiScanner.h" 7 8 #define DOT11_BSS_TYPE_UNUSED static_cast<DOT11_BSS_TYPE>(0) 9 10 namespace mozilla { 11 12 LazyLogModule gWifiScannerLog("WifiScanner"); 13 #define WIFISCAN_LOG(args) \ 14 MOZ_LOG(mozilla::gWifiScannerLog, mozilla::LogLevel::Debug, args) 15 16 class InterfaceScanCallbackData { 17 public: 18 explicit InterfaceScanCallbackData(uint32_t numInterfaces) 19 : mCurrentlyScanningInterfaces(numInterfaces) { 20 mAllInterfacesDoneScanningEvent = 21 ::CreateEventW(nullptr, // null security 22 TRUE, // manual reset event 23 FALSE, // initially nonsignaled 24 nullptr); // not named 25 MOZ_ASSERT(NULL != mAllInterfacesDoneScanningEvent); 26 } 27 28 ~InterfaceScanCallbackData() { 29 ::CloseHandle(mAllInterfacesDoneScanningEvent); 30 } 31 32 void OnInterfaceScanComplete() { 33 uint32_t val = ::InterlockedDecrement(&mCurrentlyScanningInterfaces); 34 if (!val) { 35 ::SetEvent(mAllInterfacesDoneScanningEvent); 36 } 37 } 38 39 void WaitForAllInterfacesToFinishScanning(uint32_t msToWait) { 40 ::WaitForSingleObject(mAllInterfacesDoneScanningEvent, msToWait); 41 } 42 43 private: 44 volatile uint32_t mCurrentlyScanningInterfaces; 45 HANDLE mAllInterfacesDoneScanningEvent; 46 }; 47 48 static void WINAPI OnScanComplete(PWLAN_NOTIFICATION_DATA data, PVOID context) { 49 WIFISCAN_LOG(("WinWifiScanner OnScanComplete")); 50 if (WLAN_NOTIFICATION_SOURCE_ACM != data->NotificationSource) { 51 return; 52 } 53 54 if (wlan_notification_acm_scan_complete != data->NotificationCode && 55 wlan_notification_acm_scan_fail != data->NotificationCode) { 56 return; 57 } 58 59 InterfaceScanCallbackData* cbData = 60 reinterpret_cast<InterfaceScanCallbackData*>(context); 61 cbData->OnInterfaceScanComplete(); 62 } 63 64 WifiScannerImpl::WifiScannerImpl() { 65 // NOTE: We assume that, if we were unable to load the WLAN library when 66 // we initially tried, we will not be able to load it in the future. 67 // Technically, on Windows XP SP2, a user could install the redistributable 68 // and make our assumption incorrect. We opt to avoid making a bunch of 69 // spurious LoadLibrary calls in the common case rather than load the 70 // WLAN API in the edge case. 71 mWlanLibrary.reset(WinWLANLibrary::Load()); 72 if (!mWlanLibrary) { 73 WIFISCAN_LOG( 74 ("[%p] WinWifiScanner could not initialize Windows Wi-Fi scanner", 75 this)); 76 } 77 WIFISCAN_LOG(("[%p] WinWifiScanner created WifiScannerImpl", this)); 78 } 79 80 WifiScannerImpl::~WifiScannerImpl() { 81 WIFISCAN_LOG(("[%p] WinWifiScanner destroyed WifiScannerImpl", this)); 82 } 83 84 nsresult WifiScannerImpl::GetAccessPointsFromWLAN( 85 nsTArray<RefPtr<nsIWifiAccessPoint>>& accessPoints) { 86 WIFISCAN_LOG(("[%p] WinWifiScanner::GetAccessPointsFromWLAN", this)); 87 accessPoints.Clear(); 88 89 // NOTE: We do not try to load the WLAN library if we previously failed 90 // to load it. See the note in WifiScannerImpl constructor 91 if (!mWlanLibrary) { 92 WIFISCAN_LOG( 93 ("[%p] WinWifiScanner::GetAccessPointsFromWLAN WLAN library is not " 94 "available", 95 this)); 96 return NS_ERROR_NOT_AVAILABLE; 97 } 98 99 // Get the list of interfaces. WlanEnumInterfaces allocates interface_list. 100 WLAN_INTERFACE_INFO_LIST* interface_list = nullptr; 101 if (ERROR_SUCCESS != 102 (*mWlanLibrary->GetWlanEnumInterfacesPtr())(mWlanLibrary->GetWLANHandle(), 103 nullptr, &interface_list)) { 104 WIFISCAN_LOG( 105 ("[%p] WinWifiScanner::GetAccessPointsFromWLAN WlanEnumInterfaces " 106 "failed", 107 this)); 108 return NS_ERROR_FAILURE; 109 } 110 111 // This ensures we call WlanFreeMemory on interface_list 112 ScopedWLANObject scopedInterfaceList(*mWlanLibrary, interface_list); 113 WIFISCAN_LOG( 114 ("[%p] WinWifiScanner::GetAccessPointsFromWLAN found %lu interfaces", 115 this, interface_list->dwNumberOfItems)); 116 117 if (!interface_list->dwNumberOfItems) { 118 return NS_OK; 119 } 120 121 InterfaceScanCallbackData cbData(interface_list->dwNumberOfItems); 122 123 DWORD wlanNotifySource; 124 if (ERROR_SUCCESS != (*mWlanLibrary->GetWlanRegisterNotificationPtr())( 125 mWlanLibrary->GetWLANHandle(), 126 WLAN_NOTIFICATION_SOURCE_ACM, TRUE, 127 (WLAN_NOTIFICATION_CALLBACK)OnScanComplete, &cbData, 128 NULL, &wlanNotifySource)) { 129 WIFISCAN_LOG( 130 ("[%p] WinWifiScanner::GetAccessPointsFromWLAN " 131 "WlanRegisterNotification failed", 132 this)); 133 return NS_ERROR_FAILURE; 134 } 135 136 // Go through the list of interfaces and call `WlanScan` on each 137 for (unsigned int i = 0; i < interface_list->dwNumberOfItems; ++i) { 138 if (ERROR_SUCCESS != (*mWlanLibrary->GetWlanScanPtr())( 139 mWlanLibrary->GetWLANHandle(), 140 &interface_list->InterfaceInfo[i].InterfaceGuid, 141 NULL, NULL, NULL)) { 142 WIFISCAN_LOG( 143 ("[%p] WinWifiScanner::GetAccessPointsFromWLAN WlanScan failed.", 144 this)); 145 cbData.OnInterfaceScanComplete(); 146 } 147 } 148 149 // From the MSDN documentation: 150 // "Wireless network drivers that meet Windows logo requirements are 151 // required to complete a WlanScan function request in 4 seconds" 152 cbData.WaitForAllInterfacesToFinishScanning(5000); 153 154 // Unregister for the notifications. The documentation mentions that, 155 // if a callback is currently running, this will wait for the callback 156 // to complete. 157 (*mWlanLibrary->GetWlanRegisterNotificationPtr())( 158 mWlanLibrary->GetWLANHandle(), WLAN_NOTIFICATION_SOURCE_NONE, TRUE, NULL, 159 NULL, NULL, &wlanNotifySource); 160 161 // Go through the list of interfaces and get the data for each. 162 for (uint32_t i = 0; i < interface_list->dwNumberOfItems; ++i) { 163 WLAN_BSS_LIST* bss_list; 164 if (ERROR_SUCCESS != (*mWlanLibrary->GetWlanGetNetworkBssListPtr())( 165 mWlanLibrary->GetWLANHandle(), 166 &interface_list->InterfaceInfo[i].InterfaceGuid, 167 nullptr, // Use all SSIDs. 168 DOT11_BSS_TYPE_UNUSED, 169 false, // bSecurityEnabled - 170 // unused 171 nullptr, // reserved 172 &bss_list)) { 173 WIFISCAN_LOG( 174 ("[%p] WinWifiScanner::GetAccessPointsFromWLAN unable to get BSS " 175 "list from interface %u", 176 this, i)); 177 continue; 178 } 179 180 WIFISCAN_LOG( 181 ("[%p] WinWifiScanner::GetAccessPointsFromWLAN BSS list has %lu access " 182 "points", 183 this, bss_list->dwNumberOfItems)); 184 185 // This ensures we call WlanFreeMemory on bss_list 186 ScopedWLANObject scopedBssList(*mWlanLibrary, bss_list); 187 188 // Store each discovered access point in our outparam 189 for (int j = 0; j < static_cast<int>(bss_list->dwNumberOfItems); ++j) { 190 nsWifiAccessPoint* ap = new nsWifiAccessPoint(); 191 if (!ap) { 192 continue; 193 } 194 195 const WLAN_BSS_ENTRY bss_entry = bss_list->wlanBssEntries[j]; 196 ap->setMac(bss_entry.dot11Bssid); 197 ap->setSignal(bss_entry.lRssi); 198 ap->setSSID(reinterpret_cast<char const*>(bss_entry.dot11Ssid.ucSSID), 199 bss_entry.dot11Ssid.uSSIDLength); 200 201 accessPoints.AppendElement(ap); 202 } 203 } 204 205 return NS_OK; 206 } 207 208 } // namespace mozilla 209 210 #undef WIFISCAN_LOG