Fork me on GitHub

分类 协议分析 下的文章

使用WireShark进行磁力链接协议分析

上一篇博客中,我介绍了两种方法来将磁力链接转换为一个种子文件:一种方法是直接从种子站点下载,另一种方法通过libtorrent库实现。第二种方法通过libtorrent库的强大功能,只有寥寥几行代码就实现了种子文件的转换。正如前文所说,简单的背后往往蕴含着复杂的逻辑设计,这篇博客通过对WireShark截获下的所有网络包的分析,深入学习BitTorrent协议,包括:DHT网络,KRPC协议,种子文件格式,B编码,BitTorrent扩展,uTP协议等。

一、加入DHT网络

磁力链接的发明使得P2P客户端直接从DHT网络中寻找资源,而不是传统的依赖于Tracker服务器,这样就避免了Tracker服务器的单点故障问题,所以从DHT中获取的种子有时候也叫做Trackerless torrent。DHT网络是一种分布式的去中心化网络,每个加入DHT网络的节点都要负责存储这个网络中的资源和其他成员的联系信息。小虾在他的一篇文章中做了一个非常形象的比喻:

举个形象点的例子,把DHT网络比作一个朋友圈子,你想进入这个圈子必须要有一个人带领你进去,通常会有一些特定人负责介绍你进入这个圈子。当你被A带进这个朋友圈,此刻你就只认识A而已。但是你的目的是想找奥巴马总统,所以你会问A要奥巴马的联系方式,但是A没有奥巴马的联系方式,他会介绍一个美国朋友B给你认识。于是你去问B要奥巴马的联系方式,B其实也没有奥巴马的联系方式,但是B认识一个州长C。于是你又得到了C的联系方式,C把奥巴马的联系方式告诉你之后,你就可以写信或者致电给奥巴马了。

可以看到如果一个新节点要加入到DHT网络中,它必须要先认识一个人带你进去。这样的人我们把他叫做bootstrap node,常见的bootstrap node有:router.bittorrent.comrouter.utorrent.comrouter.bitcomet.comdht.transmissionbt.com等等。所以首先我们通过DNS将其转换为IP:

dns

这里以dht.transmissionbt.com为例进行说明,从DNS查询结果得到了两个IP地址:212.129.33.50 和 91.121.59.153。

二、了解KRPC协议,获取Peers

DHT协议早在2005年就已经成为了官方BitTorrent协议的一部份,官方文档参考BEP-005,这里有一份中文翻译。DHT建立在UDP之上,想要获取需要的Peers信息,首先要了解下KademliaKRPC协议。 关于路由表和Kademlia的介绍,可以参照官方文档。我们这里的重点在于如何根据磁力链接获取拥有该磁力链接对应的种子文件信息的Peers,所以只需要了解KRPC协议。 KRPC协议是由B编码组成的一个简单的RPC结构,有4种请求:pingfind_nodeget_peersannounce_peer

  • ping: 检测节点是否可达,请求包含一个参数id,代表该节点的nodeID。对应的回复也应该包含回复者的nodeID。
  • find_node: 该请求包含两个参数idtargetid为该节点的nodeID,target为要查询的nodeID。回复中应该包含被请求节点的路由表中距离target最接近的8个nodeID。
  • get_peers: 该请求包含两个参数idinfohashid为该节点的nodeID,infohash为种子文件的SHA1哈希值,也就是磁力链接的btih值。如果被请求的节点有对应info_hash的peers,他将返回一个关键字values,这是一个列表类型的字符串。每一个字符串包含了CompactIP-address/portinfo格式的peers信息。如果被请求的节点没有这个infohash的peers,那么他将返回关键字nodes,这个关键字包含了被请求节点的路由表中离info_hash最近的K个nodes,使用Compactnodeinfo格式回复。
  • announce_peer: 如果节点正在下载torrent文件,则需要通知其他人你正在哪个端口进行下载,这样就可以分享给其他人,让其他人连接你进行下载。

