ZeroNet网络协议¶
- 每条消息均使用MessagePack编码
- 每个请求具有3个参数:- cmd: 请求命令
- req_id: 请求的唯一id (simple, incremented nonce per-connection), the client has to include this when reply to the command.
- params: 请求的参数
 
- 请求示例: {"cmd": "getFile", "req_id": 1, "params:" {"site": "1EU...", "inner_path": "content.json", "location": 0}}
- 响应示例: {"cmd": "response", "to": 1, "body": "content.json content", "location": 1132, "size": 1132}
- 错误响应示例: {"cmd": "response", "to": 1, "error": "Unknown site"}
握手¶
通过向目标网络地址发送请求,每个连接都以握手开始:
| 参数 | 描述 | 
|---|---|
| crypt | Null/None, 仅用于响应 | 
| crypt_supported | 客户端支持的一系列连接加密方法 | 
| fileserver_port | 客户端的文件服务器端口 | 
| onion | (仅用于tor)客户的洋葱地址 | 
| protocol | 客户端使用的协议版本(v1或v2) | 
| port_opened | 客户端的客户端端口打开状态 | 
| peer_id | (未在tor上使用)客户端的peer_id | 
| rev | 客户的修订号 | 
| version | 客户端版本 | 
| target_ip | 服务器的网络地址 | 
目标基于“crypt_supported”在套接字上初始化加密,然后return:
| 返回值 | 描述 | 
|---|---|
| crypt | 要使用的加密 | 
| crypt_supported | 服务器支持的连接加密方法数组 | 
| fileserver_port | 服务器的文件服务器端口 | 
| onion | (仅用于tor)服务器的洋葱地址 | 
| protocol | 服务器使用的协议版本(v1或v2) | 
| port_opened | 服务器的客户端端口打开状态 | 
| peer_id | (未在tor上使用)服务器的peer_id | 
| rev | 服务器的修订号 | 
| version | 服务器的版本 | 
| target_ip | 客户端的网络地址 | 
注意: .onion连接上不使用加密,因为Tor网络默认提供传输安全性. 注意: 如果可以假设远程客户端支持SSL,则还可以在握手之前隐式初始化SSL.
示例:
发送握手:
{ "cmd": "handshake", "req_id": 0, "params": { "crypt": None, "crypt_supported": ["tls-rsa"], "fileserver_port": 15441, "onion": "zp2ynpztyxj2kw7x", "protocol": "v2", "port_opened": True, "peer_id": "-ZN0056-DMK3XX30mOrw", "rev": 2122, "target_ip": "192.168.1.13", "version": "0.5.6" } }
返回:
{
 "protocol": "v2",
 "onion": "boot3rdez4rzn36x",
 "to": 0,
 "crypt": None,
 "cmd": "response",
 "rev": 2092,
 "crypt_supported": [],
 "target_ip": "zp2ynpztyxj2kw7x.onion",
 "version": "0.5.5",
 "fileserver_port": 15441,
 "port_opened": False,
 "peer_id": ""
}
节点请求¶
getFile site, inner_path, location, [file_size]¶
向客户端请求文件
| 参数 | 描述 | 
|---|---|
| site | 站点地址(例如:1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) | 
| inner_path | 相对于站点目录的文件路径 | 
| location | 此字节中的请求文件(一个请求中最多发送了512个字节,因此您需要多个请求以获取更大的文件) | 
| file_size | 所需文件的总大小(可选) | 
返回:
| 返回值 | 描述 | 
|---|---|
| body | 请求的文件内容 | 
| location | 发送的最后一个字节的位置 | 
| size | 文件总大小 | 
streamFile site, inner_path, location, [file_size]¶
从客户端流式传输文件
返回:
| 返回值 | 描述 | 
|---|---|
| stream_bytes | 消息包有效负载之后的文件数据长度 | 
为了避免python-msgpack序列化大型二进制字符串,文件正文直接附加在MessagePack有效负载之后. 例如,
> {"cmd": "streamFile", "id": 1, "inner_path": "content.json", "size": 1234}
< {"cmd": "response", "to": 1, "stream_bytes": 1234}
< content of the file
ZeroNet实施细节:对于大于256 kb的文件段,默认情况下启用流.
ping¶
检查客户端是否还活着
返回:
| 返回值 | 描述 | 
|---|---|
| body | Pong | 
pex site, peers, need¶
与客户端交换对等体. 同行打包到 6 bytes (4byte IP使用 inet_ntoa + 2byte 用于端口)
| 参数 | 描述 | 
|---|---|
| site | 站点地址(例如:1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) | 
| peers | 请求者已打包的对等方列表 | 
| peers_onion | 请求者已打包的Tor Onion对等方列表 | 
| need | 请求者想要的对等体数 | 
返回:
| 返回值 | 描述 | 
|---|---|
| peers | 他为该站点拥有的IPv4对等方列表(打包) | 
| peers_onion | 此网站的Tor Torion同行列表(打包) | 
“peer”列表中的每个元素都是一个打包的IPv4地址.
| IP地址 | 端口 | 
|---|---|
| 4 bytes | 2 bytes | 
peers_onion 列表中的每个元素都是一个打包的Tor Onion服务地址.
| B32解码的Tor地址 | 端口 | 
|---|---|
| binary_str[0:-2] | binary_str[-2:] | 
要恢复洋葱地址,请将第一部分通过base64.b32encode传递,并将.onion附加到返回值.
update site, inner_path, body, [diffs]¶
更新一个站点文件.
| 参数 | 描述 | 
|---|---|
| site | 站点地址(例如:1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) | 
| inner_path | 相对于站点目录的文件路径 | 
| body | 更新后的content.json的完整内容 | 
| diffs (optional) | Diff opcodes 用于content.json中的修改文件 | 
返回:
| 返回值 | 描述 | 
|---|---|
| ok | 更新成功提示消息:) | 
格式差异¶
包含修改的字典
- Key: 更改了文件到content.json的相对路径(例如:data.json)
- Value: 文件的不同操作码的列表(例如:'[['=',5],['+','\ nello new line'],['-',6]]`)
可能的不同操作码:¶
| 操作码 | 描述 | 
|---|---|
| ['=', 相同的字符] | 尚未更改文件的一部分(例如: ['=',5]) | 
| ['+', 新字符] | 添加的字符(例如:“ ['+”, \ nhello新行”]) | 
| ['-', 删除字符数] | 更新文件的完整内容(例如: ['-', 6]) | 
收到更新后,客户端将尝试使用差异修补文件. 如果它与content.json(具有不同版本的文件)提供的sha哈希不匹配,它将自动从更新的发件人重新下载整个文件.
提示: 补丁程序每个文件限制为30KB,仅用于.json文件
listModified site, since¶
列出自给定参数以来修改的content.json文件. 它曾经用于获取站点的用户提交的内容.
| 参数 | 描述 | 
|---|---|
| site | 站点地址(例如:1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) | 
| since | 自此时间戳以来,列出content.json文件. | 
返回:
| 返回值 | 描述 | 
|---|---|
| modified_files | Key: content.json inner_path 值:上次修改日期 | 
示例:
> zeronet.py --silent peerCmd 127.0.0.1 15441 listModified "{'site': '1BLogC9LN4oPDcruNz3qo1ysa133E9AGg8', 'since': 1497507030}" { "to": 1, "cmd": "response", "modified_files": { "data/users/1NM9k7VJfrb1UWw5agAvyRfSn3ws1wTJ5U/content.json": 1497579272, "data/users/1QEfmMwKVxgR4rkREbdJYjgUmF3Zy8pwHt/content.json": 1497565986, "data/users/16NS3rBdW9zpLmLSQoD8nLTtNVsRFtVBhd/content.json": 1497575039, "data/users/1CjXarXgvcNeCJ2nMQxUi4DRFWp3GEur2W/content.json": 1497513808, "data/users/1L5rGDgTs4W2V7gekSvJNhKa7XaHkVwotD/content.json": 1497615798, "data/users/1LWuc6JBhUGrKEAh1aPrPU85dEMcKmg3pS/content.json": 1497594716, "data/users/1KdnTJVBGzEZrJppFZtzfG9chukuMv8xSb/content.json": 1497584640, "data/users/1GMNmr2bDPbT4c8yVnyCoDHke52CNCdqAa/content.json": 1497614188, "data/users/1GRm9rED83Tkfi3iWS9m3LWHiRpPZehWLd/content.json": 1497827772, "data/users/12Ugp53jiMdvj1Kxa1w7c2LcXUBdGPs1oK/content.json": 1497692901, "data/users/1F6BMqittjWUStzUbRXm2kG2GQ3RdBLqFQ/content.json": 1497571485, "data/users/1GgNo3CmxPd7n2pMSF3uyqf1XHvgtTUqCe/content.json": 1497560829, "data/users/16nArdxrSaNThNp83kL8E6NLL9WD98iUne/content.json": 1497627929, "data/users/16CAJkbfNRxNJq4aKdrZ2MSYFfFGvQ8JPi/content.json": 1497664899, "data/users/1DrBS2sTD3BX5BBxG8eqYsxXSvGt9kc5HE/content.json": 1497632000, "data/users/19sggoAZ4hcorrrfWoFWP9rwfpVsL29cnZ/content.json": 1497928134, "data/users/1NYpJupegoTXL4cFpkNdLNJ4XaAhTNhPe1/content.json": 1497535771, "data/users/1R67TfYzNkCnh89EFfGmXn5LMb4hXaMRQ/content.json": 1497691787, "data/users/1C9HXUYFSVafLxanwkaFPZRcRgCEGsj2Cn/content.json": 1497572833, "data/users/1LgoHzNGWeijeZbJ8a1YgGjMCnjaM4BWG/content.json": 1497620232, "content.json": 1497623639 } }
getHashfield site¶
获取客户端下载的 [可选文件ID] (#optional-file-id).
| 参数 | 描述 | 
|---|---|
| site | 站点地址(例如:1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) | 
返回:
| 返回值 | 描述 | 
|---|---|
| hashfield_raw | 使用 array.array(“ H”,[1000,1001 ..]).tostring()编码的可选文件ID. | 
示例:
> zeronet.py --silent peerCmd 192.168.1.13 15441 getHashfield "{'site': '1Gif7PqWTzVWDQ42Mo7np3zXmGAo3DXc7h'} { 'to': 1, 'hashfield_raw': 'iG\xde\x02\xc6o\r;...', 'cmd': 'response' }
setHashfield site, hashfield_raw¶
设置请求者客户端具有的 [可选文件ID] (#optional-file-id)列表.
| 参数 | 描述 | 
|---|---|
| site | 站点地址(例如:1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) | 
| hashfield_raw | 使用 array.array(“ H”,[1000,1001 ..]).tostring()编码的可选文件ID. | 
| 返回: | 
| 返回值 | 描述 | 
|---|---|
| ok | 更新 | 
findHashIds site, hash_ids¶
查询客户端是否知道具有请求的hash_ids的任何对等方
| 参数 | 描述 | 
|---|---|
| site | 站点地址(例如:1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) | 
| hash_ids | 客户端当前正在寻找的可选文件ID的列表 | 
返回:
| 返回值 | 描述 | 
|---|---|
| peers | Key: 可选文件ID 值:使用`socket.inet_aton(ip)+ struct.pack(“ H”,port)编码的ipv4对等点列表 | 
| peers_onion | 可选文件id 值:使用`base64.b32decode(onion.replace(“.onion”,“”).upper())+ struct.pack(“ H”,port)编码的Tor同级列表. | 
示例:
> zeronet.py --silent peerCmd 192.168.1.13 15441 findHashIds "{'site': '1Gif7PqWTzVWDQ42Mo7np3zXmGAo3DXc7h', 'hash_ids': [59948, 29811]}" { 'to': 1, 'peers': { 29811: [ 'S&9\xd3Q<', '>f\x94\x98N\xa4', 'gIB\x90Q<', '\xb4\xady\xf7Q<' ], 59948: [ 'x\xcc>\xf6Q<', 'S\xa1\xddkQ<', '\x05\xac\xe8\x8dQ<', '\x05\xc4\xe1\x93Q<', 'Q\x02\xed\nQ<' ] }, 'cmd': 'response', 'peers_onion': { 29811: ['\xc7;A\xce\xbc\xd9O\xe2w<Q<'], 59948: ['\xc7;A\xce\xbc\xd9O\xe2w<Q<'] } }
可选文件ID¶
哈希的前4个字符的整数表示:
>>> int("ea2c2acb30bd5e1249021976536574dd3f0fd83340e023bb4e78d0d818adf30a"[0:4], 16)
59948
checkport port¶
检查其他对等体的请求端口.
| 参数 | 描述 | 
|---|---|
| port | 将检查的端口. | 
返回:
| 返回值 | 描述 | 
|---|---|
| status | 端口状态(“打开”或“关闭”) | 
| ip_external | 请求者的外部IP | 
大文件插件¶
getPieceFields site¶
返回字典中客户端对该站点具有的所有大文件piecefield.
| 参数 | 描述 | 
|---|---|
| site | 要求的地点 | 
返回:
| 返回值 | 描述 | 
|---|---|
| piecefields_packed | Key: Bigfile的sha512/256 merkle根哈希 值:打包piecefield | 
setPieceFields site, piecefields_packed¶
为该站点设置客户的piecefields.
| 参数 | 描述 | 
|---|---|
| site | 请求的页面地址 | 
| piecefields_packed | Key: Bigfile的sha512/256 merkle根哈希 值:打包piecefield | 
返回:
| 返回值 | 描述 | 
|---|---|
| ok | 更新 | 
Bigfile piecefield¶
将大文件下载件信息保存在一个具有1/0值的简单字符串中. (1 =已下载,0 =未下载)
示例:
1110000001表示文件大小为9-10MB,客户端以1MB的大小下载了前3MB和后1MB.
Packed format:
通过计算以“ 1”开头的重复字符,将字符串转换为int列表.
示例:
1110000001to[3, 6, 1],0000000001to[0, 9, 1],1111111111to[10]
转换后,使用array.array('H',piecefield)将其转换为更有效的typed array.
大文件Merkle根¶
在大文件哈希过程中,除了将逐段的sha512/256哈希摘要存储在piecemap文件中之外,该算法还使用 merkle-tools实现. merkle根仅用作标识大文件的ID,尚未用作验证文件的ID.
提示: 选择merkle根来标识文件,而不是文件的实际SHA-512 / 256哈希. 显然,使用后者会导致对同一文件进行两次哈希处理. (对于整个文件一次一次计件图)
提示: merkle根不用于验证碎片或大文件的完整性,因为与逐个哈希映射文件本身相比,这样做会占用更多的带宽和空间来传输和存储merkle证明以进行部分验证.
大文件片段图¶
它包含每个SHA-512/256 哈希. 片段大小和picemap文件名在content.json中定义, 例如:
...
 "files_optional": {
  "bigfile.mp4": {
   "piece_size": 1048576,
   "piecemap": "bigfile.mp4.piecemap.msgpack",
   "sha512": "d1f0d150e1e73bb1e684d370224315d7ba21e656189eb646ef7cc394d033bc2b",
   "size": 42958831
  },
...
具有以下数据结构,片断图文件打包为msgpack格式:
{
  b'bigfile.mp4': {b'sha512_pieces': [
    b"e\xde\x0fx\xec\xc5LZ9\x0e\xe7\x85E\x1b\xd5\xe4C'\xe7req\xe3<\xff\\\xbb\xc8b\xc2\xc1\x8e",
    b'\xef\xe8\xed\xfe\x16/\x96\xdb;;\x06n[8_\x06\x9ak|\xe1\x9f\xe1\xaf\x87\x96\xdd\xfd\x9bEf\xd9!',
    b'\x1c\xd6-\x1f\xce\xde{\xcd\x01\x93un =D\x0brmB-\xd1\x8c\xbf\xfe\xca\x8a\x1c\xf60\xbb\xedD',
    b'\x1aQdF\xd2\xbc\xdff{\xb7\x89\xf2\xd3\r\xa9\xe1\xefA-V\x18\xa4\xc8e\x13\x88v\x13\\&\xfbW',
    ...
  ]}
}