BitTorrent客户端:从跟踪器获取对等列表[Python] [英] BitTorrent Client : Getting Peer List From Trackers [Python]
问题描述
以下代码适用于我。使用跟踪器URL和有效载荷字典与所有URL参数调用announce_udp()。
$ b
import binascii,urllib,socket,random ,struct b $ b from bcode import bdecode $ b $ from urlparse import urlparse,urlunsplit
$ b $ def announce_udp(tracker,payload):
tracker = tracker.lower()
parsed = urlparse(tracker)
#Teporarly将udp://更改为http://以获取主机名和portnumbe
url = parsed.geturl()[3:]
url =http+ url
hostname = urlparse(url).hostname
port = urlparse(url).port
$ b sock = socket.socket( socket.AF_INET,socket.SOCK_DGRAM)
sock.settimeout(8)
conn =(socket.gethostbyname(hostname),port)
#sock.bind((socket.gethostname(), s_port))
#获取连接ID
req,transaction_id = udp_create_connection_request()
sock.sendto(req,conn);
buf = sock.recvfrom(2048)[0]
connection_id = udp_parse_connection_response(buf,transaction_id)
#Annoucing
s_port = sock.getsockname()[1 ] #get port连接到的端口号
req,transaction_id = udp_create_announce_request(connection_id,payload,s_port)
sock.sendto(req,conn)
printAnnounce Request Sent
$ buf = sock.recvfrom(2048)[0]
print收到的响应
返回udp_parse_announce_response(buf,transaction_id)
def udp_create_announce_request(connection_id,payload,s_port) :
action = 0x1 #action(1 = announce)
transaction_id = udp_get_transaction_id()
#print2.Transaction ID:,transaction_id
buf = struct.pack( !q,connection_id)#first 8字节是连接id
buf + = struct.pack(!i,action)#next 4字节是动作
buf + = struct.pack(! i,transaction_id)#后面跟着4字节的事务ID
buf + = struct.pack(!20s,urllib.unquote(payload ['info_hash'])) in
buf + = struct.pack(!20s,urllib.unquote(payload ['peer_id']))#the peer_id我们宣布
buf + = struct.pack(!q, int(urllib.unquote(payload ['downloaded'])))#下载的字节数
buf + = struct.pack(!q,int(urllib.unquote(payload ['left'])) )#剩余字节数
buf + = struct.pack(!q,int(urllib.unquote(payload ['uploaded'])))#上传的字节数
buf + = struct .pack(!i,0x2)#event 2表示开始下载
buf + = struct.pack(!i,0x0)#IP地址设置为0。
key = udp_get_transaction_id() #由客户端随机化的唯一密钥
buf + = struct.pack(!i,key)
buf + = struct.pack(!i,-1)#需要的对等数量。对于默认
,设置为-1 buf + = struct.pack(!i,s_port)将发送响应的#port
返回(buf,transaction_id)
def udp_parse_announce_response(buf,sent_transaction_id):
#print响应是:+ str(buf)
if len(buf)< 20:
raise RuntimeError(宣布时响应长度错误:%s%len(buf))
action = struct.unpack_from(!i,buf)[0] #first 4 bytes is action
res_transaction_id = struct.unpack_from(!i,buf,4)[0] #next 4字节是事务id
如果res_transaction_id!= sent_transaction_id:
raise RuntimeError(Transaction ID不符合预计的%s,得到%s
%(sent_transaction_id,res_transaction_id))
print读取响应
if action == 0x1:
print Action是3
ret = dict()
offset = 8; #操作后的下一个4字节是transaction_id,因此数据不会启动,直到字节8
ret ['interval'] = struct.unpack_from(!i,buf,offset)[0]
printInterval :+ str(ret ['interval'])
offset + = 4
ret ['leeches'] = struct.unpack_from(!i,buf,offset)[0]
打印Leeches:+ str(ret ['leeches'])
offset + = 4
ret ['seeds'] = struct.unpack_from(!i,buf,offset)[0 ]
printSeeds:+ str(ret ['seeds'])
offset + = 4
peers = list()
x = 0
while offset! = len(buf):
peers.append(dict())
peers [x] ['IP'] = struct.unpack_from(!i,buf,offset)[0]
printIP:+ socket.inet_ntoa(struct.pack(!i,peers [x] ['IP']))
offset + = 4
如果偏移量> = len (buf):
提高RuntimeError(读对等端口时出错)
peers [x] ['port'] = struct.unpack_from(!H,buf,offset)[0]
printPort:+ str(peers [x] ['port'])
offset + = 2
x + = 1
return ret,peers
else:
#出现错误,尝试提取错误字符串
error = struct.unpack_from(! s,buf,8)
printAction =+ str(action)
raise RuntimeError(Annotation:%s%error)
$ b def udp_create_connection_request ):
connection_id = 0x41727101980 #default连接ID
动作= 0x0 #action(0 =给我一个新的连接ID)
transaction_id = udp_get_transaction_id()
print1.Transaction ID:,transaction_id
buf = struct.pack(!q,connection_id)#first 8字节是连接ID
buf + = struct.pack(!i,action)#next 4字节是动作
buf + = struct.pack(!i,transaction_id)#接下来的4个字节是事务id
return(buf,transaction_id)
def udp_parse_connection_response(buf,sent_transaction_id):
if len(buf)< 16:
raise RuntimeError(错误的响应长度获取连接ID:%s%len(buf))
action = struct.unpack_from(!i,buf)[0] #first 4 bytes是行动
res_transaction_id = struct.unpack_from(!i,buf,4)[0] #next 4字节是事务id
如果res_transaction_id!= sent_transaction_id:
raise RuntimeError(事务ID在连接响应中不匹配!预期的%s,得到%s
%(sent_transaction_id,res_transaction_id))
如果动作== 0x0:
connection_id =应该是connection_id
返回connection_id
elif action == 0x3:
error =
raise RuntimeError(尝试获得连接响应时出错:%s%错误)
传递
def(!s,buf,8) udp_get_transaction_id():
return int(random.randrange(0,255))
I am trying to get the peer list from the trackers using https://wiki.theory.org/BitTorrent_Tracker_Protocol
But most of the torrent files available on the net use a udp
tracker. The above website is for HTTP trackers. The torrent files with HTTP based trackers give an error.(Passkey error , or hash error or user error or 404 not found response
)
Right now my request code for all HTTP trackers look like :
payload = urllib.urlencode(payload)
for ann in self.torrent.announce_list:
if("udp" not in ann[0]):
url = ann[0]
print "Encoded Request URL :",(url + "?" + payload)
print "Requesting Tracker for Peer List"
try:
response = urllib.urlopen(url + "?" + payload).read()
print "RESPONSE :",response
except Exception, e:
print e
I have crosschecked my request URL against the requests being made by uTorrent using wireshark. They all match. The info-hash is being generated correctly.
How do I retrieve the peer list from UDP as well as HTTP trackers ? I am trying not to use any external library like lib-torrent. Some guidance would be really helpful.
The following code worked for me. Calling announce_udp() with the tracker URL and the payload dictionary with all the URL parameeters.
import binascii, urllib, socket, random, struct
from bcode import bdecode
from urlparse import urlparse, urlunsplit
def announce_udp(tracker,payload):
tracker = tracker.lower()
parsed = urlparse(tracker)
# Teporarly Change udp:// to http:// to get hostname and portnumbe
url = parsed.geturl()[3:]
url = "http" + url
hostname = urlparse(url).hostname
port = urlparse(url).port
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(8)
conn = (socket.gethostbyname(hostname), port)
#sock.bind((socket.gethostname(),s_port))
#Get connection ID
req, transaction_id = udp_create_connection_request()
sock.sendto(req, conn);
buf = sock.recvfrom(2048)[0]
connection_id = udp_parse_connection_response(buf, transaction_id)
#Annoucing
s_port = sock.getsockname()[1] #get port number to which socket is connected
req, transaction_id = udp_create_announce_request(connection_id, payload,s_port)
sock.sendto(req, conn)
print "Announce Request Sent"
buf = sock.recvfrom(2048)[0]
print "Response received"
return udp_parse_announce_response(buf, transaction_id)
def udp_create_announce_request(connection_id, payload, s_port):
action = 0x1 #action (1 = announce)
transaction_id = udp_get_transaction_id()
# print "2.Transaction ID :", transaction_id
buf = struct.pack("!q", connection_id) #first 8 bytes is connection id
buf += struct.pack("!i", action) #next 4 bytes is action
buf += struct.pack("!i", transaction_id) #followed by 4 byte transaction id
buf += struct.pack("!20s", urllib.unquote(payload['info_hash'])) #the info hash of the torrent we announce ourselves in
buf += struct.pack("!20s", urllib.unquote(payload['peer_id'])) #the peer_id we announce
buf += struct.pack("!q", int(urllib.unquote(payload['downloaded']))) #number of bytes downloaded
buf += struct.pack("!q", int(urllib.unquote(payload['left']))) #number of bytes left
buf += struct.pack("!q", int(urllib.unquote(payload['uploaded']))) #number of bytes uploaded
buf += struct.pack("!i", 0x2) #event 2 denotes start of downloading
buf += struct.pack("!i", 0x0) #IP address set to 0. Response received to the sender of this packet
key = udp_get_transaction_id() #Unique key randomized by client
buf += struct.pack("!i", key)
buf += struct.pack("!i", -1) #Number of peers required. Set to -1 for default
buf += struct.pack("!i", s_port) #port on which response will be sent
return (buf, transaction_id)
def udp_parse_announce_response(buf, sent_transaction_id):
#print "Response is:"+str(buf)
if len(buf) < 20:
raise RuntimeError("Wrong response length while announcing: %s" % len(buf))
action = struct.unpack_from("!i", buf)[0] #first 4 bytes is action
res_transaction_id = struct.unpack_from("!i", buf, 4)[0] #next 4 bytes is transaction id
if res_transaction_id != sent_transaction_id:
raise RuntimeError("Transaction ID doesnt match in announce response! Expected %s, got %s"
% (sent_transaction_id, res_transaction_id))
print "Reading Response"
if action == 0x1:
print "Action is 3"
ret = dict()
offset = 8; #next 4 bytes after action is transaction_id, so data doesnt start till byte 8
ret['interval'] = struct.unpack_from("!i", buf, offset)[0]
print "Interval:"+str(ret['interval'])
offset += 4
ret['leeches'] = struct.unpack_from("!i", buf, offset)[0]
print "Leeches:"+str(ret['leeches'])
offset += 4
ret['seeds'] = struct.unpack_from("!i", buf, offset)[0]
print "Seeds:"+str(ret['seeds'])
offset += 4
peers = list()
x = 0
while offset != len(buf):
peers.append(dict())
peers[x]['IP'] = struct.unpack_from("!i",buf,offset)[0]
print "IP: "+socket.inet_ntoa(struct.pack("!i",peers[x]['IP']))
offset += 4
if offset >= len(buf):
raise RuntimeError("Error while reading peer port")
peers[x]['port'] = struct.unpack_from("!H",buf,offset)[0]
print "Port: "+str(peers[x]['port'])
offset += 2
x += 1
return ret,peers
else:
#an error occured, try and extract the error string
error = struct.unpack_from("!s", buf, 8)
print "Action="+str(action)
raise RuntimeError("Error while annoucing: %s" % error)
def udp_create_connection_request():
connection_id = 0x41727101980 #default connection id
action = 0x0 #action (0 = give me a new connection id)
transaction_id = udp_get_transaction_id()
print "1.Transaction ID :", transaction_id
buf = struct.pack("!q", connection_id) #first 8 bytes is connection id
buf += struct.pack("!i", action) #next 4 bytes is action
buf += struct.pack("!i", transaction_id) #next 4 bytes is transaction id
return (buf, transaction_id)
def udp_parse_connection_response(buf, sent_transaction_id):
if len(buf) < 16:
raise RuntimeError("Wrong response length getting connection id: %s" % len(buf))
action = struct.unpack_from("!i", buf)[0] #first 4 bytes is action
res_transaction_id = struct.unpack_from("!i", buf, 4)[0] #next 4 bytes is transaction id
if res_transaction_id != sent_transaction_id:
raise RuntimeError("Transaction ID doesnt match in connection response! Expected %s, got %s"
% (sent_transaction_id, res_transaction_id))
if action == 0x0:
connection_id = struct.unpack_from("!q", buf, 8)[0] #unpack 8 bytes from byte 8, should be the connection_id
return connection_id
elif action == 0x3:
error = struct.unpack_from("!s", buf, 8)
raise RuntimeError("Error while trying to get a connection response: %s" % error)
pass
def udp_get_transaction_id():
return int(random.randrange(0, 255))
这篇关于BitTorrent客户端:从跟踪器获取对等列表[Python]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!