Ruby で DNS Update (RFC 2136)

Ruby を使って RFC 2136 の DNS Update を実行する方法を調べてみた。
標準添付の Resolv::DNS クラスはルックアップの機能しか無いので、PerlNet::DNS のようなモジュールを探してみたところ、Perl の Net::DNS を移植したという 2 つのライブラリを見付けた。

両方比べてみたところ、Net::DNS の方は肝心の update 関連の機能が未実装だったので、今のところ選択肢は pNet::DNS だけのようだ。

pNet::DNS の使い方

まずはインストール。

$ sudo gem install pnet-dns

nsupdate コマンドでの操作を Ruby に置き換えるなら以下のようになる。

$ nsupdate
> server 192.168.1.1
> prereq nxdomain host1.example.com.
> update add host1.example.com. 3600 IN A 192.168.1.2
> send

↑上の操作は、以下↓のようになる。

require 'rubygems'
require 'Net/DNS'
resolver = Net::DNS::Resolver.new(:nameservers => %w(192.168.1.1))
packet = Net::DNS::Update.new_from_values('example.com')
packet.push('prereq', Net::DNS.nxdomain('host1.example.com'))
packet.push('update', Net::DNS.rr_add('host1.example.com 3600 IN A 192.168.1.2'))
resolver.send(packet)

また、TSIG (Transaction Signature) を使ってリクエストの認証を行なうには、Net::DNS::Resolver#send の直前に Net::DNS::Packet#sign_tsig を使用する。

packet.sign_tsig('キーの名前', 'Base64エンコードされた秘密鍵')
resolver.send(packet)

キーの名前や Base64 エンコードされた秘密鍵は、dnssec-keygen コマンドで作った鍵ファイルから取り出して使用する。
なお、pNet::DNS の現時点の最新リリース(0.0.4)には TSIG 関連の機能が動作しないバグがあるので、svn リポジトリから最新版を checkout してくるか、パッチ*1をあてて使用する必要がある。

感想

TSIG の動作修正パッチを作ったのは俺なんだけど、今まで誰も気付かず放置されてたのが不思議なくらいに初歩的なミスばかりで、こういう辺りは圧倒的なユーザー人口と厚みを誇る Perl とは比べものにならないなあ、と思った。