Intenet Super Deamonを使った簡単なデーモンプログラム


ここまで延々とネットワークプログラムやデーモンの作り方を 説明してきましたが、実はもっと簡単にデーモンを作る方法があります。 ただし、この方法を使うためには あなたが スーパーユーザーつまり root である必要があります。

Inetdの動作原理

デーモンプログラムのデーモンたる部分、つまり socket, bind, listen, accept, fork, close それに signal,wait などの 部分は、どのような仕事をするデーモンも皆同じ作りになります。 なので、誰かがその部分を簡単なパッケージにしていてくれても よさそうなものです。それを提供してくれるのが internet super deamon 略称 inetd と呼ばれるデーモンです。

Inetdは普通、マシンが起動するときに root権限で動き出すデーモンです。 そして外からアクセスがあるたびに適切なプログラムを起動して 各種サービスを外に提供します。デーモンプログラムの部分はすべて このinetdがまかない、データの送受信の所だけを別なプログラムで 行うわけです。Inetdの動作を決定する設定ファイルは /etc/inetd.conf, /etc/services, /etc/hosts.allow の3つのファイルです。

設定ファイル /etc/inetd.conf を見て下さい。抜粋したものを以下に載せます。

# /etc/inetd.conf ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l telnet stream tcp nowait root /usr/libexec/telnetd telnetd tcpmux stream tcp nowait root internal finger stream tcp nowait/3/10 nobody /usr/libexec/fingerd fingerd -s
有名な対外サービスの名前とそれを行うプログラム名が列挙されています。

設定ファイル /etc/services を見て下さい。抜粋したものを以下に載せます。

# /etc/services tcpmux 1/tcp #TCP Port Service Multiplexer ftp 21/tcp #File Transfer [Control] ssh 22/tcp #Secure Shell Login telnet 23/tcp smtp 25/tcp mail #Simple Mail Transfer finger 79/tcp http 80/tcp www www-http #World Wide Web HTTP
有名な対外サービスの名前とポート番号が列挙されています。 サーバー側とクライアント側の両方がこのファイルを持っていないとなりません。

設定ファイル /etc/hosts.allow を見て下さい。抜粋したものを以下に載せます。

# /etc/hosts.allow sshd : .evil.cracker.example.com : deny sshd : ALL : allow sendmail : localhost : allow sendmail : .nice.guy.example.com : allow sendmail : .evil.cracker.example.com : deny sendmail : ALL : allow finger : localhost : allow finger : .nice.guy.example.com : allow finger : .evil.cracker.example.com : deny finger : ALL : allow ALL : ALL : deny
有名なサービスに対して、クライアントのホストごとに アクセスを許可するか拒絶するかが指定されています。 例えば finger の項目では localhost からのアクセスは許可(allow)され、 .evil.craker.example.comのドメインからのアクセスは拒否(deny)され、 その他該当しない所からのアクセスはすべて許可されます。


クライアントが telnet somehost finger とすると telnetコマンドはクライアントのマシンにある /etc/services から finger の項目を探し、見つけた 79番 ポートで、相手のマシンに接続しようとします。

サーバーの inetd は 79番ポートの接続要求を知ると、 /etc/inetd.conf と /etc/service のデータを照合して、これが finger のサービスへの接続要求であると判断します。

次に inetd は /etc/hosts.allow のデータを思い出して、 finger のサービスがこのクライアントに許可できることを確認します。

そして /etc/inetd.conf に書かれた finger の設定に従って /usr/libexec/fingerd を オプション -s 付きで実行します。 その際、クライアントに継っているネットワークソケットを fingerdの標準入出力に繋げてくれます。なので fingerd プログラムは 標準入出力を読み書きするだけでクライアントと通信できるのです。 このfingerdのプログラムは実際に接続されているときだけ 実行されるので、各プログラムがデーモンになって accept の所で 寝ているよりもメモリ資源の節約になるのです。


Inetdに独自のサービスを加える

Inetdに自分独自のプログラムをサービスとして加えるには TCPMUXと呼ばれる inetd 自身が提供する特殊なサービスを利用します。 まず、サービスを提供するプログラムを以下のように作ります。 名前は hello.cc とでもしておきましょうか。

// program hello.cc #include <stdio.h> int main( void ) { char msg[64]; printf("Your name, sir/madam: " ); fflush( stdout ); fgets( msg, sizeof(msg), stdin ); printf("Hello, %s.", msg ); fflush( stdout ); return 0; }
これをコンパイルして /usr/local/libexec/hello にでも置いておきましょう。 置き場所や名前はなんでも構いません。

次に /etc/inetd.conf を編集して以下の2行を追加します。

tcpmux stream tcp nowait root internal tcpmux/hello stream tcp nowait nobody /usr/local/libexec/hello hello
helloの行にある nobody はこのプログラムを nobody 権限で実行することを意味します。

そして /etc/hosts.allow の冒頭に以下の1行を追加します。

hello : ALL : allow
こうして、現在走っている inetd デーモンに HUP シグナルを浴びせて inetd を再起動させます。オプション -wW を付けておくのが良いでしょう。

telnetで繋げて見ましょう。繋げるサービス名は tcpmux です。

[local] /home/naoki><FONT COLOR=BROWN>telnet somehost <FONT COLOR=RED>tcpmux</FONT></FONT> Trying 123.45.67.8... Connected to somehost.somewhere.com Escape character is '^]'.
と、ここで黙ってしまいますが、ここで hello リターンと打ち込んで下さい。 すると hello のプログラムが動き出します。

<FONT COLOR=BROWN>hello</FONT> Your name, sir/madam: <FONT COLOR=BROWN>Naoki</FONT> Hello, Naoki. Connection closed by foreign host. [local] /home/naoki>

一旦 inetd の設定がうまくいけば、プログラムの変更はいつでもできますし、 その変更が直ちにサービスに反映されます。 これほど簡単にネットワークサービスのプログラムを自分で作ることができて、 なおかつアクセス制限もしてくれるので、これではもう自分でネットワーク プログラムを組む必要も無いかというと、やはりプログラマは 自分でプログラムしたがるのでしょう。Inetdに管理させるサービスには 利用頻度の高いサービスは向いていません。例えば sendmail や httpd は inetdではなく独立(standalone)にデーモンとして動かすべきなのです。


Inetdから起動されるプログラムでクライアントの情報を知る

getpeername関数をデスクリプタ 0 または 1 に対して 実行するとクライアントのアドレスを入手できます。

sockaddr_in c_addr; socklen_t len = sizeof(c_addr); getpeername( 0, (sockaddr*)&c_addr, &len ); printf("Client IP %s, PORT %u\n", inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port) );

目次

Copyright(C) by Naoki Watanabe. Jan 1st, 2001.
渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp