GETADDRINFO

Section: Linux Programmer's Manual (3)
Updated: 2007-06-08
Index JM Home Page roff page
 

名前

getaddrinfo, freeaddrinfo, gai_strerror - ネットワークのアドレスとサービスを変換する  

書式

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *node, const char *service,
                const struct addrinfo *hints,
                struct addrinfo **res);

void freeaddrinfo(struct addrinfo *res);

const char *gai_strerror(int errcode);
 

説明

getaddrinfo(3) 関数は、4 つの関数 getipnodebyname(3), getipnodebyaddr(3), getservbyname(3), getservbyport(3) の機能をまとめて一つのインターフェースにしたものである。 getaddrinfo(3) 関数はスレッドセーフで、 ひとつ以上のソケットアドレス構造体を生成する。 この構造体は bind(2) や connect(2) などの関数コールでクライアント用やサーバー用のソケットを生成する際に 用いることができる。

getaddrinfo(3) 関数が作ることのできるのは IPv4 アドレス構造体だけではない。 IPv6 のサポートされているシステムなら IPv6 のソケットアドレス構造体も生成できる。 これらのソケットアドレス構造体は、 クライアントやサーバーでソケットを準備する際に、 bind(2) や connect(2) から直接利用できる。

この関数が用いる addrinfo 構造体には以下のメンバーが含まれている。

struct addrinfo {
    int              ai_flags;
    int              ai_family;
    int              ai_socktype;
    int              ai_protocol;
    size_t           ai_addrlen;
    struct sockaddr *ai_addr;
    char            *ai_canonname;
    struct addrinfo *ai_next;
};

getaddrinfo(3) は、 メモリを動的に割り当てて addrinfo 構造体のリンクリストを作成し、 そのリンクリストへのポインタを res に設定する。 各構造体は ai_next メンバーでリンクされる。 リンクリストの addrinfo 構造体は複数個になることもあり、その理由はいくつかある。 ネットワークホストがマルチホームの場合、 複数のソケットプロトコルで同じサービスが使える (ひとつが SOCK_STREM アドレスで、もうひとつが SOCK_DGRAM アドレスであるなど) 場合、がある。

ai_family, ai_socktype, ai_protocol メンバーは、それぞれ socket(2) システムコールの対応する引き数と同じ意味を持つ。 getaddrinfo(3) 関数は IPv4 と IPv6 のどちらかのアドレスファミリーのソケットアドレスを返す (ai_familyAF_INET または AF_INET6 のどちらかにセットされる)。

hints パラメータには、希望のソケットのタイプやプロトコル を指定する。 hints を NULL にすることは、どんなネットワークアドレスやプロトコルでもよいことを意味する。 このパラメータが NULL でないときは、 addrinfo 構造体へのポインタとなる。 この構造体の ai_familiy, ai_socktype, ai_protocol メンバーで、希望のソケットタイプなどを指定する。 ai_familyAF_UNSPEC を指定すると、任意のプロトコルファミリーを意味する (例えば IPv4 や IPv6)。 ai_socktypeai_protocol に 0 を指定すると、 同様に任意のソケットタイプやプロトコルを受付けることを意味する。 ai_flags メンバーには追加オプションを指定する (定義は下のとおり)。 複数のフラグを指定するには、それらの論理 OR をとって指定すればよい。 hints パラメータのその他のメンバーには、 0 (またはヌル・ポインタ) を入れなければならない。

node パラメータと service パラメータのどちらかは NULL にしてよい (両方同時 NULL は不可)。 node には数値形式のネットワークアドレス (IPv4 ならドット区切りの 10 進形式、 IPv6 なら 16 進形式) か、あるいはネットワークホスト名を指定する。 後者の場合は、ネットワークアドレスが検索され、名前解決が行なわれる。 hints.ai_flagsAI_NUMERICHOST フラグが含まれている場合は、 node パラメータは数値的なネットワークアドレスでなければならない。 AI_NUMERICHOST フラグを使うと、 時間の掛かる可能性のあるすべてのネットワークホストアドレスの検索が抑制される。

getaddrinfo(3) 関数は addrinfo 構造体のリンクリストを生成するが、 それぞれのネットワークアドレスは hints パラメータで課されたすべての制限に従っている。 hints.ai_flagsAI_CANONNAME フラグが含まれている場合、これらの addrinfo 構造体の最初の ai_canonname フィールドにはホストの公式な名前を指すように設定される。 ai_family, ai_socktype, ai_protocol 各メンバーにはソケット生成時のパラメータが設定される。 ai_addr メンバーにはソケットアドレスへのポインタが書き込まれ、 ai_addrlen メンバーにはソケットアドレスの長さ (バイト単位) が書き込まれる。

