Dipartimento di Informatica - Conversioni di Nomi ed Il DNS … · 2003. 12. 2. · 4 Esempio oggetto restituito da gethostbyname per il nome koala un nome canonico (bsdi), due alias
Post on 16-Sep-2020
2 Views
Preview:
Transcript
a.a. 2003/04
Università degli studi di SalernoLaurea in Informatica
Prof. Vincenzo Aulettaauletta@dia.unisa.it
http://www.dia.unisa.it/professori/auletta/
Conversioni di Nomi ed Indirizzi
1
Interazione con il DNSIl DNS consente di convertire dinamicamente nomi di dominio in indirizzi IP e viceversaL’interazione con il name server avviene tramite l’API resolver
l’applicazione interagisce con il name server utilizzando le funzioni dell’APIil funzionamento delle funzioni è modificabile tramite file di configurazione
/etc/resolv.conf/etc/hosts, /etc/services, ecc.
Il resolver è utilizzato in generale per calcolare corrispondenze tra nomi ed indirizzi
nomi di servizio e numeri di porta
2
Funzioni del resolver#include <netdb.h>struct hostent* gethostbyname(const char* hostname);
struct hostent* gethostbyaddr(const char* addr, size_t len, int family);
RestituisceNULL se errorepuntatore diverso da NULL se OK
Permettono di interrogare il resolver per effettuare conversioni da nomi ad indirizzi
gethostbyname: nome indirizzogethostbyaddr: indirizzo nome
3
Struttura hostentLe funzioni gethostbyname e gethostbyaddrrestituiscono puntatori ad oggetti hostent allocati staticamente
struct hostent {char* h_name; /* nome canonico */char** h_aliases; /* elenco di alias */int h_addrtype; /* tipo di indirizzo */int h_length; /* lunghezza dell’indirizzo */char** h_addr_list; /* elenco indirizzi */
};#define h_addr h_addr_list[0]
h_aliases e h_addr_list sono array di stringhe terminati da una stringa nulla
4
Esempiooggetto restituito da gethostbyname per il nome koala
un nome canonico (bsdi), due alias (koala e panda) e tre indirizzi IP 4 (140.252.1.11, 140.252.3.54, 140.252.4.54)
h_nameh_aliasesh_addrtypeh_lengthh_addr_list
bsdi
koalapandaNULL
AF_INET4
140.252.1.11140.252.3.54140.252.4.54NULL 5
Funzionamento del ResolverIl resolver può operare sia localmente che interagendo con il DNS
il file /etc/resolv.conf contiene gli indirizzi dei name server da contattare e le regole di espansioneil file /etc/hosts contiene un elenco delle corrispondenze nome/indirizzo
il file di configurazione (/etc/resolv.conf) stabilisce le regole di funzionamento del resolver
per default contatta prima i name server e poi legge il file locale
6
ErroriIn caso di errore le funzioni del resolverscrivono un codice nella variabile h_errno
la funzione hstrerror() legge il valore di h_errno e restituisce un messaggio di errore appropriatoi valori che può assumere h_errno sono definiti in <netdb.h>
HOST_NOT_FOUND Il nome specificato è sconosciutoTRY_AGAIN Un errore temporaneo si è verificato su un name serverNO_RECOVERY Un errore irrimediabile si è verificato su un name serverNO_ADDRESS (NO_DATA) Il nome specificato è valido ma non ha un indirizzo IP
7
Conversioni Servizi/Porte#include <netdb.h>struct servent* getservbyname(const char* servname, const char*protoname);
struct servent* getservbyaddr(int port, const char* protoname);
Permettono di interrogare il resolver per effettuare conversioni nomi di servizi/numeri di porta
getservbyname: nome portagetservbyaddr: porta nome
Il resolver non interagisce con il DNS ma legge soltanto il contenuto di un file locale (/etc/services)
8
Struttura serventLe funzioni getservbyname e getservbyaddrrestituiscono puntatori ad oggetti servent allocati staticamente
struct servent {char* s_name; /* nome ufficiale del servizio */char** s_aliases; /* elenco di alias */int s_port; /* numero di porta */char* s_proto; /* protocollo da utilizzare */
};
9
Altri Tipi di Interrogazioni#include <netdb.h>struct netent* getnetbyname(const char* netname);
struct netent* getnetbyaddr(long net, int type);
struct protoent* getprotobyname(const char* protoname);
struct protoent* getprotobynumber(int proto);
consentono di assegnare dei nomi a reti e protocolli
funzioni poco utilizzateIl resolver legge i file locali (/etc/networks e/etc/protocols)
10
Altre Funzioni#include <netdb.h>void sethostent(int flag);
void endhostent(void);
#include <unistd.h>#include <sys/utsname.h>int gethostname(char* name, size_t len); /* name contiene il risultato */
sethostent(true) consente di utilizzare TCP per interrrogare il DNSendhostent() ripristina l’uso di UDPgethostname() restituisce il nome dell’host corrente
stesso risultato ottenuto con uname()11
Client daytime con resolver – 1 1.legge da
linea di comando il nome del server e del servizio
2.recupera l’elenco di indirizzi IP del server
3.recupera il numero di porta ed il tipo di protocollo
int main(int argc, char **argv) {int sockd, n;char recvline[MAXLINE + 1], str[MAXLINE];struct sockaddr_in servaddr;struct in_addr **elenco_addr;struct hostent *hp;struct servent *sp;
if (argc != 3) (1)err_quit("utilizzo: dtime_client <nome dell'host> <nome del
servizio>");if ( (hp = gethostbyname(argv[1])) == NULL) (2)
err_quit("errore nella gethostbyname per l'host %s: %s",argv[1], hstrerror(h_errno));
if ( (sp = getservbyname(argv[2], "tcp")) == NULL)(3)err_quit("errore nella getservbyname per il servizio %s",
argv[2]);elenco_addr = (struct in_addr **) hp->h_addr_list;
12
Client daytime con resolver – 2 4. prova a
connettersi a tutti gli indirizzi forniti dal resolver
5. crea il socket6. riempie
servaddr7. tenta di
connettersi e se ci riesce esce dal ciclo
8. altrimenti chiude il socket
for ( ; *elenco_addr != NULL; elenco_addr++) { (4)if( (sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
err_sys("errore nella socket"); (5)bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET; (6)servaddr.sin_port = sp->s_port;memcpy(&servaddr.sin_addr, *elenco_addr, sizeof(struct
in_addr));if (connect(sockd, (SA *) &servaddr, sizeof(servaddr)) == 0)
(7)break;
err_ret("connessione non riuscita con %s:%d", inet_ntop(hp->h_addrtype, *elenco_addr, str,
sizeof(str)), ntohs(sp->s_port));close(sockd); (8)
}13
Client daytime con resolver – 3
9. se è uscito dal ciclo senza essere riuscito a stabilire la connessione esce
10.legge la data e la stampa
if (*elenco_addr == NULL) (9)err_quit("impossibile stabiliree una connessione");
while ( (n = read(sockd, recvline, MAXLINE)) > 0) { (10)recvline[n] = 0;if( fputs(recvline, stdout) == EOF )
err_sys("errore in fputs");}exit(0);
}
14
Server daytime con resolver – 1 int main(int argc, char **argv) {
pid_t pid;int listend, connd;struct sockaddr_in servaddr;char buff[MAXLINE];time_t ticks;struct servent *sp;
if( (listend = socket(AF_INET, SOCK_STREAM, 0)) < 0)err_sys("errore in socket");
if( (sp = getservbyname("daytime", "tcp")) == NULL ) {fprintf(stderr, "errore nella getservbyame perr il servizio %s", argv[2]);exit(-1);
}/* riempie la struttura servaddr */servaddr.sin_port = sp->s_port;if( (bind(listend, (SA *) &servaddr, sizeof(servaddr))) < 0)
err_sys("errore in bind"); 15
Server daytime con resolver – 2 if( listen(listend, 5) < 0 )
err_sys("errore in listen");for ( ; ; ) {
if( (connd = accept(listend, (struct sockaddr *) NULL, NULL)) < 0)err_sys("errore in accept");
if( (pid = fork()) == 0 ) {/* il figlio chiude il socket di ascolto */ticks = time(NULL);snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));if( write(connd, buff, strlen(buff)) != strlen(buff) )
err_sys("errore in write");/* il figlio chiude il socket di connessione */exit(0);
}/* il padre chiude il socket di connessione */
}}
16
Funzioni Indipendenti dal Protocollo
Le funzioni gethostbyname e gethostbyaddrdipendono dal tipo di indirizzi utilizzati e sono obsolete
Per utilizzare l’indirizzo restituito da gethostbynamedobbiamo sapere a che famiglia appartiene per copiarlo nel campo opportuno di struct sockaddr
Lo standard POSIX 1.g ha introdotto due nuove funzioni per interfacciarsi con il resolver
Sono indipendenti dal tipo di indirizziRestituiscono informazioni immediatamente utilizzabili dall’applicazione
L’applicazione non deve sapere nulla di indirizzi, protocolli, ecc. 17
Funzione getaddrinfo#include <netdb.h>int getaddrinfo(const char* host, const char* serv,
const struct addrinfo *hints, struct addrinfo **result);
RestituisceDiverso da 0 se errore0 se OK
Consente di fare interrogazioni sia per nomi di host che di servizi contemporaneamente
Legge dalla struttura puntata da hints il tipo di informazioni richiesterestituisce il risultato nella lista puntata da result
18
Struttura addrinfostruct addrinfo {
int ai_flags; /* nome canonico */int ai_family; /* famiglia di indirizzi */int ai_socktype; /* tipo di socket */int ai_protocol; /* tipo di protocollo */size_t ai_addrlength; /* lunghezza di ai_addr */
char* ai_canonname; /* puntatore al nome ufficiale */struct sockaddr *ai_addr; /* puntatore all’indirizzostruct addrinfo *ai_next; /* puntatore al nodo succ */
};
Tutti i puntatori indirizzano memoria allocata dinamicamente
L’applicazione deve preoccuparsi di rilasciare la memoriaLa funzione freeaddrinfo() rilascia tutta la memoria associata ad un oggetto addrinfo
19
Esempio Richiediamo informazioni sul servizio daytime e l’host koala
Specifichiamo che vogliamo indirizzi IP 4 e trasporto TCP
ai_flagai_familyai_socktypeai_protocolai_addrlen
AF_INET
0
AI_CANONNAME
SOCK_STREAM
ai_canonnameai_addrai_next
0NULLNULLNULL
HINTS
bsdi.xyz.com
ai_flagai_familyai_socktypeai_protocolai_addrlen
AF_INET
0
0
SOCK_STREAM
ai_canonnameai_addrai_next
16
140.252.1.11AF_INET 7
prossimo nodo
RESULTS
20
ErroriIn caso di errore la funzione getaddrinfo() restituisce un intero nonnegativo
la funzione gai_strerror() legge il valore restituito dalla funzione e restituisce un messaggio di errore appropriato
EAI_ADDRFAMILY tipo di indirizzo non supportato per nameEAI_AGAIN un errore temporaneo si è verificato su un name serverEAI_FAIL un errore irrecuperabile si è verificato su un name serverEAI_BADFLAGS valore non corretto di ai_flagsEAI_FAMILY valore di ai_family non supportatoEAI_MEMORY errore di allocazione della memoriaEAI_NODATA nessun indirizzo associato a nameEAI_NONAME name o service non conosciutoEAI_SERVICE service non supportato per ai_socktypeEAI_SOCKTYPE ai_socktype non supportatoEAI_SYSTEM errore di sistema restituito in errno 21
Funzione getnameinfo#include <netdb.h>int getaddrinfo(const struct sockaddr* sockaddr, socklen_t addrlen,
char* host, size_t hostlen,char* serv, size_t servlen, int flags);
Restituisce-1 se errore0 se OK
Consente di trasformare l’indirizzo del socket contenuto in sockaddr in due stringhe
host contiene il nome dell’host e serv contiene il nome del serviziohostlen e servlen sono le lunghezze allocate per le stringheflags consente di modificare il comportamento di default della funzione
22
Utilizzo di getaddrinfo()E’ possibile costruire delle funzioni che nascondono all’applicazione tutti i dettagli relativi alla gestione del socket
L’applicazione usa solo i nomi dell’host e del servizioLe funzioni utilizzano le informazioni restituite da getaddrinfo() per invocare le funzioni sul socket
Useremo le seguenti funzionitcp_connect() (client TCP)tcp_listen() (server TCP)udp_client() (client UDP non connesso)udp_connect() (client UDP connesso)udp_server() (server UDP)
23
Schema BaseTutte le funzioni adotteranno il seguente schema
Leggono da linea di comando nome dell’host e del servizio
È possibile anche specificare l’indirizzo IP o il numero di porta
Invocano getaddrinfo()Scorrono la lista di risultati e provano ad utilizzare ognuno degli indirizzi
Appena ne trovano uno funzionante esconoSe nessuno funziona escono con un errore
24
Tcp_connect() – 1
1.Riempie i campi di hints2.Invoca getaddrinfo
Risultato restituito nella lista puntata da risp
int tcp_connect(const char *host, const char *serv) {int sockd, n;struct addrinfo hints, *risp, *backup;char buff[MAXLINE];
bzero(&hints, sizeof(struct addrinfo));hints.ai_family = AF_UNSPEC; (1)hints.ai_socktype = SOCK_STREAM;if ( (n = getaddrinfo(host, serv, &hints, &risp)) != 0) (2)
err_quit("errore in tcp_connect per %s:%s: %s", host, serv, gai_strerror(n));backup = risp;
25
Tcp_connect() – 2
3. Cicla su tutti gli indirizzi di rispProva ad eseguire prima socket() e poi connect() ed esce se ha stabilito la connessione
4. Dealloca la memoria dinamica
do { (3)sockd = socket(risp->ai_family, risp->ai_socktype, risp->ai_protocol);if (sockd < 0)
continue;if (connect(sockd, risp->ai_addr, risp->ai_addrlen) < 0)
/* ignora questo indirizzo e chiude il socket */break;
} while ( (risp = risp->ai_next) != NULL);if (risp == NULL)
err_sys("errore in tcp_connect per %s:%s", host, serv);freeaddrinfo(backup); (4)return(sockd);
}
26
Tcp_listen() – 1
1.Richiede socket passivo2.Invoca getaddrinfo
Int tcp_listen(const char *host, const char *serv, socklen_t *addrlenp) {int listend, n;const int on = 1;struct addrinfo hints, *risp, *backup;
bzero(&hints, sizeof(struct addrinfo));hints.ai_flags = AI_PASSIVE;hints.ai_family = AF_UNSPEC;hints.ai_socktype = SOCK_STREAM;if ( (n = getaddrinfo(host, serv, &hints, &risp)) != 0)
err_quit("errore in tcp_listen per %s:%s: %s", host, serv, gai_strerror(n));backup = risp;
27
Tcp_listen() – 2 do {
listend = socket(risp->ai_family, risp->ai_socktype, risp->ai_protocol);if (listend < 0) continue;if( setsockopt(listend, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) (3)
err_sys("errore in setsockopt");if (bind(listend, risp->ai_addr, risp->ai_addrlen) < 0)
/* ignora questo indirizzo e chiude il descrittore. */break;
} while ( (risp = risp->ai_next) != NULL);if (risp == NULL) err_quit("errore in tcp_listen per %s:%s", host, serv);if( listen(listend, LISTENQ) < 0 ) err_sys("errore in listen"); (4)if (addrlenp) *addrlenp = risp->ai_addrlen; (5)freeaddrinfo(backup); (6)return(listend);
}
3. Setta il socket per poter utilizzare una porta occupata4. Se socket() e bind() riescono chiama la listen()5. Restituisce in addrlenp un puntatore alla lunghezza dell’indirizzo
assegnato al socket6. Dealloca la memoria dinamica
28
Client daytime con tcp_connect() –1
1. legge da linea di comando il nome del server e del servizio
2.recupera l’elenco di indirizzi IP del server
3.recupera il numero di porta ed il tipo di protocollo
int main(int argc, char **argv) {int sockd, n;char recvline[MAXLINE + 1];socklen_t len;struct sockaddr *sa;
if (argc != 3) (1)err_quit("utilizzo: dtime_name_client <nome dell'host>
<nome del servizio>");sockd = tcp_connect(argv[1], argv[2]);if( (sa = malloc(MAXSOCKADDR) == NULL )
err_sys(“errore in malloc”);len = MAXSOCKADDR;if( getpeername(sockd, sa, &len) < 0 )
err_sys(“errore in getpeername”);printf 29
Client daytime con tcp_connect –2
9. se è uscito dal ciclo senza essere riuscito a stabilire la connessione esce
10.legge la data e la stampa
while ( (n = read(sockd, recvline, MAXLINE)) > 0) { (10)recvline[n] = 0;if( fputs(recvline, stdout) == EOF )
err_sys("errore in fputs");}exit(0);
}
30
Server daytime con tcp_listen – 1
int main(int argc, char **argv) {pid_t pid;int listend, connd;struct sockaddr *cliaddr;socklen_taddrlen, len;struct linger ling;char buff[MAXLINE];time_t ticks;
switch(argc) { (1)case 2:
listend = tcp_listen(NULL, argv[1], &addrlen); break;case 3:
listend = tcp_listen(argv[1], argv[2], &addrlen); break;default:
err_quit("utilizzo: dtime_name_server [<host>] <servizio o porta>");}if( (cliaddr = malloc(addrlen)) == NULL ) err_sys("errore in malloc"); (2)
1. legge da linea di comando il nome del server e del servizio
2. Alloca la memoria per contenere l’indirizzo del client
31
Server daytime con tcp_listen – 2 for ( ; ; ) {
len = addrlen;if( (connd = accept(listend, cliaddr, &len)) < 0) err_sys("errore in accept");ling.l_onoff = 1;ling.l_linger = 0;if( setsockopt(connd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) < 0 )
err_sys("errore in setsockopt");if( (pid = fork()) == 0 ) {
/* il processo figlio chiude il socket di ascolto */ticks = time(NULL);snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));if( write(connd, buff, strlen(buff)) != strlen(buff) )
err_sys("errore in write");/* il processo figlio chiude il socket di connessione */exit(0);
}/* il processo padre chiude il socket di connessione */
}
32
Client e Server UDPDal sito è possibile scaricare
il codice delle funzioni udp_client(), udp_server() e udp_connect()
logica simile a tcp_connect() e tcp_listen()
Il codice di un client ed un server UDP indipendenti dal protocollo
top related