objective-c - 如何获取 Mac OS 网络配置

标签 objective-c macos

背景

我正在开发监视公司网络中机器的应用程序。 每台机器都将运行我的应用程序,并且它会跟踪有关机器状态的许多信息。

我想要跟踪的事情之一是网络配置。

  • 网络接口(interface)列表
  • ipv4
  • ipv6
  • 姓名

问题

我可以毫无问题地获取网络设备列表 ( SCNetworkInterfaceCopyAll ),或获取其 MAC 地址或 BSD 名称。

我在获取有关两种协议(protocol)(IPv4 和 IPv6)的计算机 IP 地址的信息时遇到问题。

我尝试过使用SCNetworkInterfaceGetConfigurationSCNetworkInterfaceGetExtendedConfiguration ,但我只有空值和 SCError返回kSCStatusInvalidArgument .

如果是 SCNetworkInterfaceGetExtendedConfiguration,我使用的值:kSCEntNetIPSeckSCEntNetIPv4kSCEntNetIPv6

文档不够精确和清晰,所以我found some project在使用此 API 的地方,这给了我一些提示,但仍然没有帮助。

我做错了什么?

这里是我用来探索 API 的一些测试代码(它是 C++ 代码,因为我要将应用程序移植到 Mac):

#include <iostream>
#include <string>

#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPreferences.h>
#include <SystemConfiguration/SCNetwork.h>
#include <SystemConfiguration/SCNetworkReachability.h>
#include <SystemConfiguration/SCNetworkConnection.h>
#include <SystemConfiguration/SCNetworkConfiguration.h>
#include <CoreFoundation/CoreFoundation.h>

inline void myRelease(CFTypeRef p)
{
    if (p) CFRelease(p);
}

inline std::string toStd(CFStringRef s)
{
    if (!s) {
        return {};
    }
    if (auto fastCString = CFStringGetCStringPtr(s, kCFStringEncodingUTF8)) {
        return fastCString;
    }
    auto len = CFStringGetLength(s);
    auto size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(s), kCFStringEncodingUTF8) + 1;
    std::string result(size, '\0');

    CFStringGetBytes(s, { 0, len }, kCFStringEncodingUTF8, '?', 0,
                     reinterpret_cast<UInt8 *>(&result[0]), result.size(), &size);
    result.resize(size);

    return result;
}

std::ostream &operator<<(std::ostream &out, CFStringRef s) {
    return out << toStd(s);
}

struct DebugCf {
    DebugCf(CFTypeRef p) : mP(p) {}
    std::string toString() const {
        if (!mP) {
            return "<null>";
        }
        auto idOfType = CFGetTypeID(mP);
        if (CFStringGetTypeID() == idOfType) {
           return toStd((CFStringRef)mP);
        }
        auto s = CFCopyDescription(mP);
        auto result = toStd(s);
        CFRelease(s);

        return result;
    }

private:
    CFTypeRef mP;
};

std::ostream &operator<<(std::ostream &out, const DebugCf d) {
    return out << d.toString();
}

#define LOGCF(x) " " #x "["<< DebugCf(x) << "], "

std::string SCErrorString()
{
    switch (SCError()) {
        case kSCStatusOK: return "OK";
        case kSCStatusFailed: return "Failed";
        case kSCStatusInvalidArgument: return "InvalidArgument";
        case kSCStatusAccessError: return "AccessError";
        case kSCStatusNoKey: return "NoKey";
        case kSCStatusKeyExists: return "KeyExists";
        case kSCStatusLocked: return "Locked";
        case kSCStatusNeedLock: return "NeedLock";
        case kSCStatusNoStoreSession: return "NoStoreSession";
        case kSCStatusNoStoreServer: return "NoStoreServer";
        case kSCStatusNotifierActive: return "NotifierActive";
        case kSCStatusNoPrefsSession: return "NoPrefsSession";
        case kSCStatusPrefsBusy: return "PrefsBusy";
        case kSCStatusNoConfigFile: return "NoConfigFile";
        case kSCStatusNoLink: return "NoLink";
        case kSCStatusStale: return "Stale";
        case kSCStatusMaxLink: return "MaxLink";
        case kSCStatusReachabilityUnknown: return "ReachabilityUnknown";

        default:
            return std::to_string(SCError());
    }
}

