PROGRAMMATION RÉSEAU EN PYTHON - EMMANUEL FLEURY - LABRI

 
CONTINUER À LIRE
PROGRAMMATION RÉSEAU EN PYTHON - EMMANUEL FLEURY - LABRI
Programmation Réseau en Python

                                               Emmanuel Fleury
                                         
                                                 David Renault
                                        
                                        LaBRI, Université de Bordeaux, France

                                                 8 janvier 2020

E. Fleury, D. Renault (LaBRI, France)         Programmation Réseau en Python    8 janvier 2020   1 / 30
PROGRAMMATION RÉSEAU EN PYTHON - EMMANUEL FLEURY - LABRI
Agenda

1     L’Internet

2     Les Protocoles Réseau

3     Le Protocole HTTP

    E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   2 / 30
PROGRAMMATION RÉSEAU EN PYTHON - EMMANUEL FLEURY - LABRI
Overview

1     L’Internet

2     Les Protocoles Réseau

3     Le Protocole HTTP

    E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   3 / 30
Petite Histoire d’Internet
      1960 : J.C.R. Licklider pose les premiers concepts d’Internet.
      1962 : Début du programme ARPANET mené par la DARPA ;
      1969 : L’Internet connecte quatre Universités américaines ;
                  (MIT, UCLA, UCSB et l’Université d’Utah)
      1971 : Envoi du premier e-mail par Ray Tomlinsen ;
      1973 : Vinton Cerf et Robert Kahn posent les bases de TCP/IP ;
      1985 : Premier nom de domaine enregistré ;
      1989 : Invention du Web par Tim Berners-Lee au CERN (Genève) ;
      1991 : Premier site Web mis en ligne par Tim Berners-Lee ;
      1994 : Création de Yahoo (David Filo, Jerry Yang) et d’Amazon (Jeff Bezos) ;
      1998 : Création de Google (Sergey Brin, Larry Page) ;
      2001 : Création de Wikipédia (Larry Sanger, Jimmy Wales) ;
      2004 : Création de Facebook (Mark Zuckerberg) ;
      2006 : Création de Twitter (Jack Dorsey) ;
      2010 : Création d’Instagram (Mike Krieger, Kevin Systrom) ;
      ...
 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   4 / 30
L’Internet

Définition (simplifiée)
L’Internet est constitué par l’interconnexion de réseaux informatiques
et de divers réseaux de télécommunication (câbles, satellites, . . .).

Attention !
      L’Internet n’est pas restreint seulement au Web !
      Il existe plein d’autres protocoles (FTP, SSH, ICMP, . . .) ;

      L’Internet n’est pas gratuit !
      Il faut créer et maintenir l’infra-structure réseau (FAI) ;

      L’Internet n’est pas naturellement neutre et libre !
      Certains pays (la plupart en fait) décident de filtrer l’Internet à des degrés plus ou
      moins importants selon le régime politique qui y est pratiqué.

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python        8 janvier 2020   5 / 30
Neutralité du Net

Neutralité du Net
       Principe garantissant l’égalité de
       traitement de tous les flux de données sur
       Internet sans distinction ;
       Cela exclut toute discrimination positive
       ou négative à l’égard de la source, de la
       destination ou du contenu de l’information
       transmise sur le réseau.

Les craintes
       Les FAI feront payer en fonction du service
       offert (par pays accessibles, par exemple) ;
       Les FAI pourront interdire ou rendre
       difficile l’accès à certains services en ligne
       (Netflix, Téléchargements, . . .) ;
       Un accès non-équitable aux réseaux crée
       une discrimination (pour les utilisateurs et
       pour les fournisseurs de services).

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   6 / 30
Adresses Internet (pour les machines)
      Une adresse Internet est l’identifiant d’une carte réseau

                                                                IP (Internet Protocole) version 6 ;
     IP (Internet Protocole) version 4 ;
                                                                Les adresses des interfaces réseau sont
     Les adresses des interfaces réseau sont                    codées sur 8 entiers de 16-bits (de 0x0 à
     codées sur 4 entiers de 8-bits (de 0 à 255)                0xffff) représentés sous forme
     représentés sous forme décimale et                         hexadécimale et séparées par deux points
     séparées par un point (’.’) ;                              (’:’) ;
     L’adresse totale est un entier de 32-bits                  L’adresse totale est un entier de 128-bits
     (4.109 adresses possibles) ;                               (3.1038 adresses possibles) ;
     L’adresse de la machine elle-même                          L’adresse de la machine elle-même
     (loopback) est ’127.0.0.1’ par                             (loopback) est ’::1’ par convention.
     convention.

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python                    8 janvier 2020   7 / 30
Noms de Domaines (pour les humains)
Problème : Difficile de se rappeler des adresses IP (les nombres c’est pour les machines).
Solution : On donne des noms “human-friendly” aux serveurs (Noms de Domaines) pour
s’y connecter et on demandera aux machines de faire la traduction.

 Un nom de domaine est l’identifiant lisible d’une carte réseau

Format :

Exemples de noms de domaines :
      ’google.com’, ’wikipedia.org’,
      ’impots.gouv.fr’, . . .
Exemples de Top Level Domain :
      Par activités : com, edu, org, gov, . . .
      Par pays : be, fr, uk, au, . . .

  E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python    8 janvier 2020   8 / 30
Domain Name System (DNS)
Ce sont les machines qui vont traduire le nom de domaine en adresse IP

Example : Demandons de résoudre l’URL ’www.anouar.im’.

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   9 / 30
Pratiquons un peu !
Programmons un résolveur de nom de domaine avec le module socket de Python :
import socket
domain_name = input("Donnez un nom de domaine: ")
ipv4_addr = socket.gethostbyname(domain_name)
print("L'adresse IPv4 de '{}' est {}".format(domain_name, ipv4_addr))

Donnez un nom de domaine: google.com
L'adresse IPv4 de 'google.com' est 172.217.171.238

  1   Écrivez le programme ci-dessus et essayez le avec quelques noms de domaines.
  2   Mais, notre résolveur DNS ne donne que l’adresse IPv4, il faudrait qu’il donne aussi l’IPv6.
      Utilisez les méthodes suivantes pour récupérer les adresses IPs correspondantes :
        ipv4_addr = socket.getaddrinfo(domain_name, None, socket.AF_INET)
        ipv6_addr = socket.getaddrinfo(domain_name, None, socket.AF_INET6)

      Affichez les et n’oubliez pas de filtrer le tuple de retour pour ne garder que l’addresse IP.
  3   (Avancé) En utilisant ’try...except’, couvrez le cas où le nom de domaine n’existe pas
      ou encore, qu’il ne possède qu’un type d’IP. Par exemple :
Donnez un nom de domaine: toto.net                           Donnez un nom de domaine: aaaaaaa
L'adresse IPv4 de 'toto.net' est 162.215.249.14              Le domaine aaaaaaa n'a pas d'adresse ipv4!
Le domaine toto.net n'a pas d'adresse ipv6!                  Le domaine aaaaaaa n'a pas d'adresse ipv6!

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python                 8 janvier 2020   10 / 30
Résolveur de nom de domaine (corrigé)

import socket

# Get domain name from user
domain_name = input("Donnez un nom de domaine: ")

# Resolve IPv4 domain name
ipv4_2_addr = socket.getaddrinfo(domain_name, None, socket.AF_INET)[0][4][0]
print("L'adresse IPv4 de '{}' est {}".format(domain_name, ipv4_2_addr))

# Resolve IPv6 domain name
ipv6_addr = socket.getaddrinfo(domain_name, None, socket.AF_INET6)[0][4][0]
print("L'adresse IPv6 de '{}' est {}".format(domain_name, ipv6_addr))

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   11 / 30
Résolveur de nom de domaine (corrigé)

import socket

# Get domain name from user
domain_name = input("Donnez un nom de domaine: ")

# Resolve IPv4 domain name
try:
  ipv4_2_addr = socket.getaddrinfo(domain_name, None, socket.AF_INET)[0][4][0]
  print("L'adresse IPv4 de '{}' est {}".format(domain_name, ipv4_2_addr))
except:
  print("Le domaine {} n'a pas d'adresse ipv4!".format(domain_name))

# Resolve IPv6 domain name
try:
  ipv6_addr = socket.getaddrinfo(domain_name, None, socket.AF_INET6)[0][4][0]
  print("L'adresse IPv6 de '{}' est {}".format(domain_name, ipv6_addr))
except:
  print("Le domaine {} n'a pas d'adresse ipv6!".format(domain_name))

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   12 / 30
Overview

1     L’Internet

2     Les Protocoles Réseau

3     Le Protocole HTTP

    E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   13 / 30
C’est quoi un protocole ?

Protocole Réseau
Un ensemble de règles permettant à un ensemble d’entités de se coordonner à
travers le réseau pour accomplir une tâche (souvent le transfert d’information).

Mais, les entités peuvent jouer des rôles différents selon les tâches à effectuer.

Exemple : architecture Client/Serveur

       Client : Machine ou application
       qui réclame un service à un serveur.

       Serveur : Machine ou application
       qui propose un certain nombre de
       services à des clients via le réseau.

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   14 / 30
Modèle (simplifié) du protocole réseau
Les protocoles se partagent les tâches selon un système en couches.

      Couche Applicative
      Gère les protocoles qui sont directement utilisés par les applications de l’utilisateur final ;
      Couche Transport
      Gère le transfert des données brutes sur le réseau (TCP ou UDP) ;
      Couche Réseau
      Gère l’acheminement des données jusqu’à la destination (routage) ;
      Couche Physique (ou Lien)
      Gère le transit des données sur le support physique (ondes radio, câble conducteur, . . .).
 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python                8 janvier 2020   15 / 30
Quelques autres protocoles

      FTP (File Transfer Protocol, port 21) :
      Sert à télécharger des fichiers stockés sur un serveur distant ;

      SSH (Secure Shell, port 22) :
      Sert à se connecter de manière sécurisée sur d’autres machines ;

      SMTP (Simple Mail Transfer Protocol, port 25) :
      Sert à envoyer des e-mails ;

      IMAP (Internet Message Access Protocol, port 143) :
      Sert à télécharger ses e-mails pour les consulter ;

      ...

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   16 / 30
Sockets réseau
  Une seule machine peut vouloir gérer plusieurs tâches réseau simultanément.
    Une socket est un couple constitué d’une carte réseau et d’un port.

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   17 / 30
Pratiquons un peu ! (1/3)
Programmons un serveur qui retourne tout ce qu’a dit le client à l’identique.
Le serveur
import socket
HOST = '127.0.0.1' # Fait reference a la machine locale (localhost)
PORT = 1337        # Le port sur lequel on va attendre le client

# Creation du socket encore non initialise
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.bind((HOST, PORT))    # Initialise the socket à (HOST, PORT)
sock.listen()              # Ecoute le socket en l'attente du client
conn, addr = sock.accept() # Si le client arrive, on cree la connexion
print("Connexion établie avec {}".format(addr[0]))

                   #########################################
                    #### Le code du serveur vient ici! ####
                   #########################################

conn.close() # Ferme la connexion avec le client
sock.close() # Ferme le socket sur l'ordinateur

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python    8 janvier 2020   18 / 30
Pratiquons un peu ! (2/3)

Programmons un serveur qui retourne tout ce qu’a dit le client à l’identique.
Le client
import socket
HOST = '127.0.0.1' # Fait reference à la machine locale (localhost)
PORT = 1337        # Le port sur lequel on va attendre le client

# Creation du socket encore non initialise
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.connect((HOST, PORT)) # Ouvre une connexion vers le serveur

                   ########################################
                    #### Le code du client vient ici! ####
                   ########################################

sock.close() # Ferme le socket sur l'ordinateur

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python    8 janvier 2020   19 / 30
Pratiquons un peu ! (3/3)
  1   Vérifiez que la connexion s’établit en exécutant le serveur et le client sur votre machine.
  2   Un code simplifié du serveur, une fois le client connecté à lui serait :
        data = conn.recv(1024)            # Attend un envoi de la part du client
        print("Client: " + data.decode()) # Imprime ce que lui a envoye le client
        conn.sendall(data)                # Renvoie le message au client

      Vérifiez que le serveur est bien en attente du message du client en exécutant ce code.
      Notez que l’envoi d’une chaîne de caractères Python sur le réseau, requiert de l’encoder et de la décoder
      si on la reçoit ! Cela vient du fait que les chaînes de caractères en Python contiennent plus que seulement
      les simples caractères (ce sont des objets) et on ne peut envoyer que les simples caractères sur le réseau.
  3   Un code simplifié du client, une fois qu’il est connecté serait :
        msg = input("Message pour le serveur: ")                      #     Recupere le message à envoyer
        sock.sendall(msg.encode())                                    #     Envoie le message au serveur
        data = sock.recv(1024)                                        #     Recoit une reponse du serveur
        print("Serveur: {}".format(data.decode()))                    #     Affiche la reponse du serveur

      Vérifiez que le client envoie son message au serveur et que le serveur le reçoit correctement.
  4   (Avancé) Le problème de ce protocole est qu’il n’envoie qu’un seul et unique message.
      Essayez de le transformer pour qu’il permette d’en envoyer autant que l’on veut et que le
      client termine si le message est ’quitter’. On utilisera une boucle ’while True:’.

 E. Fleury, D. Renault (LaBRI, France)     Programmation Réseau en Python                     8 janvier 2020   20 / 30
Client/Serveur (corrigé)
Le client
while True:
    msg = input("Message pour le serveur: ")
    if msg == "quitter": # Sort de la boucle si on tape 'quitter'
        break
    sock.sendall(msg.encode())
    data = sock.recv(1024)
    print("Serveur: {}".format(data.decode()))

Le serveur
while True:
    data = conn.recv(1024)
    if not data: # Sort de la boucle lorsque plus rien n'arrive
        break
    print("client: " + data.decode())
    conn.sendall(data)

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   21 / 30
Overview

1     L’Internet

2     Les Protocoles Réseau

3     Le Protocole HTTP

    E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   22 / 30
Protocole HTTP

Hyper Text Transfer Protocol (HTTP)
Un protocole de communication client-serveur qui permet de récupérer et modifier des
données présentes sur un serveur. Dans sa forme standard, il utilise le port 80.
La communication peut être chiffrée en utilisant HTTPS (Secured) sur le port 443.

Histoire des versions de HTTP
     HTTP 0.9 (1991)                                      HTTP 2.0 (2015)
     HTTP 1.0 (1996)                                      HTTP 3.0 (En cours d’élaboration)
     HTTP 1.1 (1999)                                      ...

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python             8 janvier 2020   23 / 30
Uniform Resource Locator (URL)

Le protocole HTTP identifie toutes les données par des URLs

                     userinfo             hostname                port

https://john.doe:password@www.example.com:123/forum/faq/?tag=net&cmd=new

protocol                   authority                                           path            query

     protocol :                                                           hostname et port :
     Le protocole de transfert utilisé.                                   Socket de connexion du serveur.
     authority :                                                          path :
     Les paramètres de connexion au serveur.                              Le chemin vers l’objet requis.
     userinfo :                                                           query :
     Les paramètres d’authentification.                                   Les paramètres de la requête.

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python                       8 janvier 2020   24 / 30
HTTP Requêtes et Réponses
Un client HTTP effectue des requêtes sur une URL et reçoit une réponse

Requêtes                                          Réponses
     GET : Récupère un objet                              1xx : Information
     DELETE : Détruit un objet                                   100 : Continue
                                                                 101 : Changement de protocole
     PUT : Crée un nouvel objet
                                                          2xx : Succès
     POST : Met à jour un objet
                                                                 200 : Succès
     PATCH : Modifie un objet                                    201 : Création effectuée
     TRACE : Récupère les                                 3xx : Redirection
     modifications appliquées à l’objet                          301 : Changement permanent d’URL
     OPTIONS : Récupère la liste des                      4xx : Erreur du client
     méthodes supportée par le serveur                           400 : Requête incorrecte
     CONNECT : Établi une connexion                              404 : Objet non trouvé
     en vue de créer un canal chiffré                     5xx : Erreur de serveur
     ...                                                         500 : Erreur interne du serveur

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python                 8 janvier 2020   25 / 30
Pratiquons un peu ! (1/2)
  1   Ouvrez une console puis tapez les commandes suivantes :
        #> python -m http.server 8000

  2   Connectez-vous sur votre propre machine avec un navigateur Web à l’URL :
                                         http://127.0.0.1:8000/
  3   Que constatez-vous sur votre navigateur Web ?
  4   Que constatez-vous sur la console qui contient le serveur ?
  5   Trouvez une explication aux messages qui sont apparus dans la console :
        127.0.0.1 - - [18/Dec/2018 23:44:06] "GET / HTTP/1.1" 200 -
        127.0.0.1 - - [18/Dec/2018 23:44:06] "GET /favicon.ico HTTP/1.1" 404 -
        127.0.0.1 - - [18/Dec/2018 23:44:06] code 404, message File not found
  6   Créez ce fichier HTML (index.html) et visualisez le via le serveur Python :
        
          Titre de ma page
          Bonjour tout le monde!
        
  7   Connectez-vous à votre machine en utilisant sa vraie adresse IP (pas ’127.0.0.1’).
      Quels changements constatez-vous sur la console ?
 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   26 / 30
