first commit

This commit is contained in:
MisterDaneel 2019-02-16 16:20:15 +01:00
commit 363aafe901
8 changed files with 298 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.swp
*.swo
*.pyc
*.torrent
venv
*.pdf
*.pcapng
__pycache__
*.pyc

0
code/__init__.py Normal file
View File

90
code/decoding_bencoded.py Normal file
View File

@ -0,0 +1,90 @@
import re
import logging
import binascii
class bencoding():
def __init__(self):
self.decimal_match = re.compile('\d')
self.data = b''
self.dict = {}
def get_dict(self, key):
if key not in self.dict:
return ''
start = self.dict[key][0]
end = self.dict[key][1]
return self.data[start:end]
def get_item(self, chunks):
item = chunks[self.i]
self.i += 1
if not type(item) == str:
item = bytes([item])
try:
item = item.decode('utf-8')
except:
item = '\\x{}'.format(binascii.hexlify(item))
return item
def decoding_byte_string(self, chunks, item):
# logging.debug('decoding string')
num = ''
while self.decimal_match.search(item):
num += item
item = self.get_item(chunks)
line = ''
for i in range(int(num)):
line += self.get_item(chunks)
return line
def decoding_integer(self, chunks):
# logging.debug('decoding integer')
item = self.get_item(chunks)
num = ''
while item != 'e':
num += item
item = self.get_item(chunks)
return int(num)
def decoding_list(self, chunks):
# logging.debug('decoding list')
item = self.get_item(chunks)
list = []
while item != 'e':
self.i -= 1
list.append(self._dechunk(chunks))
item = self.get_item(chunks)
return list
def decoding_dictionnary(self, chunks):
# logging.debug('decoding dictionnary')
item = self.get_item(chunks)
hash = {}
while item != 'e':
self.i -= 1
key = self._dechunk(chunks)
start = self.i
hash[key] = self._dechunk(chunks)
end = self.i
self.dict[key] = (start, end)
item = self.get_item(chunks)
return hash
def _dechunk(self, chunks):
item = self.get_item(chunks)
if item == 'd':
return self.decoding_dictionnary(chunks)
elif item == 'l':
return self.decoding_list(chunks)
elif item == 'i':
return self.decoding_integer(chunks)
elif self.decimal_match.search(item):
return self.decoding_byte_string(chunks, item)
raise "Invalid input!"
def bdecode(self, data):
self.data = data
chunks = list(self.data)
self.i = 0
root = self._dechunk(chunks)
return root

28
code/pretty.py Normal file
View File

@ -0,0 +1,28 @@
import requests
from pprint import pformat
def get_headers(headers):
res = ''
for k, v in headers.items():
res += '{}: {}\n'.format(k, v)
return res
def pretty_GET(url, headers, params):
req = requests.Request('GET', url, headers=headers, params=params)
s = requests.Session()
prepared = s.prepare_request(req)
p = '-----START-----\n'
p +=('{} {}\n{}'.format(prepared.method, prepared.url,
get_headers(prepared.headers),
)
)
if prepared.body:
pi += prepared.body
p += '------END------'
return p
def pretty_data(data):
return pformat(data)

103
code/torrent.py Normal file
View File

