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 найти причину проблемы!

Другие вопросы по тегам