Logo address

Plan 9 Network Database

2015/11/14
2015/11/26 改定
2023/11/13 mDNS に関する注意を追加(枠囲いの箇所)

設計目標

Plan9 は分散OSである。

などによって構成されることを前提として設計されている。その場合には、これらの全てが(ファイルサーバーから供給される)同一の NDB(Network Database) ファイルを見ることになる。Plan9 のネットワークデータベースはそれに耐えられるように設計されているのである。

Unix/Linux ではネットワーク関係のファイルがシステムの中でバラバラな場所に散乱している。そのことが理解(全体像の把握)を困難にしている。例えば /etc/hosts, /etc/resolv.conf, などなど。他にサーバー用には DHCP や BIND など。

Plan9 では /lib/ndb/local が全てを仕切っていると考えてよい。
ファイルサーバーを除けばディスクレスシステムが想定されており、使用されている CPU の種類も異なることも想定されている。
そうした複雑なシステムを制御しているのである。

それでは /lib/ndb/local は複雑なシンタックスになっているかと言えば、極めて単純な構造を持つ、属性と値の集まりにすぎない。

NDB ファイルの具体的な例がシステムの以下の場所に存在する。

NDB ファイルを参照するアプリケーション

サーバー

各々ネットワークを使うアプリケーションとファイルライクなインターフェースを持つ

コマンド

他多数あり

これらは CS を通じて NDB を使う

デバッグ用あるいはシェルスクリプトへのインターフェース

シンタックス

以下に NDB のシンタックスを解説するが、実際に許容されるシンタックスはここに述べるよりも遥かに緩やかである。
しかし、マニュアルに解説されている範囲と、使用例の範囲で使うのが無難である。
ここでは推奨されるシンタックスを解説する。

NDB ファイルは(一般に)複数のレコードから構成される。また1つのレコードは(一般に)複数のタプルの集まりである。
タプル(tuple)とは属性(attribute)とその値(value) の組みを言う。

レコードは行の先頭から始まる。

レコードの実例:

sys=hebe
	dom=hebe.local
	ether=6805ca0a0bf2
	ip=192.168.0.6	# IPV4
	ip=2402:6b00:22cd:bf80::6	# IPv6

図1: レコードの例

これで1つのレコードを形成する。

注意: この例では "hebe.local" となっている。ローカルドメインの意味で ".local" としていたのであるが現在では止めた方が良い。現在では ".local" は mDNS (multicast DNS) の対象となっているドメインとされている。hebe もそうであるが mDNS をサポートしていない。その場合(最近では)、Mac からのアクセスが5秒ほど遅れる。"dom=hebe.local" の行を削除するか、あるいはインターネットで使われるドメイン形式
dom=hebe.nyx.link
を指定すべきである。この注意は以下の説明の
dom=xxxx.local
(xxxx は sysname) の全てにおいて当てはまる。
2023/11/13 追加

1つのレコードの中のタプルの順序は気にしなくてもよい。ただし、同じ属性名のタプルが複数が現れる場合、全てが利用されることもあるし、先頭の1つだけが利用されることもある。検索ツールに依存する。
従って図1の代わりに例えば

ether=6805ca0a0bf2 sys=hebe
	dom=hebe.local
	ip=192.168.0.6
	ip=2402:6b00:22cd:bf80::6

図2: レコードの例

と書いてもよい。


重要: ipv4 アドレスと ipv6 アドレスを書く場合には ipv4 アドレスを先に書かなくてはならない。これはシステムのバグだと考えられる。

タプルは

属性=値
で表す。
等号 "=" の左に空白をおいてはならない。

ここでは属性は全て英字で構成される文字列になっているが、実際には非常に広い範囲の文字列が許される。
値を空文字(長さ0の文字列)にしたい場合には

属性=
あるいは
属性=""
で表す。(前者のほうが好まれているようだ)
この場合、単に
属性
でもよいが、これは過度な柔軟性であり、やめたほうがよいだろう。

例えば /lib/ndb/common の中には

tcp=rexec port=512 restricted=
と書かれている。これは
tcp=rexec port=512 restricted
と同じであるが、意識的に "=" を付けている。

値が空白を含む場合や記号 "#" で始まる場合には二重引用符で括る必要がある。例

author="Kenji Arisawa"

記号 "#" はコメント記号である。引用符の外で有効である。

コメント記号は行の先頭に書くか、記号の前に空文字を置くのが良い。
注意

