Skip to content

Commit 43d6f81

Browse files
committed
Wifi (macOS): improve security type detection
1 parent 0025c78 commit 43d6f81

File tree

1 file changed

+93
-114
lines changed

1 file changed

+93
-114
lines changed

src/detection/wifi/wifi_apple.m

Lines changed: 93 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,61 @@ @interface CWInterface()
1313
@property (readonly) struct Apple80211* device;
1414
@end
1515

16-
static NSDictionary* getWifiInfoByApple80211(CWInterface* inf)
16+
inline static NSDictionary* getWifiInfoByApple80211(CWInterface* inf)
1717
{
18-
if (!Apple80211GetInfoCopy) return NULL;
18+
if (!inf.device || !Apple80211GetInfoCopy) return NULL;
1919
CFDictionaryRef result = NULL;
2020
if (Apple80211GetInfoCopy(inf.device, &result) < 0) return NULL;
2121
return CFBridgingRelease(result);
2222
}
2323

24-
static const char* detectWifiByCoreWlan(FFlist* result)
24+
static NSDictionary* getWifiInfoBySystemProfiler(NSString* ifName)
25+
{
26+
// Warning: costs about 2s on my machine
27+
static NSArray* spData;
28+
static bool inited;
29+
if (!inited)
30+
{
31+
inited = true;
32+
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
33+
if (ffProcessAppendStdOut(&buffer, (char* const[]) {
34+
"system_profiler",
35+
"SPAirPortDataType",
36+
"-xml",
37+
"-detailLevel",
38+
"basic",
39+
NULL
40+
}) != NULL)
41+
return nil;
42+
43+
spData = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length]
44+
options:NSPropertyListImmutable
45+
format:nil
46+
error:nil];
47+
}
48+
49+
if (spData)
50+
{
51+
for (NSDictionary* data in spData[0][@"_items"])
52+
{
53+
for (NSDictionary* inf in data[@"spairport_airport_interfaces"])
54+
{
55+
if ([ifName isEqualToString:inf[@"_name"]])
56+
return inf[@"spairport_current_network_information"];
57+
}
58+
}
59+
}
60+
61+
return NULL;
62+
}
63+
64+
const char* ffDetectWifi(FFlist* result)
2565
{
2666
NSArray<CWInterface*>* interfaces = CWWiFiClient.sharedWiFiClient.interfaces;
27-
if(!interfaces)
67+
if (!interfaces)
2868
return "CWWiFiClient.sharedWiFiClient.interfaces is nil";
2969

30-
for(CWInterface* inf in interfaces)
70+
for (CWInterface* inf in interfaces)
3171
{
3272
FFWifiResult* item = (FFWifiResult*)ffListAdd(result);
3373
ffStrbufInit(&item->inf.description);
@@ -47,22 +87,25 @@ @interface CWInterface()
4787
continue;
4888

4989
ffStrbufSetStatic(&item->conn.status, inf.interfaceMode != kCWInterfaceModeNone ? "Active" : "Inactive");
50-
if(!inf.serviceActive)
90+
if(inf.interfaceMode == kCWInterfaceModeNone)
5191
continue;
5292

53-
NSDictionary* apple = NULL;
93+
NSDictionary* apple = nil; // For getWifiInfoByApple80211
94+
NSDictionary* sp = nil; // For getWifiInfoBySystemProfiler
5495

5596
if (inf.ssid) // https://developer.apple.com/forums/thread/732431
5697
ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String);
5798
else if (apple || (apple = getWifiInfoByApple80211(inf)))
58-
ffStrbufAppendS(&item->conn.ssid, [[apple valueForKey:@"SSID_STR"] UTF8String]);
99+
ffStrbufAppendS(&item->conn.ssid, [apple[@"SSID_STR"] UTF8String]);
100+
else if (sp || (sp = getWifiInfoBySystemProfiler(inf.interfaceName)))
101+
ffStrbufAppendS(&item->conn.ssid, [sp[@"_name"] UTF8String]);
59102
else
60103
ffStrbufSetStatic(&item->conn.ssid, "<unknown ssid>"); // https://developer.apple.com/forums/thread/732431
61104

62105
if (inf.bssid)
63106
ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String);
64107
else if (apple || (apple = getWifiInfoByApple80211(inf)))
65-
ffStrbufAppendS(&item->conn.bssid, [[apple valueForKey:@"BSSID"] UTF8String]);
108+
ffStrbufAppendS(&item->conn.bssid, [apple[@"BSSID"] UTF8String]);
66109

