«

»

déc 11 2009

Open Wifi Auto Connect

Je ne sais pas si vous utilisez souvent des réseaux wifi qui filtrent l’accès en utilisant un portail de capture web pour vous authentifier ? Personnellement, j’utilise régulièrement le service FreeWifi de Free…

Le principe de ces services est que le pare-feu bloque toutes les connexions qui ne sont pas authentifiés. Pour s’authentifier, il faut utiliser un navigateur Web, tenter de naviguer vers un site quelconque pour se faire rediriger vers la page d’authentification puis y entrer ses identifiants…

Comme je suis toujours plus fainéant, je viens de faire un petit script qui se charge de faire toutes ses étapes automatiquement. Le principe est de surveiller les connexions aux différents réseaux sans fils, et dès qu’un réseau est reconnu (que son nom est dans une liste), il se charge tout seul de récupérer la page du portail, puis de la renvoyer accompagnée des identifiants de connexion.

Utilisation :

  1. Téléchargez le fichier.
  2. Copiez le dans le dossier de votre choix.
  3. Créez le fichier de configuration. Il se nomme .OpenWifiAutoConnect, doit être placer dans votre dossier personnel (~) et est au format .ini. Les sections correspondent aux nom des réseaux. Les clefs correspondent au champs du formulaire ou il faut entrer une valeur. Par exemple:
    [UnReseau]
    email=test@example.com
    pass=azerty
    
    [FreeWifi]
    login=1234567890
    password=secret
    
  4. Ajoutez le programme au démarrage de votre session.
  5. Vérifier que les paquets (ubuntu / debian) suivant sont installé : python-dbus, python-notify et python-beautifulsoup
  6. Redémarrer votre session pour que le programme se lance
  7. Connectez vous à un réseau wifi et attendez que la magie s’opère. Une popup de notification vous préviendra du succès de l’authentification.

Comment ça marche ?

Avant tout, voici le code :

#!/usr/bin/env python

################################################################################
# OpenWifiAutoConnect - Stephane PUYBAREAU (puyb <at> puyb <dot> net) - 2008   # 
# Fully automated authentication to capture portal based wifi networks.        # 
# Config file: ~/.OpenWifiAutoConnect - Format: ini                            #
# Section are network SSID (names)                                             #
# key / value pair define user submited information (based on the html form)   #
#                                                                              # 
# This software is provided under the terms of the GPL v3 licence.             # 
# See http://www.gnu.org/licenses/gpl.html for more information                # 
#                                                                              # 
# This software use python dbus bindings, pynotify and BeautifulSoup modules   #
################################################################################


# The url the program will try to open hoping to be redirected to the portal
URL = 'http://perdu.com/'

import sys
import gobject
import dbus
import dbus.mainloop.glib
import urllib2, urllib
from BeautifulSoup import BeautifulSoup
import re
import ConfigParser
import os
import pynotify

def properties_changed_signal_handler(props):
    if not props.has_key('ActiveConnections'):
        return
    for device_path in props['ActiveConnections']:
        device = bus.get_object('org.freedesktop.NetworkManager', device_path)
        device_props = device.GetAll("org.freedesktop.NetworkManager.Connection.Active", dbus_interface="org.freedesktop.DBus.Properties")
        if device_props['Default']:
            return
        ap_path = device_props['SpecificObject']
        if ap_path.startswith('/org/freedesktop/NetworkManager/AccessPoint/'):
            ap = bus.get_object('org.freedesktop.NetworkManager', ap_path)
            ssid = ap.Get("org.freedesktop.NetworkManager.AccessPoint", "Ssid", dbus_interface="org.freedesktop.DBus.Properties")
            ssid = ''.join([chr(c) for c in ssid])
            if ssid not in config.sections():
                return
            print ssid
            device.connect_to_signal("PropertiesChanged", device_properties_changed_signal_handler(ssid), dbus_interface="org.freedesktop.NetworkManager.Connection.Active")

