bind-9 patch

# BIND-9 パッチ

- 概要
- アーキテクチャ
- named 修正
- lwresd 修正
- クライアントライブラリ修正
- dig/host/nslookup 修正

## 概要

mDNkit に含まれる BIND-9 用のパッチファイルを適用することにより、 BIND-9 において従来の ASCII 文字によるドメイン名に加え、 非 ASCII 文字による多言語ドメイン名を扱うことが可能になります。

具体的には、このパッチは BIND-9 に次のような機能を追加します。

- lightweight resolver API (lwres_gethostbyname など) で、 ローカルエンコーディングで表現されたホスト名を名前解決する
- 同じく名前解決結果としてローカルエンコーディングで表現された ホスト名を返す
- dig、host、nslookup コマンドがローカルエンコーディングのドメイン名を 受け付け、また問い合わせ結果に含まれるドメイン名をローカルエンコーディングで 表示する

BIND-9 へのパッチの適用方法は、パッチファイルの先頭に記載してあります。 なおパッチファイルについては、 対応する BIND のバージョンをよく確認してから、使用してください。

## アーキテクチャ

BIND-9 はそれまでの BIND-8 とはリゾルバの構成が全く異なっています。 BIND-8 まではリゾルバはアプリケーションにリンクされるライブラリとして 実装されていましたが、 BIND-9 ではリゾルバの機能の大部分が lwresd (lightweight resolver daemon) というサーバに移され、 アプリケーションにリンクされるのはlightweight resolver と呼ばれる lwresd との通信を行うためのライブラリとなっています。

本パッチはこのアーキテクチャに合わせ、多言語ドメイン名の各処理を それぞれのコンポーネントに分散させました。以下に各コンポーネントで 実装される機能とコンポーネント間のインタフェースで用いるエンコーディングを 表した図を示します。

## named 修正

named における修正は次の一点のみです。

### UTF-8 文字の透過

BIND-9 の named ではドメイン名の処理はほとんど8ビットスルーになっており、 UTF-8 等の MSB のセットされたバイトもそのまま透過します。 しかし named 内で処理に使用されているドメイン名の内部表現 (dns_name_t) からテキスト表現への変換部分は8ビットスルーになっておらず、ASCII 文字以外 はバックスラッシュでクォートされ、`\DDD` という形式の表現に 変換されてしまいます。 DNS プロトコル上で用いられるエンコーディングが RACE などの ASCII 互換 エンコーディングであれば ASCII 以外の文字がドメイン名に現れることはないので 問題ないのですが、UTF-8 を使用する場合には問題となります。

BIND-9 パッチは、この処理を修正し、変換しようとする文字がUTF-8 文字の 場合にはバックスラッシュによるクォートをしないようにします。 この動作は named の設定ファイル (named.conf) で切り替え可能であり、 設定ファイルの options セクションの `allow-utf8` パラメータで 指定します。DNS プロトコル上で用いるエンコーディングとして UTF-8 を 使用する際には次のように記述してください。

>     options {
>         ...
>         allow-utf8 yes;
>         ...
>     };

## lwresd 修正

lwresd に対しては次のような修正を加えてあります。

UTF-8 文字の透過

  これは named に対する修正と同じで、ドメイン名を内部表現から テキスト表現に変換する際のバックスラッシュによるクォーティングを 行わないようにするものです。

  named の場合、これは設定ファイル named.conf のオプションで 切り替えられるようになっていますが、lwresd の場合には自動切り替えに なっています。lwresd が読み込む クライアント設定ファイルの サーバエンコーディングエントリに設定されているコードセット名を参照し、 それが ASCII 互換エンコーディングでない場合にはバックスラッシュによる クォートをしないようにします。

エンコーディング変換

  lwresd は多言語ドメイン名のエンコーディングとして、クライアント つまり lightweight resolver ライブラリとの間の通信には UTF-8 を、 また named との通信には DNS プロトコル上のエンコーディングをそれぞれ 使用します。したがって両者の間のエンコーディング変換を行います。

  エンコーディング変換には MDN ライブラリの res モジュール を使用しています。 DNS プロトコル上のエンコーディングの指定は クライアント設定ファイルで行います。

## クライアント側ライブラリ修正

クライアント側のライブラリは、

- エンコーディング変換
- 正規化

を行うように修正されています。これらの処理には MDN ライブラリの res モジュール を使用しています。 設定はクライアント設定ファイルで行います。

