从0实现 BT 下载 :1 种子的解析

从0实现 BT 下载 :1 种子的解析

最近突然有了时间,想研究一下 p2p 网络,先做一个较容易实现的 bt 下载实现,尽量少的使用第3方库。

先写第一篇 种子的解析:

种子文件,不是全是 asc2 可见字符,有些是二进制编码,不能直接看所以需要解析。

Bencode 编码https://zh.wikipedia.org/wiki/BencodeBencode(发音为Bee-Encode)是BitTorrent用在传输数据结构的编码方式。这种编码方式支持四种资料类型:

字符串整数串列字典表Bencode最常被用在.torrent档中,文件里的元数据都是Bencode过的字典表。其也被用在tracker返回响应时使用。

虽然比用纯二进制编码效率低,但由于结构简单而且不受字节存储顺序影响(所有数字以十进制编码)——这对于跨平台性非常重要。而且具有较好的灵活性,即使存在故障的字典键,只要将其忽略并更换新的就能兼容补充。

编码方法Bencode使用ASCII字符进行编码。

一个整型数会以十进制数编码并括在i和e之间,不允许前导零(但0依然为整数0),负数如十进制表示一样使用前导负号,不允许负零。如整型数“42”编码为“i42e”,数字“0”编码为“i0e”,“-42”编码为“i-42e”。一个以字节为单位表示的字符串(字符串的字为一个字节,不一定是一个字符)会以(长度):(内容)编码,长度的值和数字编码方法一样,只是不允许负数;内容就是字符串的内容,如字符串“spam”就会编码为“4:spam”,本规则不能处理ASCII以外的字符串,为了解决这个问题,一些BitTorrent程序会以非标准的方式将ASCII以外的字符以UTF-8编码转化后再编码。线性表会以l和e括住来编码,其中的内容为Bencode四种编码格式所组成的编码字符串,如包含和字符串“spam”数字“42”的线性表会被编码为“l4:spami42ee”,注意分隔符要对应配对。字典表会以d和e括住来编码,字典元素的键和值必须紧跟在一起,而且所有键为字符串类型并按字典顺序排好。如键为“bar”值为字符串“spam”和键为“foo”值为整数“42”的字典表会被编码为“d3:bar4:spam3:fooi42ee”。对于线性表和字典的取值范围并没有限制,他们通常会包含其他元素,这样就允许对很复杂的数据结构进行编码。

多文件Torrent的结构的树形图为:Multi-file Torrent├─announce:Tracker的主服务器├─announce-list:Tracker服务器列表├─comment:种子文件的注释├─comment.utf-8:种子文件注释的utf-8编码├─creation date:种子文件建立的时间,是从1970年1月1日00:00:00到现在的秒数。├─encoding:种子文件的默认编码,比如GB2312,Big5,utf-8等├─info:所有关于下载的文件的信息│ ├─files:表示文件的名字│ │ ├─length:文件的大小(Byte)│ │ ├─path:文件的名字,在下载时不可更改│ │ └─path.utf-8:文件名的UTF-8编码,同上│ ├─name:推荐的文件夹名,此项可于下载时更改│ ├─name.utf-8:推荐的文件夹名的utf-8编码,同上│ ├─piece length:每个文件块的大小(Byte)│ ├─pieces:文件的特征信息(将所有文件按照piece length的字节大小分成块,每块计算一个SHA1值,然后将这些值连接起来所组成)│ ├─publisher:文件发布者的名字│ ├─publisher-url:文件发布者的网址│ ├─publisher-url.utf-8:文件发布者网址的utf-8编码│ └─publisher.utf-8:文件发布者的名字的utf-8编码└─nodes:这个字段包含一系列ip和相应端口的列表,是用于连接DHT初始node

