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