名前解決用の API 関数はローカルエンコーディングで表された 多言語ドメイン名が扱えるように修正されています。 また内部実装は、将来 UTF-8 エンコーディングのドメイン名を扱える API の追加が容易にできるように設計されています。これらについて この後説明します。

- 多言語化された API 関数
- 追加された内部関数

### 多言語化された API 関数

パッチによって多言語化される API 関数の一覧です。

- lwres_getaddrinfo()
- lwres_getnameinfo()
- lwres_gethostbyname()
- lwres_gethostbyname2()
- lwres_gethostbyname_r()
- lwres_gethostbyaddr()
- lwres_gethostbyaddr_r()
- lwres_getipnodebyname()
- lwres_getipnodebyaddr()

多言語化された API 関数は、従来の ASCII ドメイン名に加え、 非 ASCII ドメイン名を扱うことができます。非 ASCII ドメイン名として、 クライアント設定ファイルの内容に応じて、 新たに以下のようなドメイン名を取り扱うことができるようになります。

- ホスト.会社.jp
- www.別の会社.com

引数の型や返り値の型に変更はありません。 パッチを当てる前と同様の呼び出し方法で、従来の ASCII ドメイン名と、 非 ASCII ドメイン名の両方を扱うことができます。 非 ASCII ドメイン名については、 ローカルエンコーディングのドメイン名を引数として受け取ることができます。

これまで上記の API 関数で行なわれていた処理は、 以降で説明するそれぞれの内部関数に移行し、API 関数は、ドメイン名のエンコーディング情報をこの内部関数に渡す役割をしています。

### 追加された内部関数

多言語化された API 関数に関連して、以下の内部関数が追加されました。

lwres_getaddrinfo_enc()
  lwres_getaddrinfo() の内部関数
lwres_getnameinfo_enc()
  lwres_getnameinfo() の内部関数
lwres_gethostbyname_enc()
  lwres_gethostbyname() の内部関数
lwres_gethostbyname2_enc()
  lwres_gethostbyname2() の内部関数
lwres_gethostbyname_r_enc()
  lwres_gethostbyname_r() の内部関数
lwres_gethostbyaddr_enc()
  lwres_gethostbyaddr() の内部関数
lwres_gethostbyaddr_r_enc()
  lwres_gethostbyaddr_r() の内部関数
lwres_getipnodebyname_enc()
  lwres_getipnodebyname() の内部関数
lwres_getipnodebyaddr_enc()
  lwres_getipnodebyaddr() の内部関数

追加された内部関数についての仕様は、以下の通りです。

lwres_getaddrinfo_enc

      int
      lwres_getaddrinfo_enc(const char *, const char *, const struct addrinfo *,
      		      struct addrinfo **, int)

  lwres ライブラリの関数 lwres_getaddrinfo() に、 ローカルエンコーディングか否かの判定フラグのエントリを追加したものです。 lwres_getaddrinfo() との違いは、static 関数である set_order() 関数にエンコードタイプの判定フラグを渡している点のみです。

  関数の最後の引数がエンコードタイプの判定フラグで、 ローカルエンコーディングのときには`LWRES_ENCODING_LOCAL`、 UTF-8 エンコーディングのときには`LWRES_ENCODING_UCS`が指定可能です。 lwres_getaddrinfo() 関数から lwres_getaddrinfo_enc() 関数を呼び出すときは、 エンコードタイプの判定フラグに`LWRES_ENCODING_LOCAL`を指定しています。

  返される値は `EAI_NONAME`、`EAI_BADFLAGS`、`EAI_SYSTEM`、 `EAI_FAMILY`、`EAI_SOCKTYPE`、`EAI_SERVICE`、 `EAI_MEMORY`、`0` のいずれかです。

lwres_getnameinfo_enc

      int
      lwres_getnameinfo_enc(const struct sockaddr *, size_t, char *, size_t, char *,
      		      size_t, int, int)

  lwres ライブラリの関数 lwres_getnameinfo() に、 ローカルエンコーディングか否かの判定フラグのエントリを追加したものです。 lwres_getnameinfo() との違いは、lwres_getnamebyaddr() 関数の呼び出し部を lwres_getnamebyaddr_enc() 関数に変更している点のみです。

  関数の最後の引数がエンコードタイプの判定フラグで、 ローカルエンコーディングのときには`LWRES_ENCODING_LOCAL`、 UTF-8 エンコーディングのときには`LWRES_ENCODING_UCS`が指定可能です。 lwres_getnameinfo() 関数から lwres_getnameinfo_enc() 関数を呼び出すときは、 エンコードタイプの判定フラグに`LWRES_ENCODING_LOCAL`を指定しています。

  返される値は `ENI_NOSOCKET`、`ENI_SALEN`、`ENI_FAMILY`、 `ENI_MEMORY`、`ENI_SYSTEM`、`ENI_NOHOSTNAME`、 `SUCCESS` のいずれかです。

