master ohmu/fastfetch-openbsd / src / detection / wifi / wifi_windows.c
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}