def device_properties_changed_signal_handler(ssid):
    def handler(props):
        if not props.has_key('State'):
            return
        if props['State'] != 2:
            return
        print ssid
        section = dict(config.items(ssid))

        if login(section):
            txt = "Successfully logged on " + ssid
        else:
            txt = "Failled to log on " + ssid
        n = pynotify.Notification("Open Wifi Auto Connect", txt, "dialog-warning")
        n.set_urgency(pynotify.URGENCY_NORMAL)
        n.set_timeout(10)
        #n.add_action("clicked","Button text", callback_function, None)
        n.show()
    return handler


def login(values):
    # build an http fetcher that support cookies
    opener = urllib2.build_opener( urllib2.HTTPCookieProcessor() )
    urllib2.install_opener(opener)

    # try to open the portal page
    f = opener.open(URL)
    data = f.read()
    redirect_url = f.geturl().split('?')[0]
    f.close()
    if redirect_url == URL:
        return # Our request wasn't hijacked by the portal (maybe the wifi network isn't the default connection)

    # parse the portal page
    soup = BeautifulSoup(data)
    form = soup.find('form')
    if not form:
        return # There's no form on this page
    
    # creating the post values
    login_post = {}
    for input in form.findAll('input'):
        if input.has_key('name'):
            default = ''
            if input.has_key('type') and input['type'] == 'checkbox':
                default = 'on'
            login_post[input['name']] = input.has_key('value') and input['value'] or default

    login_post.update(values)

    # guessing the post url
    if not form.has_key('action'):
        url = redirect_url
    elif not form['action'].startswith('/'):
        url = '/'.join(redirect_url.split('/')[:-1]) + '/' + form['action']
    else:
        url = '/'.join(redirect_url.split('/')[:3]) + form['action']

    # GET ou POST ?
    postBody = None
    if form.has_key('method') and form['method'].lower() == 'post':
        postBody = urllib.urlencode(login_post)
    else:
        url += '?' + urllib.urlencode(login_post).replace(' ', '+')

    # submit the form
    f = opener.open(url, postBody)
    data = f.read()
    f.close()

    # Test if the login was a success
    f = opener.open(URL)
    data = f.read()
    status = f.geturl() == URL
    f.close()
    return status

if __name__ == '__main__':
    config = ConfigParser.RawConfigParser()
    config.read(os.path.expanduser('~/.OpenWifiAutoConnect'))

    pynotify.init( "Open Wifi Auto Connect" )

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    bus = dbus.SystemBus()
    nm = bus.get_object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
    nm.connect_to_signal("PropertiesChanged", properties_changed_signal_handler, dbus_interface="org.freedesktop.NetworkManager")

    loop = gobject.MainLoop()
    loop.run()

Le programme utilise D-Bus pour demander au NetworkManager de lui signaler chaque changement au niveau du réseau. A chaque changement, il tente de déterminer, toujours grâce à D-Bus, si ce reseau est un réseau Wifi et quel est son SSID. Si le réseau est dans le fichier de configuration, il attend que que le réseau soit correctement connecté pour lancer la fonction login avec les paramètres de connexion.