foo=#
と書いた場合、この "#" はコメントであるが
foo=bar#
と書くとこの "#" は foo の値の一部となる。この一貫性を欠いた紛らわしさは実際の利用においては避けるべきである。
従って各々
foo= #
および
foo="bar#"
と書くべきであろう。

レコードの中では空行は使わないほうが良い。使ってもエラーにはならないが意図したとおりの結果が得られないかもしれない。本来は空行はスキップすべきだと思われるが、そうにはなっていない。

NDB のシンタックスは非常に緩くできている。このファイルはブート時にも参照されるので、シンタックスエラーで停止しないように配慮されているのだろう。

検索は属性名とその値を指定して行う。また合致したレコードの中の指定した属性名の値だけを取り出せる。

マニュアル ndb(6) には

Within tuples, pairs on the same line bind tighter than pairs on different lines.
と書かれている。しかし、レコード内のタプルは同じ行に書こうが、異なる行に書こうが関係ないように思える。

NDB は Plan 9 のネットワークデータベースとして設計されているのであるが、シンプルさ故に広い適応範囲を持っている。また可読性も優れている。

ネットワークに関する Plan 9 の設定は全て NDB を通じて行なえるようになっている。

シェルスクリプトとのインタフェースが充実している。

初めての /lib/ndb/local

初めて Plan9 を試す場合には、分散 OS としてではなく、単体の Plan9 端末を構築するだあろう。この場合

dns server をリゾルバーモードで起動する。この場合、dns はキャッシュ能力だけではなく full-service resolver としての能力を持つ。

ndb/dns -r

例1

ここでは最初に HGW(home gate way) が提供する DNS サーバー(正しくは DNS proxy)を使う例から始める。

database=
	file=/lib/ndb/common
	file=/lib/ndb/local

dom=
	dns=router

ipnet=local ip=192.168.0.0 ipmask=255.255.255.0 ipgw=192.168.0.1
	ntp=ntp.jst.mfeed.ad.jp
	proto=tcp

sys=router
	dom=router.local
	ip=192.168.0.1
sys=maia
	dom=maia.local
	ether=6805ca00fc34
	ip=192.168.0.3
	ip=2402:6b00:22cd:bf80::3

database では読み取る NDB ファイルを指定する。/lib/ndb/common にはポート番号とそのサービスポートなど、システムの構成に依存しない情報が含まれている。

dom=dom="" と同じであり、DNS root を意味する。dns で recursive dns server を指定している。サブネットで他のサーバーが指定されていない限り、ここで指定した DNS サーバーが使用される。

ipnet でローカルネットワークの3つの基本情報を ipipmaskipgw で指定する。
ipnet の値はローカルネットワークの識別子である。ここでは local を使ったが他の名前でもよい。
ntp でタイムサーバーを指定する。
proto では ipnet の中で使用する通信プロトコルを指定するが、ここは他に Plan9 でしか使われない il が指定できるのみであり、実際上は tcp に固定されるはずである。

sys でホスト名(Plan9 では sysname と呼ばれている)を指定する。
このレコードでは dom 及び ip が必須になるだろう。

maia は Plan9 端末の名前である。Plan9 では ether の値を手掛かりに sysname が決定されるので、ether は必須になるはずである。IP アドレスは ip で指定する。表現形式によって IPv4 と IPv6 が区別される。

ここでは HGW を dns として使用したが、Google のパブリック DNS サービスを利用する方法もあるだろう。その場合には

dom=
	dns=google-public-dns-a.google.com
	dns=google-public-dns-b.google.com
dom=google-public-dns-a.google.com ip=8.8.8.8
dom=google-public-dns-b.google.com ip=8.8.4.4

でこの部分を置き換えればよいはずである。(パブリック DNS サービスは汚染されている危険性が高いので、好んで使うサービスではない)

由緒正しい書き方をすれば、この部分は

dom=
	ns=A.ROOT-SERVERS.NET
	ns=B.ROOT-SERVERS.NET
	ns=C.ROOT-SERVERS.NET
	ns=D.ROOT-SERVERS.NET
	ns=E.ROOT-SERVERS.NET
	ns=F.ROOT-SERVERS.NET
	ns=G.ROOT-SERVERS.NET
	ns=H.ROOT-SERVERS.NET
	ns=I.ROOT-SERVERS.NET
	ns=J.ROOT-SERVERS.NET
	ns=K.ROOT-SERVERS.NET
	ns=L.ROOT-SERVERS.NET
	ns=M.ROOT-SERVERS.NET

