Swift getnameinfo ненадежные результаты для IPv6
У меня есть следующее расширение на 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
}
}
В другой части моего кода я звоню getifaddrs
получить адреса интерфейса текущего устройства. Приведенный выше код отлично работает для IPv4, но несколько ненадежен для IPv6.
Я получаю результаты, такие как: 192.168.1.10
а также fe80::e0fa:1204:100:0
Когда я меняю линию var result: String = ""
в var result: String? = nil
, Адреса IPv6 внезапно становятся fe80::
Остальное отрезано.
Еще страннее, когда я просто переключаю var result
и var me = self
такие строки:
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
}
}
Тогда функция будет работать только для адресов IPv4. getnameinfo
вернет 4 (FAIL).
Это во время отладки, без каких-либо оптимизаций, о которых я знаю. Не имеет значения, запускаю ли я его на симуляторе или на реальном устройстве.
Может кто-нибудь объяснить, почему это происходит?
1 ответ
Проблема в том, что getnameinfo
ожидает указатель, который может быть sockaddr_in
или sockaddr_in6
, Определение функции немного сбивает с толку, потому что она ожидает sockaddr
указатель.
Поскольку я использую расширение для извлечения IP-адреса, создается копия содержимого памяти. Это не проблема для IPv4, потому что размер sockaddr_in
такой же размер, как sockaddr
, Однако для IPv6 sockaddr_in6
больше чем sockaddr
структура и некоторая соответствующая информация обрезается.
Порядок моих команд, вероятно, определял то, что было сохранено в памяти в месте, расположенном непосредственно после sockaddr
адрес. Иногда это выглядит как правильный IPv6-адрес, но на самом деле неверный.
Я решил эту проблему, переместив свое расширение в сетевой интерфейс 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
}
}
Спасибо @MartinR найти причину проблемы!