67110
switch(inf.activePHYMode)
68111
{
@@ -91,7 +134,24 @@ @interface CWInterface()
91134
ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)");
92135
break;
93136
default:
94-
ffStrbufAppendF(&item->conn.protocol, "Unknown (%ld)", inf.activePHYMode);
137+
if (inf.activePHYMode < 64)
138+
ffStrbufAppendF(&item->conn.protocol, "Unknown (%ld)", inf.activePHYMode);
139+
else if (sp || (sp = getWifiInfoBySystemProfiler(inf.interfaceName)))
140+
{
141+
ffStrbufSetS(&item->conn.protocol, [sp[@"spairport_network_phymode"] UTF8String]);
142+
if (ffStrbufStartsWithS(&item->conn.protocol, "802.11"))
143+
{
144+
const char* subProtocol = item->conn.protocol.chars + strlen("802.11");
145+
if (ffStrEquals(subProtocol, "be"))
146+
ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)");
147+
else if (ffStrEquals(subProtocol, "ax"))
148+
ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)");
149+
else if (ffStrEquals(subProtocol, "ac"))
150+
ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)");
151+
else if (ffStrEquals(subProtocol, "n"))
152+
ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)");
153+
}
154+
}
95155
break;
96156
}
97157
item->conn.signalQuality = (double) (inf.rssiValue >= -50 ? 100 : inf.rssiValue <= -100 ? 0 : (inf.rssiValue + 100) * 2);
@@ -151,17 +211,17 @@ @interface CWInterface()
151211
// Sonoma...
152212
if (apple || (apple = getWifiInfoByApple80211(inf)))
153213
{
154-
NSDictionary* authType = [apple valueForKey:@"AUTH_TYPE"];
214+
NSDictionary* authType = apple[@"AUTH_TYPE"];
155215
if (authType)
156216
{
157217
// AUTH_LOWER seems useless. `airport` verifies if its value is between 1 and 3, and prints `unknown` if not
158218

159-
NSNumber* authUpper = [authType valueForKey:@"AUTH_UPPER"];
219+
NSNumber* authUpper = authType[@"AUTH_UPPER"];
160220
if (!authUpper)
161221
ffStrbufSetStatic(&item->conn.security, "Insecure");
162222
else
163223
{
164-
int authUpperValue = [authUpper intValue];
224+
int authUpperValue = authUpper.intValue;
165225
switch (authUpperValue)
166226
{
167227
case 1:
@@ -204,6 +264,26 @@ @interface CWInterface()
204264
}
205265
}
206266
}
267+
else if (sp || (sp = getWifiInfoBySystemProfiler(inf.interfaceName)))
268+
{
269+
ffStrbufSetS(&item->conn.security, [sp[@"spairport_security_mode"] UTF8String]);
270+
ffStrbufSubstrAfterFirstS(&item->conn.security, "_mode_");
271+
if (ffStrbufEqualS(&item->conn.security, "none"))
272+
ffStrbufSetStatic(&item->conn.security, "Insecure");
273+
else
274+
{
275+
ffStrbufReplaceAllC(&item->conn.security, '_', ' ');
276+
if (ffStrbufStartsWithS(&item->conn.security, "wpa"))
277+
{
278+
item->conn.security.chars[0] = 'W';
279+
item->conn.security.chars[1] = 'P';
280+
item->conn.security.chars[2] = 'A';
281+
char* sub = strchr(item->conn.security.chars, ' ');
282+
if (sub && sub[1])
283+
sub[1] = (char) toupper(sub[1]);
284+
}
285+
}
286+
}
207287
break;
208288
default:
209289
ffStrbufAppendF(&item->conn.security, "Unknown (%ld)", inf.security);
@@ -212,104 +292,3 @@ @interface CWInterface()
212292
}
213293
return NULL;
214294
}
215-
216-
static const char* detectWifiBySystemProfiler(FFlist* result)
217-
{
218-
// Warning: costs about 2s on my machine
219-
220-
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
221-
if (ffProcessAppendStdOut(&buffer, (char* const[]) {
222-
"system_profiler",
223-
"SPAirPortDataType",
224-
"-xml",
225-
"-detailLevel",
226-
"basic",
227-
NULL
228-
}) != NULL)
229-
return "Starting `system_profiler SPAirPortDataType -xml -detailLevel basic` failed";
230-
231-
NSArray* arr = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length]
232-
options:NSPropertyListImmutable
233-
format:nil
234-
error:nil];
235-
if (!arr || !arr.count)
236-
return "system_profiler SPAirPortDataType returned an empty array";
237-
238-
for (NSDictionary* data in arr[0][@"_items"])
239-
{
240-
for (NSDictionary* inf in data[@"spairport_airport_interfaces"])
241-
{
242-
if (![inf[@"spairport_wireless_card_type"] isEqualToString:@"spairport_wireless_card_type_wifi (0x14E4, 0x4387)"]) continue;
243-
244-
FFWifiResult* item = (FFWifiResult*)ffListAdd(result);
245-
ffStrbufInitS(&item->inf.description, [inf[@"_name"] UTF8String]);
246-
ffStrbufInit(&item->inf.status);
247-
ffStrbufInit(&item->conn.status);
248-
ffStrbufInit(&item->conn.ssid);
249-
ffStrbufInit(&item->conn.bssid);
250-
ffStrbufInit(&item->conn.protocol);
251-
ffStrbufInit(&item->conn.security);
252-
item->conn.signalQuality = 0.0/0.0;
253-
item->conn.rxRate = 0.0/0.0;
254-
item->conn.txRate = 0.0/0.0;
255-
256-
NSString* status = inf[@"spairport_status_information"]; // spairport_status_connected spairport_status_disassociated spairport_status_off
257-
258-
ffStrbufSetStatic(&item->inf.status, [status isEqualToString:@"spairport_status_off"] ? "Power Off" : "Power On");
259-
260-
NSDictionary* station = inf[@"spairport_current_network_information"];
261-
if (!station) continue;
262-
263-
ffStrbufSetS(&item->conn.status, status.UTF8String + strlen("spairport_status_"));
264-
item->conn.status.chars[0] = (char) toupper(item->conn.status.chars[0]);
265-
266-
ffStrbufSetS(&item->conn.ssid, [station[@"_name"] UTF8String]);
267-
268-
ffStrbufSetS(&item->conn.protocol, [station[@"spairport_network_phymode"] UTF8String]);
269-
if (ffStrbufStartsWithS(&item->conn.protocol, "802.11"))
270-
{
271-
const char* subProtocol = item->conn.protocol.chars + strlen("802.11");
272-
if (ffStrEquals(subProtocol, "be"))
273-
ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)");
274-
else if (ffStrEquals(subProtocol, "ax"))
275-
ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)");
276-
else if (ffStrEquals(subProtocol, "ac"))
277-
ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)");
278-
else if (ffStrEquals(subProtocol, "n"))
279-
ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)");
280-
}
281-
282-
ffStrbufSetS(&item->conn.security, [station[@"spairport_security_mode"] UTF8String]);
283-
ffStrbufSubstrAfterFirstS(&item->conn.security, "_mode_");
284-
if (ffStrbufEqualS(&item->conn.security, "none"))
285-
ffStrbufSetStatic(&item->conn.security, "Insecure");
286-
else
287-
{
288-
ffStrbufReplaceAllC(&item->conn.security, '_', ' ');
289-
if (ffStrbufStartsWithS(&item->conn.security, "wpa"))
290-
{
291-
item->conn.security.chars[0] = 'W';
292-
item->conn.security.chars[1] = 'P';
293-
item->conn.security.chars[2] = 'A';
294-
char* sub = strchr(item->conn.security.chars, ' ');
295-
if (sub && sub[1])
296-
sub[1] = (char) toupper(sub[1]);
297-
}
298-
}
299-
300-
double rssiValue = strtod([station[@"spairport_signal_noise"] UTF8String], nil);
301-
item->conn.signalQuality = (double) (rssiValue >= -50 ? 100 : rssiValue <= -100 ? 0 : (rssiValue + 100) * 2);
302-
item->conn.txRate = (double) [station[@"spairport_network_rate"] longValue];
303-
}
304-
}
305-
306-
return NULL;
307-
}
308-
309-
const char* ffDetectWifi(FFlist* result)
310-
{
311-
if (@available(macOS 15.0, *))
312-
return detectWifiBySystemProfiler(result);
313-
else
314-
return detectWifiByCoreWlan(result);
315-
}

0 commit comments

Comments
 (0)