モバイル環境でのDNS

(BSD Magazine No.12)

梅本肇 / ume@mahoroba.org

モバイルとDNS

 リゾルバの初期化はアプリケーションが最初のクエリを出すときに一度だけ行われる。 /etc/resolv.confが読み込みまれるのは、そのときのみである。
 昔のUnixの一般的な利用環境ではDNSサーバーがころころ変わるようなことはなかった。 そのため、/etc/resolv.confを読み直さなければならないような状況は想定されていない。
 しかし、今日一般的になっているモバイル環境では、インターネットに接続するたびに/etc/resolv.confの書き換えが発生する。
 ここで、不都合が発生することになる。ずっと起動したままのアプリケーションは、参照すべきDNSサーバーが変わっていたとしても、そのことを知る術がなく、アプリケーション起動時のDNSサーバーをずっと参照し続けるのである。
 たとえば、Emacsをずっと起動したまま使用している読者も多いことだろう。 そして、その中でWanderlustやMewを使ってメールを読み書きしていることだろう。
 最初にEmacsを起動したのと異なった環境で使用すると、なぜかDNSがうまく引けないと悩むことになる。
 WanderlustやMewは言うまでもなくEmacsアプリケーションである。 Mewバージョン1はIMを呼んでたので、実質的にEmacsがDNSを参照することはなかったが、バージョン2になってすべてelisp化されたので、EmacsがDNS参照を行うようになっている。

DNSと仲良くするために

 では、Emacsのようにずっと起動したままで使用されるアプリケーションがDNSを参照できるようにするにはどうしたらよいだろうか?
 本稿ではFreeBSDでの設定を例に紹介する。

/ets/hostsの活用

 いつも使用するサーバーが決まっているなら、そのサーバーを/etc/hostsに登録しておくという手段が使えるであろう。 もちろんこの場合、/etc/host.confでDNSより先に/etc/hostsを参照するようにしておく必要がある*1
 しかし、この方法では、不特定多数のホストを検索することはできない。

外部プログラムの活用

 DNS参照が必要なアプリケーションを外に追い出す戦略も考えられる。
 たとえば、メール受信はfetchmailで、送信はsendmailで行うようにするなどである。 この場合、もちろんsendmailをデーモンとして起動していると何の意味もないので、inetdから起動するようにする(リスト1)。
リスト1 /etc/inetd.confの設定例
smtp stream tcp6 nowait root /usr/sbin/sendmail sendmail -bs -Am
 この例では、IPv6でのみlistenするように設定しているが、ローカルからしか呼ばないからIPv6 onlyで良いよね。まぁ、趣味の問題である。:-)
 また、sendmail 8.12.Xをinetdから起動する場合には-Amオプションが必要である。8.11.Xの場合は-Amを付けるとエラーになるので注意が必要である*2
 モバイル環境と言えども不正利用に対する対策はきちんとしておきたいものである。 ローカル以外からのメールは受け付けないように設定しておこう。 このためには、/etc/hosts.allowでローカルからしか接続を受け付けないよう制限しておくと良いだろう(リスト2)*3
リスト2 /etc/hosts.allowの設定例
sendmail : [::1] : allow
 また、sendmailはデフォルトでは起動時にインターフェイスに付いているアドレスの逆引きを行うので、この動作を行わないよう、sendmail.mcでリスト3のように指定しておこう。 ただし、この場合、ローカルからのリレーを許可するようaccess_dbなどでに設定しておく必要がある(リスト4)。
