labo 5: Application réseaux et sockets
Directory
Labo 5: applications réseaux socket
1. Makefile
vérification de la présence de make
sol@debian:~/LABOS/labo5$ which make
/usr/bin/make+
s’assurer que c’est GNU make:
sol@debian:~/LABOS/labo5$ dpkg -s make | grep GNU
 GNU Make is a utility which controls the generation of executables
affichage du contenu de Makefile
sol@debian:~/LABOS/labo5/makefile$ cat --show-tabs Makefile 
DEST_IMAGES=neuchatel.jpeg jura.jpeg
all: $(DEST_IMAGES)
^Ifile *.jpeg
clean:
^Irm -f $(DEST_IMAGES)
neuchatel.jpeg: neuchatel.png
^Ipngtopnm < $< | pnmtojpeg > $@
^I@# n'oubliez pas le TAB � gauche!
^I@echo converti $< en $@
jura.jpeg: jura.png
^Ipngtopnm < $< | pnmtojpeg > $@
^I@# n'oubliez pas le TAB � gauche!
^I@echo converti $< en $@
jeu de char de Makefille
sol@debian:~/LABOS/labo5/makefile$ file -i Makefile
Makefile: text/x-makefile; charset=iso-8859-1
rencodage de Makefille
sol@debian:~/LABOS/labo5/makefile$ recode iso-8859-1..UTF-8 Makefile 
sol@debian:~/LABOS/labo5/makefile$ file -i Makefile
Makefile: text/x-makefile; charset=utf-8
sol@debian:~/LABOS/labo5/makefile$ cat --show-tabs Makefile 
DEST_IMAGES=neuchatel.jpeg jura.jpeg
all: $(DEST_IMAGES)
^Ifile *.jpeg
clean:
^Irm -f $(DEST_IMAGES)
neuchatel.jpeg: neuchatel.png
^Ipngtopnm < $< | pnmtojpeg > $@
^I@# n'oubliez pas le TAB à gauche!
^I@echo converti $< en $@
jura.jpeg: jura.png
^Ipngtopnm < $< | pnmtojpeg > $@
^I@# n'oubliez pas le TAB à gauche!
^I@echo converti $< en $@
L’option --show-tabs remplace les tabulations par des ^
question 6
tuto makefile
cible: dependance
    commandes
$@  # nom de la cible actuelle.
Le Makefille va réencoder les fichier neuchatel.png et jura.png en neuchatel.jpg et jura.jpg
question 7
fichier1.txt: fichier0.txt
    cat fichier0.txt > fichier1.txt
Cette règle ne sera exécutée que si fichier0.txt est plus récent que fichier1.txt
Dans notre cas:
neuchatel.jpeg: neuchatel.png
	pngtopnm < $< | pnmtojpeg > $@
	@# n'oubliez pas le TAB à gauche!
	@echo converti $< en $@
La règle n’est pas exécutée car neuchatel.png n’est pas plus récent que neuchatel.jpg et pareil pour jura.png.
question 8
sol@debian:~/LABOS/labo5/makefile$ touch jura.png
sol@debian:~/LABOS/labo5/makefile$ make 
pngtopnm < jura.png | pnmtojpeg > jura.jpeg
converti jura.png en jura.jpeg
file *.jpeg
jura.jpeg:      JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 406x493, frames 3
neuchatel.jpeg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 406x493, frames 3
Comme touch jura.png modifie la date de modification de jura.png, il devient donc plus récent que jura.jpeg et la règle…
jura.jpeg: jura.png
	pngtopnm < $< | pnmtojpeg > $@
… est denouveau valable.
question 9
@ empêche l’affichage de la sortie texte d’une commande.
question 10
En utilisant le Pattern matching on peut créer des règles génériques pour entre autres convertir les fichiers d’un certain format en un autre:
Make exécute une règle même si le fichier correspondant est situé dans un sous dossier ! En cas de conflit, la règle avec la meilleure correspondance est choisie.
La syntaxe générale d’une fonction:
$(fonction arg0,arg1,arg2...).
La fonction patsubst remplace par une paterne chaque mot dans une liste donnée.
Normalement les remplacement de Wildcards se font automatiquement dans les règles mais comme dans notre cas on veut utiliser un wildcard en argument d’une fonction on doit utiliser la fonction wildcard.
syntaxe:
$(patsubst %.c,%.o,$(wildcard *.c))
Élimination de la redondance dans le Makefile original + optimisation via fonctions.
DEST_IMAGES=$(patsubst %.png, %.jpeg, $(wildcard *.png))
all: $(DEST_IMAgES)
	file *.jpeg
