DNS の謎

SunOS 4.1.4
YAMAMORI Takenori

SunOS 4.x で、DNS を直接引くための-誰もやらない-もうひとつの方法。
(LD_PRELOAD を使って libc.so.1.9 を組み換えずに済ます方法)

SunOS 4.x では、ネームサービスとしては NIS がメインで考えられています。libc の中の gethostbyname() などの名前解決のための関数は、NIS が動いていれば NIS のみを、NIS が動いていなければ /etc/hosts のみを参照し、DNS は参照されません。SunOS 4.x で DNS を引かせるためには、少し特殊な設定を行なう必要があります。 これにはいくつかの方法がよく知られていますが、 ここでは、さらに(恐らく誰もやらなかった)もうひとつの方法を紹介します。
■ SunOS 4.1.4 で DNS を引くための従来の方法

よく知られている方法として、以下の3種類があります。これをまず説明します。


■ SunOS 4.1.4 で DNS を引くためのもうひとつの方法

SunOS 4.1.4 で DNS を引くためのもうひとつの方法を紹介します。

その前に、上記の libc.so.1.9 を組み換える方法についてちょっと考えてみましょう。組み換える必要があるのは、 gethostbyname() などの、libc.so.1.9 全体から見ればごく一部の関数です。なのに、一部の関数のために libc.so.1.9 のバイナリ全体に手を加えなければならないことには違和感を感じます。

LD_PRELOAD が使える

さて、SunOS 4.1.4 の ld.so は、マニュアルには明記されていませんが環境変数 LD_PRELOAD を認識しています! (Linux 等では LD_PRELOAD はおなじみかも知れませんが、SunOS 4.1.4 においては LD_LIBRARY_PATH こそよく知られていても、LD_PRELOAD が使えることはほとんど知られていないのではないでしょうか)


libresolv.so.1.0 を作ろう

LD_PRELOAD が使えるとわかったら話は簡単です!
共有ライブラリとして libresolv.so.1.0 を作成し、DNS を引かせたいコマンドを実行する際に LD_PRELOAD を指定すればいいだけです。


libresolv.so.1.0 の作成

ここでは、bind などのソースを使わなくても、SunOS 4.1.4 付属の /usr/lib/libresolv.a を使用しても共有ライブラリが作れることを実証します。

具体的には、次のように実行します。

  $ mkdir work; cd work
  $ ar x /usr/lib/libresolv.a
  $ rm strcasecmp.o            # strcasecmp.o は libc と重複
  $ gcc -shared -o libresolv.so.1.0 *.o
なんと、これで OK です! これで実際に DNS を引かせるには、例えば、
  $ LD_PRELOAD=<絶対PATH>/libresolv.so.1.0 telnet
とやれば、この telnet は DNS を引いて動作します。

libresolv.a は PIC だった!

共有ライブラリ libresolv.so.1.0 の作成時に、通常の静的ライブラリである /usr/lib/libresolv.a をそのまま使いました。 しかし実は、この中に入っている *.o オブジェクトは、-fPIC を付けて作成されているようなのです! したがって、そのまま共有ライブラリ化して何も問題ありません。

ということは、libresolv.a を静的リンクして普通に使う場合は、逆に PIC が付いている分だけ無駄になっていると思われます。

○(参考)たとえ -fPIC が付いていなくても共有ライブラリは作れる。

libresolv.a-fPIC が付いていましたが、たとえ -fPIC が付いていない普通のオブジェクトでも、 強引に共有ライブラリを作ることが出来ます。

そもそも、-fPIC すなわちポジションインディペンデントオプションは、 何のために必要なのでしょうか? それは、共有ライブラリの text ページの書換えを無くすためです。

共有ライブラリとリンクしたバイナリの実行時、ld.so は、共有ライブラリの動的シンボルの解決を行ないます。 この時、共有ライブラリがあらかじめ -fPIC 付きで作成されていれば、text ページ部分の書き換えは無くて済むのです。

SunOS 4.1.4 をはじめ、現在の UNIX の仮想メモリは、 copy-on-write の方法を用いています。つまり、mmap された共有ライブラリの text ページについては、 リロケーション時に書換えが起きればページが単に copy-on-write されるというだけの話です。

つまり、-fPIC を付けずに共有ライブラリを作ると、text ページのコピーが起きてメモリの利用効率が悪くなったりはするものの、 共有ライブラリとしてはちゃんと動作するのです。

試しに、-fPIC が付いていないはずの、通常の静的リンク用ライブラリである libc.a から共有ライブラリを作ってみましたが、ちゃんと動きました。

LD_PRELOAD 方式の注意事項

LD_PRELOAD も、LD_LIBRARY_PATH と同様に、セキュリティー上の理由から set-uid されたコマンドでは無効になります。 このため、set-uid されていない telnetftp などでは、LD_PRELOAD 方式が使えますが、ping, rlogin, rsh などでは LD_PRELOAD 方式では DNS は引けません。(ただし、 su すれば LD_PRELOAD が有効になって DNS が引けます)


■余談■

SunOS/Solaris においては、ネームサービスとしてはあくまでも NIS がメインという考えからか、由来の異なる DNS はずっと異端扱いされてきました。

Solaris 2.x/7/8/9 などの SunOS 5.x系 では、/etc/nsswitch.conf で、NIS, NIS+, DNS, files などの参照順を自由に設定できるようにはなっていますが、 以前は、そのサンプルの設定ファイルとして、NIS, NIS+, files を使うもののみが用意され、DNS 用のサンプルは用意されていないという状況が続いていたこともありました。

Solaris のインストーラにおいても、Solaris 7 08/99 版あたりからは、インストール時のメニューで ネームサービスとして DNS を選択することが出来るようになりましたが、 以前のバージョンのインストーラでは、DNS は選択できませんでした。


To 『SunOS 4.1.4 の謎』index

To「謎の処理系 SunOS 4.1.4 with Linux/FreeBSD/Solaris」Home
yamamori@kt.rim.or.jp