Pratiquons un peu ! (2/2)
Un serveur web peut aller au-delà de la manipulation simple de fichiers. On peut
considérer les URLs comme des commandes et programmer son serveur pour répondre à
ces commandes (sans qu’il n’y ait de fichiers). Par exemple, des sites comme Google,
Twitter ou même Facebook utilisent ce principe.

  1   Créons la commande ’request’ :
        from http.server import HTTPServer, SimpleHTTPRequestHandler

        class MyHTTPRequestHandler(SimpleHTTPRequestHandler):
            def do_GET(self):
                if self.path == "/request":
                    self.send_response(200)
                    self.wfile.write('Bonjour tout le monde!'.encode())
                else:
                    self.send_error(404)

        httpd = HTTPServer(('localhost', 8080), MyHTTPRequestHandler)
        httpd.serve_forever()

      Ce serveur réagit normalement sauf sur l’URL ’http://localhost:8080/request’,
      alors il répondra en écrivant ’Bonjour tout le monde!’.
  2   Remplacez le texte ’Bonjour tout le monde!’ par un fichier HTML valide.

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   27 / 30
Bibliothèque ’telnetlib’ (1/2)
TelnetLib est une des bibliothèques de base qui permet de communiquer via le
réseau (l’autre bibliothèque de Python pour cela est ’socket’, plus généraliste
mais aussi plus difficile à utiliser).
Ouvrir une connexion
from telnetlib import Telnet

HOST = '127.0.0.1'
PORT = 23

tn = Telnet(HOST, PORT)

Passer en mode interactif
tn.interact()

Fermer une connexion
tn.close()

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   28 / 30
Bibliothèque ’telnetlib’ (2/2)
Envoyer des données
tn.write(b'Hello server!')

Réceptionner des données
En programmation, la réception de données est un problème car on est jamais sûr que la
transmission est terminée. On doit attendre un signal qui marque la fin de la transmission
(souvent un mot clé spécial ou une suite de caractères spécifiques). Cela force à envisager les
programmes comme des entités ’asynchrones’.
Voici les différentes méthodes pour réceptionner des données envoyées sur le réseau :
      Lis les données jusqu’à ce qu’il rencontre la chaîne d’octets b’fini!\n’.
        data = tn.read_until(b'fini!\n').decode('utf-8')

      Lis les données de manière bloquante (jusqu’à terminaison de la connexion) :
        data = tn.read_all().decode('utf-8')

      Lis les données de manière non bloquante :
        data = tn.read_some().decode('utf-8')

 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python           8 janvier 2020   29 / 30
Substituons nous au Navigateur Web
  1   Lancez votre serveur Web local avec la commande :
       #> python -m http.server

  2   Écrivez le programme suivant :
        from telnetlib import Telnet

        HOST = '127.0.0.1'
        PORT = 8000

        tn = Telnet(HOST, PORT)
        tn.interact()

  3   Exécutez le programme que vous venez de taper et tapez la requête suivante :
        GET / HTTP/1.1
        Host: 127.0.0.1
        Connection: close

  4   Qu’observez vous dans la console du serveur et dans celle du programme ?
 E. Fleury, D. Renault (LaBRI, France)   Programmation Réseau en Python   8 janvier 2020   30 / 30
Vous pouvez aussi lire