commit 50faf05cd96089e5bed11bc027bce13dbf25a50c Author: Sebastian Lohff Date: Sun Oct 30 01:22:45 2011 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d18402d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pyc +.*.swp diff --git a/Readme b/Readme new file mode 100644 index 0000000..80d29b1 --- /dev/null +++ b/Readme @@ -0,0 +1,5 @@ + + + +Documentation + http://www.kernel.org/doc/Documentation/networking/tuntap.txt diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..84072e2 --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +from ether2any import Ether2Any +import helper diff --git a/ether2any.py b/ether2any.py new file mode 100644 index 0000000..864348f --- /dev/null +++ b/ether2any.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +import commands +import select +import pytap +import logging +from pytap import TapDevice + +class Ether2Any: + """ Baseclass for writing arbitrary Ethernet/IP Tunnels using TUN/TAP device. + + This class handles a TUN/TAP devices and runs a select loop for it and, + if given, a set of sockets. To use this class at least sendToNet() has + to be implemented by a subclass. """ + def __init__(self, tap=True, readSockets=[]): + """ Constructor for Ether2Any. + + isTap defines if the managed device should be a tap (ethernet tunnel) + or a tun (IP tunnel). """ + self.readSockets = readSockets + # create device + self.dev = TapDevice(tap=tap) + + self.timeout = None + + def setupLogging(self, name): + l = logging.getLogger(name) + fmt = logging.Formatter("%(asctime)s - [%(levelname)s] (%(name)s) - %(message)s") + ch = logging.StreamHandler() + ch.setFormatter(fmt) + l.addHandler(ch) + + return l + + def addSocket(self, sock): + """ Add socket to readSockets set. """ + self.readSockets.append(sock) + + def delSocket(self, socket): + """ Remove socket from readSockets set. """ + try: + self.readSockets.remove(socket) + except ValueError: + pass + + # outgoing data + def sendToNet(self, packet): + """ This function has to be implemented to handle outgoing + data (read from the TUN/TAP device) """ + raise NotImplementedError("You need to overload sendToNet()") + + # incoming data + def sendToDev(self, sock): + """ This function has to be implemented to handle incoming + data which is read from the extra sockets (self.readSockets). + It will not be called when no extra readSockets are specified. """ + raise NotImplementedError("You need to overload sendToDev()") + + def setTimeout(self, t): + """ Set select timeout. """ + self.timeout = t + + def callAfterSelect(self): + """ Will be called as last operation of the mainloop when + handling of the select result / the select timeout is hit. + """ + pass + + def run(self): + """ Run main select-loop. """ + try: + while True: + sockets = [self.dev.getFD()] + self.readSockets + (readFDs, _, _) = select.select(sockets, [], [], self.timeout) + for readFD in readFDs: + if readFD == self.dev.getFD(): + self.sendToNet(self.dev.read()) + elif readFD in self.readSockets: + self.sendToDev(readFD) + self.callAfterSelect() + except KeyboardInterrupt: + self.quit() + self.dev.close() + + def quit(self): + """ Will be called after the run-select() and its processing is done. """ + pass + diff --git a/helper.py b/helper.py new file mode 100644 index 0000000..ffa639e --- /dev/null +++ b/helper.py @@ -0,0 +1,24 @@ +def getSrcMacFromPkt(packet): + if len(packet) < 16: + return None + return packet[10:16] + +def getDstMacFromPkt(packet): + if len(packet) < 10: + return None + return packet[4:10] + +def binToHexStr(binmac): + return "".join(["%02x" % ord(i) for i in binmac]) + +# checks if packet is a broadcast packet +def isBroadcast(packet): + binmac = getDstMacFromPkt(packet) + # normal broadcast + if binmac == '\xff\xff\xff\xff\xff\xff': + return True + # v6 multicast + if binmac.startswith('\x33\x33'): + return True + return False + diff --git a/pytap.py b/pytap.py new file mode 100644 index 0000000..7dc19b2 --- /dev/null +++ b/pytap.py @@ -0,0 +1,114 @@ +from fcntl import ioctl +import subprocess +import os +import struct + + +class TapDevice: + """ TUN/TAP device class """ + + # magic numbers and structlayout + TUNSETIFF = 0x400454ca + IFF_TUN = 0x0001 + IFF_TAP = 0x0002 + DEVPATH = "/dev/net/tun" + _ifreq = "16sh" + + def __init__(self, name='', tap=True, conf=None): + """ Constructor for the device. + + name - the device name, use a %d for a generated device numer + tap - if this device should be a tap device + conf - conf to pass to the ifconfig function of this class + (if None ifconfig() won't be called) + """ + self._mode = (tap and self.IFF_TAP) or self.IFF_TUN + self._fd = None + self._name = None + self._tap = tap + self._mac = None + self._mtu = 1500 + self.conf = conf + + if name == '': + self._nametpl = (tap and "tap%d") or "tun%d" + + self._createDev() + if self.conf: + self.ifconfig(**conf) + + def _createDev(self): + if self._fd: + self.close() + + self._fd = os.open(self.DEVPATH, os.O_RDWR) + ifreq = struct.pack(self._ifreq, self._nametpl, self._mode) + ret = ioctl(self._fd, self.TUNSETIFF, ifreq) + # retmode should be the same as self._mode + (retname, retmode) = struct.unpack(self._ifreq, ret) + self._name = retname.strip("\x00") + + def _ifconfig(self, params): + args = ["/sbin/ifconfig"] + params + ret = subprocess.Popen(args).wait() + if ret != 0: + raise PyTapException("Command '%s' did not return 0" % (" ".join(args),)) + + def ifconfig(self, **kwargs): + """ Calls ifconfig for the device. + All arguments will be passed to ifconfig. Use 'address' for the device address. + E.g. device.ifconfig(address="12.34.56.78", mtu=1500) + """ + args = [self._name] + if kwargs.has_key("address"): + args.append(kwargs["address"]) + del(kwargs["address"]) + args = reduce(lambda l, key: l+[key, str(kwargs[key])], + kwargs, args) + self._ifconfig(args) + + def getMac(self): + """ Get the device mac """ + # this "could" be buffered, but we never know who when changed the mac + # ==> we re-get the mac on every request + proc = subprocess.Popen("LC_ALL=C /sbin/ifconfig %s|head -n 1|egrep -o '([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}'" % self.getName(), shell=True, stdout=subprocess.PIPE) + mac = proc.stdout.read().strip() + return mac + + def up(self): + """ Bring the device up """ + self._ifconfig([self._name, "up"]) + + def down(self): + """ Bring the device down """ + self._ifconfig([self._name, "down"]) + + def getFD(self): + """ Get the device file descriptor (e.g. to use in select()) """ + return self._fd + + def getName(self): + """ Get the (real) name of the device """ + return self._name + + def read(self): + """ Read a packet from the device """ + readSize = self._mtu + if self._tap: + # don't forget the ethernet frame (not included in MTU) + readSize += 18 + data = os.read(self._fd, self._mtu) + return data + + def write(self, data): + """ Write a packet to the device """ + os.write(self._fd, data) + + def close(self): + """ Close the device """ + os.close(self._fd) + self._fd = self._name = None + +class PyTapException(Exception): + pass + diff --git a/tunnel/ircvpn/conf.py b/tunnel/ircvpn/conf.py new file mode 100644 index 0000000..53a9a58 --- /dev/null +++ b/tunnel/ircvpn/conf.py @@ -0,0 +1,83 @@ +# ___________________________________________________ +# | | +# | ircvpn - irc virtual public network configuration | +# |___________________________________________________| + +# config options +# IRC +# - server (ip, port, ssl) +# - channel +# - nick prefix +# - maximum line length +# Network +# - device: ip, netmask, mtu +# - dhclient instead of static ip? +# - routing? +# - dns? +# Tunnel +# - security settings +# - mode (hub or switch) + +import os + +Conf = { + # ======== network settings ======== + # ipsettings for the device + 'devname': '', + 'network': + { + 'address': '10.10.10.74', + 'netmask': '255.255.255.0', + #gateway: '', + 'mtu': 1400, + }, + + # hubbed ("HUB")/switched("SWITCH") Network + # HUB: communicate only over broadcastchan + # SWITCH: use query for non broadcast packages + 'mode': "HUB", + + # ======== IRC settings ======== + # irc-server to use + #ircserver = ('irc.someserver.de', 6667) + #ircserver = ('testine.someserver.de', 6667) + #ircserver = ('192.168.56.1', 6667) + 'ircserver': ('testine.someserver.de', 6667), + + # broadcast domain (where to meet other clients) + 'broadcastchan': '#broadcastchan', + + # nick prefix (needs to be the same on all clients) + 'nickPrefix': 'VPN', + + # maximum msg len + 'ircmsglen': 400, + + # NOT IMPLEMENTED: reconnect on server disconnect + 'ircReconnect': False, + 'ircReconnectDelay': 3, + + + # ======== security settings ======== + # accept packages if virtual mac != package mac + 'acceptNonMatchingMac': True, + + # ignore messages from non-mac user names + 'ignoreNonMacUser': True, + + # drop non broadcast packages from broadcast when + # in switched network mode + 'strictSwichedNetwork': False, + + # ======== extra tools settings ======== + 'voicebot': + { + 'name': 'flowControl', + 'voiceword': 'requesting network access', + }, + + # ======== misc settings ======== + # executed after being connected to the server + # arguments: + 'postConnectCmd': '/sbin/dhclient -v %s', +} diff --git a/tunnel/ircvpn/ircvpn-standalone.py b/tunnel/ircvpn/ircvpn-standalone.py new file mode 100644 index 0000000..4886e9a --- /dev/null +++ b/tunnel/ircvpn/ircvpn-standalone.py @@ -0,0 +1,184 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# TODO +# Reconnecting to IRC-Server +# Complete switched networks +# Reduce debug output / add good output +# For more security use packet id + srcnick as key for fragmented packages + +from conf import * +import pytap +from pytap import TapDevice +import select +import time +import irclib +import commands +import re +import base64 +import random + +dev = None +headerlen = 1 + 5 + 1 # flag + number + whitespace +packets = {} + +def getMacFromIf(iface): + mac = commands.getoutput("/sbin/ifconfig %s|grep %s|egrep -o '([a-fA-F0-9]{2}(:|.)){6}'" % (iface, iface)) + print "Debug: %s has mac address %s" % (iface, mac) + return mac + +def getDstMacFromPkt(packet): + if len(packet) < 10: + return None + return packet[4:10] + +def binToHexStr(binmac): + return "".join(["%02x" % ord(i) for i in binmac]) + +def isBroadcast(packet): + binmac = getDstMacFromPkt(packet) + # normal broadcast + if binmac == '\xff\xff\xff\xff\xff\xff': + return True + # v6 multicast + if binmac.startswith('\x33\x33'): + return True + return False + +def sendToIRC(dev, server): + msg = dev.read() + oldencmsg = encmsg = base64.encodestring(msg).replace("\n", "") + slices = [] + encmsglen = ircmsglen-headerlen + msgid = "%05d" % random.randint(0, 99999) + while len(encmsg) > encmsglen: + slices.append(encmsg[:encmsglen]) + encmsg = encmsg[encmsglen:] + slices.append(encmsg) + + # HUB or SWITCH? + if mode == "SWITCH" and not isBroadcast(package): + target = "VPN-%s" % (binToHexStr(getDstMacFromPkt(packet)),) + else: + target = broadcastchan + + if len(slices) == 0: + print "DEBUG: EMPTY PACKAGE FROM DEV?" + elif len(slices) == 1: + print "Debug: Sending oneliner to dev" + server.privmsg(target, "o%s %s" % (msgid, slices[0])) + else: + print "Debug: Sending fragmented package to dev" + print " ---- COMPLETE MESSAGE BEFORE SLICES ---- " + print oldencmsg + print " ---- SLICES SLICES SLICES ---- " + print "\n".join(slices) + print " ---- SLICES SLICES SLICES ---- " + server.privmsg(target, "b%s %s" % (msgid, slices.pop(0))) + while len(slices) > 1: + server.privmsg(target, "c%s %s" % (msgid, slices.pop(0))) + server.privmsg(target, "e%s %s" % (msgid, slices.pop(0))) + +def sendToDev(dev, server, isBroadcast, c, e): + parsed = pkgre.match(e.arguments()[0]) + if not parsed: + print "message could not be parsed", e.arguments()[0] + return + (flag, msgid, basemsg) = parsed.groups() + print "flag: %s msgid: %s msg: %s" % (flag, msgid, basemsg) + try: + msgid = int(msgid) + except ValueError: + print "Debug: messageid was not a number" + return + if not ignoreNonMacUser: + # FIXME: ignore the non prefix-mac user + if not nickre.match(): + print "is not allowed in our network" + print e.arguments(), irclib.nm_to_n(e.source()), e.target() + if flag == "o": + # oneliner! + print "Debug: Writing onliner to dev" + try: + msg = base64.decodestring(basemsg) + except base64.binascii.Error, e: + print "Debug: Error decoding base64 irc message (%s)" % e + return + #FIXME if packetAllowed(, , chan) + dev.write(msg) + elif flag == 'b': + if packets.has_key(msgid): + print "Warning: Overwriting lost package with id %s" % msgid + packets[msgid] = basemsg + elif flag in ('c', 'e'): + if not packets.has_key(msgid): + print "Error: Continue package has no matching entry in packets, discarding!" + else: + packets[msgid] += basemsg + if flag == 'e': + arrmsg = packets[msgid] + del(packets[msgid]) + try: + print "arrmsg is", arrmsg.replace("\n", "\n\n\n") + msg = base64.decodestring(arrmsg) + except base64.binascii.Error, e: + print "Debug: Error decoding base64 irc message (%s)" % e + return + print "Debug: writing fragmented package to dev" + print binToHexStr(msg) + dev.write(msg) + #print "GOT in %s (broadcast %s) from %s msg %s" (e.target(), isBroadcast, irclib.nm_to_n(e.source()), e.arguments()[0]) + +def packetAllowed(src, nick, packet): + if not acceptNonMatchingMac: + # check if user-mac == packetmac + # FIXME if not getDstMacFromPkt(packet) == + pass + # FIXME: Maybe move nick check to here + if mode != "HUB" and strictSwichedNetwork: + if src.startswith("#"): # its a channel + return False + return True + +def startup(): + # setup the tap device + dev = TapDevice(pytap.IFF_TAP) + dev.ifconfig(address=ip, netmask=netmask) + if gateway != "": + print "Setting default route %s" % gateway + os.sys("route add default gw %s") + + # setup IRC foo + ircnick = nickPrefix + getMacFromIf(dev.name).replace(":", "") + print "Debug: Connectiong to %s as %s" % (ircserver, ircnick) + + irc = irclib.IRC() + irc.add_global_handler("privmsg", lambda c, e: sendToDev(dev, server, False, c, e), -20) + irc.add_global_handler("pubmsg", lambda c, e: sendToDev(dev, server, True, c, e), -20) + server = irc.server() + server.connect(ircserver[0], ircserver[1], ircnick) + server.join(broadcastchan) + + try: + while True: + sockets = [dev.__fd__] + # get private irc sockets :) + sockets.extend(map(lambda x: x._get_socket(), irc.connections)) + sockets = filter(lambda x: x != None, sockets) + print "sockets", sockets + (readFDs, _, _) = select.select(sockets, [], []) + for readFD in readFDs: + print "EVENT BY", readFD + if readFD == dev.__fd__: + sendToIRC(dev, server) + else: + print "IRC event" + irc.process_once(0.1) + except KeyboardInterrupt: + pass + + print "Shutting down!" + server.quit("RST") + +if __name__ == '__main__': + startup() diff --git a/tunnel/ircvpn/ircvpn.py b/tunnel/ircvpn/ircvpn.py new file mode 100755 index 0000000..7fc9031 --- /dev/null +++ b/tunnel/ircvpn/ircvpn.py @@ -0,0 +1,186 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import sys +sys.path.append("../../../") + +from ether2any import Ether2Any + +from conf import Conf +import time +import irclib +import re +import base64 +import random +import logging +import subprocess +from ether2any.helper import getDstMacFromPkt, isBroadcast, binToHexStr + +# TODO +# replace base64 with something better +# write switching part + +class IrcVPN(Ether2Any): + headerlen = 1 + 5 + 1 # flag + number + whitespace + pkgre = re.compile("^([a-zA-Z])(\d{5}) (.*)$") + + def __init__(self): + Ether2Any.__init__(self, tap=True) + self.irclog = self.setupLogging("IrcVPN") + self.irclog.setLevel(logging.WARN) + + # === Load config values === + network = Conf.get("network", {'mtu': 1500}) + self.ircmsglen = Conf.get("ircmsglen", 400) + self.ircserver = Conf.get("ircserver") + self.broadcastchan = Conf.get("broadcastchan") + self.nickPrefix = Conf.get("nickPrefix", "VPN") + self.postConnectCmd = Conf.get("postConnectCmd", None) + self.voiceWord = Conf.get("voicebot", None) + if self.voiceWord: + self.voiceWord = self.voiceWord.get("voiceword", None) + self.mode = Conf.get("mode", "HUB") + self.ignoreNonMacUser = Conf.get("ignoreNonMacUser", True) + self.acceptNonMatchingMac = Conf.get("acceptNonMatchingMac", True) + self.strictSwichedNetwork = Conf.get("strictSwichedNetwork", False) + + if self.mode not in ("HUB", "SWITCH"): + raise ValueError("mode needs to be either HUB or SWITCH") + self.irclog.info("Starting the IRC Public Network") + self.packets = {} + self._setupIrc() + self.nickre = re.compile("^%s[a-fA-F0-9]{12}$" % (self.nickPrefix,)) + self.dev.ifconfig(**network) + self.dev.up() + + def sendToNet(self, packet): + # split message so that it's not longer than ircmsglen + oldencmsg = encmsg = base64.b64encode(packet).strip("\n") + slices = [] + encmsglen = self.ircmsglen-self.headerlen + msgid = "%05d" % random.randint(0, 99999) + while len(encmsg) > encmsglen: + slices.append(encmsg[:encmsglen]) + encmsg = encmsg[encmsglen:] + slices.append(encmsg) + + # HUB or SWITCH? + if self.mode == "SWITCH" and not isBroadcast(packet): + target = "%s%s" % (self.nickPrefix, binToHexStr(getDstMacFromPkt(packet)),) + else: + target = self.broadcastchan + self.irclog.info("Sending %d packet(s) (total len %d) to %s" % (len(slices), len(oldencmsg), target)) + + if len(slices) == 0: + self.irclog.error("Got EMPTY packet from dev!") + elif len(slices) == 1: + # send in one line (o) + self.server.privmsg(target, "o%s %s" % (msgid, slices[0])) + else: + # send fragmented (b, c, e) + self.server.privmsg(target, "b%s %s" % (msgid, slices.pop(0))) + while len(slices) > 1: + self.server.privmsg(target, "c%s %s" % (msgid, slices.pop(0))) + self.server.privmsg(target, "e%s %s" % (msgid, slices.pop(0))) + + def sendToDev(self, socket): + # proc one irc event + self.irclog.debug("Processing irc event") + self.irc.process_once(0.1) + + def sendToDevIrcCallback(self, isBroadcast, c, e): + parsed = self.pkgre.match(e.arguments()[0]) + nick = irclib.nm_to_n(e.source()) + target = e.target() + if not parsed: + self.irclog.debug("irc-input: Message could not be parsed (\"%s\")" % (e.arguments()[0],)) + return + (flag, msgid, basemsg) = parsed.groups() + self.irclog.debug("irc-input: source: %s target: %s flag: %s msgid: %s msg: %s" % (nick, target, flag, msgid, basemsg)) + if self.ignoreNonMacUser: + if not self.nickre.match(nick): + self.irclog.debug("%s is not allowed in our network" % nick) + return + if flag == "o": + # oneliner! + try: + msg = base64.b64decode(basemsg) + except base64.binascii.Error, e: + self.irclog.warning("Error decoding base64 irc message (%s)" % e) + return + if self.packetAllowed(nick, target, msg): + self.dev.write(msg) + elif flag == 'b': + if self.packets.has_key(msgid): + self.irclog.warning("Overwriting lost package with id %s" % msgid) + try: + # we need to decode at least part of the ethernet header + # choosing 64 chars at random (shouldn't break padding) + partmsg = base64.b64decode(basemsg[:64]) + if len(partmsg) < 24: + raise ValueError() + except base64.binascii.Error, ValueError: + self.irclog.warning("Could not decode parted base64 message, discarding") + return + self.packets[msgid] = basemsg + elif flag in ('c', 'e'): + if not self.packets.has_key(msgid): + self.irclog.warning("Continue package with id %d has no matching entry in packets, discarding!" % msgid) + else: + self.packets[msgid] += basemsg + if flag == 'e': + arrmsg = self.packets[msgid] + del(self.packets[msgid]) + try: + msg = base64.b64decode(arrmsg) + except base64.binascii.Error, e: + self.irclog.debug("Error decoding base64 irc message (%s)" % e) + return + if self.packetAllowed(nick, target, msg): + self.dev.write(msg) + self.irclog.debug("Packet written") + + def packetAllowed(self, nick, target, packet): + if not self.acceptNonMatchingMac and not binToHexStr(getDstMacFromPkt(packet)) == nick[-12:]: + return False + if self.mode != "HUB" and self.strictSwichedNetwork: + if target.startswith("#"): # its a channel + return False + return True + + def printNotice(self, c, e): + self.irclog.info("NOTICE: %s %s" % (e.source(), e.arguments()[0])) + + def _setupIrc(self): + ircnick = self.nickPrefix + self.dev.getMac().replace(":", "") + + self.irc = irclib.IRC() + self.irc.add_global_handler("privmsg", lambda c, e: self.sendToDevIrcCallback(False, c, e), -20) + self.irc.add_global_handler("pubmsg", lambda c, e: self.sendToDevIrcCallback(True, c, e), -20) + self.irc.add_global_handler("privnotice", self.printNotice, -20) + self.server = self.irc.server() + self.server.connect(self.ircserver[0], self.ircserver[1], ircnick) + self.server.join(self.broadcastchan) + if self.voiceWord: + self.irclog.info("Sending voiceword to %s" % self.broadcastchan) + self.server.privmsg(self.broadcastchan, self.voiceWord) + + # add sockets + self.readSockets = map(lambda x: x._get_socket(), self.irc.connections) + self.readSockets = filter(lambda x: x != None, self.readSockets) + + # execute post connect command + if self.postConnectCmd: + cmd = self.postConnectCmd + if cmd.find("%s") >= 0: + print cmd + cmd = cmd % (self.dev.getName(),) + subprocess.Popen(cmd, shell=True) + + def quit(self): + self.server.quit("RST") + +if __name__ == '__main__': + ircvpn = IrcVPN() + ircvpn.run() + diff --git a/tunnel/ircvpn/voicebot.py b/tunnel/ircvpn/voicebot.py new file mode 100755 index 0000000..eaf422e --- /dev/null +++ b/tunnel/ircvpn/voicebot.py @@ -0,0 +1,37 @@ +#!/usr/bin/python +import irclib +from conf import Conf + +def voiceThem(chan, voiceword, c, e): + if e.arguments()[0] == voiceword: + c.mode(chan, "+v "+irclib.nm_to_n(e.source())) + +def main(): + botname = None + voiceword = None + ircserver = None + broadcastchan = None + try: + cfg = Conf.get('voicebot') + botname = cfg.get('name') + voiceword = cfg.get('voiceword') + ircserver = Conf.get('ircserver') + broadcastchan = Conf.get('broadcastchan') + except: + print "Error: Bad Configuration!" + print "" + print "You need a voicebot section with a name and voiceword configured" + print "Also, ircserver and broadcastchan are needed" + return 1 + + print "Voicebot is starting.." + irc = irclib.IRC() + irc.add_global_handler("pubmsg", lambda c, e: voiceThem(broadcastchan, voiceword, c, e), -20) + server = irc.server() + server.connect(ircserver[0], ircserver[1], botname) + server.join(broadcastchan) + print "Connected, joining eventloop." + irc.process_forever() + +if __name__ == '__main__': + main() diff --git a/tunnel/qrnet/qrconf.py b/tunnel/qrnet/qrconf.py new file mode 100755 index 0000000..9e14ef3 --- /dev/null +++ b/tunnel/qrnet/qrconf.py @@ -0,0 +1,19 @@ + +import os + +netmask = "255.255.255.0" +mtu = 417 +ip = "" +camnum = 0 +packetDrop = 20 + +if os.popen("hostname", "r").read().strip() in ("navi", ): + ip = "10.44.13.1" + camnum = 0 +else: + ip = "10.44.13.2" + camnum = 2 + +if __name__ == '__main__': + print ip + diff --git a/tunnel/qrnet/qrnet.py b/tunnel/qrnet/qrnet.py new file mode 100755 index 0000000..171c82d --- /dev/null +++ b/tunnel/qrnet/qrnet.py @@ -0,0 +1,247 @@ +#!/usr/bin/python + +import sys +import os +import re +import time +import struct +import base64 +import StringIO +import opencv +from opencv import highgui, adaptors +import qrencode +import zbar +from PIL import Image +import gtk +import gobject +import urllib +import socket +import subprocess +import Queue +import threading +import mutex + +sys.path.append("../../../") +from ether2any import Ether2Any +from conf import Conf + +mqueueMutex = threading.Lock() +squeueMutex = threading.Lock() + +class QrDisplay(gtk.Window): + def __init__(self, mqueue, squeue, displog, timeout=250): + super(QrDisplay, self).__init__() + + self.fullscreen() + self.qrimgPadding = 20 + (wx, wy) = self.getHackySize() + self.timeout = timeout + self.mqueue = mqueue + self.squeue = squeue + self.displog = displog + + # build up gui + self.vbox = gtk.VBox(False, 0) + self.qrimg = gtk.Image() + self.qrimg.show() + self.vbox.pack_start(self.qrimg, True, True, self.qrimgPadding) + self.vbox.show() + + self.pbar = gtk.ProgressBar() + self.pbar.show() + self.vbox.pack_start(self.pbar, False, False, 0) + + self.add(self.vbox) + + # connect signals + self.connect("destroy", gtk.main_quit) + self.set_size_request(wx, wy) + self.maximize() + self.set_position(gtk.WIN_POS_CENTER) + self.show() + self.qrSet("") + gobject.timeout_add(self.timeout, self.checkQueue) + + def checkQueue(self): + #print "Check queue...", + mqueueMutex.acquire() + if not self.mqueue.empty(): + print "new data available" + bmsg = self.mqueue.get() + self.qrSet(bmsg) + mqueueMutex.release() + #else: + # print "no new data available" + + # progress + squeueMutex.acquire() + if not self.squeue.empty(): + (frames, success) = self.squeue.get() + if frames > 0: + self.pbar.set_fraction(success/float(frames)) + self.pbar.set_text("%02d / %02d (%03.02f)" % (success, frames, success / float(frames) * 100.0)) + print frames, success + squeueMutex.release() + return True + + def getHackySize(self): + """ Obtain current size of screen via xrandr. """ + #p = subprocess.Popen(["bash", "-c", 'xrandr|egrep "\*+"|egrep -o "[0-9]+x[0-9]+"'], stdout=subprocess.PIPE) + p = subprocess.Popen('xrandr|egrep "\*+"|egrep -o "[0-9]+x[0-9]+"', stdout=subprocess.PIPE) + p.wait() + (myout, myin) = p.communicate() + return map(lambda x: int(x)-self.qrimgPadding, myout.strip().split("x")) + + def image2pixbuf(self, im): + (a, b, c, d) = self.qrimg.get_allocation() + # correct size, must be 1:1 (so select the smaller one) + x = min(c-a, d-b) + if(x < 10): + x = 100 + imgSize = (x, x) + im = im.resize(imgSize) + file1 = StringIO.StringIO() + im.save(file1, "ppm") + contents = file1.getvalue() + file1.close() + loader = gtk.gdk.PixbufLoader("pnm") + loader.write(contents, len(contents)) + pixbuf = loader.get_pixbuf() + loader.close() + return pixbuf + + def qrSet(self, msg): + """ Set content of displayed qr image. """ + if msg == None or msg == "": + msg = "Katze" + (qrVersion, qrSize, qrImg) = qrencode.encode(msg, 0) + self.qrimg.set_from_pixbuf(self.image2pixbuf(qrImg)) + + +class DisplayThread(threading.Thread): + """ Thread that runs the GTK-GUI to display outgoing network qr codes. """ + def __init__(self, dev, mqueue, squeue, displog): + threading.Thread.__init__(self) + self.dev = dev + self.quit = False + self.qrdisplay = QrDisplay(mqueue, squeue, ) + self.mqueue = mqueue + self.squeue = squeue + self.displog = displog + + def run(self): + self.displog.info("Display GTK Gui is up and running.") + gtk.main() + +class CamThread(threading.Thread): + """ Captures images from a webcam and decodes them. + + Captures images from the first webcam it can find, decodes them + and writes them to the interface. """ + def __init__(self, dev, squeue, camlog): + threading.Thread.__init__(self) + + self.dev = dev + self.squeue = squeue + self.camlog = camlog + + self.frame = 0 + self.reportAfter = 20 # frames + self.quit = False + self.success = 0 + self.lastPacket = "" + + self.reader = highgui.cvCreateCameraCapture(camnum) + self.scanner = zbar.ImageScanner() + self.scanner.parse_config('enable') + + def run(self): + while not self.quit: + frame = highgui.cvQueryFrame(self.reader) + self.frame += 1 + + frame = opencv.cvGetMat(frame) + img = adaptors.Ipl2PIL(frame) + width, height = img.size + zimg = zbar.Image(width, height, 'Y800', img.convert("L").tostring()) + self.scanner.scan(zimg) + data = None + for symbol in zimg: + data = symbol.data + self.camlog.debug("Data is: %s" % data) + self.success += 1 + # handle data + if not self.lastPacket == data: + self.lastPacket = data + try: + msg = base64.b64decode(data) + (rawtime, packet) = (msg[0:8], msg[8:]) + ptime = struct.unpack(" base64 error" + self.camlog.error("Base64 error - could not decode packet") + except struct.error: + self.camlog.error("Header error - could not extract header information") + else: + # packet is already known, discard + pass + + # status report to gui + + if self.frame % self.reportAfter == 0: + self.frame = self.success = 0 + # too much status codes? flush 'em + squeueMutex.acquire() + if self.squeue.qsize() > self.reportAfter/2: + while not self.squeue.empty(): + self.squeue.get() + # add new status code + self.squeue.put((self.frame, self.success)) + squeueMutex.release() + + +class QrNet(Ether2Any): + pidlen = 16 + def __init__(self): + # device + Ether2Any.__init__(self, tap=True) + self.qrlog = self.setupLogging("QrNet") + self.dev.ifconfig(address=ip, netmask=netmask, mtu=mtu) + self.packetDrop = packetDrop + self.mqueue = Queue.Queue() + self.squeue = Queue.Queue() + + # thread starting... + gtk.gdk.threads_init() + + self.cam = CamThread(self.dev, self.squeue, self.setupLogging("CamThread")) + self.cam.start() + self.display = DisplayThread(self.dev, self.mqueue, self.squeue, self.setupLogging("DisplayThread")) + self.display.start() + + def sendToNet(self): + # prepare data for queue && display + self.qrlog.debug("Data from the device") + msg = self.dev.read() + # add acttime to generate "unique" images + acttime = struct.pack("" + bmsg) + + # add packet to queue, maybe drop packet + mqueueMutex.acquire() + if self.mqueue.qsize() < self.packetDrop: + self.mqueue.put(bmsg) + else: + self.qrlog.debug("Dropping packet!") + mqueueMutex.release() + + def quit(self): + self.display.quit = True + self.cam.quit = True + +if __name__ == '__main__': + qrnet = QrNet() + qrnet.run()