%.jpeg: %.png
	pngtopnm < $< | pnmtojpeg > $@
	@echo converti $< en $@
clean:
	rm -f $(DEST_IMAGES)
sources:
gnu.org
stackoverflow.com
learnxinyminutes.com
question 11
a.
sol@debian:~/LABOS/labo5/02tests$ make a.o
cc    -c -o a.o a.c
sol@debian:~/LABOS/labo5/02tests$ ls
a.c  a.o
Si on utilise le -d de make on a tout un paquet d’informations mais dans les premières lignes on peut voir que c’est bien GCC qui est utilisé pour le build.
sol@debian:~/LABOS/labo5/02tests$ make -d a.o
GNU Make 4.0
Built for x86_64-pc-linux-gnu
# ...
Finished prerequisites of target file 'a.o'.
Must remake target 'a.o'.
cc    -c -o a.o a.c
Putting child 0x10dfde0 (a.o) PID 6042 on the chain.
Live child 0x10dfde0 (a.o) PID 6042 
Reaping winning child 0x10dfde0 PID 6042 
Removing child 0x10dfde0 PID 6042 from chain.
Successfully remade target file 'a.o'.
sol@debian:~/LABOS/labo5/02tests$ ls
a.c  a.o
b.
CFLAGS et CXXFLAGS sont les noms de variables d’environnement ou de variables du Makefile qui peuvent être utilisées pour paramétrer la compilation d’un logiciel.
Ces variables sont habituellement positionnées dans un Makefile et sont ajoutées quand le compilateur est appelé. Si elles ne sont pas spécifiées dans le Makefile, alors elles seront prises directement à partir de l’environnement, si elles sont présentes.
CFLAGS permet d’ajouter des paramètres sur la ligne de commande qui appelle le compilateur C, alors que CXXFLAGS est utilisé pour la compilation C++. De même, une variable similaire, CPPFLAGS, permet de passer des paramètres sur la ligne de commande du Préprocesseur C.
- 
    -Wall : -W represents warnings. -Wall means it will show all possible warnings. 
- 
    -O2 : means optimize it. These are used in creating machine code from source code by compiler. There are many levels of optimizing. O2 is generally considered good enough, while O3 makes your file size bigger without optimizing significantly, and may make your program slower on some sort of algorithms. 
- 
    -g means debug. This will add many debugging “notes” in your final binary, so that when you run a debugger on your final binary file, the debugger will know what part of this binary file is related to which part of the original source code. 
- 
    -I/path/to/include/files : In C/C++, you can include files which compiler already knows where they are present with command: #include . If the compiler does not know where the include file you want to include is present, you can specify the path to that directory with -I/the/directory 
Il nous faut donc créer un Makefile et y ajouter une ligne CFLAGS=-Wall
sol@debian:~/LABOS/labo5/02tests$ make -Wall a.o
cc -Wall   -c -o a.o a.c
sol@debian:~/LABOS/labo5/02tests$ rm a.0
sol@debian:~/LABOS/labo5/02tests$ make a.o
cc -Wall   -c -o a.o a.c
sources:
man gcc
wikipedia.fr
cs.colby.edu
wiki.gentoo.org  (très bon site) qui explique ce point en détails
question 12
Voir question 10
question 13
voir question 10
2. Traçage et simulation
- netstat -anvoir tous les sockets
- netstat -an --inetsockets ipv4
- netstat -an --inet6sockets ipv6
- netstat --inet --listen -NNNNsocket en écoute
- netstat --inet6 --listen -NNNNsocket en écoute
- sudo netstat-p… identification du process lié (PID/prgrm name)adisplay all sockets
 nnumeric: don’t resolve name
- stracetraçage des appels system- -eexpr a qualifying expression: option=[!]all or option=[!]val1[,val2]…- strace -e socket,connect ./simple-client localhost 7000
- ltracetraçage des fonctions de la LIBC
création d’un serveur TCP avec netcat:
- nc -l -pport- ncnetcat
 - -llisten mode, for inbound connects
 - -plocal port number