在我们的实验中,因为并没有真正的下载torrent文件,所以并不需要announce_peer请求。ping非常简单,我们忽略之,只看find_nodeget_peers这两个请求。形象点说find_node请求就好比我们交朋友,如果运气好的话每次发送find_node都可以得到8个新node,再对这8个新node发送find_node,就可以得到8*8=64个新node(当然这是理想情况,我们没考虑丢包,节点不在线,返回节点数没8个这些情况)。一般情况下find_nodetarget参数就是我们自己的nodeID,所以得到的8个node都是离自己最近的(Kademlia距离,并非物理距离)。这样我们很快就得到了非常多的node信息,路由表就是这样建立起来的。 但是实际上find_node请求在这里对我们用处并不大,要想根据磁力链接获取种子信息,最重要的请求还是get_peers,它可以根据infohash来找到拥有该种子信息的peers。 下图是find_node请求,idtarget参数都是当前节点的nodeID:909f9cbdedf4f7e29e820e3fd5e00a2965450b8a

find_node

下图是对find_node请求的回复。回复报文中可以看到返回了8个node,每个node都是紧凑的nodeID IP/Port格式:

find_node_resp

下图是get_peers请求,infohash = 1619ecc9373c3639f4ee3e261638f29b33a6cbd6,正是磁力链接magnet:?xt=urn:btih:1619ecc9373c3639f4ee3e261638f29b33a6cbd6&dn;=ubuntu-14.10-desktop-i386.iso中的btih值。

get_peers

下图是对get_peers请求的回复。回复报文中包含了8个node,以及100个peer。可见包含该种子文件的peer非常多。

get_peers_resp

三、Bencoding编码和种子文件格式

Bencoding编码(简称B编码)在BitTorrent协议中非常常见,从上面的四张图中就可以看到KRPC协议采用了B编码来发送消息,不仅如此,在后面介绍的PEX和BitTorrent扩展中我们也会看到B编码的影子。而且更重要的是,BT种子文件本身就是一个B编码的字典,所以要想学习BitTorrent,首先得学习B编码。B编码是一种非常简洁的数据格式,共支持4种不同的类型:字节串、整数、列表和字典。

  • string: 格式为 <字符串长度>:<字符串>。如 hell: 4:hell
  • integer: 格式为 i<整数>e。如 i1999e 表示数字1999
  • list: 格式为 l[数据1][数据2][数据3][…]e。如 l5:hello5:worldi101ee 表示列表[hello, world, 101]
  • dictionary: 格式为 d[key1][value1][key2][value2][…]e,其中 key 必须是 string 而且按照字母顺序排序。如 d2:aai100e2:bb2:bb2:cci200ee 表示字典 {aa:100, bb:bb, cc:200}

BT种子文件整个是一个dictionary格式,比较重要的key有announce( tracker 服务器的地址)、announce-list(可选的 tracker 服务器地址)、creation date(文件创建时间)、created by(文件创建者)、info(该bt种子文件的文件信息)等。其中info对应的value根据种子包含的是单文件还是多文件有所区别,其中piece length(每一数据块的长度)和pieces(所有数据块的 SHA1 校验值)是公共部分,如果是单文件的话,则包含name(文件名称)和length(文件的长度)两个key,如果是多文件的话,则包含name(文件夹名称)和files(文件列表,每个文件列表下面是包括每一个文件的信息,文件信息是个字典)两个key。 关于Bencoding和种子文件格式更详细的信息可以参考这里这里这里,另外这里有一份BitTorrent的非官方规范,其中也有关于Bencoding的介绍。 我们这里以ubuntu-14.10-desktop-i386.iso.torrent文件为例进行介绍,使用WinHex打开该文件如下图所示:

torrent

