Previous Next Table of Contents

3. Virtuald

3.1 Comment ça marche

Toute connexion réseau est composée de deux paires adresse IP/port. L'API (Applications Program Interface, ou Interface de Programmation d'Applications) pour la programmation réseau est nommée l'API Sockets. La socket agit comme un fichier ouvert, et vous pouvez envoyer ou recevoir des données à travers une connexion réseau en lisant ou en écrivant dans la socket. Il existe une fonction, getsockname, qui retourne l'adresse IP de la socket locale. Virtuald utilise getsockname pour déterminer sur quel adresse IP de la machine locale la connexion a été faite. Virtuald lit un fichier de configuration pour récupérer le répertoire associé à cette adresse IP. Il va utiliser chroot sur ce répertoire et prendre en compte la connexion au service. Chroot change le répertoire / (le répertoire root) vers un nouveau point, de sorte que tout ce qui est au dessus de ce répertoire devient inaccessible pour le programme. Ainsi, chaque adresse IP se voit assigné un système vi! ! ! rtuel de fichiers. Pour le programme réseau, ceci est transparent, et le programme va se comporter comme si de rien n'était. Virtuald, en conjonction avec un programme comme inetd, peut être utilisé pour virtualiser n'importe quel service.

3.2 inetd

Inetd est un super serveur réseau qui écoute sur de multiples ports et, lorsqu'il reçoit une demande de connexion (par exemple, une requête POP), inetd réalise la connexion et la passe au programme spécifié. Cela évite de faire tourner des serveurs pour rien lorsqu'il n'y a aucune demande pour eux.

Un fichier /etc/inetd.conf standard ressemble à ceci :

ftp stream tcp nowait root /usr/sbin/tcpd wu.ftpd -l -a
pop-3 stream tcp nowait root /usr/sbin/tcpd in.qpop -s

Un /etc/inetd.conf virtuel ressemble à cela :

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

Chaque service se voit attribué un fichier de configuration qui contrôlera quelles IPs et quels répertoires sont autorisés pour ce service. Vous pouvez avoir un fichier de configuration principal ou de nombreux fichiers de configuration si vous désirez que chaque service se voit attribuer une liste de domaines différente. Un fichier virtual.conf ressemble à ceci :

# C'est un commentaire, comme le sont les lignes blanches

# 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

3.4 Le code source de virtuald

#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.19 $");
        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;
}


Previous Next Table of Contents