DNS resolution with IPv6 in Objective C

From Wikichris
Jump to: navigation, search

in Objective C, it's easy to resolve a hostname with NSHost.

[[NSHost hostWithName:@"www.google.com"] address]

Sadly iOS (iPhone) contains only a private version of NSHost.

I found many ways of doing this with other Objects or methods, but all of them got only IPv4 addresses in the results. So here is for the moment the only efficient method I have found.

I first tried to use CFHostStartInfoResolution as did bdunagan, but failed to adapt it to IPv6

getaddrinfo with IPv6 then IPv4

This method is not Asynchronous (all my attempts in using asynchronous methods have failed), consequently before using it I open a Popup alerting this could freeze few seconds with a slow connection.

<source lang="objc"> /**

Give the IPs corresponding to a Hostname

Sometime only 1 IPv4 is shown even if there's more.
Sometime only 1 IPv6 is shown even if there's more.
Certainly due to iOS Memory optimisation when locally cached

@author Christian Gonzalvez, http://wiki.gonzofamily.com
@param hostName A hostname
@return an Array of NSString of all the corresponding IP addresses. The first
is the Canonical name, the following are IPs (all NSString)
*/

+ (NSArray *)addressesForHostname:(NSString *)hostname { const char* hostnameC = [hostname UTF8String];

struct addrinfo hints, *res; struct sockaddr_in *s4; struct sockaddr_in6 *s6; int retval; char buf[64]; NSMutableArray *result; //the array which will be return NSMutableArray *result4; //the array of IPv4, to order them at the end NSString *previousIP = nil;

memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_family = PF_UNSPEC;//AF_INET6; hints.ai_flags = AI_CANONNAME; //AI_ADDRCONFIG, AI_ALL, AI_CANONNAME, AI_NUMERICHOST //AI_NUMERICSERV, AI_PASSIVE, OR AI_V4MAPPED

retval = getaddrinfo(hostnameC, NULL, &hints, &res); if (retval == 0) {

if (res->ai_canonname) { result = [NSMutableArray arrayWithObject:[NSString stringWithUTF8String:res->ai_canonname]]; } else { //it means the DNS didn't know this host return nil; } result4= [NSMutableArray array]; while (res) { switch (res->ai_family){ case AF_INET6: s6 = (struct sockaddr_in6 *)res->ai_addr; if(inet_ntop(res->ai_family, (void *)&(s6->sin6_addr), buf, sizeof(buf)) == NULL) { NSLog(@"inet_ntop failed for v6!\n"); } else { //surprisingly every address is in double, let's add this test if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) { [result addObject:[NSString stringWithUTF8String:buf]]; } } break;

case AF_INET: s4 = (struct sockaddr_in *)res->ai_addr; if(inet_ntop(res->ai_family, (void *)&(s4->sin_addr), buf, sizeof(buf)) == NULL) { NSLog(@"inet_ntop failed for v4!\n"); } else { //surprisingly every address is in double, let's add this test if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) { [result4 addObject:[NSString stringWithUTF8String:buf]]; } } break; default: NSLog(@"Neither IPv4 nor IPv6!");

} //surprisingly every address is in double, let's add this test previousIP = [NSString stringWithUTF8String:buf];

res = res->ai_next; } }else{ NSLog(@"no IP found"); return nil; }

return [result arrayByAddingObjectsFromArray:result4]; }</source>

References

  • CFHostStartInfoResolution use by bdunagan, I failed to adapt it to IPv6