jeudi 14 mars 2019

Chapitre 56 : Fonctions des sockets, accès à une page WEB en assembleur


Vous trouverez sur Internet, plusieurs tutoriels sur les fonctions pour gérer les sockets avec des exemples de serveurs et de clients écrits en langage C. Ces exemples m’ont servi à une adaptation à l’assembleur sur le Raspberry.
Pour la partie théorique des sockets je vous renvoie à ces tutoriels (par exemple : https://broux.developpez.com/articles/c/sockets/)
Tout d’abord, nous allons voir un petit programme amusant !! qui permet l’exécution de commandes linux d’un process à l’autre et qui nous permettra de voir l’utilisation des fonctions nécessaires.
Ces fonctions font appel à des call system linux qui curieusement ne sont pas référencés sur le site http://syscalls.kernelgrok.com/. Pour retrouver les différents codes à utiliser il faut lister un fichier se trouvant sur le raspberry avec la commande :
More /usr/include/arm-linux-gnueabihf/asm/unistd.h
Puis rechercher sur Internet, les syntaxes des commandes pour avoir les paramètres à passer à chaque fonction.
Dans ce premier programme, nous trouvons en plus des descriptions habituelles une structure de type sockaddr qui contient le type de connexion à utiliser, le port ici 4444 et l’adresse IP ici 0.0.0.0.
Dans la  partie code, nous commençons par créer la socket en utilisant le call system 281 et nous vérifions le code erreur retourné. Si la création est OK nous conservons le descripteur du socket dans le registre r4.
Puis nous enchainons sur les fonctions bind (282) qui affecte l’adresse contenue dans la structure à la socket
 listen(284) qui référence la socket comme passive cad en attente de connexion d’un client
 accept(285) qui accepte les connections avec la socket.
Enfin nous terminons avec les fonctions de duplication des descripteurs des entrées sorties standard (STDIN, STDOUT et STDERR) et nous terminons par l’appel execve qui va lancer la commande demandée à l’aide du schell.
Après compilation, ce programme doit être lancé en arrière plan comme ceci : testsocket &
En fait ce programme va écouter ce qui arrive sur le port 4444. Pour envoyer une commande sur ce port il faut d’abord exécuter la commande :
netcat -vv 0.0.0.0 4444   
Vous aurez le message suivant :
Connection to 0.0.0.0 4444 port [tcp/*] succeeded!
Qui indique le succès de la connexion. Il ne vous reste plus qu’à taper des commandes linux comme ls ou uname –a pour voir s’afficher les résultats.
Evidement dans ce cas ce programme n’a que peu d’intérêt mais vous pouvez le lancer dans une session et la lancer la commande netcat dans une autre session et c’est déjà plus intéressant. Je vous laisse le soin de trouver d’autres utilisations !!!
Pour arrêter le programme il suffit d’effectuer un control-C.

Après cette première réussite, je me suis lancé dans l’écriture d’un programme qui accède à une page d’un site web. La page d’accueil de Google étant assez lourde, j’ai pris comme exemple une petite page (/Exemple_01.php) du site ordi-senior.fr mais j’ai laissé en commentaire les adresses du site google.fr si vous voulez y accéder.
Après quelques problèmes de mise au point, ce programme fonctionne mais il faudra le compléter par des requêtes plus élaborées et par une analyse des pages web pour en extraire les données pertinentes !!!
Tout d’abord, j’ai remplacé la structure fixe du programme précèdent par une structure vide dont les données seront alimentées dans le code du programme. La réservation de l’espace nécessaire est faite dans la section .bbs.
Dans le code, nous commençons par alimenter la structure avec l’adresse ip du site à contacter et le port. Pour les sites web, le port est toujours 80 (attention il doit être stocké sur 2 octets). Pour l’adresse IP, soit on stocke les 4 parties de l’adresse octet par octet soit on stocke la totalité en hexadécimal en respectant l’ordre inverse).
Remarque : normalement, nous devrions trouver l’adresse ip d’un site en effectuant une requête à un serveur dns mais pour l’instant il nous faudra effectuer au préalable un ping nom_du_site pour avoir son adresse ip et la saisir dans le programme (quoique on pourrait programmer l’appel à la commande ping , récupérer le résultat et l’analyser pour extraire l’adresse : exercice à faire !!).
Puis nous créons la socket avec le call system connect (281)  puis nous effectuons une connexion avec l’identifiant de la socket et l’adresse de la structure précédente.
Si tout se passe bien nous préparons l’envoi de la requête qui est ici une simple chaine de caractères. Comme amélioration, il faudra plutôt créer une chaine en concaténant les informations fixes avec 2 informations variables : la ressource demandée (ici /Exemple_01.php) et le nom de l’hôte (ici ordi-senior.fr). La requête est envoyé avec le call system Send (289) qui nécessite de connaitre la longueur de la requête. Nous la calculons pour la passer par r2.
Ensuite nous entrons dans une boucle de réception des résultats. Celle-ci est nécessaire car les pages peuvent avoir des tailles importantes et le serveur va retourner les données par bloc de la taille du buffer. La fin des résultats est donnée par le code 0xA. Nous plaçons un zéro binaire à la fin de chaque bloc de données lu ce qui nous permet ici de l’afficher par notre routine standard. La fonction à utiliser est recv (291) à laquelle il faut passer l’adresse du buffer de réception et sa longueur.
Enfin le programme se termine par la fermeture de la socket.