Vous êtes sur la page 1sur 29

La programmation réseau.

Deuxième partie

v2 Labo-Unix - http://www.labo-unix.net
2001-2002

1
La programmation réseau. Deuxième partie

Table des matières


Droits de ce document 3

Introduction 4

1 Des applications compatibles IPv4 et IPv6 4


1.1 La fonction qui simplifie tout . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 Les macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.3 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2 Changer divers paramètres des sockets : setsockopt() et getsockopt() ; 9


2.1 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3 Les flags des fonctions send() et recv() 11

4 Manipuler les descripteur 11


4.1 Entrées/sorties non-bloquantes . . . . . . . . . . . . . . . . . . . . . . . 12
4.2 Entrées/sorties avec SIGIO . . . . . . . . . . . . . . . . . . . . . . . . . 12

5 Les RAW sockets 13


5.1 Manipuler les périphériques . . . . . . . . . . . . . . . . . . . . . . . . 14

6 La bibliothèque PCAP (Paquet CAPture) 16


6.1 Selectionner un périphérique . . . . . . . . . . . . . . . . . . . . . . . . 16
6.2 Obtenir des informations sur le périphérique . . . . . . . . . . . . . . . . 16
6.3 Capturer les paquets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
6.4 Utiliser des filtres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
6.5 Gestion des erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
6.6 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

7 Fin 19

8 ANNEXE 19
8.1 Les structures utilsées . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

9 GNU Free Documentation License 22


9.1 Applicability and Definitions . . . . . . . . . . . . . . . . . . . . . . . . 22
9.2 Verbatim Copying . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
9.3 Copying in Quantity . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
9.4 Modifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
9.5 Combining Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
9.6 Collections of Documents . . . . . . . . . . . . . . . . . . . . . . . . . . 26
9.7 Aggregation With Independent Works . . . . . . . . . . . . . . . . . . . 26
9.8 Translation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
9.9 Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
9.10 Future Revisions of This License . . . . . . . . . . . . . . . . . . . . . . 27

2 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

Références 28

3 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

Droits de ce document
Copyright (c) 2001 labo-unix.org
Permission vous est donnée de copier, distribuer et/ou modifier ce document selon les
termes de la Licence GNU Free Documentation License, Version 1.1 ou ultérieure pu-
bliée par la Free Software Foundation ; avec les sections inaltérables suivantes :
- pas de section inaltèrable

Une copie de cette Licence est incluse dans la section appelée GNU Free Documenta-
tion License de ce document.

4 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

Introduction
Dans la première partie nous avons vu la programmation des sockets, leur utilisation
de base. Même si nous sommes maintenant capables de réaliser de véritables applications
utilisant le réseau, nous n’avons vu qu’une petite partie de ce que l’on peut faire avec des
sockets. Dans ce cours, nous allons voir comment faire des applications qui utilisent
différents protocoles IP (v4 et v6) de manière transparente. Nous allons aussi plonger
un peu plus au cœur des sockets afin de modifier leurs comportements. Nous verrons
également comment faire ses propres trames IP (ou TCP) ainsi qu’un petit aperçu de la
libpcap pour capturer des paquets et les analyser.

1 Des applications compatibles IPv4 et IPv6


Aujourd’hui, la version 4 du protocole IP commence à montrer ses limites. On a donc
décidé de la modifier. IPv6 simplifie beaucoup de choses dans la gestion des paquets :
routage simplifié, paquets pouvant être plus petit, plage d’adresse étendues, facilement
extensible, authentification prévues. . .Mais analyser ce protocole ne rentre pas dans le
cadre de cet article. La plupart des systèmes d’exploitation courant sont maintenant com-
patibles IPv6. Le matériel compatible est également disponible. La migration d’IPv4 vers
IPv6 est prévues mais se fait très lentement. C’est notamment dû au fait que de nom-
breuses applications ne le supportent pas encore.

Evidemment, IPv4 va être encore présent pendant un moment. La meilleure façon de


migrer petit à petit d’IPv4 vers IPv6 est de faire des applications compatibles avec les 2
protocoles. Vous vous doutez bien que ce n’est pas si simple. Beaucoup de structures de
données sont modifiées pour IPv6. En C, dans le cours précédent, la seule donnée liée au
protocole que nous utilisions était la structure sockaddr.

Les structures de données sont différentes pour IPv4 et IPv6. Ainsi si pour les sockets
IPv4 nous utilisions la structure sockaddr in pour stocker l’adresse et le service (port), il
existe la structure sockaddr in6 qui est l’équivalent pour IPv6. Ce sont deux structures
similaires mais incompatibles.

Voici les deux structures :


struct sockaddr_in6 {
u_int8_t sin6_len; /* length of this struct(sa_family_t)*/
u_int8_t sin6_family; /* AF_INET6 (sa_family_t) */
u_int16_t sin6_port; /* Transport layer port # (in_port_t)*/
u_int32_t sin6_flowinfo; /* IP6 flow information */
struct in6_addr sin6_addr; /* IP6 address */
u_int32_t sin6_scope_id; /* scope zone index */
};

struct sockaddr_in {
u_char sin_len;
u_char sin_family; /* la famille du protocole */
u_short sin_port; /* le port au format reseau */
struct in_addr sin_addr; /* l’adresse IP au format reseau */

5 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

char sin_zero[8];
};
On voit aisément que les deux structures n’ont pas du tout la même taille. Ce qui
pose un réel problème. En effet, si nous voulons manipuler à la fois les sockets IPv4
ET IPv6, nous devons donc manipuler indépendamment des structures sockaddr in et
sockaddr in6. Dans ce cas, soit nous dupliquons le code afin d’avoir une partie traitant
les sockets IPv4 et une autre les sockets IPv6 (ce qui est très lourd) soit nous nous basons
sur quelques règles simples du langage C :
– Les structures doivent subir les mêmes règles d’alignement en mémoire.
– Le premier champ doit toujours être aligné au début de cette structure.
– Des bytes de séparation peuvent par contre être introduits par le compilateur entre
deux champs afin d’obtenir un alignement correcte pour le type concerné.
– L’ordre des éléments dans une structure doit être conservé.
– Deux valeurs du même type ont toujours les mêmes règles d’alignement et la même
taille.
Pour respecter tout cela on utilise la structure sockaddr storage. Si les structures so-
ckaddr in et sockaddr in6 sont incompatibles, il est toutefois envisageable de créer une
structure plus longue dans laquelle il sera possible de copier aussi bien des objets de
type struct sockaddr in que de type struct sockaddr in6. Il serait même judicieux de
prévoir de l’espace supplémentaire pour de futures versions pouvant nécessiter des struc-
tures plus longues (IPv7 ?). Cette longue structure se nomme sockaddr storage.
Elle est censée être capable de stocker n’importe quelle adresse et n’importe quel iden-
tifiant de service, pour n’importe quel protocole supporté par le système d’exploitation
(IPv4, IPv6, X25, . . .).

