diff --git a/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py b/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py index ae674c00..defa9266 100644 --- a/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py +++ b/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py @@ -3,7 +3,7 @@ import urllib.request import struct import socket -import bencode +import bencode_open from lib.subtl.subtl import UdpTrackerClient import socks import sockshandler @@ -133,9 +133,7 @@ class SiteAnnouncerPlugin(object): # Decode peers try: - peer_data = bencode.decode(response)["peers"] - if type(peer_data) is not bytes: - peer_data = peer_data.encode() + peer_data = bencode_open.loads(response)[b"peers"] response = None peer_count = int(len(peer_data) / 6) peers = [] diff --git a/requirements.txt b/requirements.txt index 8e756f66..7c063e3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,6 @@ PySocks>=1.6.8 pyasn1 websocket_client gevent-websocket -bencode.py coincurve python-bitcoinlib maxminddb diff --git a/src/lib/bencode_open/LICENSE b/src/lib/bencode_open/LICENSE new file mode 100644 index 00000000..f0e46d71 --- /dev/null +++ b/src/lib/bencode_open/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Ivan Machugovskiy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/lib/bencode_open/__init__.py b/src/lib/bencode_open/__init__.py new file mode 100644 index 00000000..e3c783cc --- /dev/null +++ b/src/lib/bencode_open/__init__.py @@ -0,0 +1,160 @@ +def loads(data): + if not isinstance(data, bytes): + raise TypeError("Expected 'bytes' object, got {}".format(type(data))) + + offset = 0 + + + def parseInteger(): + nonlocal offset + + offset += 1 + had_digit = False + abs_value = 0 + + sign = 1 + if data[offset] == ord("-"): + sign = -1 + offset += 1 + while offset < len(data): + if data[offset] == ord("e"): + # End of string + offset += 1 + if not had_digit: + raise ValueError("Integer without value") + break + if ord("0") <= data[offset] <= ord("9"): + abs_value = abs_value * 10 + int(chr(data[offset])) + had_digit = True + offset += 1 + else: + raise ValueError("Invalid integer") + else: + raise ValueError("Unexpected EOF, expected integer") + + if not had_digit: + raise ValueError("Empty integer") + + return sign * abs_value + + + def parseString(): + nonlocal offset + + length = int(chr(data[offset])) + offset += 1 + + while offset < len(data): + if data[offset] == ord(":"): + offset += 1 + break + if ord("0") <= data[offset] <= ord("9"): + length = length * 10 + int(chr(data[offset])) + offset += 1 + else: + raise ValueError("Invalid string length") + else: + raise ValueError("Unexpected EOF, expected string contents") + + if offset + length > len(data): + raise ValueError("Unexpected EOF, expected string contents") + offset += length + + return data[offset - length:offset] + + + def parseList(): + nonlocal offset + + offset += 1 + values = [] + + while offset < len(data): + if data[offset] == ord("e"): + # End of list + offset += 1 + return values + else: + values.append(parse()) + + raise ValueError("Unexpected EOF, expected list contents") + + + def parseDict(): + nonlocal offset + + offset += 1 + items = {} + + while offset < len(data): + if data[offset] == ord("e"): + # End of list + offset += 1 + return items + else: + key, value = parse(), parse() + if not isinstance(key, bytes): + raise ValueError("A dict key must be a byte string") + if key in items: + raise ValueError("Duplicate dict key: {}".format(key)) + items[key] = value + + raise ValueError("Unexpected EOF, expected dict contents") + + + def parse(): + nonlocal offset + + if data[offset] == ord("i"): + return parseInteger() + elif data[offset] == ord("l"): + return parseList() + elif data[offset] == ord("d"): + return parseDict() + elif ord("0") <= data[offset] <= ord("9"): + return parseString() + + raise ValueError("Unknown type specifier: '{}'".format(chr(data[offset]))) + + result = parse() + + if offset != len(data): + raise ValueError("Expected EOF, got {} bytes left".format(len(data) - offset)) + + return result + + +def dumps(data): + result = bytearray() + + + def convert(data): + nonlocal result + + if isinstance(data, str): + raise ValueError("bencode only supports bytes, not str. Use encode") + + if isinstance(data, bytes): + result += str(len(data)).encode() + b":" + data + elif isinstance(data, int): + result += b"i" + str(data).encode() + b"e" + elif isinstance(data, list): + result += b"l" + for val in data: + convert(val) + result += b"e" + elif isinstance(data, dict): + result += b"d" + for key in sorted(data.keys()): + if not isinstance(key, bytes): + raise ValueError("Dict key can only be bytes, not {}".format(type(key))) + convert(key) + convert(data[key]) + result += b"e" + else: + raise ValueError("bencode only supports bytes, int, list and dict") + + + convert(data) + + return bytes(result)