GETADDRINFO(3) | Linux Programmer's Manual | GETADDRINFO(3) |
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():
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() が返すソケットアドレス集合に対する基準を指定する。
hints が指し示す構造体の他のすべてのフィールドには 0 かヌルポインターを適切に入れなければならない。
hints に NULL を指定するのは、 ai_socktype と ai_protocol に 0 を、 ai_family に AF_UNSPEC を、 ai_flags に (AI_V4MAPPED | AI_ADDRCONFIG) を設定するのと等価である (POSIX では ai_flags には別のデフォルト値が規定されている; 「備考」を参照)。 node には、数値形式のネットワークアドレス (IPv4 の場合は inet_aton(3) でサポートされているドット区切りの数字による表記、 IPv6 の場合は inet_pton(3) でサポートされている 16 進数の文字列形式) もしくは ネットワークホスト名を指定する。 ネットワークホスト名を指定した場合には、そのネットワークアドレスが検索され、 名前解決が行なわれる。 hints.ai_flags に AI_NUMERICHOST フラグが含まれている場合は、 node は数値形式のネットワークアドレスでなければならない。 AI_NUMERICHOST フラグを使うと、時間の掛かる可能性のあるネットワークホストアドレスの検索は すべて抑制される。
hints.ai_flags に AI_PASSIVE フラグが指定され、かつ node が NULL の場合、 返されるソケットアドレスは コネクションを accept(2) するためのソケットを bind(2) するのに適したものとなる。 返されるソケットアドレスには「ワイルドカードアドレス」 (IPv4 アドレスの場合は INADDR_ANY、 IPv6 アドレスの場合は IN6ADDR_ANY_INIT) が入る。 ワイルドカードアドレスは、任意のホストのネットワークアドレスで接続を 受け付けようとするアプリケーション (通常はサーバー) で用いられる。 node が NULL でない場合、 AI_PASSIVE フラグは無視される。
hints.ai_flags に AI_PASSIVE フラグがセットされていない場合、 返されるソケットアドレスは connect(2), sendto(2), sendmsg(2) での使用に適したものとなる。 node が NULL の場合、ネットワークアドレスにはループバックインターフェイスの アドレス (IPv4 アドレスの場合は INADDR_LOOPBACK IPv6 アドレスの場合は IN6ADDR_LOOPBACK_INIT)が設定される。 これは同じホスト上で動作している接続相手と通信するような アプリケーションで用いられる。
service により、返される各アドレス構造体のポート番号が決まる。 この引き数がサービス名 (services(5) 参照) の場合、対応するポート番号に翻訳される。 この引き数には 10 進数も指定することができ、 この場合にはバイナリへの変換だけが行われる。 service が NULL の場合、返されるソケットアドレスのポート番号は 初期化されないままとなる。 hints.ai_flags に AI_NUMERICSERV が指定され、かつ service が NULL でない場合、 service は数値のポート番号を含む文字列を指し示さなければならない。 このフラグは、名前解決サービスが不要であることが分かっている場合に、 サービスの起動を抑制するために用いられる。
node と service のどちらかは NULL にしてよいが、両方同時に NULL にしてはならない。
getaddrinfo() 関数は、 addrinfo 構造体のメモリー確保を行い、 addrinfo 構造体のリンクリストを初期化し、 res にリストの先頭へのポインターを入れて返す。 このとき、各構造体のネットワークアドレスは node と service に一致し、 hints で課されたすべての制限を満たすものとなる。 リンクリストの要素は ai_next フィールドにより連結される。
リンクリストの addrinfo 構造体は複数個になることもあり、その理由はいくつかある。 ネットワークホストがマルチホームである、 複数のプロトコルでアクセスできる (例えば AF_INET と AF_INET6 の両方) 、 複数のソケット種別で同じサービスが利用できる (例えば、ひとつが SOCK_STREM アドレスで、もうひとつが SOCK_DGRAM アドレスである)、がある。 通常は、アプリケーションは返された順序でアドレスを試すべきである。 getaddrinfo() の中で使用される並べ替え関数は RFC 3484 で定義されている。 特殊なシステムでは、 /etc/gai.conf を編集することで、この順序を微調整することができる (/etc/gai.conf は glibc 2.5 以降で利用できる)。
hints.ai_flags に AI_CANONNAME フラグが含まれている場合、返されるリストの最初の addrinfo 構造体の ai_canonname フィールドはホストの公式な名前を指すように設定される。
返される各々の addrinfo 構造体の残りのフィールドは以下のように初期化される。
hints.ai_flags が AI_ADDRCONFIG を含む場合、 res が指すリストには、ローカルシステムに最低一つの IPv4 アドレスが設定されている場合のみ IPv4 アドレスが返され、 ローカルシステムに最低一つの IPv6 アドレスが設定されている場合にのみ IPv6 アドレスが返される。 なお、この場合には、ループバックアドレスは有効に設定されたアドレスとはみなされない。 このフラグは、例えば、IPv4 だけのシステムで、 getaddrinfo() が必ず IPv6 ソケットアドレスを返さないことを保証するのに役立つ。 IPv4 だけのシステムでは、IPv6 アドレスは connect(2) や bind(2) で必ず失敗することになる。
hints.ai_flags に AI_V4MAPPED が指定されていて、 hints.ai_family に AF_INET6 が指定され、 マッチする IPv6 アドレスが見つからなかった場合、 res が指すリストには IPv4-mapped IPv6 アドレスが返される。 hints.ai_flags に AI_V4MAPPED と AI_ALL の両方が指定されている場合、 res が指すリストには IPv6 アドレスと IPv4-mapped IPv6 アドレスの 両方が返される。 AI_V4MAPPED が指定されていない場合、 AI_ALL は無視される。
freeaddrinfo() 関数は、 リンクリスト res に対して動的に割り当てられたメモリーを解放する。
glibc 2.3.4 から、 getaddrinfo() は入出力するホスト名を透過的に国際化ドメイン名 (IDN) 形式 (RFC 3490 の Internationalizing Domain Names in Applications (IDNA) を参照のこと) と変換することを選択的に認めるように拡張されている。 4 つの新しいフラグが定義されている:
入力名に非 ASCII 文字が含まれている場合、 IDN 符号化形式が使われる。 非 ASCII 文字が含まれている(ピリオドで区切られる)部分ノード名は、 名前解決機能に渡される前に ASCII 互換符号化形式 (ACE) を使って 符号化される。
AI_CANONIDN 名前が ACE で符号化されている場合、一つまたは複数の名前の構成要素の先頭に xn-- を含んでいる。 これらの構成要素を読み込み可能な形に変換するために、 AI_CANONNAME と共に AI_CANONIDN フラグを渡すことも出来る。 返される文字列は現在のロケールの符号化形式で符号化されている。
getaddrinfo() は成功すると 0 を返し、失敗すると以下の非 0 のエラーコードのいずれかを返す。
gai_strerror() 関数を用いると、これらのエラーコードを人間に可読な文字列に変換できるので、 エラー報告に適するだろう。
/etc/gai.conf
POSIX.1-2001. 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-2001 によると、 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(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 %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, 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 %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 プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2014-04-06 | GNU |