c-使用getaddrinfo将IPv4地址转换为IPv6地址时,服务端口丢失



我正在Mac OS X 10.11.2中测试IPv6,发现了一个奇怪的问题。

我使用getaddrinfo将主机名解析为IPv6 addr:

#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, const char * argv[]) {
struct addrinfo * res, * addr;
struct addrinfo hints;
char buffer[128];
struct sockaddr_in6 * sockaddr_v6;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
if (getaddrinfo("www.google.com", "80", &hints, &res)) {
//if (getaddrinfo("216.58.199.4", "80", &hints, &res)) {
printf("getaddrinfo failed with errno(-%d)n", errno);
return 0;
}
for (addr = res;addr;addr = addr->ai_next)
{
if (addr->ai_family == AF_INET6)
{
sockaddr_v6 = (struct sockaddr_in6 *)addr->ai_addr;
printf("ipv6 addr is %s %d)n", inet_ntop(AF_INET6, &sockaddr_v6->sin6_addr, buffer, sizeof(buffer)), ntohs(sockaddr_v6->sin6_port));
}
}
freeaddrinfo(res);
return 0;
}

输出为

"ipv6 addr is 64:ff9b::d83a:c704 80". everything is ok ! 
"www.google.com" is resolved to "64:ff9b::d83a:c704", sin6_port is 80.

但当我使用"216.58.199.4"而不是"www.google.com"时,"216.58.199.4""www.google.com"IPv4地址。

//if (getaddrinfo("www.google.com", "80", &hints, &res)) {
if (getaddrinfo("216.58.199.4", "80", &hints, &res)) {

输出为CCD_ 6。将"216.58.199.4"转换为"64:ff9b::d83a:c704"是可以的,但奇怪的是80 become 0的服务端口。

有人能解释吗?

这是一个影响iOS 9和Mac OS X 10.11的错误。它已在iOS 10和macOS 10.12中修复,但以下是可用于支持运行iOS 9和Mac OS X 10.11的设备的变通方法:

解决方法1:使用服务名称

如果您使用的是已知或注册的端口号,则可以将服务名称而不是端口号作为字符串传递。在本例中,只需将"80"替换为"http":

if (getaddrinfo("216.58.199.4", "http", &hints, &res)) {

由于该错误仅限于数字解析,因此使用服务名称仍然有效。已知服务的完整列表可以在/etc/services中找到。

解决方法2:手动将端口写入sockaddr

如果不使用/etc/services中的端口号,可以手动将正确的端口号写入struct sockaddr。如果你这样做,重要的是:

  • 只有在端口为零时才写入,以确保在修复错误后此代码将停用
  • 请记住,sockaddr使用网络字节顺序,因此需要使用htons()来转换端口号

以下是应用于您的示例的变通方法:

for (addr = res;addr;addr = addr->ai_next)
{
if (addr->ai_family == AF_INET6)
{
sockaddr_v6 = (struct sockaddr_in6 *)addr->ai_addr;
// START WORKAROUND
if (sockaddr_v6->sin6_port == 0)
{
sockaddr_v6->sin6_port = htons(80);
}
// END WORKAROUND
printf("ipv6 addr is %s %d)n", inet_ntop(AF_INET6, &sockaddr_v6->sin6_addr, buffer, sizeof(buffer)), ntohs(sockaddr_v6->sin6_port));
}
}

最新更新