PROGRAMMATION RÉSEAU EN PYTHON - EMMANUEL FLEURY - LABRI
←
→
Transcription du contenu de la page
Si votre navigateur ne rend pas la page correctement, lisez s'il vous plaît le contenu de la page ci-dessous
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 / 30Agenda
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 / 30Overview
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 / 30Petite 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 / 30L’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 / 30Neutralité 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 / 30Adresses 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 / 30Noms 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 / 30Domain 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 / 30Ré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 / 30Ré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 / 30Overview
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 / 30C’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 / 30Modè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 / 30Quelques 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 / 30Sockets 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 / 30Pratiquons 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 / 30Pratiquons 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 / 30Pratiquons 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 / 30Client/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 / 30Overview
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 / 30Protocole 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 / 30Uniform 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 / 30HTTP 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 / 30Pratiquons 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 / 30Pratiquons 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 / 30Bibliothè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 / 30Substituons 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 / 30Vous pouvez aussi lire