Proyecto

General

Perfil

44 jredrejo
#!/usr/bin/python -tt
# -*- coding: utf-8 -*-

"""Módulo auxiliar para obtener información a través
del comando iwlist, perteneciente a las herramientas
wireless-tools

Francisco Mora Sánchez
IES Maestro Juan Calero
adminies.maestrojuancalero@edu.juntaextremadura.net


"""

import os
import re
import locale
from subprocess import Popen, STDOUT, PIPE, call

# Expresiones Regulares.
_re_mode = (re.I | re.M | re.S)
essid_pattern = re.compile('.*ESSID:"?(.*?)"?\s*\n', _re_mode)
ap_mac_pattern = re.compile('.*Address: (.*?)\n', _re_mode)
channel_pattern = re.compile('.*Channel:?=? ?(\d\d?)', _re_mode)
strength_pattern = re.compile('.*Quality:?=? ?(\d+)\s*/?\s*(\d*)', _re_mode)
altstrength_pattern = re.compile('.*Signal level:?=? ?(\d+)\s*/?\s*(\d*)', _re_mode)
signaldbm_pattern = re.compile('.*Signal level:?=? ?(-\d\d*)', _re_mode)

def to_unicode(x):
""" Convierte una cadena a codificación utf-8. """
# Si ésta es una cadena unicode, la codifica y la devuelve
if not isinstance(x, basestring):
return x
if isinstance(x, unicode):
return x.encode('utf-8')
encoding = locale.getpreferredencoding()
try:
ret = x.decode(encoding).encode('utf-8')
except UnicodeError:
try:
ret = x.decode('utf-8').encode('utf-8')
except UnicodeError:
try:
ret = x.decode('latin-1').encode('utf-8')
except UnicodeError:
ret = x.decode('utf-8', 'replace').encode('utf-8')

return ret

def EjecutaRegex(regex, cadena):
""" ejecuta búsqueda de expresión regular en una cadena """
m = regex.search(cadena)
if m:
return m.groups()[0]
else:
return None

def Ejecuta(comando, include_stderr=False, return_pipe=False,
return_obj=False, return_retcode=True):
""" Ejecuta un comando.

Ejecuta el comando dado, retornando la salida del programa
o un pipe para leer la salida.

argumentos --
comando - Comando a ejecutar
include_std_err - Boleano, especifica si la salida de error debe
ser incluida en el pipe.
return_pipe - Boleano, especifica si el pipe del comando se
devuelve. Si es False, todo lo que devolverá
es la cadena de salida del comando.
return_obj - Si True, Ejecuta devolverá el objeto Popen
para el comando que se ha ejecutado.

"""
if not isinstance(comando, list):
comando = to_unicode(str(comando))
comando = comando.split()
if include_stderr:
err = STDOUT
fds = True
else:
err = None
fds = False
if return_obj:
std_in = PIPE
else:
std_in = None

# Debemos asegurarnos que los resultados del comando ejecutado
# están en inglés, así ajustaremos un entorno temporal.
tmpenv = os.environ.copy()
tmpenv["LC_ALL"] = "C"
tmpenv["LANG"] = "C"

try:
f = Popen(comando, shell=False, stdout=PIPE, stdin=std_in, stderr=err,
close_fds=fds, cwd='/', env=tmpenv)
except OSError, e:
print "Fallo ejecuando comando %s : %s" % (str(comando), str(e))
return ""

if return_obj:
return f
if return_pipe:
return f.stdout
else:
return f.communicate()[0]

def FrecuenciaACanal(frecuencia):
""" Transforma una frecuencia a canal.

Nota: Esta función es una búsqueda en diccionario, por lo que
la frecuencia debe estar en el diccionario para que pueda
devolverse un canal válido.

Parámetros:
frecuencia -- cadena conteniendo la frecuencia

Devuelve:
El número de canal, o None si no se encuentra.
"""
ret = None
freq_dict = {'2.412 GHz': 1, '2.417 GHz': 2, '2.422 GHz': 3,
'2.427 GHz': 4, '2.432 GHz': 5, '2.437 GHz': 6,
'2.442 GHz': 7, '2.447 GHz': 8, '2.452 GHz': 9,
'2.457 GHz': 10, '2.462 GHz': 11, '2.467 GHz': 12,
'2.472 GHz': 13, '2.484 GHz': 14 }
try:
ret = freq_dict[frecuencia]
except KeyError:
print "No se puede determinar el canal para la frecuencia: " + str(frecuencia)
return ret

