【问题标题】:How to fetch Mac OS network configuration如何获取 Mac OS 网络配置
【发布时间】:2018-09-25 14:18:02
【问题描述】:

背景

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

我要跟踪的一件事是网络配置。

  • 网络接口列表
  • ipv4
  • ipv6
  • 姓名

问题

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

我在获取两种协议(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;
}

【问题讨论】:

标签: objective-c macos


【解决方案1】:

感谢@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;
}

【讨论】:

    猜你喜欢
    • 2011-05-08
    • 1970-01-01
    • 2020-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多