である。
ns で authoritative name server を指定する。A.ROOT-SERVERS.NET などの IP アドレスは /lib/ndb/common に含まれている。

属性とその値のまとめ

sys

syssysname を指定する。いわゆるホスト名である。

dom

dom は domain を表す。
この値は(マニュアルには FQDN となっているが) root を起点とした相対ドメイン名である。つまり末尾に "." を付けない。

ns

dom と一緒に用いる。
ns で autoritative name server を指定する。

dns

dns で recursive name server を指定する。
dom と一緒に指定することもできるが、ipnet の中で指定した場合には dns の値は dhcpd によって dns として宣伝される。

ip

IPv4 または IPv6 の IP アドレスを指定する。

ether

ethernet アドレスを指定する。この値はホスト名の自動決定に使われる。

その他

その他多数の属性が定義されている。詳しくは ndb(6) を見よ。

ローカルネットワーク中の分散システムとしての /lib/ndb/local

ここではファイルサーバーを中心としてシステムが組まれていることを想定する。

例2

以下の例で示す Plan9 分散システムのブレィヤーの名称を

hebe はネットワーク上の Unix や Windows などにも DHCP や DNS のサービスを提供する。また DHCP は CPU サーバーや端末に対してブートに必要な情報を提供する。

hebe での dns の実行は

ndb/dns -s
で行う。オプション "-s" は UDP port 53 でもサービスを行うことを意味している。
またファイルサーバーでは同時に dhcpd を動かし、DNS のアドレスを宣伝する。筆者の場合には
ip/dhcpd 192.168.0.100 150
を実行している。これによってクライアントに 192.168.0.100 から始まる 150 個の IP アドレスをリースする。(つまり最大の IP アドレスは 192.168.0.249)
また dhcpd は bootp サーバーとして働く。

ファイルサーバー以外は

ndb/dns -r
を実行する。オプション "-r" はリゾルバーモードを表す。従って以下リゾルバーと言う。

実例として、筆者の /lib/ndb/local の一部分を示す。

database=
	file=/lib/ndb/common
	file=/lib/ndb/local
dom=
	ns=A.ROOT-SERVERS.NET
	ns=B.ROOT-SERVERS.NET
	ns=C.ROOT-SERVERS.NET
	ns=D.ROOT-SERVERS.NET
	ns=E.ROOT-SERVERS.NET
	ns=F.ROOT-SERVERS.NET
	ns=G.ROOT-SERVERS.NET
	ns=H.ROOT-SERVERS.NET
	ns=I.ROOT-SERVERS.NET
	ns=J.ROOT-SERVERS.NET
	ns=K.ROOT-SERVERS.NET
	ns=L.ROOT-SERVERS.NET
	ns=M.ROOT-SERVERS.NET

dom=local soa=
	refresh=3600 ttl=3600
	ns=hebe
	mbox=arisawa
	auth=hebe
ipnet=local ip=192.168.0.0 ipmask=255.255.255.0
	ipgw=192.168.0.1
	ntp=ntp.jst.mfeed.ad.jp
	auth=hebe
	fs=hebe
	proto=tcp
	dns=hebe
	dnsdomain=local
sys=maia
	dom=maia.local
	ether=6805ca00fc34
	ip=192.168.0.3
	ip=2402:6b00:22cd:bf80::3
	#bootf=/386/9pcf
	bootf=/386/9bootpxe
sys=meg
	dom=meg.local
	ether=001b21d5a3e9
	ip=192.168.0.4
	ip=2402:6b00:22cd:bf80::4
	bootf=/386/9bootpxe		# 9front
sys=hebe
	dom=hebe.local
	ether=6805ca0a0bf2
	ip=192.168.0.6
	ip=2402:6b00:22cd:bf80::6

/lib/ndb/local の一部分

解説

属性 dom

dom=local
dom の値は root から見たドメイン名の相対アドレスで、ここのレコードでは属性 soarefreshttl 及び ns が現れている。
soa は start of authority (マニュアルには start of area となっている)
ここでの soa はフラグ的な使い方であるが、値を持つこともあるらしい。
refreshttl は各々 refresh time および time to live で単位は秒。
ns は autoritative name server で、ここでは hebe が指定されている。
ここの authcpu コマンドなどでファイルサーバーや CPU サーバーにアクセスする際の認証に使われる。authdom を使って
authdom=local
	auth=hebe
