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?
The problem is that
getnameinfoexpects a pointer that can either be asockaddr_inor asockaddr_in6. The definition of the function is a bit confusing because it expects asockaddrpointer.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_inis the same size as asockaddr. However for IPv6, thesockaddr_in6is larger than thesockaddrstruct 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
sockaddraddress. 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:Thank you @MartinR finding the cause of the problem!