【问题标题】:Why do I get duplicate addrinfo objects in the linked-list returned by getaddrinfo()?为什么我在 getaddrinfo() 返回的链表中得到重复的 addrinfo 对象?
【发布时间】:2017-01-21 19:16:38
【问题描述】:

这是我的代码。

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

int main()
{
    struct addrinfo hints, *res, *p;
    int error;

    memset(&hints, 0, sizeof hints);

    /* If we comment or remove the following line, the duplicate entries
     * disappear */
    hints.ai_family = AF_INET;

    error = getaddrinfo("localhost", "http", &hints, &res);
    if (error != 0) {
        printf("Error %d: %s\n", error, gai_strerror(error));
        return 1;
    }

    for (p = res; p != NULL; p = p->ai_next)
    {
        if (p->ai_family == AF_INET) {
            struct sockaddr_in *addr = (struct sockaddr_in *) p->ai_addr;
            char ip[INET_ADDRSTRLEN];

            printf("ai_flags: %d; ai_family: %d; ai_socktype: %d; "
                   "ai_protocol: %2d; sin_family: %d; sin_port: %d; "
                   "sin_addr: %s; ai_canonname: %s\n",
                   p->ai_flags, p->ai_family, p->ai_socktype,
                   p->ai_protocol, addr->sin_family, ntohs(addr->sin_port),
                   inet_ntop(AF_INET, &addr->sin_addr, ip, INET_ADDRSTRLEN),
                   p->ai_canonname);
        } else if (p->ai_family == AF_INET6) {
            struct sockaddr_in6 *addr = (struct sockaddr_in6 *) p->ai_addr;
            char ip[INET6_ADDRSTRLEN];

            printf("ai_flags: %d; ai_family: %d; ai_socktype: %d; "
                   "ai_protocol: %2d; sin6_family: %d; sin6_port: %d; "
                   "sin6_addr: %s; ai_canonname: %s\n",
                   p->ai_flags, p->ai_family, p->ai_socktype,
                   p->ai_protocol, addr->sin6_family, ntohs(addr->sin6_port),
                   inet_ntop(AF_INET6, &addr->sin6_addr, ip, INET6_ADDRSTRLEN),
                   p->ai_canonname);
        }
    }

    return 0;
}

这是输出。

$ gcc -std=c99 -D_POSIX_SOURCE -Wall -Wextra -pedantic bar.c && ./a.out
ai_flags: 0; ai_family: 2; ai_socktype: 1; ai_protocol:  6; sin_family: 2; sin_port: 80; sin_addr: 127.0.0.1; ai_canonname: (null)
ai_flags: 0; ai_family: 2; ai_socktype: 2; ai_protocol: 17; sin_family: 2; sin_port: 80; sin_addr: 127.0.0.1; ai_canonname: (null)
ai_flags: 0; ai_family: 2; ai_socktype: 1; ai_protocol:  6; sin_family: 2; sin_port: 80; sin_addr: 127.0.0.1; ai_canonname: (null)
ai_flags: 0; ai_family: 2; ai_socktype: 2; ai_protocol: 17; sin_family: 2; sin_port: 80; sin_addr: 127.0.0.1; ai_canonname: (null)

输出显示第 1 项和第 3 项完全相同。同样,第 2 个和第 4 个条目完全相同。为什么我们会在结果中出现这些重复项?

如果我们从代码中注释或删除以下行,那么重复的条目就会消失。

    /* If we comment or remove the following line, the duplicate entries
     * disappear */
    /* hints.ai_family = AF_INET; */

这是本例中的输出。

$ gcc -std=c99 -D_POSIX_SOURCE -Wall -Wextra -pedantic bar.c && ./a.out
ai_flags: 0; ai_family: 10; ai_socktype: 1; ai_protocol:  6; sin6_family: 10; sin6_port: 80; sin6_addr: ::1; ai_canonname: (null)
ai_flags: 0; ai_family: 10; ai_socktype: 2; ai_protocol: 17; sin6_family: 10; sin6_port: 80; sin6_addr: ::1; ai_canonname: (null)
ai_flags: 0; ai_family: 2; ai_socktype: 1; ai_protocol:  6; sin_family: 2; sin_port: 80; sin_addr: 127.0.0.1; ai_canonname: (null)
ai_flags: 0; ai_family: 2; ai_socktype: 2; ai_protocol: 17; sin_family: 2; sin_port: 80; sin_addr: 127.0.0.1; ai_canonname: (null)

