Browse Source

Initial commit

Sebastian Lohff 9 years ago
commit
50faf05cd9
12 changed files with 990 additions and 0 deletions
  1. 2
    0
      .gitignore
  2. 5
    0
      Readme
  3. 2
    0
      __init__.py
  4. 87
    0
      ether2any.py
  5. 24
    0
      helper.py
  6. 114
    0
      pytap.py
  7. 83
    0
      tunnel/ircvpn/conf.py
  8. 184
    0
      tunnel/ircvpn/ircvpn-standalone.py
  9. 186
    0
      tunnel/ircvpn/ircvpn.py
  10. 37
    0
      tunnel/ircvpn/voicebot.py
  11. 19
    0
      tunnel/qrnet/qrconf.py
  12. 247
    0
      tunnel/qrnet/qrnet.py

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
1
+*.pyc
2
+.*.swp

+ 5
- 0
Readme View File

@@ -0,0 +1,5 @@
1
+
2
+
3
+
4
+Documentation
5
+	http://www.kernel.org/doc/Documentation/networking/tuntap.txt

+ 2
- 0
__init__.py View File

@@ -0,0 +1,2 @@
1
+from ether2any import Ether2Any
2
+import helper

+ 87
- 0
ether2any.py View File

@@ -0,0 +1,87 @@
1
+# -*- coding: utf-8 -*-
2
+import commands
3
+import select
4
+import pytap
5
+import logging
6
+from pytap import TapDevice
7
+
8
+class Ether2Any:
9
+	""" Baseclass for writing arbitrary Ethernet/IP Tunnels using TUN/TAP device.
10
+	
11
+	This class handles a TUN/TAP devices and runs a select loop for it and,
12
+	if given, a set of sockets. To use this class at least sendToNet() has
13
+	to be implemented by a subclass. """
14
+	def __init__(self, tap=True, readSockets=[]):
15
+		""" Constructor for Ether2Any.
16
+
17
+		isTap defines if the managed device should be a tap (ethernet tunnel)
18
+		or a tun (IP tunnel). """
19
+		self.readSockets = readSockets
20
+		# create device
21
+		self.dev = TapDevice(tap=tap)
22
+		
23
+		self.timeout = None
24
+	
25
+	def setupLogging(self, name):
26
+		l = logging.getLogger(name)
27
+		fmt = logging.Formatter("%(asctime)s - [%(levelname)s] (%(name)s) - %(message)s")
28
+		ch = logging.StreamHandler()
29
+		ch.setFormatter(fmt)
30
+		l.addHandler(ch)
31
+		
32
+		return l
33
+	
34
+	def addSocket(self, sock):
35
+		""" Add socket to readSockets set. """
36
+		self.readSockets.append(sock)
37
+	
38
+	def delSocket(self, socket):
39
+		""" Remove socket from readSockets set. """
40
+		try:
41
+			self.readSockets.remove(socket)
42
+		except ValueError:
43
+			pass
44
+
45
+	# outgoing data
46
+	def sendToNet(self, packet):
47
+		""" This function has to be implemented to handle outgoing
48
+		data (read from the TUN/TAP device) """
49
+		raise NotImplementedError("You need to overload sendToNet()")
50
+	
51
+	# incoming data
52
+	def sendToDev(self, sock):
53
+		""" This function has to be implemented to handle incoming
54
+		data which is read from the extra sockets (self.readSockets).
55
+		It will not be called when no extra readSockets are specified. """
56
+		raise NotImplementedError("You need to overload sendToDev()")
57
+	
58
+	def setTimeout(self, t):
59
+		""" Set select timeout. """
60
+		self.timeout = t
61
+	
62
+	def callAfterSelect(self):
63
+		""" Will be called as last operation of the mainloop when
64
+		handling of the select result / the select timeout is hit.
65
+		"""
66
+		pass
67
+	
68
+	def run(self):
69
+		""" Run main select-loop. """
70
+		try:
71
+			while True:
72
+				sockets = [self.dev.getFD()] + self.readSockets
73
+				(readFDs, _, _) = select.select(sockets, [], [], self.timeout)
74
+				for readFD in readFDs:
75
+					if readFD == self.dev.getFD():
76
+						self.sendToNet(self.dev.read())
77
+					elif readFD in self.readSockets:
78
+						self.sendToDev(readFD)
79
+				self.callAfterSelect()
80
+		except KeyboardInterrupt:
81
+			self.quit()
82
+		self.dev.close()
83
+	
84
+	def quit(self):
85
+		""" Will be called after the run-select() and its processing is done. """
86
+		pass
87
+

+ 24
- 0
helper.py View File

@@ -0,0 +1,24 @@
1
+def getSrcMacFromPkt(packet):
2
+	if len(packet) < 16:
3
+		return None
4
+	return packet[10:16]
5
+
6
+def getDstMacFromPkt(packet):
7
+	if len(packet) < 10:
8
+		return None
9
+	return packet[4:10]
10
+
11
+def binToHexStr(binmac):
12
+	return "".join(["%02x" % ord(i) for i in binmac])
13
+
14
+# checks if packet is a broadcast packet
15
+def isBroadcast(packet):
16
+	binmac = getDstMacFromPkt(packet)
17
+	# normal broadcast
18
+	if binmac == '\xff\xff\xff\xff\xff\xff':
19
+		return True
20
+	# v6 multicast
21
+	if binmac.startswith('\x33\x33'):
22
+		return True
23
+	return False
24
+

