この節では、ネットワークアドレスを検出したり、構築したりするルーチンについて説明します。特に明記しない限り、インターネットファミリだけに適用されます。
リモートホスト上のサービスを検出するには、クライアントとサーバーが通信を行う前にさまざまなレベルの割り当てを行う必要があります。サービスには、人が使用するための名前が付いています。サービス名とホスト名は、ネットワークアドレスに変換され、そのネットワークアドレスを使用してホストを検出し、ホストへの経路を指定します。割り当ての細部は、ネットワークアーキテクチャーによって異なります。
標準ルーチンは、ホスト名をネットワークアドレスに、ネットワーク名をネットワーク番号に、プロトコル名をプロトコル番号に、サービス名をポート番号にマッピングします。標準ルーチンはまた、サーバープロセスとの通信で使用するために適切なプロトコルも指定します。標準ルーチンを使用する場合は、ファイル netdb.h を組み込む必要があります。
インタフェース getaddrinfo(3SOCKET)、getnameinfo(3SOCKET)、gai_strerror(3SOCKET)、および freeaddrinfo(3SOCKET) を使用すると、ホスト上のサービスの名前とアドレスを簡単に変換できます。これらは、getipnodebyname(3SOCKET)、gethostbyname(3NSL)、および getservbyname(3SOCKET) の API よりも新しいインタフェースです。IPv6 アドレスと IPv4 アドレスは、どちらも透過的に処理されます。
getaddrinfo(3SOCKET) ルーチンは、指定されたホスト名とサービス名に結合アドレスとポート番号を返します。getaddrinfo(3SOCKET) が返す情報は動的に割り当てられるので、この情報は freeaddrinfo(3SOCKET) を使用して解放し、メモリーリークを回避する必要があります。getnameinfo(3SOCKET) は、指定されたアドレスとポート番号に関連付けられたホスト名とサービス名を返します。gai_strerror(3SOCKET) を呼び出すと、getaddrinfo(3SOCKET) と getnameinfo(3SOCKET) から返される EAI_xxx コードに基づくエラーメッセージが出力されます。
次に、getaddrinfo(3SOCKET) の使用例を示します。
struct addrinfo *res, *aip;
struct addrinfo hints;
int error;
/* Get host address. Any type of address will do. */
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, sock_opt;
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);
}
インターネットホスト名とアドレスのマッピングは、gethostent(3NSL) に定義するように hostent 構造体によって表現されます。
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* hostaddrtype(e.g.,AF_INET6) */
int h_length; /* length of address */
char **h_addr_list; /* list of addrs, null terminated */
};
/*1st addr, net byte order*/
#define h_addr h_addr_list[0]
インターネットホスト名を hostent 構造体にマッピングする
インターネットホストアドレスを hostent 構造体にマッピングする
hostent 構造体のメモリーを解放する
インターネットホストアドレスを文字列にマッピングする
このルーチンは、ホストの名前、その別名、アドレスタイプ、および NULL で終了する可変長アドレスのリストを含む hostent 構造体を返します。このアドレスリストが必要なのは、ホストが多くのアドレスを持つことができるためです。h_addr 定義は下位互換性のためであり、この定義は hostent 構造体のアドレスリストの最初のアドレスです。
ネットワーク名をネットワーク番号にマッピングし、netent 構造体を返すルーチンです。
/*
* Assumes that a network number fits in 32 bits.
*/
struct netent {
char *n_name; /* official name of net */
char **n_aliases; /* alias list */
int n_addrtype; /* net address type */
int n_net; /* net number, host byte order */
};
getnetbyname(3SOCKET)、getnetbyaddr_r(3SOCKET)、および getnetent(3SOCKET) は、前述のホストルーチンに対応するネットワーク側のルーチンです。
protoent 構造体は、getprotobyname(3SOCKET)、getprotobynumber(3SOCKET)、および getprotoent(3SOCKET) で使用され、getprotoent(3SOCKET) で定義されるプロトコル名マッピングを定義します。
struct protoent {
char *p_name; /* official protocol name */
char **p_aliases /* alias list */
int p_proto; /* protocol number */
};
インターネットファミリサービスは、特定の既知のポートに常駐し、特定のプロトコルを使用します。サービス名とポート番号のマッピングは、getprotoent(3SOCKET) で定義される servent 構造体によって記述されます。
struct servent {
char *s_name; /* official service name */
char **s_aliases; /* alias list */
int s_port; /* port number, network byte order */
char *s_proto; /* protocol to use */
};
getservbyname(3SOCKET) は、サービス名、およびオプションとして修飾プロトコルを servent 構造体にマッピングします。次の呼び出しは、
sp = getservbyname("telnet", (char *) 0);
任意のプロトコルを使用する Telnet サーバーのサービス仕様を返します。次の呼び出しは、
sp = getservbyname("telnet", "tcp");
TCP プロトコルを使用する Telnet サーバーを返します。getservbyport(3SOCKET) と getservent(3SOCKET) も提供されます。getservbyport(3SOCKET) には、getservbyname(3SOCKET) によって使用されるインタフェースに似たインタフェースがあります。つまり、オプションのプロトコル名を指定して、ルックアップを修飾できます。
その他にも、名前とアドレスの操作を簡易化するルーチンはいくつかあります。次の表に、可変長のバイト列、およびバイトスワッピングのネットワークアドレスと値を要約します。
表 8–2 実行時ライブラリルーチン|
インタフェース |
機能説明 |
|---|---|
|
バイト列を比較する。同じ場合は 0、異なる場合は 0 以外の値を返す |
|
|
s2 の n バイトを s1 にコピーする |
|
|
base の最初の n バイトの領域に値 value を割り当てる |
|
|
ホストからネットワークへの 32 ビット整数バイトオーダー変換 |
|
|
ホストからネットワークへの 16 ビット整数バイトオーダー変換 |
|
|
ネットワークからホストへの 32 ビット整数バイトオーダー変換 |
|
|
ネットワークからホストへの 16 ビット整数バイトオーダー変換 |
バイトスワッピングルーチンを使用するのは、アドレスはネットワークオーダーで供給されるとオペレーティングシステムが考えるためです。一部のアーキテクチャーでは、ホストバイトオーダーがネットワークバイトオーダーと異なるため、プログラムは必要に応じて値をバイトスワップする必要があります。そのため、ネットワークアドレスを返すルーチンは、ネットワークオーダーで返します。バイトスワッピング問題が発生するのは、ネットワークアドレスを解釈する場合だけです。たとえば、次のコードは TCP ポートまたは UDP ポートをフォーマットします。
printf("port number %d\n", ntohs(sp->s_port));
これらのルーチンを必要としないマシンでは、アドレスは NULL マクロとして定義されます。