Initial commit
This commit is contained in:
commit
50faf05cd9
|
@ -0,0 +1,2 @@
|
||||||
|
*.pyc
|
||||||
|
.*.swp
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
http://www.kernel.org/doc/Documentation/networking/tuntap.txt
|
|
@ -0,0 +1,2 @@
|
||||||
|
from ether2any import Ether2Any
|
||||||
|
import helper
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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: <command> <device>
|
||||||
|
'postConnectCmd': '/sbin/dhclient -v %s',
|
||||||
|
}
|
|
@ -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()
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
|
@ -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
|
||||||
|
|
|
@ -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("<d", rawtime)
|
||||||
|
self.camlog.debug("Network packet! Heade (time) is %s" % (ptime,))
|
||||||
|
self.dev.write(packet)
|
||||||
|
except base64.binascii.Error:
|
||||||
|
print " ==> 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("<d", time.time())
|
||||||
|
bmsg = base64.b64encode(acttime + msg)
|
||||||
|
self.qrlog.debug("==>" + 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()
|
Loading…
Reference in New Issue