2016-09-14 7 views
0

ここに私のコードです。getaddrinfo()によって返されたリンクリストに重複するaddrinfoオブジェクトがあるのはなぜですか?

#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の場合は、コード内に存在するが、::1で始まる/etc/hostsの行がコメントアウトされている場合、実際に重複したエントリが消えます。

$ 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のエントリのみを選択するために使用された場合でも、理由を知っていただきたいと思います。

答えて

1

これはglibcのの長年のバグ/機能です。あなたは

::1 localhost 

のようなあなたのhostsファイルでのIPv6のlocalhostエントリを、持っているとき、それは自動的にAF_INET名前解決のために使用されます。この動作は、このコメントをウルリック・ドレパーにより、2006年11月に導入されました:

NSS/nss_files /ファイル・hosts.c(LINE_PARSER):彼らはマッピングできる場合、IPv4のサポートIPv6の形式のアドレスが照会します。

一部の人々は、それはバグだと思うと、このトピックの長い議論(first onesecond one)と少なくとも二つのバグ報告があったが、この変更が行われた理由を、誰もが本当に説明していないので、私は誰唯一の人物を推測しますそれはウルリッヒ自身が行うことができます。しかし、恐らく2012年5月からUlrichがglibcに取り組んでいないにもかかわらず、このコードはglibcのすべての現代版にまだ存在しているので、おそらくいくつかのシナリオでは便利です。あなたは、この動作が気に入らない場合

は、あなたがチューニングhostsファイルは、IPv6ループバックアドレスのlocalhost名前を持たないようにすることができますように、他のいくつかの名前を使用します。

::1 localhost6 

や人々が考えて、いくつかのディストリビューションを使用しますバグだと、実際にはopenSUSE(おそらくSLES/SLED)のように、ちょうどpatches this behaviour awayというglibcパッケージを維持しています。

1

静的テーブル参照ファイルでホスト名(/ etc/hosts)を確認します。

2つの行に同じcanonical_hostname 'localhost'がある場合、getaddrinfoは重複するaddrinfoを返します。

あなたの/ etc/hostsファイルは次のようになります。

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

私の/ etc/hostsはあなたのものとよく似ています。私はなぜそれが重複した 'addrinfo'sを返すべきか理解していません。 2番目のエントリはIPv6アドレス ':: 1'です。私のコードでは、特にヒントを使ってIPv4アドレスだけを選択しています。 –

+0

コードから 'hints.ai_family = AF_INET'を削除すると、重複したエントリがないことを示す別の出力を追加しました。重複したエントリは、コード内で 'hints.ai_family = AF_INET'でIPv4エントリのみを選択した場合にのみ表示されます。 –

+0

@LoneLearner:IPv6エントリをコメントアウトしてみてください。私のマシン上にこのような問題はないので、ディストリビューション(またはglibcバージョン?)固有の[this](https://bugzilla.redhat.com/show_bug.cgi?id=496300)のようなものかもしれません。 –

関連する問題