Créer un proxy personnalisé avec launchd (2/2)

Cet article est la suite de la première partie : "Créer un proxy personnalisé avec launchd (1/2)"

Coder votre proxy personnalisé.

C'est théoriquement l'étape du travail la plus intéressante, puisque c'est ici que l'on peut exprimer toute sa créativité, et surtout apporter la réponse à un de ses problèmes. Comme le programme sera lancé à la demande par launchd, et que c'est ce dernier qui va gérer le socket de communication avec le client, la tâche est simplifiée. En contrepartie, le programme ne peut pas être trop lourd, car des lancements à répétition seraient alors pénalisants pour le reste du système.
Mac OS X est livré avec de nombreux langages de script et de programmation (C, Java, PHP, Python, Ruby, Perl, Shell…). Contrairement à mon habitude, j'ai choisi de coder mon proxy en PHP. De la sorte, je pouvais réutiliser mon code dans un autre contexte au cas où le fonctionnement en proxy ne donnait pas satisfaction.
Voici le squelette de mon script PHP, à titre d'exemple :

#!/usr/bin/php
<?php
// get request from http client
$request = explode(" ", trim(fgets(STDIN)));
$MyGET = $request[1];
// curl request
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $MyGET);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
// cleanup
curl_close($ch);   

// callback
function fetch_author($matches) {
    ../..
    return $matches[1].$matches[2].$author[1]." ".$matches[3];
}

// html parsing
$apresreplace = preg_replace_callback('/.../',"fetch_author",$output);

// output
echo $apresreplace;
?>

J'ai volontairement simplifié et tronqué mon code. La seule chose qui compte est le squelette du programme. Première remarque, le script fonctionne en mode "ligne de commande". C'est du PHP, mais il ne sera pas interprété par Apache, donc les variables intéressantes de $_SERVER, de $_GET, ... n'existent pas. Je récupère la requête du client HTTP en lisant tout simplement la première ligne de STDIN.
Ensuite, j'utilise les fonctions de la librairie CURL pour aspirer le contenu distant qui correspond à l'URL transmise par le client. Puis je fais sur ce contenu un rechercher-remplacer via la fonction preg_replace_callback et via la fonction attachée que j'ai créée (dite fonction de callback).
Les détails ne sont pas importants, à ce stade ce qu'il faut bien comprendre, c'est que mon script de proxy a reçu l'URL demandée par le client. Il a téléchargé le contenu correspondant à cette URL, et il l'a enrichi selon mes besoins. La dernière ligne du script renvoie dans la sortie standard le contenu enrichi. Le client (mon lecteur de flux RSS) reçoit alors la réponse à sa requête.

Lancer le proxy à la demande

Launchd est l'outil idéal pour lancer le proxy dès qu'une connexion est initiée dans sa direction. Le fonctionnement est alors similaire à inetd (ou xinetd). La problématique est la même que dans le cas de la création d'un tunnel ssh à la demande, donc je passe sur les détails.
Voici le fichier plist de configuration qu'il faut charger dans launchd pour activer le proxy :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Debug</key>
        <false/>
        <key>Label</key>
        <string>tld.monsuper.proxy</string>
        <key>ProgramArguments</key>
        <array>
                <string>/chemin/du/programme/de/proxy</string>
        </array>
        <key>Sockets</key>
        <dict>
                <key>Listeners</key>
                <dict>
                        <key>SockNodeName</key>
                        <string>127.0.0.1</string>
                        <key>SockServiceName</key>
                        <string>8080</string>
                        <key>SockType</key>
                        <string>stream</string>
                </dict>
        </dict>
        <key>StandardErrorPath</key>
        <string>/tmp/proxy.err</string>
        <key>inetdCompatibility</key>
        <dict>
                <key>Wait</key>
                <false/>
        </dict>
</dict>
</plist>

Après avoir adapté le Label, le chemin du programme, le port de connexion, et éventuellement le fichier de stderr, il faut enregistrer ce fichier plist dans ~/Library/LaunchDaemons/ (pour vous seul) ou dans /Library/LaunchDaemon/ (pour tous les utilisateurs de la machine). Ensuite il faut le charger via launchctl load /chemin/du/fichier.plist.

C'est terminé. Toutes les briques du proxy personnalisé sont en place. Le client (si il honore bien les préférences systèmes) utilisera le proxy désigné dans le fichier .PAC. Launchd recevra la requête et la transmettra au programme du proxy. Ce dernier fera les traitements sur les données téléchargées, et renverra le résultat au client.

edit : style et coquilles
edit : ajout de l'IP 127.0.0.1 pour le bind, ça évite que le proxy puisse êtes utilisé par n'importe qui sur le réseau… :)

Related posts

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.