背景
我正在开发监视公司网络中机器的应用程序。 每台机器都将运行我的应用程序,并且它会跟踪有关机器状态的许多信息。
我想要跟踪的事情之一是网络配置。
- 网络接口(interface)列表
- ipv4
- ipv6
- 姓名
问题
我可以毫无问题地获取网络设备列表 ( SCNetworkInterfaceCopyAll ),或获取其 MAC 地址或 BSD 名称。
我在获取有关两种协议(protocol)(IPv4 和 IPv6)的计算机 IP 地址的信息时遇到问题。
我尝试过使用SCNetworkInterfaceGetConfiguration和 SCNetworkInterfaceGetExtendedConfiguration ,但我只有空值和 SCError返回kSCStatusInvalidArgument .
如果是 SCNetworkInterfaceGetExtendedConfiguration
,我使用的值:kSCEntNetIPSec
、kSCEntNetIPv4
、kSCEntNetIPv6
。
文档不够精确和清晰,所以我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,
¤t,
&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/