と独立させても良い。両者で auth が指定されている場合には authdom での指定の方が優先度が高い注1


注1: マニュアル authsrv(2) を見よ。

属性 ipnet

ipnet=local
ここでは ipnet の値としてドメイン名が選ばれているが、他の名前を使ってもよい。この値はネットワークの識別子である。ここのレコードでは属性 ipipmaskipgw が必須である。ここではその他に、ntp サーバー、認証サーバー、プロトコルが指定されているが、これらは sys を含むレコードに個別に書いてもよい。(その場合には override される)
dns では recursive name server を指定する。ipnet の中の dns の値は DHCP で宣伝される。
dnsdomain は、いわゆる探索ドメインである。これは Plan9 ネットワークの中で使用されている。
ipnet における authfsndb/ipquery コマンドによる属性探索の対象となっており、システムの初期設定に利用されている。

属性 sys

sys=maia
このレコードの中で dom の値として maia のドメイン名が指定されている。これは root から見たドメイン名の相対アドレスである。ether はイーサーネットアドレス、ip は IP アドレスである。IPv4 だけでなく IPv6 も指定できる注1
bootf でブートファイルを指定する。ここでは PXE ローダーが指定されている。


注1: 以前は IPv6 アドレスを指定するのに ipv6 属性が使われていた。現在もマニュアルにはこの属性名が残っている。

属性とその値のまとめ

soa

start of authority (マニュアルには start of area となっている)
dom と共に用いる。
この場合 ns で authoritative name server を指定し、
refreshttl で各々 refresh time および time to live が指定される。単位は秒。
他に retryexpireserial が指定できる。

また mbox で管理者のメールアドレスを指定する。その場合の形式は

mbox=person
mbox=person@machine.dom
mbox=person.machine.dom
を選択できる注1。省略した場合には postmaster.$ns となる。ここに $nsns の値である。

ここの部分は DNS にアクセスしたクライアントに知らされる。
LAN の中での実験を見る限り、Plan9 の ndb は寡黙である。
探索に失敗した場合にのみ ns の値と mbox の値がクライアントに知らされる。
LAN の外でのサービスを行う場合には nsmbox には full domain name が要求されるだろう。


注1: look /sys/src/cmd/ndb/dblookup.c

ipnet

ipnet の値はネットワーク識別子である。(従って自由に名前を選べるが重複は許されない)
ipnet の中の属性とその値は必要に応じて DHCP あるいは BOOTP でクライアントに渡される。その場合、sys の中に同じ属性名があれば、sys の値が優先される。
従って、ipgw なども sys 側においても構わないが、通常は ipnet に置けるものは ipnet に置くだろう。
ファイルシステムをマウントできれば、クライアントは /lib/ndb/local を直接参照すれば構わないので、マウントするまでに必要な情報がブート時にクライアントに渡されると考えてよい。

dnsdomain

検索ドメインを指定する。複数可。
Plan9 クライアントが利用している。
Unix の /etc/resolv.conf の search の値に相当する。

auth

認証サーバーを指定する

fs

ファイルサーバーを指定する。

bootf

bootp のカーネルを指定する。
PXE ロードの場合にはヘルパープログラムを指定する。

遠隔地の Plan9 システムを利用する場合の /lib/ndb/local

例3

ここでも筆者の例を基に解説する。
筆者の場合、自宅から大学の研究室の Plan9 システムを使うニーズが存在する。
この場合、認証サーバーが異なる。
また筆者の場合には認証サーバーは正式なサーバーとして大学に登録されていない。従って /lib/ndb/local の中で、名前と IP アドレスを設定しなくてはならない。

現在、大学で動いている Plan9 システムは 2 台構成である。

目標は

database=
	file=/lib/ndb/common
	file=/lib/ndb/local
dom=
	ns=A.ROOT-SERVERS.NET
	ns=B.ROOT-SERVERS.NET
	ns=C.ROOT-SERVERS.NET
	ns=D.ROOT-SERVERS.NET
	ns=E.ROOT-SERVERS.NET
	ns=F.ROOT-SERVERS.NET
	ns=G.ROOT-SERVERS.NET
	ns=H.ROOT-SERVERS.NET
	ns=I.ROOT-SERVERS.NET
	ns=J.ROOT-SERVERS.NET
	ns=K.ROOT-SERVERS.NET
	ns=L.ROOT-SERVERS.NET
	ns=M.ROOT-SERVERS.NET