penser à utiliser Wireshark !
question 1
Afficher les permissions:
-rwxr-xr-x 1 sol sol 10995 Mar 25  2015 a-tracer
question 2
Lancer et déterminer le PID
sol@debian:~/LABOS/labo5/03Trace$ ./a-tracer &
[1] 6834
question 3
Déterminer les ports en écoute
sol@debian:~$ sudo netstat -p -n --inet --listen 6834
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      453/sshd        
tcp        0      0 0.0.0.0:12345           0.0.0.0:*               LISTEN      6834/a-tracer   
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      844/exim4       
udp        0      0 0.0.0.0:59362           0.0.0.0:*                           828/dhclient    
udp        0      0 0.0.0.0:68              0.0.0.0:*                           828/dhclient    
udp        0      0 10.0.2.15:123           0.0.0.0:*                           498/ntpd        
udp        0      0 127.0.0.1:123           0.0.0.0:*                           498/ntpd        
udp        0      0 0.0.0.0:123             0.0.0.0:*                           498/ntpd        
L’option -n permet de ne pas résoudre les noms.
Notre application utilise donc le port 12345.
question 4
Après un kill du process…
sol@debian:~$ sudo netstat -p -n --inet --listen 6834
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      453/sshd        
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      844/exim4       
udp        0      0 0.0.0.0:59362           0.0.0.0:*                           828/dhclient    
udp        0      0 0.0.0.0:68              0.0.0.0:*                           828/dhclient    
udp        0      0 10.0.2.15:123           0.0.0.0:*                           498/ntpd        
udp        0      0 127.0.0.1:123           0.0.0.0:*                           498/ntpd        
udp        0      0 0.0.0.0:123             0.0.0.0:*                           498/ntpd  
GONE !
question 5
a.
Dans le cycle de vie des sockets d’un serveur TCP nous retrouvons les fonctions socket() bind() listen() et accept() qui sont utilisées avant l’établissement de la connexion.
///...
socket      (PF_INET, SOCK_STREAM, IPPROTO_IP)      = 3
setsockopt  (3, SOL_SOCKET, SO_REUSEADDR, [1], 4)   = 0
bind        (3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen      (3, 1)                                  = 0
accept      (3, 
Sur cette première capture des appels system nous retrouvons les fonctions nos fonctions:
int socket(int domain, int type, int protocol);
Cette fonction crée un socket. On remarque qu’un socket est donc de type int. Renvoie le descripteur du socket nouvellement créé.
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
Lie un socket avec une structure sockaddr.
int listen(int s, int backlog);
Définie la taille de la file de connexions en attente pour le socket s.
int accept(int sock, struct sockaddr *adresse, socklen_t *longueur);
Accepte la connexion d’un socket sur le socket sock. Le socket aura été préalablement lié avec un port avec la fonction bind
L’argument adresse sera remplie avec les informations du client qui s’est connecté.
Cette fonction retourne un nouveau socket, qui devra être utilisé pour communiquer avec le client.
La dernière fonction est particulièrement révélatrice. C’est bien le cycle de vie d’un serveur TCP qui attend un qu’un client se connecte. D’où le fait que la capture de strace soie en stand by à cet endroit.
sources
super tuto programmation sockets
b.
Cycle de vie sockets TCP
Il s’agit des appels système utilisés pour:
- créér des sockets
- les lier à un port connu
- accepter des connexions TCP
Les étapes passées dans notre cas sont donc: création et bind
c.
acceptation (c’est la phase la plus hard et la plus longue d’une rupture)
question 6
Après la commande nc localhost 12345 nous avons:
accept(3, Process 7127 attached
 <unfinished ...>
[pid  7127] close(3)                    = 0
[pid  7127] fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
[pid  7127] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe86d900000
[pid  7127] write(1, "7127: new connection from client"..., 547127: new connection from client 127.0.0.1 port 47530
) = 54
[pid  7127] fcntl(4, F_GETFL)           = 0x2 (flags O_RDWR)
[pid  7127] brk(0)                      = 0x245a000
[pid  7127] brk(0x247b000)              = 0x247b000
[pid  7127] fstat(4, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
[pid  7127] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe86d8ff000
[pid  7127] lseek(4, 0, SEEK_CUR)       = -1 ESPIPE (Illegal seek)
[pid  7127] read(4, 
a.
si nous reprenons l’explication de la fonction accept():
Accepte la connexion d’un socket sur le socket
sock. Le socket aura été préalablement lié avec un port avec la fonctionbindL’argument adresse sera remplie avec les informations du client qui s’est connecté. Cette fonction retourne un nouveau socket, qui devra être utilisé pour communiquer avec le client.
accept(3, Process 7127 attached
nous dit donc que le le process avec le PID 7127 s’est connecté sur le socket 3. pas sur, (poser la question)
ps -u nous renvoie que le process 7127 est:
sol       7127  0.0  0.0   4220    80 pts/1    S+   18:01   0:00 ./a-tracer
b.
Ici nous voyons qu’une seconde instance de a-tracer est apparue ce qui colle avec l’explication de la fonction accept()
sol@debian:~/LABOS/labo5/03Trace$ sudo netstat -anp --inet
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      453/sshd        
tcp        0      0 0.0.0.0:12345           0.0.0.0:*               LISTEN      7125/a-tracer   
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      844/exim4       
tcp        0      0 127.0.0.1:12345         127.0.0.1:47534         ESTABLISHED 7127/a-tracer   
tcp        0      0 127.0.0.1:12345         127.0.0.1:47533         TIME_WAIT   -               
tcp        0      0 127.0.0.1:47534         127.0.0.1:12345         ESTABLISHED 7126/nc         
udp        0      0 0.0.0.0:59362           0.0.0.0:*                           828/dhclient    
udp        0      0 0.0.0.0:68              0.0.0.0:*                           828/dhclient    
udp        0      0 10.0.2.15:123           0.0.0.0:*                           498/ntpd        
udp        0      0 127.0.0.1:123           0.0.0.0:*                           498/ntpd        
udp        0      0 0.0.0.0:123             0.0.0.0:*                           498/ntpd    
c.
Encore une fois, grace à l’explication de la fonction accept() nous pouvons dire que le processus initial, est toujours en écoute alors que le process qu’elle a retourné s’est lié au socket 47534. Nous le voyons sur le netstat -anp --inet précédent.
et voilà la dernière ligne qui désigne le process de netcat propriétaire du socket 47534
tcp        0      0 127.0.0.1:47534         127.0.0.1:12345         ESTABLISHED 7126/nc  
d.
Il y a n + 1 = 3 sockets.
a-tracer est donc un server TCP en mode flux.
e.
accept()
f.
-f -- follow forks
g.
-p pid -- trace process with process id PID, may be repeated
question 7
si on reprend tout depuis le début en monitorant Loopback avec Wireshark, c’est la fête:
- création du premier process de a-tracer.
    Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name cp 0 0 0.0.0.0:12345 0.0.0.0:* LISTEN 7303/a-tracer
- première connexion sur le process de a-tracer
Le joli 3 way hand shake ! Les adresses IP sont les mêmes comme c’est un une communication entre un client TCP et un client au sein de la même machine.
nous avons tous les informations concernant les ports dans wireshark mais netstat nous donnes ces infos aussi:
tcp        0      0 127.0.0.1:12345         127.0.0.1:47541         ESTABLISHED 7308/a-tracer   
tcp        0      0 127.0.0.1:47541         127.0.0.1:12345         ESTABLISHED 7307/nc  
7303 reste en écoute
- seconde connexion sur le process de a-tracer
tcp        0      0 127.0.0.1:12345         127.0.0.1:47545         ESTABLISHED 7343/a-tracer   
tcp        0      0 127.0.0.1:47545         127.0.0.1:12345         ESTABLISHED 7342/nc   
nouveau 3 hand shake, 7303 fidèle au poste toujours en écoute pendant que son fils fait sa fête au le port 47545
- création d’un nouveau processus netcatque nous trackons avecstrace nc localhost 12345
une foule d’informations dont la fin nous montre le début du cycle de vie coté client. Les appels système interessants sont:
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
et
connect(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
question 8
sol@debian:~$ strace -e socket,bind,recvfrom nc -l -p 12345 -u localhost
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
recvfrom(3, "Wed May 24 19:48:45 CDT 2017\n", 8192, MSG_PEEK, {sa_family=AF_INET, sin_port=htons(46262), sin_addr=inet_addr("127.0.0.1")}, [16]) = 29
invalid connection to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 46262
 = -1 ENOENT (No such file or directory)
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
write(3, "Wed May 24 19:48:45 CDT 2017\n", 29) = 29
question 9
sol       7303  0.0  0.0   4080   660 pts/1    S+   18:56   0:00 ./a-tracer
sol       7308  0.0  0.0      0     0 pts/1    Z+   18:56   0:00 [a-tracer] <defunct>
sol       7342  0.0  0.0   6328  1660 pts/3    S+   19:15   0:00 nc localhost 12345
sol       7343  0.0  0.0   4220    80 pts/1    S+   19:15   0:00 ./a-tracer
sol       7390  0.0  0.0      0     0 pts/1    Z+   19:36   0:00 [a-tracer] <defunct>
Concernant les processus fils terminés qui se transforment en “processus” zombie:
Un soucis de reallocation mémoire après la destruction d’un objet?
3. Client TCP simple (Linux) *pas pour le labo
ressources:
simple-client.c (base)
video explicative
tuto sockets
mon poste sur les sockets
question 3 (a,b,c,d)
Implémentation de la fonction tcp_connect()
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
static FILE *tcp_connect(const char *hostname, const char *port);
int main(int argc, char **argv)
{
    int result = EXIT_FAILURE;
    if (argc == 3)
    {
        FILE *f;
        if ((f = tcp_connect(argv[1], argv[2])))
        {
        }
        /* else: failure */
    }
    else
    {
        fprintf(stderr, "%s remote-host port\n", argv[0]);
        fprintf(stderr, "%s: bad args.\n", argv[0]);
    }
    return result;
}
static FILE *tcp_connect(const char *hostname, const char *port)
{
    FILE *f = NULL;
    int s; 
    struct addrinfo hints; 
    struct addrinfo *result, *rp;
    hints.ai_family = AF_UNSPEC; /* IPv4 or v6 */ 
    hints.ai_socktype = SOCK_STREAM; /* TCP */ 
    hints.ai_flags = 0; 
    hints.ai_protocol = 0; /* any protocol */
    if ((s = getaddrinfo(hostname, port, &hints, &result))) 
    { 
        fprintf(stderr, "getaddrinfo(): failed: %s.\n", gai_strerror(s)); 
    } 
    else 
    { 
        for (rp = result; rp != NULL; rp = rp->ai_next) 
        {
            // AFFICHER UNE ADR 
            char ipname[INET_ADDRSTRLEN]; 
            char servicename[6]; /* "65535\0" */
            if (!getnameinfo(rp->ai_addr, 
                             rp->ai_addrlen, 
                             ipname, 
                             sizeof(ipname), 
                             servicename, 
                             sizeof(servicename), 
                             NI_NUMERICHOST|NI_NUMERICSERV)) 
            { 
                printf("Trying connection to host %s:%s ...\n", 
                        ipname, 
                        servicename); 
            
                /* CREATION DE SOCKET */ 
                if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) 
                { 
                    perror("socket() failed: "); 
                }
                /* connexion */ 
                if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) 
                {
                    // ...
                } 
                else 
                { 
                    perror("connect(): "); 
                }
            }
        }
        freeaddrinfo(result);
    }
    return f;
}
questions 4,5,6,7
Appels système: après strace -e socket,connect ./simple-client localhost 7000
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE) = 3
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(7000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3
connect(3, { sa_family=AF_INET6,
             sin6_port=htons(7000),
             inet_pton(AF_INET6, "::1", &sin6_addr),
             sin6_flowinfo=0,
             sin6_scope_id=0 
          }, 
          28
        ) 
= 0
Trying connection to host ::1:7000 ...
socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP) = 3
connect(
        3, { sa_family=AF_INET6, 
             sin6_port=htons(7000), 
             inet_pton(AF_INET6, "::1", &sin6_addr),
             sin6_flowinfo=0, sin6_scope_id=0
            }, 28
        ) 
= -1 ECONNREFUSED (Connection refused)
connect(): : Connection refused
Trying connection to host 127.0.0.1:7000 ...
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(7000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
+++ exited with 1 +++
fonctions de la libc utilisées après 
ltrace ./simple-client localhost 7000
__libc_start_main(0x4007a6, 3, 0x7ffe79c13c18, 0x4009b0 <unfinished ...>
getaddrinfo("localhost", "7000", 0x7ffe79c13ab0, 0x7ffe79c13aa8)            = 0
getnameinfo(0x16271e0, 28, "", 16, "Xg\356w\355\177", 6, 3)                 = 0
printf("Trying connection to host %s:%s "..., "::1", 
       "7000"Trying connection to host ::1:7000 ... )                       = 39 
socket(10, 1, 6)                                                            = 3
connect(3, 0x16271e0, 28, 0x16271e0)                                        = -1
perror("connect(): "connect(): : Connection refused)
                                                                            = <void>
getnameinfo(0x1627190, 16, "::1", 16, "7000", 6, 3)                         = 0
printf("Trying connection to host %s:%s "..., "127.0.0.1", 
       "7000"Trying connection to host 127.0.0.1:7000 ...)
                                                                            = 45
socket(2, 1, 6)                                                             = 4
connect(4, 0x1627190, 16, 0x1627190)                                        = -1
perror("connect(): "connect(): : Connection refused)
                                                                            = <void>
freeaddrinfo(0x16271b0)                                                     = <void>
+++ exited (status 1) +++
question 8
Ajout d’un printf() dans notre code affin d’avoir un affichage brut de la valeur du champ sin_port:
// ...
// printf("Trying connection to host %s:%s ...\n", 
//         ipname, 
//         servicename); 
    // affichage brut de la valeur du champ sin_port
   if (rp->ai_family == AF_INET)
   {
       printf("HACK: raw port number: %d\n",
               ((struct sockaddr_in * ) rp->ai_addr)   ->sin_port);
   }                 // cast
// CREATION DE SOCKET 
// ...
Qui donne le résultat suivant:
sol@debian:~/LABOS/labo5/04CliendTCP/01simple-client$ ./simple-client localhost 7000
Trying connection to host ::1:7000 ...
connect(): : Connection refused
Trying connection to host 127.0.0.1:7000 ...
HACK: raw port number: 22555
Nous avons effectivement une situation étrange. L’affichage de brut de notre port est différent de celui que nous avons introduit pourtant il se connecte bien sur le port 7000 et non le 22555.
On dirait donc que c’est un problème d’affichage du dans le bout de code que nous venons d’introduire.
La lecture du man de getaddrinfo était particulièrement interessante. Non seulement je crois avoir compris que c’est un problème d’endianess mais on y trouve un exemple d’un serveur et d’un client UDP.
endianess:
L’ordre dans lequel les octets sont organisés dans une case mémoire ou dans une communication. Big endian et Little endian sont deux architectures différentes.
Big endian
byte de poids fort à gauche.
Rangement en mémoire de la valeur 0xA0B70708 dans une structure mémoire de cases de 1 byte
| adr: | 0 | 1 | 2 | 3 | |
|---|---|---|---|---|---|
| val: | A0 | B7 | 07 | 08 | … | 
Rangement en mémoire de la valeur 0xA0B70708 dans une structure mémoire de cases de 2 byte:
| 0 | 1 | 2 | 3 | ||
|---|---|---|---|---|---|
| A0 | B7 | 07 | 08 | … | 
Tous les protocoles TCP/IP communiquent en big-endian
Little endian
byte de poid faible à gauche. 
Rangement en mémoire de la valeur 0xA0B70708 dans une structure mémoire de cases de 1 byte
| adr: | 0 | 1 | 2 | 3 | |
|---|---|---|---|---|---|
| val: | 08 | 07 | B7 | A0 | … | 
Rangement en mémoire de la valeur 0xA0B70708 dans une structure mémoire de cases de 2 byte:
| 0 | 1 | 2 | 3 | ||
|---|---|---|---|---|---|
| 07 | 08 | A0 | B7 | … | 
X86 ainsi que la majorité des pc actuels fonctionne en Little endian
reponse
- 7000 = 0x 1B 58
- 22555 = 0x 58 1B
Bingo ! C’est donc bien un problème d’endianess.
question 9
a, b, c:
- 
    conversion d’un socket en fichier text dans la fonction tcp_connect() 
- 
    ajout d’un buffer et d’un fgets() dans le main qui va capturer la première ligne de ce que le serveur a à nous dire et puts() l’ajouter dans buff.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
static FILE *tcp_connect(const char *hostname, const char *port);
int main(int argc, char **argv)
{
    int result = EXIT_FAILURE;
    if (argc == 3)
    {
        FILE *f;
        if ((f = tcp_connect(argv[1], argv[2])))
        {
            // creation d'un buffer affin de stocker la premiere ligne
            char buff[1024];
            if (fgets(buff, sizeof(buff), f))
            { 
                puts(buff);
            }
        }
        // else: fail
    }
    else
    {
        fprintf(stderr, "%s remote-host port\n", argv[0]);
        fprintf(stderr, "%s: bad args.\n", argv[0]);
    }
    return result;
}
static FILE *tcp_connect(const char *hostname, const char *port)
{
    FILE *f = NULL;
    int s; 
    struct addrinfo hints; 
    struct addrinfo *result, *rp;
    hints.ai_family = AF_UNSPEC; // IPv4 or v6  
    hints.ai_socktype = SOCK_STREAM; // TCP  
    hints.ai_flags = 0; 
    hints.ai_protocol = 0; // any protocol 
    if ((s = getaddrinfo(hostname, port, &hints, &result))) 
    { 
        fprintf(stderr, "getaddrinfo(): failed: %s.\n", gai_strerror(s)); 
    } 
    else 
    { 
        for (rp = result; rp != NULL; rp = rp->ai_next) 
        {
            // AFFICHER UNE ADR 
            char ipname[INET_ADDRSTRLEN]; 
            char servicename[6]; // "65535\0" 
            if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, 
                             ipname, sizeof(ipname), 
                             servicename, sizeof(servicename), 
                             NI_NUMERICHOST|NI_NUMERICSERV)) 
            { 
                printf("Trying connection to host %s:%s ...\n", 
                        ipname, 
                        servicename); 
                //     // affichage brut de la valeur du champ sin_port
                // if (rp->ai_family == AF_INET)
                // {
                //     printf("HACK: raw port number: %d\n",
                //           ((struct sockaddr_in * ) rp->ai_addr)->sin_port);
                // }                 // cast
            
                // CREATION DE SOCKET  
                if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) 
                { 
                    perror("socket() failed: "); 
                }
                // CONNEXION 
                else
                { 
                    if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) 
                    {
                        // conversion de socket en un FILE *
                        // fdopen() permet d'utiliser un socket comme un fichier
                        if ((f = fdopen(s, "r+"))) 
                        { // utilisation de fonctions classiques maintenant possible
                            break; // !! Si on return maintenant, la memoire n'est pas liberee!
                        }
                    } 
                    else 
                    { 
                        perror("connect(): "); 
                    }
                    close(s); // new -> delete | open -> close
                }
            }
        }
        freeaddrinfo(result);
    }
    return f;
}
4. Client HTTP
Pour pas abuser sur la longueur de la page, Le détail de comment en arriver à ce code se trouve sur une autre page
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netdb.h> // hostent
#include<arpa/inet.h> // inet_addr
#include<unistd.h>
bool resolveDomain(char *ip);
int main(int argc, char **argv)
{
    // test arguments
    if (argc != 3)
    {
        fprintf(stderr, "\n%s bad args\n", argv[0]);
        return 1;
    }
    // variables
    int   _socket;
    char* _message;
    char  _buffer[4096];
    struct sockaddr_in _server;
    // s'assure qu'argv[1] est une IP notation point.
    resolveDomain(argv[1]);
    // creation du socket
    _socket = socket(AF_INET, SOCK_STREAM, 0); // ipv4, TCP, 0 (=IPPROTO_IP)
    if (_socket == -1)
    {
        puts("Could not create socket");
    }   
    // initialisation des membres de l'objet _server
    _server.sin_addr.s_addr = inet_addr(argv[1]); // ip
    _server.sin_family = AF_INET;                 // ipv4
    _server.sin_port = htons(80);                 // port (client HTTP = port 80)
    
    // connexion au serveur
    if (connect(_socket, (struct sockaddr *)&_server, sizeof(_server)) < 0)
    {
        puts("connection error\n");
        return 1;
    }
    puts("\nconnected\n");
    // envoie requete HTTP
    _message = "GET / HTTP/1.1\r\n\r\n";
    if (send(_socket, _message, strlen(_message), 0) < 0)
    {
        puts("Send failed\n");
        return 1;
    }
    // reponse du serveur
    if (recv(_socket, _buffer, 4096, 0) < 0)
    {
        puts("recv failed");
    }
    puts("Reply received\n");
    puts(_buffer);
    close(_socket);
    return 0;
}
bool resolveDomain(char *ip)
{
    char *_hostname = ip;
    struct hostent *_he;
    struct in_addr **_addr_list;
    if ((_he = gethostbyname(_hostname)) == NULL)
    {
        puts("gethostbyname error");
        return 1;
    }
    _addr_list = (struct in_addr ** )_he->h_addr_list;
    strcpy(ip, inet_ntoa(*_addr_list[0]));
    return 0;
}
sol@debian:~/code/sockets/clientHTTP-Lab5$ ./clientHTTPv1 gandalf.teleinf.labinfo.eiaj.ch 80
connected
Reply received
HTTP/1.1 400 Bad Request
Date: Tue, 30 May 2017 12:33:58 GMT
Server: Apache/2.2.22 (Debian)
Vary: Accept-Encoding
Content-Length: 301
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.2.22 (Debian) Server at _default_ Port 80</address>
</body></html>
sol@debian:~/code/sockets/clientHTTP-Lab5$ ./clientHTTPv1 www.google.com 80
connected
Reply received
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Referrer-Policy: no-referrer
Location: http://www.google.ch/?gfe_rd=cr&ei=1GYtWbrcHqTC8gfh2q6ABA
Content-Length: 258
Date: Tue, 30 May 2017 12:34:28 GMT
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.ch/?gfe_rd=cr&ei=1GYtWbrcHqTC8gfh2q6ABA">here</A>.
</BODY></HTML>
5. serveur TCP multiprocessus
Pour pas abuser sur la longueur de la page, Le détail de comment en arriver à ce code se trouvent sur une autre page
#include<stdio.h>
#include <stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netdb.h>    
#include<arpa/inet.h>
#include<unistd.h>
int main(int argc, char **argv)
{
   // variables
    int    _socket, _newSock, c;
    struct sockaddr_in _server, _client;
    char*  _message;
    // creation de socket
    _socket = socket(AF_INET, SOCK_STREAM, 0);
    if (_socket == -1)
    {
        printf("\nCould not create socket\n");
    }
    // initialisation des membres de l'objet _server
    _server.sin_family      = AF_INET;
    _server.sin_addr.s_addr = INADDR_ANY;
    _server.sin_port        = htons(atoi(argv[1])); // conversion (char->int)->NetworkShort
    // bind
    if (bind(_socket, (struct sockaddr * )&_server, sizeof(_server)) < 0)
    {
        puts("\nbind failded\n");
    }
    puts("\nbind done\n");
    // ecoute
    listen(_socket, 3);
    // Accepte les connexions
    puts("Waiting for incoming connections...\n");
    c = sizeof(struct sockaddr_in);
    while ( (_newSock = accept(_socket, (struct sockaddr * )&_client, (socklen_t * )&c)) )
    {
        // affiche les infos du _client
        printf("Connection from %s:%d accepted\n",inet_ntoa(_client.sin_addr), ntohs(_client.sin_port));
        // repond au client
        _message = "Hey client! \n.";
        write(_newSock, _message, strlen(_message));
    }
    if (_newSock < 0)
    {
        puts("accept failed\n");
    }
    return 0;
}
6. serveur TCP multi-thread
Pour pas abuser sur la longueur de la page, Le détail de comment en arriver à ce code se trouvent sur une autre page
makefile:
TARGET=serveurTCPmulti-Threads
CFLAGS=-Wall -lpthread
all: $(TARGET)
clean:
	rm -f $(TARGET)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>    // strlen
#include<sys/socket.h>
#include<arpa/inet.h> // inet_addr
#include<unistd.h>    // write
#include<pthread.h>   // threading
void* connection_handler(void *);
int main(int argc, char **argv)
{
   // variables
    int    _socket, _newSock, c;
    int *  _newSockPtr;
    struct sockaddr_in _server, _client;
    char*  _message;
    // creation de socket
    _socket = socket(AF_INET, SOCK_STREAM, 0);
    if (_socket == -1)
    {
        printf("\nCould not create socket\n");
    }
    // initialisation des membres de l'objet _server
    _server.sin_family      = AF_INET;
    _server.sin_addr.s_addr = INADDR_ANY;
    _server.sin_port        = htons(atoi(argv[1])); // conversion (char->int)->NetworkShort
    // bind
    if (bind(_socket, (struct sockaddr * )&_server, sizeof(_server)) < 0)
    {
        puts("\nbind failded\n");
    }
    puts("\nbind done\n");
    // ecoute
    listen(_socket, 3);
    // Accepte les connexions
    puts("Waiting for incoming connections...\n");
    c = sizeof(struct sockaddr_in);
    while ( (_newSock = accept(_socket, (struct sockaddr * )&_client, (socklen_t * )&c)) )
    {
        // affiche les infos du _client
        printf("Connection from %s:%d accepted\n",inet_ntoa(_client.sin_addr), ntohs(_client.sin_port));
        // repond au client
        _message = "Hey client! You will be assigned to a handler... \n.";
        write(_newSock, _message, strlen(_message));
        // creation d'un nouveau thread
        pthread_t sniffer_thread;
        _newSockPtr = (int *)malloc(sizeof(int));
        *_newSockPtr = _newSock;
        if (pthread_create( &sniffer_thread, NULL, connection_handler, (void * ) _newSockPtr) < 0)
        {
            puts("could not create thread\n");
            return 1;
        }
        perror("Handler assigned\n");
    }
    if (_newSock < 0)
    {
        puts("accept failed\n");
    }
    return 0;
}
void* connection_handler(void *socket_desc)
{
    // prend la description du socket
    int sock = *(int *)socket_desc;
    int read_size;
    char *message, client_message[2000];
    // envoie quelques messages au client
    message = "Greetings! I'm your connection handler\n";
    write(sock, message, strlen(message));
    message = "Now type somthing and I will repeat it.";
    write(sock, message, strlen(message));
    // reception de message
    while ((read_size = recv(sock, client_message, 2000, 0)) > 0)
    {
        // renvoie le message au client
        write(sock, client_message, strlen(client_message));
    }
    if (read_size == 0)
    {
        puts("Client disconnected");
        fflush(stdout);
    }
    else if (read_size == -1)
    {
        perror("recv failed");
    }
    // libere la memoire
    free(socket_desc);
    return 0;
}







