Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS: How to specify DNS to be used to resolve hostname to IP address?

As the title says I have hostname (eg www.example.com) that I want to resolve using specified DNS server. For example in one case I want to use google's IPv4 DNS and in other case google's IPv6 DNS.

I have browsed SO for something like this on iOS, and found questions like this one (Swift - Get device's IP Address), so I am sure it can be done, but I am unclear how?

How can I do this?

EDIT 06/07/2018

@mdeora suggested solution from http://www.software7.com/blog/programmatically-query-specific-dns-servers-on-ios/

This solution works but only if I use IPv4 DNS, for example google's "8.8.8.8". If I try to use IPv6 DNS 2001:4860:4860::8888, i get nothing.

I have managed to change conversion:

void setup_dns_server(res_state res, const char *dns_server)
{
    res_ninit(res);
    struct in_addr addr;

//    int returnValue = inet_aton(dns_server, &addr);
    inet_pton(AF_INET6, dns_server, &addr); // for IPv6 conversion

    res->nsaddr_list[0].sin_addr = addr;
    res->nsaddr_list[0].sin_family = AF_INET;
    res->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
    res->nscount = 1;

};

But still have trouble with this:

void query_ip(res_state res, const char *host, char ip[])
{
    u_char answer[NS_PACKETSZ];//NS_IN6ADDRSZ 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);


    if(ns_msg_count(handle, ns_s_an) > 0) {
        ns_rr rr;
        if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) {
            strcpy(ip, inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
        }
    }
}

I get -1 for len. From what I gather it seems I need to configure res_state for IPv6.

like image 504
MegaManX Avatar asked Jun 05 '18 15:06

MegaManX


2 Answers

Here the code from my blogpost, that was already mentioned above, just slightly adapted to use IPv6.

Adapt setup_dns_server

First we could start with the changes to setup_dns_server:

void setup_dns_server(res_state res, const char *dns_server) {
    struct in6_addr addr;

    inet_pton(AF_INET6, dns_server, &addr);

    res->_u._ext.ext->nsaddrs[0].sin6.sin6_addr = addr;
    res->_u._ext.ext->nsaddrs[0].sin6.sin6_family = AF_INET6;
    res->_u._ext.ext->nsaddrs[0].sin6.sin6_port = htons(NS_DEFAULTPORT);
    res->nscount = 1;
}

Add __res_state_ext

This wouldn't compile because of a missing struct __res_state_ext. This structure is unfortunately in a private header file.

But the definition of that one can be take from here: https://opensource.apple.com/source/libresolv/libresolv-65/res_private.h.auto.html :

struct __res_state_ext {
    union res_sockaddr_union nsaddrs[MAXNS];
    struct sort_list {
        int af;
        union {
            struct in_addr  ina;
            struct in6_addr in6a;
        } addr, mask;
    } sort_list[MAXRESOLVSORT];
    char nsuffix[64];
    char bsuffix[64];
    char nsuffix2[64];
};

The struct can be added e.g. at the top of the file.

Adapt resolveHost

The changes here include the longer buffer for ip (INET6_ADDRSTRLEN). res_ninit moved from setup_dns_server into this method and is matched now with a res_ndestroy.

+ (NSString *)resolveHost:(NSString *)host usingDNSServer:(NSString *)dnsServer {
    struct __res_state res;
    char ip[INET6_ADDRSTRLEN];
    memset(ip, '\0', sizeof(ip));

    res_ninit(&res);
    setup_dns_server(&res, [dnsServer cStringUsingEncoding:NSASCIIStringEncoding]);
    query_ip(&res, [host cStringUsingEncoding:NSUTF8StringEncoding], ip);
    res_ndestroy(&res);

    return [[NSString alloc] initWithCString:ip encoding:NSASCIIStringEncoding];
}

Retrieving IPv6 addresses

The changes above are already sufficient if you just want to use a IPv6 address for your DNS server. So in query_ip there are no changes necessary if you still want to retrieve the IPv4 addresses.

In case you would like to retrieve IPv6 addresses from the DNS server also, you can do this:

void query_ip(res_state res, const char *host, char ip[]) {
    u_char answer[NS_PACKETSZ];
    int len = res_nquery(res, host, ns_c_in, ns_t_aaaa, answer, sizeof(answer));

    ns_msg handle;
    ns_initparse(answer, len, &handle);


    if(ns_msg_count(handle, ns_s_an) > 0) {
        ns_rr rr;
        if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) {
            inet_ntop(AF_INET6, ns_rr_rdata(rr), ip, INET6_ADDRSTRLEN);
        }
    }
}

Please note: we use here ns_t_aaaa to get AAAA resource records (quad-A record), because in DNS this specifies the mapping between IPv6 address and hostname. For many hosts, there is no such quad-A record, meaning you can just reach them via IPv4.

Call

You would call it e.g. like so:

NSString *resolved = [ResolveUtil resolveHost:@"www.google.com" usingDNSServer:@"2001:4860:4860::8888"];
NSLog(@"%@", resolved);

The result would the look like this:

test output

Disclaimer

These are just simple example calls, that demonstrate the basic usage of the functions. There is no error handling.

like image 184
Stephan Schlecht Avatar answered Oct 23 '22 18:10

Stephan Schlecht


Below is the revised code for setting up dns -

void setup_dns_server(res_state res, const char *dns_server)
    {
        res_ninit(res);
        struct in_addr6 addr;

    //    int returnValue = inet_aton(dns_server, &addr);
        inet_pton(AF_INET6, dns_server, &addr); // for IPv6 conversion

        res->nsaddr_list[0].sin_addr = addr;
        res->nsaddr_list[0].sin_family = AF_INET6;
        res->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
        res->nscount = 1;

    };

And the query code -

void query_ip(res_state res, const char *host, char ip[])
{
    u_char answer[NS_PACKETSZ];//NS_IN6ADDRSZ 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);


    if(ns_msg_count(handle, ns_s_an) > 0) {
        ns_rr rr;
        if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) {
            strcpy(ip, inet_ntoa(*(struct in_addr6 *)ns_rr_rdata(rr)));
        }
    }
}

PS - I have not been able to test it, but it should work for ipv6 dns.

like image 27
mdeora Avatar answered Oct 23 '22 19:10

mdeora