So I did some tests with DNSServiceGetAddrInfo, but then realized we need to get SRV records as well. So I performed some initial tests using DNSServiceQueryRecord. Here is the sample code I used for the test-
#import <dns_sd.h>
#include <stdio.h>
#include <string.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <dns_util.h>
// no memory management, all vars valid for the lifetime of the app
static FILE *logFile;
static struct ifaddrs *ifaddr;
#pragma pack(push, 1)
typedef struct resource_record
{
uint8_t zero;
uint16_t rrtype;
uint16_t rrclass;
uint32_t ttl;
uint16_t rdlen;
char rdata[1];
} resource_record;
#pragma pack(pop)
void dnsQueryRecordCallback (DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context)
{
fprintf(logFile, "Response from index:%d error:%d context:%s", interfaceIndex, errorCode, context);
uint32_t resource_size = sizeof(resource_record) + rdlen;
resource_record *resource = (resource_record *)calloc(resource_size, 1);
resource->zero = 0;
resource->rrtype = htons(rrtype);
resource->rrclass = htons(rrclass);
resource->ttl = htonl(ttl);
resource->rdlen = htons(rdlen);
memcpy(resource->rdata, rdata, rdlen);
dns_resource_record_t * rr = dns_parse_resource_record((const char *)resource, resource_size);
if (rr)
{
switch(rrtype)
{
case kDNSServiceType_A:
{
char buf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(rr->data.A->addr.s_addr), buf, INET_ADDRSTRLEN);
fprintf(logFile, " IPv4:%s", buf);
}
break;
case kDNSServiceType_AAAA:
{
char buf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(rr->data.AAAA->addr.s6_addr), buf, INET6_ADDRSTRLEN);
fprintf(logFile, " IPv6:%s", buf);
}
break;
case kDNSServiceType_SRV:
fprintf(logFile, " target:%s port:%d priority:%d weight:%d", rr->data.SRV->target, rr->data.SRV->port, rr->data.SRV->priority, rr->data.SRV->weight);
break;
default:
fprintf(logFile, " type:%u", rrtype);
break;
}
}
fprintf(logFile, "\n");
}
void logQuery(const char *fqdn, uint16_t rrtype)
{
fprintf(logFile, "Querying %s ", fqdn);
switch (rrtype)
{
case kDNSServiceType_A:
fprintf(logFile, "A");
break;
case kDNSServiceType_AAAA:
fprintf(logFile, "AAAA");
break;
case kDNSServiceType_SRV:
fprintf(logFile, "SRV");
break;
default:
fprintf(logFile, "%u", rrtype);
break;
}
fprintf(logFile, " Record on ");
}
void dnsQueryRecord(FILE *outLogFile, const char *fqdn, uint16_t rrtype, bool queryAny, bool queryByIndex)
{
logFile = outLogFile ? outLogFile : stdout;
rrtype = rrtype == 0 ? kDNSServiceType_AAAA : rrtype;
logQuery(fqdn, rrtype);
if (queryAny)
{
DNSServiceRef sdRefAll;
DNSServiceErrorType error;
if ((error = DNSServiceQueryRecord(&sdRefAll,
0,
0,
fqdn,
rrtype,
kDNSServiceClass_IN,
dnsQueryRecordCallback,
"Any")) == kDNSServiceErr_NoError)
{
DNSServiceSetDispatchQueue(sdRefAll, dispatch_get_main_queue());
}
fprintf(logFile, "Any retcode:%d\n", error);
}
if (queryByIndex)
{
if (getifaddrs(&ifaddr) != -1)
{
struct ifaddrs *ifa;
char *last_if_name = "";
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (strcmp(last_if_name, ifa->ifa_name) != 0)
{
logQuery(fqdn, rrtype);
last_if_name = ifa->ifa_name;
unsigned int index = if_nametoindex(ifa->ifa_name);
unsigned short flags = ifa->ifa_flags;
fprintf(logFile, "index:%d name:%s", index, ifa->ifa_name);
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd != -1)
{
struct ifreq ifr;
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ - 1);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1)
{
flags = ifr.ifr_flags;
}
close(fd);
}
fprintf(logFile, " isUp:%s", flags & IFF_UP ? "Yes" : "No");
if (flags & IFF_UP)
{
DNSServiceRef sdRef;
DNSServiceErrorType error;
if ((error = DNSServiceQueryRecord(&sdRef,
0,
index,
fqdn,
rrtype,
kDNSServiceClass_IN,
dnsQueryRecordCallback,
ifa->ifa_name)) == kDNSServiceErr_NoError)
{
DNSServiceSetDispatchQueue(sdRef, dispatch_get_main_queue());
fprintf(logFile, " start");
}
fprintf(logFile, " retcode:%d", error);
}
fprintf(logFile, "\n");
}
}
}
}
}
My initial tests where done with an IPv6 address on Cell and IPv4 address on Wifi. I made two different calls
dnsQueryRecord(NULL, "apple.com", 0, true, true);
This will return the AAAA records for Any as well as all interfaces that are up. Since apple.com doesn't have a AAAA record, it will return a synthesized record for the NAT64. The results were-
Querying apple.com AAAA Record on Any retcode:0
Querying apple.com AAAA Record on index:1 name:lo0 isUp:Yes retcode:-65540
Querying apple.com AAAA Record on index:2 name:pdp_ip0 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:3 name:pdp_ip3 isUp:No
Querying apple.com AAAA Record on index:4 name:pdp_ip1 isUp:No
Querying apple.com AAAA Record on index:5 name:pdp_ip2 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:6 name:pdp_ip4 isUp:No
Querying apple.com AAAA Record on index:7 name:ap1 isUp:No
Querying apple.com AAAA Record on index:8 name:en0 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:9 name:en1 isUp:No
Querying apple.com AAAA Record on index:10 name:en3 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:11 name:awdl0 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:12 name:ipsec0 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:13 name:ipsec1 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:14 name:ipsec2 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:15 name:utun0 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:16 name:ipsec3 isUp:Yes start retcode:0
Querying apple.com AAAA Record on index:17 name:ipsec4 isUp:Yes start retcode:0
Response from index:0 error:0 context:pdp_ip0 IPv6:2604:5580:22::118e:a03b
Response from index:0 error:0 context:pdp_ip0 IPv6:2604:5580:22::11ac:e02f
Response from index:0 error:0 context:pdp_ip0 IPv6:2604:5580:22::11b2:603b
So the response came back from only the IPv6 interface. Take note: the value interfaceIndex in the callback is always zero, but the documentation states that it should be the index of interface responding (in this case 2). I noticed the same behavior with DNSServiceGetAddrInfo as well that the passed value is always 0.
Next I called with: (which queries the A record)
dnsQueryRecord(NULL, "apple.com", kDNSServiceType_A, true, true);
Querying apple.com A Record on index:3 name:pdp_ip3 isUp:No
Querying apple.com A Record on index:4 name:pdp_ip1 isUp:No
Querying apple.com A Record on index:5 name:pdp_ip2 isUp:Yes start retcode:0
Querying apple.com A Record on index:6 name:pdp_ip4 isUp:No
Querying apple.com A Record on index:7 name:ap1 isUp:No
Querying apple.com A Record on index:8 name:en0 isUp:Yes start retcode:0
Querying apple.com A Record on index:9 name:en1 isUp:No
Querying apple.com A Record on index:10 name:en3 isUp:Yes start retcode:0
Querying apple.com A Record on index:11 name:awdl0 isUp:Yes start retcode:0
Querying apple.com A Record on index:12 name:ipsec0 isUp:Yes start retcode:0
Querying apple.com A Record on index:13 name:ipsec1 isUp:Yes start retcode:0
Querying apple.com A Record on index:14 name:ipsec2 isUp:Yes start retcode:0
Querying apple.com A Record on index:15 name:utun0 isUp:Yes start retcode:0
Querying apple.com A Record on index:16 name:ipsec3 isUp:Yes start retcode:0
Querying apple.com A Record on index:17 name:ipsec4 isUp:Yes start retcode:0
Response from index:0 error:0 context:Any IPv4:17.178.96.59
Response from index:0 error:0 context:Any IPv4:17.142.160.59
Response from index:0 error:0 context:Any IPv4:17.172.224.47
Response from index:0 error:0 context:pdp_ip0 IPv4:17.172.224.47
Response from index:0 error:0 context:pdp_ip0 IPv4:17.178.96.59
Response from index:0 error:0 context:pdp_ip0 IPv4:17.142.160.59
Response from index:0 error:0 context:en0 IPv4:17.142.160.59
Response from index:0 error:0 context:en0 IPv4:17.172.224.47
Response from index:0 error:0 context:en0 IPv4:17.178.96.59
This time the Any, cell and WiFi interface responded with an A record. This seems correct. The Any interface seems to be the default route (in this case WiFi). Also the interfaceIndex parameters is always 0 again.
-----------------
So to answer your question, I can use the system DNS to query on a per interface. Except for the interfaceIndex parameter being always zero (this can be worked around by using the context parameter to determine the interface), it seems to be working. I'll do more tests with two IPv6 interfaces to make sure I get proper synthesized AAAA records.
It still would be nice to get the DNS server IPs from the interface as well. Specifically for logging purposes to diagnose network setups where the DNS server could return a different IPv4 address for each interface.