c - 为什么设置 AI_PASSIVE 时 getaddrinfo() 返回第一个 IPv4 而不是 IPv6?

标签 c sockets dns getaddrinfo

根据https://www.amazon.com/Unix-Network-Programming-Sockets-Networking/dp/0131411551 :

This statement in the POSIX specification also implies that if the AI_PASSIVE flag is specified without a hostname, then the IPv6 wildcard address (IN6ADDR_ANY_INIT or 0::0) should be returned as a sockaddr_in6 structure, along with the IPv4 wildcard address (INADDR_ANY or 0.0.0.0), which is returned as a sockaddr_in structure. It also makes sense to return the IPv6 wildcard address first because we will see in Section 12.2 that an IPv6 server socket can handle both IPv6 and IPv4 clients on a dual-stack host.


但是,如果我尝试设置 AI_PASSIVE标志(当然还有 null 主机名),就像被动服务器的情况一样,那么我将首先获得 IPv4而不是 IPv6正如书中建议的那样:
#include <netdb.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>

#define HOSTLEN 128
#define PORTLEN 16

int main()
{
    int retcode;
    struct addrinfo hints, *res;
    char output[HOSTLEN], portbuf[PORTLEN];

    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_UNSPEC;
    //exmaple of socktype to be SOCK_STREAM
    hints.ai_socktype = SOCK_STREAM;

    //example port "ftp"
    if ((retcode = getaddrinfo(NULL, "ftp", &hints, &res)) != 0)
    {
        printf("getaddrinfo error: %s\n", gai_strerror(retcode));
    }

    do
    {
        switch (res->ai_family)
        {
        case AF_INET:;
            struct sockaddr_in *sin = (struct sockaddr_in *)res->ai_addr;
            inet_ntop(res->ai_family, &sin->sin_addr, output, HOSTLEN);
            snprintf(portbuf, PORTLEN, ":%d", ntohs(sin->sin_port));
            strcat(output, portbuf);
            break;

        case AF_INET6:;
            struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr;
            output[0] = '[';
            inet_ntop(res->ai_family, &sin6->sin6_addr, output + 1, HOSTLEN - 1);
            snprintf(portbuf, PORTLEN, "]:%d", ntohs(sin6->sin6_port));
            strcat(output, portbuf);
            break;
        }

        printf("address returned from getaddrinfo: %s\n", output);
    } while ((res = res->ai_next) != NULL);
}
输出:
address returned from getaddrinfo: 0.0.0.0:21
address returned from getaddrinfo: [::]:21
所以我可以看到,第一个地址从 getaddrinfo() 返回, 是 sockaddr_in (ipv4) 而不是 sockaddr_in6 (IPv6)。那么这本书是不正确的还是仅仅是因为一些配置?
我问的原因是因为它在尝试连接客户端时会导致问题,它为服务器指定 IPv6 地址(地址从 getaddrinfo() 返回,设置 AI_PASSIVE)并返回 0.0.0.0 而不是 0::0 并因此返回客户端永远不会连接。那么如何让 getaddrinfo 在设置 AI_PASSIVE 时返回 0::0 而不是 0.0.0.0 呢?

最佳答案

getaddrinfo 的预期使用模型与 AI_PASSIVE是你绑定(bind)它返回监听的所有地址,而不仅仅是第一个。
至于为什么特定实现首先返回 v4 结果,我不确定。一个可能的动机是 v6 套接字的默认行为,用于接受 v4 连接的“任何”地址(通过 v4 映射地址)。如果您首先绑定(bind) v6,那么您可能会在第一次和第二次绑定(bind)之间获得此类 v4mapped 客户端。如果先绑定(bind) v4,则确保所有 v4 连接都在 v4 套接字上。如果 v6 any 已经绑定(bind),甚至可能绑定(bind) v4 失败。

关于c - 为什么设置 AI_PASSIVE 时 getaddrinfo() 返回第一个 IPv4 而不是 IPv6?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65888168/

相关文章:

c - 在宏 C 内部或外部定义函数(高中断)

c 结构和字节设置/排序

C 程序使用 I/O 重定向复制文件

c - 如何使用 tcp/ip 将文件及其文件名从客户端发送到服务器?

Perl 套接字连接

c - 在调用 socket() 之前将 future 的文件描述符设置为 0

windows - 修改所有传出数据包的 TCP/IP header 中的目标端口

python - 以编程方式检查域是否受 DNSSEC 保护

javascript - 如何在C中将UNICODE/UTF-8转换为GB2312

heroku - 如何为 Name.com 和 Heroku 应用配置 DNS 记录