Hi,
I am working on an iOS app which requires to resolve DNS programmatically.
Consider this as a proxy to resolve all dns queries on iPhone. I receive DNS queries from each app on iPhone and send back corresponding IPList
I have tried a couple of methods but both have same kind of responses. The one I decided to move with is given below resolveHost function written in objective-c and c I am calling this method from swift code.
This is how I am calling from swift, also sharing sample host/url, value of host can be any domain ("google.com, apple.com etc") or a domain/host as a result of trails when you open a site in mkwebview
let host = "www.opera.com"
let ipArray = ResolveUtil().resolveHost(host, usingDNSServer: "8.8.8.8") as! [String]
More specifically Facebook app does not work well with IPs returned from function resolveHost
By not working well I mean app does not connect to IPs returned from the functions
Some times it returns 192.16.192.16 as part of other IPs for some hosts/domains. What is this IP?
- (NSArray*)resolveHost:(NSString *)host usingDNSServer:(NSString *)dnsServer
{
NSMutableArray* result = [[NSMutableArray alloc] init];
struct __res_state res;
setup_dns_server(&res, [dnsServer cStringUsingEncoding:NSASCIIStringEncoding]);
int count;
char** ips = query_ips(&res, [host cStringUsingEncoding:NSUTF8StringEncoding], &count);
for (int i=0; i<count; i++){
[result addObject:[[NSString alloc] initWithCString:ips[i] encoding:NSASCIIStringEncoding]];
}
for (int i=0; i<count; i++){
free(ips[i]);
}
free(ips);
ips = NULL;
return result;
}
char ** query_ips(res_state res, const char *host, int* count)
{
u_char answer[NS_PACKETSZ];
int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
int messageCount = ns_msg_count(handle, ns_s_an);
*count = messageCount;
char **ips = malloc(messageCount * sizeof(char *));
for (int i=0; i < messageCount; i++) {
ips[i] = malloc(16 * sizeof(char));
memset(ips[i], '\0', sizeof(16));
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
strcpy(ips[i], inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
}
}
return ips;
}
Other Method
func resolveIp(_ hostUrl:String) -> [String]{
var ips:[String] = [String]()
let host = CFHostCreateWithName(nil,hostUrl as CFString).takeRetainedValue()
CFHostStartInfoResolution(host, .addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray? {
for case let theAddress as NSData in addresses {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
&hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
let numAddress = String(cString: hostname)
ips.append(numAddress)
}
}
}
Logger.info("\(#function) validIPs:\(ips.joined(separator: "-")) url:\(hostUrl)")
return ips
}
I might have not explained the problem statement in my original question but I managed to fix the bug, so I thought I should write here my findings. My app works as a dns proxy, so its main responsibility was to resolve domains and return IPs.
I used resolveHost function to resolve the IP. This function has all the issues mentioned by zrzka so if somebody wants to use please do consider his points.
The problem I had was that the function returns a few IPs against specific hosts/domains which does not seem valid, I am saying invalid because these were not pingable IPs and from Wireshark I confirmed connection on these IPs were unsuccessful, even if returned IPList contains valid IP at some index it was still causing unnecessary delay due to first try on invalid IPs as they reside before valid IPs in the list.
On further investigation I came to know these invalid IPs were against answer type CNAME which depicts Alias in DNS record, I don't know I should still call them invalid or not but ignoring them did the job for me. Now I only accept A type or AAAA type answers from DNS response. I have achieved this by a simple check in the following function.
char ** query_ips(res_state res, const char *host, int* count)
{
u_char answer[NS_PACKETSZ];
int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
int messageCount = ns_msg_count(handle, ns_s_an);
*count = messageCount;
char **ips = malloc(messageCount * sizeof(char *));
for (int i=0; i < messageCount; i++) {
ips[i] = malloc(16 * sizeof(char));
memset(ips[i], '\0', sizeof(16));
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
if (1 == rr.type || 28 == rr.type) // here is the new check
strcpy(ips[i], inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
}
}
return ips;
}