Implémentation d'un Proxy TCP Simple en Python

Le proxy TCP fonctionne comme un point de passage réseau qui intercepte et redirige les connexions entre un client et un serveur. Il établit une liaison via un hôte intermédiaire pour relayer les données de manière transparente.

Objectifs de la mise en œuvre

Ce proxy doit assurer plusieurs fonctions clés : visualiser les échanges entre les entités, manipuler les paquets en transit, et gérer les connexions socket de manière fiable.

Affcihage des données échangées

Pour analyser le trafic, une fonction de dump hexadécimal convertit les données brutes en une représentation lisible, montrant à la fois les valeurs hexadécimales et les caractères imprimables.

def hex_dump(info, step=16, output=True):
    if isinstance(info, bytes):
        info = info.decode('latin-1')  # Décodage pour gérer tous les octets
    segments = []
    for position in range(0, len(info), step):
        portion = info[position:position + step]
        hex_values = ' '.join(f'{byte:02x}' for byte in portion.encode('latin-1'))
        ascii_repr = ''.join(chr(b) if 32 <= b < 127 else '.' for b in portion.encode('latin-1'))
        segments.append(f"{position:04x}  {hex_values:<{step*3}}  {ascii_repr}")
    if output:
        for line in segments:
            print(line)
    else:
        return segments

Traiteemnt des flux de données

Les fonctions de réception récupèrent les données des sockets avec un délai d'attente, tandis que les gestionnaires permettent de modifier les requêtes et réponses avant leur transfert.

def obtenir_donnees(connexion):
    accumulateur = b""
    connexion.settimeout(5)
    try:
        while True:
            fragment = connexion.recv(4096)
            if not fragment:
                break
            accumulateur += fragment
    except:
        pass
    return accumulateur

def ajuster_requete(contenu):
    # Ici, on peut insérer des transformations sur les requêtes
    return contenu

def ajuster_reponse(contenu):
    # Ici, on peut insérer des transformations sur les réponses
    return contenu

def gerer_proxy(socket_client, hote_cible, port_cible, recevoir_en_premier):
    socket_cible = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socket_cible.connect((hote_cible, port_cible))
    if recevoir_en_premier:
        reponse_cible = obtenir_donnees(socket_cible)
        hex_dump(reponse_cible)
        reponse_cible = ajuster_reponse(reponse_cible)
        if reponse_cible:
            socket_client.send(reponse_cible)
    while True:
        requete_client = obtenir_donnees(socket_client)
        if requete_client:
            print(f"[*] Reçu {len(requete_client)} octets du client.")
            hex_dump(requete_client)
            requete_client = ajuster_requete(requete_client)
            socket_cible.send(requete_client)
        reponse_cible = obtenir_donnees(socket_cible)
        if reponse_cible:
            print(f"[*] Reçu {len(reponse_cible)} octets du serveur.")
            hex_dump(reponse_cible)
            reponse_cible = ajuster_reponse(reponse_cible)
            socket_client.send(reponse_cible)
        if not requete_client and not reponse_cible:
            socket_client.close()
            socket_cible.close()
            break

Gestion des connexions réseau

Le serveur proxy écoute sur un port local et crée un nouveau thread pour chaque connexion entrante, déléguant le traitement à la fonction de proxy.

def demarrer_serveur(ecoute_hote, ecoute_port, destination_hote, destination_port, recevoir_en_premier):
    serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        serveur.bind((ecoute_hote, ecoute_port))
    except Exception as err:
        print(f"Échec de liaison sur {ecoute_hote}:{ecoute_port} : {err}")
        sys.exit(1)
    serveur.listen(5)
    print(f"En écoute sur {ecoute_hote}:{ecoute_port}")
    while True:
        client_socket, adresse = serveur.accept()
        print(f"Connexion entrante de {adresse[0]}:{adresse[1]}")
        fil = threading.Thread(
            target=gerer_proxy,
            args=(client_socket, destination_hote, destination_port, recevoir_en_premier)
        )
        fil.start()

Validation de l'application

Pour tester le proxy, configurez un environnement avec deux machines virtuelles : l'une exécutant le proxy sur le port 9000, et l'autre agissant comme client et serveur distant. Observez les journaux pour vérifier le relais des données.

Étiquettes: Python TCP sockets Proxy multithreading

Publié le 25 juillet à 01h14