lwres_gethostbyname_enc

      struct hostent *
      lwres_gethostbyname_enc(const char *, int)

  lwres ライブラリの関数 lwres_gethostbyname() に、 ローカルエンコーディングか否かの判定フラグのエントリを追加したものです。 lwres_gethostbyname() との違いは、lwres_getipnodebyname() 関数の呼び出し部を lwres_getipnodebyname_enc() 関数に変更している点のみです。

  関数の最後の引数がエンコードタイプの判定フラグで、 ローカルエンコーディングのときには`LWRES_ENCODING_LOCAL`、 UTF-8 エンコーディングのときには`LWRES_ENCODING_UCS`が指定可能です。 lwres_gethostbyname() 関数から lwres_gethostbyname_enc() 関数を呼び出すときは、 エンコードタイプの判定フラグに`LWRES_ENCODING_LOCAL`を指定しています。

  lwres_getipnodebyname_enc() から返って来た hostent 構造体へのポインタを返します。

lwres_gethostbyname2_enc

      struct hostent *
      lwres_gethostbyname2_enc(const char *, int, int)

  lwres ライブラリの関数 lwres_gethostbyname2() に、 ローカルエンコーディングか否かの判定フラグのエントリを追加したものです。 lwres_gethostbyname2() との違いは、lwres_getipnodebyname() 関数の呼び出し部を lwres_getipnodebyname_enc() 関数に変更している点のみです。

  関数の最後の引数がエンコードタイプの判定フラグで、 ローカルエンコーディングのときには`LWRES_ENCODING_LOCAL`、 UTF-8 エンコーディングのときには`LWRES_ENCODING_UCS`が指定可能です。 lwres_gethostbyname2() 関数から lwres_gethostbyname2_enc() 関数を呼び出すときは、 エンコードタイプの判定フラグに`LWRES_ENCODING_LOCAL`を指定しています。

  lwres_getipnodebyname_enc() から返って来た hostent 構造体へのポインタを返します。

lwres_gethostbyname_r_enc

      struct hostent *
      lwres_gethostbyname_r_enc(const char *, struct hostent *, char *,
      			  int, int *, int)

  lwres ライブラリの関数 lwres_gethostbyname_r() に、 ローカルエンコーディングか否かの判定フラグのエントリを追加したものです。 lwres_gethostbyname_r() との違いは、lwres_getipnodebyname() 関数の呼び出し部を lwres_getipnodebyname_enc() 関数に変更している点のみです。

  関数の最後の引数がエンコードタイプの判定フラグで、 ローカルエンコーディングのときには`LWRES_ENCODING_LOCAL`、 UTF-8 エンコーディングのときには`LWRES_ENCODING_UCS`が指定可能です。 lwres_gethostbyname_r() 関数から lwres_gethostbyname_r_enc() 関数を呼び出すときは、 エンコードタイプの判定フラグに`LWRES_ENCODING_LOCAL`を指定しています。

  エラーのときは NULL、正常のときは値が格納された hostent 構造体へのポインタを返します。

lwres_gethostbyaddr_enc

      struct hostent *
      lwres_gethostbyaddr_enc(const char *, int, int, int)

  lwres ライブラリの関数 lwres_gethostbyaddr() に、 ローカルエンコーディングか否かの判定フラグのエントリを追加したものです。 lwres_gethostbyaddr() との違いは、lwres_getipnodebyaddr() 関数の呼び出し部を lwres_getipnodebyaddr_enc() 関数に変更している点のみです。

  関数の最後の引数がエンコードタイプの判定フラグで、 ローカルエンコーディングのときには`LWRES_ENCODING_LOCAL`、 UTF-8 エンコーディングのときには`LWRES_ENCODING_UCS`が指定可能です。 lwres_gethostbyaddr() 関数から lwres_gethostbyaddr_enc() 関数を呼び出すときは、 エンコードタイプの判定フラグに`LWRES_ENCODING_LOCAL`を指定しています。

  lwres_getipnodebyaddr_enc() から返って来た hostent 構造体へのポインタを返します。