L’intérêt d’avoir une structure plus (’trop’) grande est que si nous avons réservé as-
sez d’espace pour un objet de type struct sockaddr storage, nous pouvons copier à
ce même endroit une structure sockaddr in ou une structure sockaddr in6. Le tout est
que sizeof(struct sockaddr in) < sizeof(struct sockaddr storage) et que sizeof(struct so-
ckaddr in6) < sizeof(struct sockaddr storage) soient vérifiés.

Si une application utilise la structure générique sockaddr storage au lieu de sockaddr in,
il est garanti qu’elle saura manipuler simultanément des adresses IPv4 et IPv6. Cepen-
dant, lorsque l’on a un objet de type struct sockaddr storage, il peut être très utile de
savoir quelle structure y a réellement été recopiée. Si l’on souhaite connaı̂tre le numéro
de port, par exemple, il faudra lire le champ sin port en IPv4 et le champ sin6 port en
IPv6. On ne peut pas prendre l’un pour l’autre, car ces données peuvent être stockées à
des endroits différents en mémoire, et c’est sans évoquer les adresses IP dont le format est
totalement incompatible. La solution est très simple : ajouter au début de chaque structure
le même champ, dont le rôle sera de contenir une valeur caractéristique du protocole. Ce
champ se nomme ss family (ou ss family sous linux) .

Cette composition des structures répond parfaitement à tous nos besoins. Il est pos-
sible de stocker des objets de type struct sockaddr in ou struct sockaddr in6 dans une

6 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

structure sockaddr storage. Et lorsque l’on est en présence d’un objet de ce type, nous
pouvons en connaı̂tre le protocole exact.

1.1 La fonction qui simplifie tout


Pour résoudre les noms avec IPv6, il existe toujours les fonctions gethostbyname(),
getservbyname() mais il existe une fonction (basée sur les précédentes) beaucoup plus
générique : getadddrinfo().

Celle-ci va se baser sur des indices pour essayer de remplir une structure sockaddr storage.
Elle se charge des nom de machines et de services, que ce soit IPv4 ou IPv6.
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *nodename, const char *servname,


const struct addrinfo *hints, struct addrinfo **res);
nodename est une chaı̂ne de caractère contenant le nom de la machine (”www.labo-
unix.org”) ou son adresse IP (”212.198.130.1” ou ” : :1”). Il peut être NULL si on ne
souhaite pas résoudre l’IP.
servname est une chaı̂ne de caractère contenant le nom du service (”pop3”) ou bien
son numéro de port (”110”). Il peut lui aussi être NULL si on ne s’intéresse pas à la
résolution du service.
hints est une structure contenant des indices qui vont modifier notre recherche.
res est l’emplacement où seront placés les résultats.
le type struct addrinfo :
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
size_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for nodename */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
Hints va nous permettre de donner quelques indices sur le résultat à obtenir :
– ai flags : 0 ou une combinaison logique des valeurs représentées par les macros
suivantes : AI PASSIVE, AI CANONNAME et AI NUMERICHOST.
– ai family : indique la famille d’adresse : AF INET, AF INET6 ou AF UNSPEC.
– ai socktype : le type de connexion : SOCK DGRAM ou SOCK STREAM.
– ai protocol : IPPROTO IP, IPPROTO ICMP, IPPROTO TCP ...etc.
– ai addrlen, ai addr, ai canonname et ai next sont à laisser vides.
Le résultat sera stocké à l’adresse écrite dans res. Il s’agit aussi d’une structure addrinfo
mais les derniers champs seront remplis :
– ai addrlen : la taille de la structure (sizeof(struct sockaddr in) pour IPv4 et si-
zeof(struct sockaddr in6) pour IPv6).
– ai addr pointe sur une structure sockaddr in ou sockaddr in6.

7 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

– Si AI CANONNAME a été spécifié, ai canonname contient le nom de machine


qui a été résolu.
Et comme il peut y avoir plusieur réponse, il s’agit d’une liste chaı̂née. ai next pointe sur
la structure suivante, et vaut NULL lorsqu’on est en fin de liste.

Si getaddrinfo() renvoie une valeur non nulle, c’est qu’il y a eu une erreur. Celle-ci
peut être obtenue avec la fonction gai strerror().
Enfin, il ne faut pas oublier de libérer la place en mémoire prise par la liste chaı̂née res.
Ceci s’effectue grâce à la fonction freeaddrinfo().

1.2 Les macros


Pour le type d’adresse :
– AF INET permet de spécifier une adresse IPv4.
– AF INET6 permet de spécifier une adresse IPv6.
– AF UNSPEC permet de ne pas spécifier de type d’adresse.
Pour IPv4 :
– INADDR ANY représente n’importe quelle adresse.
– INADDR NONE représente une adresse non-assignée ou inconnue.
– INADDR LOOPBACK représente une adresse locale de la machine (127.x.x.x).
– INADDR BROADCAST représente toutes les machines susceptibles de répondre à
une diffusion.
– IN MULTICAST() renvoie un valeur non nulle si l’adresse appartient à la classe
multicast.
– IN BADCLASS() renvoie une valeur non nulle si l’adresse fait partie d’une classe
reservée.
– INET ADDRSTRLEN le nombre maximal de caractères nécessaires pour noter une
adresse IPv4.
Pour IPv6 :
– IN6 IS ADDR MULTICAST renvoie une valeur non nulle si l’adresse représente
un groupe de machines.
– IN6 IS ADDR UNSPECIFIED renvoie une valeur non nulle si l’adresse n’est pas
valide.
– INET6 ADDRSTRLEN la taille maximale d’une chaı̂ne contenant la notation chiffrée
d’une adresse IPv6.
Il y a aussi une structure globale de 16 octets (in6addr any) représentant n’importe
quelle adresse IPv6. Ainsi qu’une structure globale in6addr loopback contenant l’adresse
locale ( : :1).

1.3 Exemple
/* $Id: Socket2.tex,v 1.5 2002/02/01 00:11:02 danseurfou Exp $
*
* Il s’agit du meme client que dans la premiere partie
* du cours mais celui-ci utilise getaddrinfo() au lieu de
* gethostbyname()
*/

8 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define PORT "1543"


#define HOST "localhost"

int main(int ac, char *av[])


