Presentazione del corso - Università degli Studi di Milano

Transcript

Presentazione del corso - Università degli Studi di Milano
Risoluzione Nomi
e Indirizzi in C
Corso di laurea in Informatica
Laboratorio di Reti di Calcolatori
A.A. 2014-2015
Simone Bassis
[email protected]
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
Una nota sugli OS
o
Assumiamo di lavorare su Linux, BSD o altri
sistemi Unix
o Usando come compilatore GNU gcc
o
Per piattaforme
o Solaris/SunOS
• È necessario aggiungere alcune direttive in fase di compilazione
– -lnsl –lsocket –lresolv
– -lxnet
o Windows
•
•
•
•
Dopo diversi anni… sentitevi liberi di usare windows, ma…
Valutate se installare Cygwin (collezione di tool Unix per Windows)
O crearvi una partizione Linux
Oppure usate le librerie WinSocks
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
363
1
Una nota su Windows
#include <winsock.h>
{
WSADATA wsaData; // se non dovesse funzionare
// MAKEWORD(1,1) for Winsock 1.1,
// MAKEWORD(2,0) for Winsock 2.0:
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) {
fprintf(stderr, "WSAStartup failed.\n");
exit(1);
}
Linkando le librerie Winsock
o
o
wsock32.lib, winsock32.lib o ws2_32.lib
Ripulendo il sistema al termine
o
o
WSACleanup()
Facendo attenzione ad alcune differenze
o
o
o
o
o
Closesocket()
select() su socket descriptor
Presenza della classe Csocket
CreateProcess() e CreateThread()
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
364
POSIX
o
o
POSIX (Portable Operating System Interface for Unix) indica la
famiglia degli standard definiti dall’IEEE denominati formalmente
IEEE 1003. Il nome standard internazionale è ISO/IEC 9945.
Gli standard POSIX derivano da un progetto, iniziato circa nel
1985, finalizzato alla standardizzazione
o delle API per i software sviluppati per le diverse varianti dei sistemi
operativi UNIX
o dei comandi di shell e utility interfaces
per software compatibili su diverse piattaforme Unix e anche
diversi sistemi operativi
o
Posix per Windows
o Cygwin: POSIX-compliant development and run-time environment
o Microsoft POSIX subsystem, un sottosistema Windows opzionale (no
threads, no sockets)
o Microsoft Windows Services for UNIX (full POSIX compliance)
•
È built in nelle versioni Enterprise e Ultimate di Windows 7
o UWIN dalla AT&T Research implementa un layer POSIX sopra le APIs Win32
o MKS Toolkit
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
365
2
Socket in C
o
Un modo per dialogare con altri programmi usando
Unix file descriptor
o
Un programma Unix fa I/O avvalendosi di un file
descriptor
o Un intero associato ad un file aperto
•
•
•
•
•
•
•
Connessione di rete
FIFO
Pipe
Terminale
Un file vero e proprio
…
Tutto in Unix è un file!
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
366
Socket in C
o
o
È sufficiente ottenere un socket descriptor tramite
socket()
E invocare send() e recv()
o Si possono usare anche read() e write() ma offrono
meno controlli
o
Quali Socket?
o Non solo…
• Internet Socket
• Unix Socket
• X.25 Socket
o Ma molti altri
• In funzione della piattaforma Unix
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
367
3
/* Address families. */
#define
AF_UNSPEC
#define
AF_LOCAL
#define
AF_UNIX
#define
AF_FILE
#define
AF_INET
#define
AF_AX25
#define
AF_IPX
#define
AF_APPLETALK
#define
AF_NETROM
#define
AF_BRIDGE
#define
AF_ATMPVC
#define
AF_X25
#define
AF_INET6
#define
AF_ROSE
#define
AF_DECnet
#define
AF_NETBEUI
#define
AF_SECURITY
#define
AF_KEY
#define
AF_NETLINK
#define
AF_ROUTE
#define
AF_PACKET
#define
AF_ASH
#define
AF_ECONET
#define
AF_ATMSVC
#define
AF_SNA
#define
AF_IRDA
#define
AF_PPPOX
#define
AF_WANPIPE
#define
AF_BLUETOOTH
#define
AF_MAX
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
22
23
24
25
31
32
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Unspecified. */
Local to host (pipes and file-domain). */
Old BSD name for AF_LOCAL. */
Another non-standard name for AF_LOCAL. */
IP protocol family. */
Amateur Radio AX.25. */
Novell Internet Protocol. */
Appletalk DDP. */
Amateur radio NetROM. */
Multiprotocol bridge. */
ATM PVCs. */
Reserved for X.25 project. */
IP version 6. */
Amateur Radio X.25 PLP. */
Reserved for DECnet project. */
Reserved for 802.2LLC project. */
Security callback pseudo AF. */
AF_KEY key management API. */
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Alias to emulate 4.4BSD. */
Packet family. */
Ash. */
Acorn Econet. */
ATM SVCs. */
Linux SNA Project */
IRDA sockets. */
PPPoX sockets. */
Wanpipe API sockets. */
Bluetooth sockets. */
For now.. */
S. Bassis ([email protected])
Università di Milano – DI
368
Tre Tipologie di Socket
o
SOCK_STREAM: Stream Socket
o Connessione affidabile a due vie
o Che fa uso del protocollo TCP
o
SOCK_DGRAM: Datagram Socket
o Connectionless
o Che fa uso del protocollo UDP
o
SOCK_RAW: Raw Socket
o Accesso diretto alla rete
•
o
“Raw sockets allow new IPv4 protocols to be implemented in user space. A raw
socket receives or sends the raw datagram not including link level headers.”
In realtà su alcune piattaforme anche
o
o
o
o
SOCK_RDM (Reliable Datagram Message)
SOCK_SEQPACKET (Reliable Sequenced Packet Service)
SOCK_DCCP (Datagram Congestion Control Protocol)
SOCK_PACKET (Datalink Access)
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
369
4
Byte-Order
o
Se voglio memorizzare/inviare il numero
esadecimale b34f uso i due byte
o b3 seguito da 4f (Big Endian)
o 4f seguito da b3 (Little Endian)
• Piattaforme Intel o compatibili
o Big Endian
• sembra più sensato
• È anche definito Network Byte Order
• Da non confondersi con Host Byte Order
– Dipende ovviamente dalla piattaforma
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
370
Byte-Order
o
Sulla rete i dati viaggiano sempre in Network Byte Order
o È sufficiente assumere che l’Host Byte Order non sia corretto
o E usare funzioni che effettuano le opportune conversioni
• Così da rendere portabile il proprio codice
o
Due tipi di dati possono essere convertiti
o long (4 byte)
o short (2 byte)
o
Tramite
o
o
o
o
htons()
htonl()
ntohs()
ntohl()
host to network
host to network
network to host
network to host
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
short
long
short
long
371
5
Alcuni tipi di dati
socket descriptor
int
addrinfo: rappresenta indirizzi e hostname
struct addrinfo {
int
ai_flags;
int
ai_family;
int
ai_socktype;
int
ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
/*
/*
/*
/*
/*
/*
/*
/*
AI_PASSIVE, AI_CANONNAME, etc. */
AF_INET, AF_INET6, AF_UNSPEC */
SOCK_STREAM, SOCK_DGRAM */
use 0 for ‘any’ */
size of ai_addr in byte; tipo size_t su OS meno recenti*/
struct sockadder_in or _in6 */
full canonical hostname */
this struct can form a linked list, next node */
•
•
Si riempie tramite getaddrinfo()
Si può forzare IPv4, IPv6 o lasciarlo non specificato
•
•
Si possono ottenere diversi risultati tra cui scegliere (è una linked list)
Struct sockaddr è la parte più importante
•
Per una versione agnostica del codice
•
contiene informazioni sull’indirizzo per una ampia famiglia di socket
getaddrinfo() fa il grosso del lavoro
•
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
372
Alcuni tipi di dati
sockaddr:
conetiene informazioni su socket address
per vari tipi di socket
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
•
•
// address family, AF_xxx
// 14 bytes of protocol address
È un contenitore generico di indirizzi
Per fortuna esistono strutture parallele che si possono castare
vicendevolmente
sockaddr per indirizzi IPv4
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
•
//
//
//
//
Address family, AF_INET
Port number (in Network Byte Order)
Internet address
Same size as struct sockaddr
i byte in sin_zero devono essere settati a zero con memset()
struct sockaddr_in foo;
memset(&foo, 0, sizeof foo);
•
e compare un nuovo internet address in_addr…
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
373
6
Alcuni tipi di dati
Internet address: una struct per motivi storici
struct sin_addr {
uint32_t s_addr;
};
•
•
// a 32-bit int (4 bytes)
se ina è una struct sockaddr_in, allora ina.sin_addr.s_addr è un
riferimento all’indirizzo IP
all’epoca era una union, ma grazie a #define non cambia nulla
Ma anche: sockaddr per indirizzi IPv6
struct sockaddr_in6 {
u_int16_t sin6_family;
u_int16_t sin6_port;
u_int32_t sin6_flowinfo;
struct in6_addr sin6_addr;
u_int32_t sin6_scope_id;
struct sin6_addr {
unsigned char s6_addr[16];
};
//
//
//
//
//
address family, AF_INET6
port number, Network Byte Order
IPv6 traffic class and flow information
IPv6 address
Set of interfaces for a scope
// IPv6 address
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
374
Alcuni tipi di dati
o sockaddr generiche sia per IPv4 che per IPv6
struct sockaddr_storage {
sa_family_t ss_family;
// address family
// ciò che segue è un padding dipendente dall’implementezione… potete ignorarlo
// è definite in questo modo per questioni legate all’allineamento della struct
char __ss_pad1[_SS_PAD1SIZE];
int64_t __ss_align;
char __ss_pad2[_SS_PAD2SIZE];
};
•
•
Usato quando non si sa se l’indirizzo che si riceverà è IPv4 o IPv6
In funzione di sa_family (AF_INET or AF_INET6) castate nello struct
sockaddr opportuno
oppure: sockaddr per Unix domain socket:
struct sockaddr_un {
unsigned short sun_family;
char sun_path[108];
}
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
/* AF_UNIX */
375
7
Manipolare indirizzi IP
o
o
Per fortuna, ci sono diverse funzioni che ci vengono in aiuto
inet_pton()
o converte un indirizzo IP espresso in dot notation (o hex and colons) in uno
struct in_addr o struct in6_addr in funzione della famiglia
specificata (AF_INET o AF_INET6).
o “pton” sta per “presentation to network”
o In passato inet_addr() o inet_aton() ma non funzionano con IPv6
struct sockaddr_in sa;
// IPv4
struct sockaddr_in6 sa6;
// IPv6
inet_pton(AF_INET, "192.0.2.1", &(sa.sin_addr));
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr));
o
// IPv4
//IPv6
Attenzione al valore di ritorno
o 1: successo
o 0: l’indirizzo sorgente non è valido nella famiglia specificata
o -1: se la famiglia specificata non è supportata; errno è settato a
EAFNOSUPPORT
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
376
Manipolare indirizzi IP
o
inet_ntop()
o Come prima, ma al contrario
o macro INET_ADDRSTRLEN e INET6_ADDRSTRLEN definiscono la
lunghezza di un indirizzo IPv4 e IPv6
o Ricordatevi di controllare il valore di ritorno
•
•
Guardatevi il manuale per gestire correttamente tutte le casistiche
Sulle slide non sempre verranno gestiti opportunamente i codici di ritorno
// IPv4:
char ip4[INET_ADDRSTRLEN];
// buffer per la stringa IPv4
struct sockaddr_in sa;
// supponiamo contenga un indirizzo valido
inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("The IPv4 address is: %s\n", ip4);
// IPv6:
char ip6[INET6_ADDRSTRLEN];
// buffer per la stringa IPv6
struct sockaddr_in6 sa6;
// supponiamo contenga un indirizzo valido
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("The address is: %s\n", ip6);
o La funzione inet_ntoa() è deprecata
o In entrambi i casi non viene effettuata una risoluzione tramite DNS
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
377
8
// Helper function:
// Converte un indirizzo struct sockaddr address in una string,
// IPv4 and IPv6:
Nota: helper
function come
quella a fianco
sono essenziali
per sviluppare
codice portabile
e funzionante su
diversi sistemi di
indirizzamento
char *get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen)
{
switch(sa->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), s, maxlen);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), s, maxlen);
break;
default:
strncpy(s, "Unknown AF", maxlen);
return NULL;
// IPv4 demo di inet_ntop() e inet_pton()
}
struct sockaddr_in sa;
return s;
char str[INET_ADDRSTRLEN];
}
Manipolare
indirizzi IP
// si memorizza l’indirizzo IP in sa
inet_pton(AF_INET, "192.0.2.33", &(sa.sin_addr));
// si riottiene
inet_ntop(AF_INET, &(sa.sin_addr), str, INET_ADDRSTRLEN);
printf("%s\n", str);
// stampa "192.0.2.33"
// IPv6 demo di inet_ntop() e inet_pton()
struct sockaddr_in6 sa;
char str[INET6_ADDRSTRLEN];
// si memorizza l’indirizzo IP in sa
inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(sa.sin6_addr));
// si riottiene
inet_ntop(AF_INET6, &(sa.sin6_addr), str, INET6_ADDRSTRLEN);
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
378
printf("%s\n", str);
// stampa "2001:db8:8714:3a90::12"
S. Bassis ([email protected])
Università di Milano – DI
System Call
getaddrinfo()
o
o
Chiamando queste funzioni, è il kernel a prendere il controllo dell’esecuzione
Attenzione all’error checking e a controlli sulle entry restituite da
getaddrinfo()
o
getaddrinfo(): serve per effettuare DNS lookup e service name lookup
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *node,
const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
o
o
o
// es. "www.example.com" o indirizzo IP
// es. "http" o port number
node: hostname o indirizzo IP
service: port number o service name
hints: struct addrinfo inizializzata con informazioni rilevanti
Nota: in passato gethostbyname() o getservbyname()
o
o
Bisognava caricare il risultato in uno struct sockaddr_in
Non funziona con IPv6
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
379
9
System Call getaddrinfo()
Server side
int status;
struct addrinfo hints;
struct addrinfo *servinfo;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
// punterà ai risultati
// per assicurarsi che lo struct sia vuoto
// accetta sia IPv4 che IPv6
// TCP stream socket
if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
// ora servinfo punta ad una linked list di 1 o più struct addrinfo
// ... si eseguono tutte le istruzioni del caso finchè non servirà più servinfo
freeaddrinfo(servinfo);
o
// un po’ di pulizia sulla linked-list
Flags: ce ne sono diversi, ma AI_PASSIVE è il più rilevante
o
Se AI_PASSIVE è specificato e node è NULL, allora l’indirizzo restituito potrà essere
associato ad una socket che accetta connessioni.
•
•
o
Conterrà il "wildcard address" (INADDR_ANY per IPv4, IN6ADDR_ANY_INIT per IPv6).
Questo sarà seguito da una chiamata a bind() che inserirà nello struct sockaddr l’indirizzo
dell’host corrente
Se AI_PASSIVE non è specificato, allora l’indirizzo restituito potrà essere associato ad
una socket per connettersi o inviare messaggi
•
Se node è NULL, l’indirizzo di rete è settato all’indirizzo dell’interfaccia di loopback
(INADDR_LOOPBACK per IPv4, IN6ADDR_LOOPBACK_INIT per IPv6)
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
380
System Call
getaddrinfo()
Client side
int status;
struct addrinfo hints;
struct addrinfo *servinfo;
// punterà ai risultati
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
// per assicurarsi che lo struct sia vuoto
// accetta sia IPv4 che IPv6
// TCP stream socket
// pronto per la connessione
status = getaddrinfo("www.example.net", "3490", &hints, &servinfo);
// ora servinfo punta ad una linked list di 1 o più struct addrinfo
// etc. etc.
o
o
AF_UNSPEC può essere sostituito da AF_INET o AF_INET6
gai_strerror() stampa l’errore nel caso in cui il valore di ritorno sia diverso da 0
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
381
10
showip.c
/*
showip.c – mostra gli indirizzi IP di un host passato da
linea di comando
*/
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
#include <stdio.h>
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
#include <string.h>
return 2;
#include <sys/types.h>
}
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
printf("IP addresses for %s:\n\n", argv[1]);
int main(int argc, char *argv[])
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
for(p = res;p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// si ottiene il puntatore all’indirizzo stesso,
// facendo attenzione ai diversi campi in IPv4 e IPv6:
if (p->ai_family == AF_INET) {
// IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else {
// IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
if (argc != 2) {
fprintf(stderr,"usage:
showip hostname\n");
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
Stampa l’indirizzo IP degli host
specificati a linea di comando
// si converte l’indirizzo IP e si stampa
$ showip www.example.net
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
IP addresses for www.example.net:
IPv4: 192.0.2.88
$ showip ipv6.example.com
IP addresses for ipv6.example.com:
IPv4: 192.0.2.101
IPv6: 2001:db8:8c00:22::171
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
}
freeaddrinfo(res);
return 0;
// un po’ di pulizia
382
Esercizio C1
Scrivere un programma il più possibile simile al
programma nslookup, disponibile sia in
ambiente unix che windows, cercando di
implementarne le sole caratteristiche di base:
•
•
•
Conversione multipla da nomi a indirizzi
Conversione nei due sensi IP -> nome e viceversa
Modalità come parametro
–
•
Il valore da convertire è passato come parametro al
programma
esempio: NSLookUP www.unimi.it
Modalità in linea
–
Il programma presenta un prompt e poi tenta di
convertire tutto ciò che viene digitato
Suggerimento: può essere opportuno usare tanto la
funzione getaddrinfo() quanto getnameinfo()
Laboratorio di Reti di Calcolatori (Informatica) - A.A. 2014-2015
S. Bassis ([email protected])
Università di Milano – DI
383
11

Documenti analoghi

Basic Sniffer Tutorial

Basic Sniffer Tutorial unsigned char tos; //Tipo del servizio unsigned short t_len; //Lunghezza totale unsigned short ident; //IP Ident unsigned short frag_and_flags; //Frammenti e Flags unsigned char ttl; //Time To Live...

Dettagli

non bloccante - Università degli Studi di Milano

non bloccante - Università degli Studi di Milano Buona norma avere un protocollo che preveda come prefisso la dim. attuale del messaggio private ByteBuffer packMessage(String text) { ByteBuffer data = (ByteBuffer)Charset.forName("UTF-8").encode(t...

Dettagli

Presentazione del corso - Università degli Studi di Milano

Presentazione del corso - Università degli Studi di Milano fruire del servizio, però possono essere utili per

Dettagli

IPv6 transition - the Netgroup at Politecnico di Torino

IPv6 transition - the Netgroup at Politecnico di Torino Far more than 50% of the code related to the socket interface must be changed. And, for the rest, who knows?

Dettagli

NR #12 - OoCities

NR #12 - OoCities VB. Cosa spesso associata alla perdita delle licenze di Access. Nel caso (E SOLO IN QUESTO CASO) si abbia installato VB5 e/o Access 97 e' possibile scaricarsi da www.spippolatori.com il mio program...

Dettagli