void exploreInterface(SCNetworkInterfaceRef netInterface) {
    auto macString = SCNetworkInterfaceGetHardwareAddressString(netInterface);
    auto bsdName = SCNetworkInterfaceGetBSDName(netInterface);

    std::cout << bsdName << ' ' << macString << '\n';

    CFDictionaryRef current = NULL;
    CFDictionaryRef active = NULL;
    CFArrayRef available = NULL;

    auto result = SCNetworkInterfaceCopyMediaOptions(netInterface,
                                                     &current,
                                                     &active,
                                                     &available,
                                                     1);

    std::cout << "SCNetworkInterfaceCopyMediaOptions: "
        << (bool)result << " e=" << SCErrorString()
        // << LOGCF(current) << LOGCF(active) << LOGCF(available)
        << '\n';

    auto interfaceConfig = SCNetworkInterfaceGetConfiguration(netInterface);
    std::cout << "SCNetworkInterfaceGetConfiguration: " << LOGCF(interfaceConfig)
            << "e = " << SCErrorString() << '\n';


    auto interfaceExtConfig = SCNetworkInterfaceGetExtendedConfiguration(netInterface, kSCEntNetIPSec);
    std::cout << "SCNetworkInterfaceGetExtendedConfiguration: " << LOGCF(interfaceExtConfig)
        << "e = " << SCErrorString() << '\n';

    std::cout << "SCNetworkInterfaceGetInterfaceType: "
        << SCNetworkInterfaceGetInterfaceType(netInterface)
        << "  SCNetworkInterfaceGetLocalizedDisplayName: "
        << SCNetworkInterfaceGetLocalizedDisplayName(netInterface) << '\n';

    auto supportedProtocols = SCNetworkInterfaceGetSupportedProtocolTypes(netInterface);
    std::cout << "SCNetworkInterfaceGetSupportedProtocolTypes: "
    << LOGCF(supportedProtocols) << '\n';

    myRelease(current);
    myRelease(active);
    myRelease(available);
}

int main(int argc, const char * argv[]) {
    auto allNetwork = SCNetworkInterfaceCopyAll();

    auto count = CFArrayGetCount(allNetwork);
    for (CFIndex i=0; i<count; ++i) {
        auto netInterface = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(allNetwork, i);
        exploreInterface(netInterface);
        std::cout << "-------------------------\n";
    }
    CFRelease(allNetwork);

    return 0;
}

最佳答案

感谢@Hofi 和@johnelemans 的帮助。这个提示足以找到答案。 对于其他可能有类似问题的人:

获取Mac地址SCDynamicStore必须使用。由此我可以 fetch BSD name稍后需要它来获取连接详细信息。

现在使用SCDynamicStore API有点奇怪。 要探索可以做什么,可以使用命令行工具:scutil 。 最好不带参数运行它并输入以下命令:

list
list State:/Network/Interfaces/.*
get State:/Network/Interface/en0/IPv6
d.show

我找到了examples here .

根据从该工具获得的信息,我想出了如何获取所需数据。 C++ 测试代码:

#include <iostream>
#include <string>

#include <SystemConfiguration/SystemConfiguration.h>
#include <CoreFoundation/CoreFoundation.h>

inline void myRelease(CFTypeRef p)
{
    if (p) CFRelease(p);
}

inline std::string toStd(CFStringRef s)
{
    if (!s) {
        return {};
    }
    if (auto fastCString = CFStringGetCStringPtr(s, kCFStringEncodingUTF8)) {
        return fastCString;
    }
    auto len = CFStringGetLength(s);
    auto size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(s), kCFStringEncodingUTF8) + 1;
    std::string result(size, '\0');

    CFStringGetBytes(s, { 0, len }, kCFStringEncodingUTF8, '?', 0,
                     reinterpret_cast<UInt8 *>(&result[0]), result.size(), &size);
    result.resize(size);

    return result;
}

std::ostream &operator<<(std::ostream &out, CFStringRef s) {
    return out << toStd(s);
}

struct DebugCf {
    DebugCf(CFTypeRef p) : mP(p) {}
    std::string toString() const {
        if (!mP) {
            return "<null>";
        }
        auto idOfType = CFGetTypeID(mP);
        if (CFStringGetTypeID() == idOfType) {
           return toStd((CFStringRef)mP);
        }
        auto s = CFCopyDescription(mP);
        auto result = toStd(s);
        CFRelease(s);

        return result;
    }

private:
    CFTypeRef mP;
};