{
int code;
struct addrinfo indice, *resultat, *scan;
char *port = PORT;
char *hostname = HOST;
char *requete = "HEAD / HTTP/1.0\n\n";
char buffer[1024];

switch(ac) {
case 3:
port = av[2];
case 2:
hostname = av[1];
case 1:
break;
default:
printf("Usage:\n\t%s [hostname [port]]\n\n", av[0]);
return -1;
}

bzero(&indice, sizeof indice);

/* on veut se connecter en TCP que ce soit IPv4 ou IPv6 */


indice.ai_family = AF_UNSPEC;
indice.ai_socktype = SOCK_STREAM;
indice.ai_protocol = IPPROTO_IP;

if((code = getaddrinfo(hostname, port, &indice, &resultat))) {


printf("*** erreur: %s [%s]\n", hostname, gai_strerror(code));
return -1;
}

scan = resultat;

/* on va essayer tous les resultats jusqu’a ce qu’il y en ait un qui marche */


while(scan != NULL) {
int sd;

if((sd = socket(scan->ai_family, scan->ai_socktype, scan->ai_protocol)) < 0) {


perror("socket");
goto suivant;
}

if(connect(sd, scan->ai_addr, scan->ai_addrlen) < 0) {

9 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

perror("connect");
} else {
send(sd, requete, strlen(requete));
while(recv(sd, buffer, 1024, 0) > 0)
printf(buffer);
close(sd);
break;
}
close(sd);
suivant:
scan = scan->ai_next;
}

freeaddrinfo(resultat);

return 0;
}

2 Changer divers paramètres des sockets : setsockopt() et getsockopt() ;


Ces deux fonctions permettent de gérer certaines options au niveau des protocoles ou
bien des sockets.
Par exemple, il peut être utile de changer le timeout sur un socket. En effet, lorsque nous
utilisons les fonctions connect() ou recv(), celles-ci se bloquent jusqu’à ce que l’opération
désirée se réalise. Ca peut-être très génant.

On peut aussi par exemple vouloir changer la taille du buffer d’émission ou de réception,
pour des applications spécifiques. Mais voyons :
#include <sys/types.h>
#include <sys/socket.h>

int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen);
s est le socket sur lequel opérer.

level permet de situer le niveau de nos opérations :


– sur le sockets (SOL SOCKET)
– sur le protocole IP (IPPROTO IP)
– sur le protocole TCP (IPPROTO TCP)
– sur le protocole UDP (IPPROTO UDP)
– . . .(pour les protocoles, voir < netinet/in.h >
optname donne le nom du paramètre à changer.

optval donnera la valeur du paramètre (1 => activé, 0 => désactivé ou un chiffre


lorsqu’il s’agit d’un tampon ou d’un timeout) et optlen la taille de la valeur (qui est
généralement un int).
Ces deux fonctions renvoient -1 si il y eu une erreur, 0 sinon.

Les noms de paramètres au niveau du socket (donc avec level == SOL SOCKET) :
– SO DEBUG : afficher des informations de debuggage

10 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

– SO ACCEPTCONN : le socket est un socket d’écoute (c’est la cas après un listen())


– SO REUSEADDR : on autorise la réutilisation de l’adresse locale
– SO REUSEPORT : on autorise la réutilisation de l’adresse et du port
– SO KEEPALIVE : on garde les connexions ouvertes
– SO DONTROUTE : on n’utilise pas le routage standard
– SO BROADCAST : on peut émettre en broadcast
– SO USELOOPBACK : bypass hardware when possible ( ? ? pas testé)
– SO LINGER : on attend avant de fermer le socket si il reste des données à envoyer
– SO OOBINLINE : les données prioritaires sont ajoutées au trafic normal
– SO ACCEPTFILTER : on utilise des filtres (pas dispo partout)
– SO SNDBUF : taille du buffer d’envoie
– SO RCVBUF : taille du buffer de réception
– SO SNDLOWAT : quelle taille minimum est nécessaire avant d’envoyer
– SO RCVLOWAT : quelle taille minimum est nécessaire avant que read() retourne
– SO SNDTIMEO : timeout d’envoie
– SO RCVTIMEO : timeout de réception
– SO ERROR : on récupere la dernière erreur
– SO TYPE : on récupère le type du socket
On peut aussi modifier de nombreux paramètres au niveau IP ou bien TCP. Nous
verrons ça peut-être plus tard. C’est déjà utile de savoir qu’ils existent. Les voilà en vrac :
– IP OPTIONS : buf/ip opts ; set/get IP options */
– IP HDRINCL : int ; header is included with data */
– IP TOS : int ; IP type of service and preced. */
– IP TTL : int ; IP time to live */
– IP RECVOPTS : bool ; receive all IP opts w/dgram */
– IP RECVRETOPTS : bool ; receive IP opts for response */
– IP RECVDSTADDR : bool ; receive IP dst addr w/dgram */
– IP RETOPTS : ip opts ; set/get IP options */
– IP MULTICAST IF : u char ; set/get IP multicast i/f */
– IP MULTICAST TTL : u char ; set/get IP multicast ttl */
– IP MULTICAST LOOP : u char ; set/get IP multicast loopback */
– IP ADD MEMBERSHIP : ip mreq ; add an IP group membership */
– IP DROP MEMBERSHIP : ip mreq ; drop an IP group membership */
– IP MULTICAST VIF : set/get IP mcast virt. iface */
– IP RSVP ON : enable RSVP in kernel */
– IP RSVP OFF : disable RSVP in kernel */
– IP RSVP VIF ON : set RSVP per-vif socket */
– IP RSVP VIF OFF : unset RSVP per-vif socket */
– IP PORTRANGE : int ; range to choose for unspec port */
– IP RECVIF : bool ; receive reception if w/dgram */
– TCP NODELAY don’t delay send to coalesce packets */
– TCP MAXSEG : set maximum segment size */
– TCP NOPUSH : don’t push last block of write */
– TCP NOOPT : don’t use TCP options */

11 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

2.1 Exemple
Lorsque l’on ferme un socket, si des données ne sont pas envoyées, elles sont per-
dues. Il y a une option très pratique avec setsockopt() qui permet d’attendre l’envoie des
données encore présentes avant de fermer le socket : SO LINGER. Nous allons faire une
fonction qui modifie ce paramètres sur notre socket.
/* sock est notre socket */
int set_option(int sock)
{
int on = 1;
if(setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&on, sizeof on)) {
perror("setsockopt");
return -1;
} else
return 0;
}

3 Les flags des fonctions send() et recv()


