名前
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);
glibc
向けの機能検査マクロの要件
( feature_test_macros(7) 参照):
getaddrinfo(), freeaddrinfo(), gai_strerror():
Since glibc 2.22: _POSIX_C_SOURCE >= 200112L
Glibc 2.21 and earlier: _POSIX_C_SOURCE
説明
getaddrinfo() は、(インターネットのホストとサービスを識別する) node と service を渡すと、一つ以上の addrinfo 構造体を返す。それぞれの addrinfo 構造体には、 bind(2) や connect(2) を呼び出す際に指定できるインターネットアドレスが格納されている。 getaddrinfo() 関数は、 gethostbyname(3) と getservbyname(3) の機能をまとめて一つのインターフェースにしたものであるが、 これらの関数と違い、 getaddrinfo() はリエントラントであり、 getaddrinfo() を使うことでプログラムは IPv4 と IPv6 の違いに関する依存関係を なくすことができる。 getaddrinfo() が用いる addrinfo 構造体は以下のフィールドを含む。struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct addrinfo *ai_next; };
hints 引数は addrinfo 構造体を指し示し、この構造体を用いて res が指すリストに入れて返すソケットアドレス構造体を選択するための基準を指定する。 hints が NULL でない場合、 hints は addrinfo 構造体を指し示し、その構造体のフィールド ai_family, ai_socktype, ai_protocol で getaddrinfo() が返すソケットアドレス集合に対する基準を指定する。
- ai_family
- このフィールドは返されるアドレスの希望のアドレスファミリーを指定する。 このフィールドに指定できる有効な値としては AF_INET と AF_INET6 がある。 また、値 AF_UNSPEC を指定すると、 getaddrinfo() は node と service で使用できるいずれかのアドレスファミリー (例えば IPv4 か IPv6) の ソケットアドレスを返すことを求められる。
- ai_socktype
- このフィールドは推奨のソケット型 (例えば SOCK_STREAM や SOCK_DGRAM) を指定する。 このフィールドに 0 を指定すると、任意のソケット型のソケットアドレスを getaddrinfo() が返してよいことを意味する。
- ai_protocol
- このフィールドは返されるソケットアドレスのプロトコルを指定する。 このフィールドに 0 を指定すると、任意のプロトコルののソケットアドレスを getaddrinfo() が返してよいことを意味する。
- ai_flags
- このフィールドは、追加のオプション (下記) を指定する。 複数のフラグを指定する際には、それらのビット単位の OR をとって指定する。
- *
- ai_family, ai_socktype, ai_protocol フィールドはソケット生成パラメーターを返す (これらのフィールドの意味は socket(2) の同じ名前の引数と同じである)。 例えば、 ai_family は AF_INET や AF_INET6 を返し、 ai_socktype は SOCK_DGRAM や SOCK_STREAM を返し、 ai_protocol はそのソケットのプロトコルを返す。
- *
- ai_addr フィールドにはソケットアドレスへのポインターが書き込まれ、 ai_addrlen フィールドにはソケットアドレスの長さがバイト単位で書き込まれる。
国際化ドメイン名のための 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() は成功すると 0 を返し、失敗すると以下の非 0 のエラーコードのいずれかを返す。- EAI_ADDRFAMILY
- 指定されたネットワークホストには、 要求されたアドレスファミリーのネットワークアドレスがない。
- EAI_AGAIN
- ネームサーバーから一時的な失敗 (temporary failure) を意味する返事が返された。後でもう一度試してみよ。
- EAI_BADFLAGS
- hints.ai_flags のフラグに不正なフラグが含まれている。または、 hints.ai_flags に AI_CANONNAME が含まれていて、かつ name が NULL であった。
- EAI_FAIL
- ネームサーバーから恒久的な失敗 (permanent failure) を意味する返事が返された。
- EAI_FAMILY
- 要求されたアドレスファミリーがサポートされていない。
- EAI_MEMORY
- メモリーが足りない。
- EAI_NODATA
- 指定されたネットワークホストは存在するが、 ネットワークアドレスがひとつも定義されていない。
- EAI_NONAME
- node と service のどちらかが不明、または node と service の両方が NULL だった場合、または AI_NUMERICSERV が hints.ai_flags に指定されていて、 hints.ai_flags と service が数値のポート番号の文字列でない。
- EAI_SERVICE
- 要求されたサービスは、要求されたソケットタイプでは利用できない。 他のソケットタイプでなら利用可能かもしれない。 このエラーが発生する例としては、 service が "shell" (ストリームソケットでのみ利用できるサービス) で、 hints.ai_protocol に IPPROTO_UDP が指定されたり、 hints.ai_socktype に SOCK_DGRAM が指定されたりした場合がある。 また、 service が NULL 以外で、 hints.ai_socktype に SOCK_RAW (サービスの考え方をサポートしていないソケット種別) が指定された場合にも、このエラーが発生する。
- EAI_SOCKTYPE
- 要求されたソケットタイプがサポートされていない。 このエラーが発生する例としては、 hints.ai_socktype と hints.ai_protocol が矛盾している場合 (例えば hints.ai_socktype が SOCK_DGRAM で hints.ai_protocol が IPPROTO_TCP) がある。
- EAI_SYSTEM
- その他のシステムエラー。詳しくは errno を調べること。
ファイル
/etc/gai.conf属性
この節で使用されている用語の説明は attributes(7) を参照のこと。Interface | Attribute | Value |
getaddrinfo() | Thread safety | MT-Safe env locale |
freeaddrinfo(), gai_strerror() | Thread safety | MT-Safe |
準拠
POSIX.1-2001, POSIX.1-2008. getaddrinfo() 関数は RFC 2553 に記載されている。注意
getaddrinfo() は、IPv6 scope-ID を指定するために address %scope-id 記法をサポートしている。 AI_ADDRCONFIG, AI_ALL, AI_V4MAPPED は glibc 2.3.3 以降で利用可能である。 AI_NUMERICSERV は glibc 2.3.4 以降で利用可能である。 POSIX.1 によると、 hints に NULL が指定された場合、 ai_flags を 0 とみなすべきとされている。 GNU C ライブラリでは、この場合に、代わりに ai_flags を (AI_V4MAPPED | AI_ADDRCONFIG) とみなすようになっている。 この値の方が標準規格の改善になると考えられているからである。例
以下のプログラムは、 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(hints)); 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); } freeaddrinfo(result); /* No longer needed */ if (rp == NULL) { /* No address succeeded */ fprintf(stderr, "Could not bind\n"); exit(EXIT_FAILURE); } /* Read datagrams and echo them back to sender */ for (;;) { peer_addr_len = sizeof(peer_addr); 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 %zd bytes from %s:%s\n", 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; 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(hints)); 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); } freeaddrinfo(result); /* No longer needed */ if (rp == NULL) { /* No address succeeded */ fprintf(stderr, "Could not connect\n"); exit(EXIT_FAILURE); } /* Send remaining command-line arguments as separate datagrams, and read responses from server */ for (int j = 3; j < argc; j++) { len = strlen(argv[j]) + 1; /* +1 for terminating null byte */ if (len > 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 %zd bytes: %s\n", nread, buf); } exit(EXIT_SUCCESS); }
関連項目
getaddrinfo_a(3), gethostbyname(3), getnameinfo(3), inet(3), gai.conf(5), hostname(7), ip(7)この文書について
この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。2020-11-01 | GNU |