Resolving the IP Addresses from given DNS String Asynchronously

Hey everyone,

I'm tackling a scenario where I need to fetch a comprehensive list of both IPv4 and IPv6 addresses linked to a particular DNS. I know about the POSIX function getaddrinfo(), but I'm on the lookout for an asynchronous solution. Previously, I could've used CFHost, but unfortunately, it's been deprecated. Any suggestions or insights on how to achieve this asynchronously would be greatly appreciated!

Thanks, Harshal

Answered by DTS Engineer in 787319022

First up, don’t use top-level code. Put it into a function. Top-level code in Swift is weird.

Second, to get a callback you must schedule your DNSServiceRef somehow. The easiest way to do that is with DNSServiceSetDispatchQueue. And if you schedule on the main queue, you need to ‘park’ the main thread is dispatch_main rather than sleep.

I recommend that you look at the code I referenced in my previous email. This is a low-level API and thus not trivial to use. That’s especially true when you’re using Swift.

Finally, sockaddr is a very hard type to use correctly in Swift, and inet_ntop is bad form in any language. I recommend getnameinfo with NI_NUMERIC{HOST,SERV}. See QSocket: Addresses.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

These days my go-to API for asynchronous DNS resolution is DNSServiceGetAddrInfo.

The DNS-SD API is a bit tricky to call from high-level languages. If you’re using Objective-C, check out the DNSSDObjects sample code. If you’re using Swift, see this post. Neither of these call DNSServiceGetAddrInfo, but they show how to use DNS-SD in general.


Oh, and I can’t talk about DNS without my standard warning: If your ultimate goal is to connect to this address, you’re much better off using a connect-by-name API. TN3151 Choosing the right networking API talk about this in some detail.

OTOH, these APIs are fine if you’re not actually connecting, for example, you’re building a DNS debugging tool.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for the response. I was trying to write a poc and validate this with the following code:

import Foundation
import dnssd

var vSDRef: DNSServiceRef?

let callback: DNSServiceGetAddrInfoReply = { (sdef, flags, interface_idx, errorcode, hostname, resolvedAddresses, ttl, context) in
    
    NSLog("Callback called...")
    
    if errorcode == kDNSServiceErr_NoError {
        
        if let hostname = hostname, let address = resolvedAddresses {
            
            var add_string = [CChar](repeating: 0, count: Int(INET6_ADDRSTRLEN))
            
            switch Int32(address.pointee.sa_family) {
            case AF_INET:
                var ipv4_addr = address.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee }
                inet_ntop(AF_INET, &(ipv4_addr.sin_addr), &add_string, socklen_t(INET_ADDRSTRLEN))
            case AF_INET6:
                var addr6 = address.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) { $0.pointee }
                inet_ntop(AF_INET6, &(addr6.sin6_addr), &add_string, socklen_t(INET6_ADDRSTRLEN))
            default:
                NSLog("Unknown address family")
                return
            }
            
            NSLog("Resolved \(String(cString: hostname)) to \(String(cString: add_string))")
        } else {
            NSLog("Error resolving DNS address: \(errorcode)")
        }
    }
}
let returned_error = DNSServiceGetAddrInfo(&vSDRef, DNSServiceFlags(kDNSServiceFlagsLongLivedQuery), 0, DNSServiceProtocol(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6), "example.com", callback, nil)

if returned_error == kDNSServiceErr_NoError {
    NSLog("DNS lookup started...")

} else {
    NSLog("Error starting DNS resolution: \(returned_error)")
}

NSLog("Resolved if else")

sleep (100)```

tcpdump showed that we are getting a response but the callback never gets triggered.
Can you help me out here please.

Thanks,
 Harshal.
Accepted Answer

First up, don’t use top-level code. Put it into a function. Top-level code in Swift is weird.

Second, to get a callback you must schedule your DNSServiceRef somehow. The easiest way to do that is with DNSServiceSetDispatchQueue. And if you schedule on the main queue, you need to ‘park’ the main thread is dispatch_main rather than sleep.

I recommend that you look at the code I referenced in my previous email. This is a low-level API and thus not trivial to use. That’s especially true when you’re using Swift.

Finally, sockaddr is a very hard type to use correctly in Swift, and inet_ntop is bad form in any language. I recommend getnameinfo with NI_NUMERIC{HOST,SERV}. See QSocket: Addresses.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for the detailed feedback and guidance. I really appreciate you taking the time to provide such helpful information. The code you referenced in your previous email was very useful, and I've incorporated the suggestions you provided. Specifically, I've moved the top-level code into a function called performDNSLookup(), which is a more appropriate structure for Swift. I've also used DNSServiceSetDispatchQueue to schedule the DNSServiceRef on the main queue, and I'm using dispatchMain() to "park" the main thread and keep the callback active.

Additionally, I've replaced the use of inet_ntop with getnameinfo and NI_NUMERIC{HOST,SERV}, as you recommended, to handle the sockaddr type more effectively in Swift. I've tested the updated code, and it's working as expected.

Thank you again for your valuable guidance.

Regards,

Harshal

Hello @eskimo ,

Just to confirm again, is their support for "getaddrinfo" on all the iOS platforms as well ?

is their support for getaddrinfo on all the iOS platforms as well?

Yes, but with the same caveats as on macOS.

The DNS-SD API, including DNSServiceGetAddrInfo, is also available on all Apple platforms [1].

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Although there’s the usual watchOS caveat, described in TN3135 Low-level networking on watchOS.

Resolving the IP Addresses from given DNS String Asynchronously
 
 
Q