diff --git a/tunnel/rfc1149/README b/tunnel/rfc1149/README new file mode 100644 index 0000000..cc16ea1 --- /dev/null +++ b/tunnel/rfc1149/README @@ -0,0 +1,7 @@ + + +Installation +============ +You need + * tweepy >= 1.8 (https://github.com/tweepy/tweepy.git) + * python-bytearray diff --git a/tunnel/rfc1149/conf.py b/tunnel/rfc1149/conf.py new file mode 100644 index 0000000..c65f23e --- /dev/null +++ b/tunnel/rfc1149/conf.py @@ -0,0 +1,35 @@ +# ___________________________________________________ +# | | +# | ircvpn - irc virtual public network configuration | +# |___________________________________________________| + +import os + +Conf = { + # ======== network settings ======== + # ipsettings for the device + 'devname': '', + 'network': + { + 'address': '10.10.10.74', + 'netmask': '255.255.255.0', + #gateway: '', + 'mtu': 1400, + }, + + # ======== Twitter settings ======== + 'twitter': + { + # account to read network traffic from + 'endpoint': None, + + # if set to None, these will be overwritten by config_auth.py + # if they are set to none there, this script will give you an + # url to authorize this script to twitter and write the + # access key / access secret to config_auth.py + 'ACCESS_KEY': None, + 'ACCESS_SECRET': None, + }, +} + +import conf_auth diff --git a/tunnel/rfc1149/conf_auth.py b/tunnel/rfc1149/conf_auth.py new file mode 100644 index 0000000..fdb6a71 --- /dev/null +++ b/tunnel/rfc1149/conf_auth.py @@ -0,0 +1,7 @@ +from conf import Conf + +# WARNING: This config will be overwritten if you use the scripts internal +# way to get an access key. +if not Conf['twitter']['ACCESS_KEY']: + Conf['twitter']['ACCESS_KEY'] = None + Conf['twitter']['ACCESS_SECRET'] = None diff --git a/tunnel/rfc1149/rfc1149.py b/tunnel/rfc1149/rfc1149.py new file mode 100755 index 0000000..3af1957 --- /dev/null +++ b/tunnel/rfc1149/rfc1149.py @@ -0,0 +1,129 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import base64 +import logging +import math +import re +import sys +import threading +import time +import tweepy +from uphelper import UPHelper + +sys.path.append("../../../") + +from conf import Conf +from ether2any import Ether2Any +from ether2any.helper import getDstMacFromPkt, isBroadcast, binToHexStr + +class TwittStreamHandler(tweepy.StreamListener): + def __init__(self, dev): + super(TwittStreamHandler, self).__init__() + self.dev = dev + self.fragments = {} + + def on_status( self, status ): + print '-' * 20 + print "incoming:", unicode(status.text) + print "hex:", binToHexStr(status.text), len(status.text) + # reassemble messages, put them together if nessecary. + + def on_limit(self, track): + print "We got limited ", track + print "At the moment there is no error-handling for this, so we just kill everything. Remember: This software doesn't even deserve the label 'alpha' ;)" + sys.exit(1) + + def on_error(self, status_code): + print "We got an error code: ", status_code + if status_code == 401: + print "Better check 'yer twitter-credentials." + sys.exit(2) + else: + print "At the moment there is no error-handling for this, so we just kill everything. Remember: This software doesn't even deserve the label 'alpha' ;)" + sys.exit(1) + + def on_timeout(self, status_code): + print "Got an timeout: ", status_code + print "At the moment there is no error-handling for this, so we just kill everything. Remember: This software doesn't even deserve the label 'alpha' ;)" + +class DownstreamThread(threading.Thread): + def __init__(self, dev, auth, endpoint): + threading.Thread.__init__(self) + self.auth = auth + self.endpoint = endpoint + self.dev = dev + self.daemon = True + + def run(self): + stream = tweepy.Stream(auth=self.auth, listener=TwittStreamHandler(self.dev)) + stream.userstream() + stream.filter(self.endpoint, None) + stream.sample() + +class RFC1149(Ether2Any): + def __init__(self): + Ether2Any.__init__(self, tap=False) + + network = Conf.get("network", {'mtu': 1400}) + self.twitterConf = Conf.get("twitter", None) + self.endpoint = self.twitterConf['endpoint'] + if not self.endpoint: + print "No endpoint in configuration, please add one." + sys.exit(1) + self.dev.ifconfig(**network) + self.dev.up() + + self._setupTwitter() + self.downstream = DownstreamThread(dev=self.dev, auth=self.auth, endpoint=self.endpoint) + self.downstream.start() + + def _requestTwitterTokens(self): + auth = tweepy.OAuthHandler(self._dec(self.ck), self._dec(self.cs)) + pass + + def _setupTwitter(self): + ck = [17, 39, 65, 39, 25, 22, 38, 30, 20, 38, 33, 69, 27, 0, 61, + 31, 61, 34, 42, 32] + cs = [71, 37, 71, 37, 57, 36, 18, 2, 59, 64, 63, 58, 23, 61, 74, + 30, 34, 68, 38, 33, 56, 1, 3, 74, 29, 9, 41, 32, 33, 7, 52, + 8, 70, 20, 30, 25, 38, 51, 57, 66, 53, 0, 42] + self.auth = tweepy.OAuthHandler(self._dec(ck), self._dec(cs)) + if not self.twitterConf['ACCESS_KEY']: + # request tokens, get access, write tokens down. + auth_url = self.auth.get_authorization_url() + print "We have no access token for a twitter account. Please visit the" + print "url printed down below, login and report back with the PIN." + print + print " Authorization URL: %s" % auth_url + print + verifier = raw_input('PIN: ').strip() + self.auth.get_access_token(verifier) + self.twitterConf['ACCESS_KEY'] = self.auth.access_token.key + self.twitterConf['ACCESS_SECRET'] = self.auth.access_token.secret + authConf = open("conf_auth.py", "w") + authConf.write("""from conf import Conf + +# WARNING: This config was overwritten! If you change it be sure +# that you know, what you are doing. +if not Conf['twitter']['ACCESS_KEY']: + Conf['twitter']['ACCESS_KEY'] = "%s" + Conf['twitter']['ACCESS_SECRET'] = "%s" +""" % (self.twitterConf['ACCESS_KEY'], self.twitterConf['ACCESS_SECRET'])) + authConf.close() + self.auth.set_access_token(self.twitterConf['ACCESS_KEY'], self.twitterConf['ACCESS_SECRET']) + self.api = tweepy.API(self.auth) + + def _dec(self, s): + """ ... """ + return "".join(map(lambda x: chr(x+48), reversed(s))) + + def sendToNet(self, packet): + for subPacket in UPHelper.encode(packet): + print subPacket + +if __name__ == '__main__': + rfc = RFC1149() + print "Starting RFC1149 ip-over-twitter service..." + rfc.run() + diff --git a/tunnel/rfc1149/uphelper.py b/tunnel/rfc1149/uphelper.py new file mode 100644 index 0000000..c46ac6d --- /dev/null +++ b/tunnel/rfc1149/uphelper.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import random +import bitarray + +class UPHelper(): + """ The Unicode Packet Helper + + Twitter supports 140 chars, while a char can be a unicode + character. For a unicode character there are 2^20 possibilities. + For the sake of lazyness we put two bytes in each character, using + only 2^16. The remaining 4 bits can be used for metadata or whatever. + + The header in the metadata is as following: + + <9 bits length of payload> + <32 bit random paket id>""" + + @staticmethod + def intToBits(n, length): + """ Convert the number n to a bitarray of length. """ + i = 0 + c = 1 + ret = [False] * length + while i < length: + ret[length-(i+1)] = (n & c) > 0 + i += 1 + c <<= 1 + return ret + + @staticmethod + def bitsToInt(bits): + ret = 0 + for i in bits: + ret = (ret << 1) | (i and 1 or 0) + return ret + + @staticmethod + def encode(data): + """ Generate list of packets with a header from data. """ + packetId = random.randint(0, 2**32) + fragments = [] + while len(data) > 280: + fragments.append(data[0:280]) + data = data[280:] + if len(data) > 0: + fragments.append(data) + + # convert to twitter message + for y in range(len(fragments)): + fragment = fragments[y] + lenX = len(fragment) + # pad packet if it is not long enouth / not aligned + if len(fragment) < 2*11: + fragment = fragment + "\x00" * (2*11-len(fragment)) + if len(fragment) % 2 == 1: + fragment += "\x00" + + # write header (bits: 1 fragment, 9 length, 32 id) + header = bitarray.bitarray(1) + # write fragment-bit + header[0] = (y+1 == len(fragments)) + # append packet length + header.extend(UPHelper.intToBits(lenX, 9)) + # add packet id + header.extend(UPHelper.intToBits(packetId, 32)) + # padding to complete last 4 bytes + header.extend([False, False]) + + i = 0 + h = 0 + ret = "" + while i+1 < len(fragment): + val = ord(fragment[i]) << 8 | ord(fragment[i+1]) + if h < 11: + val |= UPHelper.bitsToInt(header[h*4:(h+1)*4]) << 16 + h += 1 + ret += unichr(val) + i += 2 + fragments[y] = ret + return fragments + + @staticmethod + def decode(packet): + """ Decodes an unicodestring (packet) back to header + data + + Returns: tupel(isFragmented, packetLen, packetId, data) """ + if len(packet) < 11: + raise ValueError("This is not a valid packet, header is too short (should be at least 11, is %d)" % len(packet)) + header = bitarray.bitarray() + for i in range(11): + header.extend("{:04b}".format(ord(packet[i]) >> 16)) + isFragmented = header[0] + + packetLen = UPHelper.bitsToInt(header[1:9]) + packetId = UPHelper.bitsToInt(header[9:]) + rawData = map(lambda x: ord(x) & 0xFFFF, packet) + data = [] + for p in rawData: + data.append(chr(p >> 8)) + data.append(chr(p & 255)) + data = "".join(data) + + return (isFragmented, packetLen, packetId, data) + +if __name__ == '__main__': + print UPHelper.encode("foo") + print UPHelper.decode(UPHelper.encode("foo")[0]) + msg = "".join([chr(i) for i in range(256)]) + msg += "".join([chr(i) for i in range(256)]) + p = UPHelper.encode(msg) + print repr(msg) + for x in p: + print UPHelper.decode(x) +