admin管理员组

文章数量:1530842

2023年12月17日发(作者:)

Emule Kad协议手册

文档编写:kernel,huby

版权所有: emuledev@

一、概述 3

二、协议参数分析 ........................................................................................................................... 3

2.1 BootStrap Req/Res .............................................................................................................. 3

2.2 Hello Req/Res ..................................................................................................................... 4

2.3 Kad Req/Res .................................................................................................................... 4

2.4 Kad Search/Publish Req/Res ............................................................................................... 6

2.5 Kad Firewalled Req/Res ..................................................................................................... 6

2.6 Kad FindBuddy 7

2.7 kad Callback Req ................................................................................................................ 7

三、KAD Search Action .................................................................................................................. 7

3.1、SendFindValue ................................................................................................................. 8

3.2、StorePackt ........................................................................................................................ 8

3.2.1 GetInfo相关协议: ................................................................................................ 9

3.2.2 Publish 相关协议: ............................................................................................. 9

四、Emule Buddy机制分析 ............................................................................................................ 9

4.1 网络协议包序列图: ...................................................................................................... 10

五、Emule Kad 数据结构分析 ..................................................................................................... 11

附录1( OPCode List): .................................................................................................................... 12

附录2(Question List): ................................................................................................................. 14

一、概述

Kad使用UDP协议,通过eMule软件的UDP端口发送和接收数据

这个宏定义了我能够接受的KAD最高版本

#define KADEMLIA_VERSION

在ed2k协议里面被使用

CUpDownClient::SendHelloTypePacket

0x02

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

4B

2B

2B

1B

The sender Kad ID

The sender IP

The sender UDP Port

The sender TCP Port

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

1B

16B

4B

2B

2B

1B

0x18

KADEMLIA_HELLO_RES

回应KADEMLIA_HELLO_REQ,协议包格式和KADEMLIA_HELLO_REQ一样

OP_KADEMLIAHEADER

byOpcode

The sender Kad ID

The sender IP

The sender UDP Port

The sender TCP Port

0

KADEMLIA2_HELLO_REQ

参照CKademliaUDPListener::SendMyDetails

1B

1B

16B

2B

1B

1B

1B+32B

1B+32B

KADEMLIA2_HELLO_RES

回应KADEMLIA2_HELLO_REQ,协议包格式和KADEMLIA2_HELLO_REQ一样

OP_KADEMLIAHEADER

byOpcode

The Sender KadId

The sender UDP Port

KADEMLIA_VERSION

Tag Count(2)

TAG_USER_COUNT(uint32)

TAG_FILE_COUNT(uint32)

我们可以看到,在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

1B

1B

16B

16B

OP_KADEMLIAHEADER

byOpcode

Search Tye

Target Hash

当前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

1B

16B

1B

n*25B 16B

4B

2B

2B

1B

Kad2.0

0x21 KADEMLIA2_REQ

参考 CSearch::SendFindValue(CContact* pContact)

1B

1B

1B

16B

16B

0x29 KADEMLIA2_RES

OP_KADEMLIAHEADER

byOpcode

Search Tye

Target Hash

当前Contacting 的Contact ID

OP_KADEMLIAHEADER

byOpcode

Target UINT128

更接近Target的Contact Count

// Max count is 32. size 817.. // 16B + 1B + 25B(32)

Contact ID

IP

UDP Port

TCP Port

Type (发送这个信息时有填写ContactType值,但Response中处理的时候好像没处理)

参考 CKademliaUDPListener::Process_KADEMLIA2_REQ

和 CKademliaUDPListener::Process_KADEMLIA2_RES

1B

1B

16B

1B

n*25B

16B

4B

2B

2B

OP_KADEMLIAHEADER

byOpcode

Target UINT128

更接近Target的Contact Count

// Max count is 32. size 817.. // 16B + 1B + 25B(32)

Contact ID

IP

UDP Port

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

KADEMLIA_FIND_VALUE

KADEMLIA_STORE

SearchType

FILE

KEYWORD

NOTES

FINDSOURCE

STOREFILE

说明

STOREKEYWORD

STORENOTES:

FINDBUDDY

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

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

//UnUsed(老的协议)

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)

{

list->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)

{

list->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

#define KADEMLIA2_BOOTSTRAP_REQ

#define KADEMLIA_BOOTSTRAP_RES

#define KADEMLIA2_BOOTSTRAP_RES

#define KADEMLIA_HELLO_REQ

#define KADEMLIA2_HELLO_REQ

#define KADEMLIA_HELLO_RES

#define KADEMLIA2_HELLO_RES

#define KADEMLIA_REQ

(receiver) 16>

#define KADEMLIA2_REQ

#define KADEMLIA_RES

#define KADEMLIA_SEARCH_REQ

[ext]

//#define UNUSED

0x31 // Old Opcode, don't use.

0x32 //

0x34 //

0x35 //

0x38 // (

0x39 // Old Opcode, don't use.

0x3A // (

0x3B //

0x40 // (

0x41 // Old Opcode, don't use.

0x42 //

#define KADEMLIA_SEARCH_NOTES_REQ

#define KADEMLIA2_SEARCH_KEY_REQ

#define KADEMLIA2_SEARCH_NOTES_REQ

#define KADEMLIA_SEARCH_RES

//#define UNUSED

[16]> *(CNT2))*(CNT1)

#define KADEMLIA_SEARCH_NOTES_RES

#define KADEMLIA2_SEARCH_RES

#define KADEMLIA_PUBLISH_REQ

//#define UNUSED

[16]> *(CNT2))*(CNT1)

#define KADEMLIA_PUBLISH_NOTES_REQ

[2]> *(CNT2))*(CNT1)

0x30 //

0x28 // *(CNT)

0x29 // #define KADEMLIA2_RES

0x21 //

0x20 //

0x18 //

0x19 //

0x10 //

0x11 //

0x08 // *(CNT)

0x09 //

0x00 //

0x01 //

0x33 //

#define KADEMLIA2_SEARCH_SOURCE_REQ

[16]> *(CNT2))*(CNT1)

#define KADEMLIA2_PUBLISH_KEY_REQ

#define KADEMLIA2_PUBLISH_SOURCE_REQ

#define KADEMLIA2_PUBLISH_NOTES_REQ

#define KADEMLIA_PUBLISH_RES

//#define UNUSED

#define KADEMLIA_PUBLISH_NOTES_RES

#define KADEMLIA2_PUBLISH_RES

#define KADEMLIA_FIREWALLED_REQ

#define KADEMLIA_FINDBUDDY_REQ

#define KADEMLIA_CALLBACK_REQ

#define KADEMLIA_FIREWALLED_RES

#define KADEMLIA_FIREWALLED_ACK_RES

#define KADEMLIA_FINDBUDDY_RES

0x43 //

0x44 //

0x45 //

0x48 //

0x49 // Old Opcode, don't use.

0x4A //

0x4B //

0x50 //

0x51 //

0x52 //

0x58 //

0x59 // (null)

0x5A //

附录2(Question List):

1 对方的KadId 是怎么知道的?

:如果这个Source是server告诉我的,那么就发Hello后知道的?

:对方通过kad opcode 协议发Source告诉我的(20个)?

存储在自己的KadContact列表中等待处理

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网络的标准就是有没有联系人(当然是活动的,不是存储的静态列表)

9 、一个peer的Buddy丢了以后,重新找了Buddy,别的peer

怎么知道呢?

本文标签: 信息协议发送时候网络