runidn の仕様書
概要
runidn は、名前解決に gethostbyname()
などの標準的な関数
を使用している UNIX アプリケーションを、再コンパイルなしに国際化ドメイン名
を扱えるようにするためのコマンドです。
ここでは、runidn の仕様について解説します。 使用方法については、 runidn のガイド のほうをご覧ください。
実装
runidn は共有ライブラリのプリロード機構を用いて、標準的な名前解決用の 関数を、国際化ドメイン名の処理機能を持つ別のバージョンのものに動的に 置き換えることで、クライアントでの国際化ドメイン名の処理を実現します。
多くのシステムでは、共有ライブラリのプリロードは
環境変数LD_PRELOAD
を用いて実現されています。
この環境変数にプリロードしたい共有ライブラリファイルのパス名を指定すると、
アプリケーションの実行時にアプリケーションに標準の共有ライブラリがリンク
される前に指定したライブラリがリンクされます。
これによって標準のライブラリ関数を置換えることができます。
runidnの実体はこの環境変数LD_PRELOAD
を設定して引数で
指定されたコマンドを起動するだけのシェルスクリプトであり、実際に
国際化ドメイン名の処理を行うのはアプリケーションにリンクされる
共有ライブラリ libidnkitres
です。
置き換える関数
libidnkitres
が置き換える対象としている関数は、次の通り
です。
-
gethostbyname()
-
gethostbyname_r()
-
gethostbyname2()
-
gethostbyname2_r()
-
gethostbyaddr()
-
gethostbyaddr_r()
-
getipnodebyname()
-
getipnodebyaddr()
-
getaddrinfo()
-
getnameinfo()
-
freehostent()
-
freeaddrinfo()
freehostent()
と freeaddrinfo()
はそれ自体が
名前解決を行うわけではありませんが、関連する名前解決関数のメモリ管理等
の都合により置き換わります。
なお、runidn が置き換えるこれらの関数には、システムによって引数の型、 引数の個数、戻り値の型などが微妙に異なるものがあります。
gethostbyaddr()
関数については、以下の 3 通りの
形式を認識できるようになっています。
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type); struct hostent *gethostbyaddr(const char *addr, size_t len, int type); struct hostent *gethostbyaddr(const char *addr, int len, int type);
getnameinfo()
関数については、以下の 4 通りの
形式を認識できるようになっています。
int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, unsigned int flags); int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags); int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags); int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags);
gethostbyname_r()
関数については、以下の 2 通りの
形式を認識できるようになっています。
struct hostent *gethostbyname_r(const char *name, struct hostent *result, char *buffer, int buflen, int *h_errnop); int gethostbyname_r(const char *restrict name, struct hostent *restrict result_buf, char *restrict buffer, size_t buflen, struct hostent **restrict result, int *restrict h_errnop);
gethostbyaddr_r()
関数については、
以下の 2 通りの形式を認識できるようになっています。
int gethostbyaddr_r(const char *addr, int length, int format, struct hostent *restrict result_buf, char *restrict buf, size_t buflen, struct hostent **restrict result, int *restrict h_errnop); struct hostent *gethostbyaddr_r(const char *addr, int length, int type, struct hostent *result, char *buffer, int buflen, int *h_errnop);
その他の関数については、それぞれ下記のような形式を認識できるように なっています。
struct hostent *gethostbyname(const char *name); struct hostent *gethostbyname2(const char *name, int af); struct hostent *getipnodebyname(const char *name, int af, int flags, int *error_num); struct hostent *getipnodebyaddr(const void *src, size_t len, int af, int *error_num); void freehostent(struct hostent *ptr); int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res); void freeaddrinfo(struct addrinfo *ai); int gethostbyname2_r(const char *name, int af, struct hostent *restrict result_buf, char *restrict buf, size_t buflen, struct hostent **restrict result, int *restrict h_errnop);
システムに用意されている名前解決関数が runidn の想定外の形式であった場合、 最悪 runidn のコンパイル中にエラーが起きることもあります。
処理の詳細
アプリケーションが runidn によって置き変わった名前解決関数を呼び出すと、 置き変わった関数の内部では次のような処理を行います。
- 関数への引数として渡されたドメイン名を、ローカルエンコーディング から IDN エンコーディングにエンコードします。
- libidnkitres によって置き換わる前の、同名の元の関数を呼び出します。 このとき、関数に渡す引数は、置き換えた関数に渡されたものとほぼ同じ ものを用いますが、ドメイン名だけは先ほど IDN エンコーディングに エンコードしたものを渡します。
- 元の関数から問い合わせ結果のデータを受け取ったら、データに含まれる ドメイン名を IDN エンコーディングからローカルエンコーディングに デコードします。
- 置き換えた関数の処理を終え、関数の呼び出し元へ戻ります。 先ほどローカルエンコーディングにデコードした問い合わせ結果を、 アプリケーションへ返します。
ドメイン名のエンコードやデコードの処理はすべて
idnkit ライブラリ (libidnkit) の高レベル API
(api モジュール) を使用しています。
現在の実装では libidnkitres
は idnkit ライブラリ (libidnkit) を
呼び出すのではなく、libidnkit 自体を内部に抱え込むような形態になっています。