dom=aichi-u.ac.jp
	auth=hera
ipnet=labo
	ip=202.250.160.0 ipmask=255.255.255.0 ipgw=202.250.160.254
	smtp=ar
	fs=hera
	auth=hera
	prot=tcp
	ntp=smtp.aichi-u.ac.jp
sys=ar
	ip=202.250.160.40  ether=6805ca0301b7
	dom=ar.aichi-u.ac.jp
	bootf=/386/9bootpxe
sys=plan9
	dom=plan9.aichi-u.ac.jp
	ip=202.250.160.122
sys=hera
	ip=202.250.160.71  ether=74d435609637
	dom=hera.aichi-u.ac.jp

dom=local soa=
	refresh=3600 ttl=3600
	ns=hebe
	mbox=arisawa
	auth=hebe
ipnet=local
	ip=192.168.0.0 ipmask=255.255.255.0 ipgw=192.168.0.1
	ntp=ntp.jst.mfeed.ad.jp
	auth=hebe
	fs=hebe
	proto=tcp
	dns=hebe
	dnsdomain=local
sys=maia
	dom=maia.local
	ether=6805ca00fc34
	ip=192.168.0.3
	ip=2402:6b00:22cd:bf80::3
	#bootf=/386/9pcf
	bootf=/386/9bootpxe
sys=meg
	dom=meg.local
	ether=001b21d5a3e9
	ip=192.168.0.4
	ip=2402:6b00:22cd:bf80::4
	bootf=/386/9bootpxe		# 9front
sys=hebe
	dom=hebe.local
	ether=6805ca0a0bf2
	ip=192.168.0.6
	ip=2402:6b00:22cd:bf80::6

/lib/ndb/local の一部分

/lib/ndb/local の書き方(スタイル)について

各レコードの最初のタプルはレコード内の何を使ってもよいのではあるが、可読性のためにはルールを決めておいた方が良いであろう。
筆者は次の4パターンで分類している。

そして、次の優先順位で考える

  1. database 属性を持つ場合には、database を先頭に書く
  2. sys 属性を持つ場合には、sys を先頭に書く
  3. ipnet 属性を持つ場合には、ipnet を先頭に書く
  4. dom 属性を持つ場合には、dom を先頭に書く

Plan9 マニュアイルやシステム付属の /lib/ndb/local のサンプルでは、ip が先頭に書かれていることが多いが、賛成できない。現在では IPv6 との関係で複数の ip を同一のホストに割り当てる場合があり、この考え方は破綻する。

属性とその値のまとめ

auth

ipnet あるいは sys に置く場合と(sys 属性を持たない) dom に置く場合は役割が異なる。

authdom

認証ドメイン。auth を指定する。dom で代用可能。(authsrv(2) を見よ)

smtp

ipnet に置いて、Plan9 システムで使う smtp サーバーを指定する。

トリック

筆者は「お名前.com」で Web 用に幾つかの名前を登録している。
例えば

ar.nyx.link
p9.nyx.link
などである。これらはサーバー hebe の仮想ホストのアドレスでもある。

さて問題は、これらの IP アドレスがインターネットから見るのと、LAN から見るのとは異なることにある。
インターネットから見えるグローバル IPv4 アドレスは動的に変化する。そして LAN の中からグローバルアドレスでこれらのサーバーにアクセスすると何か良くない予感がする。そこで LAN の中ではローカルアドレス(192.168.0.6) が付与されているかのように見せたいのである。これは実際に可能であって

dom=nyx.link soa=
	refresh=3600 ttl=3600
	ns=hebe
dom=ar.nyx.link ip=192.168.0.6
dom=p9.nyx.link ip=192.168.0.6

で解決する。


注意: これは LAN の中でしか通用しない。ノートパソコンに Plan9 をインストールして持ち運ぶ場合にはトラブルの原因になる。

ネームサーバー

サーバーをインターネットに公開するためにはネームサーバーへの登録が必要になる。
筆者の場合には家庭内サーバーなので、「お名前.com」のネームサーバーを利用しているが、場合によってはインターネットからアクセスされるネームサーバーを自分で持つ必要があろう。
この場合の(インターネットからアクセスされる) ndb/dns

ndb/nds -Rs
のオプションを添えて動かせばよいはずである。
その場合には、たぶん /lib/ndb/local を環境に合わせて修正が要求されるだろうが、筆者には実験環境がないので正確なことは言えない。

コマンド

