ohmu
·
2026-10-15
1#include "wifi.h"
2#include "common/library.h"
3#include "common/windows/unicode.h"
4
5#include <windows.h>
6#include <wlanapi.h>
7
8static void convertIfStateToString(WLAN_INTERFACE_STATE state, FFstrbuf* result) {
9 switch (state) {
10 case wlan_interface_state_not_ready:
11 ffStrbufSetStatic(result, "Not ready");
12 break;
13 case wlan_interface_state_connected:
14 ffStrbufSetStatic(result, "Connected");
15 break;
16 case wlan_interface_state_ad_hoc_network_formed:
17 ffStrbufSetStatic(result, "Ad hoc network formed");
18 break;
19 case wlan_interface_state_disconnecting:
20 ffStrbufSetStatic(result, "Disconnecting");
21 break;
22 case wlan_interface_state_disconnected:
23 ffStrbufSetStatic(result, "Disconnected");
24 break;
25 case wlan_interface_state_associating:
26 ffStrbufSetStatic(result, "Associating");
27 break;
28 case wlan_interface_state_discovering:
29 ffStrbufSetStatic(result, "Discovering");
30 break;
31 case wlan_interface_state_authenticating:
32 ffStrbufSetStatic(result, "Authenticating");
33 break;
34 default:
35 ffStrbufSetStatic(result, "Unknown");
36 break;
37 }
38}
39
40typedef struct _WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO {
41 UCHAR ucLinkID;
42 ULONG ulChannelCenterFrequencyMhz;
43 ULONG ulBandwidth;
44 LONG lRssi;
45 WLAN_RATE_SET wlanRateSet;
46} WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO, *PWLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO;
47
48typedef struct _WLAN_REALTIME_CONNECTION_QUALITY {
49 DOT11_PHY_TYPE dot11PhyType;
50 ULONG ulLinkQuality;
51 ULONG ulRxRate;
52 ULONG ulTxRate;
53 BOOL bIsMLOConnection;
54 ULONG ulNumLinks;
55 // Array of size ulNumLinks
56 WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO linksInfo[];
57} WLAN_REALTIME_CONNECTION_QUALITY, *PWLAN_REALTIME_CONNECTION_QUALITY;
58
59enum { wlan_intf_opcode_realtime_connection_quality = 19 };
60
61const char* ffDetectWifi(FFlist* result) {
62 FF_LIBRARY_LOAD_MESSAGE(wlanapi, "wlanapi" FF_LIBRARY_EXTENSION, 1)
63 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanOpenHandle)
64 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanEnumInterfaces)
65 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanQueryInterface)
66 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanFreeMemory)
67 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanCloseHandle)
68 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanGetNetworkBssList)
69
70 DWORD curVersion;
71 HANDLE hClient = nullptr;
72 WLAN_INTERFACE_INFO_LIST* ifList = nullptr;
73 const char* error = nullptr;
74
75 if (ffWlanOpenHandle(2 /*maxClientVersion*/, nullptr, &curVersion, &hClient) != ERROR_SUCCESS) {
76 error = "WlanOpenHandle() failed";
77 goto exit;
78 }
79
80 if (ffWlanEnumInterfaces(hClient, nullptr, &ifList) != ERROR_SUCCESS) {
81 error = "WlanEnumInterfaces() failed";
82 goto exit;
83 }
84
85 for (uint32_t index = 0; index < ifList->dwNumberOfItems; ++index) {
86 WLAN_INTERFACE_INFO* ifInfo = (WLAN_INTERFACE_INFO*) &ifList->InterfaceInfo[index];
87
88 FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result);
89 ffStrbufInitWS(&item->inf.description, ifInfo->strInterfaceDescription);
90 ffStrbufInit(&item->inf.status);
91 ffStrbufInit(&item->conn.status);
92 ffStrbufInit(&item->conn.ssid);
93 ffStrbufInit(&item->conn.bssid);
94 ffStrbufInit(&item->conn.protocol);
95 ffStrbufInit(&item->conn.security);
96 item->conn.signalQuality = -DBL_MAX;
97 item->conn.rxRate = -DBL_MAX;
98 item->conn.txRate = -DBL_MAX;
99 item->conn.channel = 0;
100 item->conn.channelWidth = 0;
101 item->conn.frequency = 0;
102
103 convertIfStateToString(ifInfo->isState, &item->inf.status);
104
105 if (ifInfo->isState != wlan_interface_state_connected) {
106 continue;
107 }
108
109 WLAN_CONNECTION_ATTRIBUTES* connInfo = nullptr;
110 DWORD bufSize = sizeof(*connInfo);
111 WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_query_only;
112
113 if (ffWlanQueryInterface(hClient,
114 &ifInfo->InterfaceGuid,
115 wlan_intf_opcode_current_connection,
116 nullptr,
117 &bufSize,
118 (PVOID*) &connInfo,
119 &opCode) != ERROR_SUCCESS) {
120 continue;
121 }
122
123 convertIfStateToString(connInfo->isState, &item->conn.status);
124 ffStrbufAppendNS(&item->conn.ssid,
125 connInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength,
126 (const char*) connInfo->wlanAssociationAttributes.dot11Ssid.ucSSID);
127
128 for (size_t i = 0; i < sizeof(connInfo->wlanAssociationAttributes.dot11Bssid); i++) {
129 ffStrbufAppendF(&item->conn.bssid, "%.2X:", connInfo->wlanAssociationAttributes.dot11Bssid[i]);
130 }
131 ffStrbufTrimRight(&item->conn.bssid, ':');
132
133 switch (connInfo->wlanAssociationAttributes.dot11PhyType) {
134 case dot11_phy_type_fhss:
135 ffStrbufAppendS(&item->conn.protocol, "802.11 (FHSS)");
136 break;
137 case dot11_phy_type_dsss:
138 ffStrbufAppendS(&item->conn.protocol, "802.11 (DSSS)");
139 break;
140 case dot11_phy_type_irbaseband:
141 ffStrbufAppendS(&item->conn.protocol, "802.11 (IR)");
142 break;
143 case dot11_phy_type_ofdm:
144 ffStrbufAppendS(&item->conn.protocol, "802.11a");
145 break;
146 case dot11_phy_type_hrdsss:
147 ffStrbufAppendS(&item->conn.protocol, "802.11b");
148 break;
149 case dot11_phy_type_erp:
150 ffStrbufAppendS(&item->conn.protocol, "802.11g");
151 break;
152 case dot11_phy_type_ht:
153 ffStrbufAppendS(&item->conn.protocol, "802.11n (Wi-Fi 4)");
154 break;
155 case dot11_phy_type_vht:
156 ffStrbufAppendS(&item->conn.protocol, "802.11ac (Wi-Fi 5)");
157 break;
158 case dot11_phy_type_dmg:
159 ffStrbufAppendS(&item->conn.protocol, "802.11ad (WiGig)");
160 break;
161 case dot11_phy_type_he:
162 ffStrbufAppendS(&item->conn.protocol, "802.11ax (Wi-Fi 6)");
163 break;
164 case dot11_phy_type_eht:
165 ffStrbufAppendS(&item->conn.protocol, "802.11be (Wi-Fi 7)");
166 break;
167 default:
168 ffStrbufAppendF(&item->conn.protocol, "Unknown (%u)", (unsigned) connInfo->wlanAssociationAttributes.dot11PhyType);
169 break;
170 }
171
172 item->conn.signalQuality = connInfo->wlanAssociationAttributes.wlanSignalQuality;
173 item->conn.rxRate = connInfo->wlanAssociationAttributes.ulRxRate / 1000.;
174 item->conn.txRate = connInfo->wlanAssociationAttributes.ulTxRate / 1000.;
175
176 if (connInfo->wlanSecurityAttributes.bSecurityEnabled) {
177 switch (connInfo->wlanSecurityAttributes.dot11AuthAlgorithm) {
178 case DOT11_AUTH_ALGO_80211_OPEN:
179 ffStrbufAppendS(&item->conn.security, "802.11 Open");
180 break;
181 case DOT11_AUTH_ALGO_80211_SHARED_KEY:
182 ffStrbufAppendS(&item->conn.security, "802.11 Shared");
183 break;
184 case DOT11_AUTH_ALGO_WPA:
185 ffStrbufAppendS(&item->conn.security, "WPA");
186 break;
187 case DOT11_AUTH_ALGO_WPA_PSK:
188 ffStrbufAppendS(&item->conn.security, "WPA-PSK");
189 break;
190 case DOT11_AUTH_ALGO_WPA_NONE:
191 ffStrbufAppendS(&item->conn.security, "WPA-None");
192 break;
193 case DOT11_AUTH_ALGO_RSNA:
194 ffStrbufAppendS(&item->conn.security, "WPA2");
195 break;
196 case DOT11_AUTH_ALGO_RSNA_PSK:
197 ffStrbufAppendS(&item->conn.security, "WPA2-PSK");
198 break;
199 case DOT11_AUTH_ALGO_WPA3:
200 ffStrbufAppendS(&item->conn.security, "WPA3");
201 break;
202 case DOT11_AUTH_ALGO_WPA3_SAE:
203 ffStrbufAppendS(&item->conn.security, "WPA3-SAE");
204 break;
205 case 10 /* DOT11_AUTH_ALGO_OWE */:
206 ffStrbufAppendS(&item->conn.security, "OWE");
207 break;
208 case 11 /* DOT11_AUTH_ALGO_WPA3_ENT */:
209 ffStrbufAppendS(&item->conn.security, "WPA3-ENT");
210 break;
211 default:
212 ffStrbufAppendF(&item->conn.security, "Unknown (%u)", (unsigned) connInfo->wlanSecurityAttributes.dot11AuthAlgorithm);
213 break;
214 }
215 if (connInfo->wlanSecurityAttributes.bOneXEnabled) {
216 ffStrbufAppendS(&item->conn.security, " 802.11X");
217 }
218 } else {
219 ffStrbufAppendS(&item->conn.security, "Insecure");
220 }
221
222 WLAN_REALTIME_CONNECTION_QUALITY* connectionQuality = nullptr;
223 bufSize = 0;
224 if (ffWlanQueryInterface(hClient,
225 &ifInfo->InterfaceGuid,
226 wlan_intf_opcode_realtime_connection_quality,
227 nullptr,
228 &bufSize,
229 (PVOID*) &connectionQuality,
230 &opCode) == ERROR_SUCCESS) {
231 const WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO* bestLink = nullptr;
232 for (ULONG linkIndex = 0; linkIndex < connectionQuality->ulNumLinks; ++linkIndex) {
233 const WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO* link = &connectionQuality->linksInfo[linkIndex];
234 if (!bestLink || link->lRssi > bestLink->lRssi) {
235 bestLink = link;
236 }
237 }
238 if (bestLink) {
239 item->conn.channelWidth = (uint16_t) bestLink->ulBandwidth;
240 item->conn.frequency = (uint16_t) bestLink->ulChannelCenterFrequencyMhz;
241 }
242 }
243 if (connectionQuality) {
244 ffWlanFreeMemory(connectionQuality);
245 }
246
247 if (item->conn.frequency == 0) {
248 WLAN_BSS_LIST* bssList = nullptr;
249 if (ffWlanGetNetworkBssList(hClient,
250 &ifInfo->InterfaceGuid,
251 &connInfo->wlanAssociationAttributes.dot11Ssid,
252 connInfo->wlanAssociationAttributes.dot11BssType,
253 connInfo->wlanSecurityAttributes.bSecurityEnabled,
254 nullptr,
255 &bssList) == ERROR_SUCCESS) {
256 if (bssList->dwNumberOfItems > 0) {
257 item->conn.frequency = (uint16_t) (bssList->wlanBssEntries[0].ulChCenterFrequency / 1000);
258 }
259 ffWlanFreeMemory(bssList);
260 }
261 }
262
263 ffWlanFreeMemory(connInfo);
264
265 ULONG* channelNumber = 0;
266 bufSize = sizeof(*channelNumber);
267 if (ffWlanQueryInterface(hClient,
268 &ifInfo->InterfaceGuid,
269 wlan_intf_opcode_channel_number,
270 nullptr,
271 &bufSize,
272 (PVOID*) &channelNumber,
273 &opCode) == ERROR_SUCCESS) {
274 item->conn.channel = (uint16_t) *channelNumber;
275 ffWlanFreeMemory(channelNumber);
276 }
277 }
278
279exit:
280 if (ifList) {
281 ffWlanFreeMemory(ifList);
282 }
283 if (hClient) {
284 ffWlanCloseHandle(hClient, nullptr);
285 }
286 return error;
287}