Wifi (macOS): improve security type detection

This commit is contained in:
李通洲
2024-09-19 09:32:10 +08:00
parent 0025c78cce
commit 43d6f81b01
+93 -114
View File
@@ -13,21 +13,61 @@ int Apple80211GetInfoCopy(struct Apple80211 *handle, CFDictionaryRef *info) __at
@property (readonly) struct Apple80211* device;
@end
static NSDictionary* getWifiInfoByApple80211(CWInterface* inf)
inline static NSDictionary* getWifiInfoByApple80211(CWInterface* inf)
{
if (!Apple80211GetInfoCopy) return NULL;
if (!inf.device || !Apple80211GetInfoCopy) return NULL;
CFDictionaryRef result = NULL;
if (Apple80211GetInfoCopy(inf.device, &result) < 0) return NULL;
return CFBridgingRelease(result);
}
static const char* detectWifiByCoreWlan(FFlist* result)
static NSDictionary* getWifiInfoBySystemProfiler(NSString* ifName)
{
// Warning: costs about 2s on my machine
static NSArray* spData;
static bool inited;
if (!inited)
{
inited = true;
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
if (ffProcessAppendStdOut(&buffer, (char* const[]) {
"system_profiler",
"SPAirPortDataType",
"-xml",
"-detailLevel",
"basic",
NULL
}) != NULL)
return nil;
spData = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length]
options:NSPropertyListImmutable
format:nil
error:nil];
}
if (spData)
{
for (NSDictionary* data in spData[0][@"_items"])
{
for (NSDictionary* inf in data[@"spairport_airport_interfaces"])
{
if ([ifName isEqualToString:inf[@"_name"]])
return inf[@"spairport_current_network_information"];
}
}
}
return NULL;
}
const char* ffDetectWifi(FFlist* result)
{
NSArray<CWInterface*>* interfaces = CWWiFiClient.sharedWiFiClient.interfaces;
if(!interfaces)
if (!interfaces)
return "CWWiFiClient.sharedWiFiClient.interfaces is nil";
for(CWInterface* inf in interfaces)
for (CWInterface* inf in interfaces)
{
FFWifiResult* item = (FFWifiResult*)ffListAdd(result);
ffStrbufInit(&item->inf.description);
@@ -47,22 +87,25 @@ static const char* detectWifiByCoreWlan(FFlist* result)
continue;
ffStrbufSetStatic(&item->conn.status, inf.interfaceMode != kCWInterfaceModeNone ? "Active" : "Inactive");
if(!inf.serviceActive)
if(inf.interfaceMode == kCWInterfaceModeNone)
continue;
NSDictionary* apple = NULL;
NSDictionary* apple = nil; // For getWifiInfoByApple80211
NSDictionary* sp = nil; // For getWifiInfoBySystemProfiler
if (inf.ssid) // https://developer.apple.com/forums/thread/732431
ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String);
else if (apple || (apple = getWifiInfoByApple80211(inf)))
ffStrbufAppendS(&item->conn.ssid, [[apple valueForKey:@"SSID_STR"] UTF8String]);
ffStrbufAppendS(&item->conn.ssid, [apple[@"SSID_STR"] UTF8String]);
else if (sp || (sp = getWifiInfoBySystemProfiler(inf.interfaceName)))
ffStrbufAppendS(&item->conn.ssid, [sp[@"_name"] UTF8String]);
else
ffStrbufSetStatic(&item->conn.ssid, "<unknown ssid>"); // https://developer.apple.com/forums/thread/732431
if (inf.bssid)
ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String);
else if (apple || (apple = getWifiInfoByApple80211(inf)))
ffStrbufAppendS(&item->conn.bssid, [[apple valueForKey:@"BSSID"] UTF8String]);
ffStrbufAppendS(&item->conn.bssid, [apple[@"BSSID"] UTF8String]);
switch(inf.activePHYMode)
{
@@ -91,7 +134,24 @@ static const char* detectWifiByCoreWlan(FFlist* result)
ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)");
break;
default:
ffStrbufAppendF(&item->conn.protocol, "Unknown (%ld)", inf.activePHYMode);
if (inf.activePHYMode < 64)
ffStrbufAppendF(&item->conn.protocol, "Unknown (%ld)", inf.activePHYMode);
else if (sp || (sp = getWifiInfoBySystemProfiler(inf.interfaceName)))
{
ffStrbufSetS(&item->conn.protocol, [sp[@"spairport_network_phymode"] UTF8String]);
if (ffStrbufStartsWithS(&item->conn.protocol, "802.11"))
{
const char* subProtocol = item->conn.protocol.chars + strlen("802.11");
if (ffStrEquals(subProtocol, "be"))
ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)");
else if (ffStrEquals(subProtocol, "ax"))
ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)");
else if (ffStrEquals(subProtocol, "ac"))
ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)");
else if (ffStrEquals(subProtocol, "n"))
ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)");
}
}
break;
}
item->conn.signalQuality = (double) (inf.rssiValue >= -50 ? 100 : inf.rssiValue <= -100 ? 0 : (inf.rssiValue + 100) * 2);
@@ -151,17 +211,17 @@ static const char* detectWifiByCoreWlan(FFlist* result)
// Sonoma...
if (apple || (apple = getWifiInfoByApple80211(inf)))
{
NSDictionary* authType = [apple valueForKey:@"AUTH_TYPE"];
NSDictionary* authType = apple[@"AUTH_TYPE"];
if (authType)
{
// AUTH_LOWER seems useless. `airport` verifies if its value is between 1 and 3, and prints `unknown` if not
NSNumber* authUpper = [authType valueForKey:@"AUTH_UPPER"];
NSNumber* authUpper = authType[@"AUTH_UPPER"];
if (!authUpper)
ffStrbufSetStatic(&item->conn.security, "Insecure");
else
{
int authUpperValue = [authUpper intValue];
int authUpperValue = authUpper.intValue;
switch (authUpperValue)
{
case 1:
@@ -204,6 +264,26 @@ static const char* detectWifiByCoreWlan(FFlist* result)
}
}
}
else if (sp || (sp = getWifiInfoBySystemProfiler(inf.interfaceName)))
{
ffStrbufSetS(&item->conn.security, [sp[@"spairport_security_mode"] UTF8String]);
ffStrbufSubstrAfterFirstS(&item->conn.security, "_mode_");
if (ffStrbufEqualS(&item->conn.security, "none"))
ffStrbufSetStatic(&item->conn.security, "Insecure");
else
{
ffStrbufReplaceAllC(&item->conn.security, '_', ' ');
if (ffStrbufStartsWithS(&item->conn.security, "wpa"))
{
item->conn.security.chars[0] = 'W';
item->conn.security.chars[1] = 'P';
item->conn.security.chars[2] = 'A';
char* sub = strchr(item->conn.security.chars, ' ');
if (sub && sub[1])
sub[1] = (char) toupper(sub[1]);
}
}
}
break;
default:
ffStrbufAppendF(&item->conn.security, "Unknown (%ld)", inf.security);
@@ -212,104 +292,3 @@ static const char* detectWifiByCoreWlan(FFlist* result)
}
return NULL;
}
static const char* detectWifiBySystemProfiler(FFlist* result)
{
// Warning: costs about 2s on my machine
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
if (ffProcessAppendStdOut(&buffer, (char* const[]) {
"system_profiler",
"SPAirPortDataType",
"-xml",
"-detailLevel",
"basic",
NULL
}) != NULL)
return "Starting `system_profiler SPAirPortDataType -xml -detailLevel basic` failed";
NSArray* arr = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length]
options:NSPropertyListImmutable
format:nil
error:nil];
if (!arr || !arr.count)
return "system_profiler SPAirPortDataType returned an empty array";
for (NSDictionary* data in arr[0][@"_items"])
{
for (NSDictionary* inf in data[@"spairport_airport_interfaces"])
{
if (![inf[@"spairport_wireless_card_type"] isEqualToString:@"spairport_wireless_card_type_wifi (0x14E4, 0x4387)"]) continue;
FFWifiResult* item = (FFWifiResult*)ffListAdd(result);
ffStrbufInitS(&item->inf.description, [inf[@"_name"] UTF8String]);
ffStrbufInit(&item->inf.status);
ffStrbufInit(&item->conn.status);
ffStrbufInit(&item->conn.ssid);
ffStrbufInit(&item->conn.bssid);
ffStrbufInit(&item->conn.protocol);
ffStrbufInit(&item->conn.security);
item->conn.signalQuality = 0.0/0.0;
item->conn.rxRate = 0.0/0.0;
item->conn.txRate = 0.0/0.0;
NSString* status = inf[@"spairport_status_information"]; // spairport_status_connected spairport_status_disassociated spairport_status_off
ffStrbufSetStatic(&item->inf.status, [status isEqualToString:@"spairport_status_off"] ? "Power Off" : "Power On");
NSDictionary* station = inf[@"spairport_current_network_information"];
if (!station) continue;
ffStrbufSetS(&item->conn.status, status.UTF8String + strlen("spairport_status_"));
item->conn.status.chars[0] = (char) toupper(item->conn.status.chars[0]);
ffStrbufSetS(&item->conn.ssid, [station[@"_name"] UTF8String]);
ffStrbufSetS(&item->conn.protocol, [station[@"spairport_network_phymode"] UTF8String]);
if (ffStrbufStartsWithS(&item->conn.protocol, "802.11"))
{
const char* subProtocol = item->conn.protocol.chars + strlen("802.11");
if (ffStrEquals(subProtocol, "be"))
ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)");
else if (ffStrEquals(subProtocol, "ax"))
ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)");
else if (ffStrEquals(subProtocol, "ac"))
ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)");
else if (ffStrEquals(subProtocol, "n"))
ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)");
}
ffStrbufSetS(&item->conn.security, [station[@"spairport_security_mode"] UTF8String]);
ffStrbufSubstrAfterFirstS(&item->conn.security, "_mode_");
if (ffStrbufEqualS(&item->conn.security, "none"))
ffStrbufSetStatic(&item->conn.security, "Insecure");
else
{
ffStrbufReplaceAllC(&item->conn.security, '_', ' ');
if (ffStrbufStartsWithS(&item->conn.security, "wpa"))
{
item->conn.security.chars[0] = 'W';
item->conn.security.chars[1] = 'P';
item->conn.security.chars[2] = 'A';
char* sub = strchr(item->conn.security.chars, ' ');
if (sub && sub[1])
sub[1] = (char) toupper(sub[1]);
}
}
double rssiValue = strtod([station[@"spairport_signal_noise"] UTF8String], nil);
item->conn.signalQuality = (double) (rssiValue >= -50 ? 100 : rssiValue <= -100 ? 0 : (rssiValue + 100) * 2);
item->conn.txRate = (double) [station[@"spairport_network_rate"] longValue];
}
}
return NULL;
}
const char* ffDetectWifi(FFlist* result)
{
if (@available(macOS 15.0, *))
return detectWifiBySystemProfiler(result);
else
return detectWifiByCoreWlan(result);
}