master ohmu/fastfetch-openbsd / src / detection / wifi / wifi_apple.m
ohmu  ·  2026-10-15
  1#include "wifi.h"
  2#include "common/percent.h"
  3#include "common/processing.h"
  4#include "common/strutil.h"
  5#include "common/apple/cf_helpers.h"
  6
  7#import <CoreWLAN/CoreWLAN.h>
  8#import <IOKit/IOKitLib.h>
  9#import <IOKit/kext/KextManager.h>
 10
 11@interface CWNetworkProfile ()
 12@property (readonly, retain, nullable) NSArray<NSDictionary*>* bssidList;
 13@end
 14
 15const char* ffDetectWifi(FFlist* result) {
 16    NSArray<CWInterface*>* interfaces = CWWiFiClient.sharedWiFiClient.interfaces;
 17    if (!interfaces) {
 18        return "CWWiFiClient.sharedWiFiClient.interfaces is nil";
 19    }
 20
 21    for (CWInterface* inf in interfaces) {
 22        FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result);
 23        ffStrbufInit(&item->inf.description);
 24        ffStrbufInit(&item->inf.status);
 25        ffStrbufInit(&item->conn.status);
 26        ffStrbufInit(&item->conn.ssid);
 27        ffStrbufInit(&item->conn.bssid);
 28        ffStrbufInit(&item->conn.protocol);
 29        ffStrbufInit(&item->conn.security);
 30        item->conn.signalQuality = -DBL_MAX;
 31        item->conn.rxRate = -DBL_MAX;
 32        item->conn.txRate = -DBL_MAX;
 33        item->conn.channel = 0;
 34        item->conn.channelWidth = 0;
 35        item->conn.frequency = 0;
 36
 37        ffStrbufSetS(&item->inf.description, inf.interfaceName.UTF8String);
 38        ffStrbufSetStatic(&item->inf.status, inf.powerOn ? "Power On" : "Power Off");
 39
 40        if (!inf.powerOn) {
 41            continue;
 42        }
 43
 44        ffStrbufSetStatic(&item->conn.status, inf.interfaceMode != kCWInterfaceModeNone ? "Active" : "Inactive");
 45        if (inf.interfaceMode == kCWInterfaceModeNone) {
 46            continue;
 47        }
 48
 49        FF_STRBUF_AUTO_DESTROY ipconfig = ffStrbufCreate();
 50
 51        CWNetworkProfile* networkProfile = inf.configuration.networkProfiles.firstObject;
 52
 53        if (inf.ssid) { // https://developer.apple.com/forums/thread/732431
 54            ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String);
 55        } else if (networkProfile.ssid) {
 56            ffStrbufSetStatic(&item->conn.ssid, inf.configuration.networkProfiles.firstObject.ssid.UTF8String);
 57        } else {
 58            ffStrbufSetStatic(&item->conn.ssid, "<redacted>"); // https://developer.apple.com/forums/thread/732431
 59        }
 60
 61        if (inf.bssid) {
 62            ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String);
 63        } else if (networkProfile.bssidList) {
 64            ffStrbufSetStatic(&item->conn.bssid, [networkProfile.bssidList.firstObject[@"BSSID"] UTF8String]);
 65        } else {
 66            ffStrbufSetStatic(&item->conn.bssid, "<redacted>");
 67        }
 68
 69        switch (inf.activePHYMode) {
 70            case kCWPHYModeNone:
 71                ffStrbufSetStatic(&item->conn.protocol, "none");
 72                break;
 73            case kCWPHYMode11a:
 74                ffStrbufSetStatic(&item->conn.protocol, "802.11a");
 75                break;
 76            case kCWPHYMode11b:
 77                ffStrbufSetStatic(&item->conn.protocol, "802.11b");
 78                break;
 79            case kCWPHYMode11g:
 80                ffStrbufSetStatic(&item->conn.protocol, "802.11g");
 81                break;
 82            case kCWPHYMode11n:
 83                ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)");
 84                break;
 85            case kCWPHYMode11ac:
 86                ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)");
 87                break;
 88            case 6 /*kCWPHYMode11ax*/:
 89                ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)");
 90                break;
 91            case 7 /*kCWPHYMode11be?*/:
 92                ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)");
 93                break;
 94            default:
 95                if (inf.activePHYMode < 8) {
 96                    ffStrbufAppendF(&item->conn.protocol, "Unknown (%ld)", inf.activePHYMode);
 97                }
 98                break;
 99        }