Dans la première partie du cours, nous avons vu que send() et recv() avait un quatrième
paramètre : flags. La majeure partie de ces options peuvent être changées au niveau du
socket avec setsockopt(). Mais on peut aussi les gérer au cas par cas lorsque l’on envoie
ou récéptionne des données.
Avec send() :
– MSG OOB : ce sont des données prioritaires
– MSG DONTROUTE : on utilise directement les interfaces sans se soucier des routes
– MSG EOR : c’est la fin des données (utilisé seulement avec certains protocoles)
– MSG EOF : c’est la fin de la transaction (utilisé seulement avec certains protocoles)
Pour les deux :
– MSG TRUNC : data discarded before delivery ( ? ? ? pas testé)
– MSG CTRUNC : control data lost before delivery ( ? ? ? pas testé)
– MSG DONTWAIT : le message sera non-bloquant (voir plus loin)
avec recv() :
– MSG OOB : on ne lit que les données prioritaires
– MSG PEEK : lit les données sans les retirer du buffer. Une nouvelle lecture renverra
la même chose
– MSG WAITALL : attend jusqu’à la fin de la requête ou bien jusqu’à une erreur

4 Manipuler les descripteur


#include <fcntl.h>

int fcntl(int fd, int cmd, ...);

La fonction fcntl() (file control) permet de manipuler les descripteurs de fichier (donc
aussi de socket).
fd est le desripteur de fichier.

cmd est la commande. En fonction de cette commande, fcntl() peut prendre un troisième
paramètre : int arg.

12 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

les différentes commandes qui nous interessent :


– F GETFL : obtenir les flags du descripteur
– F SETFL : changer les flags du descripteur
– F GETOWN : obtenir les PID qui reçoive SIGIO
– F SETOWN : changer les PID qui reçoive SIGIO
Les flags qui nous interessent pour F GETFL et F SETFL sont les suivants :
– O ASYNC : recevoir SIGIO lorsqu’il y a des données
– O FSYNC : synchroniser les écritures (n’utilise pas le cache du disque)
– O NONBLOCK : entrées/sorties non-bloquantes

4.1 Entrées/sorties non-bloquantes


Nous avons vu que connect() se bloque lorsqu’il n’arrive pas a se connecter ou qu’il
attends que la connexion s’établisse et que recv() attends d’avoir des données à lire avant
de les retourner. On peut diminuer le timeout du socket mais ça n’empechera pas un
certain temps d’attente. D’autant plus que si on diminue trop le timeout du socket, plus
aucune connexion ne sera possible (il faut de quelques millisecondes à quelques secondes
pour que les paquets arrivent à une machine). Il existe une autre solution pour éviter cela :
faire des sockets non-bloquants.

Pour ceci, il faut d’abord créer le socket. Ensuite, grâce à fcntl(), on peut rendre le
socket non-bloquant. A partir de là, si on utilise la fonction connect(), elle ne se bloquera
pas en attendant l’établissement de la connexion. Elle va renvoyer une erreur (si les pa-
ramètres sont corrects) : EINPROGRESS qui signifie que l’opération est en cours mais
pas terminée. Pour savoir si la connexion s’est correctement passée, on pourra utiliser
les fonctions poll() ou select() en testant l’écriture sur le socket. Evidemment, on peut
toujours bloquer poll() et select() avec un timeout mais on peut aussi leur demander de
tester directement sans se bloquer (avec un timeout de 0).

Ensuite, si l’on utilise read() et que la fonction renvoie l’erreur EAGAIN, c’est qu’il
n’y a rien à lire sur le socket. Idem pour send(). On peut ainsi ne pas bloquer l’appli-
cation sur une opération de lecture ou d’écriture. Si des données sont présentes, read()
fonctionne normalement (idem pour send()).

Pour passer le socket en mode non-bloquant, on procède ainsi :


/* sock est notre socket */
int set_nonblock(int sock)
{
int flags = fcntl(sock, F_GETFL);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
/* je ne gere pas les codes de retour et il peut y avoir une erreur */
}

4.2 Entrées/sorties avec SIGIO


Une autre façon de ne pas bloquer l’application sur une opération de lecture ou d’écriture
est d’utiliser les signaux. Le principe est de créer un socket, puis de demander au système

13 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

d’envoyer le signal SIGIO à l’application lorsqu’il y aura des données à lire dessus. Dans
notre programme, nous redirigerons le signal SIGIO vers la fonction de lecture du socket.

Pour rediriger le signal SIGIO, nous utiliserons la fonction signal() :


#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);


Ne fuyez pas devant le prototype barbare. Le premier paramètre est tout simplement le
numéro du signal (définit par SIGINT, SIGUSR1, SIGHUP...) et le deuxième paramètre
est un pointeur sur une fonction qui ne renvoie rien et prend un int comme paramètre.

Voyons un exemple :
int read_sock(int signal)
{
/* c’est notre fonction de lecture que je ne detaillerai pas.
* Sachez seulement que le paramètre n’est pas le socket mais
* le signal (ici SIGIO). Et n’oubliez pas que l’on utilise pas
* n’importe quelle fonction pendant la gestion d’un signal...
*/
}
/* Comme d’hab, sock est notre socket */
int set_sigio(int sock)
{
int flags = fcntl(sock, F_GETFL);
signal(SIGIO, read_sock);
/* il ne faut pas oublier de spécifier quel PID recevra
* le signal: */
fcntl(sock, F_SETOWN, getpid());
/* ensuite on demande de recevoir le SIGIO sur ce descripteur: */
fcntl(sock, F_SETFL, flags | O_ASYNC);
/* je n’ai pas gerer le eventuelles erreurs... */
}
Grâce à ces quelques lignes, le programme recevra SIGIO dès qu’il y aura des données
à lire sur le socket. Si vous répétez l’opération avec plusieurs sockets, ce sera à vous de
les différencier (grâce à poll() ou select()) pour savoir lequel attends des données qui sont
la cause du signal.

5 Les RAW sockets


Avec UNIX (et maintenant aussi avec WinXP) on peut créer soit-même les paquets
qui vont transiter sur le réseau. C’est à dire que l’on va nous-même créer les en-têtes TCP
ou UDP ainsi que IP. Pour cela, il faut créer ce que l’on appelle un RAW socket :
socket(PF_INET, SOCK_RAW, IPPROTO_IP);

On peut remplacer IPPROTO IP par un autre protocole. Si vous voulez créer vous
même les en-têtes IP, alors il faut le spécifier au système grâce à :
#include <netinet/ip.h>
int on = 1;
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (void *)&on, sizeof on);

14 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