node が NULL の場合、各ソケット構造体のネットワークアドレスは AI_PASSIVE フラグに基づいて初期化される (このフラグは hints.ai_flags に設定される)。 AI_PASSIVE フラグがセットされている場合、 各ソケット構造体のネットワークアドレスは設定されないままとなる (訳注: IPv4 の場合 INADDR_ANY に、IPv6 の場合 IN6ADDR_ANY_INIT に等しい)。 これはすべてのネットワークアドレスでクライアント接続を待ち受けるような サーバーアプリケーションで用いられる。 AI_PASSIVE フラグがセットされていない場合、 ネットワークアドレスは loopback インターフェースアドレスに設定される。 これは同じネットワークホスト上で動作しているサーバーに接続するような クライアントアプリケーションで用いられる。

hints.ai_flagsAI_ADDRCONFIG を含む場合、 result が指すリストには、 ローカルシステムに最低一つの IPv4 アドレスが設定されている場合は IPv4 アドレスが返され、 ローカルシステムに最低一つの IPv6 アドレスが設定されている場合は IPv6 アドレスが返される。

hint.ai_flagsAI_V4MAPPED が指定されていて、 hints.ai_familyAF_INET6 が指定され、 マッチする IPv6 アドレスが見つからなかった場合、 result が指すリストには IPv4-mapped IPv6 アドレスが返される。 hints.ai_familyAI_V4MAPPEDAI_ALL の両方が指定されている場合、 result が指すリストには IPv6 アドレスと IPv4-mapped IPv6 アドレスの 両方が返される。 AI_V4MAPPED が指定されていない場合、 AI_ALL は無視される。

service は、各ソケット構造体のネットワークアドレスのポート番号を設定する。 service が NULL の場合、ポート番号は初期化されない。 hints.ai_flagsAI_NUMERICSERV が指定され、 service が NULL でない場合、 service は数値のポート番号を含む文字列を指し示さなければならない。 このフラグは、名前解決サービスが不要であることが分かっている場合に、 サービスの起動を抑制するために用いられる。

freeaddrinfo(3) 関数は、 リンクリスト res に対して動的に割り当てられたメモリを解放する。  

国際化ドメイン名のための getaddrinfo() の拡張

glibc 2.3.4 から、 getaddrinfo() は入出力するホスト名を透過的に国際化ドメイン名 (IDN) 形式 (RFC 3490 の Internationalizing Domain Names in Applications (IDNA) を参照のこと) と変換することを選択的に認めるように拡張されている。 4 つの新しいフラグが定義されている:

AI_IDN
このフラグが指定されると、 node で与えられたノード名は必要があれば IDN 形式に変換される。 ソース符号化形式は現在のロケールのものである。

入力名に非 ASCII 文字が含まれている場合、 IDN 符号化形式が使われる。 非 ASCII 文字が含まれている(ピリオドで区切られる)部分ノード名は、 名前解決機能に渡される前に ASCII 互換符号化形式 (ACE) を使って 符号化される。

AI_CANONIDN
AI_CANONNAME が指定されている場合、 getaddrinfo() は名前の検索に成功した後、 返された addrinfo 構造体に対応するノードの正規名を返す。 返り値は名前解決機能から返された値の正確なコピーである。

AI_CANONIDN 名前が ACE で符号化されている場合、一つまたは複数の名前の構成要素の先頭に xn-- を含んでいる。 これらの構成要素を読み込み可能な形に変換するために、 AI_CANONNAME と共に AI_CANONIDN フラグを渡すことも出来る。 返される文字列は現在のロケールの符号化形式で符号化されている。

AI_IDN_ALLOW_UNASSIGNED, AI_IDN_USE_STD3_ASCII_RULES
これらのフラグが設定されると、 IDNA ハンドリングを使うときにそれぞれ IDNA_ALLOW_UNASSIGNED (非割り当て Unicode コードポイントを許可する) と IDNA_USE_STD3_ASCII_RULES (出力が STD3 準拠ホスト名であることを確認する) フラグが有効になる。

 

返り値

getaddrinfo(3) は成功すると 0 を返し、失敗すると以下の非 0 のエラーコードのいずれかを返す。
EAI_ADDRFAMILY
指定されたネットワークホストには、 要求されたアドレスファミリーのネットワークアドレスがない。
EAI_AGAIN
ネームサーバーから一時的な失敗 (temporary failure) を意味する返事が返された。後でもう一度試してみよ。
EAI_BADFLAGS
ai_flags のフラグに不正なフラグが含まれている。
EAI_FAIL
ネームサーバーから恒久的な失敗 (permanent failure) を意味する返事が返された。
EAI_FAMILY
要求されたアドレスファミリーがサポートされていない。
EAI_MEMORY
メモリが足りない。
EAI_NODATA
指定されたネットワークホストは存在するが、 ネットワークアドレスがひとつも定義されていない。
EAI_NONAME
nodeservice のどちらかが不明、または nodeservice の両方が NULL だった場合、または AI_NUMERICSERVhints.ai_flags に指定されていて、 hints.ai_flagsservice が数値のポート番号の文字列でない。
EAI_SERVICE
要求されたサービスは、要求されたソケットタイプでは利用できない。 他のソケットタイプでなら利用可能かもしれない。
EAI_SOCKTYPE
要求されたソケットタイプがサポートされていない。
EAI_SYSTEM
その他のシステムエラー。詳しくは errno を調べること。

