c - 使用 AF_UNSPEC 时,getaddrinfo() 仅返回 ipv6

标签 c networking ipv6 ipv4 getaddrinfo

当我想连接到服务器(在本地运行)并且不知道应用程序是否使用 ipv4、ipv6 或两者时,我应该调用 getaddrinfo() 两次,一次使用 AF_INET 和一次 AF_INET6,并尝试所有返回的地址?

一些背景: getaddrinfo() 提供了一种与 ipv4/ipv6 无关的主机名解析方法。我在网上找到了一些指南,指出连接到服务器的常用算法是使用带有 AF_UNSPEC 提示的 getaddrinfo(),然后尝试列表中返回的地址。

但是,在我的设置中,当我使用 AF_UNSPEC 时,getaddrinfo() 返回一个 ipv6 条目,主机是 ”本地主机”。 另一方面,如果我明确请求 IPv4,getaddrinfo() 会返回一个 IPv4 地址。

此示例使用 AF_UNSPEC 调用一次 getaddrinfo(),使用 AF_INET 调用一次,并遍历返回的列表并打印地址族条目:

#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 中的条目,如果找到匹配的条目,它只返回那些,并且不会尝试以不同方式解析主机名。

由于我的 /etc/hosts 文件仅包含 localhost 的 ipv6 条目,因此只有 AF_UNSPEC 返回该条目。对于 AF_INET,该条目不被视为符合条件,localhost 已正确解析为 127.0.0.1

最佳答案

这确实是一个有趣的问题:

这是一个案例,由glibc的nss-files模块专门处理;如果对地址族为 AF_INET 的本地主机发出请求并解析/etc/hosts 的 v6 本地主机条目,则它会隐式转换为地址为 127.0.0.1< 的 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 本地主机地址。

关于c - 使用 AF_UNSPEC 时,getaddrinfo() 仅返回 ipv6,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55627377/

相关文章:

c - 检测 Control 键何时被释放

c - 帮助 scanf 在 Big Endian 系统上表现不同

c++ - 以高数据包速率最小化丢弃的 UDP 数据包 (Windows 10)

python - Scapy发送ipv6数据包失败

ipv6 - 将 IPv4 应用移植到双栈 IPv4/IPv6

iOS IAP 不适用于仅 ipv6 网络

c - gcc 预处理器中的去字符串化

java - 错误的 IP 地址返回给在 tomcat 中运行的 Java 程序和代码

go - gRPC 到远程服务器的带宽较慢

c - malloc() 是否分配连续的内存块?