100        item->conn.signalQuality = ffRssiToSignalQuality((int) inf.rssiValue);
101        item->conn.txRate = inf.transmitRate;
102
103        switch (inf.security) {
104            case kCWSecurityNone:
105                ffStrbufSetStatic(&item->conn.security, "Insecure");
106                break;
107            case kCWSecurityWEP:
108                ffStrbufSetStatic(&item->conn.security, "WEP");
109                break;
110            case kCWSecurityWPAPersonal:
111                ffStrbufSetStatic(&item->conn.security, "WPA Personal");
112                break;
113            case kCWSecurityWPAPersonalMixed:
114                ffStrbufSetStatic(&item->conn.security, "WPA Personal Mixed");
115                break;
116            case kCWSecurityWPA2Personal:
117                ffStrbufSetStatic(&item->conn.security, "WPA2 Personal");
118                break;
119            case kCWSecurityPersonal:
120                ffStrbufSetStatic(&item->conn.security, "Personal");
121                break;
122            case kCWSecurityDynamicWEP:
123                ffStrbufSetStatic(&item->conn.security, "Dynamic WEP");
124                break;
125            case kCWSecurityWPAEnterprise:
126                ffStrbufSetStatic(&item->conn.security, "WPA Enterprise");
127                break;
128            case kCWSecurityWPAEnterpriseMixed:
129                ffStrbufSetStatic(&item->conn.security, "WPA Enterprise Mixed");
130                break;
131            case kCWSecurityWPA2Enterprise:
132                ffStrbufSetStatic(&item->conn.security, "WPA2 Enterprise");
133                break;
134            case kCWSecurityEnterprise:
135                ffStrbufSetStatic(&item->conn.security, "Enterprise");
136                break;
137            case 11 /*kCWSecurityWPA3Personal*/:
138                ffStrbufSetStatic(&item->conn.security, "WPA3 Personal");
139                break;
140            case 12 /*kCWSecurityWPA3Enterprise*/:
141                ffStrbufSetStatic(&item->conn.security, "WPA3 Enterprise");
142                break;
143            case 13 /*kCWSecurityWPA3Transition*/:
144                ffStrbufSetStatic(&item->conn.security, "WPA3 Transition");
145                break;
146            case 14 /*kCWSecurityOWE*/:
147                ffStrbufSetStatic(&item->conn.security, "OWE");
148                break;
149            case 15 /*kCWSecurityOWETransition*/:
150                ffStrbufSetStatic(&item->conn.security, "OWE Transition");
151                break;
152            default:
153                ffStrbufAppendF(&item->conn.security, "Unknown (%ld)", inf.security);
154                break;
155        }
156
157        item->conn.channel = (uint16_t) inf.wlanChannel.channelNumber;
158        switch (inf.wlanChannel.channelWidth) {
159            case kCWChannelWidth20MHz: item->conn.channelWidth = 20; break;
160            case kCWChannelWidth40MHz: item->conn.channelWidth = 40; break;
161            case kCWChannelWidth80MHz: item->conn.channelWidth = 80; break;
162            case kCWChannelWidth160MHz: item->conn.channelWidth = 160; break;
163            default: item->conn.channelWidth = 0; break;
164        }
165
166        FF_IOOBJECT_AUTO_RELEASE io_object_t service = IOServiceGetMatchingService(MACH_PORT_NULL, IOBSDNameMatching(MACH_PORT_NULL, 0, item->inf.description.chars));
167        FF_CFTYPE_AUTO_RELEASE CFNumberRef frequency;
168        // Note: IO80211ChannelFrequency is only available when the interface is connected
169        while (!(frequency = IORegistryEntryCreateCFProperty(service, CFSTR("IO80211ChannelFrequency"), nullptr, kNilOptions))) {
170            io_object_t parentService = IO_OBJECT_NULL;
171            if (IORegistryEntryGetParentEntry(service, kIOServicePlane, &parentService) == KERN_SUCCESS) {
172                IOObjectRelease(service);
173                service = parentService;
174            } else {
175                IOObjectRelease(service);
176                service = IO_OBJECT_NULL;
177                break;
178            }
179        }
180        if (frequency) {
181            int64_t value;
182            if (ffCfNumGetInt64(frequency, &value) == nil) {
183                item->conn.frequency = (uint16_t) value;
184            }
185        }
186    }
187
188    return nullptr;
189}