How to get full DNS responses from the system resolver? DNSServiceQueryRecord not returning errors..

I would like to get the full DNS responses from the system resolver. I'm using DNSServiceQueryRecord, but I can't get negative responses. How do I get the negative responses?

I need the full response because they have clues about network-level censorship. For instance, mismatched case in the name, bad answer RR type, missing SOA record on no answers response.

On Android I can use android_res_nquery, but I couldn't find anything similar on iOS and macOS. The closest I found was DNSServiceQueryRecord, which at least gives me resource records, so I can inspect TTL and name case.

After some struggle, I was able to make it work. I'm using Go with cgo for that: https://github.com/fortuna/gio-test/blob/fortuna-dns/sysresolver_darwin.go https://github.com/fortuna/gio-test/blob/fortuna-dns/sysresolver_darwin_export.go

My sequence of calls is:

DNSServiceQueryRecord(sdRef, 0, 0, fullname, rrtype, rrclass, (DNSServiceQueryRecordReply)goCallback, context);

fd := C.DNSServiceRefSockFD(sdRef)

nReady, err := unix.Poll([]unix.PollFd{{Fd: int32(fd), Events: unix.POLLIN | unix.POLLERR | unix.POLLHUP}}, timeout)

serviceErr = C.DNSServiceProcessResult(sdRef)

// Here my callback gets called, multiple times for multiple answers.

C.DNSServiceRefDeallocate(sdRef)

I'm able to get positive answers, even multiple answers. But the Poll doesn't return when there are no answers (like for CNAME www.example.com). I expected the poll to return on negative answers, and my callback to be called with an error when calling DNSServiceProcessResult.

Is that not the expected behavior? How do I get notified that a query has no answers?

  • Another data point: I can see the request being sent on Wireshark, and the negative response coming back, but my code never gets notified about it.

Add a Comment

Replies

I made significant progress. It turned out I need the flag kDNSServiceFlagsReturnIntermediates to return errors and intermediate CNAME records. (I was already checking kDNSServiceFlagsMoreComing in the callback to keep iterating over the answers)

The flags documentation is completely missing, but the source code lists all the flags, with comprehensive comments: https://opensource.apple.com/source/mDNSResponder/mDNSResponder-878.70.2/mDNSShared/dns_sd.h.auto.html

I'm almost there! I can now get negative responses. However, there's still a problem. If the system already has a CNAME chain cached, but not the address record, it will return the CNAME chain immediately, with the last CNAME record with the kDNSServiceFlagsMoreComing == 0. I believe the A/AAAA record will come later, but with the "more coming" == 0 I can't tell there will be more results. So should I wait, or should I consider that an error?

Next, I'm going to try stopping only once the response record type matches the question, or I hit an error.