问题
I have the following extension on sockaddr
:
extension sockaddr {
/// Indicates if this is an IPv4 address.
var isIPv4: Bool {
return sa_family == UInt8(AF_INET)
}
/// Indicates if this is an IPv6 address.
var isIPv6: Bool {
return sa_family == UInt8(AF_INET6)
}
/// Returns the address in string notation.
var address: String? {
var result: String = ""
var me = self
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(&me, socklen_t(me.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
result = String(cString: hostname)
}
return result
}
}
In an other part of my code I'm calling getifaddrs
to get the interface addresses of the current device. The code above works fine for IPv4, but is somewhat unreliable for IPv6.
I get results like: 192.168.1.10
and fe80::e0fa:1204:100:0
When I change the line var result: String = ""
to var result: String? = nil
. The IPv6 addresses suddenly become fe80::
, the rest is cut off.
Even weirder, when I just switch the var result
and the var me = self
lines like this:
extension sockaddr {
/// Indicates if this is an IPv4 address.
var isIPv4: Bool {
return sa_family == UInt8(AF_INET)
}
/// Indicates if this is an IPv6 address.
var isIPv6: Bool {
return sa_family == UInt8(AF_INET6)
}
/// Returns the address in string notation.
var address: String? {
var me = self
var result: String = ""
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(&me, socklen_t(me.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
result = String(cString: hostname)
}
return result
}
}
Then the function will only work for IPv4 addresses. The getnameinfo
will return 4 (FAIL).
This is during debugging, with no optimizations that I know of. It doesn't matter if I run it on a simulator or real device.
Could someone please explain why this is happening?
回答1:
The problem is that getnameinfo
expects a pointer that can either be a sockaddr_in
or a sockaddr_in6
. The definition of the function is a bit confusing because it expects a sockaddr
pointer.
Because I'm using an extension to extract the IP address a copy is being made of the memory contents. This isn't a problem for IPv4 because the size of a sockaddr_in
is the same size as a sockaddr
. However for IPv6, the sockaddr_in6
is larger than the sockaddr
struct and some relevant information is cut off.
The order of my commands probably determined what was stored in memory at the location directly after the sockaddr
address. Sometimes it would look like a proper IPv6 address, but in reality incorrect.
I've resolved this issue by moving my extension to the network interface ifaddrs
:
extension ifaddrs {
/// Returns the IP address.
var ipAddress: String? {
var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
let address = ifa_addr.pointee
let result = getnameinfo(ifa_addr, socklen_t(address.sa_len), &buffer, socklen_t(buffer.count), nil, socklen_t(0), NI_NUMERICHOST)
return result == 0 ? String(cString: buffer) : nil
}
}
Thank you @MartinR finding the cause of the problem!
来源:https://stackoverflow.com/questions/44478074/swift-getnameinfo-unreliable-results-for-ipv6