bdunagan

Brian Dunagan

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
Cocoa Tip: Extend NSNumber System Preferences Pane Lock
LinkedIn GitHub Email