I am trying to detect whether a VPN connection is active in macOS from within a sandboxed application, but am having a hard time finding a common solution for various VPN services. Currently, I am using 2 approaches (see below), but these methods don't appear to be reliable for all VPN services/clients.
The below methods work great for the Cisco AnyConnect client and my VPN service, but for other VPN clients/services, such as Viscosity (and presumably other OpenVPN clients), these methods simply don't work. When comparing the values from not-active-VPN-connection to active-VPN-connection, no changes are detected in either DNS servers or the primary service value.
I should also mention that I have seen this post and don't think it is a good solution for this particular use case. I don't want to assume that for every VPN service out there that is always going to be a host that is only available when the VPN connection is active.
Any guidance would be greatly appreciated. Thank you in advance.
1) Looking at the current DNS servers (taken from here)
- (NSString *)getDNSAddressesCSV
{
NSMutableArray *addresses = [NSMutableArray new];
union res_sockaddr_union servers[NI_MAXSERV];
int serversFound = res_9_getservers(_state, servers, NI_MAXSERV);
char hostBuffer[NI_MAXHOST];
for (int i = 0; i < serversFound; i ++)
{
union res_sockaddr_union s = servers[i];
if (s.sin.sin_len > 0)
{
if (EXIT_SUCCESS == getnameinfo((struct sockaddr *)&s.sin, // Pointer to your struct sockaddr
(socklen_t)s.sin.sin_len, // Size of this struct
(char *)&hostBuffer, // Pointer to hostname string
sizeof(hostBuffer), // Size of this string
nil, // Pointer to service name string
0,
NI_NUMERICHOST))
{ // Flags given
[addresses addObject:[NSString stringWithUTF8String:hostBuffer]];
}
}
}
return [addresses componentsJoinedByString:@","];
}
and 2) Getting the current primary service from SCDynamicStore (taken from here)
- (NSString *) getVPNService
{
SCDynamicStoreRef dynamicStoreDomainState = SCDynamicStoreCreate(NULL,
CFSTR("appNameGoesHere"),
NULL,
NULL);
if (dynamicStoreDomainState)
{
NSString *netIPv4Key = [NSString stringWithFormat:@"%@/%@/%@/%@",
kSCDynamicStoreDomainState,
kSCCompNetwork,
kSCCompGlobal,
kSCEntNetIPv4];
NSMutableDictionary *netIPv4Dictionary = (NSMutableDictionary *) CFBridgingRelease(SCDynamicStoreCopyValue(dynamicStoreDomainState, (CFStringRef)netIPv4Key));
if (netIPv4Dictionary )
{
NSString *primaryService = [netIPv4Dictionary objectForKey:(NSString *)kSCDynamicStorePropNetPrimaryService];
if (primaryService)
{
CFRelease(dynamicStoreDomainState);
CFRelease((__bridge CFTypeRef)(netIPv4Key));
return primaryService;
}
else
{
CFRelease(dynamicStoreDomainState);
CFRelease((__bridge CFTypeRef)(netIPv4Key));
return @"";
}
}
CFRelease(dynamicStoreDomainState);
}
return @"";
}