+ 114
- 0
pytap.py View File

@@ -0,0 +1,114 @@
1
+from fcntl import ioctl
2
+import subprocess
3
+import os
4
+import struct
5
+
6
+
7
+class TapDevice:
8
+	""" TUN/TAP device class """
9
+	
10
+	# magic numbers and structlayout
11
+	TUNSETIFF = 0x400454ca
12
+	IFF_TUN   = 0x0001
13
+	IFF_TAP   = 0x0002
14
+	DEVPATH   = "/dev/net/tun"
15
+	_ifreq = "16sh"
16
+
17
+	def __init__(self, name='', tap=True, conf=None):
18
+		""" Constructor for the device.
19
+		
20
+		name - the device name, use a %d for a generated device numer
21
+		tap  - if this device should be a tap device
22
+		conf - conf to pass to the ifconfig function of this class
23
+		       (if None ifconfig() won't be called)
24
+		"""
25
+		self._mode = (tap and self.IFF_TAP) or self.IFF_TUN
26
+		self._fd = None
27
+		self._name = None
28
+		self._tap = tap
29
+		self._mac = None
30
+		self._mtu = 1500
31
+		self.conf = conf
32
+		
33
+		if name == '':
34
+			self._nametpl = (tap and "tap%d") or "tun%d"
35
+		
36
+		self._createDev()
37
+		if self.conf:
38
+			self.ifconfig(**conf)
39
+	
40
+	def _createDev(self):
41
+		if self._fd:
42
+			self.close()
43
+		
44
+		self._fd = os.open(self.DEVPATH, os.O_RDWR)
45
+		ifreq = struct.pack(self._ifreq, self._nametpl, self._mode)
46
+		ret = ioctl(self._fd, self.TUNSETIFF, ifreq)
47
+		# retmode should be the same as self._mode
48
+		(retname, retmode) = struct.unpack(self._ifreq, ret)
49
+		self._name = retname.strip("\x00")
50
+	
51
+	def _ifconfig(self, params):
52
+		args = ["/sbin/ifconfig"] + params
53
+		ret = subprocess.Popen(args).wait()
54
+		if ret != 0:
55
+			raise PyTapException("Command '%s' did not return 0" % (" ".join(args),))
56
+	
57
+	def ifconfig(self, **kwargs):
58
+		""" Calls ifconfig for the device.
59
+		All arguments will be passed to ifconfig. Use 'address' for the device address.
60
+		E.g. device.ifconfig(address="12.34.56.78", mtu=1500)
61
+		"""
62
+		args = [self._name]
63
+		if kwargs.has_key("address"):
64
+			args.append(kwargs["address"])
65
+			del(kwargs["address"])
66
+		args = reduce(lambda l, key: l+[key, str(kwargs[key])],
67
+							             kwargs, args)
68
+		self._ifconfig(args)
69
+	
70
+	def getMac(self):
71
+		""" Get the device mac """
72
+		# this "could" be buffered, but we never know who when changed the mac
73
+		# ==> we re-get the mac on every request
74
+		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)
75
+		mac = proc.stdout.read().strip()
76
+		return mac
77
+	
78
+	def up(self):
79
+		""" Bring the device up """
80
+		self._ifconfig([self._name, "up"])
81
+	
82
+	def down(self):
83
+		""" Bring the device down """
84
+		self._ifconfig([self._name, "down"])
85
+	
86
+	def getFD(self):
87
+		""" Get the device file descriptor (e.g. to use in select()) """
88
+		return self._fd
89
+	
90
+	def getName(self):
91
+		""" Get the (real) name of the device """
92
+		return self._name
93
+	
94
+	def read(self):
95
+		""" Read a packet from the device """
96
+		readSize = self._mtu
97
+		if self._tap:
98
+			# don't forget the ethernet frame (not included in MTU)
99
+			readSize += 18
100
+		data = os.read(self._fd, self._mtu)
101
+		return data
102
+	
103
+	def write(self, data):
104
+		""" Write a packet to the device """
105
+		os.write(self._fd, data)
106
+	
107
+	def close(self):
108
+		""" Close the device """
109
+		os.close(self._fd)
110
+		self._fd = self._name = None
111
+
112
+class PyTapException(Exception):
113
+	pass
114
+

+ 83
- 0
tunnel/ircvpn/conf.py View File

