Every network connection is made up of two IP address/port pairs.
The API (Applications Program Interface) for network programming is
called the Sockets API. The socket acts like an open file and by
reading/writing to it you can send data over a network connection.
There is a function call getsockname
that will return the
IP address of the local socket. Virtuald uses getsockname
to determine which IP on the local machine is being accessed. Virtuald reads
a config file to retrieve the directory associated with that IP. It will
chroot
to that directory and hand the
connection off to the service. Chroot
resets / or the root
directory to a new point so everything higher in the directory tree is cut
off from the running program. Therefore, each IP address gets their own
virtual filesystem. To the network program this is transparent
and the program will behave like nothing happened. Virtuald
in conjunction with a program like inetd can then be used to
virtualize any service.
Inetd is a network super server that listens at multiple ports and when it receives a connection (for example, an incoming pop request), inetd performs the network negotiation and hands the network connection off to the specified program. This prevents servers from running idly when they are not needed.
A standard /etc/inetd.conf file looks like this:
ftp stream tcp nowait root /usr/sbin/tcpd wu.ftpd -l -a pop-3 stream tcp nowait root /usr/sbin/tcpd in.qpop -s
A virtual /etc/inetd.conf file looks like this:
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
Each service gets a conf file that will control what IPs and directories are allowed for that service. You can have one master conf file or several conf files if you want each service to get a different list of domains. A virtual.conf file looks like this:
# 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
#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.21 $"); 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; }