How can I get a real IP address from DNS query in Swift?

Refer to the following Swift code to get DNS resolution for a website. One method uses CFHostStartInfoResolution whereas other one uses gethostbyname.

Both these APIs support all/multiple IP address resolution.

private func urlToIP_cfHostResolution(_ url: String) -> [String] {

    var ipList: [String] = []

    let host = CFHostCreateWithName(nil,url as CFString).takeRetainedValue()

    CFHostStartInfoResolution(host, .addresses, nil)

    var success: DarwinBoolean = false

    if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray? {

        for case let theAddress as NSData in addresses {

            var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))

            if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
                       &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {

                ipList.append(String(cString: hostname))
            }    
        }
    }

    return ipList
}

This method returns ["151.101.129.69", "151.101.1.69", "151.101.193.69", "151.101.65.69"] for www.stackoverflow.com

private func urlToIP_gethostbyname(_ url: URL) -> [String] {

    var ipList: [String] = []

    guard let hostname = url.host else {

        return ipList
    }

    guard let host = hostname.withCString({gethostbyname($0)}) else {

        return ipList
    }

    guard host.pointee.h_length > 0 else {

        return ipList
    }

    var index = 0

    while host.pointee.h_addr_list[index] != nil {

        var addr: in_addr = in_addr()

        memcpy(&addr.s_addr, host.pointee.h_addr_list[index], Int(host.pointee.h_length))

        guard let remoteIPAsC = inet_ntoa(addr) else {

            return ipList
        }

        ipList.append(String.init(cString: remoteIPAsC))

        index += 1
    }

    return ipList
}

This method also returns ["151.101.129.69", "151.101.1.69", "151.101.193.69", "151.101.65.69"] for www.stackoverflow.com

Hope this helps.


Your code retrieves the address as a "socket address" structure. getnameinfo() can be used to convert the address into a numerical IP string (code recycled from https://stackoverflow.com/a/25627545/1187415, now updated to Swift 2):

let host = CFHostCreateWithName(nil,"www.google.com").takeRetainedValue()
CFHostStartInfoResolution(host, .Addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray?,
    let theAddress = addresses.firstObject as? NSData {
    var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
    if getnameinfo(UnsafePointer(theAddress.bytes), socklen_t(theAddress.length),
        &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
            if let numAddress = String.fromCString(hostname) {
                print(numAddress)
            }
    }
}

Output (example): 173.194.112.147

Note also the usage of takeRetainedValue() in the first line, because CFHostCreateWithName() has "Create" in its name the therefore returns a (+1) retained object.


Update for Swift 3/Xcode 8:

let host = CFHostCreateWithName(nil,"www.google.com" as CFString).takeRetainedValue()
CFHostStartInfoResolution(host, .addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray?,
    let theAddress = addresses.firstObject as? NSData {
    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
    if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
                   &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
        let numAddress = String(cString: hostname)
        print(numAddress)
    }
}

Or, to get all IP addresses for the host:

let host = CFHostCreateWithName(nil,"www.google.com" as CFString).takeRetainedValue()
CFHostStartInfoResolution(host, .addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray? {
    for case let theAddress as NSData in addresses {
        var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
        if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
                       &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
            let numAddress = String(cString: hostname)
            print(numAddress)
        }
    }
}