图的右边我将其转换成容易阅读的格式,可以看到上面介绍的announceannounce-listinfo等字段。其中info字段对于一个种子文件来说最为重要,根据磁力链接从DHT网络中下载种子文件也是下载info字段的内容,其他内容包括announceannounce-listcreated by等在DHT网络中并没有保留。磁力链接的infohash也是根据info字段来计算的,info字段的pieces为每个数据块的校验值,其作用是验证下载下来的文件是否正确,如果下载下来的文件块计算出来的SHA1值和pieces中的SHA1校验值不一致,该数据块要重新下载。 所以,我们可以看出根据磁力链接下载文件是分成两个步骤的:先根据infohash下载种子文件的info字段,种子文件并不是必须的,但是info字段却必不可少;然后根据infohash下载源文件,将下载的每一个数据块和info中的对应的SHA1校验码进行比较,不一致重新下载该数据块。我们这里研究的其实就是第一步,要注意的是info字段的下载也是分块的,下面将进行介绍,下载完成后使用infohash进行校验。 需要注意的是,一般的种子文件会包含announce,也就是tracker服务器的地址,如果没有tracker服务器,文件中可能会包含nodesnodes是存有种子信息的peer节点,这样的种子文件就是trackerless torrent。如果有nodes客户端直接从nodes获取种子信息,而从DHT网络中下载下来的种子文件既没有annouce也没有nodes,客户端只能通过info字段计算出hashinfo,再从bootstrap node节点开始在DHT网络中寻找种子信息。

四、uTP协议

在介绍info字段的下载之前,我们还要了解下uTP协议,它是一个基于UDP的开放的BT点对点文件共享协议。在uTP协议出现之前,BT下载会占用网络中大量的链接,直接导致其它网络应用服务质量下载和网络的拥堵,因此有很多ISP都开始限制BT的下载。uTP减轻了网络延迟并解决了传统的基于TCP的BT协议所遇到的拥塞控制问题,提供可靠的有序的传送。 一个有效的uTP数据包包含下面格式的报头:

uTP-header

其中,我们最为关心的是typeconnection idseq_nrack_nr这几个值。type字段表示包类型,uTP的包类型有下面5种:

  • ST_DATA = 0: 最重要的数据包,uTP就是使用该类型的包传送数据
  • ST_FIN = 1: 关闭连接,这是uTP连接的最后一个包,类似于TCP中的FIN
  • ST_STATE = 2: 简单的应答包,表明已从对方收到了数据包,该包不包含任何数据,seq_nr值不变
  • ST_RESET = 3: 终止连接,类似于TCP中的RST
  • ST_SYN = 4: 初始化连接,类似于TCP中的SYN,这是uTP连接的第一个包

关于uTP协议的内容参考官方文档BEP-029uTP的一个很重要的特点是使用connection id来标识一次连接,而不是每个包算一次连接。所以在分析ST_DATA时,需要注意找所有connection id相同的数据包,然后按seq_nr排序,seq_nr应该是依次递增的(注意ST_STATE包不会增加seq_nr值),如果发现两个ST_DATAseq_nr值相同则说明后面那个报文是重复报文需要忽略掉,如果发现两个ST_DATAseq_nr值不是连续的,中间差了一个或多个,则可能是由于网络原因发生了丢包现象,数据包将不可用。 下面是一个简单的ST_SYN报文,从192.168.18.33发送到112.208.162.161,seq_nr = 31445,ack_nr = 0, connection id = 14487

uTP-syn

112.208.162.161收到ST_SYN报文后,会向192.168.18.33发送一个ST_STATE报文,表示已收到。如果没有回复,则连接不能建立。如下图所示,seq_nr = 9690,ack_nr = 31445,connection id = 14487

uTP-state

uTP连接建立之后,就开始传送需要的数据了。peer和peer之间传送数据也是遵循着一定的规范,这就是下面要讲的Peer Wire协议。

五、Peer Wire协议

Peer Wire协议是Peer之间的通信协议,通常由一个握手消息开始。握手消息的格式是这样的:<pstrlen><pstr><reserved><info_hash><peer_id> 下面是一个握手报文的示例:

peer-wire-handshake

在BitTorrent协议的v1.0版本, pstrlen = 19, pstr = "BitTorrent protocol",info_hash是上文中提到的磁力链接中的btihpeer_id每个客户端都不一样,但是有着一定的规则,根据前面几个字符可以推断出客户端的类型,譬如这里的peer_id-LT开头可以大致推断出客户端为libtorrent这里有一份非常全面的列表,这里也有一个列表。 收到握手消息后,对方也会回复一个握手消息,并且开始协商一些基本的信息。如下图是握手报文的回复:

peer-wire-handshake-resp

从上图中可以看到,对方除了回复了一个握手消息外,在握手消息的后面还附带了一个扩展消息,一个bitfield消息和多个have消息。这些都是Peer Wire协议基本的通信消息,拥有统一的格式:<length prefix><message ID><payload>,可以从message ID看出是哪种类型的消息。Peer Wire消息有:keep-alive、choke(0)、unchoke(1)、interested(2)、not interested(3)、have(4)、bitfield(5)、request(6)、piece(7)、cancel(8)、port(9),另外还有下面要介绍的扩展协议extend(20)。 关于Peer Wire协议的详细内容,请参考BitTorrent的规范,这里是中文翻译这里也有一点资料。 在下载文件时,这些消息扮演着非常重要的角色,而在这里,我们只关注message ID为20的extend扩展协议。

六、BitTorrent协议扩展与ut_metadata和ut_pex

经过了上面层层的铺垫,终于到了最后一步下载我们的种子文件了。获取种子文件的metadata使用的是BitTorrent的协议扩展,BEP-010BEP-009是关于这个的权威规范,这里有一份BEP-009的中文翻译。 根据BEP-010我们知道,扩展消息一般在Peer Wire握手之后立即发出,是一个B编码的字典,其中有一个key很重要,那便是mm也是一个字典,表示客户端支持的所有扩展以及每个扩展的编号。我们将上图中握手消息后的扩展消息写成下面的格式:

{
    e: 0,
    ipv4: xxx,
    ipv6: xxx,
    complete_ago: 1,
    m:
    {
        upload_only: 3,
        lt_donthave: 7,
        ut_holepunch: 4,
        ut_metadata: 2,
        ut_pex: 1,
        ut_comment: 6
    },
    matadata_size: 45377,
    p: 33733,
    reqq: 255,
    v: BitTorrent 7.9.3
    yp: 19616,
    yourip: xxx
}

其中ut_pex表示该客户端支持PEX(Peer Exchange)ut_metadata表示支持BEP-009(也就是交换种子文件的metadata)。PEX在这里派不上用场,我们主要关心metadata的交换。接着上面的握手消息,我们在完成双方握手之后,并且得到了对方支持的扩展信息。接着我们发出下面的请求:

metadata_request

该请求也通知对方我们支持的扩展情况,然后后面接着一个扩展消息。如何识别一个扩展消息是什么消息呢?我们从上面的m字典可以看到可能会有多种不同的扩展消息,那么我们这个消息是哪个扩展呢?答案是message ID后面那个数字,这个数字对应着m字典中的编号。譬如我们这里的消息是:00 00 00 1b 14 02 ... 00 00 00 1b 表示消息长度为 0x1b (27 bytes) 14 表示是 扩展消息(0x14 = 20) 02 对应上面m字典中的 ut_metadata 所以我们这个消息是ut_metadata消息,下面我们再来研究下BEP-009BEP-009定义了三种不同的消息类型:requestdatareject。我们这里的图显示的是[msg_type: 0, piece: 2]正是request消息,意思是向对象请求第二个piece的数据。而piece又是什么意思呢?根据BEP-009我们知道,种子文件的metadata(也就是info部分)会按16KB分成若干块,除最后一块每一块的大小都是16KB,每一块从0开始按顺序进行编号。所以这个请求的意思就是向对象请求第三块的metadata。我们再来看一下torrent文件,如下图:

torrent-info

从图中形象的表示可以看到torrent文件整个info的长度为45377,这个值正是上面握手报文后的扩展消息中的metadata_size的值。在发送request消息之后,接下来对方应该回复data消息(如果对方有数据)或reject消息(如果对方没有数据)。下图是针对上面的request消息的回复:

metadata_resp

msg_type为1表示是回复就是我所需要的数据,但是注意这里的数据并没完,由于uTP协议的缘故,我们可以根据connection id找到这个连接后续的所有数据。 这里其实一共收到了三个消息,我们分别来看一下:

00 00 00 03 09 83 c5 --> message ID为9,port消息,表示端口号为0x83c5 = 33733
00 00 00 03 14 03 01 --> message ID为20(0x14),extend消息,编号03upload_only,表示设置upload_only = 1
00 00 31 70 14 02 xx --> message ID为20(0x14),extend消息,编号02ut_metadata,后面的xx表示[msg_type: 1, piece: 2, total_size: 45377]和相应块的metadata数据

看第三个消息可以知道消息长度为0x3170,这个长度包括了[msg_type...]这一串字符串的长度,共0x2f个字节,我们将其减去就得到了piece2的长度:0x3170 - 0x2f = 0x3141 我们上面说过每个块的大小应该是16KB,也就是0x4000,这里的大小为0x3141,只可能是最后一块。我们稍微计算验证下,将整个info的长度45377(0xb141)按16KB分块:

piece 0: 0x0001 ~ 0x4000 长度0x4000
piece 1: 0x4001 ~ 0x8000 长度0x4000
piece 2: 0x8001 ~ 0xb141 长度0x3141

可以看到piece2正是最后一块,大小为0x3141。至此我们得到了第二块的metadata,然后通过request消息获取piece0和piece1获取第一和第二块的metadata,将三块的消息合并成torrent文件info字段,然后再加上create datecreate bycomment等信息,种子文件就算完成下载了。

七、校验info_hash

我们将从DHT网络中下载的种子文件和原始的种子文件进行比较,可以看到annouceannouce-list字段都丢掉了,create date发生了变化,info字段不变:

torrent-comp

另外,我们也可以使用这篇文章中介绍的方法将种子文件转换为磁力链接,检查infohash的值是否一致。从下面的代码也可以看出infohash的计算只和info字段有关。

import bencode, hashlib, base64, urllib

torrent = open('ubuntu.torrent', 'rb').read()
metadata = bencode.bdecode(torrent)
hashcontents = bencode.bencode(metadata)

参考

  1. 写了个磁力搜索的网页 - 收录最近热门分享的资源
  2. BEP-005 DHT Protocol
  3. 【P2P网络】BitTorrent的DHT协议(译自官方版本)
  4. BitTorrentDraftDHTProtocol
  5. torrent文件分析
  6. bttorrent 种子文件结构解析
  7. BEncode编码方式以及torrent文件的一些内容
  8. Bittorrent Protocol Specification v1.0
  9. BitTorrent协议规范
  10. BEP-020 Peer ID Conventions
  11. BEP-029 uTorrent transport protocol
  12. peer之间的通信协议
  13. BEP-010 Extension Protocol
  14. extension protocol for bittorrent
  15. BEP-009 Extension for Peers to Send Metadata Files
  16. 【P2P网络】Extension for Peers to Send Metadata Files翻译稿
  17. Python将BT种子文件转换为磁力链的两种方法
  18. 常见P2P协议之BitTorrent 分析
  19. BT(带中心Tracker)通信协议的分析
  20. BT协议具体分析
  21. BitTorrent 协议分析与实现
  22. How PEX protocol (Magnetic links) finds it first IP?
  23. BT协议库libtorrent的种子文件解析方法探究
扫描二维码,在手机上阅读!

磁力链接是如何实现下载的?

一直很好奇磁力链接是如何工作的,只通过简单的一串字符就能下载任何文件,这确实是很神奇的一件事。而简单的背后,往往蕴含着非常复杂的逻辑和巧妙的设计,磁力链接正是如此。通过一段时间的学习,对磁力链接及相关的概念有了一定的了解,Magnet、Torrent、P2P、DHT、KAD等等等等,在这里做个小结。

一、磁力链接与种子文件

磁力链接并不是一个新概念,早在2002年,相关的标准草稿就已经制定了。但直到2012年海盗湾为规避版权问题删除了站点上的所有Torrent文件停止了下载服务,并将之前所有的种子转换为磁力链接之后,磁力链接技术才真正的飞速发展起来。说起磁力链接,不能不提BT;而说起BT,则不能不提P2P。在很早的网络时代,下载都是简单的集中式客户端/服务器模式,一个或多个服务器支撑成千上万的客户端连接下载,不仅带宽遇到了瓶颈,而且太容易出现单点故障。这时P2P被提出来解决这个问题,P2P也不是一经提出就完美无缺的,在经过了Napster到Gnutella到BitTorrent三代的P2P技术的发展才渐进成熟。(这里有对P2P的科普) 维基百科上对BT做了详细的说明:

根据BitTorrent协议,文件发布者会根据要发布的文件生成提供一个.torrent文件,即种子文件,也简称为“种子”。 种子文件本质上是文本文件,包含Tracker信息和文件信息两部分。Tracker信息主要是BT下载中需要用到的Tracker服务器的地址和针对Tracker服务器的设置,文件信息是根据对目标文件的计算生成的,计算结果根据BitTorrent协议内的Bencode规则进行编码。它的主要原理是需要把提供下载的文件虚拟分成大小相等的块,块大小必须为2k的整数次方(由于是虚拟分块,硬盘上并不产生各个块文件),并把每个块的索引信息和Hash验证码写入种子文件中;所以,种子文件就是被下载文件的“索引”。 下载者要下载文件内容,需要先得到相应的种子文件,然后使用BT客户端软件进行下载。 下载时,BT客户端首先解析种子文件得到Tracker地址,然后连接Tracker服务器。Tracker服务器回应下载者的请求,提供下载者其他下载者(包括发布者)的IP。下载者再连接其他下载者,根据种子文件,两者分别告知对方自己已经有的块,然后交换对方所没有的数据。此时不需要其他服务器参与,分散了单个线路上的数据流量,因此减轻了服务器负担。 下载者每得到一个块,需要算出下载块的Hash验证码与种子文件中的对比,如果一样则说明块正确,不一样则需要重新下载这个块。这种规定是为了解决下载内容准确性的问题。 一般的HTTP/FTP下载,发布文件仅在某个或某几个服务器,下载的人太多,服务器的带宽很易不胜负荷,变得很慢。而BitTorrent协议下载的特点是,下载的人越多,提供的带宽也越多,下载速度就越快。同时,拥有完整文件的用户也会越来越多,使文件的“寿命”不断延长。

综上所述,可以看出Tracker服务器在BT网络中充当着非常重要的作用,和传统的客户端/服务器模式一样,Tracker服务器同样会存在单点故障问题。所以在BT技术的基础上,后来又衍生出DHT网络和磁力链接技术,DHT全称为分布式哈希表(Distributed Hash Table),是一种分布式存储方法。DHT网络是Tracker-less的,不依赖于其他的Tracker服务器。在这种情况下,每个客户端负责一个小范围的路由,并负责存储一小部分数据,从而实现整个DHT网络的寻址和存储。使用支持该技术的BT下载软件,用户无需连上Tracker就可以下载,因为软件会在DHT网络中寻找下载同一文件的其他用户并与之通讯,开始下载任务。 在网络中定位资源最简单的方法是URL(统一资源定位符),它是通过资源的位置来进行定位。而在DHT网络中,则是使用URN(统一资源名称)来进行定位,磁力链接就是基于文件内容的散列函数值来链接到特定文件,生成一个唯一的文件识别符,从而在DHT网络中定位并下载文件。 一个最简单的磁力链接格式如下:
magnet:?xt=urn:btih:51df6808c739174c8f264701ba94460c5238d6ce
其中urn为统一资源名称,btih是BitTorrent Info Hash的缩写,是BitTorrent使用的Hash函数。除了btih还可以是其他类型的Hash函数,但不如btih用的多。这一串长度为40的字符串正是文件内容的Hash,BT下载工具就根据这个Hash在DHT网络中定位下载文件。

二、libtorrent实现磁力链接转BT种子

处理磁力链接最简单的库莫过于Python中的libtorrent,它封装了几乎所有的BitTorrent相关的内容,以至于你可以在10分钟内使用libtorrent写出一个自己的BT下载工具。libtorrent中的add_magnet_uri方法是用来解析磁力链接的,该方法非常简单是完全透明的,不用关心磁力链接或DHT协议之类的细节,方法返回一个句柄,在磁力链接未成功解析之前,这个句柄的has_metadata方法会一直返回False,一旦成功解析到磁力链接,根据这个句柄调用get_torrent_info方法即可获取种子信息了。示例代码如下:

handle = lt.add_magnet_uri(sess, link, params)

# waiting for metadata
while (not handle.has_metadata()):
    time.sleep(5)    

# create a torrent
torinfo = handle.get_torrent_info()
torfile = lt.create_torrent(torinfo)

注意,在使用libtorrent处理磁力链接之前,首先需要记得调用add_dht_routerstart_dht启用DHT,这就是所谓的bootstrap node。如果不启用DHT,可能要很久都不能解析磁链。如下:

sess = lt.session()
sess.add_dht_router('router.bittorrent.com', 6881)
sess.add_dht_router('router.utorrent.com', 6881)
sess.add_dht_router('router.bitcomet.com', 6881)
sess.add_dht_router('dht.transmissionbt.com', 6881)
sess.start_dht();

常见的bootstrap nodebittorrent、utorrent、bitcomet、transmissionbt等,端口都是6881。完整的代码在这里

三、BT种子转磁力链接

上面讲了这么多,大家会发现磁力链接的下载原理其实就是先根据磁力链接获取种子文件,然后再根据种子文件进一步下载。这是因为种子文件才有分片信息,文件大小,文件名等必要的信息,所以种子是必不可少的。那么既然有了种子文件,我们为什么还需要磁力链接呢?这是因为现在对BT的封锁太严重,传播种子并不是那么简单的事了,动不动就被和谐了,所以磁链的作用就是便于传输,因为磁链就是一个小小的文本,而种子确是一个文件。所以总结一下他们的优缺点:
A. 种子:稳定性高,信息多,不便于传播扩散
B. 磁力链接:仍需要先获得种子,不稳定,可能在某时刻不能获取,但是很便于扩散。

使用libtorrent将种子转换为磁链简单的不能再简单了。另外也可以使用bencode来进行解析,这里有代码示例

def torrent2magnet(torrent_file):
    info = lt.torrent_info(torrent_file)
    link = "magnet:?xt=urn:btih:%s&dn=%s" % (info.info_hash(), info.name())
    return link

四、种子站点的缓存

使用libtorrent将磁力链接转换成种子文件,实际上是效率很低下的工作,运行上面的Python脚本,可以发现转换一个磁力链接至少也要花费5到10秒的时间,有的磁力链接更要将近一分钟才能转换。那么如何提高转换速度呢?其实,大多数的下载工具在下载磁力链接时会将种子文件保留在服务器上,所以我们在转换之前先判断下种子服务器上是否已经存在该种子,如果存在则直接下载,这将比通过DHT网络转换磁力链接快的多。常见的种子服务器有:(网上很多种子服务器都已经被墙,或者已关闭,或者提供了验证码不能直接下载了)

  1. http://bt.box.n0808.com
    这是迅雷的种子库,种子文件地址格式为:http://bt.box.n0808.com/HASH值最前面两位/HASH值最后面两位/HASH值.torrent
  2. http://magnet2torrent.com/
    实际上调的是torcache的接口,格式为:https://torcache.net/torrent/HASH值.torrent
  3. http://torrent-cache.bitcomet.org:36869/
    这是BitComet的服务器,格式为:http://torrent-cache.bitcomet.org:36869/get_torrent?info_hash=HASH值&key;=KEY值。关于KEY值的计算可以参考这篇文章
  4. http://magnet.vuze.com/
    这是Vuze的种子库,格式为:http://magnet.vuze.com/magnetLookup?hash=KEY值,其中KEY值是Hash的base32编码。

参考

  1. P2P技术科普
  2. 磁力链接 - 维基百科
  3. BitTorrent (协议) - 维基百科
  4. 根据磁力链导出BT种子
  5. 通过 BT 种子 Hash 值从 BitComet 服务器上下载种子文件
  6. 根据磁力链获得BT种子
  7. 磁力链接转换为种子文件 magnet to torrent
  8. libtorrent中从magnet link生成torrent
  9. Converting a Magnet Link into a Torrent
  10. Python将BT种子文件转换为磁力链的两种方法
扫描二维码,在手机上阅读!