リスト3 sendmail.mcの設定例
define(`confDONT_PROBE_INTERFACES', `True')
リスト4 access_dbの設定例
Connect:IPv6:::1	RELAY
Connect:127.0.0.1	RELAY

いつも同じDNSサーバーを使用

 DHCPやPPPによってアナウンスされるDNSサーバーは使用せず、いつも同じDNSサーバーを使うという戦略もあるだろう。常にインターネット上で生活するのであれば有効である。 しかし、ファイアウォールなどがあり、必ずしも特定のDNSサーバーを参照することができないような環境では、この手は使えない。

DNSプロキシを使用

 ようするに、参照するDNSサーバーを変えなければ良いのである。
 このような場合には、自ホストでDNSサーバーを用意し、ネットワーク接続時にフォワード先を切り替えるのが有効だろう。 だが、DNSサーバーを自前で持つのはちょっと大がかりすぎるかもしれない。
 ここでは、DNSプロキシを利用する方法を紹介しよう。
 まず、常にローカルのDNSを参照するよう、/etc/resolv.confを設定しておく。 そして、ネットにつながったら、DNSプロキシ側の設定を変更し、DNSプロキシを再起動する。
 ここでは、DNSプロキシとしてtotd*4を使用する。 totdは単なるDNSプロキシではなく、IPv6→IPv4トランスレータ用のDNSプロキシであるが、IPv6も喋ることができるDNSプロキシとして重宝する。 totdはports/packagesになっている*5
 DNSプロキシにはtotd以外にも、pdns*6などいくつかある。 好みに応じて選択するとよいだろう。
 ちなみに、pdnsdはキャッシュ機能を備えているのが特徴であり、バージョン1.0.0以降でIPv6をサポートしている。 pdnsdもports / packagesになっている*7。 ただし、IPv6は有効になっていないので、IPv6で使用したい場合には、CONFIGURE_ARGSに--enable-ipv6を加える必要がある。

/etc/resolv.confの設定

 /etc/resolv.confは常にローカルのDNSを参照するように設定しておく(リスト5)。 nameserverに0.0.0.0を設定すると、ローカルでDNSが起動されていると参照するが、起動していないとすぐに参照するのをあきらめる。
 /etc/resolv.confがなかった場合のリゾルバのデフォルトの動きなので、検索順序やリゾルバに対するオプションを設定したりしないのであれば、/etc/resolv.confは特に用意しなくてもよい。
 ただし、/etc/resolv.confがない、あるいはsearchまたはdomain行でドメイン名を指定していない場合は、ホスト名のドメイン部分を使用するので、ホスト名はドメインを含んだFQDNで指定しておこう。
リスト5 /etc/resolv.confの例
search example.com
nameserver 0.0.0.0

DHCPでの設定例

 ではDHCPを使用している場合の設定を見ていこう。
 ISC版DHCPクライアント*8は、DHCPの状態が変化した際に/sbin/dhclient-scriptを呼び出す。 このスクリプトの中で、状態に応じてスクリプトを起動したりするために/etc/dhclient-enter-hooksと/etc/dhclient-exit-hooksの2つのフックが用意されている。
 また、make_resolv_conf()という関数が定義されており、DNSサーバのアドレスを得た時にこの関数が呼び出され、/etc/resolv.confが生成ようになっている。 関数に分離してあるのは、上書きすることでこの挙動を変更できるようにするためである。
 つまり、DHCPでDNSサーバーのアドレスをもらった際に/etc/resolv.confを更新するのではなく別の処理をさせるには、/etc/dhclient-enter-hooksで、make_resolv_conf()を再定義すればよい。
 リスト6では、DNSサーバのアドレスが変化した、あるいはtotdが起動されていない場合に、/usr/local/etc/totd.confを生成し、totdの再起動をおこなっている。
リスト6 /etc/dhclient-enter-hooksの例
make_resolv_conf() {
   if [ x"$new_domain_name_servers" != \ 
        x"$old_domain_name_servers" -o \ 
        x`ps axc | awk '$5 ~/^totd$/ {print $1}'` = x ]; then
       killall totd
       echo retry 5 > /usr/local/etc/totd.conf
       for nameserver in $new_domain_name_servers; do
           echo forwarder $nameserver port 53 \ 
               >> /usr/local/etc/totd.conf
       done
       /usr/local/sbin/totd
   fi
}
 後始末として、カードを抜いたりサスペンドした際にDNSプロキシを停止させるよう設定しておこう。 「/etc/stop_if.インターフェイス名」という名称でスクリプトを用意しておくと、カードを抜いた際に実行される(リスト7)。
リスト7 /etc/stop_if.wi0の例
killall totd

PPPでの設定例

 次に、PPPを使用している場合の設定である。
 pppは、DNSサーバーのアドレスは受け取るが、/etc/resolv.confを自動的に書き換えないように設定する(リスト8)。 DNSサーバーのアドレスを取得するようenable dnsを指定するが、併せてresolv readonlyを指定することで、/etc/resolv.confを書き換えなくなる。
リスト8 /etc/ppp/ppp.confの例
default:
 set log Phase Chat LCP IPCP CCP tun command
 set device /dev/cuaa2
 set speed 115200
 set dial "ABORT BUSY ABORT NO\\sCARRIER TIMEOUT 5 \
          \"\" AT OK-AT-OK ATE1Q0 OK \\dATD\\T TIMEOUT 40 CONNECT"
 set timeout 180
 enable dns
 resolv readonly

provider:
 set phone "電話番号"
 set authname "ユーザ名"
 set authkey "パスワード"
 set ifaddr 0.0.0.0/0 0.0.0.0/0 0.0.0.0 0.0.0.0
 add default HISADDR
 dial
 DNSサーバーのアドレスはDNS0に入ってくるので、/etc/ppp/ppp.linkupで DNS0をフォワード先に指定した/usr/local/etc/totd.confを生成し、totdを再起動する(リスト9)。
リスト9 /etc/ppp/ppp.linkupの例
MYADDR:
 ! sh -c "killall totd"
 ! sh -c "printf '%s\\n%s\\n' 'retry 5' 'forwarder DNS0 port 53' >/usr/local/etc/totd.conf"
 ! sh -c "/usr/local/sbin/totd"
 後処理として、/etc/ppp/ppp.linkdownで、pppの接続が切れたらtotdを停止させる(リスト10)。
リスト10 /etc/ppp/ppp.linkdownの例
MYADDR:
 ! sh -c "killall totd"

おわりに

 モバイル環境ではDNS参照は厄介なもののひとつである。本来、リゾルバ自体がもう少し賢くなるべきなのだろうが、互換性の問題があり、改造は容易ではない。
 本稿では、筆者が日頃使用している、DNSプロキシを用いた設定を紹介した。
 本稿が快適なモバイル環境構築の参考になれば幸いである。


*1) FreeBSDではデフォルトで/etc/hostsを先に参照するようになっている。
*2) 4.5-RELEASEのsendmailは8.11.6であるが、最近の4-STABLEでは8.12.3になっている。
*3) FreeBSD標準添付のsendmailはlibwrapが組み込まれている。
*4) http://www.vermicelli.pasta.cs.uit.no/ipv6/software.html
*5) ports/net/totd/
*6) http://home.t-online.de/home/Moestl/
*7) ports/net/pdnsd/
*8) FreeBSDでは標準添付


All Rights Reserved, Copyright (C) 2002 Hajimu UMEMOTO
Last Modified Nov 13, 2002
ume@mahoroba.org