给定 BSD 名称或对网络接口的引用,如何获取其网关和 DNS 服务器?

Given the BSD name or a reference to a network interface, how to get its gateway and DNS servers?

在 macOS 应用程序中,我正在尝试获取网络接口的网关/路由器和 DNS 服务器。该接口可以通过其 BSD 名称或使用带 SCNetworkInterfaceRefSCNetworkServiceRef 的系统配置来标识。 (以合适者为准。)

具体来说,如果用户打开“网络”系统首选项和 select 此连接,我想要获得将显示给用户的相同信息。 (请注意,它的网关在网络系统首选项中称为“路由器”,出于这个问题的目的,我交替使用这两个术语。)

此应用程序是用 Objective-C 编写的,但 Swift 解决方案也很好,因为通常可以直接将其移植到 Objective-C。

我已经想出了如何做到这一点,给出 SCNetworkServiceRef。这两个函数将 return 指定网络的网关和 DNS 服务器:

CFStringRef copyNetworkServiceGateway(SCNetworkServiceRef service)
{
    CFStringRef result = NULL;
    CFStringRef interfaceServiceID = SCNetworkServiceGetServiceID(service);
    CFStringRef servicePath = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Service/%@/IPv4"),
                                                       interfaceServiceID);
    SCDynamicStoreRef dynamicStoreRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault,
                                                             CFSTR("your.app.name.here"), NULL, NULL);
    CFDictionaryRef propList = (CFDictionaryRef)SCDynamicStoreCopyValue(dynamicStoreRef, servicePath);
    
    if (propList) {    
        result = (CFStringRef)CFDictionaryGetValue(propList, CFSTR("Router"));
        CFRetain(result);
        CFRelease(propList);
    }
    
    CFRelease(servicePath);
    CFRelease(dynamicStoreRef);
    
    return result;
}

CFArrayRef copyNetworkServiceDNSServers(SCNetworkServiceRef service)
{
    CFArrayRef result = NULL;
    CFStringRef interfaceServiceID = SCNetworkServiceGetServiceID(service);
    // If the user has added custom DNS servers, then we have to use this path to find them:
    CFStringRef servicePath = CFStringCreateWithFormat(NULL, NULL, CFSTR("Setup:/Network/Service/%@/DNS"),
                                                       interfaceServiceID);
    SCDynamicStoreRef dynamicStoreRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault,
                                                             CFSTR("your.app.name.here"), NULL, NULL);
    CFDictionaryRef propList = (CFDictionaryRef)SCDynamicStoreCopyValue(dynamicStoreRef, servicePath);
    CFRelease(servicePath);
    
    if (!propList) {
        // In this case, the user has not added custom DNS servers and we use this path to
        // get the default DNS servers:
        servicePath = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Service/%@/DNS"),
                                               interfaceServiceID);
        propList = (CFDictionaryRef)SCDynamicStoreCopyValue(dynamicStoreRef, servicePath);
        CFRelease(servicePath);
    }
    
    if (propList) {
        result = (CFArrayRef)CFDictionaryGetValue(propList, CFSTR("ServerAddresses"));
        
        if (result) {
            CFRetain(result);
        }
        
        CFRelease(propList);
    }
    
    CFRelease(dynamicStoreRef);
    
    return result;
}