簡體   English   中英

如何從 Swift 中的 DNS 查詢中獲取真實 IP 地址?

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

我想從 Swift 中的 DNS 查詢中獲取 IP 地址(如 192.168.0.1 或 87.12.56.50)。 我用 100 種不同的方法嘗試了 100 次......沒有任何幫助,所以我不得不尋求幫助。 到目前為止,這是我的代碼:

let host = CFHostCreateWithName(nil,"subdomain.of.stackoverflow.com").takeUnretainedValue();
CFHostStartInfoResolution(host, .Addresses, nil);
var success: Boolean = 0;
let addresses = CFHostGetAddressing(host, &success).takeUnretainedValue() as NSArray;
if(addresses.count > 0){
   let theAddress = addresses[0] as NSData;
   println(theAddress);
}

好的......這些是我嘗試實現但沒有成功的代碼的鏈接: https : //gist.github.com/mikeash/bca3a341db74221625f5
如何在 iOS 上執行 DNS 查詢
在 Swift 中從 NSData 對象創建一個數組
CFHostGetAddressing() 是否支持 ipv6 DNS 條目?
在 Swift 中做一個簡單的 DNS 查找

您的代碼將地址檢索為“套接字地址”結構。 getnameinfo()可用於將地址轉換為數字 IP 字符串(從https://stackoverflow.com/a/25627545/1187415回收的代碼,現在更新到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)
            }
    }
}

輸出(示例): 173.194.112.147

還要注意第一行中takeRetainedValue()的用法,因為CFHostCreateWithName()在其名稱中有“Create”,因此返回一個 (+1) 保留對象。


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)
    }
}

或者,要獲取主機的所有IP 地址:

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)
        }
    }
}

請參閱以下 Swift 代碼以獲取網站的 DNS 解析。 一種方法使用CFHostStartInfoResolution而另一種方法使用gethostbyname

這兩個 API 都支持所有/多個 IP 地址解析。

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
}

此方法返回["151.101.129.69", "151.101.1.69", "151.101.193.69", "151.101.65.69"]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
}

這種方法也返回["151.101.129.69", "151.101.1.69", "151.101.193.69", "151.101.65.69"]www.stackoverflow.com

希望這可以幫助。

許多答案使用不推薦使用的bytes來檢索sockaddr以提供給getnameinfo 我們現在使用address.withUnsafeBytes 例如,在 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)
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM