ネットワークインタフェース

標準のルーチン

ネットワークアドレスを見つけ、構成しなければならない場合があります。この節では、ネットワークアドレスを操作するルーチンについて説明します。特に明記しない限り、この節に示す関数はインターネットファミリにだけ適用されます。

リモートホスト上のサービスを見つけるには、クライアントとサーバーが通信を行う前にさまざまなレベルの割り当てを行う必要があります。サービスには、人間が使用するための名前が付いています。サービス名とホスト名は、ネットワークアドレスに変換しなければなりません。最後に、そのアドレスを使用してホストを見つけ、ホストへの経路を定めます。割り当ての細部は、ネットワークアーキテクチャによって異なります。望ましいのは、ホストに名前が付いていることをネットワークが要求せず、ホストの物理位置の同一性を保護できることです。ホストのアドレスが指定されていると、ホストの位置をより柔軟に見つけることができます。

標準ルーチンは、ホスト名をネットワークアドレスに、ネットワーク名をネットワーク番号に、プロトコル名をプロトコル番号に、サービス名をポート番号に、適切なプロトコルをサーバープロセスとの通信における使用にそれぞれ割り当てます。標準ルーチンのどれかを使用する場合は、ファイル netdb.h を含めなければなりません。

ホスト名とサービス名

インタフェース getaddrinfo(3SOCKET)getnameinfo(3SOCKET)、および freeaddrinfo(3SOCKET) を使用すると、ホスト上のサービスの名前とアドレスを簡単に変換できます。IPv6 の場合、getipnodebyname(3SOCKET)getservbyname(3SOCKET) を呼び出してアドレスの結合方法を決定する代わりに、これらのインタフェースを使用できます。同様に IPv4 では、これらのインタフェースを gethostbyname(3NSL)getservbyname(3SOCKET) の代わりに使用できます。IPv6 アドレスと IPv4 アドレスは、どちらも透過的に処理されます。

getaddrinfo(3SOCKET) は、指定されたホストの結合アドレスとポート番号、およびサービス名を返します。getaddrinfo(3SOCKET) が返す情報はすべて動的に割り当てられるため、メモリーリークが防止されるように freeaddrinfo(3SOCKET) を使用して解放しなければなりません。getnameinfo(3SOCKET) は、指定されたアドレスとポート番号に関連付けられたホスト名とサービス名を返します。getaddrinfo(3SOCKET)getnameinfo(3SOCKET) が返す EAI_xxx コードに基づくエラーメッセージを出力するには、gai_strerror(3SOCKET) を呼び出します。

getaddrinfo(3SOCKET) の使用例を次に示します。

    struct addrinfo         *res, *aip;
    struct addrinfo         hints;
    int                     sock = -1;
    int                     error;

    /* ホストアドレスを取得する。アドレスのタイプは問わない。*/
    bzero(&hints, sizeof (hints));
    hints.ai_flags = AI_ALL|AI_ADDRCONFIG;
    hints.ai_socktype = SOCK_STREAM;

    error = getaddrinfo(hostname, servicename, &hints, &res);
    if (error != 0) {
        (void) fprintf(stderr, "getaddrinfo: %s for host %s service %s¥n",
                 gai_strerror(error), hostname, servicename);
        return (-1);
    }
 

res が指す構造体の getaddrinfo(3SOCKET) が返す情報を処理したあと、次の文によって記憶領域を解放する必要があります。

        freeaddrinfo(res);

次の例に示すように、getnameinfo(3SOCKET) はエラー原因を調べるのに特に便利です。

    struct sockaddr_storage faddr;
    int                     sock, new_sock;
    socklen_t               faddrlen;
    int                     error;
    char                    hname[NI_MAXHOST];
    char                    sname[NI_MAXSERV];

     ...
         faddrlen = sizeof (faddr);
         new_sock = accept(sock, (struct sockaddr *)&faddr, &faddrlen);
         if (new_sock == -1) {
             if (errno != EINTR && errno != ECONNABORTED) {
                 perror("accept");
             }
             continue;
         }        
         error = getnameinfo((struct sockaddr *)&faddr, faddrlen, hname, 
                     sizeof (hname), sname, sizeof (sname), 0);
         if (error) {
             (void) fprintf(stderr, "getnameinfo: %s¥n",
                         gai_strerror(error));
         } else {
             (void) printf("Connection from %s/%s¥n", hname, sname);
         }

hostent - ホスト名

インターネットホスト名からアドレスへの割り当ては、次のように hostent 構造体で示されます。

struct hostent {
	   char  *h_name;            /* ホストの正式名称 */
	   char  **h_aliases;        /* 別名リスト */
	   int   h_addrtype;         /* ホストアドレスのタイプ (AF_INET6 など) */
	   int   h_length;           /* アドレスの長さ */
	   char  **h_addr_list;      /* NULL で終わるアドレスのリスト */
};
/* 最初のアドレス、ネットワークバイトオーダー */
#define h_addr h_addr_list[0]