@@ -0,0 +1,83 @@
1
+#  ___________________________________________________
2
+# |                                                   |
3
+# | ircvpn - irc virtual public network configuration |
4
+# |___________________________________________________|
5
+
6
+# config options
7
+# IRC
8
+#    - server (ip, port, ssl)
9
+#	 - channel
10
+#	 - nick prefix
11
+#	 - maximum line length
12
+# Network
13
+#    - device: ip, netmask, mtu
14
+#    - dhclient instead of static ip?
15
+#    - routing?
16
+#	 - dns?
17
+# Tunnel
18
+#	 - security settings
19
+#	 - mode (hub or switch)
20
+
21
+import os
22
+
23
+Conf = {
24
+	# ======== network settings ========
25
+	# ipsettings for the device
26
+	'devname': '',
27
+	'network':
28
+		{
29
+			'address': '10.10.10.74',
30
+			'netmask': '255.255.255.0',
31
+			#gateway: '',
32
+			'mtu': 1400,
33
+		},
34
+	
35
+	# hubbed ("HUB")/switched("SWITCH") Network
36
+	# 	HUB:    communicate only over broadcastchan
37
+	#   SWITCH: use query for non broadcast packages
38
+	'mode': "HUB",
39
+		
40
+	# ======== IRC settings ========
41
+	# irc-server to use
42
+	#ircserver = ('irc.someserver.de', 6667)
43
+	#ircserver = ('testine.someserver.de', 6667)
44
+	#ircserver = ('192.168.56.1', 6667)
45
+	'ircserver': ('testine.someserver.de', 6667),
46
+	
47
+	# broadcast domain (where to meet other clients)
48
+	'broadcastchan': '#broadcastchan',
49
+	
50
+	# nick prefix (needs to be the same on all clients)
51
+	'nickPrefix': 'VPN',
52
+	
53
+	# maximum msg len
54
+	'ircmsglen': 400,
55
+	
56
+	# NOT IMPLEMENTED: reconnect on server disconnect
57
+	'ircReconnect': False,
58
+	'ircReconnectDelay': 3,
59
+	
60
+
61
+	# ======== security settings ========
62
+	# accept packages if virtual mac != package mac
63
+	'acceptNonMatchingMac': True,
64
+	
65
+	# ignore messages from non-mac user names
66
+	'ignoreNonMacUser': True,
67
+	
68
+	# drop non broadcast packages from broadcast when
69
+	# in switched network mode
70
+	'strictSwichedNetwork': False,
71
+	
72
+	# ======== extra tools settings ========
73
+	'voicebot':
74
+		{
75
+			'name': 'flowControl',
76
+			'voiceword': 'requesting network access',
77
+		},
78
+	
79
+	# ======== misc settings ========
80
+	# executed after being connected to the server
81
+	# arguments: <command> <device>
82
+	'postConnectCmd': '/sbin/dhclient -v %s',
83
+}

+ 184
- 0
tunnel/ircvpn/ircvpn-standalone.py View File