def get_link_quality(red):
""" Obtiene la calidad del enlace desde la salida iwlist.
"""
try:
[(strength, max_strength)] = strength_pattern.findall(red)
except ValueError:
(strength, max_strength) = (None, None)
if strength in ['', None]:
try:
[(strength, max_strength)] = altstrength_pattern.findall(red)
except ValueError:
# Si el patrón no encuentra coincidencias
# retornamos 101
return 101
if strength not in ['', None] and max_strength:
#print "strength,max",strength,max_strength
return (100 * int(strength) // int(max_strength))
elif strength not in ["", None]:
#print "strength,max",strength,max_strength
return int(strength)
else:
#print "strength,max",strength,max_strength
return None

def ParseAccessPoint(red):
""" Examina una red wifi desde la salida de iwlist.
Parámetros:
red -- cadena que contiene la identificación de la red.
Devuelve:
Un diccionario que contiene las propiedades de la red wifi
examinada.
"""
ap = {}
ap['essid'] = EjecutaRegex(essid_pattern, red)
try:
ap['essid'] = to_unicode(ap['essid'])
except (UnicodeDecodeError, UnicodeEncodeError):
print 'Problema Unicode con el essid de la red actual, ignorando!!'
return None
if ap['essid'] in ['Hidden', '<hidden>', "", None]:
print 'hidden'
ap['oculta'] = True
ap['essid'] = "<hidden>"
else:
ap['oculta'] = False
# Canal - Para interfaces que no tienen un número de canal,
# convertir la frecuencia.
ap['canal'] = EjecutaRegex(channel_pattern, red)
if ap['canal'] == None:
freq = EjecutaRegex(freq_pattern, red)
ap['canal'] = FrecuenciaACanal(freq)
# BSSID
ap['bssid'] = EjecutaRegex(ap_mac_pattern, red)
# Calidad del enlace
# Ajusta strength a -1 si no se encuentra calidad
ap['calidad'] = get_link_quality(red)
if ap['calidad'] is None:
ap['calidad'] = -1
# Signal Strength (only used if user doesn't want link
# quality displayed or it isn't found)
if EjecutaRegex(signaldbm_pattern, red):
ap['intensidad'] = EjecutaRegex(signaldbm_pattern, red)
return ap

def getWirelessInterfaces():
""" Extract wireless device names from /proc/net/wireless.

Returns empty list if no devices are present.

>>> getWirelessInterfaces()
['eth1', 'wifi0']

"""
device = re.compile('[a-z]{2,}[0-9]*:')
ifnames = []

fp = open('/proc/net/wireless', 'r')
for line in fp:
try:
# append matching pattern, without the trailing colon
ifnames.append(device.search(line).group()[:-1])
except AttributeError:
pass

return ifnames

def ObtieneRedes(interface):
""" Obtiene una lista de redes wifi disponibles. La usamos para
obtener via iwlist datos de las redes disponibles. De momento
lo usamos para obtener los valores del calidad e intensidad,
ya que la librería pythonwifi no parece dar los valores correctos
de estos parámetros.

Parámetros:
interface -- Interfaz sobre la que escanear
Devuelve:
Diccionario cuyas claves son ls bssids y cada elemento
es otro diccionario cuyas claves con las características
recogidas por iwlist
"""
cmd = 'iwlist ' + interface + ' scan'
resultado = Ejecuta(cmd)
# Divide las redes, utilizando Cell como punto de división
# de esta forma podemos mirar una red cada vez.
# Los espacios alrededor de ' Cell ' son para evitar el caso
# de que alguien tenga un essid llamado Cell...
redes = resultado.split( ' Cell ' )
# An array for the access points
access_points = []
access_points = {}
for red in redes:
# Solo usa secciones donde haya un ESSID.
if 'ESSID:' in red:
# Añadir la red a la lista de redes
entry = ParseAccessPoint(red)
if entry is not None:
# Normalmente solo tenemos bssids duplicados con redes
# ocultas. Solo nos fijamos en el essid real, para
# que esté en la lista.
if (entry['bssid'] not in access_points or not entry['oculta']):
access_points[entry['bssid']] = entry
return access_points

if __name__ == "__main__":
datos_redes = ObtieneRedes('eth1')
print datos_redes