单文件Torrent的结构的树形图为:(各个字段含义同多文件Torrent一样,只是单文件Torrent没有files字段)Single-File Torrent├─announce:Tracker的主服务器├─announce-list:Tracker服务器列表├─comment:种子文件的注释├─comment.utf-8:种子文件注释的utf-8编码├─creation date:种子文件建立的时间,是从1970年1月1日00:00:00到现在的秒数。├─encoding:种子文件的默认编码,比如GB2312,Big5,utf-8等├─info:所有关于下载的文件的信息│ ├─length:文件的大小(Byte)│ ├─name:推荐的文件夹名,此项可于下载时更改│ ├─name.utf-8:推荐的文件夹名的utf-8编码,同上│ ├─piece length:每个文件块的大小(Byte)│ ├─pieces:文件的特征信息(将所有文件按照piece length的字节大小分成块,每块计算一个SHA1值,然后将这些值连接起来所组成)│ ├─publisher:文件发布者的名字│ ├─publisher-url:文件发布者的网址│ ├─publisher-url.utf-8:文件发布者网址的utf-8编码│ └─publisher.utf-8:文件发布者的名字的utf-8编码└─nodes:这个字段包含一系列ip和相应端口的列表,是用于连接DHT初始node

比较关键的数据:

Tracker服务器列表 文件名 文件大小 文件块sha1值 都可以直接解析出来

特征码:SHA1 虽然是20字节 但是显示的时候,是按16进制显示就变成了40个字符

特征值 :872FDFA84B97B5EC7BA668CAB5FF2840FE4B220F

下面看 Wireshark 抓包的特征值

是原始20字节的二进制,经过 urlencode 编码后:%87%2F%DF%A8K%97%B5%EC%7B%A6h%CA%B5%FF%28%40%FEK%22%0F

下面试着 url 解码看看,结果是乱码。

关键的问题 info_hash 怎么计算出来?

通过上面的介绍,应该明白了,先计算出来 info 字典的 SHA1 的值,然后在进行 urlencode 编码就能得到这个值,所以要用到2个库 openssl 中含加密库 curl 中含 url 功能。

其实,我很轻松的就解码出来了仅有一个文件的 bt 种子: [NC-Raws] 本田小狼與我 _ Super Cub - 07 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4].torrent

但是又找了另外2个种子,含多个文件的种子,也含有 DTH 网络的信息的种子解析失败,原因后面会说。

DTH 网络信息,放到种子文件的 nodes 里面,重点来了。

专业的事必须使用专业的工具

SHA1 的计算方式,就是从 info 开始,到 nodes 这里,含 0x65 0x35 对应字符 e5 结束的 SHA1 的值,在转 url 编码就对了。

找了好几个种子每次看位置都不一样,感觉很奇怪,原来是因为 notepad++ hex 这个插件 十六进制和 显示的 asc2 字符根本对不上,后来换了专业的 winhex 终于确定了是这个位置。

代码已开源到 github 可以到置顶的贴里面去找。

解析文件列表并计算大小:

讯雷显示:

1 void Bencode::showFileEntity()

2 {

3 int file_length = 0;

4 std::string file_path = "";

5

6 for ( auto &file : file_entity_list )

7 {

8 file_length = file.getLength();

9 file_path = file.getPath();

10

11 //filter _____padding_file_0_如果您看到此文件,请升级到BitComet(比特彗星)0.85或以上版本____

12

13 if ( file_path.find("_____padding_file_" ) )

14 {

15 LOG_INFO("file length:%d %d.%dMB path:%s", file_length, file_length/1024/1024,

16 (file_length - (file_length/1024/1024 * 1024 * 1024))*1000/1024/1024/10,

17 file_path.c_str());

18 }

19 }

20 }

相关推荐

上古卷轴5矮人金属锭:获得与使用指南 上古卷轴5矮人金属锭:获得与使用指南
疯狂动物园动物图鉴大全 动物抓捕汇总
365bet在线足球开户

疯狂动物园动物图鉴大全 动物抓捕汇总

📅 09-29 👁️ 2084
国家文物局关于印发《古建筑修缮项目施工规程(试行)》的通知