Emule Kad协议手册
文档编写:kernel,huby
版权所有: emuledev@verycd.com
3
一、概述
3
二、协议参数分析
3
2.1 BootStrap Req/Res
4
2.2 Hello Req/Res
4
2.3 Kad Req/Res
5
2.4 Kad Search/Publish Req/Res
6
2.5 Kad Firewalled Req/Res
6
2.6 Kad FindBuddy Req/Res
6
2.7 kad Callback Req
7
三、KAD Search Action
7
3.1、SendFindValue
8
3.2、StorePackt
8
3.2.1 GetInfo相关协议:
8
3.2.2 Publish 相关协议:
9
四、Emule Buddy
机制
综治信访维稳工作机制反恐怖工作机制企业员工晋升机制公司员工晋升机制员工晋升机制图
分析
9
4.1 网络协议包序列图:
11
五、Emule Kad 数据结构分析
12
附录1( OPCode List):
14
附录2(Question List):
一、概述
Kad使用UDP协议,通过eMule软件的UDP端口发送和接收数据
这个宏定义了我能够接受的KAD最高版本
#define KADEMLIA_VERSION
0x02
在ed2k协议里面被使用
CUpDownClient::SendHelloTypePacket
Kad版本:分为1.0和2.0
区别:使用两套独立的opcode
从2.0开始具有可扩展性,将版本信息写入协议,日后的扩展不再需要修改opcode
0.47a默认使用的版本为Kad1.0,但是支持2.0
0.47c没有测试过
KAD协议基本上是成对的一个REQ(Request)就有一个对应的RES(Respone)
所有具有KAD头的包,(有些可能是压缩的),最终被送到Kademlia::Process中处理
Process处理各种不同的opcode并将这些数据送到对应的处理函数,在REQ消息的处理函数中,解析发送过来的数据,并且构造RES数据包并发送出去
二、协议参数分析
2.1 BootStrap Req/Res
0x00 KADEMLIA_BOOTSTRAP_REQ
发送请求,这个时候急需扩大自己的KAD网络
总共发送25B, 参照CKademliaUDPListener::SendMyDetails
16B
The sender Kad ID
4B
The sender IP
2B
The sender UDP Port
2B
The sender TCP Port
1B
0
0x08 KADEMLIA_BOOTSTRAP_RES
Packet Param: 返回20个Peer信息+自己的信息
KADEMLIA2_BOOTSTRAP_REQ
KADEMLIA2_BOOTSTRAP_RES
2.2 Hello Req/Res
0x10 KADEMLIA_HELLO_REQ
总共发送2B+25B, 参照CKademliaUDPListener::SendMyDetails
1B
OP_KADEMLIAHEADER
1B
byOpcode
16B
The sender Kad ID
4B
The sender IP
2B
The sender UDP Port
2B
The sender TCP Port
1B
0
0x18 KADEMLIA_HELLO_RES
回应KADEMLIA_HELLO_REQ,协议包格式和KADEMLIA_HELLO_REQ一样
KADEMLIA2_HELLO_REQ
参照CKademliaUDPListener::SendMyDetails
1B
OP_KADEMLIAHEADER
1B
byOpcode
16B
The Sender KadId
2B
The sender UDP Port
1B
KADEMLIA_VERSION
1B
Tag Count(2)
1B+32B
TAG_USER_COUNT(uint32)
1B+32B
TAG_FILE_COUNT(uint32)
KADEMLIA2_HELLO_RES
回应KADEMLIA2_HELLO_REQ,协议包格式和KADEMLIA2_HELLO_REQ一样
我们可以看到,在2.0版本中,KadHello交换数据包括了自己Kad中已知的用户数和文件数, 但接收方都没有处理其中的Tag信息
2.3 Kad Req/Res
0x20 KADEMLIA_REQ
向一个Contact发送KAD Search 请求,希望得到更接近一个Hash 值的Contact 信息
Search类型包括:KADEMLIA_FIND_VALUE
KADEMLIA_STORE
KADEMLIA_FIND_NODE
参考 CSearch::SendFindValue(CContact* pContact)
1B
OP_KADEMLIAHEADER
1B
byOpcode
1B
Search Tye
16B
Target Hash
16B
当前Contacting 的Contact ID
0x28 KADEMLIA_RES
返回更接近一个hash value的Contact信息,
这样我们可以获得更加逼近hash的Contact 信息,也就是说我们更加有可能找到含有这个hash信息的Contact 〔回应方不管具体的Search类型,只是把有更接近Target的Contact 发送给请求方〕
参考 CKademliaUDPListener::Process_KADEMLIA_REQ
和 CKademliaUDPListener::Process_KADEMLIA_RES
1B
OP_KADEMLIAHEADER
1B
byOpcode
16B
Target UINT128
1B
更接近Target的Contact Count
// Max count is 32. size 817.. // 16B + 1B + 25B(32)
n*25B
16B
Contact ID
4B
IP
2B
UDP Port
2B
TCP Port
1B
Type (发送这个信息时有填写ContactType值,但Response中处理的时候好像没处理)
Kad2.0
0x21 KADEMLIA2_REQ
参考 CSearch::SendFindValue(CContact* pContact)
1B
OP_KADEMLIAHEADER
1B
byOpcode
1B
Search Tye
16B
Target Hash
16B
当前Contacting 的Contact ID
0x29 KADEMLIA2_RES
参考 CKademliaUDPListener::Process_KADEMLIA2_REQ
和 CKademliaUDPListener::Process_KADEMLIA2_RES
1B
OP_KADEMLIAHEADER
1B
byOpcode
16B
Target UINT128
1B
更接近Target的Contact Count
// Max count is 32. size 817.. // 16B + 1B + 25B(32)
n*25B
16B
Contact ID
4B
IP
2B
UDP Port
2B
TCP Port
2.4 Kad Search/Publish Req/Res
在kad 网络内获取需要的索引信息或发布信息
GetInfo相关协议:
KADEMLIA_SEARCH_REQ
//UnUsed(老的协议)
KADEMLIA_SEARCH_NOTES_REQ
KADEMLIA2_SEARCH_KEY_REQ
KADEMLIA2_SEARCH_SOURCE_REQ
KADEMLIA2_SEARCH_NOTES_REQ
KADEMLIA_SEARCH_RES
KADEMLIA_SEARCH_NOTES_RES
KADEMLIA2_SEARCH_RES
Publish 相关协议:
KADEMLIA_PUBLISH_REQ
//UnUsed(老的协议)
KADEMLIA_PUBLISH_NOTES_REQ
KADEMLIA2_PUBLISH_KEY_REQ
KADEMLIA2_PUBLISH_SOURCE_REQ
KADEMLIA2_PUBLISH_NOTES_REQ
KADEMLIA_PUBLISH_RES
KADEMLIA_PUBLISH_NOTES_RES
KADEMLIA2_PUBLISH_RES
2.5 Kad Firewalled Req/Res
请求别的Peer检测自己是否FireWalled
0x50 KADEMLIA_FIREWALLED_REQ
0x58 KADEMLIA_FIREWALLED_RES
2.6 Kad FindBuddy Req/Res
Emule 中的LowId 需要积极主动找 HighId作自己的Buddy,以便于和其它Peer通信
Exam: LowId(A) 主动FindBuddy HighId(B)
0x51 KADEMLIA_FINDBUDDY_REQ
//packet param: <128bit Target(Low.A KadId~)>
0x5a KADEMLIA_FINDBUDDY_RES
//packet param:
//B在接收到了FINDBUDDY_REQ之后,立即回答自己的TCP Port
Buddy 之间互相保持通信联系:
OP_BUDDYPING Param
OP_BUDDYPONG Param
20Minute间隔交互PINGPONG一次,并且HighId一方有防止LowId 频繁发送PING(LowId至少必须间隔10Min发送一次)
2.7 kad Callback Req
0x52 KADEMLIA_CALLBACK_REQ
OP_CALLBACK
三、KAD Search Action
KAD 网络中的Search 主要分为以下三组Action操作:
#define KADEMLIA_FIND_VALUE
0x02
#define KADEMLIA_STORE
0x04
#define KADEMLIA_FIND_NODE
0x0B
ActionType
SearchType
说明
KADEMLIA_FIND_VALUE
STOREFILE
STOREKEYWORD
STORENOTES
FINDBUDDY
KADEMLIA_STORE
FILE
KEYWORD
NOTES:
FINDSOURCE:
KADEMLIA_FIND_NODE
NODE:
NODECOMPLETE:
Kad的操作又分为两个阶段
(Ⅰ)先尽量找到与Target最近的 的Contact〔SendFindValue〕
(Ⅱ)直到找不到更近的Contact后,就在当前最近的Contact开始做具体的StorePacket Value操作(Publish or GetInfo) [Emule目前代码实现是:一个Contact如果3s之内没有返回更接近Target的Contact,则可以对该Contact进行Value操作了]
3.1、SendFindValue
查找与Target 更接近的 的Contact:
# Trace: Kad SendFindValue(CContact* pContact) CallStack
CSearch::Go( )
|->CSearch::SendFindValue( ... )
CSearch::JumpStart( )
|->CSearch::SendFindValue
CSearch::ProcessResponse
|->CSearch::SendFindValue //从Res结果中筛选出最好,继续SendFindValue
相关协议:
KADEMLIA_REQ
KADEMLIA2_REQ
KADEMLIA_RES
KADEMLIA2_RES
3.2、StorePackt
对Contact 发包,完成不同Action的Value操作 (主要是Publish和GetInfo)
#Trace: Csearch::StorePackt
CKademlia::Process( )
|->CSearchManager::JumpStart( )
|->CSearch::JumpStart( )
|->CSearch::StorePacket( )
3.2.1 GetInfo相关协议:
KADEMLIA_SEARCH_REQ
//UnUsed(老的协议)
KADEMLIA_SEARCH_NOTES_REQ
KADEMLIA2_SEARCH_KEY_REQ
KADEMLIA2_SEARCH_SOURCE_REQ
KADEMLIA2_SEARCH_NOTES_REQ
KADEMLIA_SEARCH_RES
KADEMLIA_SEARCH_NOTES_RES
KADEMLIA2_SEARCH_RES
3.2.2 Publish 相关协议:
KADEMLIA_PUBLISH_REQ
//UnUsed(老的协议)
KADEMLIA_PUBLISH_NOTES_REQ
KADEMLIA2_PUBLISH_KEY_REQ
KADEMLIA2_PUBLISH_SOURCE_REQ
KADEMLIA2_PUBLISH_NOTES_REQ
KADEMLIA_PUBLISH_RES
KADEMLIA_PUBLISH_NOTES_RES
KADEMLIA2_PUBLISH_RES
四、Emule Buddy机制分析
Emule 中的Buddy 机制是为了增进Emule中HihgId-LowId/LowId-LowId的通信,使得一个公网的Peer可以和处于NAT内的Peer之间相互通信,同时加上适当的策略,也可以让两个处于NAT内的Peer之间相互通信。下面是关于Emule Buddy的一些分析结论:
LowId 发现自己处于NAT内时,则主动开始在Kad网络内寻找Buddy,如果一个HighId没有为别的LowId做Buddy,则可以成为该LowId的Buddy。
LowId 的Buddy 必须是 HighId,反之,HighId的Buddy必须是LowId。
目前,0.47c 版本的Buddy 机制还是一对一。
LowId不是在自己的KadId周围附近找Buddy,是在KadId~方向找自己的Buddy,这样可以增进Kad网络的交互,不会引起小范围内的信息孤岛现象。
一个LowId 拿到HighId的Buddy后就可以向Kad网络内发布自己的Peer信息了。
〔发布信息包含: 自己的KadId~,Buddy IP,Buddy Udp Port〕
第一次的FINDBUDY 必须是在程序启动后的五分钟之后,因为之后的FINDBUDDY 是在丢失了 BUDDY 之后进行(但同时)
4.1 网络协议包序列图:
4.2 FindBuddy 过程状态图
4.3 Buddy实现框架代码List:
# LowId(A)
① 发送 KADEMLIA_FINDBUDDY_REQ
CSearch::StorePacket( )
{
Case FINDBUDDY:
//packet param: <128bit Target(Low.A KadId~)>
CKademlia::GetUDPListener()->SendPacket(&m_pfileSearchTerms, KADEMLIA_FINDBUDDY_REQ, pFromContact->GetIPAddress(), pFromContact->GetUDPPort());
}
②
接收 KADEMLIA_FINDBUDDY_RES
CKademliaUDPListener::Process_KADEMLIA_FINDBUDDY_RES (const byte *pbyPacketData, uint32 uLenPacket, uint32 uIP, uint16 uUDPPort)
{
theApp.clientlist->RequestBuddy(&contact); // now Peer.B Kad State is KS_QUEUED_BUDDY
}
③
Low.A TryToConnect to High.B
CClietList::Process( ... )
{
cur_client->SetKadState(KS_CONNECTING_BUDDY);// Peer.B KadState Changed to KS_CONNECTING_BUDDY
cur_client->TryToConnect(true); // Low.A TryToConnect to High.B
}
④ Low.A 连接 High.B 成功
CUpDownClient::ConnectionEstablished()
{
…
case KS_CONNECTING_BUDDY:
SetKadState(KS_CONNECTED_BUDDY); //Peer.B Kad State changed to KS_CONNECTED_BUDDY
}
# HighId(B)
① 接收 KADEMLIA_FINDBUDDY_REQ
CKademliaUDPListener::Process_KADEMLIA_FINDBUDDY_REQ (const byte *pbyPacketData, uint32 uLenPacket, uint32 uIP, uint16 uUDPPort)
{
theApp.clientlist->IncomingBuddy(&contact, &BuddyID); //Peer.A Kad State is KS_INCOMING_BUDDY
//回发 KADEMLIA_FINDBUDDY_RES packet
//packet param:
SendPacket(&fileIO2, KADEMLIA_FINDBUDDY_RES, uIP, uUDPPort);
}
②
CUpDownClient::ConectionEstablished( )
{
case KS_INCOMING_BUDDY:
SetKadState(KS_CONNECTED_BUDDY); //Peer.A Kad State changed to KS_CONNECTED_BUDDY
}
五、Emule Kad 数据结构分析
附录1( OPCode List):
关于KAD的Opcodes,Kad1.0个2.0相互对应
// KADEMLIA (opcodes) (udp)
#define KADEMLIA_BOOTSTRAP_REQ
0x00
//
#define KADEMLIA2_BOOTSTRAP_REQ
0x01
//
#define KADEMLIA_BOOTSTRAP_RES
0x08
// *(CNT)
#define KADEMLIA2_BOOTSTRAP_RES
0x09
//
#define KADEMLIA_HELLO_REQ
0x10
//
#define KADEMLIA2_HELLO_REQ
0x11
//
#define KADEMLIA_HELLO_RES
0x18
//
#define KADEMLIA2_HELLO_RES
0x19
//
#define KADEMLIA_REQ
0x20
//
#define KADEMLIA2_REQ
0x21
//
#define KADEMLIA_RES
0x28
// *(CNT)
#define KADEMLIA2_RES
0x29
//
#define KADEMLIA_SEARCH_REQ
0x30
// [ext]
//#define UNUSED
0x31
// Old Opcode, don't use.
#define KADEMLIA_SEARCH_NOTES_REQ
0x32
//
#define KADEMLIA2_SEARCH_KEY_REQ
0x33
//
#define KADEMLIA2_SEARCH_SOURCE_REQ
0x34
//
#define KADEMLIA2_SEARCH_NOTES_REQ
0x35
//
#define KADEMLIA_SEARCH_RES
0x38
// ( *(CNT2))*(CNT1)
//#define UNUSED
0x39
// Old Opcode, don't use.
#define KADEMLIA_SEARCH_NOTES_RES
0x3A
// ( *(CNT2))*(CNT1)
#define KADEMLIA2_SEARCH_RES
0x3B
//
#define KADEMLIA_PUBLISH_REQ
0x40
// ( *(CNT2))*(CNT1)
//#define UNUSED
0x41
// Old Opcode, don't use.
#define KADEMLIA_PUBLISH_NOTES_REQ
0x42
// *(CNT2))*(CNT1)
#define
KADEMLIA2_PUBLISH_KEY_REQ
0x43
//
#define
KADEMLIA2_PUBLISH_SOURCE_REQ
0x44
//
#define KADEMLIA2_PUBLISH_NOTES_REQ
0x45
//
#define KADEMLIA_PUBLISH_RES
0x48
//
//#define UNUSED
0x49
// Old Opcode, don't use.
#define KADEMLIA_PUBLISH_NOTES_RES
0x4A
//
#define
KADEMLIA2_PUBLISH_RES
0x4B
//
#define KADEMLIA_FIREWALLED_REQ
0x50
//
#define KADEMLIA_FINDBUDDY_REQ
0x51
//
#define KADEMLIA_CALLBACK_REQ
0x52
//
#define KADEMLIA_FIREWALLED_RES
0x58
//
#define KADEMLIA_FIREWALLED_ACK_RES
0x59
// (null)
#define KADEMLIA_FINDBUDDY_RES
0x5A
//
附录2(Question List):
1 对方的KadId 是怎么知道的?
:如果这个Source是server告诉我的,那么就发Hello后知道的?
:对方通过kad opcode 协议发Source告诉我的(20个)?
存储在自己的KadContact列
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
中等待处理
2 对方的Buddy信息我是怎么知道的?
在tcp hello的时候,有两个tag指明buddy的ip和udpport
之后当我们要通知LowID的Peer来callback我的时候,我们调用CUpDownClient::TryToConnect,发用KAD消息给BUDDY,并把这个Client的连接状态设定为等待回连(DS_WAITCALLBACKKAD)
3 Buddy是怎么确认的?(怎么在多个Contact中选一个作为我的Buddy)
KadState切换顺序?
4 Buddy相互之间(LowId<->HighId)如何长期保持联系?
当Buddy连接后会通过Ping的方式保持连接,Ping的发送方是LowID,每10分钟发送一次OP_BUDDYPING,而HighID在延时大于13分钟才回复一次OP_BUDDYPONG,这样导致的结果是最终LowID
Ping两次HighID回复一次。
Buddy现在是一对一的,可以做成一对多吗?可以有小量Peer抱成团吗?
需要修改数据结构
我认为可以做成一对多,可以增加一个BuddyList来保存
5 Kad1,Kad2 ?什么时候开始用Kad2?
Kad2 兼容Kad1 是怎么做到的?
我们可以很方便的扩充Kad2吗?
当对方的用户使用KAD2的opcode请求的时候,我们就使用KAD2的opcode进行回复
由于KAD1和2使用不同的opcode,所以只要实现这两套opcode的process函数就可以轻松处理
KAD1和2的主要功能是重复的,但是有一些小的改进,比如在KAD2中HELLO支持Tag,并且加入了两个Tag,
内容
财务内部控制制度的内容财务内部控制制度的内容人员招聘与配置的内容项目成本控制的内容消防安全演练内容
分别是Peer自己KAD所知的用户数和文件数(这些信息可能用来改善KAD的算法)
25个字节的Peer信息最后一个字节被设定为KAD版本信息,KAD2的改进
此外,我们需要得到对方支持的opcode的最高版本,在tcp hello握手的时候会相互交换信息。
KAD2的扩展是有可能的,但是如果增加版本号,有可能和未来的KAD协议相冲突。在某些OPCODE中增加Tag信息,则是有可能的。
6 所有不同way拿到的source,都包含了这个Peer的哪些信息?
如果需要一个Peer更丰富的信息,通过哪些途径可以获取信息?
:以下途径都可以获取Source,但获取的信息都不一定是完整的
SF_SERVER
SF_KADEMLIA
SF_SOURCE_EXCHANGE
SF_PASSIVE
SF_LINK
7 是怎么表示我是否已经有了Buddy?
在CClientList中Process不断处理KAD的状态转换,当状态是KS_CONNECTED_BUDDY的时候就说明已经有连接,此时必须每隔一段时间发送PING PONG等成对的opcode
是通过CClientList的成员变量 m_nBuddyStatus 来表示当前的Buddy的状态的。Buddy的状态如下:
enum buddyState
{
Disconnected, // 没有连接上Buddy
Connecting, // 正在连接Buddy
Connected // 已经连接上Buddy
};
CClientList创建时m_nBuddyStatus = Disconnected
Buddy的状态为未连接。具体的状态切换我已经在图四里面用绿色字体标示出来了。
8 第一次加入Kad应该做什么?是怎么加入kad的 ?
需要更多Contact的时候发 BootStrap Req 吗?
确定是否已经加入了KAD网络,调用CKademlia::IsConnected()来检测
IsConnected()函数调用m_pInstance->m_pPrefs->HasHadContact(); 来确定,也就是说,我们加入KAD网络的
标准
excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载
就是有没有联系人(当然是活动的,不是存储的静态列表)
9 、一个peer的Buddy丢了以后,重新找了Buddy,别的peer
怎么知道呢?