Tor源码分析三 — 客户端执行流程(初始化)

  Tor系统中,主机的身份包括有这几种:Client,Bridge Server,Relay Server,Directory Server。

  当然,有的时候一台主机是可以身兼数个身份,提供不同的服务或获取服务。

  我们从最简单的客户端配置的Tor来进行分析,事先了解整个Tor系统的执行规程,之后再具体分析其他身份时候的不同操作,从而加快了解系统的速度。此处要说明的是,由于笔者对Windows的编程不甚了解,暂时就去除源码中所有为了让Tor系统具有夸平台性的Windows代码部分。

1. Tor系统的入口函数

  大部分源程序的入口函数是main。Tor系统为了实现更加简便的单元测试,将main函数设为调用tor_main函数,后者是整个Tor系统的执行主函数。上述关系可以在Tor_main.c文件中看到,该文件中只是用很少的几行描述了main与tor_main之间的关系。

2. Tor系统初始化

  系统初始化包括四个函数:

  1)update_approx_time(time(NULL))

    记录当前时间的估值,存于cached_approx_time,一般每秒钟被系统其它部分调用一次。在系统开始运行时,最先执行。

  2)tor_threads_init()

    根据Tor系统是否被允许是多线程运行而执行不同的操作。

    若不允许多线程运行,则不做操作;

    若允许,记录main_thread_id,并根据系统的平台,设置thread_initialized标签以及对多线程参数进行初始化。

  3)init_logging()

    初始化系统日志信息列表。

    初始化对log进行操作的互斥量log_mutex,设置log_mutex_initialized标签。在系统刚刚启动时,会新建log日志消息链表pending_cb_message

    注:smartlist是Tor系统实现的一个链表系统,可以执行基本链表操作等。

  4)tor_init()

    系统最主要的初始化函数。

    i)初始化系统最重要的几个全局变量:

       time_of_process_start,进程开始真正运行的时间;

       connection_array,系统所有连接链表,连接包括DIR, AP, OR, EXIT等类型的连接;

       closeable_connection_lst,系统所有要关闭的连接链表;

       active_linked_connection_lst系统所有活动的linked连接链表,linked连接包括DIR, AP等类型的连接。

    ii)输出调试信息和设置应用程序名appname

    iii)初始化三个子功能件:

       rep_hist_init(),reputation history初始化,包括摘要映射表history_map,带宽数组bw_array,预测端口predicted_ports_list等初始化;

       rend_cache_init(),初始化服务描述符缓存rend_cacherend_cache_v2_dir

       addressmap_init(),初始化地址映射addressmapvirtaddress_reversemap

    iv)通过tor配置文件及命令行参数初始化tor系统,生成全局选项变量global_options;options_init_from_torrc()(非常重要,读者要自行详细分析)

       开启系统监听的端口;生成Lock与State文件;解析固化在系统代码内的10个目录服务器地址;解析GeoIp文件;执行其他相关操作。

    v)初始化OpenSSL:crypto_global_init()(略)

3. option_init_from_torrc()函数解析

  该函数指定了Tor运行的执行操作:CMD_RUN_TOR,CMD_LIST_FINGERPRINT,CMD_VERIFY_CONFIG,CMD_HASH_PASSWORD。

  其中,CMD_RUN_TOR是Tor系统真正的执行命令。当输入的参数argv不存在上述后面三个命令之时,系统默认执行Tor主线命令,启动Tor系统。

  而在系统启动之前,还需要进行默认配置文件,输入配置文件以及命令参数的综合解析,所以该函数中出现以下执行代码:

    1)cf_default = load_torrc_from_disk(argc, argv, 1);读取默认配置文件中的配置参数,输出为整个字符串。

    2)cf = load_torrc_from_disk(argc, argv, 0);读取命令行输入的配置文件的配置参数,输出为整个字符串。

    3)options_init_from_string(cf_default, cf, command, command_arg, &errmsg);利用输入的所有配置参数初步启动系统。

      cf = config;cf_default = config default;

      command = 系统主命令;command_arg = 系统主命令参数,其实就是一般的命令行输入参数;errmsg为错误输出消息。

  该函数的重点,在于调用了options_init_from_string()函数,从而往下调用到set_options()函数,而后又深层调用到了options_act_reversible()和options_act()。上述深层调用到的函数,会很细节地初始化Tor系统的大多部分,此处就不再赘述。另外关于配置,不得不提的是:默认配置,输入配置文件以及命令行的配置,三个配置之间的选择规则,是三者的优先级逐级递增。也就是说,输入配置文件的配置会先覆盖默认配置文件的配置,命令行的配置会再次覆盖输入配置文件的配置。实际上这个部分还有更复杂的规则,具体细则,可以参看Tor Manual的详细说明。此处就黏贴如下,不再翻译:

  By default, an option on the command line overrides an option found in the configuration file, and an option in a configuration file overrides one in the defaults file.

  This rule is simple for options that take a single value, but it can become complicated for options that are allowed to occur more than once: if you specify four SOCKSPorts in your configuration file, and one more SOCKSPort on the command line, the option on the command line will replace all of the SOCKSPorts in the configuration file. If this isn’t what you want, prefix the option name with a plus sign, and it will be appended to the previous set of options instead.

  Alternatively, you might want to remove every instance of an option in the configuration file, and not replace it at all: you might want to say on the command line that you want no SOCKSPorts at all. To do that, prefix the option name with a forward slash.