A partir de là, vous pouvez envoyer les paquets grâce à la fonction sendto(). Le
deuxième paramètre va être votre paquet TCP/IP (ou UDP/IP ou ICMP). Vous devez
donc allouer une zone mémoire qui comprendra l’en-tête IP puis l’en-tête TCP puis les
données. Mais il vous faut correctement remplir les en-têtes TCP et IP dont voici les
structures (sur un FreeBSD) :
struct ip {
u_int ip_hl:4, /* header length */
ip_v:4; /* version */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
};

struct tcphdr {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
u_int th_x2:4, /* (unused) */
th_off:4; /* data offset */
u_char th_flags;
u_short th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};
Remarque : avec linux, le nom de la structure pour les en-tête IP est : struct iphdr.

Vous pouvez aussi très bien créer des paquets ICMP ou UDP/IP ou tout autre protocole
existant, voir même d’un protocole n’existant pas. Encore une fois le but de ce cours
n’est pas de vous expliquer le fonctionnement de TCP/IP. Nous ne rentrerons pas dans
les détails. Vous avez toutes les informations nécessaire pour faire vos paquets.

Si on peut les créer, c’est qu’on peut aussi les analyser. Et c’est beaucoup plus interes-
sant. Avec un RAW socket, on peut obtenir les paquets IP, les décortiquer, voir comment
cela fonctionne. . .et faire son propre analyseur de réseau.
Mais si vous voulez regarder passer tous les paquets qui passent sur le réseau (en tout
cas sur votre cable réseau), il va falloir le demander à la carte réseau.

5.1 Manipuler les périphériques


#include <sys/ioctl.h>

int ioctl(int d, unsigned long request, ...);

Il s’agit d’une fonction permettant de manipuler les paramètres d’un périphérique. On


s’approche déjà nettement plus du matériel. Nous n’allons voir cette fonction que pour

15 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

changer quelques paramètres de la carte réseau, notamment le mode promiscuous. Celui-


ci permet à la carte de capturer tous les paquets se trouvant sur le brin. Autrement vous
n’auriez que les paquets qui vous sont destinés (beaucoup moins drôle).

Le premier paramètre de la fonction ioctl() est un descripteur ouvert. Dans notre cas
un socket, mais celà pourrait aussi être un périphérique (/dev/dsp par exemple pour mo-
difier les paramètres de la carte son). Le deuxième paramètre est le nom du paramètre à
modifier. Pour les sockets, ils sont dans le fichier < sys/sockio.h >. Mais si vous vou-
lez toucher au frame buffer, il va falloir regarder dans < sys/f bio.h > par exemple
(et pour un FreeBSD). Ou bien < sys/soundcard.h > pour la carte son. Tous les
périphériques peuvent être accédés grâce à cette fonction. Le troisième paramètre de
ioctl() est spécifique au périphérique. Dans notre cas, il s’agit d’une structure ifreq (do-
cumentée en annexe).

Cette structure contient différents paramètres de la carte réseau. On va d’abord récuperer


les paramètres courants, puis les modifier. Pour passer en mode promiscuous, il vous faut
les droits administrateur (root). Un exemple veut mieux qu’un long discours :
/* interface est le nom de la carte réseau:
* eth0, eth1... pour linux.
* ed0, xl0... ou autre pour BSD
*/
#include <sys/sockio.h>
#include <sys/ioctl.h>
#include <net/if.h>
int set_promisc(char *interface, int sock)
{
struct ifreq ifr;

bzero(&ifr, sizeof ifr);

strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));

/* on récupere les paramètres courants */


if(ioctl(sd, SIOCGIFFLAGS, &ifr) == -1) {
perror("ioctl get");
return -1;
}

/* on modifie les flags pour que la carte soit en mode promiscuous */


ifr.ifr_flags |= IFF_PROMISC;

/* et on change les paramètres */


if(ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) {
perror("ioctl set promisc");
return -1;
} else
puts("promiscuous mode enable for %s\n", interface);
}

Maintenant vous pouvez analyser tous les paquets passant sur votre brin : vous pouvez
les recevoir avec la fonction recvfrom() et regarder comment sont constituées les trames.
N’oubliez pas de remettre la carte en mode normal à la fin du programme.

16 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

6 La bibliothèque PCAP (Paquet CAPture)


Même si la méthode précédente marche très bien, elle n’est pas portable sur tous
les unix. Surtout que certains implémentent des facilités pour capturer les paquets. Pour
simplifier l’écriture d’utilitaires d’analyse réseau, il existe une bibliothèque : la libpcap.
Elle permet même de capturer les paquets ethernet...

Voici les fonctions les plus utiles. N’oubliez pas d’inclure < pcap.h > et de lier avec
la libpcap (-lpcap) lors de la compilation.

6.1 Selectionner un périphérique


char *pcap_lookupdev(char *errbuf);

Va nous renvoyer le nom d’un périphérique que l’on peut scruter. errbuf est un tampon
de taille minimum PCAP ERRBUF SIZE.
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms,
char *ebuf);

Permet d’ouvrir un périphérique pour capturer les paquets. device est ce que nous a
renvoyé la fonction précédentes. snaplen est la taille maximum des paquets que l’on veut.
promisc détermine si la carte doit être en mode promiscuous. to ms est le temps d’attente
d’un paquet en millisecondes avant de renvoyer une erreur. ebuf est le même tampon
d’erreur que précédemment. La structure pcap t renvoyée sera passée en paramètre à
toutes les fonctions qui vont suivre. Renvoie NULL en cas d’erreur.

6.2 Obtenir des informations sur le périphérique


int pcap_lookupnet(char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp,
char *errbuf);

Permet d’obtenir le masque de réseau et l’adresse de réseau de l’interface. Retourne


-1 si erreur (et errbuf contient le message d’erreur).
int pcap_datalink(pcap_t *p);

Renvoie le type de média utilisé.


int pcap_stats(pcap_t *p, struct pcap_stat *ps);

Permet d’obtenir des statistiques sur l’interface.

6.3 Capturer les paquets


int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user);

Collecte et passe les paquets à la fonction callback. La fonction callback va recevoir


trois paramètres : le premier est user, le deuxième est de type u char * et contient les
données du paquet, le dernier est de type pcap pkthdr et contient diverses informations
sur le paquet (structure définie dans l’annexe). cnt est la nombre maximum de paquets à
traiter. La fonction s’arrêtera s’il y a une erreur ou si elle arrive au nombre maximum de
paquets à traiter.
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user);

17 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

Fait exactement la même chose mais ne s’arrêtera que lorsqu’elle arrivera au nombre
de paquets à traiter (paramètre cnt, si -1 alors elle boucle à l’infini).
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h);