@@ -0,0 +1,184 @@
1
+#!/usr/bin/python
2
+# -*- coding: utf-8 -*-
3
+
4
+# TODO
5
+#	Reconnecting to IRC-Server
6
+#	Complete switched networks
7
+#	Reduce debug output / add good output
8
+#	For more security use packet id + srcnick as key for fragmented packages
9
+
10
+from conf import *
11
+import pytap
12
+from pytap import TapDevice
13
+import select
14
+import time
15
+import irclib
16
+import commands
17
+import re
18
+import base64
19
+import random
20
+
21
+dev = None
22
+headerlen = 1 + 5 + 1 # flag + number + whitespace
23
+packets = {}
24
+
25
+def getMacFromIf(iface):
26
+	mac = commands.getoutput("/sbin/ifconfig %s|grep %s|egrep -o '([a-fA-F0-9]{2}(:|.)){6}'" % (iface, iface))
27
+	print "Debug: %s has mac address %s" % (iface, mac)
28
+	return mac
29
+
30
+def getDstMacFromPkt(packet):
31
+	if len(packet) < 10:
32
+		return None
33
+	return packet[4:10]
34
+
35
+def binToHexStr(binmac):
36
+	return "".join(["%02x" % ord(i) for i in binmac])
37
+
38
+def isBroadcast(packet):
39
+	binmac = getDstMacFromPkt(packet)
40
+	# normal broadcast
41
+	if binmac == '\xff\xff\xff\xff\xff\xff':
42
+		return True
43
+	# v6 multicast
44
+	if binmac.startswith('\x33\x33'):
45
+		return True
46
+	return False
47
+
48
+def sendToIRC(dev, server):
49
+	msg = dev.read()
50
+	oldencmsg = encmsg = base64.encodestring(msg).replace("\n", "")
51
+	slices = []
52
+	encmsglen = ircmsglen-headerlen
53
+	msgid = "%05d" % random.randint(0, 99999)
54
+	while len(encmsg) > encmsglen:
55
+		slices.append(encmsg[:encmsglen])
56
+		encmsg = encmsg[encmsglen:]
57
+	slices.append(encmsg)
58
+	
59
+	# HUB or SWITCH?
60
+	if mode == "SWITCH" and not isBroadcast(package):
61
+		target = "VPN-%s" % (binToHexStr(getDstMacFromPkt(packet)),)
62
+	else:
63
+		target = broadcastchan
64
+	
65
+	if len(slices) == 0:
66
+		print "DEBUG: EMPTY PACKAGE FROM DEV?"
67
+	elif len(slices) == 1:
68
+		print "Debug: Sending oneliner to dev"
69
+		server.privmsg(target, "o%s %s" % (msgid, slices[0]))
70
+	else:
71
+		print "Debug: Sending fragmented package to dev"
72
+		print " ---- COMPLETE MESSAGE BEFORE SLICES ---- "
73
+		print oldencmsg
74
+		print " ---- SLICES SLICES SLICES ---- "
75
+		print "\n".join(slices)
76
+		print " ---- SLICES SLICES SLICES ---- "
77
+		server.privmsg(target, "b%s %s" % (msgid, slices.pop(0)))
78
+		while len(slices) > 1:
79
+			server.privmsg(target, "c%s %s" % (msgid, slices.pop(0)))
80
+		server.privmsg(target, "e%s %s" % (msgid, slices.pop(0)))
81
+
82
+def sendToDev(dev, server, isBroadcast, c, e):
83
+	parsed = pkgre.match(e.arguments()[0])
84
+	if not parsed:
85
+		print "message could not be parsed", e.arguments()[0]
86
+		return
87
+	(flag, msgid, basemsg) = parsed.groups()
88
+	print "flag: %s msgid: %s msg: %s" % (flag, msgid, basemsg)
89
+	try:
90
+		msgid = int(msgid)
91
+	except ValueError:
92
+		print "Debug: messageid was not a number"
93
+		return
94
+	if not ignoreNonMacUser:
95
+		# FIXME: ignore the non prefix-mac user
96
+		if not nickre.match():
97
+			print "is not allowed in our network"
98
+	print e.arguments(), irclib.nm_to_n(e.source()), e.target()
99
+	if flag == "o":
100
+		# oneliner!
101
+		print "Debug: Writing onliner to dev"
102
+		try:
103
+			msg = base64.decodestring(basemsg)
104
+		except base64.binascii.Error, e:
105
+			print "Debug: Error decoding base64 irc message (%s)" % e
106
+			return
107
+		#FIXME if packetAllowed(, , chan)
108
+		dev.write(msg)
109
+	elif flag == 'b':
110
+		if packets.has_key(msgid):
111
+			print "Warning: Overwriting lost package with id %s" % msgid
112
+		packets[msgid] = basemsg
113
+	elif flag in ('c', 'e'):
114
+		if not packets.has_key(msgid):
115
+			print "Error: Continue package has no matching entry in packets, discarding!"
116
+		else:
117
+			packets[msgid] += basemsg
118
+			if flag == 'e':
119
+				arrmsg = packets[msgid]
120
+				del(packets[msgid])
121
+				try:
122
+					print "arrmsg is", arrmsg.replace("\n", "\n\n\n")
123
+					msg = base64.decodestring(arrmsg)
124
+				except base64.binascii.Error, e:
125
+					print "Debug: Error decoding base64 irc message (%s)" % e
126
+					return
127
+				print "Debug: writing fragmented package to dev"
128
+				print binToHexStr(msg)
129
+				dev.write(msg)
130
+	#print "GOT in %s (broadcast %s) from %s msg %s" (e.target(), isBroadcast, irclib.nm_to_n(e.source()), e.arguments()[0])
131
+
132
+def packetAllowed(src, nick, packet):
133
+	if not acceptNonMatchingMac:
134
+		# check if user-mac == packetmac
135
+		# FIXME if not getDstMacFromPkt(packet) == 
136
+		pass
137
+	# FIXME: Maybe move nick check to here
138
+	if mode != "HUB" and strictSwichedNetwork:
139
+		if src.startswith("#"): # its a channel
140
+			return False
141
+	return True
142
+
143
+def startup():
144
+	# setup the tap device
145
+	dev = TapDevice(pytap.IFF_TAP)
146
+	dev.ifconfig(address=ip, netmask=netmask)
147
+	if gateway != "":
148
+		print "Setting default route %s" % gateway
149
+		os.sys("route add default gw %s")
150
+	
151
+	# setup IRC foo
152
+	ircnick = nickPrefix + getMacFromIf(dev.name).replace(":", "")
153
+	print "Debug: Connectiong to %s as %s" % (ircserver, ircnick)
154
+	
155
+	irc = irclib.IRC()
156
+	irc.add_global_handler("privmsg", lambda c, e: sendToDev(dev, server, False, c, e), -20)
157
+	irc.add_global_handler("pubmsg",  lambda c, e: sendToDev(dev, server, True,  c, e), -20)
158
+	server = irc.server()
159
+	server.connect(ircserver[0], ircserver[1], ircnick)
160
+	server.join(broadcastchan)
161
+	
162
+	try:
163
+		while True:
164
+			sockets = [dev.__fd__]
165
+			# get private irc sockets :)
166
+			sockets.extend(map(lambda x: x._get_socket(), irc.connections))
167
+			sockets = filter(lambda x: x != None, sockets)
168
+			print "sockets", sockets
169
+			(readFDs, _, _) = select.select(sockets, [], [])
170
+			for readFD in readFDs:
171
+				print "EVENT BY", readFD
172
+				if readFD == dev.__fd__:
173
+					sendToIRC(dev, server)
174
+				else:
175
+					print "IRC event"
176
+					irc.process_once(0.1)
177
+	except KeyboardInterrupt:
178
+		pass
179
+	
180
+	print "Shutting down!"
181
+	server.quit("RST")
182
+
183
+if __name__ == '__main__':
184
+	startup()

+ 186
- 0
tunnel/ircvpn/ircvpn.py View File