Cette fonction commence par mettre en place un client HTTP supportant les cookies (il est possible (FreeWifi le fait) que le portail nous envoie des cookies). Il tente d’ouvrir une page vers un site quelconque (en l’occurrence perdu.com. Le client supporte les redirections HTTP, il teste donc si la requette a été redirigé. Si c’est le cas, le HTML est analysé avec Beautiful Soup pour récupérer toutes les informations sur le formulaire. Elle sont modifiées avec les informations de connexion provenant du fichier de configuration. Puis, le formulaire est envoyé. Enfin, il vérifie si l’authentification à réussi en tentant de joindre à nouveau perdu.com…

Limitations :

  • Ce programme ne fonctionnera pas si le SSID du réseau n’est pas toujours le même. J’ai cru comprendre que les réseaux Fon n’avait pas tous le même nom, si c’est le cas, ça ne fonctionnera pas (enfin, pas pour tous les réseau Fon, mais juste pour ceux dont on a donner les noms.
  • Ce programme ne fonctionnera pas non plus si les noms des champs à remplire dans le formulaire sont variables.
  • Enfin, ça ne fonctionnera pas non plus si il y a un captcha (une joli image où l’on doit jouer à Champolion pour deviner ce qu’il ya d’inscrit).

Voilà, j’espère que ça va servir à quelqu’un (d’autre que moi)… Si il y a un problème, n’hésitez pas à laisser un commentaire… Et si il n’y a pas de problème, vous pouvez aussi laisser un commentaire ;-)

20 comments

Passer au formulaire de commentaire

  1. itichi

    sympas le programme, sa fait longtemps que je voulais coder ça, je vais donc le tester en Belgique.

    Par contre, il est peut-être utile d’ajouter une requête automatique vers un site genre perdu tout les X temps, car les freewifi et autres, ont la fâcheuse tendance à fermer la session s’il n’y a pas de transferts de données tout les X temps malgré qu’on reste connectés au réseau ;)

    Une simple requête ping tout les X temps devrait faire l’affaire.

    Beau travail ;)

  2. Julien

    Hello !
    Super idée, j’ai une appli sur android pour le faire, et je cherchais justement la même chose pour mon eeepc.
    Par contre tu as un moyen pour vérifier le certificat ssl de la page web ?
    Il serait dommage de donner les login/pass à n’importe quel portail qui s’annonce sous le bon SSID…
    Je n’ai malheureusement pas les compétences pour faire les modifs moi même.

  3. manatlan

    J’ai codé le même truc, sur le même principe … et je l’utilise avec succès depuis 6 mois.
    Par contre, je ne l’ai jamais releasé ;-)

    Pour les FON (et je suis membre fon), je crois pouvoir dire que les reseaux fon commencent toujours par "FON_" …

    Par contre, je l’ai jamais codé dans le mien ;-) … n’utilisant que le freewifi now …

  4. Bristow

    Bonjour et merci pour ce script que je vais utiliser sans modération quand cela fonctionnera :)

    J’ai suivi la procédure mais j’ai du louper quelque chose. J’ai téléchargé le fichier .py et mis dans mon repertoire ~/scripts/. Puis j’ai crée avec gedit le fichier .OpenWifiAutoConnect. Pas contre, tu notes, il faut qu’il soit au format .ini, cela veut dire quoi ? Il faut une extension .ini ? il faut le formater avec une commande ?

    Merci de la précision.

    que j’aimerai savoir développer…

  5. slamp

    Bonjour,

    Teste sous gentoo avec:
    dbus-python 0.83.0-r1
    beautifulsoup 3.0.7
    notify-python 0.1.1-r1

    J’ai l’erreur suivante:
    Traceback (most recent call last):
    File "./OpenWifiAutoConnect.py", line 136, in <module>
    nm = bus.get_object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
    File "/usr/lib64/python2.6/site-packages/dbus/bus.py", line 244, in get_object
    follow_name_owner_changes=follow_name_owner_changes)
    File "/usr/lib64/python2.6/site-packages/dbus/proxies.py", line 241, in __init__
    self._named_service = conn.activate_name_owner(bus_name)
    File "/usr/lib64/python2.6/site-packages/dbus/bus.py", line 183, in activate_name_owner
    self.start_service_by_name(bus_name)
    File "/usr/lib64/python2.6/site-packages/dbus/bus.py", line 281, in start_service_by_name
    ‘su’, (bus_name, flags)))
    File "/usr/lib64/python2.6/site-packages/dbus/connection.py", line 622, in call_blocking
    message, timeout)
    dbus.exceptions.DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.NetworkManager was not provided by any .service f@les

    @Bristow, un fichier au format ini a la structure suivante:
    [section]
    variable=valeur

    Il y a un exemple en debut de page ( 3/ )

  6. neFAST

    Ca mercherait sur un Ipod Touch ? Python a été porté, donc …
    Quelqu’un a testé ?

  7. Puyb

    @slamp > Il semblerait que tu n’aies pas NetworkManager. Le script interroge NetworkManager pour connaitre l’état du réseau. Tu utilises quelle distribution Linux ?

    @neFATS > Dans sa version actuelle, le script a besoin du NetworkManager pour connaitre l’état du réseau. Ce qui veut dire qu’il ne fonctionne que sous Linux (et peut être *BSD, je ne sais si ils utilisent aussi NetworkManager). Je ne sais pas comment détecter les changements lié au réseau sous MacOSX. Si tu sais comment faire, dan ce cas, tu peux utiliser la méthode login du script pour lancer une procédure de login.

     import OpenWifiAutoConnect OpenWifiAutoConnect.login({ 'user': 'moi', 'pass': 'secret'}) 

    @manatlan > Je n’ai pas Fon, mais je suis en train de tester avec un amis pour le faire marcher avec. Entre autre, je n’avais pas prévu que l’attribut action du formulaire puisse être une URL complète (avec http://). Je publierai un correctif dès que ça marchera…

    @Julien > pas bête la vérification du SSL, je vais regarder ça… Est ce que tu le fait dans ton appli androïd ? Et si oui, comment ?

    @itichi > Je vais réfléchir pour le hearbeat… Je n’y avais pas pensé, il n’y en a pas besoin pour FreeWifi.

    @Bristow > Un fichier INI, c’est juste une façon de le formater (avec un éditeur de texte). Les utilisateurs de Windows 3.1 le connaisse bien ;-)

  8. Julien

    @Puyb : J’ai l’exemple (en java) pour FreeWIFI (pour Android) : code.google.com/p/freewif…

    En java a priori c’est simple de vérifier le certificat. Ca t’aide ?

  9. bricowifi

    bein avec urlib, pour sécuriser le truc, y a l’option chiante qui consiste à utiliser m2crypto, ducoup ca fait une dépendance en plus. Sinon vite fait et secure un truc du style:
    si ssid freewifi verification ndd, si ssid neuf vérification ndd. Et pour fon l’a c’est chiant car les ssid varient, donc pas de vérification,ou autre idée que je n’ai pas en tête.
    Après c’est une idée comme ca :)

  10. Puyb

    J’ai trouvé un truc en utilisant le binding python de cURL pour vérifier la validité du certificat…

    Sinon, je pense donner la possibilité de mettre une expression régulière pour matcher le SSID du réseau, mais ça impliquerait de changer le format du fichier de config… Je me demande si je ne vais pas tout simplement le mettre un fichier python avec un simple dictionnaire…

  11. fernetbianca

    Bonjour à tous,
    J’ai installé ubuntu sur un eeepc (il s’agit de la version "ubuntu netbook remix 9.10"). J’ai copier le fichier python à ~ & le fichier Open(…).ini avec:
    [FreeWifi]
    login=blabla
    password=blabla…

    j’ai fais en sorte que Open(…).py démarre à l’ouverture de session, mais je n’ai pas de connexion automatique. J’ai manqué une étape?

    Merci & joyeux noël à tous & à toutes…

  12. Jase

    Bonjour,

    Bonne année à Toutes et Tous,

    Et pour Windaube ?

  13. Puyb

    fernetbianca > Le nom du fichier de config n’est pas « OpenWifiAutoConnect.ini », mais « .OpenWifiAutoConnect » (sans les guillemets…)

    Jase > Pas de version pour Windows, et il n’y en aurra jamais de ma part, je ne l’utilise pas (il parait qu’il y a des gens qui y arrivent…)… Si, comme tu l’écris, tu trouves que c’est une daube, tu n’as qu’a arrêter de l’utiliser ! Maintenant, tu peux reprendre mon code et l’adapter à Windows. Tu as juste à refaire, si c’est possible, la partie qui détecte un changement au niveau du wifi. Pour la connexion, tu peux reprendre la fonction login, elle devrait être compatible sans changement… La seul chose que je te demande, c’est de ne pas garder pour toi tes modifications et de les redistribuer (la GPL ne t’y oblige pas)…

  14. manatlan

    Je viens aux nouvelles … comment va le script ?
    est-il capable de se connecter aux FONs now ?

    sinon, on pourrait aussi le GUI’iser avec grun : pypi.python.org/pypi/grun…
    histoire de manipuler tout par GUI … (genre une icon dans la systray (pour afficher l’état), et des boites de cnx pour les authent))
    je peux aider évidemment … ayant déjà développer un truc du style pour mon eee.

  15. Puyb

    Je n’ai pas eu le temps de m’en occuper.

    Pour le GUI, je ne suis pas convaincu par l’intérêt d’une icône de notification (on a déjà trop !). Pour notifier de l’état du réseau, AMHA l’icône du network manager + les notifications du script suffisent… Après, pourquoi pas un petit GUI pour la configuration, mais ce n’est pas dans mes priorités… D’ailleurs, je me demande si ça ne serait pas plus élégant de faire ça sous forme d’une extension Firefox qui analyse le contenu de la page pour remplir le fichier de configuration ? Si quelqu’un se sent le courage de faire ça ;-)

    Enfin, j’ai eu l’occasion de me rendre dans un McDonalds. Pour activer le wifi gratuit, il ne fallait pas soumettre un formulaire comme je l’avait présumé, mais juste cliquer sur un lien… Il faudrait que je prévois la nouvelle version en conséquence. Je pense notamment à donner la possibilité de préciser un sélecteur CSS qui indique sur quel lien / bouton il faut cliquer dans le fichier de configuration…

  16. dayedaye

    quelqu’un t’il essayé ce programme avec le portable twin tact de neuf telecom.
    je l’utulise souvent avec net appel , pour une connexion sur free wifi.
    merci pour tous tout info , je suis preneur .

  17. manatlan

    I did it …

    Bon finallement, j’ai pris le temps de refaire ma version, et j’ai packager tout ça.
    http://www.manatlan.com/blog/ano...

    C’est du pygtk, dans la systray, ça fait peu ou prou pareil … et il a du GUI
    Il est plugin’able : et donc, ça devrait être simple d’ajouter d’autres hotspots …

  18. jarreboum

    Bonjour, je viens de découvrir votre script et il est très pratique lorsqu’on se déplace régulièrement.

    Malheureusement je n’arrive pas à le faire fonctionner.
    J’ai téléchargé votre fichier OpenWifiAutoConnect.py que j’ai rendu exécutable (pour tester sans redémarrer)
    J’ai créé .OpenWifiAutoConnect dans mon dossier personnel
    J’ai vérifié qu’il était bien nommé au bon endroit en lançant gedit ~/.OpenWifiAutoConnect
    Lorsque je me connecte au réseau FreeWifi, après un instant j’ai: Failed to log on FreeWifi.
    J’ai vérifié les identifiants du fichier en me connectant manuellement, aucun problème. Pas de faute, j’ai tout fait par copier coller.

    J’imagine que mon problème vient de mon fichier .OpenWifiAutoConnect, mais je n’arrive pas à le cerner.
    Problème d’authorisations? Je n’ai pas de retour particulier lorsque je lance OpenWifiAutoConnect.py en ligne de commande (Je ne le vois même pas dans la liste des processus).

    Bref j’ai essayé de cerner le problème avec mes petits moyens, mais j’arrive à ma limite je crois.

  19. jarreboum

    Bonjour, en remplaçant l’adresse du INI dans le script cela fonctionne.
    J’ai remplacé ~/.OpenWifiAutoConnect par /home/jarreboum/.OpenWifiAutoConnect.
    J’utilise Ubuntu 10.04.

  20. Puyb

    Heureux que tu ais réussi à le faire fonctionner, par contre, je reste étonné de la solution que tu as utilisé… Lançais-tu le script en tant que "jarreboum" ?

Laisser un commentaire

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

Vous pouvez utiliser les balises HTML suivantes : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>