Skip to content

Instantly share code, notes, and snippets.

  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save ptrv/978c419c505a5d178ac3 to your computer and use it in GitHub Desktop.
Lookup IP Address by User Defined Ethernet Name on Mac OS X
//
// includes
//
#import <SystemConfiguration/SystemConfiguration.h>
#import <CoreFoundation/CoreFoundation.h>
#import <arpa/inet.h>
#import <string>
//
// desc: convert IPv4 address from binary format to numbers-and-dots notation in a string
//
// params: networkAddress[in] - IPv4 address in binary format to be converted to string
//
// returns: returns networkAddress in nubers-and-dots notation
//
std::string ConvertNetworkAddressToString(int networkAddress)
{
// convert to string and return it
struct in_addr address_struct;
memset(&address_struct, 0, sizeof(address_struct));
address_struct.s_addr = htonl(networkAddress);
return std::string(inet_ntoa(address_struct));
}
//
// desc: fetch IPv4 address for network service, aka network interface,
// with the user defined name.
//
// params: userDefinedName[in] - user defined name of network service to get IPv4 address for.
// this is whats displayed under System Preferences->Network, for
// example, "Ethernet 1", "WiFi", etc...
//
// returns: returns IPv4 address in numbers-and-dots notation if successful
// returns 0.0.0.0 if failed (logs errors as debug messages to VLog)
//
std::string IPAddressForEthernetInterfaceWithUserDefinedName(const char *userDefinedName)
{
// validate params
if (!userDefinedName) {
return std::string("0.0.0.0");
}
// first check if they want to go to localhost
if (strcmp(userDefinedName, "localhost") == 0) {
return std::string("127.0.0.1"); // just return localhost as destination, and we are done
}
// get network service
SCNetworkServiceRef networkServiceRef = CopyNetworkServiceRefWithUserDefinedName(userDefinedName);
if (!networkServiceRef) {
return std::string("0.0.0.0");
}
// get ip address from network service
CFStringRef address = CopyIPAddressFromNetworkService(networkServiceRef);
if (!address) {
CFRelease(networkServiceRef);
return std::string("0.0.0.0");
}
// validate string
if (CFStringGetLength(address) <= 0) {
CFRelease(address);
CFRelease(networkServiceRef);
return std::string("0.0.0.0");
}
// convert to std::string
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
std::string returnAddress = std::string([(NSString *)address UTF8String]);
[pool drain];
// cleanup
CFRelease(address);
CFRelease(networkServiceRef);
// return ip address
return returnAddress;
}
//
// desc: fetch IPv4 broadcast address for network service, aka network interface, with the user
// defined name.
//
// params: userDefinedName[in] - user defined name of network service to get IPv4 broadcast address for.
// this is whats displayed under System Preferences->Network, for
// example, "Ethernet 1", "WiFi", etc...
//
// returns: returns IPv4 broadcast address in numbers-and-dots notation if successful
// returns 0.0.0.0 if failed (logs errors as debug messages to VLog)
//
std::string BroadcastAddressForEthernetInterfaceWithUserDefinedName(const char *userDefinedName)
{
// validate params
if (!userDefinedName) {
return std::string("0.0.0.0");
}
// first check if they want to go to localhost
if (strcmp(userDefinedName, "localhost") == 0) {
return std::string("127.0.0.1"); // just return localhost as destination, and we are done
}
// get network service
SCNetworkServiceRef networkServiceRef = CopyNetworkServiceRefWithUserDefinedName(userDefinedName);
if (!networkServiceRef) {
return std::string("0.0.0.0");
}
// get ip address from network service
CFStringRef addressString = CopyIPAddressFromNetworkService(networkServiceRef);
if (!addressString) {
CFRelease(networkServiceRef);
return std::string("0.0.0.0");
}
// get subnet mask from network service
CFStringRef subnetMaskString = CopySubnetMaskFromNetworkService(networkServiceRef);
if (!subnetMaskString) {
CFRelease(addressString);
CFRelease(networkServiceRef);
return std::string("0.0.0.0");
}
// validate strings
if (CFStringGetLength(addressString) <= 0 || CFStringGetLength(subnetMaskString) <= 0) {
CFRelease(addressString);
CFRelease(subnetMaskString);
CFRelease(networkServiceRef);
return std::string("0.0.0.0");
}
// generate broadcast address
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
in_addr_t address = htonl(inet_addr([(NSString *)addressString UTF8String]));
in_addr_t subnetMask = htonl(inet_addr([(NSString *)subnetMaskString UTF8String]));
[pool drain];
int broadcastAddresss = address | ~subnetMask;
// cleanup
CFRelease(addressString);
CFRelease(subnetMaskString);
CFRelease(networkServiceRef);
// return ip address
return ConvertNetworkAddressToString(broadcastAddresss);
}
//
// desc: copies the first IPv4 address associated with the network service.
//
// params: networkServiceRef[in] - network service to get IPv4 address for
//
// returns: returns first IPv4 address associated with the network service if successful
// returns NULL if failed (logs errors as debug messages to VLog)
//
CFStringRef CopyIPAddressFromNetworkService(SCNetworkServiceRef networkServiceRef) {
return CopyIPv4PropertyFromNetworkService(kSCPropNetIPv4Addresses, networkServiceRef);
}
//
// desc: copies the first IPv4 subnet mask associated with the network service.
//
// params: networkServiceRef[in] - network service to get IPv4 subnet mask for
//
// returns: returns first IPv4 subnet mask associated with the network service if successful
// returns NULL if failed (logs errors as debug messages to VLog)
//
CFStringRef CopySubnetMaskFromNetworkService(SCNetworkServiceRef networkServiceRef) {
return CopyIPv4PropertyFromNetworkService(kSCPropNetIPv4SubnetMasks, networkServiceRef);
}
//
// desc: copies requested property from network service. this assumes the property your are
// fetching from the network service is an array of strings. it will return the first
// string in the array. done for convience reasons since we generally want one ip address, subnet mask, etc...
// for network service. for example, kSCPropNetIPv4Addresses will return an array of
// all addresses for this network service, but typically we want just the top one in the list.
//
// params: property[in] - property you want from the network service, kSCPropNetIPv4Addresses, kSCPropNetIPv4SubnetMasks, etc...
// networkServiceRef[in] - network service to get property from
//
// returns: returns copy of property value if successful
// returns NULL if failed to retrieve property (logs errors as debug messages to VLog)
//
CFStringRef CopyIPv4PropertyFromNetworkService(CFStringRef property, SCNetworkServiceRef networkServiceRef)
{
// validate parameter
if (!networkServiceRef) {
return NULL;
}
CFDictionaryRef dictionary = CopyDynamicStoreDictionaryForNetworkService(networkServiceRef, kSCEntNetIPv4);
if (!dictionary) {
return NULL;
}
CFStringRef string = GetFirstStringInArrayWithKeyInDictionary(property, dictionary);
if (!string) {
CFRelease(dictionary);
return NULL;
}
// retain the ip address cause we will blitz the dictionary in a sec
CFStringRef stringCopy = CFStringCreateCopy(NULL, string);
// cleanup
CFRelease(dictionary);
return stringCopy;
}
//
// desc: copies the property list for a specific network service
//
// params: dynamicStoreRef[in] - dynamic store used to copy value from, user should create this using SCDynamicStoreCreate()
// networkServiceRef[in] - network service to get property list for
// domain[in] - domain to get property list for (kSCDynamicStoreDomainState, kSCDynamicStoreDomainSetup, etc...)
// key[in] - key to get property list for (kSCEntNetIPv4, kSCEntNetIPv6, etc...)
//
// returns: returns CFPropertListRef contain property list of requested key if successful
// caller must release returned CFPropertyListRef using CFRelease
// returns NULL if failed, check logs for more info
//
CFPropertyListRef CopyDynamicStorePropertyListForNetworkService(SCDynamicStoreRef dynamicStoreRef, SCNetworkServiceRef networkServiceRef, CFStringRef domain, CFStringRef key)
{
// create the key we want
CFStringRef dynamicStoreKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, domain, SCNetworkServiceGetServiceID(networkServiceRef), key);
if (!dynamicStoreKey) {
return NULL;
}
// get property list for key
CFPropertyListRef propertyList = SCDynamicStoreCopyValue(dynamicStoreRef, dynamicStoreKey);
if (!propertyList) {
CFRelease(dynamicStoreKey);
return NULL;
}
CFRelease(dynamicStoreKey);
return propertyList;
}
//
// desc: copies the dynamic store (persistent store) dictionary for the network service.
// if you don't know what i'm talking about, (cause i sure as hell didn't when i
// started trying to figure this stuff out) go here
// ( https://developer.apple.com/library/mac/#documentation/Networking/Conceptual/SystemConfigFrameworks/SC_UnderstandSchema/SC_UnderstandSchema.html#//apple_ref/doc/uid/TP40001065-CH203-CHDIHDCG ).
//
// params: networkServiceRef[in] - network service to get dynamic store dictionary for
//
// returns: returns dictionary if succesfully fetchs property list for network service
// returns NULL if failed (logs errors as debug messages to VLog)
//
CFDictionaryRef CopyDynamicStoreDictionaryForNetworkService(SCNetworkServiceRef networkServiceRef, CFStringRef key)
{
// validate params
if (!networkServiceRef || !key) {
return NULL;
}
// create dynamic store
SCDynamicStoreRef dynamicStoreRef = SCDynamicStoreCreate(NULL, CFSTR("CopyDynamicStoreDictionaryForNetworkService"), NULL, NULL);
if (!dynamicStoreRef) {
return NULL;
}
// try to get it from domain state first
CFPropertyListRef propertyList = CopyDynamicStorePropertyListForNetworkService(dynamicStoreRef, networkServiceRef, kSCDynamicStoreDomainState, key);
if (!propertyList) {
// since that failed, lets try domain setup
propertyList = CopyDynamicStorePropertyListForNetworkService(dynamicStoreRef, networkServiceRef, kSCDynamicStoreDomainSetup, key);
if (!propertyList) {
CFRelease(dynamicStoreRef);
return NULL;
}
}
// make sure this is dictionary type
if (CFGetTypeID(propertyList) != CFDictionaryGetTypeID()) {
CFRelease(propertyList);
CFRelease(dynamicStoreRef);
return NULL;
}
// cleanup
CFRelease(dynamicStoreRef);
// return dictionary
return (CFDictionaryRef)propertyList;
}
//
// desc: copy network service with user defined name.
//
// params: userDefinedName[in] - user defined name of network service to get IPv4 broadcast address for.
// this is whats displayed under System Preferences->Network, for
// example, "Ethernet 1", "WiFi", etc...
//
// returns: returns SCNetworkServiceRef to network service with user defined name is successful
// returns NULL if failed (logs errors as debug messages to VLog)
//
SCNetworkServiceRef CopyNetworkServiceRefWithUserDefinedName(const char *userDefinedName)
{
// validate params
if (!userDefinedName) {
return NULL;
}
// create preferences
SCPreferencesRef preferences = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("PRG"), NULL);
if (!preferences) {
return NULL;
}
// get list of all interface devices
CFArrayRef serviceArray = SCNetworkServiceCopyAll(preferences);
if (!serviceArray) {
CFRelease(preferences);
return NULL;
}
// get array of valid service id's
CFArrayRef validServiceIds = CreateArrayOfValidNetworkServiceIds();
// look for out guy
SCNetworkServiceRef returnNetworkServiceRef = NULL;
for (int i=0; i < CFArrayGetCount(serviceArray); i++) {
SCNetworkServiceRef networkServiceRef = (SCNetworkServiceRef)CFArrayGetValueAtIndex(serviceArray, i);
// if not valid service id, then skip
CFStringRef serviceId = SCNetworkServiceGetServiceID(networkServiceRef);
if (!CFArrayContainsValue(validServiceIds, CFRangeMake(0, CFArrayGetCount(validServiceIds)), serviceId)) {
continue;
}
// if we got a match on service name
NSString *serviceName = (NSString *)SCNetworkServiceGetName(networkServiceRef);
if (strcmp([serviceName UTF8String], userDefinedName) == 0) {
returnNetworkServiceRef = networkServiceRef; // save off our interface so we can return it
CFRetain(returnNetworkServiceRef); // retain object so we don't lose it, up to caller to release
break;
}
}
CFRelease(preferences);
CFRelease(serviceArray);
CFRelease(validServiceIds);
return returnNetworkServiceRef;
}
//
// desc: copy IPv4 protocol with user defined name.
//
// params: userDefinedName[in] - user defined name of network service to get IPv4 broadcast address for.
// this is whats displayed under System Preferences->Network, for
// example, "Ethernet 1", "WiFi", etc...
//
// returns: returns SCNetworkProtocolRef to network service IPv4 protocol with user defined name is successful
// returns NULL if failed (logs errors as debug messages to VLog)
//
SCNetworkProtocolRef CopyIPv4NetworkProtocolWithUserDefinedName(const char *userDefinedName)
{
// validate params
if (!userDefinedName) {
return NULL;
}
SCNetworkServiceRef networkServiceRef = CopyNetworkServiceRefWithUserDefinedName(userDefinedName);
if (!networkServiceRef) {
return NULL;
}
// get ipv4 protocol from service
SCNetworkProtocolRef protocol = SCNetworkServiceCopyProtocol(networkServiceRef, kSCEntNetIPv4);
if (!protocol) {
CFRelease(networkServiceRef);
return NULL;
}
// cleanup
CFRelease(networkServiceRef);
return protocol;
}
//
// desc: convience function for fecthing the first string object in an array of string objects
// that is stored in a |dictionary| with |key| value.
//
// params: key[in] - dictionary key, object for key is expected to be an array of CFStringRef objects
// dictionary[in] - dictionary to look the key up in
//
// returns: returns string of first object in the array with |key| in the |dictionary|
// returns NULL if failed (logs errors as debug messages to VLog)
//
CFStringRef GetFirstStringInArrayWithKeyInDictionary(CFStringRef key, CFDictionaryRef dictionary)
{
// validate params
if (!key || !dictionary) {
return NULL;
}
// fetch value
CFTypeRef object = CFDictionaryGetValue(dictionary, key);
if (!object) {
return NULL;
}
// if this isn't an array, then bail
if (CFGetTypeID(object) != CFArrayGetTypeID()) {
return NULL;
}
// make sure we have items in the array
CFArrayRef array = (CFArrayRef)object;
if (CFArrayGetCount(array) <= 0) {
return NULL;
}
// get first object in list
object = CFArrayGetValueAtIndex(array, 0);
if (!object) {
return NULL;
}
// make sure it's a string
if (CFGetTypeID(object) != CFStringGetTypeID()) {
return NULL;
}
return (CFStringRef)object;
}
//
// desc: create a list of valid network service ids. this are all the network service
// id's listed in the Setup: domain.
//
// returns: returns array of valid network service id's if successful
// it is up to the caller to release this using CFRelease()
// returns NULL if failed to create list of valid network service id's, check logs for more info
//
CFArrayRef CreateArrayOfValidNetworkServiceIds()
{
@autoreleasepool {
CFStringRef pattern = CFSTR("Setup:/Network/Service/[^/]+");
// create dynamic store
SCDynamicStoreRef dynamicStoreRef = SCDynamicStoreCreate(NULL, CFSTR("CreateListOfActiveIPv4NetworkServiceIds"), NULL, NULL);
if (!dynamicStoreRef) {
return NULL;
}
// get property list for key
CFArrayRef keyList = SCDynamicStoreCopyKeyList(dynamicStoreRef, pattern);
if (!keyList) {
CFRelease(dynamicStoreRef);
return NULL;
}
CFRelease(dynamicStoreRef);
// strip prefix "Setup:/Network/Service/" so we have just the service id
NSMutableArray *serviceIdList = [[NSMutableArray alloc] initWithCapacity:CFArrayGetCount(keyList)];
if (!serviceIdList) {
CFRelease(keyList);
return NULL;
}
for (NSString *key in (NSArray *)keyList) {
NSString *serviceId = [key substringFromIndex:[key length] - 36]; // service id should be at end and is 36 characters long, so get that substring from end
[serviceIdList addObject:serviceId];
}
CFRelease(keyList);
return (CFArrayRef)serviceIdList;
}
}
//
// desc: return if a network service id is valid or not. checks
// to see if network service id exist in the Setup: domain to
// detemine if it is valid or not.
//
// params: networkServiceId[in] - network service id, for example "91B9C4B4-BB98-445D-BFDB-2D92DADE3B6B"
//
// returns: returns true if network service id exist in the Setup: domain
// returns false if network service id does NOT exist in the Setup: domain
//
bool IsValidNetworkServiceId(CFStringRef networkServiceId)
{
CFArrayRef validServiceIds = CreateArrayOfValidNetworkServiceIds();
if (!validServiceIds) {
return false;
}
if (CFArrayContainsValue(validServiceIds, CFRangeMake(0, CFArrayGetCount(validServiceIds)), networkServiceId)) {
CFRelease(validServiceIds);
return true;
}
CFRelease(validServiceIds);
return false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment