3.4. ソケットとネットワーク接続

ソケットは情報を伝える手段として、特にネットワーク越しの通信に使用されて います。 ソケットは、そもそも Unix システムの流れの 1 つである BSD 系が開発しました。 しかし他の Unix ライクなシステムに対しても、およそ移植しやすくなっています。 Linux や System V 系はソケットを BSD と同レベルでサポートしており、Open Group の Single Unix Specification [Open Group 1997]でも必須とされています。 以前 System V システムでは、ネットワーク越しの通信インタフェースを別に持って いました(互換性がありませんでした)が、Solaris のようなシステムもソケットを サポートしているのは注目に値します。 socket(2)は情報を伝えるための接続ポイントを作成し、それを示すディスクリプタ を返します。これはファイルに対する open(2)とある意味同じです。 ソケットのパラメタには、プロトコル・ファミリーとタイプを指定します。つまり インターネット・ドメイン(TCP/IP version 4)や Novell の IPX もしくは「Unix ドメイン」というように。 サーバは、通常は bind(2)を呼んでから listen(2)を呼び、accept(2) もしくは select(2)します。 クライアントはというと、bind(2)(省略される場合あり)を呼び、connect(2)します。 詳しい情報はそれぞれの man を参照してください。 ソケットを利用するのに man だけでは難しいかもしれません。 Hall "Beej" [1999]のような資料を読んで、どうやってこれらのシステムコールを 組み合わせて利用するのかを学ぶのもよいと思います。

「Unix ドメイン・ソケット」はネットワーク・プロトコルに実際は該当しません。 というのは、同一マシン上で接続するに過ぎないからです。 (このドキュメントを書いている時点の標準的な Linux カーネルでは) ストリームとしてこのソケットを使う場合、名前つきパイプと非常に似てはいますが、 際だった長所があります。 Unix ドメイン・ソケットがコネクション指向である点に注目してください。 ソケットに対する新しい接続は、それぞれ新しい接続チャネルとなります。この点が 名前つきパイプとは大きく異なる点です。 この特性によって、Unix ドメイン・ソケットは、名前つきパイプの代わりに 使われ、IPC を実装し、重要なサービスを広く提供しています。 名前なしパイプもそうなのですが、socketpair(2)を使って、名前なしの Unix ドメイン・ソケットも使えます。名前なし Unix ドメイン・ソケットは、名前なし パイプとある程度似ているので、IPC に使えます。

Unix ドメイン・ソケットには、セキュリティに関する興味深い点がいくつかあります。 まず、Unix ドメイン・ソケットはファイルシステム上に存在するように見え、stat(2) も利用できますが、open(2)では開けません(socket(2)とその仲間のインタフェース を使わなければいけません)。 次に、Unix ドメイン・ソケットはファイル・ディスクリプタでプロセス間をやりとり します(ファイルの中身ではなく)。 この変わった機能は、これまで色々な場面で使われてきました。他の IPC の手段 では、この機能を利用できません(ディスクリプタは、そもそもコンピュータ・ サイエンスの言葉で言う「ケイパビリティ」の制限版として利用できます)。 ファイルディスクリプタを sendmsg(2)を使って送り、そこにある msg(メッセージ) フィールドに当たる msg_control が control message のヘッダ配列を指し 示しています(msg_controllen フィールドは配列が何バイトあるのかを指定 していなければいけません)。 control message は cmsghdr 構造体になっていて、制御データの後に実データが 続いています。この目的に使うなら、cmsg_type に SCM_RIGHTS を設定してください。 ファイルディスクリプタは recvmsg(2) を使って取り出し、後は似たような方法で データに行き着きます。 正直いうと、この機能はとても奇をてらった方法ですが、知っておいても無駄では ありません。

Linux 2.2 以降で Unix ドメイン・ソケットにさらに機能が追加されています。 それは相手側の「認証」ができる点です(pid や uid、gid を利用可能)。 サンプルコードは下記のようになります。
 /* fd= file descriptor of Unix domain socket connected
    to the client you wish to identify */

 struct ucred cr;
 int cl=sizeof(cr);

 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
   printf("Peer's pid=%d, uid=%d, gid=%d\n",
           cr.pid, cr.uid, cr.gid);

標準的な Unix での慣例では、TCP や UDP のローカルなポート番号は 1024 より 小さく、root の権限が必要になります。一方プロセスは 1024 以上のポートで あれば、制限無しにつなげられます。 Linux はこの慣例を踏襲していて、さらにプロセスは CAP_NET_BIND_SERVICE というケイパビリティが必要となり、 これで 1024 以下のポート番号に接続できます。 通常このケイパビリティは、euid が 0 であるプロセスだけが持つことができます。 冒険好きな人なら、Linux のソースを調べればわかります。 Linux 2.2.12 なら /usr/src/linux/net/ipv4/af_inet.c にある関数の inet_bind()がそれです。