lwres_gethostbyaddr_r_enc

      struct hostent *
      lwres_gethostbyaddr_r_enc(const char *, int, int, struct hostent *,
      			  char *, int, int *, int)

  lwres ライブラリの関数 lwres_gethostbyaddr_r() に、 ローカルエンコーディングか否かの判定フラグのエントリを追加したものです。 lwres_gethostbyaddr_r() との違いは、lwres_getipnodebyaddr() 関数の呼び出し部を lwres_getipnodebyaddr_enc() 関数に変更している点のみです。

  関数の最後の引数がエンコードタイプの判定フラグで、 ローカルエンコーディングのときには`LWRES_ENCODING_LOCAL`、 UTF-8 エンコーディングのときには`LWRES_ENCODING_UCS`が指定可能です。 lwres_gethostbyaddr_r() 関数から lwres_gethostbyaddr_r_enc() 関数を呼び出すときは、 エンコードタイプの判定フラグに`LWRES_ENCODING_LOCAL`を指定しています。

  エラーのときは NULL、正常のときは値が格納された hostent 構造体へのポインタを返します。

lwres_getipnodebyname_enc

      struct hostent *
      lwres_getipnodebyname_enc(const char *, int, int, int *, int)

  lwres ライブラリの関数 lwres_getipnodebyname() に、 ローカルエンコーディングか否かの判定フラグのエントリを追加したものです。 lwres_getipnodebyname() との違いは、lwres_getaddrsbyname() 関数の呼び出し部を lwres_getaddrsbyname_enc() 関数に変更している点のみです。

  関数の最後の引数がエンコードタイプの判定フラグで、 ローカルエンコーディングのときには`LWRES_ENCODING_LOCAL`、 UTF-8 エンコーディングのときには`LWRES_ENCODING_UCS`が指定可能です。 lwres_getipnodebyname() 関数から lwres_getipnodebyname_enc() 関数を呼び出すときは、 エンコードタイプの判定フラグに`LWRES_ENCODING_LOCAL`を指定しています。

  エラーのときは NULL、正常のときは値が格納された hostent 構造体へのポインタを返します。

lwres_getipnodebyaddr_enc

      struct hostent *
      lwres_getipnodebyaddr_enc(const void *, size_t, int, int *, int)

  lwres ライブラリの関数 lwres_getipnodebyaddr() に、 ローカルエンコーディングか否かの判定フラグのエントリを追加したものです。 lwres_getipnodebyaddr() との違いは、lwres_getnamebyaddr() 関数の呼び出し部を lwres_getnamebyaddr_enc() 関数に変更している点のみです。

  関数の最後の引数がエンコードタイプの判定フラグで、 ローカルエンコーディングのときには`LWRES_ENCODING_LOCAL`、 UTF-8 エンコーディングのときには`LWRES_ENCODING_UCS`が指定可能です。 lwres_getipnodebyaddr() 関数から lwres_getipnodebyaddr_enc() 関数を呼び出すときは、 エンコードタイプの判定フラグに`LWRES_ENCODING_LOCAL`を指定しています。

  エラーのときは NULL、正常のときは値が格納された hostent 構造体へのポインタを返します。

## dig/host/nslookup 修正

BIND-9 には DNS の問い合わせツールとして dig、host、nslookup という コマンドが付属します。mDNkit の BIND-9 パッチにより、これらはいずれも 多言語ドメイン名が扱えるようになります。具体的には以下の機能が 付加されます。

- エンコーディング変換
  コマンド引数、あるいは標準入力から入力された、ローカルエンコーディングで 表現されたドメイン名を、DNS プロトコル上で用いられるエンコーディングに変換し、 DNS の問い合わせを発行します。

  また DNS の返答に含まれる多言語ドメイン名をローカルエンコーディングに 変換し、表示します。

- 正規化
  DNS の問い合わせで、ローカルエンコーディングで表現されたドメイン名を DNS プロトコル上で用いられるエンコーディングに変換する際、ドメイン名の 正規化を行います。

これらの処理には MDN ライブラリの res モジュール を使用しています。 設定はクライアント設定ファイルで行います。