commit ae406076f7a5691a9922a9b66d29c29f03fb2892 Author: Tomek Dubrownik Date: Thu Jan 26 19:58:09 2012 +0100 initial commit diff --git a/at.py b/at.py new file mode 100644 index 0000000..e7e58fe --- /dev/null +++ b/at.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +import logging +import sqlite3 +import threading +import traceback +from datetime import datetime +from wsgiref import simple_server +from pesto import Response, dispatcher_app +from time import sleep, time +from collections import namedtuple +from jinja2 import Environment, FileSystemLoader + +import config + +dispatcher = dispatcher_app() +logger = logging.getLogger() +env = Environment(loader=FileSystemLoader('templates')) +conn = None +updater = None + +from functools import wraps +def render(filepath): + def decorator(f): + @wraps(f) + def func(request, *a, **kw): + template = env.get_template(filepath) + data = f(request, *a, **kw) + return Response([template.render(**data)]) + return func + return decorator + +def strfts(ts, format='%d/%m/%Y %H:%M'): + return datetime.fromtimestamp(ts).strftime(format) +env.filters['strfts'] = strfts + +def setup_db(): + conn = sqlite3.connect(config.db) + conn.row_factory = sqlite3.Row + return conn + +DeviceInfo = namedtuple('DeviceInfo', ['hwaddr', 'owner', 'ignored']) +User = namedtuple('User', ['login', 'passwd', 'url']) + +def get_device_info(conn, hwaddr): + return list(get_device_infos(conn, (hwaddrs,)))[0] + +def get_device_infos(conn, hwaddrs): + stmt = '''select hwaddr, name, ignored, login, url from + devices left join users on devices.owner = users.userid + where devices.hwaddr in (''' + ','.join(['?'] * len(hwaddrs)) + ')' + for row in conn.execute(stmt, hwaddrs): + owner = User(row['login'], None, row['url']) if row['login'] else None + ignored = row['ignored'] + yield DeviceInfo(row['hwaddr'], owner, ignored) + +class Updater(threading.Thread): + def __init__(self, cap_file, timeout, *a, **kw): + self.cap_file = cap_file + self.timeout = timeout + self.lock = threading.Lock() + self.active = {} + threading.Thread.__init__(self, *a, **kw) + def purge_stale(self): + now = time() + for addr, atime in self.active.items(): + if now - atime > self.timeout: + del self.active[addr] + def get_active_devices(self): + self.lock.acquire() + self.purge_stale() + r = dict(self.active) + self.lock.release() + return r + def update(self, hwaddr): + self.lock.acquire() + self.active[hwaddr] = time() + self.lock.release() + def run(self): + while True: + try: + f = open(self.cap_file, 'r', buffering=0) + logger.info('Updater ready on cap file %s', self.cap_file) + while True: + hwaddr = f.readline().strip() + if not hwaddr: + break + self.update(hwaddr) + logger.info('logged dhcp request from %s', hwaddr) + logging.warning('Cap file %s closed, reopening', self.cap_file) + except Exception as e: + logging.error('Updater got an exception:\n' + \ + traceback.format_exc(e)) + sleep(10.0) + +@dispatcher.match('/', 'GET') +@render('main.html') +def now_at(request): + devices = updater.get_active_devices() + device_infos = list(get_device_infos(conn, devices.keys())) + device_infos.sort(key=lambda di: devices.__getitem__) + users = list(dict((info.owner, devices[info.hwaddr]) for info in device_infos + if info.owner and not info.ignored).iteritems()) + users.sort(key=lambda (u, a): a, reverse=True) + unknown = set(devices.keys()) - set(d.hwaddr for d in device_infos) + return dict(users=users, unknown=unknown) + +port = 8080 +if __name__ == '__main__': + print env.list_templates() + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler()) + conn = setup_db() + updater = Updater(config.cap_file, config.timeout) + updater.start() + server = simple_server.make_server('', port, dispatcher) + server.serve_forever() diff --git a/cap.py b/cap.py new file mode 100644 index 0000000..7651e6c --- /dev/null +++ b/cap.py @@ -0,0 +1,30 @@ +import logging +import pcapy +import struct + +interface = 'wlan0' +target = './dhcp-cap' +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) +logger.addHandler(logging.StreamHandler()) + +def hwaddr_ascii(packet): + # picking up MAC directly from ethernet frame + return ':'.join('%02x' % ord(c) for c in packet[6:12]) + +def capture_dhcp(itf): + f = open(target, 'w') + reader = pcapy.open_live(itf, 4096, False, 5000) + reader.setfilter('udp dst port 67') + def callback(header, packet): + hwaddr = hwaddr_ascii(packet) + logger.info('Captured dhcp request from %s', hwaddr) + f.write(hwaddr + '\n') + f.flush() + try: + while True: + reader.dispatch(1, callback) + except KeyboardInterrupt: + pass + +capture_dhcp('wlan0') diff --git a/config.py b/config.py new file mode 100644 index 0000000..f3d3033 --- /dev/null +++ b/config.py @@ -0,0 +1,4 @@ +db = './at.db' +cap_file = './dhcp-cap' +timeout = 300 + diff --git a/dbsetup.sql b/dbsetup.sql new file mode 100644 index 0000000..441a266 --- /dev/null +++ b/dbsetup.sql @@ -0,0 +1,13 @@ +create table users ( + userid integer primary key, + login varchar(50) unique not null, + pass character(64), + url varchar(300) +); + +create table devices ( + hwaddr character(17) primary key, + name varchar(50), + owner integer references users(userid), + ignored boolean +); diff --git a/templates/main.html b/templates/main.html new file mode 100644 index 0000000..9b0769c --- /dev/null +++ b/templates/main.html @@ -0,0 +1,14 @@ + +Recently at hackerspace: + +
+There are {{ unknown|length }} unknown devices operating. +