DNSServiceSetDispatchQueue with DNSServiceGetAddrInfo does not return callback

I was trying to implement FQDN to Address resolution using DNSServiceGetAddrInfo API of DNS_SD. Learning from this thread I wanted to use DNSServiceSetDispatchQueue to get results back asynchronously. Here is how I have implemented it.


var result = DNSServiceGetAddrInfo(&dnsServiceRef, kDNSServiceFlagsTimeout, 0, DNSServiceFlags(kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6), hostName.cString(using: .utf8), dnssd_callback, pointer)
        if result != kDNSServiceErr_NoError || dnsServiceRef == nil {
            delegate?.didFinishDNSResolution(.failed(.error(from: result)))
            stopResolution()
        } else {
            result = DNSServiceSetDispatchQueue(dnsServiceRef, DispatchQueue.main)
            if result != kDNSServiceErr_NoError {
                delegate?.didFinishDNSResolution(.failed(.error(from: result)))
                stopResolution()
            }
        }

However, using this implementation I do not get callbacks. If I use DNSServiceProcessResult, I am getting responses successfully. If I call DNSServiceProcessResult after DNSServiceSetDispatchQueue, then also I receive callbacks successfully. But, that is discouraged in the documentation of DNSServiceSetDispatchQueue.


> * After using DNSServiceSetDispatchQueue on a DNSServiceRef, calling DNSServiceProcessResult

* on the same DNSServiceRef will result in undefined behavior and should be avoided.


I tried DNSSDObjects and SRVResolver (with a bit change DNSServiceSetDispatchQueue) samples and they both work pretty fine. But when I use the same in my code, it does not work. So, is it possible that DNSServiceSetDispatchQueue can be used with every DNS_SD API except for DNSServiceGetAddrInfo? Or is there a missing piece?


Any help would be greatly appreciated.

Accepted Reply

We have a server issue where we need to resolve FQDN and connect with IP address due to session details not being synced.

Be warned, connecting by IP address is likely to cause problems in certain situations (DNS64/NAT64, VPN On Demand, and so on).

Also, what platform are you targeting?

Can we use

DNSServiceGetAddrInfo
API instead of
CFHost
API for address resolution?

Yes.

Which one should be preferred and why?

Both should work equally well. I prefer

CFHost
because its API fits in with the platform better (it works just like any of the other bazillion run loop based APIs out there).

Why

DNSServiceGetAddrInfo
does not work with
DNSServiceSetDispatchQueue
?

I’m not sure what you’re doing wrong here. Pasted in at the end of this response is some code that I wrote to do this. When I put that in a test tool project and run it on a Mac here in my office, it prints the following:

will start resolve
did start resolve
did resolve example.com. to 2606:2800:220:1:248:1893:25c8:1946
did resolve example.com. to 93.184.216.34

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
import Foundation
import dnssd

func main() {
    print("will start resolve")
    var sdRefQ: DNSServiceRef? = nil
    let err = DNSServiceGetAddrInfo(&sdRefQ, 0, 0, DNSServiceProtocol(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6), "example.com", { (_, _, _, err, hostnameQ, saQ, _, _) in
        guard err == kDNSServiceErr_NoError else {
            print("did not resolve, resolve failed, err: \(err)")
            return
        }
        let hostStr = String(cString: hostnameQ!)
        let sa = saQ!
        var addr = [CChar](repeating: 0, count: Int(NI_MAXHOST))
        let err2 = getnameinfo(sa, socklen_t(sa.pointee.sa_len), &addr, socklen_t(addr.count), nil, 0, NI_NUMERICHOST | NI_NUMERICSERV)
        guard err2 == 0 else {
            print("did not resolve, address-to-string failed, err: \(err)")
            return
        }
        let addrStr = String(cString: addr)
        print("did resolve \(hostStr) to \(addrStr)")
    }, nil)
    guard err == kDNSServiceErr_NoError else {
        print(err)
        return
    }
    let sdRef = sdRefQ!
    let junk = DNSServiceSetDispatchQueue(sdRef, .main)
    assert(junk == kDNSServiceErr_NoError)
    print("did start resolve")
    dispatchMain()
}

main()
exit(EXIT_SUCCESS)

Replies

I was trying to implement FQDN to Address resolution using

DNSServiceGetAddrInfo
API of DNS_SD.

Is there a reason you need to do this resolution using this very low-level API? There are good reasons to use