@@ -0,0 +1,186 @@
1
+#!/usr/bin/python
2
+# -*- coding: utf-8 -*-
3
+
4
+import sys
5
+sys.path.append("../../../")
6
+
7
+from ether2any import Ether2Any
8
+
9
+from conf import Conf
10
+import time
11
+import irclib
12
+import re
13
+import base64
14
+import random
15
+import logging
16
+import subprocess
17
+from ether2any.helper import getDstMacFromPkt, isBroadcast, binToHexStr
18
+
19
+# TODO
20
+#	replace base64 with something better
21
+#	write switching part
22
+
23
+class IrcVPN(Ether2Any):
24
+	headerlen = 1 + 5 + 1 # flag + number + whitespace
25
+	pkgre = re.compile("^([a-zA-Z])(\d{5}) (.*)$")
26
+	
27
+	def __init__(self):
28
+		Ether2Any.__init__(self, tap=True)
29
+		self.irclog = self.setupLogging("IrcVPN")
30
+		self.irclog.setLevel(logging.WARN)
31
+		
32
+		# === Load config values ===
33
+		network = Conf.get("network", {'mtu': 1500})
34
+		self.ircmsglen = Conf.get("ircmsglen", 400)
35
+		self.ircserver = Conf.get("ircserver")
36
+		self.broadcastchan = Conf.get("broadcastchan")
37
+		self.nickPrefix = Conf.get("nickPrefix", "VPN")
38
+		self.postConnectCmd = Conf.get("postConnectCmd", None)
39
+		self.voiceWord = Conf.get("voicebot", None)
40
+		if self.voiceWord:
41
+			self.voiceWord = self.voiceWord.get("voiceword", None)
42
+		self.mode = Conf.get("mode", "HUB")
43
+		self.ignoreNonMacUser = Conf.get("ignoreNonMacUser", True)
44
+		self.acceptNonMatchingMac = Conf.get("acceptNonMatchingMac", True)
45
+		self.strictSwichedNetwork = Conf.get("strictSwichedNetwork", False)
46
+		
47
+		if self.mode not in ("HUB", "SWITCH"):
48
+			raise ValueError("mode needs to be either HUB or SWITCH")
49
+		self.irclog.info("Starting the IRC Public Network")
50
+		self.packets = {}
51
+		self._setupIrc()
52
+		self.nickre = re.compile("^%s[a-fA-F0-9]{12}$" % (self.nickPrefix,))
53
+		self.dev.ifconfig(**network)
54
+		self.dev.up()
55
+	
56
+	def sendToNet(self, packet):
57
+		# split message so that it's not longer than ircmsglen
58
+		oldencmsg = encmsg = base64.b64encode(packet).strip("\n")
59
+		slices = []
60
+		encmsglen = self.ircmsglen-self.headerlen
61
+		msgid = "%05d" % random.randint(0, 99999)
62
+		while len(encmsg) > encmsglen:
63
+			slices.append(encmsg[:encmsglen])
64
+			encmsg = encmsg[encmsglen:]
65
+		slices.append(encmsg)
66
+		
67
+		# HUB or SWITCH?
68
+		if self.mode == "SWITCH" and not isBroadcast(packet):
69
+			target = "%s%s" % (self.nickPrefix, binToHexStr(getDstMacFromPkt(packet)),)
70
+		else:
71
+			target = self.broadcastchan
72
+		self.irclog.info("Sending %d packet(s) (total len %d) to %s" % (len(slices), len(oldencmsg), target))
73
+		
74
+		if len(slices) == 0:
75
+			self.irclog.error("Got EMPTY packet from dev!")
76
+		elif len(slices) == 1:
77
+			# send in one line (o)
78
+			self.server.privmsg(target, "o%s %s" % (msgid, slices[0]))
79
+		else:
80
+			# send fragmented (b, c, e)
81
+			self.server.privmsg(target, "b%s %s" % (msgid, slices.pop(0)))
82
+			while len(slices) > 1:
83
+				self.server.privmsg(target, "c%s %s" % (msgid, slices.pop(0)))
84
+			self.server.privmsg(target, "e%s %s" % (msgid, slices.pop(0)))
85
+	
86
+	def sendToDev(self, socket):
87
+		# proc one irc event
88
+		self.irclog.debug("Processing irc event")
89
+		self.irc.process_once(0.1)
90
+	
91
+	def sendToDevIrcCallback(self, isBroadcast, c, e):
92
+		parsed = self.pkgre.match(e.arguments()[0])
93
+		nick = irclib.nm_to_n(e.source())
94
+		target = e.target()
95
+		if not parsed:
96
+			self.irclog.debug("irc-input: Message could not be parsed (\"%s\")" % (e.arguments()[0],))
97
+			return
98
+		(flag, msgid, basemsg) = parsed.groups()
99
+		self.irclog.debug("irc-input: source: %s target: %s flag: %s msgid: %s msg: %s" % (nick, target, flag, msgid, basemsg))
100
+		if self.ignoreNonMacUser:
101
+			if not self.nickre.match(nick):
102
+				self.irclog.debug("%s is not allowed in our network" % nick)
103
+				return
104
+		if flag == "o":
105
+			# oneliner!
106
+			try:
107
+				msg = base64.b64decode(basemsg)
108
+			except base64.binascii.Error, e:
109
+				self.irclog.warning("Error decoding base64 irc message (%s)" % e)
110
+				return
111
+			if self.packetAllowed(nick, target, msg):
112
+				self.dev.write(msg)
113
+		elif flag == 'b':
114
+			if self.packets.has_key(msgid):
115
+				self.irclog.warning("Overwriting lost package with id %s" % msgid)
116
+			try:
117
+				# we need to decode at least part of the ethernet header
118
+				# choosing 64 chars at random (shouldn't break padding)
119
+				partmsg = base64.b64decode(basemsg[:64])
120
+				if len(partmsg) < 24:
121
+					raise ValueError()
122
+			except base64.binascii.Error, ValueError:
123
+				self.irclog.warning("Could not decode parted base64 message, discarding")
124
+				return
125
+			self.packets[msgid] = basemsg
126
+		elif flag in ('c', 'e'):
127
+			if not self.packets.has_key(msgid):
128
+				self.irclog.warning("Continue package with id %d has no matching entry in packets, discarding!" % msgid)
129
+			else:
130
+				self.packets[msgid] += basemsg
131
+				if flag == 'e':
132
+					arrmsg = self.packets[msgid]
133
+					del(self.packets[msgid])
134
+					try:
135
+						msg = base64.b64decode(arrmsg)
136
+					except base64.binascii.Error, e:
137
+						self.irclog.debug("Error decoding base64 irc message (%s)" % e)
138
+						return
139
+					if self.packetAllowed(nick, target, msg):
140
+						self.dev.write(msg)
141
+			self.irclog.debug("Packet written")
142
+	
143
+	def packetAllowed(self, nick, target, packet):
144
+		if not self.acceptNonMatchingMac and not binToHexStr(getDstMacFromPkt(packet)) == nick[-12:]:
145
+			return False
146
+		if self.mode != "HUB" and self.strictSwichedNetwork:
147
+			if target.startswith("#"): # its a channel
148
+				return False
149
+		return True
150
+	
151
+	def printNotice(self, c, e):
152
+		self.irclog.info("NOTICE: %s %s" % (e.source(), e.arguments()[0]))
153
+	
154
+	def _setupIrc(self):
155
+		ircnick = self.nickPrefix + self.dev.getMac().replace(":", "")
156
+		
157
+		self.irc = irclib.IRC()
158
+		self.irc.add_global_handler("privmsg", lambda c, e: self.sendToDevIrcCallback(False, c, e), -20)
159
+		self.irc.add_global_handler("pubmsg",  lambda c, e: self.sendToDevIrcCallback(True,  c, e), -20)
160
+		self.irc.add_global_handler("privnotice",  self.printNotice, -20)
161
+		self.server = self.irc.server()
162
+		self.server.connect(self.ircserver[0], self.ircserver[1], ircnick)
163
+		self.server.join(self.broadcastchan)
164
+		if self.voiceWord:
165
+			self.irclog.info("Sending voiceword to %s" % self.broadcastchan)
166
+			self.server.privmsg(self.broadcastchan, self.voiceWord)
167
+		
168
+		# add sockets
169
+		self.readSockets = map(lambda x: x._get_socket(), self.irc.connections)
170
+		self.readSockets = filter(lambda x: x != None, self.readSockets)
171
+		
172
+		# execute post connect command
173
+		if self.postConnectCmd:
174
+			cmd = self.postConnectCmd
175
+			if cmd.find("%s") >= 0:
176
+				print cmd
177
+				cmd = cmd % (self.dev.getName(),)
178
+			subprocess.Popen(cmd, shell=True)
179
+	
180
+	def quit(self):
181
+		self.server.quit("RST")
182
+
183
+if __name__ == '__main__':
184
+	ircvpn = IrcVPN()
185
+	ircvpn.run()
186
+

