From e649adbe376b173cea6e7254ba185f4d8d589d75 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 24 Jun 2016 22:45:56 +0200 Subject: [PATCH] Framework for extended message encoding - helper classes for encoding/decoding messages - includes both old as well as new extended one (msgpack+zlib) - the classes are unused yet and are supposed to be for experimenting --- bencode/BTL.py | 2 - bencode/__init__.py | 142 ---------------------------------------- src/helper_msgcoding.py | 86 ++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 144 deletions(-) delete mode 100644 bencode/BTL.py delete mode 100644 bencode/__init__.py create mode 100644 src/helper_msgcoding.py diff --git a/bencode/BTL.py b/bencode/BTL.py deleted file mode 100644 index 58b0d6d2..00000000 --- a/bencode/BTL.py +++ /dev/null @@ -1,2 +0,0 @@ -class BTFailure(Exception): - pass diff --git a/bencode/__init__.py b/bencode/__init__.py deleted file mode 100644 index ae5f6e9a..00000000 --- a/bencode/__init__.py +++ /dev/null @@ -1,142 +0,0 @@ -# coding: utf-8 -# The contents of this file are subject to the BitTorrent Open Source License -# Version 1.1 (the License). You may not copy or use this file, in either -# source code or executable form, except in compliance with the License. You -# may obtain a copy of the License at http://www.bittorrent.com/license/. -# -# Software distributed under the License is distributed on an AS IS basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. - -# Written by Petru Paler -# Modifed by Peter Ĺ urda - -from BTL import BTFailure - - -def decode_int(x, f): - f += 1 - newf = x.index('e', f) - n = int(x[f:newf]) - if x[f] == '-': - if x[f + 1] == '0': - raise ValueError - elif x[f] == '0' and newf != f+1: - raise ValueError - return (n, newf+1) - -def decode_string(x, f): - colon = x.index(':', f) - n = int(x[f:colon]) - if x[f] == '0' and colon != f+1: - raise ValueError - colon += 1 - return (x[colon:colon+n], colon+n) - -def decode_list(x, f): - r, f = [], f+1 - while x[f] != 'e': - v, f = decode_func[x[f]](x, f) - r.append(v) - return (r, f + 1) - -def decode_dict(x, f): - r, f = {}, f+1 - while x[f] != 'e': - k, f = decode_string(x, f) - r[k], f = decode_func[x[f]](x, f) - return (r, f + 1) - -decode_func = {} -decode_func['l'] = decode_list -decode_func['d'] = decode_dict -decode_func['i'] = decode_int -decode_func['0'] = decode_string -decode_func['1'] = decode_string -decode_func['2'] = decode_string -decode_func['3'] = decode_string -decode_func['4'] = decode_string -decode_func['5'] = decode_string -decode_func['6'] = decode_string -decode_func['7'] = decode_string -decode_func['8'] = decode_string -decode_func['9'] = decode_string - -def bdecode(x): - try: - r, l = decode_func[x[0]](x, 0) - except (IndexError, KeyError, ValueError): - raise BTFailure("not a valid bencoded string") - if l != len(x): - raise BTFailure("invalid bencoded value (data after valid prefix)") - return r - -from types import StringType, IntType, LongType, DictType, ListType, TupleType, UnicodeType, NoneType - - -class Bencached(object): - - __slots__ = ['bencoded'] - - def __init__(self, s): - self.bencoded = s - -def encode_bencached(x,r): - r.append(x.bencoded) - -def encode_int(x, r): - r.extend(('i', str(x), 'e')) - -def encode_bool(x, r): - if x: - encode_int(1, r) - else: - encode_int(0, r) - -def encode_string(x, r): - r.extend((str(len(x)), ':', x)) - -def encode_unicode(x, r): - raw = x.encode('utf8') - r.extend((str(len(raw)), ':', raw)) - -def encode_none(x, r): - r.extend('0:') - -def encode_list(x, r): - r.append('l') - for i in x: - encode_func[type(i)](i, r) - r.append('e') - -def encode_dict(x,r): - r.append('d') - ilist = x.items() - ilist.sort() - for k, v in ilist: - r.extend((str(len(k)), ':', k)) - encode_func[type(v)](v, r) - r.append('e') - -encode_func = {} -encode_func[Bencached] = encode_bencached -encode_func[IntType] = encode_int -encode_func[LongType] = encode_int -encode_func[StringType] = encode_string -encode_func[ListType] = encode_list -encode_func[TupleType] = encode_list -encode_func[DictType] = encode_dict -encode_func[UnicodeType] = encode_unicode -encode_func[NoneType] = encode_none - -try: - from types import BooleanType - encode_func[BooleanType] = encode_bool -except ImportError: - pass - -def bencode(x): - r = [] - encode_func[type(x)](x, r) - return ''.join(r) diff --git a/src/helper_msgcoding.py b/src/helper_msgcoding.py new file mode 100644 index 00000000..0aa1cda9 --- /dev/null +++ b/src/helper_msgcoding.py @@ -0,0 +1,86 @@ +#!/usr/bin/python2.7 + +import msgpack +import zlib + +from debug import logger + +BITMESSAGE_ENCODING_IGNORE = 0 +BITMESSAGE_ENCODING_TRIVIAL = 1 +BITMESSAGE_ENCODING_SIMPLE = 2 +BITMESSAGE.ENCODING_EXTENDED = 3 + +class MsgEncode(object): + def __init__(self, message, encoding = BITMESSAGE_ENCODING_SIMPLE): + self.data = None + self.encoding = encoding + self.length = 0 + if self.encoding == BITMESSAGE_ENCODING_EXTENDED: + self.encodeExtended(message) + elif self.encoding == BITMESSAGE_ENCODING_SIMPLE: + self.encodeSimple(message) + elif self.encoding == BITMESSAGE_ENCODING_TRIVIAL: + self.encodeTrivial(message) + + def encodeExtended(self, message): + try: + self.data = zlib.compress(msgpack.dumps({"": "message", "subject": message['subject'], "message": ['body']}), 9) + except zlib.error: + logger.error ("Error compressing message") + raise + except msgpack.exceptions.PackException: + logger.error ("Error msgpacking message") + raise + self.length = len(self.data) + + def encodeSimple(self, message): + self.data = 'Subject:' + message['subject'] + '\n' + 'Body:' + message['body'] + self.length = len(self.data) + + def encodeTrivial(self, message): + self.data = message['body'] + self.length = len(self.data) + +class MsgDecode(object): + def __init__(self, encoding, data): + self.encoding = encoding + if self.encoding == BITMESSAGE_ENCODING_EXTENDED: + self.decodeExtended(data) + elif self.encoding in [BITMESSAGE_ENCODING_SIMPLE, BITMESSAGE_ENCODING_TRIVIAL]: + self.decodeSimple(data) + return + + def decodeExtended(self, data): + try: + tmp = msgpack.loads(zlib.decompress(data)) + except zlib.error: + logger.error ("Error decompressing message") + raise + except (msgpack.exceptions.UnpackException, + msgpack.exceptions.ExtraData): + logger.error ("Error msgunpacking message") + raise + try: + if tmp[""] == "message": + self.body = tmp["body"] + self.subject = tmp["subject"] + except: + logger.error ("Malformed message") + raise + + def decodeSimple(self, data): + bodyPositionIndex = string.find(data, '\nBody:') + if bodyPositionIndex > 1: + subject = message[8:bodyPositionIndex] + # Only save and show the first 500 characters of the subject. + # Any more is probably an attack. + subject = subject[:500] + body = message[bodyPositionIndex + 6:] + else: + subject = '' + body = message + # Throw away any extra lines (headers) after the subject. + if subject: + subject = subject.splitlines()[0] + self.subject = subject + self.message = body