这就是我的/etc/hosts 的样子。

$ cat /etc/hosts
127.0.0.1       localhost
127.0.1.1       debian1

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

如果hints.ai_family = AF_INET 出现在代码中,但如果/etc/hosts 中以::1 开头的行被注释掉,那么重复的条目确实会消失。

$ gcc -std=c99 -D_POSIX_SOURCE -Wall -Wextra -pedantic bar.c && ./a.out
ai_flags: 0; ai_family: 2; ai_socktype: 1; ai_protocol:  6; sin_family: 2; sin_port: 80; sin_addr: 127.0.0.1; ai_canonname: (null)
ai_flags: 0; ai_family: 2; ai_socktype: 2; ai_protocol: 17; sin_family: 2; sin_port: 80; sin_addr: 127.0.0.1; ai_canonname: (null)

但我仍然想知道为什么 /etc/hosts 中的 IPv6 条目会导致重复条目,即使 hints.ai_family = AF_INET 仅用于选择 IPv4 条目。

【问题讨论】:

    标签: c sockets ip


    【解决方案1】:

    检查主机名(/etc/hosts)的静态表查找文件。

    当有两行具有相同的 canonical_hostname 'localhost' 时,getaddrinfo 将返回重复的 addrinfo。

    您的 /etc/hosts 可能如下所示:

     127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
     ::1 localhost
    

    【讨论】:

    • 我的 /etc/hosts 确实和你的很像。我不明白为什么它应该返回重复的addrinfos。第二个条目用于 IPv6 地址::1。在我的代码中,我使用 hints 专门选择 IPv4 地址。
    • 我添加了另一组输出,显示当我从代码中删除 hints.ai_family = AF_INET 时没有重复条目。仅当我在代码中选择仅带有hints.ai_family = AF_INET 的 IPv4 条目时才会出现重复条目​​。
    • @LoneLearner:尝试注释掉 IPv6 条目。它可能类似于 this,它是发行版(或 glibc 版本?)特定的,因为在我的机器上没有这样的问题。
    • @RomanKhimov 如果/etc/hosts 中以::1 开头的行被注释掉,重复的条目确实会消失。
    • @RomanKhimov 您的评论似乎回答了这个问题。如果您可以使用 Redhat Bugzilla 的链接编写您自己的答案,我会接受它作为正确答案。
    【解决方案2】:

    这是 glibc 的一个长期存在的错误/功能。当您的 hosts 文件中有 IPv6 localhost 条目时,例如

    ::1 localhost
    

    它会自动用于AF_INET 名称解析。此行为由 Ulrich Drepper 于 2006 年 11 月引入,并附有以下评论:

    nss/nss_files/files-hosts.c (LINE_PARSER):如果 IPv4 查询可以映射,则支持 IPv6 样式地址。

    有些人认为这是错误,尽管至少有两个错误报告(first onesecond one)对此主题进行了长时间的讨论,但没有人真正解释为什么要进行此更改,所以我猜只有一个人能做到这一点的是乌尔里希本​​人。但它可能在某些情况下很有用,因为即使 Ulrich 自 2012 年 5 月起就不再使用 glibc,但该代码仍然存在于每个现代版本的 glibc 中。

    如果您不喜欢这种行为,您可以调整您的hosts 文件,使其没有localhost 名称作为 IPv6 环回地址,使用其他名称,例如:

    ::1 localhost6
    

    或者使用一些人们认为这是一个错误实际上维护 glibc 包的发行版,比如 openSUSE(可能还有 SLES/SLED),它只是 patches this behaviour away

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-19
      • 1970-01-01
      • 1970-01-01
      • 2020-05-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多