Renvoie seulement le prochain paquet. Les données sont renvoyées et la structure


pcap pkthdr est remplie.

6.4 Utiliser des filtres


Pour savoir comment utiliser les filtres, reportez vous à BPF (Berkeley Paquet Filter).
int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize,
bpf_u_int32 netmask);

Permet de compiler la règle str dans le filtre fp. optimize contrôle si une optimisation
sur le résultat doit être effectuée. netmask est le masque de sous-réseau de l’interface.
int pcap_setfilter(pcap_t *p, struct bpf_program *fp);

Permet de spécifier quels filtres seront utilisés pour capturer les paquets.
void pcap_freecode(struct bpf_program *);

Permet de libérer la mémoire utilisée par une structure bpf program.

6.5 Gestion des erreurs


void pcap_perror(pcap_t *p, char *prefix) : Permet d’afficher le mes-
sage d’erreur.
char *pcap_geterr(pcap_t *p) : Permet d’obtenir le message d’erreur.
char *pcap_strerror(int error) : C’est la même chose que la fonction de la
libc strerror().

6.6 Exemple
/* Un petit exemple de capture de paquet
*/

#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>

int main(int argc, char **argv)


{
int i;
char *dev;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* descr;
const u_char *packet;
struct pcap_pkthdr hdr;
struct ether_header *eptr;
u_char *ptr;

18 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

/* trouver un périphérique */
if((dev = pcap_lookupdev(errbuf)) == NULL) {
printf("%s\n",errbuf);
return -1;
}

printf("peripherique: %s\n",dev);

if((descr = pcap_open_live(dev, BUFSIZ, 0, 0, errbuf)) == NULL) {


printf("pcap_open_live(): %s\n", errbuf);
return -1;
}

/* on va recupere le prochain paquet */

if((packet = pcap_next(descr, &hdr)) == NULL) {


puts("Impossible de recuperer un paquet");
return -1;
}

printf("Capture paquet de taille %d\n", hdr.len);


printf("Recu a: %s\n",ctime((const time_t*)&hdr.ts.tv_sec));

eptr = (struct ether_header *)packet;

/* quel type de paquet on a ? */


if (ntohs(eptr->ether_type) == ETHERTYPE_IP) {
printf("Type 0x%x => IP\n", ntohs(eptr->ether_type));
} else if (ntohs(eptr->ether_type) == ETHERTYPE_ARP) {
printf("Type 0x%x => ARP\n", ntohs(eptr->ether_type));
} else {
printf("Type 0x%x not IP", ntohs(eptr->ether_type));
return -1;
}

ptr = eptr->ether_dhost;
i = ETHER_ADDR_LEN;
printf(" Destination Adresse: ");
do{
printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
} while(--i > 0);
puts("");

ptr = eptr->ether_shost;
i = ETHER_ADDR_LEN;
printf(" Source Adresse: ");
do{
printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
} while(--i > 0);
puts("");

return 0;
}

19 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

7 Fin
J’espère que ces quelques informations vous aurons interéssées. Et que maintenant
vous êtes capables de faire des applications réseau conséquentes. La programmation C
n’est pas évidente mais lorsque l’on connait suffisamment le domaine, c’est un réel plai-
sir car il n’y a que très peu de limites. Seulement votre imagination. . .

Pour finir un grand merci à Jedi (www.jedi.claranet.fr) pour la partie IPv4/IPv6.

8 ANNEXE
8.1 Les structures utilsées
Attention il s’agit des structures extrait d’un FreeBSD. Sur linux, il se peut qu’il y ait
des changements.

struct sockaddr_in6 {
u_int8_t sin6_len; /* length of this struct(sa_family_t)*/
u_int8_t sin6_family; /* AF_INET6 (sa_family_t) */
u_int16_t sin6_port; /* Transport layer port # (in_port_t)*/
u_int32_t sin6_flowinfo; /* IP6 flow information */
struct in6_addr sin6_addr; /* IP6 address */
u_int32_t sin6_scope_id; /* scope zone index */
};

struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
size_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for nodename */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};

struct ip {
u_int ip_hl:4, /* header length */
ip_v:4; /* version */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
};

struct tcphdr {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */

20 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

tcp_seq th_seq; /* sequence number */


tcp_seq th_ack; /* acknowledgement number */
u_int th_x2:4, /* (unused) */
th_off:4; /* data offset */
u_char th_flags;
u_short th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};

struct udphdr {
u_short uh_sport; /* source port */
u_short uh_dport; /* destination port */
u_short uh_ulen; /* udp length */
u_short uh_sum; /* udp checksum */
};

struct icmp {
u_char icmp_type; /* type of message, see below */
u_char icmp_code; /* type sub code */
u_short icmp_cksum; /* ones complement cksum of struct */
union {
u_char ih_pptr; /* ICMP_PARAMPROB */
struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
struct ih_idseq {
n_short icd_id;
n_short icd_seq;
} ih_idseq;
int ih_void;
struct ih_pmtu {
n_short ipm_void;
n_short ipm_nextmtu;
} ih_pmtu;

struct ih_rtradv {
u_char irt_num_addrs;
u_char irt_wpa;
u_int16_t irt_lifetime;
} ih_rtradv;
} icmp_hun;
#define icmp_pptr icmp_hun.ih_pptr
#define icmp_gwaddr icmp_hun.ih_gwaddr
#define icmp_id icmp_hun.ih_idseq.icd_id
#define icmp_seq icmp_hun.ih_idseq.icd_seq
#define icmp_void icmp_hun.ih_void
#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
union {
struct id_ts {
n_time its_otime;
n_time its_rtime;
n_time its_ttime;
} id_ts;

21 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

struct id_ip {
struct ip idi_ip;
} id_ip;
struct icmp_ra_addr id_radv;
u_int32_t id_mask;
char id_data[1];
} icmp_dun;
#define icmp_otime icmp_dun.id_ts.its_otime
#define icmp_rtime icmp_dun.id_ts.its_rtime
#define icmp_ttime icmp_dun.id_ts.its_ttime
#define icmp_ip icmp_dun.id_ip.idi_ip
#define icmp_radv icmp_dun.id_radv
#define icmp_mask icmp_dun.id_mask
#define icmp_data icmp_dun.id_data
};