DNSServiceGetAddrInfo
, but most of the time you’re better served by one of the alternatives:
  • If you’re making a network connection, you should aim to use one of our connect-by-name APIs. These do the resolution for you, and handle a bunch of ‘fun’ edge cases.

  • If you want to use map a DNS name to a set of addresses for other reasons — say for logging purposes — you can use

    CFHost
    . See CFHostSample for the details.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for your response. The reasons I was trying dns_sd API are that:


  • We have a server issue where we need to resolve FQDN and connect with IP address due to session details not being synced.
  • I did try CFHost APIs for DNS resolution but we found a memory leak in that implementation, which wasn't too difficult to fix but we were looking for other options as well. Thats when we found this DNS_SD API which we can use to query server address and we thoughts of giving it a try. Also, DNSServiceGetAddrInfo with DNSServiceSetDispatchQueue looked way simpler than CFHost APIs.

So, my questions are:

  • Can we use DNSServiceGetAddrInfo API instead of CFHost API for address resolution?
  • Which one should be preferred and why?
  • The original question: Why DNSServiceGetAddrInfo does not work with DNSServiceSetDispatchQueue?

We have a server issue where we need to resolve FQDN and connect with IP address due to session details not being synced.

Be warned, connecting by IP address is likely to cause problems in certain situations (DNS64/NAT64, VPN On Demand, and so on).

Also, what platform are you targeting?

Can we use

DNSServiceGetAddrInfo
API instead of
CFHost
API for address resolution?

Yes.

Which one should be preferred and why?

Both should work equally well. I prefer

CFHost
because its API fits in with the platform better (it works just like any of the other bazillion run loop based APIs out there).

Why

DNSServiceGetAddrInfo
does not work with
DNSServiceSetDispatchQueue
?

I’m not sure what you’re doing wrong here. Pasted in at the end of this response is some code that I wrote to do this. When I put that in a test tool project and run it on a Mac here in my office, it prints the following:

will start resolve
did start resolve
did resolve example.com. to 2606:2800:220:1:248:1893:25c8:1946
did resolve example.com. to 93.184.216.34

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
import Foundation
import dnssd

func main() {
    print("will start resolve")
    var sdRefQ: DNSServiceRef? = nil
    let err = DNSServiceGetAddrInfo(&sdRefQ, 0, 0, DNSServiceProtocol(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6), "example.com", { (_, _, _, err, hostnameQ, saQ, _, _) in
        guard err == kDNSServiceErr_NoError else {
            print("did not resolve, resolve failed, err: \(err)")
            return
        }
        let hostStr = String(cString: hostnameQ!)
        let sa = saQ!
        var addr = [CChar](repeating: 0, count: Int(NI_MAXHOST))
        let err2 = getnameinfo(sa, socklen_t(sa.pointee.sa_len), &addr, socklen_t(addr.count), nil, 0, NI_NUMERICHOST | NI_NUMERICSERV)
        guard err2 == 0 else {
            print("did not resolve, address-to-string failed, err: \(err)")
            return
        }
        let addrStr = String(cString: addr)
        print("did resolve \(hostStr) to \(addrStr)")
    }, nil)
    guard err == kDNSServiceErr_NoError else {
        print(err)
        return
    }
    let sdRef = sdRefQ!
    let junk = DNSServiceSetDispatchQueue(sdRef, .main)
    assert(junk == kDNSServiceErr_NoError)
    print("did start resolve")
    dispatchMain()
}

main()
exit(EXIT_SUCCESS)

Thanks for code snippet, eskimo. It works fine for me too. I noticed that you are calling dispatchMain() on line #31. If I include that in my code, I start getting callbacks. However, since dispatchMain() never returns, it blocks the method called and does not appear to be the solution I would like to have. Is there any other/better way one can make the callback trigger from DNSServiceSetDispatchQueue?


PS: I am currently targetting iOS/iPad OS.

dispatchMain
is only necessary because this is a command-line tool, and something has to stop the tool from quitting and run work scheduled on the main queue. In an iOS app, UIKit takes care of that.

To test this out, I took the bulk of my code (lines 5 through 30), put it in an

IBAction
inside an iOS app, and wired a button up to that action. When I run the app and click the button, the address resolves just like before.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Not sure what exactly was the mistake but I am able to make it work now with a bit of mix n fix based on your example. Once obvious mistake however was that I was using DNSServiceFlags(kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6) instead of DNSServiceProtocol(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6).


Thanks a lot for your help.