November 28 2009
iPhone Tip: No NSHost
On the iPhone, I miss NSHost. CocoaTouch does contain a private version of this Cocoa class, but again, it is private. Recently, I needed to resolve a DNS name to an IP address in an iPhone app. It’s a one-liner in Cocoa: [[NSHost hostWithName:@”www.google.com”] address]. Not so on the iPhone without NSHost. So, with a little help from Apple’s CFHostSample sample project and their docs on CFHost, I put together this short snippet.
2010-02-09 Update: What about the local IP addresses and ethernet MAC addresses? Building off the code from Zach Waugh (developer of QuickPic), I added two methods for getting the set of IP addresses and ethernet addresses. The wireless connection seems to be en0, while the cellular connection is pdp_ip0. Keep in mind that the IP addresses are what the device believes, not what the outside world sees (What Is My IP?), thanks to NATs.
2010-08-17 Update: What about names for addresses? Again referencing Apple’s CFHostSample, I added a quick wrapper for CFHostGetNames.
// MIT license
// Remember to add CFNetwork.framework to your project using Add=>Existing Frameworks.
#import "BDHost.h"
#import <CFNetwork/CFNetwork.h>
#import <netinet/in.h>
#import <netdb.h>
#import <ifaddrs.h>
#import <arpa/inet.h>
#import <net/ethernet.h>
#import <net/if_dl.h>
@implementation BDHost
+ (NSString *)addressForHostname:(NSString *)hostname {
NSArray *addresses = [BDHost addressesForHostname:hostname];
if ([addresses count] > 0)
return [addresses objectAtIndex:0];
else
return nil;
}
+ (NSArray *)addressesForHostname:(NSString *)hostname {
// Get the addresses for the given hostname.
CFHostRef hostRef = CFHostCreateWithName(kCFAllocatorDefault, (CFStringRef)hostname);
BOOL isSuccess = CFHostStartInfoResolution(hostRef, kCFHostAddresses, nil);
if (!isSuccess) return nil;
CFArrayRef addressesRef = CFHostGetAddressing(hostRef, nil);
if (addressesRef == nil) return nil;
// Convert these addresses into strings.
char ipAddress[INET6_ADDRSTRLEN];
NSMutableArray *addresses = [NSMutableArray array];
CFIndex numAddresses = CFArrayGetCount(addressesRef);
for (CFIndex currentIndex = 0; currentIndex < numAddresses; currentIndex++) {
struct sockaddr *address = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addressesRef, currentIndex));
if (address == nil) return nil;
getnameinfo(address, address->sa_len, ipAddress, INET6_ADDRSTRLEN, nil, 0, NI_NUMERICHOST);
if (ipAddress == nil) return nil;
[addresses addObject:[NSString stringWithCString:ipAddress encoding:NSASCIIStringEncoding]];
}
return addresses;
}
+ (NSString *)hostnameForAddress:(NSString *)address {
NSArray *hostnames = [BDHost hostnamesForAddress:address];
if ([hostnames count] > 0)
return [hostnames objectAtIndex:0];
else
return nil;
}
+ (NSArray *)hostnamesForAddress:(NSString *)address {
// Get the host reference for the given address.
struct addrinfo hints;
struct addrinfo *result = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
int errorStatus = getaddrinfo([address cStringUsingEncoding:NSASCIIStringEncoding], NULL, &hints, &result);
if (errorStatus != 0) return nil;
CFDataRef addressRef = CFDataCreate(NULL, (UInt8 *)result->ai_addr, result->ai_addrlen);
if (addressRef == nil) return nil;
freeaddrinfo(result);
CFHostRef hostRef = CFHostCreateWithAddress(kCFAllocatorDefault, addressRef);
if (hostRef == nil) return nil;
CFRelease(addressRef);
BOOL isSuccess = CFHostStartInfoResolution(hostRef, kCFHostNames, NULL);
if (!isSuccess) return nil;
// Get the hostnames for the host reference.
CFArrayRef hostnamesRef = CFHostGetNames(hostRef, NULL);
NSMutableArray *hostnames = [NSMutableArray array];
for (int currentIndex = 0; currentIndex < [(NSArray *)hostnamesRef count]; currentIndex++) {
[hostnames addObject:[(NSArray *)hostnamesRef objectAtIndex:currentIndex]];
}
return hostnames;
}
+ (NSArray *)ipAddresses {
NSMutableArray *addresses = [NSMutableArray array];
struct ifaddrs *interfaces = NULL;
struct ifaddrs *currentAddress = NULL;
int success = getifaddrs(&interfaces);
if (success == 0) {
currentAddress = interfaces;
while(currentAddress != NULL) {
if(currentAddress->ifa_addr->sa_family == AF_INET) {
NSString *address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)currentAddress->ifa_addr)->sin_addr)];
if (![address isEqual:@"127.0.0.1"]) {
NSLog(@"%@ ip: %@", [NSString stringWithUTF8String:currentAddress->ifa_name], address);
[addresses addObject:address];
}
}
currentAddress = currentAddress->ifa_next;
}
}
freeifaddrs(interfaces);
return addresses;
}
+ (NSArray *)ethernetAddresses {
NSMutableArray *addresses = [NSMutableArray array];
struct ifaddrs *interfaces = NULL;
struct ifaddrs *currentAddress = NULL;
int success = getifaddrs(&interfaces);
if (success == 0) {
currentAddress = interfaces;
while(currentAddress != NULL) {
if(currentAddress->ifa_addr->sa_family == AF_LINK) {
NSString *address = [NSString stringWithUTF8String:ether_ntoa((const struct ether_addr *)LLADDR((struct sockaddr_dl *)currentAddress->ifa_addr))];
// ether_ntoa doesn't format the ethernet address with padding.
char paddedAddress[80];
int a,b,c,d,e,f;
sscanf([address UTF8String], "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
sprintf(paddedAddress, "%02X:%02X:%02X:%02X:%02X:%02X",a,b,c,d,e,f);
address = [NSString stringWithUTF8String:paddedAddress];
if (![address isEqual:@"00:00:00:00:00:00"] && ![address isEqual:@"00:00:00:00:00:FF"]) {
NSLog(@"%@ mac: %@", [NSString stringWithUTF8String:currentAddress->ifa_name], address);
[addresses addObject:address];
}
}
currentAddress = currentAddress->ifa_next;
}
}
freeifaddrs(interfaces);
return addresses;
}
@end