Plan9 で使用される dns 関係のコマンドシンタックスだけを簡単にまとめる。詳しくは ndb(8) を見よ。

ndb/query

ndb/query [ -am ] [ -f dbfile ] attr value [ rattr ]

ndb/ipquery

ndb/ipquery attr value rattr...

ipquery は器用なコマンドで、例えば

ndb/ipquery sys maia ip ipgw auth
とすると
ip=192.168.0.3 ip=2402:6b00:22cd:bf80::3 ipgw=192.168.0.1 auth=192.168.0.6
を表示してくれる。ただし現状では ipv6 アドレスを ipv4 アドレスの先に書くと、正しく働かない。これはバグとみなすべきである。
マニュアルには載っていないが、実際には -f dbfile オプションがある。

ndb/csquery

ndb/csquery [ -s ] [ server [ addr... ] ]

ndb/dnsquery

ndb/dnsquery

ndb/dnsdebug

ndb/dnsdebug [ -rx ] [ -f dbfile ] [ [ @server ] domain-name [ type ] ]

ndb/dnstcp

ndb/dnstcp [ -rR ] [ -f dbfile ] [ -x netmtpt ] [ conn-dir ]

ndb/dnsinform

ndb/inform [ -x netmtpt ]

ndb/cs

ndb/cs [ -4n ] [ -f dbfile ] [ -x netmtpt ]

ndb/dns

ndb/dns [ -norRs ] [ -a maxage ] [ -f dbfile ] [ -N target ] [ -x netmtpt ] [ -z program ]

-r resolver only

ndb/dns -r

-s answer domain requests sent to UDP port 53

ndb/dns -s

-R ignore the 'recursive' bit on incoming requests. Do not complete lookups on behalf of remote systems.

ndb/dns -R

/net

/net/dns

/net/dns が dns とのインターフェースになっている。
通常はアプリケーションプログラムから利用される。すなわち /net/dns を通じてリモートの dns サーバーにリクエストを送り、結果を受け取る。Python からの利用例を Appendix に示す。

また /net/dns はローカルな dns サーバに対して特殊なコマンドを送ることもできる。
これに関してはソースコードを見ないと分からない。
例えば

echo restart >> /net/dns
で dns を再起動できる。(注意: ">" ではなく ">>" を使うこと)

使えるコマンドをまとめると、

stats の出力例

term% cat dnsstats
# system hebe
# slave procs high-water mark	16
# queries received by 9p	9709
# queries received by udp	6236
# queries answered from memory	13764
# queries sent by udp	5387
# responses arriving within 0.1 s.	3840
# responses arriving within 0.2 s.	1260
# responses arriving within 0.3 s.	227
# responses arriving within 0.4 s.	5
# responses arriving within 0.5 s.	3
# responses arriving within 3.2 s.	0

# queries sent & timed-out	104
# cname queries timed-out	1
# ipv6  queries timed-out	92

# negative answers received	1999
# negative answers w Rserver set	0
# negative answers w bad delegation	0
# negative answers w bad delegation & no answers	0
# negative answers w no Rname set	625
# negative answers cached	1374
hebe%

Appendix

dnsquery in Python

以下に Python で書かれた dnsquery 関数を紹介する。
要点は

である。

リクエストの書き方は

ar.aichi-u.ac.jp ip
のようにドメイン名とリクエストを書き込む。
この結果は
ar.aichi-u.ac.jp ip	202.250.160.40
のように得られる。
結果を読むときには、複数行の答えを得る可能性を考えておく必要がある。

import sys
import os
from string import *

def dnsquery(q,name="/net/dns"):
	"""
	usage:
	print dnsquery("ar.aichi-u.ac.jp ip")
	print dnsquery("ar.aichi-u.ac.jp ip","/net/dns")
	print dnsquery("202.250.160.40 ptr")
	"""
	f = os.open(name,os.O_RDWR)
	t = split(q)
	if t[1] == "ptr":
		u = t[0].split(".")
		q = join((u[3],u[2],u[1],u[0]),".")+".in-addr.arpa ptr"
	r = []
	try:
		os.write(f,q)
		os.lseek(f,0,0)
		v = os.read(f,256) # "" if not answered
		while v:
			r = r + [v.split()[2]]
			v = os.read(f,256)
	except:
		pass
	os.close(f)
	if r == []:
		return None
	return r

いつまで待ってもデータが読み取れないかも知れない。従って運用にあたっては dnsquery に対して alarm タイマーをセットする必要があるだろう。