次のページ 前のページ 目次へ

3. バーチャルデーモン(virtuald)

3.1 どのような動作をするか

ネットワーク接続は"IPアドレス"と"ポート番号"のペア からなっています。ネットワークプログラミングのAPI(Applications Program Interface)はsockets apiと呼ばれます。ソケットは開いたファイルのように 振舞い、これに対する読み書きをすることでネットワーク経由でデータをやりとり ができます。

ローカルソケットのIPアドレスを返すファンクションコールgetsockname があります。

virtualdはgetsockname(訳注:/lib/libc.so)を、 どのローカルマシンのIPがアクセスされているかを決めるのに用います。 Virtualdは設定ファイルを読み込み、そのIPに対応するディレクトリを取得します。 そのディレクトリに chrootした後、実際に行われるサービスに接続を 引き渡します。

chroot はルートディレクトリ'/' を別のディレクトリにセット し直し、そのディレクトリ(新しいルートディレクトリ)よりも上にある全てのもの は実行されているプログラムからは見えません(切り離されます)。 こうして各IPアドレスはそれぞれの仮想ファイルシステムを取得します。 これはネットワークプログラムからは透過的なので(上に書いたような操作は 隠してあるので)、プログラムは何事もなかったかのように動作します。

このようにして、inetdといったプログラムと連結されたVirtuald はいろいろなサービスを仮想化して使うことができるのです。

3.2 inetd

Inetdは複数のポートを監視し、接続があった場合に(例えばpop要求があったとき などに)ネットワークネゴシエーション行って指定されたプログラムに接続を 引き渡すスーパーサーバです。 これにより、必要がなくて何もしていないサーバがないようにします。

標準的な /etc/inetd.confファイルは

ftp stream tcp nowait root /usr/sbin/tcpd wu.ftpd -l -a
pop-3 stream tcp nowait root /usr/sbin/tcpd in.qpop -s
となります。(訳注:pop-3はpop3の場合はpop3にして下さい)。

また仮想的な/etc/inetd.confファイルは

ftp stream tcp nowait root /usr/bin/virtuald virtuald /virtual/conf.ftp wu.ftpd -l -a
pop-3 stream tcp nowait root /usr/bin/virtuald virtuald /virtual/conf.pop in.qpop -s
となります。

3.3 virtual.conf

それぞれのサービスに対応するIPとディレクトリをコントロールする confファイルを取得します。一つのマスターconfファイルがあり、また ドメインの異なるリストのサービスが必要なときはそれに応じたconfファイル を用意することができます。virtual.confは以下のような内容です。

# This is a comment and so are blank lines

# Format IP <SPACE> dir <NOSPACES>
10.10.10.129 /virtual/foo.bar.com
10.10.10.130 /virtual/bar.foo.com
10.10.10.157 /virtual/boo.la.com
(訳注:実際の中身は各設定環境に合わせて書き換えて下さい。 そしてこのファイルは(上のinitd.confの設定に合わせた)該当ディレクトリ およびファイル名にコピーします)。

3.4 virtualdのソース

(訳注:このVirtualdのソースは gcc -o viturald virtuald.cとして コンパイルできます。また後の文章と合わせるために/usr/binに コピーします)


#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>

#define BUFSIZE 8192

main(int argc,char **argv)
{
        char buffer[BUFSIZE];
        char *ipaddr,*dir;

        logit("Virtuald Starting: $Revision: 1.2 $");
        if (!argv[1])
        {
                logit("invalid arguments: no conf file");
                quitting_virtuald(0);
        }
        if (!argv[2])
        {
                logit("invalid arguments: no program to run");
                quitting_virtuald(0);
        }
        if (getipaddr(&ipaddr))
        {
                logit("getipaddr failed");
                quitting_virtuald(0);
        }
        sprintf(buffer,"Incoming ip: %s",ipaddr);
        logit(buffer);
        if (iptodir(&dir,ipaddr,argv[1]))
        {
                logit("iptodir failed");
                quitting_virtuald(0);
        }
        if (chroot(dir)<0)
        {
                logit("chroot failed: %m");
                quitting_virtuald(0);
        }
        sprintf(buffer,"Chroot dir: %s",dir);
        logit(buffer);
        if (chdir("/")<0)
        {
                logit("chdir failed: %m");
                quitting_virtuald(0);
        }
        if (execvp(argv[2],argv+2)<0)
        {
                logit("execvp failed: %m");
                quitting_virtuald(0);
        }
}

int logit(char *buf)
{
        openlog("virtuald",LOG_PID,LOG_DAEMON);
        syslog(LOG_ERR,buf);
        closelog();
        return 0;
}

int quitting_virtuald(int retval)
{
        exit(retval);
        return 0;
}

int getipaddr(char **ipaddr)
{
        struct sockaddr_in virtual_addr;
        static char ipaddrbuf[BUFSIZE];
        int virtual_len;
        char *ipptr;

        virtual_len=sizeof(virtual_addr);
        if (getsockname(0,(struct sockaddr *)&virtual_addr,&virtual_len)<0)
        {
                logit("getipaddr: getsockname failed: %m");
                return -1;
        }
        if (!(ipptr=inet_ntoa(virtual_addr.sin_addr)))
        {
                logit("getipaddr: inet_ntoa failed: %m");
                return -1;
        }
        strncpy(ipaddrbuf,ipptr,sizeof(ipaddrbuf)-1);
        *ipaddr=ipaddrbuf;
        return 0;
}

int iptodir(char **dir,char *ipaddr,char *filename)
{
        char buffer[BUFSIZE],*bufptr;
        static char dirbuf[BUFSIZE];
        FILE *fp;

        if (!(fp=fopen(filename,"r")))
        {
                logit("iptodir: fopen failed: %m");
                return -1;
        }
        *dir=NULL;
        while(fgets(buffer,BUFSIZE,fp))
        {
                buffer[strlen(buffer)-1]=0;
                if (*buffer=='#' || *buffer==0)
                        continue;
                if (!(bufptr=strchr(buffer,' ')))       
                {
                        logit("iptodir: strchr failed");
                        return -1;
                }
                *bufptr++=0;
                if (!strcmp(buffer,ipaddr))
                {
                        strncpy(dirbuf,bufptr,sizeof(dirbuf)-1);
                        *dir=dirbuf;
                        break;
                }
        }
        if (fclose(fp)==EOF)
        {
                logit("iptodir: fclose failed: %m");
                return -1;
        }
        if (!*dir)
        {
                logit("iptodir: ip not found in conf file");
                return -1;
        }
        return 0;
}


次のページ 前のページ 目次へ