+ 37
- 0
tunnel/ircvpn/voicebot.py View File

@@ -0,0 +1,37 @@
1
+#!/usr/bin/python
2
+import irclib
3
+from conf import Conf
4
+
5
+def voiceThem(chan, voiceword, c, e):
6
+	if e.arguments()[0] == voiceword:
7
+		c.mode(chan, "+v "+irclib.nm_to_n(e.source()))
8
+
9
+def main():
10
+	botname = None
11
+	voiceword = None
12
+	ircserver = None
13
+	broadcastchan = None
14
+	try:
15
+		cfg = Conf.get('voicebot')
16
+		botname = cfg.get('name')
17
+		voiceword = cfg.get('voiceword')
18
+		ircserver = Conf.get('ircserver')
19
+		broadcastchan = Conf.get('broadcastchan')
20
+	except:
21
+		print "Error: Bad Configuration!"
22
+		print ""
23
+		print "You need a voicebot section with a name and voiceword configured"
24
+		print "Also, ircserver and broadcastchan are needed"
25
+		return 1
26
+	
27
+	print "Voicebot is starting.."
28
+	irc = irclib.IRC()
29
+	irc.add_global_handler("pubmsg", lambda c, e: voiceThem(broadcastchan, voiceword, c, e), -20)
30
+	server = irc.server()
31
+	server.connect(ircserver[0], ircserver[1], botname)
32
+	server.join(broadcastchan)
33
+	print "Connected, joining eventloop."
34
+	irc.process_forever()
35
+
36
+if __name__ == '__main__':
37
+	main()