getipnodebyname(3SOCKET) は、インターネットホスト名を hostent 構造体に割り当てます。getipnodebyaddr(3SOCKET) は、インターネットホストアドレスを hostent 構造体に割り当てます。freehostent(3SOCKET) は、hostent 構造体のメモリーを解放します。inet_ntop(3SOCKET) は、インターネットホストアドレスを表示可能な文字列に割り当てます。

このルーチンは、ホストの名前、その別名、アドレスタイプ (アドレスファミリ)、および NULL で終わる可変長アドレスのリストを含む hostent 構造体を返します。このアドレスリストが必要なのは、ホストが多くのアドレスを持つことができるためです。h_addr 定義は下位互換性のためであり、この定義は hostent 構造体のアドレスリストの最初のアドレスです。

netent - ネットワーク名

次に、ネットワーク名を番号に割り当て、netent 構造体を戻すルーチンを示します。

/*
 * ネットワーク番号が 32 ビットに収まると想定します。
 */
struct netent {
   char     *n_name;      /* ネットの正式名称 */
   char     **n_aliases;  /* 別名リスト */
   int      n_addrtype;   /* ネットアドレスのタイプ */
   int      n_net;        /* ネット番号、ホストバイトオーダー */
};

getnetbyname(3SOCKET)getnetbyaddr_r(3SOCKET)、および getnetent(3SOCKET) は、上記のホストルーチンに対するネットワーク側のルーチンです。

protoent - プロトコル名

protoent 構造体は、getprotobyname(3SOCKET)getprotobynumber(3SOCKET)、および getprotoent(3SOCKET) で使用されるプロトコル名マッピングを定義します。

struct protoent {
   char     *p_name;          /* プロトコルの正式名称 */
   char     **p_aliases       /* 別名リスト */
   int      p_proto;          /* プロトコル番号 */
};

servent - サービス名

インターネットファミリサービスは、特定の既知のポートに常駐し、特定のプロトコルを使用します。サービス名からポート番号へのマッピングは、servent 構造体で表現されます。

struct servent {
   char     *s_name;         /* サービスの正式名称 */
   char     **s_aliases;     /* 別名リスト */
   int      s_port;          /* ポート番号、ネットワークバイトオーダー */
   char     *s_proto;        /* 使用するプロトコル */
};
getservbyname(3SOCKET) は、サービス名と修飾プロトコルを servent 構造体に割り当てます (修飾プロトコルは省略可能)。次の呼び出しは、任意のプロトコルを使用する Telnet サーバーのサービス仕様を返します。
sp = getservbyname("telnet", (char *) 0);
次の呼び出しは、TCP プロトコルを使用する Telnet サーバーを返します。
sp = getservbyname("telnet", "tcp");

getservbyport(3SOCKET)getservent(3SOCKET) も提供されます。getservbyport(3SOCKET) には、getservbyname(3SOCKET) のインタフェースに似たインタフェースがあります (ルックアップを修飾するためオプションのプロトコル名を指定可能)。

その他のルーチン

名前とアドレスの操作を簡易化するルーチンには、アドレスに関連するデータベースルーチンのほかにもいくつかあります。表 2-3 は、可変長のバイト列、およびバイトスワッピングのネットワークアドレスと値を要約したものです。

表 2-3 実行時ライブラリルーチン

呼び出し 

機能説明 

memcmp(3C)

バイト列を比較する。同じ場合は 0、異なる場合は 0 以外の値を返す

memcpy(3C)

s2n バイトを s1 にコピーする

memset(3C)

base の最初の n バイトの領域に値 value を割り当てる

htonl(3SOCKET)

ホストからネットワークバイトオーダーへの 32 ビット量の変換 

htons(3SOCKET)

ホストからネットワークバイトオーダーへの 16 ビット量の変換 

ntohl(3SOCKET)

ネットワークからホストバイトオーダーへの 32 ビット量の変換 

ntohs(3SOCKET)

ネットワークからホストバイトオーダーへの 16 ビット量の変換 

バイトスワッピングルーチンが提供されているのは、アドレスはネットワークオーダーで供給されるとオペレーティングシステムが考えるためです。一部のアーキテクチャでは、ホストバイトオーダーがネットワークバイトオーダーと異なるため、プログラムは必要に応じて値のバイトスワップを行わなければなりません。ネットワークアドレスを返すルーチンは、ネットワークオーダーで返します。バイトスワッピング問題が発生するのは、ネットワークアドレスを解釈する場合だけです。たとえば、次のコードは TCP ポートまたは UDP ポートをフォーマットします。

printf("port number %d¥n", ntohs(sp->s_port));

これらのルーチンを必要としないマシンでは、アドレスは NULL マクロとして定義されます。