#! /usr/bin/env python # -*- encoding: iso-8859-15 -*- # Gestion du portail captif : # - lecture d'une socket pour les IP à ajouter # - lecture du fichier de lease DHCP pour les expirer import os from popen2 import Popen4 import sys import time from syslog import openlog, syslog, LOG_ERR, LOG_LOCAL4, LOG_INFO, LOG_LOCAL3, LOG_PERROR, LOG_PID from lease import Leases from select import select class Server: """Serveur (sur socket) gérant le hotspot""" def __init__(self, socket): """Initialise le serveur. Il écoutera sur le pipe nommé `socket'.""" # syslog openlog("hotspot.py", LOG_PERROR | LOG_PID) syslog(LOG_INFO | LOG_LOCAL4, "Demarrage du demon hotspot") # Initialisation de la socket self._socket_name = socket self._dontclean = False os.mkfifo(socket, 0600) self.socket = file(socket, "a+") def __del__(self): try: self.socket.close() except: pass if not self._dontclean: os.unlink(self._socket_name) def execute(self,cmd): """Exécute une commande. Gère les erreurs par log dans syslog.""" p = Popen4(cmd) p.tochild.close() out = p.fromchild.read() p.fromchild.close() exit = os.WEXITSTATUS(p.wait()) if exit: # Une erreur a eu lieu syslog(LOG_ERR | LOG_LOCAL4, "Error %d executing `%s' : %s" % (exit, cmd.strip(), out.strip())) else: return out def add_ip(self,ip): """Ajout d'une IP au firewall""" cmd = "pfctl -t clients_hotspot_autorises -Tadd %s" % ip if self.execute(cmd): syslog(LOG_INFO | LOG_LOCAL3, "Nouveau client hotspots: %s" % ip) def del_ip(self,ip): """Retire une règle du firewall""" cmd = "pfctl -t clients_hotspot_autorises -Tdelete %s" % ip if self.execute(cmd): syslog(LOG_INFO | LOG_LOCAL3, "Suppression du client: %s" % ip) def list_ips(self): """Liste les IP actuellement dans le firewall""" cmd = "pfctl -t clients_hotspot_autorises -Tshow" ips = self.execute(cmd) if not ips: return [] return filter(lambda y: y.strip() != '', map(lambda x: x.strip(), ips.split("\n"))) def prune_ips(self): """Vire les IP qui ne sont plus utilisées""" l = Leases().leases deleted = False for ip in self.list_ips(): match = filter(lambda x: x.ip == ip, l) if not match: # Plus de bail du tout self.del_ip(ip) deleted = True else: # On vérifie si le bail n'a pas expiré depuis plus de une minute if match[0].end < time.gmtime(time.time()-60): self.del_ip(ip) deleted=True if deleted: syslog(LOG_INFO | LOG_LOCAL3, "Clients hotspots: %s" % (", ".join(self.list_ips())) or "aucun") def daemonize(self): """Passe en arrière plan""" # Fork if os.fork() > 0: self._dontclean = True sys.exit(0) # Bon répertoire os.chdir("/") # Masque indépendant de l'utilisateur os.umask(022) # Chef de file os.setsid() # Second fork pour avoir son groupe if os.fork() > 0: self._dontclean = True sys.exit(0) # Redirection des E/S si = file('/dev/null', 'r') so = file('/dev/null', 'a+') se = file('/dev/null', 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) def start(self): """Boucle principale""" s = self.socket.fileno() while True: # On attend du monde sur la socket result = select([s],[],[],60)[0] if result: # On a quelqu'un, on lit une ligne ip = self.socket.readline().strip() self.add_ip(ip) # Dans tous les cas, on vire les IP inutiles self.prune_ips() if __name__ == "__main__": s = Server("/tmp/hotspot.socket") s.daemonize() s.start()