gai_strerror(3) 関数を用いると、これらのエラーコードを人間に可読な文字列に変換できるので、 エラー報告に適するだろう。  

準拠

POSIX.1-2001. getaddrinfo() 関数は RFC 2553 に記載されている。  

注意

AI_ADDRCONFIG, AI_ALL, AI_V4MAPPED は glibc 2.3.3 以降で利用可能である。 AI_NUMERICSERV は glibc 2.3.4 以降で利用可能である。  

以下のプログラムは、 getaddrinfo(), gai_strerror(), freeaddrinfo(), getnameinfo(3) の使い方を示したものである。 プログラムは UDP データグラムの echo サーバとクライアントである。

サーバのプログラム:


#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>  
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>

#define BUF_SIZE 500

int
main(int argc, char *argv[])
{
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int sfd, s;
    struct sockaddr_storage peer_addr;
    socklen_t peer_addr_len;
    ssize_t nread;
    char buf[BUF_SIZE];

    if (argc != 2) {
        fprintf(stderr, "Usage: %s port\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
    hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */
    hints.ai_protocol = 0;          /* Any protocol */
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;

    s = getaddrinfo(NULL, argv[1], &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }

    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully bind(2).
       If socket(2) (or bind(2)) fails, we (close the socket
       and) try the next address. */

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype, 
                rp->ai_protocol);
        if (sfd == -1)
            continue;

        if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
            break;                  /* Success */

        close(sfd);
    }

    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not bind\n");
        exit(EXIT_FAILURE);
    }

    freeaddrinfo(result);           /* No longer needed */

    /* Read datagrams and echo them back to sender */

    for (;;) {
        peer_addr_len = sizeof(struct sockaddr_storage);
        nread = recvfrom(sfd, buf, BUF_SIZE, 0,
                (struct sockaddr *) &peer_addr, &peer_addr_len);
        if (nread == -1)
            continue;               /* Ignore failed request */

        char host[NI_MAXHOST], service[NI_MAXSERV];

        s = getnameinfo((struct sockaddr *) &peer_addr,
                        peer_addr_len, host, NI_MAXHOST,
                        service, NI_MAXSERV, NI_NUMERICSERV);
       if (s == 0)
            printf("Received %ld bytes from %s:%s\n",
                    (long) nread, host, service);
        else
            fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));


        if (sendto(sfd, buf, nread, 0,
                    (struct sockaddr *) &peer_addr,
                    peer_addr_len) != nread)
            fprintf(stderr, "Error sending response\n");
    }
}

クライアントのプログラム:


#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define BUF_SIZE 500

int
main(int argc, char *argv[])
{
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int sfd, s, j;
    size_t len;
    ssize_t nread;
    char buf[BUF_SIZE];

    if (argc < 3) {
        fprintf(stderr, "Usage: %s host port msg...\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* Obtain address(es) matching host/port */

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
    hints.ai_flags = 0;
    hints.ai_protocol = 0;          /* Any protocol */

    s = getaddrinfo(argv[1], argv[2], &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }

    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully connect(2).
       If socket(2) (or connect(2)) fails, we (close the socket
       and) try the next address. */

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype,
                     rp->ai_protocol);
        if (sfd == -1)
            continue;

        if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
            break;                  /* Success */

        close(sfd);
    }

    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not connect\n");
        exit(EXIT_FAILURE);
    }

    freeaddrinfo(result);           /* No longer needed */

    /* Send remaining command-line arguments as separate 
       datagrams, and read responses from server */

    for (j = 3; j < argc; j++) {
        len = strlen(argv[j]) + 1;
                /* +1 for terminating null byte */

        if (len + 1 > BUF_SIZE) {
            fprintf(stderr,
                    "Ignoring long message in argument %d\n", j);
            continue;
        }

        if (write(sfd, argv[j], len) != len) {
            fprintf(stderr, "partial/failed write\n");
            exit(EXIT_FAILURE);
        }

        nread = read(sfd, buf, BUF_SIZE);
        if (nread == -1) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        printf("Received %ld bytes: %s\n", (long) nread, buf);
    }

    exit(EXIT_SUCCESS);
}
 

関連項目

getipnodebyaddr(3), getipnodebyname(3) getnameinfo(3),


 

Index

名前
書式
説明
国際化ドメイン名のための getaddrinfo() の拡張
返り値
準拠
注意
関連項目

This document was created by man2html, using the manual pages.
Time: 04:31:42 GMT, November 19, 2007