5

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.

MegaManX
  • 8,766
  • 12
  • 51
  • 83
  • Your question is not very clear, can you provide a specific example? Also the DNS does not resolve URLs to IP addresses but hostnames to IP addresses. The fact of using an IPv6 available recursive nameserver to have, or not, `AAAA` records in the answer are separate: you can query a nameserver over IPv4 and still get back `AAAA` records. The content transmitted is orthogonal to the transport used to transmit it. – Patrick Mevzek Jun 05 '18 at 15:12
  • Also what are tags `ios`, `objective-c` and `swift` doing there? You are not providing any piece of code you wish us to see and discuss so you are kind of off-topic here. See instead [Webmasters.se], [su] or [sf]. – Patrick Mevzek Jun 05 '18 at 15:12
  • Let me clear that a liitle bit – MegaManX Jun 05 '18 at 15:14
  • This does not explain why you specifically want 1) first Google (they are far from the only Public DNS resolvers out there and 2) more importantly, specifically under IPv6 transport? If the zone you are interested in publish some `AAAA` records you will get them whatever IP version you use to query the recursive nameserver. Your question is hence still not clear, nor the reasons for the tags you use. Otherwise for specific DNS "retargeting" per zone, see `dnsmasq`. – Patrick Mevzek Jun 05 '18 at 15:17

3 Answers3

3

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.

Stephan Schlecht
  • 26,556
  • 1
  • 33
  • 47
2

You can do this using below swift code -

import Foundation
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = ["dig", "@8.8.8.8", "google.com"]

let pipe = Pipe()
task.standardOutput = pipe
task.launch()

let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)

print(output!)

In the above code use the DNS server of your choice by replacing 8.8.8.8

For Objective-C iOS refer below link - https://www.software7.com/blog/programmatically-query-specific-dns-servers-on-ios/

mdeora
  • 4,152
  • 2
  • 19
  • 29
  • This is for OSX, I need it for iOS. Process() is undefined on iOS – MegaManX Jun 06 '18 at 15:11
  • looks like its not straight forward for iOS, though I have updated the answer for iOS. – mdeora Jun 06 '18 at 15:58
  • 1
    making it to work for IPV6 should be simple, let me know if you are facing any specific issue ? – mdeora Jun 06 '18 at 19:51
  • Also just encountered this - https://developer.apple.com/documentation/networkextension/nednssettings it has a function to set the DNS servers, and ipv6 complaint too – mdeora Jun 07 '18 at 05:50
  • yes, I am unable to modify this solution to work with IPv6 google DNS which is 2001:4860:4860::8888. What gives me trouble is this line int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer)); Here I always get -1, for len. I will update my question with details – MegaManX Jun 07 '18 at 07:59
  • inet_pton(AF_INET6, dns_server, &addr); what does this return ? if its not returning 1, than thats something needs to be looked into. – mdeora Jun 07 '18 at 11:46
  • that part is ok, it returns 1... but res_nquery is returning len = -1 – MegaManX Jun 07 '18 at 11:59
  • Can you verify if your network supports IPV6 transmission? I mean can you use a IPv6 DNS server to resolve any host from your device ? – mdeora Jun 07 '18 at 12:18
  • there is a issue in your code - sin_family should also be set to - AF_INET6 – mdeora Jun 07 '18 at 12:20
  • also set the struct in_addr to in6_addr – mdeora Jun 07 '18 at 12:28
  • Yup, i have verified that my network supports IPv6. I tested from my mac. Also changing family to AF_INET6 does nothing. And if I change n_addr to in6_addr , than i get compiler error for res->nsaddr_list[0].sin_addr = addr. I have tried many combinations for now, but to no avail – MegaManX Jun 07 '18 at 12:39
1

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.

mdeora
  • 4,152
  • 2
  • 19
  • 29
  • res->nsaddr_list[0].sin_addr = addr; Tried this also myself, but it won't compile. In documents for res_state, i see there is some extension for IPv6, but I don't know how to configure it properly – MegaManX Jun 07 '18 at 12:41
  • what ios SDK your are on ? – mdeora Jun 07 '18 at 12:52
  • it seems like in_addr to be replaced with in_addr6 from the below apple documentation. https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html – mdeora Jun 07 '18 at 13:15
  • iOS11 SDK... It can not assign in6_addr to in_addr – MegaManX Jun 07 '18 at 13:28
  • 1
    hey, you might want to look at this until i get my hands on this code - https://github.com/Bouke/DNS/tree/feature/resolver/Sources Someone is trying to implement it in Swift. – mdeora Jun 08 '18 at 10:07