Search code examples
iosswiftobjective-cdns

iOS Resolve DNS programmatically is returning invalid IPs sometimes


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
    }

Solution

  • 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;
    }