struct ifreq {
char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
short ifru_flags[2];
int ifru_metric;
int ifru_mtu;
int ifru_phys;
int ifru_media;
caddr_t ifru_data;
int ifru_cap[2];
} ifr_ifru;
#define ifr_addr ifr_ifru.ifru_addr /* address */
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
#define ifr_flags ifr_ifru.ifru_flags[0] /* flags */
#define ifr_prevflags ifr_ifru.ifru_flags[1] /* flags */
#define ifr_metric ifr_ifru.ifru_metric /* metric */
#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
#define ifr_phys ifr_ifru.ifru_phys /* physical wire */
#define ifr_media ifr_ifru.ifru_media /* physical media */
#define ifr_data ifr_ifru.ifru_data /* for use by interface */
#define ifr_reqcap ifr_ifru.ifru_cap[0] /* requested capabilities */
#define ifr_curcap ifr_ifru.ifru_cap[1] /* current capabilities */
};

struct pcap_pkthdr {
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* lebgth this packet (off wire) */
}

22 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

9 GNU Free Documentation License


Version 1.1, March 2000

Copyright copyright 2000 Free Software Foundation, Inc.


59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies of this license document,
but changing it is not allowed.

Preamble
The purpose of this License is to make a manual, textbook, or other written document
“free” in the sense of freedom : to assure everyone the effective freedom to copy and
redistribute it, with or without modifying it, either commercially or noncommercially.
Secondarily, this License preserves for the author and publisher a way to get credit for
their work, while not being considered responsible for modifications made by others.
This License is a kind of “copyleft”, which means that derivative works of the docu-
ment must themselves be free in the same sense. It complements the GNU General Public
License, which is a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software, because
free software needs free documentation : a free program should come with manuals provi-
ding the same freedoms that the software does. But this License is not limited to software
manuals ; it can be used for any textual work, regardless of subject matter or whether it
is published as a printed book. We recommend this License principally for works whose
purpose is instruction or reference.

9.1 Applicability and Definitions


This License applies to any manual or other work that contains a notice placed by
the copyright holder saying it can be distributed under the terms of this License. The
“Document”, below, refers to any such manual or work. Any member of the public is a
licensee, and is addressed as “you”.
A “Modified Version” of the Document means any work containing the Document or a
portion of it, either copied verbatim, or with modifications and/or translated into another
language.
A “Secondary Section” is a named appendix or a front-matter section of the Document
that deals exclusively with the relationship of the publishers or authors of the Document to
the Document’s overall subject (or to related matters) and contains nothing that could fall
directly within that overall subject. (For example, if the Document is in part a textbook of
mathematics, a Secondary Section may not explain any mathematics.) The relationship
could be a matter of historical connection with the subject or with related matters, or of
legal, commercial, philosophical, ethical or political position regarding them.
The “Invariant Sections” are certain Secondary Sections whose titles are designated,
as being those of Invariant Sections, in the notice that says that the Document is released
under this License.

23 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

The “Cover Texts” are certain short passages of text that are listed, as Front-Cover
Texts or Back-Cover Texts, in the notice that says that the Document is released under
this License.
A “Transparent” copy of the Document means a machine-readable copy, represented
in a format whose specification is available to the general public, whose contents can be
viewed and edited directly and straightforwardly with generic text editors or (for images
composed of pixels) generic paint programs or (for drawings) some widely available dra-
wing editor, and that is suitable for input to text formatters or for automatic translation
to a variety of formats suitable for input to text formatters. A copy made in an otherwise
Transparent file format whose markup has been designed to thwart or discourage sub-
sequent modification by readers is not Transparent. A copy that is not “Transparent” is
called “Opaque”.
Examples of suitable formats for Transparent copies include plain ASCII without mar-
kup, Texinfo input format, LATEX input format, SGML or XML using a publicly available
DTD, and standard-conforming simple HTML designed for human modification. Opaque
formats include PostScript, PDF, proprietary formats that can be read and edited only by
proprietary word processors, SGML or XML for which the DTD and/or processing tools
are not generally available, and the machine-generated HTML produced by some word
processors for output purposes only.
The “Title Page” means, for a printed book, the title page itself, plus such following
pages as are needed to hold, legibly, the material this License requires to appear in the title
page. For works in formats which do not have any title page as such, “Title Page” means
the text near the most prominent appearance of the work’s title, preceding the beginning
of the body of the text.

9.2 Verbatim Copying


You may copy and distribute the Document in any medium, either commercially or
noncommercially, provided that this License, the copyright notices, and the license notice
saying this License applies to the Document are reproduced in all copies, and that you
add no other conditions whatsoever to those of this License. You may not use technical
measures to obstruct or control the reading or further copying of the copies you make
or distribute. However, you may accept compensation in exchange for copies. If you
distribute a large enough number of copies you must also follow the conditions in section
3.
You may also lend copies, under the same conditions stated above, and you may pu-
blicly display copies.

9.3 Copying in Quantity


If you publish printed copies of the Document numbering more than 100, and the
Document’s license notice requires Cover Texts, you must enclose the copies in covers
that carry, clearly and legibly, all these Cover Texts : Front-Cover Texts on the front
cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly
identify you as the publisher of these copies. The front cover must present the full title
with all words of the title equally prominent and visible. You may add other material

24 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

