我正在尝试快速进行简单的DNS查找。到目前为止,这是我拥有的代码:
let hostRef = CFHostCreateWithName(kCFAllocatorDefault, "google.com").takeRetainedValue() var resolved = CFHostStartInfoResolution(hostRef, CFHostInfoType.Addresses, nil) let addresses = CFHostGetAddressing(hostRef, &resolved).takeRetainedValue() as NSArray
此时,“地址” NSArray中的每个元素都是一个CFDataRef对象,该对象包装了一个sockaddr结构。
由于CFDataRef可以免费连接到NSData,因此可以像这样循环遍历它们:
for address: AnyObject in addresses { println(address) // address is of type NSData. }
到目前为止,一切都很好(我认为)。当我在单元测试中运行时,这会打印出有效的外观数据。这是我被卡住的地方。为了我的一生,我不知道如何将NSData对象中的字节转换为sockaddr结构。
如何将COpaquePointer类型的address.bytes转换为ac struct?任何帮助表示赞赏。我把头撞在墙上,试图解决这个问题。
有关使用的更简单解决方案getnameinfo,请参见Martin的回答:如何在Swift中从DNS查询中获取真实IP地址?
getnameinfo
已针对Swift 5 / IPv6更新:
CFHostGetAddressing可以将返回的对象作为数据桥接到Swift ,并使用和强制转换为in_addr/ 。in6_addrwithUnsafeBytesassumingMemoryBound(to:)
CFHostGetAddressing
in_addr
in6_addr
withUnsafeBytes
assumingMemoryBound(to:)
这是一个完整的示例,用于inet_ntop将IPv4 / IPv6地址转换为字符串:
inet_ntop
import CFNetwork import Foundation protocol NetworkAddress { static var family: Int32 { get } static var maxStringLength: Int32 { get } } extension in_addr: NetworkAddress { static let family = AF_INET static let maxStringLength = INET_ADDRSTRLEN } extension in6_addr: NetworkAddress { static let family = AF_INET6 static let maxStringLength = INET6_ADDRSTRLEN } extension String { init<A: NetworkAddress>(address: A) { // allocate a temporary buffer large enough to hold the string var buf = ContiguousArray<Int8>(repeating: 0, count: Int(A.maxStringLength)) self = withUnsafePointer(to: address) { rawAddr in buf.withUnsafeMutableBufferPointer { String(cString: inet_ntop(A.family, rawAddr, $0.baseAddress, UInt32($0.count))) } } } } func addressToString(data: Data) -> String? { return data.withUnsafeBytes { let family = $0.baseAddress!.assumingMemoryBound(to: sockaddr_storage.self).pointee.ss_family // family determines which address type to cast to (IPv4 vs IPv6) if family == numericCast(AF_INET) { return String(address: $0.baseAddress!.assumingMemoryBound(to: sockaddr_in.self).pointee.sin_addr) } else if family == numericCast(AF_INET6) { return String(address: $0.baseAddress!.assumingMemoryBound(to: sockaddr_in6.self).pointee.sin6_addr) } return nil } } let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com" as CFString).takeRetainedValue() var resolved = DarwinBoolean(CFHostStartInfoResolution(host, .addresses, nil)) let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [Data]? print(addresses?.compactMap(addressToString))
您可以使用NSData方法getBytes(_, length:)方法,并使用前缀&运算符将sockaddr结构传递给inout参数:
getBytes(_, length:)
&
var data: NSData ... var address: sockaddr ... data.getBytes(&address, length: MemoryLayout<sockaddr>.size)
为Swift 3更新:
let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com" as CFString).takeRetainedValue() var resolved = DarwinBoolean(CFHostStartInfoResolution(host, .addresses, nil)) let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]? if let data = addresses?.first { var storage = sockaddr_storage() data.getBytes(&storage, length: MemoryLayout<sockaddr_storage>.size) if Int32(storage.ss_family) == AF_INET { let addr4 = withUnsafePointer(to: &storage) { $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee } } // prints 74.125.239.132 print(String(cString: inet_ntoa(addr4.sin_addr), encoding: .ascii)) } }
2015年6月3日更新: 现在可以轻松地对C结构进行零初始化了,这变得更加简单:
let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com").takeRetainedValue() var resolved = CFHostStartInfoResolution(host, .Addresses, nil) let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]? if let data = addresses?.first { var storage = sockaddr_storage() data.getBytes(&storage, length: sizeof(sockaddr_storage)) if Int32(storage.ss_family) == AF_INET { let addr4 = withUnsafePointer(&storage) { UnsafePointer<sockaddr_in>($0).memory } // prints 74.125.239.132 println(String(CString: inet_ntoa(addr4.sin_addr), encoding: NSASCIIStringEncoding)) } }
不幸的是,这需要sockaddr首先初始化。为了避免这种情况,您可以执行以下操作:
sockaddr
func makeWithUnsafePointer<T>(body: UnsafePointer<T> -> ()) -> T { let ptr = UnsafePointer<T>.alloc(sizeof(T)) body(ptr) return ptr.move() } let addr: sockaddr = makeWithUnsafePointer { data.getBytes($0 as UnsafePointer<sockaddr>, length: sizeof(sockaddr)) }
或这个:
func makeWithUninitialized<T>(body: inout T -> ()) -> T { let ptr = UnsafePointer<T>.alloc(sizeof(T)) body(&ptr.memory) return ptr.move() } let addr = makeWithUninitialized { (inout addr: sockaddr) in data.getBytes(&addr, length: sizeof(sockaddr)) }