mirror of https://github.com/midoks/mdserver-web
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.
142 lines
3.9 KiB
142 lines
3.9 KiB
6 years ago
|
#!/usr/bin/env python
|
||
|
# encoding: utf-8
|
||
|
import socket
|
||
|
import math
|
||
|
from struct import pack, unpack
|
||
|
from socket import inet_ntoa
|
||
|
from threading import Timer, Thread
|
||
|
from time import sleep, time
|
||
|
from hashlib import sha1
|
||
|
|
||
|
from simdht_worker import entropy
|
||
|
from bencode import bencode, bdecode
|
||
|
|
||
|
|
||
|
BT_PROTOCOL = "BitTorrent protocol"
|
||
|
BT_MSG_ID = 20
|
||
|
EXT_HANDSHAKE_ID = 0
|
||
|
|
||
|
def random_id():
|
||
|
hash = sha1()
|
||
|
hash.update(entropy(20))
|
||
|
return hash.digest()
|
||
|
|
||
|
def send_packet(the_socket, msg):
|
||
|
the_socket.send(msg)
|
||
|
|
||
|
def send_message(the_socket, msg):
|
||
|
msg_len = pack(">I", len(msg))
|
||
|
send_packet(the_socket, msg_len + msg)
|
||
|
|
||
|
def send_handshake(the_socket, infohash):
|
||
|
bt_header = chr(len(BT_PROTOCOL)) + BT_PROTOCOL
|
||
|
ext_bytes = "\x00\x00\x00\x00\x00\x10\x00\x00"
|
||
|
peer_id = random_id()
|
||
|
packet = bt_header + ext_bytes + infohash + peer_id
|
||
|
|
||
|
send_packet(the_socket, packet)
|
||
|
|
||
|
def check_handshake(packet, self_infohash):
|
||
|
try:
|
||
|
bt_header_len, packet = ord(packet[:1]), packet[1:]
|
||
|
if bt_header_len != len(BT_PROTOCOL):
|
||
|
return False
|
||
|
except TypeError:
|
||
|
return False
|
||
|
|
||
|
bt_header, packet = packet[:bt_header_len], packet[bt_header_len:]
|
||
|
if bt_header != BT_PROTOCOL:
|
||
|
return False
|
||
|
|
||
|
packet = packet[8:]
|
||
|
infohash = packet[:20]
|
||
|
if infohash != self_infohash:
|
||
|
return False
|
||
|
|
||
|
return True
|
||
|
|
||
|
def send_ext_handshake(the_socket):
|
||
|
msg = chr(BT_MSG_ID) + chr(EXT_HANDSHAKE_ID) + bencode({"m":{"ut_metadata": 1}})
|
||
|
send_message(the_socket, msg)
|
||
|
|
||
|
def request_metadata(the_socket, ut_metadata, piece):
|
||
|
"""bep_0009"""
|
||
|
msg = chr(BT_MSG_ID) + chr(ut_metadata) + bencode({"msg_type": 0, "piece": piece})
|
||
|
send_message(the_socket, msg)
|
||
|
|
||
|
def get_ut_metadata(data):
|
||
|
ut_metadata = "_metadata"
|
||
|
index = data.index(ut_metadata)+len(ut_metadata) + 1
|
||
|
return int(data[index])
|
||
|
|
||
|
def get_metadata_size(data):
|
||
|
metadata_size = "metadata_size"
|
||
|
start = data.index(metadata_size) + len(metadata_size) + 1
|
||
|
data = data[start:]
|
||
|
return int(data[:data.index("e")])
|
||
|
|
||
|
def recvall(the_socket, timeout=5):
|
||
|
the_socket.setblocking(0)
|
||
|
total_data = []
|
||
|
data = ""
|
||
|
begin = time()
|
||
|
|
||
|
while True:
|
||
|
sleep(0.05)
|
||
|
if total_data and time()-begin > timeout:
|
||
|
break
|
||
|
elif time()-begin > timeout*2:
|
||
|
break
|
||
|
try:
|
||
|
data = the_socket.recv(1024)
|
||
|
if data:
|
||
|
total_data.append(data)
|
||
|
begin = time()
|
||
|
except Exception:
|
||
|
pass
|
||
|
return "".join(total_data)
|
||
|
|
||
|
def download_metadata(address, infohash, metadata_queue, timeout=5):
|
||
|
metadata = None
|
||
|
start_time = time()
|
||
|
try:
|
||
|
the_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
|
the_socket.settimeout(timeout)
|
||
|
the_socket.connect(address)
|
||
|
|
||
|
# handshake
|
||
|
send_handshake(the_socket, infohash)
|
||
|
packet = the_socket.recv(4096)
|
||
|
|
||
|
# handshake error
|
||
|
if not check_handshake(packet, infohash):
|
||
|
return
|
||
|
|
||
|
# ext handshake
|
||
|
send_ext_handshake(the_socket)
|
||
|
packet = the_socket.recv(4096)
|
||
|
|
||
|
# get ut_metadata and metadata_size
|
||
|
ut_metadata, metadata_size = get_ut_metadata(packet), get_metadata_size(packet)
|
||
|
#print 'ut_metadata_size: ', metadata_size
|
||
|
|
||
|
# request each piece of metadata
|
||
|
metadata = []
|
||
|
for piece in range(int(math.ceil(metadata_size/(16.0*1024)))):
|
||
|
request_metadata(the_socket, ut_metadata, piece)
|
||
|
packet = recvall(the_socket, timeout) #the_socket.recv(1024*17) #
|
||
|
metadata.append(packet[packet.index("ee")+2:])
|
||
|
|
||
|
metadata = "".join(metadata)
|
||
|
#print 'Fetched', bdecode(metadata)["name"], "size: ", len(metadata)
|
||
|
|
||
|
except socket.timeout:
|
||
|
pass
|
||
|
except Exception, e:
|
||
|
pass #print e
|
||
|
|
||
|
finally:
|
||
|
the_socket.close()
|
||
|
metadata_queue.put((infohash, address, metadata, 'pt', start_time))
|
||
|
|