Avanti Indietro Indice

3. Virtuald

3.1 Introduzione

Ogni connessione di rete è basata su due coppie di indirizzi IP e porte. L'`API' (Applications Program Interface) per la programmazione di rete viene chiamata `Socket API'. Un `socket' si comporta come un file aperto, con operazioni di lettura/scrittura su di esso è possibile scambiare dati su una connessione di rete. C'è una funzione chiamata getsockname che restituisce l'indirizzo IP del socket locale. Virtuald in primo luogo utilizza getsockname per determinare a quale indirizzo IP della macchina locale si vuole accedere. Quindi legge da un file di configurazione quale directory è associata a tale indirizzo IP. Virtuald fa chroot a quella directory e passa la connessione al servizio. Chroot reimposta `/', la directory radice, in un nuovo punto dell'albero delle directory, in modo tale che il programma in esecuzione non possa accedere a nulla fuori da questo ramo. Quindi ogni indirizzo IP è associato ad un proprio filesystem virtuale. Tutto ciò è trasparente per il programma di rete, che si comporterà come se niente fosse successo. Virtuald può quindi essere utilizzato insieme con un programma come inetd per rendere virtuale un servizio.

3.2 Inetd

Inetd è un super server di rete che sta in ascolto su varie porte e, quando riceve una connessione (ad esempio, una richiesta pop in entrata), effettua la fase di negoziazione e passa la connessione ad un programma che gestisce lo specifico servizio. Questo per evitare che vengano eseguiti dei servizi che restano inattivi quando inutilizzati.

Un file /etc/inetd.conf standard appare così:

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

Il file /etc/inetd.conf di un sistema in cui si utilizza virtuald appare così:

ftp stream tcp nowait root /usr/local/bin/virtuald \
        virtuald /virtual/conf.ftp wu.ftpd -l -a
pop-3 stream tcp nowait root /usr/local/bin/virtuald \
        virtuald /virtual/conf.pop in.qpop -s

3.3 File di configurazione

Ciascun servizio ha un file di configurazione che controlla quali indirizzi IP e directory sono autorizzati per quel servizio. Si può avere o un unico file principale oppure più file di configurazione, se si desidera che ad ogni servizio sia associato una lista diversa di domini. Un tipico file di configurazione appare così:

# Questo è un commento e allo stesso modo vengono trattate le linee vuote

# Formato: IndirizzoIP [spazio] directory [nessun spazio]
10.10.10.129 /virtual/domain1.com
10.10.10.130 /virtual/domain2.com
10.10.10.157 /virtual/domain3.com

# Opzione predefinita per tutti gli altri indirizzi IP
default /

3.4 Codice sorgente

Questo è il codice sorgente in C del programma virtuald. È necessario compilarlo e installarlo in /usr/local/bin con permessi 0755, utente root, e gruppo root. L'unica opzione di compilazione è VERBOSELOG che attiva/disattiva la registrazione delle connessioni nei file di log:

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

#undef VERBOSELOG

#define BUFSIZE 8192

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)
        {
                syslog(LOG_ERR,"getipaddr: getsockname failed: %m");
                return -1;
        }
        if (!(ipptr=inet_ntoa(virtual_addr.sin_addr)))
        {
                syslog(LOG_ERR,"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")))
        {
                syslog(LOG_ERR,"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,' ')))
                {
                        syslog(LOG_ERR,"iptodir: strchr failed");
                        return -1;
                }
                *bufptr++=0;
                if (!strcmp(buffer,ipaddr))
                {
                        strncpy(dirbuf,bufptr,sizeof(dirbuf)-1);
                        *dir=dirbuf;
                        break;
                }
                if (!strcmp(buffer,"default"))
                {
                        strncpy(dirbuf,bufptr,sizeof(dirbuf)-1);
                        *dir=dirbuf;
                        break;
                }
        }
        if (fclose(fp)==EOF)
        {
                syslog(LOG_ERR,"iptodir: fclose failed: %m");
                return -1;
        }
        if (!*dir)
        {
                syslog(LOG_ERR,"iptodir: ip not found in conf file");
                return -1;
        }
        return 0;
}

int main(int argc,char **argv)
{
        char *ipaddr,*dir;

        openlog("virtuald",LOG_PID,LOG_DAEMON);

#ifdef VERBOSELOG
        syslog(LOG_ERR,"Virtuald Starting: $Revision: 1.49 $");
#endif
        if (!argv[1])
        {
                syslog(LOG_ERR,"invalid arguments: no conf file");
                exit(0);
        }
        if (!argv[2])
        {
                syslog(LOG_ERR,"invalid arguments: no program to run");
                exit(0);
        }
        if (getipaddr(&ipaddr))
        {
                syslog(LOG_ERR,"getipaddr failed");
                exit(0);
        }
#ifdef VERBOSELOG
        syslog(LOG_ERR,"Incoming ip: %s",ipaddr);
#endif
        if (iptodir(&dir,ipaddr,argv[1]))
        {
                syslog(LOG_ERR,"iptodir failed");
                exit(0);
        }
        if (chroot(dir)<0)
        {
                syslog(LOG_ERR,"chroot failed: %m");
                exit(0);
        }
#ifdef VERBOSELOG
        syslog(LOG_ERR,"Chroot dir: %s",dir);
#endif
        if (chdir("/")<0)
        {
                syslog(LOG_ERR,"chdir failed: %m");
                exit(0);
        }
        if (execvp(argv[2],argv+2)<0)
        {
                syslog(LOG_ERR,"execvp failed: %m");
                exit(0);
        }

        closelog();

        exit(0);
}


Avanti Indietro Indice