为什么我在getaddrinfo()返回的链表中得到重复的addrinfo对象?

时间:2016-09-14 04:53:13

标签: c sockets ip

这是我的代码。

${data_1}

这是输出。

#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;
}

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

如果我们在代码中注释或删除以下行,则重复的条目将消失。

$ 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)

这是这种情况下的输出。

    /* 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) 中的IPv6条目会导致重复条目,即使使用/etc/hosts仅选择IPv4条目。

2 个答案:

答案 0 :(得分: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

或者使用一些分布,人们认为它是一个bug实际上是在维护glibc包,比如只有patches this behaviour away的openSUSE(可能还有SLES / SLED)。

答案 1 :(得分:1)

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

当两行有相同的canonical_hostname&#39; localhost&#39;时,getaddrinfo将返回重复的addrinfo。

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

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