getnameinfo的IPv6结果不可靠

getnameinfo的IPv6结果不可靠

本文介绍了Swift getnameinfo的IPv6结果不可靠的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在sockaddr上具有以下扩展名:

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
  }
}

在代码的另一部分中,我正在调用getifaddrs以获取当前设备的接口地址.上面的代码对于IPv4可以正常工作,但是对于IPv6则有些不可靠.

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.

我得到如下结果:192.168.1.10fe80::e0fa:1204:100:0

当我将行var result: String = ""更改为var result: String? = nil时. IPv6地址突然变为fe80::,其余部分被切断.

When I change the line var result: String = "" to var result: String? = nil. The IPv6 addresses suddenly become fe80::, the rest is cut off.

甚至很奇怪,当我像这样切换var resultvar me = self行时:

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
  }
}

然后该功能仅适用于IPv4地址. getnameinfo将返回4(失败).

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?

推荐答案

问题是getnameinfo需要一个可以为sockaddr_insockaddr_in6的指针.该函数的定义有点令人困惑,因为它需要一个sockaddr指针.

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.

因为我正在使用扩展名提取IP地址,因此正在复制内存内容.对于IPv4而言,这不是问题,因为sockaddr_in的大小与sockaddr的大小相同.但是,对于IPv6,sockaddr_in6大于sockaddr结构,并且一些相关信息被切除.

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.

我的命令顺序可能决定了sockaddr地址之后的位置中存储在内存中的内容.有时看起来像是正确的IPv6地址,但实际上是错误的.

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.

我已通过将扩展程序移至网络接口ifaddrs解决了此问题:

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
  }
}

谢谢@MartinR找到问题的原因!

Thank you @MartinR finding the cause of the problem!

这篇关于Swift getnameinfo的IPv6结果不可靠的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 18:24