DNS 种子节点的处理
P2P 网络的建立是在系统启动的第 12 步,最后时刻调用 CConnman::Start
方法开始的。
本部分内容在 net.cpp
、net_processing.cpp
等文件中。
ThreadDNSAddressSeed
这个线程的目标是,只有在需要地址时才查询 DNS 种子,当我们不需要 DNS 种子时,会避免 DNS 种子查询。这样可以通过创建更少的识别 DNS 请求来提高用户隐私,通过减少种子对网络拓扑的影响来减少信任,并减少种子的流量。
线程定义在 net.cpp
文件的 1603 行。下面我们开始进行具体的解读。
如果对等节点的数量大于 0,且没有指定
-forcednsseed
,或指定了但值为false
,进行下面的处理:遍历所有的节点,如果节点已成功连接,且不是引导节点,且
fOneShot
属性为假,且不是不是手动连接的,且不是入站节点,那么变量nRelevant
加1。如果变量
nRelevant
大于2,即 P2P 网络已经可用,则退出函数。获取并遍历所有的 DNS 种子节点。
下面,对上面的代码进行讲解。
如果指定了代理,则调用
AddOneShot
方法,保存当前 DNS 种子节点到vOneShots
集合中。否则,进行下面的处理:生成两个集合
vIPs
、vAdd
。vIPs
集合中保存的是CNetAddr
对象,代表了一个IP地址。vAdd
集合中保存的是CAddress
对象,CAddress
继承自CService
,后者又继承自CAddress
,包含了一些关于对等节点别的信息。调用
GetDesirableServiceFlags
方法,获得服务标志位。调用
strprintf
函数,格式化 DNS 种子节点的地址。strprintf
是一个宏定义,实际调用的是 Boost 库的tfm::format
。生成类型为
CNetAddr
的地址对象resolveSource
,并调用其SetInternal
方法,设置resolveSource
的 IP。如果出错,则返回处理下一个。调用
LookupHost
方法,根据 DNS 种子节点获取其保存的对等节点列表。并保存在vIPs
集合中。LookupHost
方法内部主要调用了LookupIntern
方法进行处理。下面我们看下后者的具体处理。生成一个地址对象
addr
。然后调用其SetSpecial
方法进行处理。在该方法内部,如果 DNS种子节点不是以
.onion
结尾,即不是暗网地址,则直接返回假。否则进行下面的处理。调用
DecodeBase32
方法,解析不包括暗网后缀在内的具体的地址。接下来,检查地址的长度是否不等于指定的长度,如果是则返回假。否则,对地址进行处理并转化为IP地址,然后返回真。如果前面方法返回的结果为真,即 DNS 种子为暗网地址,则把当前地址加入
vIP
集合,并返回。生成一个类型为
addrinfo
的结构体对象 aiHint,并设置其各个属性值。生成一个类型为
addrinfo
的结构体对象 aiRes,然后调用getaddrinfo
方法,根据 DNS 种子节点来获取一个地址链接表。这个方法是系统提供的方法,返回的是一个 sockaddr 结构的链表而不是一个地址清单。第一个参数是一个主机名或者地址串,第二个参数是一个服务名或者10进制端口号数串,第三个参数可以是一个空指针,也可以是一个指向某个addrinfo结构的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示,最后一个参数是返回的结果。
接下来只要地址信息链表不空,且当前获取的对等节点IP数量小于指定的数量或者指定的数量是0(即不限制对等节点的数量),就循环这个链表进行下面的处理。
根据返回的地址信息对象,是IPV4 或者是 IPV6,生成生成不同的
CNetAddr
对象。如果这个地址对象不是内部 IP,则保存到vIP
集合中。从地址信息链表中取得下一个地址信息对象。调用
freeaddrinfo
方法,释放getaddrinfo
方法所申请的内存空间。根据
vIP
集合的大小,返回真假。
如果
LookupHost
方法返回结果为真,即根据当前 DNS 种子节点查找到了至少一个对等节点,则进行下面的处理。遍历
vIPs
集合,根据当前的 IP 地址,生成一个CAddress
地址对象,并保存在vAdd
集合中,同时把代表找到节点的变量found
加1。调用地址管理器的
Add
方法,保存多个地址。具体代码如下:
如果
LookupHost
方法返回结果为假,即根据当前 DNS 种子节点没找到一个对等节点,则调用AddOneShot
方法进行处理。AddOneShot
方法内部简单地把当前 DNS 种子加入vOneShots
集合。
2.1、CAddrMan::Add 方法
下面我们对地址管理器的 Add
方法做下介绍。这个方法位于 addrman.h
文件的 540 行。
这个方法主体是一个 for 循环,遍历 CAddress
集合,针对每一个 CAddress
对象调用 Add_
方法进行处理。并返回是否添加成功。代码如下:
接下来,我们来看一下 Add_
方法。这个方法在 addrman.cpp
文件的第254行。
如果当前地址是不可路由的,则直接返回假。
调用
Find
方法,根据地址对象找到其对应的地址信息。如果地址对象来源对象,设置变量
nTimePenalty
等于0。如果找到对应的地址信息,则设置地址信息的相关属性
如果没有找到对应的地址信息,则生成新的地址信息。
在
Create
方法中,生成一个新的CAddrInfo
对象,并放到mapInfo
集合中,同时在在mapAddr
集合中增加对应的条目。具体代码如下:接下来处理其他一些信息,代码比较简单不详述。
返回真。
Last updated
Was this helpful?