gethostbyname(3)のmanによると「gethostbyname*() と gethostbyaddr*() は過去のものである。」とあり、代わりにgetaddrinfo(3)とgetnameinfo(3)を使うようにとかかれているのでとりあえずめも。
ホスト名からIPアドレスを引く場合はgetaddrinfo(3)でIPアドレスからホスト名を引く場合はgetnameinfo(3)。
まずはgetaddrinfo(3)。関数プロトタイプはこれで、1番目の引数はホスト、2番目はポート。3番目のhintsはオプションでNULLを渡しても良いが、例えばIPv4のみ出調べたいなら構造体のメンバに値を設定する必要がある。最後の引数は結果を保存する変数。これはアドレスが複数返ってくる可能性があるので。連結リストが使われてている。
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
使い方はだいたいこの様な感じで。IPアドレスはresのai_addrをsockaddr_inへのポインタにキャストしてそこからsin_addr構造体のs_addrメンバ変数にセットされているのでinet_ntop(3)で文字列形式でIPアドレスを取得。
static void host2ip(const char *target) { struct addrinfo *result = NULL; struct addrinfo hints = { 0 }; struct addrinfo *info = NULL; int ret = 0; hints.ai_socktype = SOCK_STREAM; ret = getaddrinfo(target, NULL, &hints, &result); if (ret != 0) { printf("[-]Error %s\n", gai_strerror(ret)); exit(-1); } for (info = result; info != NULL; info = info->ai_next) { char buf[INET_ADDRSTRLEN] = { 0 }; struct in_addr ia; ia.s_addr = ((struct sockaddr_in *)(info->ai_addr))->sin_addr.s_addr; inet_ntop(info->ai_family, &ia, buf, sizeof(buf)); printf("%s -> %s\n", target, buf); } freeaddrinfo(result); }
getaddrinfo()を使った場合、プログラム側でデータの解放処理をする必要があるので最後にfreeaddrinfo()を呼ぶのを忘れずに。
次のgetnameinfo(3)はgetaddrinfo(3)よりはシンプル。
一番目のsockaddr構造体にはsin_familyにプロトコルファミリー、sin_addrにIPアドレス(をバイナリ形式にしたもの)をセットする。
hostとかservは結果を保存するバッファでオプションなのでいらなければNULLを渡す。
int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags);
プロトコルファミリーにAF_INETを指定して名前を引くのはこんな形で。
static void ip2host(const char *target) { struct sockaddr_in sa = { 0 }; char buf[NI_MAXHOST] = { 0 }; int ret = 0; sa.sin_family = AF_INET; inet_pton(sa.sin_family, target, &sa.sin_addr); ret = getnameinfo((const struct sockaddr *) &sa, sizeof(sa), buf, sizeof(buf), NULL, 0, NI_NAMEREQD); if (ret != 0) { printf("[-]Error %s\n", gai_strerror(ret)); exit(-1); } printf("%s -> %s\n", target, buf); }
getaddrinfo(3),getnameinfo(3)ともにエラーの場合は返り値をgai_strerror()に渡すとエラーメッセージを返してくれるのでそれを使えます。
これで実際に試してみるとこのようになります。
[masami@saga:~/codes]$ ./a.out -h yahoo.co.jp yahoo.co.jp -> 203.216.243.240 yahoo.co.jp -> 124.83.187.140 [masami@saga:~/codes]$ dig yahoo.co.jp ; <<>> DiG 9.9.3-rl.13207.22-P2-RedHat-9.9.3-5.P2.fc19 <<>> yahoo.co.jp ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41975 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;yahoo.co.jp. IN A ;; ANSWER SECTION: yahoo.co.jp. 12 IN A 203.216.243.240 yahoo.co.jp. 12 IN A 124.83.187.140 ;; Query time: 1 msec ;; SERVER: 192.168.1.1#53(192.168.1.1) ;; WHEN: Tue Sep 17 19:26:15 JST 2013 ;; MSG SIZE rcvd: 61
使ったソースはgistにあります。