多言語ドメイン名変換 API
概要
多言語ドメイン名の処理は次の2つの処理から構成されます。
- エンコーディング変換
- NAMEPREP
mDNkit では、これに加えて地域化のために
- デリミタマッピング
- トップレベルドメインに基づくローカルマッピング
の2つの処理を追加しています。
多言語ドメイン処理のアーキテクチャ
IDNA
では、これらの処理はすべてアプリケーションで行うことになっており、
例えば名前解決用の関数 gethostbyname を呼び出して
多言語ドメイン名の名前解決を行う場合には、
すでにこれらの処理が行われた結果の文字列を渡すことになっています。
mDNkit はアプリケーションでこれらの処理を行うために 2種類の API を用意しています。
- 高レベル API
- 一般アプリケーションのためのシンプルで使いやすい API です。 この API を使用すれば、アプリケーションに簡単に多言語ドメイン処理機能を 追加することができます。ただし細かい設定などはできません。
- 低レベル API
- 高レベル API ではできないような細かい設定をする必要がある 一部の特殊なアプリケーションのための API です。 高レベル API はこの低レベル API の上に作られています。
低レベル API は特殊なアプリケーションのために用意された API ですので、
ここでは説明しません。もし低レベル API を使用したい場合には、
MDN ライブラリ仕様書の
resconfモジュール
および
resモジュール
をご覧ください。
高レベル API は次の3つの関数から構成されます。
- 初期化を行う
mdn_nameinit - 名前解決などのために多言語ドメイン名をエンコードする
mdn_encodename - DNS サーバなどから返ってきた多言語ドメイン名をデコードする
mdn_decodename
以下、それぞれの関数の説明をします。
MDN ライブラリ仕様書の
apiモジュール
も合わせてご覧ください。
初期化
多言語ドメイン名の処理のための初期化を行うためには
mdn_nameinit を用います。
mdn_result_t mdn_nameinit(void)
この関数は MDN ライブラリ全体の初期化を行い、次に 多言語ドメイン名の処理のための各種の設定が記述されている 設定ファイル (mdn.conf) を読み込みます。
この関数は戻り値として実行結果を示す
mdn_result_t 型の値を返します。
本関数が返すコードとその意味は次の通りです。
mdn_success- 初期化が正常に終了した。
mdn_nofile- 設定ファイルがオープンできなかった。
mdn_invalid_syntax- 設定ファイルにシンタックスエラーがある。
mdn_invalid_name- 設定ファイルに指定されている名前 (エンコーディング名など) に誤りがある。
mdn_nomemory- malloc に失敗した。
この関数を複数回呼び出すこともできますが、その場合 MDN ライブラリ全体の初期化が行われるのは最初の呼び出しの時だけで、 2回目以降は設定ファイルの再読み込みのみが行われます。 アプリケーションの実行中に設定ファイルの内容を変更した場合、 この関数を呼び出すことで最新の設定に合わせることができます。
この関数をあらかじめ呼び出さずにエンコードやデコード関数を 呼び出しても構いません。その場合には、エンコードやデコードの処理に先だち この関数で行われるような初期化の処理が暗黙的に行われます。
エンコード
多言語ドメイン名のエンコード処理、つまり名前解決関数などへ渡すための
文字列に変換するには mdn_encodename を用います。
mdn_result_t mdn_encodename(int actions, const char *from,
char *to, size_t tolen)
from で指定されるドメイン名に対して、actions で 指定される処理を行い、その結果を to の指す領域に書き込みます。 tolen は to の指す領域の大きさ (バイト数) で、 tolen を越えて書き込むことはありません。
この関数による多言語ドメイン名のエンコード処理は一般的には 次のような手順になります。
- エンコーディング変換
アプリケーションの使用しているローカルエンコーディング (SJIS 等) から UTF-8 への変換 - デリミタマッピング
特定の文字を、ドメイン名の区切りであるドットに変換 - トップレベルドメインに基づくローカルマッピング
入力されたドメイン名のトップレベルドメインに対応するマッピング - NAMEPREP
多言語ドメイン名の正規化、禁止文字の検出 - エンコーディング変換
UTF-8 から、多言語ドメイン名の標準エンコーディング (IDN エンコーディング) への変換
引数 actions で、これらのどの処理を実際に実行するかを以下に示す フラグで指定します。 実際に actions に指定するのはこれらのビット毎の論理和です。
MDN_LOCALCONV- ローカルエンコーディングから UTF-8 への変換を行う
MDN_DELIMMAP- デリミタマッピングを行う
MDN_LOCALMAP- トップレベルドメインに基づくローカルマッピングを行う
MDN_NAMEPREP- 多言語ドメイン名の正規化、禁止文字の検出を行う
MDN_UNASCHECK- NAMEPREP の中のオプショナルな機能 (実行してもしなくてもよい) である 未定義文字の検出を実行する
MDN_IDNCONV- UTF-8 から IDN エンコーディングへの変換を行う
通常のアプリケーションは MDN_UNASCHECK を除くすべての処理を
行えばよいはずです。そのために MDN_ENCODE_APP というマクロが
定義されており、actions にこの値を指定すれば未定義文字の検出を
除くすべての処理が行われます。
なお、上記の各処理でのパラメータはすべて mDNkit の 設定ファイル (mdn.conf) で設定されたものが使用されます。以下に使用されるパラメータを記します。
- デリミタマッピングにおいてドットにマッピングする文字
- ローカルマッピング規則
- NAMEPREP のバージョン
- IDN エンコーディングとして使用するエンコーディング名
この関数は戻り値として実行結果を示す
mdn_result_t 型の値を返します。
本関数が返すコードとその意味は次の通りです。
mdn_success- 処理が正常に終了した。
mdn_invalid_action- actions で誤ったフラグを指定している。
mdn_invalid_encoding- from で指定された文字列のエンコーディングに誤りがある。
mdn_prohibited-
入力文字列中に禁止文字が存在する。
フラグ
MDN_UNASCHECKを指定した場合には未定義文字が 存在した場合にもこのコードを返す。 mdn_buffer_overflow- tolen の値が小さすぎて結果を格納できない。
mdn_nomemory- malloc に失敗した。
なお、初期化関数 mdn_nameinit を事前に呼び出さずに
本関数を呼び出した場合には、変換処理に先だって初期化が行われます。
この場合には本関数は上に示した他に次のような実行結果を返すことがあります。
mdn_nofile- 設定ファイルがオープンできなかった。
mdn_invalid_syntax- 設定ファイルにシンタックスエラーがある。
mdn_invalid_name- 設定ファイルに指定されている名前 (エンコーディング名など) に誤りがある。
デコード
多言語ドメイン名のデコード処理、つまり名前解決関数などから返された
エンコード済のドメイン名文字列を、アプリケーションが使用している
エンコーディングの文字列に変換するには mdn_decodename を用います。
mdn_result_t mdn_decodename(int actions, const char *from,
char *to, size_t tolen)
from で指定されるドメイン名に対して、actions で 指定される処理を行い、その結果を to の指す領域に書き込みます。 tolen は to の指す領域の大きさ (バイト数) で、 tolen を越えて書き込むことはありません。
この関数による多言語ドメイン名のデコード処理は一般的には 次のような手順になります。
- エンコーディング変換
多言語ドメイン名の標準エンコーディング (IDN エンコーディング) から UTF-8 への変換 - エンコーディング変換
UTF-8 からアプリケーションの使用しているローカルエンコーディング (SJIS 等) への変換
名前解決関数などから返されるホスト名は、DNS サーバに登録する際に あらかじめ NAMEPREP など必要な正規化が行われているはずなので 処理はエンコーディング変換のみです。
引数 actions で、これらのどの処理を実際に実行するかを以下に示す フラグで指定します。 実際に actions に指定するのはこれらのビット毎の論理和です。
MDN_IDNCONV- IDN エンコーディングから UTF-8 への変換を行う
MDN_LOCALCONV- UTF-8 からローカルエンコーディングへの変換を行う。 ただしローカルエンコーディングに対応する文字がないために 変換不可能な場合には、代替エンコーディングと呼ばれる エンコーディングへの変換を行う。
通常のアプリケーションは これら両方の処理を
行えばよいはずです。そのために MDN_DECODE_APP というマクロが
定義されており、actions にこの値を指定すれば上記の2つの
処理が行われます。
なお、上記の各処理でのパラメータはすべて mDNkit の 設定ファイル (mdn.conf) で設定されたものが使用されます。以下に使用されるパラメータを記します。
- IDN エンコーディングとして使用するエンコーディング名
- 代替エンコーディングのエンコーディング名
この関数は戻り値として実行結果を示す
mdn_result_t 型の値を返します。
本関数が返すコードとその意味は次の通りです。
mdn_success- 処理が正常に終了した。
mdn_invalid_action- actions で誤ったフラグを指定している。
mdn_invalid_encoding- from で指定された文字列のエンコーディングに誤りがある。
mdn_buffer_overflow- tolen の値が小さすぎて結果を格納できない。
mdn_nomemory- malloc に失敗した。
なお、初期化関数 mdn_nameinit を事前に呼び出さずに
本関数を呼び出した場合には、変換処理に先だって初期化が行われます。
この場合には本関数は上に示した他に次のような実行結果を返すことがあります。
mdn_nofile- 設定ファイルがオープンできなかった。
mdn_invalid_syntax- 設定ファイルにシンタックスエラーがある。
mdn_invalid_name- 設定ファイルに指定されている名前 (エンコーディング名など) に誤りがある。
プログラム作成方法
上記の API を使用したプログラムを作る方法、および注意点などについて まとめておきます。
- インクルードファイル
高レベル API を使用するプログラムでは、stddef.hとmdn/api.hの2つのヘッダファイルをインクルードしてください。#include <stddef.h> #include <mdn/api.h> - ロケール設定
mDNkit はアプリケーションの使用しているローカルエンコーディングを ロケール情報、あるいは環境変数MDN_LOCAL_CODESETから 取得します。ロケール情報から取得する場合にはアプリケーションの先頭でsetlocaleを行い、アプリケーションのロケールを正しく 設定してください。 - エラー表示
API はいずれもmdn_result_t型の値を返します。この値から 対応するメッセージ文字列を得るための関数mdn_result_tostringが用意されています。エラーメッセージを表示する際に使うことができます。 この関数については 仕様書の説明をご覧ください。 - コンパイルとリンク
コンパイルする際には、-Iオプションで mDNkit の ヘッダファイルのインストールディレクトリ (デフォルトでは/usr/local/include) を指定してください。
またリンクの際は MDN ライブラリをリンクしてください。もし iconv が標準ライブラリにない場合には、iconv の ライブラリも合わせてリンクしてください。cc -I/usr/local/include example.c -L/usr/local/lib -lmdn -liconv
プログラム例
ここでは、上記の API を使用して多言語ドメイン名の名前解決を行うための 簡単なプログラムを紹介します。
#include <stdio.h>
#include <stddef.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <mdn/api.h>
int main(int ac, char **av)
{
struct hostent *hp;
char buf1[256];
char buf2[256];
char addrbuf[100];
mdn_result_t r;
/* ロケールを設定する */
setlocale(LC_ALL, "");
if (ac != 2) {
fprintf(stderr, "Usage: %s hostname\n", av[0]);
return 1;
}
/* gethostbyname を呼ぶ前に名前を変換する */
if ((r = mdn_encodename(MDN_ENCODE_APP, av[1],
buf1, sizeof(buf1))) != mdn_success) {
fprintf(stderr, "mdn_encodename: %s\n", mdn_result_tostring(r));
return 1;
}
/* 名前解決を行う */
if ((hp = gethostbyname(buf1)) == NULL) {
fprintf(stderr, "gethostbyname failed\n");
return 1;
}
/* 返された名前をローカルエンコーディングに変換する */
if ((r = mdn_decodename(MDN_DECODE_APP, hp->h_name,
buf2, sizeof(buf2))) != mdn_success) {
fprintf(stderr, "mdn_decodename: %s\n", mdn_result_tostring(r));
return 1;
}
printf("%s %s\n",
inet_ntop(hp->h_addrtype, hp->h_addr, addrbuf, sizeof(addrbuf)),
buf2);
return 0;
}

