Created
February 26, 2015 16:24
-
-
Save ptrv/978c419c505a5d178ac3 to your computer and use it in GitHub Desktop.
Lookup IP Address by User Defined Ethernet Name on Mac OS X
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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