on the covers in addition. Copying with changes limited to the covers, as long as they
preserve the title of the Document and satisfy these conditions, can be treated as verbatim
copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you should put
the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest
onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more than 100,
you must either include a machine-readable Transparent copy along with each Opaque
copy, or state in or with each Opaque copy a publicly-accessible computer-network lo-
cation containing a complete Transparent copy of the Document, free of added mate-
rial, which the general network-using public has access to download anonymously at no
charge using public-standard network protocols. If you use the latter option, you must
take reasonably prudent steps, when you begin distribution of Opaque copies in quantity,
to ensure that this Transparent copy will remain thus accessible at the stated location until
at least one year after the last time you distribute an Opaque copy (directly or through
your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document well
before redistributing any large number of copies, to give them a chance to provide you
with an updated version of the Document.

9.4 Modifications
You may copy and distribute a Modified Version of the Document under the conditions
of sections 2 and 3 above, provided that you release the Modified Version under precisely
this License, with the Modified Version filling the role of the Document, thus licensing
distribution and modification of the Modified Version to whoever possesses a copy of it.
In addition, you must do these things in the Modified Version :
– Use in the Title Page (and on the covers, if any) a title distinct from that of the
Document, and from those of previous versions (which should, if there were any,
be listed in the History section of the Document). You may use the same title as a
previous version if the original publisher of that version gives permission.
– List on the Title Page, as authors, one or more persons or entities responsible for
authorship of the modifications in the Modified Version, together with at least five
of the principal authors of the Document (all of its principal authors, if it has less
than five).
– State on the Title page the name of the publisher of the Modified Version, as the
publisher.
– Preserve all the copyright notices of the Document.
– Add an appropriate copyright notice for your modifications adjacent to the other
copyright notices.
– Include, immediately after the copyright notices, a license notice giving the public
permission to use the Modified Version under the terms of this License, in the form
shown in the Addendum below.
– Preserve in that license notice the full lists of Invariant Sections and required Cover
Texts given in the Document’s license notice.
– Include an unaltered copy of this License.

25 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

– Preserve the section entitled “History”, and its title, and add to it an item stating at
least the title, year, new authors, and publisher of the Modified Version as given on
the Title Page. If there is no section entitled “History” in the Document, create one
stating the title, year, authors, and publisher of the Document as given on its Title
Page, then add an item describing the Modified Version as stated in the previous
sentence.
– Preserve the network location, if any, given in the Document for public access to
a Transparent copy of the Document, and likewise the network locations given in
the Document for previous versions it was based on. These may be placed in the
“History” section. You may omit a network location for a work that was published
at least four years before the Document itself, or if the original publisher of the
version it refers to gives permission.
– In any section entitled “Acknowledgements” or “Dedications”, preserve the sec-
tion’s title, and preserve in the section all the substance and tone of each of the
contributor acknowledgements and/or dedications given therein.
– Preserve all the Invariant Sections of the Document, unaltered in their text and in
their titles. Section numbers or the equivalent are not considered part of the section
titles.
– Delete any section entitled “Endorsements”. Such a section may not be included in
the Modified Version.
– Do not retitle any existing section as “Endorsements” or to conflict in title with any
Invariant Section.
If the Modified Version includes new front-matter sections or appendices that qualify
as Secondary Sections and contain no material copied from the Document, you may at
your option designate some or all of these sections as invariant. To do this, add their titles
to the list of Invariant Sections in the Modified Version’s license notice. These titles must
be distinct from any other section titles.
You may add a section entitled “Endorsements”, provided it contains nothing but en-
dorsements of your Modified Version by various parties – for example, statements of peer
review or that the text has been approved by an organization as the authoritative definition
of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage of up
to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified
Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added
by (or through arrangements made by) any one entity. If the Document already includes
a cover text for the same cover, previously added by you or by arrangement made by the
same entity you are acting on behalf of, you may not add another ; but you may replace
the old one, on explicit permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission
to use their names for publicity for or to assert or imply endorsement of any Modified
Version.

9.5 Combining Documents


You may combine the Document with other documents released under this License,
under the terms defined in section 4 above for modified versions, provided that you in-

26 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

clude in the combination all of the Invariant Sections of all of the original documents,
unmodified, and list them all as Invariant Sections of your combined work in its license
notice.
The combined work need only contain one copy of this License, and multiple identical
Invariant Sections may be replaced with a single copy. If there are multiple Invariant
Sections with the same name but different contents, make the title of each such section
unique by adding at the end of it, in parentheses, the name of the original author or
publisher of that section if known, or else a unique number. Make the same adjustment
to the section titles in the list of Invariant Sections in the license notice of the combined
work.
In the combination, you must combine any sections entitled “History” in the various
original documents, forming one section entitled “History” ; likewise combine any sec-
tions entitled “Acknowledgements”, and any sections entitled “Dedications”. You must
delete all sections entitled “Endorsements.”

9.6 Collections of Documents


You may make a collection consisting of the Document and other documents released
under this License, and replace the individual copies of this License in the various docu-
ments with a single copy that is included in the collection, provided that you follow the
rules of this License for verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it indivi-
dually under this License, provided you insert a copy of this License into the extracted
document, and follow this License in all other respects regarding verbatim copying of
that document.

9.7 Aggregation With Independent Works


A compilation of the Document or its derivatives with other separate and independent
documents or works, in or on a volume of a storage or distribution medium, does not as a
whole count as a Modified Version of the Document, provided no compilation copyright
is claimed for the compilation. Such a compilation is called an “aggregate”, and this Li-
cense does not apply to the other self-contained works thus compiled with the Document,
on account of their being thus compiled, if they are not themselves derivative works of
the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the Docu-
ment, then if the Document is less than one quarter of the entire aggregate, the Docu-
ment’s Cover Texts may be placed on covers that surround only the Document within the
aggregate. Otherwise they must appear on covers around the whole aggregate.

9.8 Translation
Translation is considered a kind of modification, so you may distribute translations of
the Document under the terms of section 4. Replacing Invariant Sections with translations
requires special permission from their copyright holders, but you may include translations
of some or all Invariant Sections in addition to the original versions of these Invariant
Sections. You may include a translation of this License provided that you also include the

27 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

original English version of this License. In case of a disagreement between the translation
and the original English version of this License, the original English version will prevail.

9.9 Termination
You may not copy, modify, sublicense, or distribute the Document except as expressly
provided for under this License. Any other attempt to copy, modify, sublicense or distri-
bute the Document is void, and will automatically terminate your rights under this Li-
cense. However, parties who have received copies, or rights, from you under this License
will not have their licenses terminated so long as such parties remain in full compliance.

9.10 Future Revisions of This License


The Free Software Foundation may publish new, revised versions of the GNU Free
Documentation License from time to time. Such new versions will be similar in spirit to
the present version, but may differ in detail to address new problems or concerns. See
http ://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number. If the Document
specifies that a particular numbered version of this License ”or any later version” applies
to it, you have the option of following the terms and conditions either of that specified
version or of any later version that has been published (not as a draft) by the Free Software
Foundation. If the Document does not specify a version number of this License, you may
choose any version ever published (not as a draft) by the Free Software Foundation.

ADDENDUM : How to use this License for your documents


To use this License in a document you have written, include a copy of the License in
the document and put the following copyright and license notices just after the title page :
Copyright c YEAR YOUR NAME. Permission is granted to copy, distribute
and/or modify this document under the terms of the GNU Free Documenta-
tion License, Version 1.1 or any later version published by the Free Software
Foundation ; with the Invariant Sections being LIST THEIR TITLES, with the
Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A
copy of the license is included in the section entitled “GNU Free Documenta-
tion License”.
If you have no Invariant Sections, write “with no Invariant Sections” instead of saying
which ones are invariant. If you have no Front-Cover Texts, write “no Front-Cover Texts”
instead of “Front-Cover Texts being LIST” ; likewise for Back-Cover Texts.
If your document contains nontrivial examples of program code, we recommend re-
leasing these examples in parallel under your choice of free software license, such as the
GNU General Public License, to permit their use in free software.

28 Labo-Unix - Supinfo Paris - 2001/2002


La programmation réseau. Deuxième partie

Références
[1] http ://www.whitefang.com/rin/

29 Labo-Unix - Supinfo Paris - 2001/2002