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
getnameinfo
expects a pointer that can either be asockaddr_in
or asockaddr_in6
. The definition of the function is a bit confusing because it expects asockaddr
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 asockaddr
. However for IPv6, thesockaddr_in6
is larger than thesockaddr
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
:Thank you @MartinR finding the cause of the problem!