[C言語] ホスト名からIPアドレスを取得する方法を解説
C言語でホスト名からIPアドレスを取得するには、getaddrinfo
関数を使用します。
この関数は、ホスト名を解決し、addrinfo
構造体のリストを返します。
リスト内の各addrinfo
構造体には、IPアドレス情報が含まれています。
取得したaddrinfo
構造体のai_addr
フィールドをsockaddr_in
構造体にキャストし、inet_ntop
関数を使ってIPアドレスを文字列形式に変換します。
この方法はIPv4とIPv6の両方に対応しています。
ホスト名からIPアドレスを取得する方法
gethostbyname関数の使い方
関数の概要と引数
gethostbyname関数
は、ホスト名を指定して、そのホストに関連付けられたIPアドレスを取得するための古典的な方法です。
この関数は、ホスト名を引数として受け取り、hostent
構造体へのポインタを返します。
- 引数:
const char *name
: 取得したいホスト名を指定します。- 戻り値:
- 成功時には
hostent
構造体へのポインタを返し、失敗時にはNULL
を返します。
使用例とサンプルコード
以下は、gethostbyname関数
を使用してホスト名からIPアドレスを取得するサンプルコードです。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
int main() {
// ホスト名を指定
const char *hostname = "www.example.com";
struct hostent *host_info;
struct in_addr **addr_list;
// ホスト情報を取得
if ((host_info = gethostbyname(hostname)) == NULL) {
// エラー処理
herror("gethostbyname");
return 1;
}
// IPアドレスを表示
addr_list = (struct in_addr **)host_info->h_addr_list;
for (int i = 0; addr_list[i] != NULL; i++) {
printf("IP Address: %s\n", inet_ntoa(*addr_list[i]));
}
return 0;
}
IP Address: 93.184.216.34
このコードは、指定したホスト名に関連付けられたIPアドレスを取得し、表示します。
gethostbyname
はIPv4アドレスのみをサポートしているため、IPv6アドレスを取得する場合はgetaddrinfo
を使用する必要があります。
getaddrinfo関数の使い方
関数の概要と引数
getaddrinfo関数
は、ホスト名やサービス名を指定して、アドレス情報を取得するためのより新しい方法です。
この関数は、IPv4とIPv6の両方をサポートしています。
- 引数:
const char *node
: 取得したいホスト名またはIPアドレスを指定します。const char *service
: サービス名またはポート番号を指定します。const struct addrinfo *hints
: アドレス情報の取得条件を指定します。struct addrinfo **res
: 結果を格納するためのポインタを指定します。- 戻り値:
- 成功時には0を返し、失敗時にはエラーコードを返します。
使用例とサンプルコード
以下は、getaddrinfo関数
を使用してホスト名からIPアドレスを取得するサンプルコードです。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main() {
// ホスト名を指定
const char *hostname = "www.example.com";
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
// hints構造体をゼロクリア
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // IPv4またはIPv6
hints.ai_socktype = SOCK_STREAM;
// アドレス情報を取得
if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
// IPアドレスを表示
for (p = res; p != NULL; p = p->ai_next) {
void *addr;
const char *ipver;
// IPv4とIPv6のアドレスを取得
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// IPアドレスを文字列に変換
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf("%s: %s\n", ipver, ipstr);
}
freeaddrinfo(res); // メモリを解放
return 0;
}
IPv4: 93.184.216.34
このコードは、指定したホスト名に関連付けられたIPv4およびIPv6アドレスを取得し、表示します。
getaddrinfo
は、より柔軟で現代的な方法であり、IPv6をサポートするため、推奨されます。
エラーハンドリングの方法
エラーコードの確認
C言語でネットワークプログラミングを行う際、エラーハンドリングは非常に重要です。
gethostbyname
やgetaddrinfo関数
が失敗した場合、特定のエラーコードを確認することで、問題の原因を特定できます。
- gethostbyname:
h_errno変数
を使用してエラーコードを確認します。- 主なエラーコード:
HOST_NOT_FOUND
: 指定したホストが見つかりません。NO_ADDRESS
: ホストにはIPアドレスがありません。NO_RECOVERY
: 回復不能なエラーが発生しました。TRY_AGAIN
: 一時的なエラーが発生しました。- getaddrinfo:
- 関数の戻り値を確認し、
gai_strerror関数
を使用してエラーメッセージを取得します。
エラー処理の実装例
エラー処理を実装する際は、エラーコードを確認し、適切なメッセージを表示することが重要です。
以下に、getaddrinfo
のエラー処理の例を示します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int main() {
const char *hostname = "invalid.example.com";
struct addrinfo hints, *res;
int status;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
// アドレス情報を取得
if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
// 正常に取得できた場合の処理
// ...
freeaddrinfo(res);
return 0;
}
この例では、getaddrinfo
が失敗した場合に、gai_strerror
を使用してエラーメッセージを表示しています。
エラーが発生した場合は、適切なメッセージを表示し、プログラムを終了するか、再試行するなどの処理を行います。
応用例
複数のIPアドレスを取得する方法
ホスト名に複数のIPアドレスが関連付けられている場合、getaddrinfo関数
を使用することで、すべてのIPアドレスを取得できます。
getaddrinfo
は、結果をリンクリスト形式で返すため、リストを順にたどることで複数のアドレスを取得できます。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main() {
const char *hostname = "www.example.com";
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
printf("IP addresses for %s:\n\n", hostname);
for (p = res; p != NULL; p = p->ai_next) {
void *addr;
const char *ipver;
if (p->ai_family == AF_INET) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf("%s: %s\n", ipver, ipstr);
}
freeaddrinfo(res);
return 0;
}
このコードは、指定したホスト名に関連付けられたすべてのIPアドレスを取得し、表示します。
IPv4とIPv6の両方のアドレスをサポートしています。
非同期でのIPアドレス取得
非同期でIPアドレスを取得するには、スレッドや非同期I/Oを利用する方法があります。
ここでは、POSIXスレッドを使用して非同期にIPアドレスを取得する例を示します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <netdb.h>
#include <arpa/inet.h>
void *get_ip_address(void *hostname) {
struct addrinfo hints, *res;
int status;
char ipstr[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo((char *)hostname, NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return NULL;
}
void *addr;
if (res->ai_family == AF_INET) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
addr = &(ipv4->sin_addr);
} else {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
addr = &(ipv6->sin6_addr);
}
inet_ntop(res->ai_family, addr, ipstr, sizeof ipstr);
printf("IP Address: %s\n", ipstr);
freeaddrinfo(res);
return NULL;
}
int main() {
pthread_t thread;
const char *hostname = "www.example.com";
if (pthread_create(&thread, NULL, get_ip_address, (void *)hostname) != 0) {
fprintf(stderr, "Error creating thread\n");
return 1;
}
pthread_join(thread, NULL);
return 0;
}
この例では、スレッドを使用して非同期にIPアドレスを取得しています。
pthread_create関数
でスレッドを作成し、get_ip_address関数
を実行します。
DNSキャッシュを利用した効率化
DNSキャッシュを利用することで、同じホスト名に対するIPアドレスの取得を効率化できます。
DNSキャッシュは、OSやネットワークライブラリによって自動的に管理されることが多いですが、アプリケーションレベルでキャッシュを実装することも可能です。
- キャッシュの利点:
- 同じホスト名に対する繰り返しのDNSクエリを減らし、ネットワーク負荷を軽減します。
- レスポンス時間を短縮します。
- 実装のポイント:
- ホスト名とIPアドレスのペアをデータ構造(例:ハッシュテーブル)に保存します。
- 新しいクエリが来た際に、キャッシュをまず確認し、存在しない場合のみDNSクエリを実行します。
IPv6アドレスの取得方法
IPv6アドレスを取得するには、getaddrinfo関数
を使用します。
この関数は、IPv4とIPv6の両方をサポートしており、hints
構造体のai_family
フィールドをAF_INET6
に設定することで、IPv6アドレスのみを取得できます。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main() {
const char *hostname = "www.example.com";
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET6; // IPv6のみ
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
printf("IPv6 addresses for %s:\n\n", hostname);
for (p = res; p != NULL; p = p->ai_next) {
void *addr;
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf("IPv6: %s\n", ipstr);
}
freeaddrinfo(res);
return 0;
}
このコードは、指定したホスト名に関連付けられたIPv6アドレスを取得し、表示します。
ホスト名から逆引きでホスト名を取得する方法
IPアドレスからホスト名を逆引きするには、getnameinfo関数
を使用します。
この関数は、IPアドレスをホスト名に変換するために使用されます。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
int main() {
const char *ip = "93.184.216.34";
struct sockaddr_in sa;
char host[1024];
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
inet_pton(AF_INET, ip, &sa.sin_addr);
if (getnameinfo((struct sockaddr *)&sa, sizeof sa, host, sizeof host, NULL, 0, 0) != 0) {
fprintf(stderr, "getnameinfo: error\n");
return 1;
}
printf("Host name: %s\n", host);
return 0;
}
このコードは、指定したIPアドレスに関連付けられたホスト名を取得し、表示します。
getnameinfo関数
を使用することで、IPアドレスからホスト名を逆引きできます。
まとめ
ホスト名からIPアドレスを取得する方法について、gethostbyname
とgetaddrinfo
の使い方や違い、エラーハンドリングの方法を学びました。
振り返ると、getaddrinfo
はIPv6をサポートし、スレッドセーフであるため、現代のネットワークプログラミングにおいて推奨されることがわかります。
この記事を参考に、実際のプログラムでホスト名からIPアドレスを取得する機能を実装してみてください。