getaddrinfo() returns 使用 AF_UNSPEC 时只有 ipv6
getaddrinfo() returns only ipv6 when using AF_UNSPEC
当我想连接到服务器(运行 本地)并且不知道应用程序是否使用 ipv4、ipv6 或两者时,我应该调用 getaddrinfo()
两次,一次使用 AF_INET
和一次 AF_INET6
,并尝试所有 returned 地址?
一些背景:
getaddrinfo()
为 ipv4/ipv6-agnostic 主机名解析提供了一种方法。我在网上找到了一些指导方针,指出连接到服务器的常见算法是使用 getaddrinfo()
和 AF_UNSPEC 提示,然后尝试在列表中输入 return 的地址。
但是,在我的设置中,当我使用 AF_UNSPEC
时,getaddrinfo()
仅 return 是一个 ipv6 条目,主机是 "localhost"
。
另一方面,如果我明确要求 IPv4,getaddrinfo()
会 return 一个 IPv4 地址。
此示例使用 AF_UNSPEC
调用 getaddrinfo()
一次,使用 AF_INET
调用一次,并遍历 returned 列表并打印条目的地址族:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
const char* family_to_string(int family) {
switch (family) {
case AF_INET:
return "AF_INET";
case AF_INET6:
return "AF_INET6";
case AF_UNSPEC:
return "AF_UNSPEC";
default:
return "UNKNOWN";
}
}
int main(void) {
struct addrinfo hints;
struct addrinfo *res, *it;
static const char* host = "localhost";
static const char* port = "42420";
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_UNSPEC;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
int ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_INET;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
return 0;
}
当我收到这个输出时,我感到很惊讶:
getaddrinfo(.. AF_UNSPEC ..):
entry for AF_INET6
getaddrinfo(.. AF_INET ..):
entry for AF_INET
在深入了解 getaddrinfo()
的行为后,它似乎首先检查 /etc/hosts
中的条目,如果找到匹配的条目,它只会 return 那些,并且不会尝试以不同方式解析主机名。
因为我的 /etc/hosts
文件只包含一个用于 localhost
的 ipv6 条目,只有 returned 用于 AF_UNSPEC
。对于 AF_INET
,条目不被视为符合条件,localhost
被正确解析为 127.0.0.1
。
这确实是一个有趣的问题:
这是一个案例,由glibc的nss-files
模块专门处理;如果请求地址族 AF_INET
的本地主机并且解析了 /etc/hosts 的 v6 本地主机条目,它会隐式转换为地址 127.0.0.1
.[=17 的 v4 本地主机条目=]
请参阅 nss/nss_files/files-hosts.c
(第 70 行左右),其中执行此转换:
else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
{
in_addr_t localhost = htonl (INADDR_LOOPBACK);
memcpy (entdata->host_addr, &localhost, sizeof (localhost));
}
当请求 AF_UNSPEC
时,不会采用此分支,因此只有在 /etc/hosts
中有明确条目时,您才会解析 v4 本地主机地址。
当我想连接到服务器(运行 本地)并且不知道应用程序是否使用 ipv4、ipv6 或两者时,我应该调用 getaddrinfo()
两次,一次使用 AF_INET
和一次 AF_INET6
,并尝试所有 returned 地址?
一些背景:
getaddrinfo()
为 ipv4/ipv6-agnostic 主机名解析提供了一种方法。我在网上找到了一些指导方针,指出连接到服务器的常见算法是使用 getaddrinfo()
和 AF_UNSPEC 提示,然后尝试在列表中输入 return 的地址。
但是,在我的设置中,当我使用 AF_UNSPEC
时,getaddrinfo()
仅 return 是一个 ipv6 条目,主机是 "localhost"
。
另一方面,如果我明确要求 IPv4,getaddrinfo()
会 return 一个 IPv4 地址。
此示例使用 AF_UNSPEC
调用 getaddrinfo()
一次,使用 AF_INET
调用一次,并遍历 returned 列表并打印条目的地址族:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
const char* family_to_string(int family) {
switch (family) {
case AF_INET:
return "AF_INET";
case AF_INET6:
return "AF_INET6";
case AF_UNSPEC:
return "AF_UNSPEC";
default:
return "UNKNOWN";
}
}
int main(void) {
struct addrinfo hints;
struct addrinfo *res, *it;
static const char* host = "localhost";
static const char* port = "42420";
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_UNSPEC;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
int ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_INET;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
return 0;
}
当我收到这个输出时,我感到很惊讶:
getaddrinfo(.. AF_UNSPEC ..):
entry for AF_INET6
getaddrinfo(.. AF_INET ..):
entry for AF_INET
在深入了解 getaddrinfo()
的行为后,它似乎首先检查 /etc/hosts
中的条目,如果找到匹配的条目,它只会 return 那些,并且不会尝试以不同方式解析主机名。
因为我的 /etc/hosts
文件只包含一个用于 localhost
的 ipv6 条目,只有 returned 用于 AF_UNSPEC
。对于 AF_INET
,条目不被视为符合条件,localhost
被正确解析为 127.0.0.1
。
这确实是一个有趣的问题:
这是一个案例,由glibc的nss-files
模块专门处理;如果请求地址族 AF_INET
的本地主机并且解析了 /etc/hosts 的 v6 本地主机条目,它会隐式转换为地址 127.0.0.1
.[=17 的 v4 本地主机条目=]
请参阅 nss/nss_files/files-hosts.c
(第 70 行左右),其中执行此转换:
else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
{
in_addr_t localhost = htonl (INADDR_LOOPBACK);
memcpy (entdata->host_addr, &localhost, sizeof (localhost));
}
当请求 AF_UNSPEC
时,不会采用此分支,因此只有在 /etc/hosts
中有明确条目时,您才会解析 v4 本地主机地址。