+ 19
- 0
tunnel/qrnet/qrconf.py View File

@@ -0,0 +1,19 @@
1
+
2
+import os
3
+
4
+netmask = "255.255.255.0"
5
+mtu = 417
6
+ip = ""
7
+camnum = 0
8
+packetDrop = 20
9
+
10
+if os.popen("hostname", "r").read().strip() in ("navi", ):
11
+	ip = "10.44.13.1"
12
+	camnum = 0
13
+else:
14
+	ip = "10.44.13.2"
15
+	camnum = 2
16
+
17
+if __name__ == '__main__':
18
+	print ip
19
+

+ 247
- 0
tunnel/qrnet/qrnet.py View File

@@ -0,0 +1,247 @@
1
+#!/usr/bin/python
2
+
3
+import sys
4
+import os
5
+import re
6
+import time
7
+import struct
8
+import base64
9
+import StringIO
10
+import opencv
11
+from opencv import highgui, adaptors
12
+import qrencode
13
+import zbar
14
+from PIL import Image
15
+import gtk
16
+import gobject
17
+import urllib
18
+import socket
19
+import subprocess
20
+import Queue
21
+import threading
22
+import mutex
23
+
24
+sys.path.append("../../../")
25
+from ether2any import Ether2Any
26
+from conf import Conf
27
+
28
+mqueueMutex = threading.Lock()
29
+squeueMutex = threading.Lock()
30
+
31
+class QrDisplay(gtk.Window):
32
+	def __init__(self, mqueue, squeue, displog, timeout=250):
33
+		super(QrDisplay, self).__init__()
34
+		
35
+		self.fullscreen()
36
+		self.qrimgPadding = 20
37
+		(wx, wy) = self.getHackySize()
38
+		self.timeout = timeout
39
+		self.mqueue = mqueue
40
+		self.squeue = squeue
41
+		self.displog = displog
42
+		
43
+		# build up gui
44
+		self.vbox = gtk.VBox(False, 0)
45
+		self.qrimg = gtk.Image()
46
+		self.qrimg.show()
47
+		self.vbox.pack_start(self.qrimg, True, True, self.qrimgPadding)
48
+		self.vbox.show()
49
+		
50
+		self.pbar = gtk.ProgressBar()
51
+		self.pbar.show()
52
+		self.vbox.pack_start(self.pbar, False, False, 0)
53
+		
54
+		self.add(self.vbox)
55
+		
56
+		# connect signals
57
+		self.connect("destroy", gtk.main_quit)
58
+		self.set_size_request(wx, wy)
59
+		self.maximize()
60
+		self.set_position(gtk.WIN_POS_CENTER)
61
+		self.show()
62
+		self.qrSet("")
63
+		gobject.timeout_add(self.timeout, self.checkQueue)
64
+	
65
+	def checkQueue(self):
66
+		#print "Check queue...",
67
+		mqueueMutex.acquire()
68
+		if not self.mqueue.empty():
69
+			print "new data available"
70
+			bmsg = self.mqueue.get()
71
+			self.qrSet(bmsg)
72
+		mqueueMutex.release()
73
+		#else:
74
+		#	print "no new data available"
75
+		
76
+		# progress
77
+		squeueMutex.acquire()
78
+		if not self.squeue.empty():
79
+			(frames, success) = self.squeue.get()
80
+			if frames > 0:
81
+				self.pbar.set_fraction(success/float(frames))
82
+				self.pbar.set_text("%02d / %02d (%03.02f)" % (success, frames, success / float(frames) * 100.0)) 
83
+			print frames, success
84
+		squeueMutex.release()
85
+		return True
86
+		
87
+	def getHackySize(self):
88
+		""" Obtain current size of screen via xrandr. """
89
+		#p = subprocess.Popen(["bash", "-c", 'xrandr|egrep "\*+"|egrep -o "[0-9]+x[0-9]+"'], stdout=subprocess.PIPE)
90
+		p = subprocess.Popen('xrandr|egrep "\*+"|egrep -o "[0-9]+x[0-9]+"', stdout=subprocess.PIPE)
91
+		p.wait()
92
+		(myout, myin) = p.communicate()
93
+		return map(lambda x: int(x)-self.qrimgPadding, myout.strip().split("x"))
94
+	
95
+	def image2pixbuf(self, im):
96
+		(a, b, c, d) = self.qrimg.get_allocation()
97
+		# correct size, must be 1:1 (so select the smaller one)
98
+		x = min(c-a, d-b)
99
+		if(x < 10):
100
+			x = 100
101
+		imgSize = (x, x)
102
+		im = im.resize(imgSize)
103
+		file1 = StringIO.StringIO()  
104
+		im.save(file1, "ppm")  
105
+		contents = file1.getvalue()  
106
+		file1.close()  
107
+		loader = gtk.gdk.PixbufLoader("pnm")  
108
+		loader.write(contents, len(contents))  
109
+		pixbuf = loader.get_pixbuf()  
110
+		loader.close()
111
+		return pixbuf
112
+	
113
+	def qrSet(self, msg):
114
+		""" Set content of displayed qr image. """
115
+		if msg == None or msg == "":
116
+			msg = "Katze"
117
+		(qrVersion, qrSize, qrImg) = qrencode.encode(msg, 0)
118
+		self.qrimg.set_from_pixbuf(self.image2pixbuf(qrImg))
119
+
120
+
121
+class DisplayThread(threading.Thread):
122
+	""" Thread that runs the GTK-GUI to display outgoing network qr codes. """
123
+	def __init__(self, dev, mqueue, squeue, displog):
124
+		threading.Thread.__init__(self)
125
+		self.dev = dev
126
+		self.quit = False
127
+		self.qrdisplay = QrDisplay(mqueue, squeue, )
128
+		self.mqueue = mqueue
129
+		self.squeue = squeue
130
+		self.displog = displog
131
+		
132
+	def run(self):
133
+		self.displog.info("Display GTK Gui is up and running.")
134
+		gtk.main()
135
+
136
+class CamThread(threading.Thread):
137
+	""" Captures images from a webcam and decodes them.
138
+
139
+	Captures images from the first webcam it can find, decodes them
140
+	and writes them to the interface. """
141
+	def __init__(self, dev, squeue, camlog):
142
+		threading.Thread.__init__(self)
143
+		
144
+		self.dev = dev
145
+		self.squeue = squeue
146
+		self.camlog = camlog
147
+		
148
+		self.frame = 0
149
+		self.reportAfter = 20 # frames
150
+		self.quit = False
151
+		self.success = 0
152
+		self.lastPacket = ""
153
+		
154
+		self.reader = highgui.cvCreateCameraCapture(camnum)
155
+		self.scanner = zbar.ImageScanner()
156
+		self.scanner.parse_config('enable')
157
+	
158
+	def run(self):
159
+		while not self.quit:
160
+			frame = highgui.cvQueryFrame(self.reader)
161
+			self.frame += 1
162
+			
163
+			frame = opencv.cvGetMat(frame)
164
+			img = adaptors.Ipl2PIL(frame)
165
+			width, height = img.size
166
+			zimg = zbar.Image(width, height, 'Y800', img.convert("L").tostring())
167
+			self.scanner.scan(zimg)
168
+			data = None
169
+			for symbol in zimg:
170
+				data = symbol.data
171
+				self.camlog.debug("Data is: %s" % data)
172
+				self.success += 1
173
+				# handle data
174
+				if not self.lastPacket == data:
175
+					self.lastPacket = data
176
+					try:
177
+						msg = base64.b64decode(data)
178
+						(rawtime, packet) = (msg[0:8], msg[8:])
179
+						ptime = struct.unpack("<d", rawtime)
180
+						self.camlog.debug("Network packet! Heade (time) is %s" % (ptime,))
181
+						self.dev.write(packet)
182
+					except base64.binascii.Error:
183
+						print " ==> base64 error"
184
+						self.camlog.error("Base64 error - could not decode packet")
185
+					except struct.error:
186
+						self.camlog.error("Header error - could not extract header information")
187
+				else:
188
+					# packet is already known, discard
189
+					pass
190
+
191
+			# status report to gui
192
+			
193
+			if self.frame % self.reportAfter == 0:
194
+				self.frame = self.success = 0
195
+			# too much status codes? flush 'em
196
+			squeueMutex.acquire()
197
+			if self.squeue.qsize() > self.reportAfter/2:
198
+				while not self.squeue.empty():
199
+					self.squeue.get()
200
+			# add new status code
201
+			self.squeue.put((self.frame, self.success))
202
+			squeueMutex.release()
203
+					
204
+
205
+class QrNet(Ether2Any):
206
+	pidlen = 16
207
+	def __init__(self):
208
+		# device
209
+		Ether2Any.__init__(self, tap=True)
210
+		self.qrlog = self.setupLogging("QrNet")
211
+		self.dev.ifconfig(address=ip, netmask=netmask, mtu=mtu)
212
+		self.packetDrop = packetDrop
213
+		self.mqueue = Queue.Queue()
214
+		self.squeue = Queue.Queue()
215
+		
216
+		# thread starting...
217
+		gtk.gdk.threads_init()
218
+		
219
+		self.cam = CamThread(self.dev, self.squeue, self.setupLogging("CamThread"))
220
+		self.cam.start()
221
+		self.display = DisplayThread(self.dev, self.mqueue, self.squeue, self.setupLogging("DisplayThread"))
222
+		self.display.start()
223
+	
224
+	def sendToNet(self):
225
+		# prepare data for queue && display
226
+		self.qrlog.debug("Data from the device")
227
+		msg = self.dev.read()
228
+		# add acttime to generate "unique" images
229
+		acttime = struct.pack("<d", time.time())
230
+		bmsg = base64.b64encode(acttime + msg)
231
+		self.qrlog.debug("==>" + bmsg)
232
+		
233
+		# add packet to queue, maybe drop packet
234
+		mqueueMutex.acquire()
235
+		if self.mqueue.qsize() < self.packetDrop:
236
+			self.mqueue.put(bmsg)
237
+		else:
238
+			self.qrlog.debug("Dropping packet!")
239
+		mqueueMutex.release()
240
+	
241
+	def quit(self):
242
+		self.display.quit = True
243
+		self.cam.quit = True
244
+
245
+if __name__ == '__main__':
246
+	qrnet = QrNet()
247
+	qrnet.run()

Loading…
Cancel
Save