You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
6.3 KiB

13 years ago
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Sebastian Lohff <seba@seba-geek.de>
# Licensed under GPL v3 or later
13 years ago
import sys
sys.path.append("../../../")
import base64
13 years ago
import irclib
import logging
13 years ago
import re
import random
import subprocess
import time
from conf import Conf
from ether2any import Ether2Any
13 years ago
from ether2any.helper import getDstMacFromPkt, isBroadcast, binToHexStr
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):
13 years ago
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()