ネットワーク接続は"IPアドレス"と"ポート番号"のペア
からなっています。ネットワークプログラミングのAPI(Applications Program
Interface)はsockets api
と呼ばれます。ソケットは開いたファイルのように
振舞い、これに対する読み書きをすることでネットワーク経由でデータをやりとり
ができます。
ローカルソケットのIPアドレスを返すファンクションコールgetsockname
があります。
virtualdはgetsockname
(訳注:/lib/libc.so
)を、
どのローカルマシンのIPがアクセスされているかを決めるのに用います。
Virtualdは設定ファイルを読み込み、そのIPに対応するディレクトリを取得します。
そのディレクトリに chroot
した後、実際に行われるサービスに接続を
引き渡します。
chroot
はルートディレクトリ'/' を別のディレクトリにセット
し直し、そのディレクトリ(新しいルートディレクトリ)よりも上にある全てのもの
は実行されているプログラムからは見えません(切り離されます)。
こうして各IPアドレスはそれぞれの仮想ファイルシステムを取得します。
これはネットワークプログラムからは透過的なので(上に書いたような操作は
隠してあるので)、プログラムは何事もなかったかのように動作します。
このようにして、inetdといったプログラムと連結されたVirtuald はいろいろなサービスを仮想化して使うことができるのです。
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となります。
それぞれのサービスに対応する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の設定に合わせた)該当ディレクトリ およびファイル名にコピーします)。
(訳注:この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; }