【问题标题】:IPv6 Compatibility Issues with Low-Level C API's [closed]IPv6 与低级 C API 的兼容性问题 [关闭]
【发布时间】:2017-09-17 19:13:38
【问题描述】:

Apple 需要 IPv6 兼容性。似乎有点为时过早,但我想有人必须强制它被广泛采用。

我的一个客户有一些遗留代码,由于这种不兼容性,这些代码将不再被批准用于 App Store。软件不是我自己写的,特别是没接触过这部分代码。在低级 C 网络方面,我并不是一个忍者。

我告诉客户,部分问题在于他们有一个仅支持 IPv4 的服务器,并且让我们将 IPv4 地址硬编码到该服务器。我将 IPv4 地址更新为域名,并告诉他们他们的服务器必须支持 IPv6。因此,他们将它们全部移动,并在进行任何测试之前拨动了开关。几天前我收到通知,他们在商店里的所有软件都不再工作了。这就是我们所处的困境。

这是我可以使用一些帮助的潜在许多问题之一。

不仅连接到端口的服务器不响应 IPv6,而且使用的一些较低级别的 API 不兼容。

我遇到的第一件事就是gethostbyname()的使用。显然这不支持 IPv6。我一直在尝试使用 getaddrinfo() 修复它,但我的 sockaddr 并不完全相同。

我可以看到的第二个问题是显然我需要使用AF_UNSPEC 而不是AF_INET。所以我试图通过以下方式打开一个套接字:

int sockfd = socket(AF_UNSPEC, SOCK_STREAM, 0);

我总是得到-1。

如果我使用AF_INET 打开袜子,我会走得更远,但是我遇到了这个问题:

otherAddr   sockaddr *  0x618000220680  0x0000618000220680
   sa_len   __uint8_t   '\x1c'
   sa_family    sa_family_t '\x1e'
   sa_data  char [14]   "\x13\x88"  
otherAddrCast   sockaddr *  0x7000052d3c28  0x00007000052d3c28
   sa_len   __uint8_t   '\0'
   sa_family    sa_family_t '\0'
   sa_data  char [14]   "\x13\x88\"Ԛ\\" 

所以有些sa_data 是一样的。如果我不将sockaddr_in 转换为struct sockaddr*,那么sa_data 是一样的。

问题是我不知道这大部分是什么意思。帮忙?

编辑:这是我的代码。假设我只得到 1 个地址(因为我是)

    struct hostent* server = gethostbyname([_hostname UTF8String]);
    struct addrinfo *ai;
    struct addrinfo hints;


    memset(&hints, 0x00, sizeof(hints));
    hints.ai_family   = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    NSString* portString = [NSString stringWithFormat:@"%d", _port];
    getaddrinfo([_hostname UTF8String], [portString cStringUsingEncoding:kCFStringEncodingASCII] , &hints, &ai);

    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    bcopy(server->h_addr, &addr.sin_addr.s_addr, server->h_length);
    addr.sin_port = htons(_port);
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    _addr = (struct sockaddr*)&addr;
    int status = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    int status2 = connect(socket(ai->ai_family,ai->ai_socktype,0), ai->ai_addr, ai->ai_addrlen);

【问题讨论】:

  • 您可能需要查找 IPv6 套接字教程。对于stackoverflow,这个问题可能有点宽泛。
  • “似乎有点为时过早” - 是的,只是 IPv4 的地址用完了,而 IPv6 标准化的时间只有 20 年。绝对过早...
  • 我知道标准已经存在,不幸的是这还不够。许多消费者 ISP(大多数应用程序用户)不完全支持 IPv6。这给试图在没有合理测试路径的情况下支持它的人们带来了相当大的挑战。

标签: objective-c c sockets


【解决方案1】:

您根本不应该将AF_UNSPECsocket() 一起使用。您必须使用 AF_INET (IPv4) 或 AF_INET6 (IPv6)。

您可以将AF_UNSPECgetaddrinfo()hints 输入参数一起使用,以表明您愿意同时接受IPv4 和IPv6 地址作为输出。输出中的实际地址将是AF_INETAF_INET6。或者,您可以将提示设置为 AF_INET 以仅用于 IPv4 输出。或 AF_INET6 仅用于 IPv6 输出。

您应该遍历getaddrinfo() 返回的列表。对于列表中的每个地址:

  • 将其ai_familyai_socktypeai_protocol 字段传递给socket()
  • 然后将其ai_addrai_addrlen 字段传递给bind()(服务器)或connect()(客户端)。

对为侦听服务器报告的所有地址以及为客户端报告的所有地址重复此操作,直到成功连接为止。

这样,您创建的每个套接字都与它正在使用的地址的 IP 版本相匹配,并且您将适当的匹配 sockaddr_in (IPv4) 或 sockaddr_in6 (IPv6) 传递给 bind()/connect() .

一旦你成功侦听了服务器套接字,或者成功连接了客户端套接字,如果你需要使用accept()getsockname()getpeername() 从套接字检索 IP,请务必将其传递给sockaddr_storage 结构要填写。sockaddr_storage 足够大,可以容纳所有已定义的基于sockaddr 的结构(并且有很多)。如果成功,您可以根据其sa_family 字段(分别为AF_INETAF_INET6)将其类型转换为sockaddr_insockaddr_in6。其他类似的功能也是如此,例如inet_pton()inet_ntop()


更新:鉴于您显示的客户端代码,请尝试以下操作:

struct addrinfo hints = {0};
hints.ai_family   = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

NSString* portString = [NSString stringWithFormat:@"%d", _port];

struct addrinfo *ai;
int sockfd = -1;

int status = getaddrinfo([_hostname UTF8String], [portString cStringUsingEncoding:kCFStringEncodingASCII] , &hints, &ai);
if (status == 0) {
    sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    if (sockfd == -1) {
        status = errno;
    }
    else if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
        status = errno;
        close(sockfd);
        sockfd = -1;
    }
    freeaddrinfo(ai);
}

if (sockfd == -1) {
    // handle status error as needed...
}
else {
    // use sockfd as needed...
    close(sockfd);
}

【讨论】:

  • 投反对票的人,请解释你投反对票的原因。
  • 感谢您回答我的问题。我很少处理任何低级 API。感谢您花时间了解,尽管有问题,但由于缺乏这些库的经验,我几乎没有办法进一步完善它。您花时间解释了我使用常量和类型的方式没有意义的地方,而其他人只是将其标记为过于宽泛。再次感谢您。
  • 我在这方面走得更远,不幸的是我不知道为什么我的结果连接不同。见上面的编辑
  • @Xials:准确定义“产生的连接不同”。您有两个套接字和两个连接。他们有什么“不同”?在任何情况下,您都不会在使用 gethostbyname() 之前检查其结果是否有错误。你为什么打电话给gethostbyname()?摆脱它。在使用 getaddrinfo() 之前,您也没有检查结果是否有错误。另外,去掉你的 sockaddr_in addr 变量,你不需要它。使用getaddrinfo() 提供的ai_addr。你为什么要打两次connect()
  • @Xials:从数据处理的角度来看,您显示的两个 connect() 调用在逻辑上是相同的(假设没有报告错误),因此它们之间的唯一区别必须在于 IP gethostbyname() 返回的地址与getaddrinfo() 返回的 IP 地址。这两个函数都可以按任意顺序返回多个 IP,而您显示的代码并未考虑这种可能性。
猜你喜欢
  • 2012-01-28
  • 1970-01-01
  • 2012-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-15
相关资源
最近更新 更多