[C言語] ホスト名からIPアドレスを取得する方法を解説

C言語でホスト名からIPアドレスを取得するには、getaddrinfo関数を使用します。

この関数は、ホスト名を解決し、addrinfo構造体のリストを返します。

リスト内の各addrinfo構造体には、IPアドレス情報が含まれています。

取得したaddrinfo構造体のai_addrフィールドをsockaddr_in構造体にキャストし、inet_ntop関数を使ってIPアドレスを文字列形式に変換します。

この方法はIPv4とIPv6の両方に対応しています。

この記事でわかること
  • gethostbynameとgetaddrinfoの関数の使い方と違い
  • 複数のIPアドレスを取得する方法
  • 非同期でのIPアドレス取得の実装方法
  • 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言語でネットワークプログラミングを行う際、エラーハンドリングは非常に重要です。

gethostbynamegetaddrinfo関数が失敗した場合、特定のエラーコードを確認することで、問題の原因を特定できます。

  • 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アドレスからホスト名を逆引きできます。

よくある質問

gethostbynameとgetaddrinfoの違いは何ですか?

gethostbynamegetaddrinfoは、どちらもホスト名からIPアドレスを取得するための関数ですが、いくつかの重要な違いがあります。

  • gethostbyname:
  • IPv4アドレスのみをサポートしています。
  • スレッドセーフではありません。
  • 古いAPIであり、現代のネットワークプログラミングには推奨されません。
  • getaddrinfo:
  • IPv4とIPv6の両方をサポートしています。
  • スレッドセーフであり、より柔軟な設計です。
  • 現代のネットワークプログラミングにおいて推奨されるAPIです。

IPv6アドレスを取得するにはどうすればいいですか?

IPv6アドレスを取得するには、getaddrinfo関数を使用するのが一般的です。

getaddrinfoは、IPv4とIPv6の両方をサポートしており、hints構造体のai_familyフィールドをAF_INET6に設定することで、IPv6アドレスのみを取得できます。

例:hints.ai_family = AF_INET6;と設定します。

エラーが発生した場合の対処法は?

エラーが発生した場合は、以下の方法で対処します。

  1. エラーコードの確認:
  • gethostbynameの場合、h_errno変数を確認します。
  • getaddrinfoの場合、関数の戻り値を確認し、gai_strerror関数を使用してエラーメッセージを取得します。
  1. エラーメッセージの表示:
  • エラーコードに基づいて、適切なエラーメッセージを表示します。

例:fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));

  1. 再試行または終了:
  • 一時的なエラーの場合は、再試行することを検討します。
  • 回復不能なエラーの場合は、プログラムを終了するか、ユーザーに通知します。

まとめ

ホスト名からIPアドレスを取得する方法について、gethostbynamegetaddrinfoの使い方や違い、エラーハンドリングの方法を学びました。

振り返ると、getaddrinfoはIPv6をサポートし、スレッドセーフであるため、現代のネットワークプログラミングにおいて推奨されることがわかります。

この記事を参考に、実際のプログラムでホスト名からIPアドレスを取得する機能を実装してみてください。

当サイトはリンクフリーです。出典元を明記していただければ、ご自由に引用していただいて構いません。

関連カテゴリーから探す

  • URLをコピーしました!
目次から探す