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

前端 未结 3 525

I want to get the IP address (like 192.168.0.1 or 87.12.56.50) from DNS query in Swift. I tried 100 times with 100 different methods ... Nothing helped me, so I\'ll have to

相关标签:
3条回答
  • 2020-11-30 07:27

    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.

    0 讨论(0)
  • 2020-11-30 07:30

    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)
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-30 07:38

    A number of the answers are using the deprecated bytes to retrieve the sockaddr to supply to getnameinfo. We’d use address.withUnsafeBytes now. For example, in Swift 5:

    /// Returns the string representation of the supplied address.
    ///
    /// - parameter address: Contains a `(struct sockaddr)` with the address to render.
    ///
    /// - returns: A string representation of that address.
    
    func stringRepresentation(forAddress address: Data) -> String? {
        address.withUnsafeBytes { pointer in
            var hostStr = [Int8](repeating: 0, count: Int(NI_MAXHOST))
    
            let result = getnameinfo(
                pointer.baseAddress?.assumingMemoryBound(to: sockaddr.self),
                socklen_t(address.count),
                &hostStr,
                socklen_t(hostStr.count),
                nil,
                0,
                NI_NUMERICHOST
            )
            guard result == 0 else { return nil }
            return String(cString: hostStr)
        }
    }
    
    0 讨论(0)
提交回复
热议问题