Reverse DNS on the iPhone
Reverse DNS via the usual OS X means doesn't seem to work on the iPhone. It looks like this is a known bug/limitation. The new apple developer forums (login required) have a thread that's dedicated to the problem. (rdar://problem/5929766 is mentioned there). Apple seems to have used the eraser on some other of their DNS code on the iPhone. Saurik has an interesting hack to work - around a perhaps related change in DNS behavior.
You can actually get reverse DNS lookup on the iPhone to work using res_query (which is in libresolv, so make sure you link against libresolv.dylib) to query DNS and dns_parse_packet, which you'll find in dns_utils.h, to do the work of parsing out the DNS server reply works rather well. I could not get the recommended tools in dns.h to work, but I did discover that res_query returns the same raw reply from the DNS Server that dns_parse_packet expects from the utilities in dns.h. The code:
You can actually get reverse DNS lookup on the iPhone to work using res_query (which is in libresolv, so make sure you link against libresolv.dylib) to query DNS and dns_parse_packet, which you'll find in dns_utils.h, to do the work of parsing out the DNS server reply works rather well. I could not get the recommended tools in dns.h to work, but I did discover that res_query returns the same raw reply from the DNS Server that dns_parse_packet expects from the utilities in dns.h. The code:
#define T_PTR 12 /* domain name pointer */
#define C_IN 1 /* the arpa internet */
static const int kBufLen = 1500;
//takes a string like 1.1.168.192.in-addr.arpa and turns it into the hostname
char *_reverseDns(const char* arpaPoint)
{
int len = -1;
char buffer[kBufLen];
res_init();
int queryType = T_PTR; /* domain name pointer */
int arpanet = C_IN; /* the arpa internet = 1*/
len = res_query(arpaPoint, arpanet, queryType, (u_char *)buffer, kBufLen);
char *resStr;
dns_reply_t *reply;
if (len > 0)
{
reply = dns_parse_packet(buffer, len);
resStr = ((*(reply->answer))->data).PTR->name;
}
else
{
if (errno)
fprintf(stderr, "Error, res_query() error value: %d\n", errno);
if (61 == errno)
resStr = "Could not connect to DNS";
else
resStr = "DNS entry Not Found";
}
return resStr;
}
//makes 192.168.1.1 into 1.1.168.192.in-addr.arpa
NSString *makeDNSLookupString(NSString *inString)
{
NSString *eachString = @"";
NSScanner *scanner = [NSScanner scannerWithString:inString];
NSUInteger scanLocation = 0;
NSUInteger stringLen = [inString length];
NSString *outStr = @"";
while([scanner scanUpToString:@"." intoString:&eachString])
{
eachString = [eachString stringByAppendingString:@"."];
outStr = [eachString stringByAppendingString:outStr];
scanLocation = [scanner scanLocation];
if (scanLocation < stringLen)
[scanner setScanLocation:scanLocation + 1]; // hop the . character
}
outStr = [outStr stringByAppendingString:@"in-addr.arpa"];
return outStr;
}
NSString *reverseDNS(NSString *ipAddress)
{
NSString *arpaString = makeDNSLookupString(ipAddress);
//NSLog(@"arpaString = %@", arpaString);
NSString *returnString = [NSString stringWithCString:_reverseDns([arpaString cStringUsingEncoding:NSASCIIStringEncoding]) encoding:NSASCIIStringEncoding];
return returnString;
}
You can find a simple example app that uses this here.
It also uses a nice trick from The iPhone Developer's Cookbook
Running reverseDNS(@"192.168.1.139") (which is the IP address of my imac) on the iPhone connected via wi-fi to my local network returns @"FreakinIMac" (which is the host name of my imac). It even returns the DD-WRT hostname of my router. It returns @"DNS entry Not Found if it can't find an entry or errs out and it will log errno if set by res_query to the console. You'll probably want to wrap some error handling code around this if you plan to use it in a real app.
Other useful stuff: You can find the source for libresolv here, which was quite helpful. The source to dns.c and dns_util.c proved useful as well. A post on an apple mailing list also pointed me in a direction. Having the source to the libraries you use is a great help. Thanks to Apple for making these available; keep 'em coming!
Note: If you want to log the whole parsed reply from the DNS server, use dns_print_reply (also in dns_utils) with DNS_PRINT_ANSWER as an arg. There is probably a lot more you can do with this method of DNS lookup. I'm guessing you can send pretty much any kind of DNS query and parse the result, just watch out for (and apply his genius patch, if necessary) that bug that Saurik found.
Note: Watching network traffic with a packet sniffer like Wireshark is quite informative.