源码分析到这里,大家应该已经大致了解到Tor系统的前期启动没有做任何的下载操作。前期启动的最关键环节,就是正常开启Libevent的调度机制,从而有条不紊地进行系统内所有子模块的维护等。我们需要再次强调的是,系统的主进程内,是没有做任何直接的获取网络状态,获取路由描述符,获取额外路由信息的操作。系统将这些操作视为需要实时维护的工作,因为所有这些网络信息都有其时限。所以,对这些网络信息的获取,全部置于秒回调函数之后进行。秒回调函数能够控制对这些信息的检测和获取,这些在前一节中已经有部分描述。
我们将在本文中再次重申秒回调函数中最重要的执行线路,以弄清Tor系统是如何获取网络信息,从而开启链路以至于完成整个系统的成功建立。由于本文中所提到的许多函数嵌套层次非常之多,我们只针对最重要的函数加以说明;同时,我们会略去许多不被执行的函数。这些不被执行的函数,大多情况下是因为执行条件不满足而未能够通过函数内部执行前检测,或更甚者未能通过函数外部的判断语句检测。关于这些情况,请大家自行查看函数细节,此处就不再一个一个详细解释。
1. update_networkstatus_downloads
前面介绍的秒回调函数之中最重要的维护函数即为事件调度函数。事件调度函数中,大部分的内容由于与时间相关而不会在短期内执行。具体的执行间隔可以参照之前章节中源码的注释。在简要的对该函数进行分析之后,我们发现,客户端系统刚刚启动之时,最终重点调用的代码如下:
static void
run_scheduled_events(time_t now)
{
......
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
......
if (time_to_check_descriptor < now && !options->DisableNetwork) { //每一分钟进行一次网络状态的下载检测
......
time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
......
/* Also, once per minute, check whether we want to download any
* networkstatus documents.
*/
update_networkstatus_downloads(now); //开启网络状态的下载
}
......
}
以下为网络状态下载函数的函数体:
/** Launch requests for networkstatus documents and authority certificates as
* appropriate. */
void
update_networkstatus_downloads(time_t now)
{
const or_options_t *options = get_options();
if (should_delay_dir_fetches(options)) //使用Bridge的情况下,若不知道任何可用Bridge的信息,则延迟下载
return;
if (authdir_mode_any_main(options) || options->FetchV2Networkstatus) //权威服务器适当地下载V2网络状态信息,客户端不需要
update_v2_networkstatus_cache_downloads(now);
update_consensus_networkstatus_downloads(now); //首先下载网络共识
update_certificate_downloads(now); //其次下载权威服务器证书,没有需要验证的网络共识之前不会执行
}
依据这样的执行流程,系统开始进入网络共识的下载。值得说明的是,开启了第一次下载之后,当下一次秒回调函数重新执行到此处之时,会发现已经有向权威服务器请求网络共识的连接存在,于是此处的函数便不再执行。接下来我们详细分析获取网络共识所需网络连接的建立:
/** If we want to download a fresh consensus, launch a new download as
* appropriate. */
static void
update_consensus_networkstatus_downloads(time_t now)
{
int i;
const or_options_t *options = get_options();
for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
/* XXXX need some way to download unknown flavors if we are caching. */
......
if (! we_want_to_fetch_flavor(options, i)) //一般作为当前版本的客户端,只希望获取FLAV_MICRODESC类型的网络共识,其他类型全部跳过
continue;
c = networkstatus_get_latest_consensus_by_flavor(i); //当前无最新网络共识则返回为空
if (! (c && c->valid_after <= now && now <= c->valid_until)) {
/* No live consensus? Get one now!*/
time_to_download_next_consensus[i] = now; //更新需求下载网络共识的时间
}
if (time_to_download_next_consensus[i] > now)
return; /* Wait until the current consensus is older. */
resource = networkstatus_get_flavor_name(i); //要发送给目录服务器的请求内容,针对FLAV_MICRODESC类型的网络共识,其值为microdesc
if (!download_status_is_ready(&consensus_dl_status[i], now, //网络共识下载状态正常
CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
continue; /* We failed downloading a consensus too recently. */
if (connection_dir_get_by_purpose_and_resource( //没有当前正在下载网络共识的连接
DIR_PURPOSE_FETCH_CONSENSUS, resource))
continue; /* There's an in-progress download.*/
waiting = &consensus_waiting_for_certs[i]; //若有正在等待证书的网络共识,则尽量延长时间以等待证书的获取,以处理该网络共识成为可用状态的情况
if (waiting->consensus) {
/* XXXX make sure this doesn't delay sane downloads. */
if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
continue; /* We're still getting certs for this one. */
} else {
if (!waiting->dl_failed) {
download_status_failed(&consensus_dl_status[i], 0);
waiting->dl_failed=1;
}
}
}
log_info(LD_DIR, "Launching %s networkstatus consensus download.",
networkstatus_get_flavor_name(i));
//网络共识获取函数
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
ROUTER_PURPOSE_GENERAL, resource,
PDS_RETRY_IF_NO_SERVERS);
}
}