std::ostream &operator<<(std::ostream &out, const DebugCf d) {
    return out << d.toString();
}

#define LOGCF(x) " " #x "["<< DebugCf(x) << "], "

std::string SCErrorString()
{
    switch (SCError()) {
        case kSCStatusOK: return "OK";
        case kSCStatusFailed: return "Failed";
        case kSCStatusInvalidArgument: return "InvalidArgument";
        case kSCStatusAccessError: return "AccessError";
        case kSCStatusNoKey: return "NoKey";
        case kSCStatusKeyExists: return "KeyExists";
        case kSCStatusLocked: return "Locked";
        case kSCStatusNeedLock: return "NeedLock";
        case kSCStatusNoStoreSession: return "NoStoreSession";
        case kSCStatusNoStoreServer: return "NoStoreServer";
        case kSCStatusNotifierActive: return "NotifierActive";
        case kSCStatusNoPrefsSession: return "NoPrefsSession";
        case kSCStatusPrefsBusy: return "PrefsBusy";
        case kSCStatusNoConfigFile: return "NoConfigFile";
        case kSCStatusNoLink: return "NoLink";
        case kSCStatusStale: return "Stale";
        case kSCStatusMaxLink: return "MaxLink";
        case kSCStatusReachabilityUnknown: return "ReachabilityUnknown";

        default:
            return std::to_string(SCError());
    }
}

void exploreServiceQuery(CFStringRef query, CFStringRef serviceId, SCDynamicStoreRef scSession) {
    auto resolvedQuery =
    CFStringCreateWithFormat(kCFAllocatorDefault,
                             NULL,
                             query, serviceId);

    auto dic = (CFDictionaryRef)SCDynamicStoreCopyValue(scSession,
                                                        resolvedQuery);

    std::cout << resolvedQuery << " - " << LOGCF(dic) << '\n';

    myRelease(dic);
    CFRelease(resolvedQuery);
}

void printIpv4Data(SCDynamicStoreRef scSession,
                   CFStringRef bsdName) {
    exploreServiceQuery(CFSTR("State:/Network/Interface/%@/IPv4"),
                        bsdName,
                        scSession);
}

void printIpv6Data(SCDynamicStoreRef scSession,
                   CFStringRef bsdName) {
    exploreServiceQuery(CFSTR("State:/Network/Interface/%@/IPv6"),
                        bsdName,
                        scSession);
}

void desiriedData() {
    auto allInterfaces = SCNetworkInterfaceCopyAll();

    auto scSession = SCDynamicStoreCreate(kCFAllocatorDefault,
                                          CFSTR("Custom"),
                                          NULL,
                                          NULL);

    auto count = CFArrayGetCount(allInterfaces);
    for (CFIndex i = 0; i < count; ++i) {
        auto netInterface = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(allInterfaces, i);

        auto macString = SCNetworkInterfaceGetHardwareAddressString(netInterface);
        auto bsdName = SCNetworkInterfaceGetBSDName(netInterface);

        std::cout << bsdName << " " << macString << '\n';
        printIpv4Data(scSession, bsdName);
        printIpv6Data(scSession, bsdName);
    }

    CFRelease(scSession);
    CFRelease(allInterfaces);
}

int main(int argc, const char * argv[]) {
    desiriedData();

    return 0;
}

关于objective-c - 如何获取 Mac OS 网络配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52500407/

相关文章:

ios - 将 Admob 广告添加到 UITableView

ios - 如何将图像文件上传到保管箱?

macos - Homebrew R 构建缺少开罗

macos - 在 mac os x 上使用 boot2docker 运行 docker?

c++ - 编写一个可以打开和使用另一个程序的程序 : (Audio program)

macos - 将 JonesForth 移植到 macOS v10.15 (Catalina)

Bash:哪个 <app> 返回的位置与预期的不同

ios - 为什么解雇ViewControllerAnimated : completion: method can't dealloc view controller

ios - iOS 6 下的错误约束

iphone - 在没有按钮连接的情况下以编程方式执行 Segue?