@ -0,0 +1,103 @@
from code.decoding_bencoded import bencoding
from code.torrentclientfactory import Transmission292
from code.pretty import pretty_data, pretty_GET
from hashlib import sha1
from urllib.parse import quote_plus
import requests
import logging
import random
from tqdm import tqdm
from time import sleep
from struct import unpack
logging.basicConfig(level=logging.DEBUG)
class torrent():
def __init__(self, torrent_tile):
with open(torrent_tile, 'rb') as tf:
data = tf.read()
self.b_enc = bencoding()
self.metainfo = self.b_enc.bdecode(data)
self.info = self.metainfo['info']
print(pretty_data(self.info))
self.torrentclient = Transmission292(self.tracker_info_hash())
def tracker_info_hash(self):
raw_info = self.b_enc.get_dict('info')
hash_factory = sha1()
hash_factory.update(raw_info)
hashed = hash_factory.hexdigest()
sha = bytearray.fromhex(hashed)
return str(quote_plus(sha))
def send_request(self, params, headers):
url = self.metainfo['announce']
print(pretty_GET(url, headers, params))
while True:
try:
r = requests.get(url, params=params, headers=headers)
except requests.exceptions.ConnectionError as e:
sleep(1)
continue
break
return r.content
def tracker_start_request(self):
tc = self.torrentclient
headers = tc.get_headers()
params = tc.get_query(uploaded=0,
downloaded=0,
left=self.info['length'],
event='started')
print('----------- First Command to Tracker --------')
content = self.send_request(params, headers)
self.tracker_response_parser(content)
def tracker_response_parser(self, tr_response):
b_enc = bencoding()
response = b_enc.bdecode(tr_response)
print('----------- Received Tracker Response --------')
print(pretty_data(response))
raw_peers = b_enc.get_dict('peers')
i = 0
peers = []
while i<len(raw_peers)-6:
peer = raw_peers[i:i+6]
i+=6
unpacked_ip = unpack('BBBB', peer[0:4])
ip = ".".join(str(i) for i in unpacked_ip)
unpacked_port = unpack('!H', peer[4:6])
port = unpacked_port[0]
peers.append((ip, port))
self.interval = response['interval']
def wait(self):
pbar = tqdm(total=self.interval)
print('sleep: {}'.format(self.interval))
t = 0
while t < self.interval:
t += 1
pbar.update(1)
sleep(1)
pbar.close()
def tracker_process(self):
while True:
print('----------- Sending Command to Tracker --------')
min_up = self.interval-(self.interval*0.1)
max_up = self.interval
uploaded = 500000*random.randint(min_up, max_up)
tc = self.torrentclient
headers = tc.get_headers()
params = tc.get_query(uploaded=uploaded,
downloaded=0,
left=0,
event='stopped')
content = self.send_request(params, headers)
self.tracker_response_parser(content)
self.wait()

View File

@ -0,0 +1,60 @@
import random
import string
class Transmission292():
def __init__(self, info_hash):
self.name = "Transmission 2.92 (14714)"
parameters = {}
# urlencoded 20-byte SHA1 hash of the value of the info key from the Metainfo file
parameters['info_hash'] = info_hash
# urlencoded 20-byte string used as a unique ID for the client
parameters["peer_id"] = self.generate_peer_id()
# The port number that the client is listening on
parameters["port"] = random.randint(1025, 65535)
# Number of peers that the client would like to receive from the tracker
parameters["numwant"] = 80
# An additional identification that is not shared with any other peers
parameters["key"] = self.generate_key()
# Setting this to 1 indicates that the client accepts a compact response
parameters["compact"] = 0
# Setting this to 1 indicates that the client accepts crypto
parameters["supportcrypto"] = 1
self.parameters = parameters
def get_headers(self):
headers = {}
headers['User-Agent'] = 'Transmission/2.92'
headers['Accept'] = '*/*'
headers['Accept-Encoding'] = 'Accept-Encoding: gzip;q=1.0, deflate, identity'
return headers
def get_query(self, uploaded, downloaded, left, event=None):
# The total amount uploaded (since the client sent the 'started' event)
self.parameters["uploaded"] = uploaded
# The total amount downloaded (since the client sent the 'started' event)
self.parameters["downloaded"] = downloaded
# The number of bytes this client still has to download
self.parameters["left"] = left
# If specified, must be one of started, completed, stopped
if event:
self.parameters["event"] = event
params = '&'.join('{}={}'.format(k, v)
for k, v in self.parameters.items())
return params
def id_generator(self, chars, size):
id = ''
for _ in range(size):
id += random.choice(chars)
return id
def generate_peer_id(self):
chars = string.ascii_lowercase + string.digits
rand_id = self.id_generator(chars, 12)
peer_id = "-TR2920-" + rand_id
return peer_id
def generate_key(self):
chars = 'ABCDEF' + string.digits
key = self.id_generator(chars, 8)
return key

6
main.py Normal file
View File

@ -0,0 +1,6 @@
from code.torrent import torrent
to = torrent('test.torrent')
rep = to.tracker_start_request()
to.tracker_process()

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
requests
tqdm