Tor version: v0.4.1.5
1. tor_run_main
tor_run_main(const tor_main_configuration_t _tor_cfg)
新版本的 tor 中 main.c 文件在src/main/
下
子系统初始化
subsystems_init();
该函数定义如下:
int
subsystems_init(void)
{
return subsystems_init_upto(MAX_SUBSYS_LEVEL);
}
/**
* Initialize all the subsystems whose level is less than or equal to
* target_level; exit on failure.
**/
int
subsystems_init_upto(int target_level)
{
// initialize the subsystem_initialized array
check_and_setup();
for (unsigned i = 0; i < n_tor_subsystems; ++i) {
const subsys_fns_t *sys = tor_subsystems[i];
if (!sys->supported)
continue;
if (sys->level > target_level)
break;
if (sys_initialized[i])
continue;
int r = 0;
if (sys->initialize) {
// Note that the logging subsystem is designed so that it does no harm
// to log a message in an uninitialized state. These messages will be
// discarded for now, however.
log_debug(LD_GENERAL, "Initializing %s", sys->name);
r = sys->initialize();
}
if (r < 0) {
fprintf(stderr, "BUG: subsystem %s (at %u) initialization failed.\n",
sys->name, i);
raw_assert_unreached_msg("A subsystem couldn't be initialized.");
}
sys_initialized[i] = true;
}
return 0;
}
接着是初始化日志警告安全等级
void
init_protocol_warning_severity_level(void)
{
atomic_counter_init(&protocol_warning_severity_level);
set_protocol_warning_severity_level(LOG_WARN);
}
初始化两个变量argc
和argv
一般 argc 代表运行时传递给 main 函数的命令行参数的个数**argv[] 则是一个指针数组,存放指向每个参数的指针
int argc = tor_cfg->argc + tor_cfg->argc_owned;
char **argv = tor_calloc(argc, sizeof(char*));
// memcpy为内存拷贝
memcpy(argv, tor_cfg->argv, tor_cfg->argc*sizeof(char*));
if (tor_cfg->argc_owned)
memcpy(argv + tor_cfg->argc, tor_cfg->argv_owned,
tor_cfg->argc_owned*sizeof(char*));
给所有的子系统添加 publish/subscribe 关系
/** Install the publish/subscribe relationships for all the subsystems. */
static void
pubsub_install(void)
{
pubsub_builder_t *builder = pubsub_builder_new();
int r = subsystems_add_pubsub(builder);
// tor_assert是tor中的断言机制,类似于其他语言抛出异常?
tor_assert(r == 0);
r = tor_mainloop_connect_pubsub(builder); // consumes builder
tor_assert(r == 0);
}
代码块的 code 表示运行tor_init()
,传入的参数为之前定义的 argc 和 argv
具体 tor_init()分析请看第 2 节
{
int init_rv = tor_init(argc, argv);
if (init_rv) {
tor_free_all(0);
result = (init_rv < 0) ? -1 : 0;
goto done;
}
}
2. tor_init
tor_init()是 Tor command-line client 的主入口,返回“0”表示初始化系统成功
char progname[256];
int quiet = 0;
time_of_process_start = time(NULL);
// 初始化连接池
tor_init_connection_lists();
/* Have the log set up with our application name. */
tor_snprintf(progname, sizeof(progname), "Tor %s", get_version());
// 设置应用程序名称
log_set_application_name(progname);
/* Initialize the history structures. */
// 包括history_map()和bw_arrays_init():带宽数组初始化
rep_hist_init();
/* Initialize the service cache. */
// 初始化服务描述符缓存
rend_cache_init();
addressmap_init(); /* Init the client dns cache. Do it always, since it's
* cheap. */
/* Initialize the HS subsystem. */
// 缓存Hidden Service子系统
hs_init();
根据命令行输入的参数情况来确定quiet
的值,我认为quiet
代表一个日志等级
Tor 默认状态下是在控制台打印notice
级别以上的日志(errors > warnings > notice),如果配置文件中定义了在其他地方打印日志,则会停止在控制台打印日志
如果添加--hush
选项,则告诉 Tor 仅在控制台打印warnings
和errors
级别的日志(可以理解为warnings
级别以上)
如果添加--quiet
选项,则告诉 Tor 不要在控制台打印任何日志
{
/* We search for the "quiet" option first, since it decides whether we
* will log anything at all to the command line. */
config_line_t *opts = NULL, *cmdline_opts = NULL;
const config_line_t *cl;
(void) config_parse_commandline(argc, argv, 1, &opts, &cmdline_opts);
for (cl = cmdline_opts; cl; cl = cl->next) {
// 注意strcmp是字符串相等返回0,所以这里代表选项中有--hush,后面亦然
if (!strcmp(cl->key, "--hush"))
quiet = 1;
if (!strcmp(cl->key, "--quiet") ||
!strcmp(cl->key, "--dump-config"))
quiet = 2;
/* The following options imply --hush */
if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") ||
!strcmp(cl->key, "--list-torrc-options") ||
!strcmp(cl->key, "--library-versions") ||
!strcmp(cl->key, "--list-modules") ||
!strcmp(cl->key, "--hash-password") ||
!strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) {
if (quiet < 1)
quiet = 1;
}
}
config_free_lines(opts);
config_free_lines(cmdline_opts);
}
/* give it somewhere to log to initially */
switch (quiet) {
case 2:
/* no initial logging */
// quiet最高级别,在控制台不打印任何日志
break;
case 1:
// 仅打印级别 >= warnings的日志
add_temp_log(LOG_WARN);
break;
default:
// 默认日志级别: >= notice
add_temp_log(LOG_NOTICE);
}
quiet_level = quiet;
接下来就是从 torrc 初始化配置,具体看第 3 节的分析
// 利用options_init_from_torrc函数从torrc加载配置
int init_rv = options_init_from_torrc(argc,argv);
if (init_rv < 0) {
// 输入错误的配置项提示
log_err(LD_CONFIG,"Reading config failed--see warnings above.");
return -1;
} else if (init_rv > 0) {
// We succeeded, and should exit anyway -- probably the user just said
// "--version" or something like that.
return 1;
}
3. options_init_from_torrc
应该是通过config_parse_commandline
函数获取从 command-line 中输入的配置项,存入global_cmdline_options
和global_cmdline_only_options
/** Configuration options set by command line. */
static config_line_t *global_cmdline_options = NULL;
/** Non-configuration options set by the command line */
static config_line_t *global_cmdline_only_options = NULL;
...
if (! have_parsed_cmdline) {
/* Or we could redo the list every time we pass this place.
* It does not really matter */
if (config_parse_commandline(argc, argv, 0, &global_cmdline_options,
&global_cmdline_only_options) < 0) {
goto err;
}
// 如果从command-line获取配置项成功,标记为1
have_parsed_cmdline = 1;
}
cmdline_only_options = global_cmdline_only_options;
接下来就是通过config_line_find
函数来查找cmdline_only_options
中的配置项,类似格式如下:
...
if (config_line_find(cmdline_only_options, "-h") ||
config_line_find(cmdline_only_options, "--help")) {
// 对command-line option的操作
print_usage();
return 1;
}
...
上述的命令都是列出相关信息的配置项,并不会启动 tor 的主函数
后面的代码是决定 tor 以何种方式运行
Tor 中以枚举的方式列出了其他执行模式:
enum {
CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD,
CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG,
CMD_KEYGEN,
CMD_KEY_EXPIRATION,
} command;
CMD_RUN_TOR
是 Tor 系统真正的执行命令,也是默认的执行模式。
command = CMD_RUN_TOR;
for (p_index = cmdline_only_options; p_index; p_index = p_index->next) {
if (!strcmp(p_index->key,"--keygen")) {
command = CMD_KEYGEN;
} else if (!strcmp(p_index->key, "--key-expiration")) {
command = CMD_KEY_EXPIRATION;
command_arg = p_index->value;
} else if (!strcmp(p_index->key,"--list-fingerprint")) {
command = CMD_LIST_FINGERPRINT;
} else if (!strcmp(p_index->key, "--hash-password")) {
command = CMD_HASH_PASSWORD;
command_arg = p_index->value;
} else if (!strcmp(p_index->key, "--dump-config")) {
command = CMD_DUMP_CONFIG;
command_arg = p_index->value;
} else if (!strcmp(p_index->key, "--verify-config")) {
command = CMD_VERIFY_CONFIG;
}
}
我们来分析下几个参数选项所对应的模式:
CMD_KEYGEN:--keygen
: 如果没有 master key,会为 relay 创建一个新的ed25519 master identity key
;如果有 master key,则创建临时的签名密钥和证书。Doc:https://trac.torproject.org/projects/tor/wiki/doc/TorRelaySecurity/OfflineKeysTor 默认在
~/.tor/keys
中生成相关的 key。可以通过--DataDirectory
指定 keys 的保存路径,注意运行 Tor 的 daemon 必须对该文件夹有读写权限。CMD_KEY_EXPIRATION: --key-expiration
:目前只有唯一参数sign
,运行“tor -key-expiration sign”将尝试查找您的签名密钥证书,并将在日志和 stdout 中以 ISO-8601 格式输出签名密钥证书的到期时间,格式为“signing-cert-expiry:2017-07-25 08:30:15 UTC”。CMD_LIST_FINGERPRINT: --list-fingerprint
:生成密钥,并输出 nickname 和 fingerprint。(我测的时候有点问题)CMD_HASH_PASSWORD: --hash-password
: 为 control port 生成一个 hashed password。CMD_DUMP_CONFIG: --dump-config
: 我的理解就是输出当前的配置项到控制台(stdout),输出配置有三个级别:short、non-builtin、full
CMD_VERIFY_CONFIG: --verfiy-config
: Verify the configuration file is valid.
接下来就是读取默认配置文件的配置参数,以及读入命令行输入的配置文件的配置参数(-f
参数代表命令行输入配置文件)
// 读取默认配置文件的配置参数
cf_defaults = load_torrc_from_disk(cmdline_only_options, 1);
// 读取命令行内指定的配置文件中的配置参数,通过"-f"指定
const config_line_t *f_line = config_line_find(cmdline_only_options, "-f");
const int read_torrc_from_stdin =
(f_line != NULL && strcmp(f_line->value, "-") == 0);
if (read_torrc_from_stdin) {
cf = load_torrc_from_stdin();
} else {
// 如果找不到,则寻找系统中存在的torrc
cf = load_torrc_from_disk(cmdline_only_options, 0);
}
接着是利用options_init_from_string
所有加载的命令行中的配置参数、默认配置参数或指定配置文件的参数初步初始化系统
// cf_defaults:默认配置文件里的配置项
// cf:-f指定的配置文件
// command:tor的运行模式
// command_arg:command命令后附带的参数
// errmsg:错误信息
retval = options_init_from_string(cf_defaults, cf, command, command_arg, &errmsg);
从这里我发现,tor 加载配置项的覆盖方式是:输入配置文件的配置会覆盖默认配置文件,而命令行配置则会覆盖输入配置文件的配置。
options_init_from_string
的具体初始化过程暂不深究
发表评论