Reputation: 37647
I'm developing application which monitors machines in corporate network. Each machines will run my application and it tracks many things regarding machine status.
One of the things I want to track is network configuration.
I have no problem to get list of the network devices (SCNetworkInterfaceCopyAll), or fetching its MAC address or BSD name.
I have problem fetching information about machine IP address for both protocols (IPv4 and IPv6).
I've tried use SCNetworkInterfaceGetConfiguration and SCNetworkInterfaceGetExtendedConfiguration, but I've got only null values and SCError returns kSCStatusInvalidArgument.
In case of SCNetworkInterfaceGetExtendedConfiguration
I used values: kSCEntNetIPSec
, kSCEntNetIPv4
, kSCEntNetIPv6
.
Documentation is not precise and clear, so I've found some project where this API is used, this give me some hints, but still doesn't help.
What I'm doing wrong?
Here some test code I'm using to explore API (it is C++ code since I'm porting application to 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;
}
Upvotes: 2
Views: 1407
Reputation: 37647
Thanks @Hofi and @johnelemans for help. This hints was enough to find answer. For others which could have similar problem:
To fetch Mac address SCDynamicStore must be used. From that I can fetch BSD name which is needed later to fetch connection details.
Now use of SCDynamicStore API is a bit strange.
To explore what can be done there is command line tool: scutil
.
It is best to run it without parameters and type this commands:
list
list State:/Network/Interfaces/.*
get State:/Network/Interface/en0/IPv6
d.show
I've found examples here.
Based on information gained from this tool I've come up how to fetch needed data. Test code in 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;
}
Upvotes: 2