diff options
author | Nick Mathewson <nickm@torproject.org> | 2013-09-13 12:36:55 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2013-09-13 12:36:55 -0400 |
commit | e35c972851686b5f6f2181e4eddb592f07fd7587 (patch) | |
tree | 359d389cf71396cc8d7a640fc18b89eb87134489 | |
parent | e0b2cd061bd62fc790d434b2da7ecc51ed100904 (diff) | |
parent | bf5e1e19f7c65845543001a18b8277c7a906814c (diff) | |
download | tor-e35c972851686b5f6f2181e4eddb592f07fd7587.tar.gz tor-e35c972851686b5f6f2181e4eddb592f07fd7587.zip |
Merge branch 'bug4647_squashed'
-rw-r--r-- | changes/bug4647 | 18 | ||||
-rw-r--r-- | doc/tor.1.txt | 2 | ||||
-rw-r--r-- | src/or/config.c | 220 | ||||
-rw-r--r-- | src/or/config.h | 10 | ||||
-rw-r--r-- | src/or/confparse.c | 19 | ||||
-rw-r--r-- | src/or/confparse.h | 4 | ||||
-rw-r--r-- | src/or/control.c | 2 | ||||
-rw-r--r-- | src/or/main.c | 63 | ||||
-rw-r--r-- | src/or/or.h | 4 | ||||
-rwxr-xr-x | src/test/test_cmdline_args.py | 242 |
10 files changed, 495 insertions, 89 deletions
diff --git a/changes/bug4647 b/changes/bug4647 new file mode 100644 index 0000000000..f756a7db1e --- /dev/null +++ b/changes/bug4647 @@ -0,0 +1,18 @@ + o Minor bugfixes: + + - Use a single command-line parser for parsing torrc options on the + command line and for finding special command-line options to avoid + inconsistent behavior for torrc option arguments that have the same + names as command-line options. Fixes bugs 4647 and 9578; bugfix on + 0.0.9pre5. + + - No longer allow 'tor --hash-password' with no arguments. Fixes bug + 9573; bugfix on 0.0.9pre5. + + o Minor features: + + - Support a --dump-config optoin to dump some or all of the configured + options. Mainly useful for debugging the command-line option parsing + code. + + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 737e7b4f8d..a1e1c08ba4 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -46,7 +46,7 @@ COMMAND-LINE OPTIONS configuration file, and by those on the command line. (Default: @CONFDIR@/torrc-defaults.) -**--hash-password**:: +**--hash-password** __PASSWORD__:: Generates a hashed password for control port access. **--list-fingerprint**:: diff --git a/src/or/config.c b/src/or/config.c index 17d6728851..9a8d1e77f5 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -550,6 +550,9 @@ static int parse_outbound_addresses(or_options_t *options, int validate_only, char **msg); static void config_maybe_load_geoip_files_(const or_options_t *options, const or_options_t *old_options); +static int options_validate_cb(void *old_options, void *options, + void *default_options, + int from_setconf, char **msg); /** Magic value for or_options_t. */ #define OR_OPTIONS_MAGIC 9090909 @@ -561,7 +564,7 @@ STATIC config_format_t options_format = { STRUCT_OFFSET(or_options_t, magic_), option_abbrevs_, option_vars_, - (validate_fn_t)options_validate, + options_validate_cb, NULL }; @@ -724,6 +727,7 @@ or_options_free(or_options_t *options) smartlist_free(options->NodeFamilySets); } tor_free(options->BridgePassword_AuthDigest_); + tor_free(options->command_arg); config_free(&options_format, options); } @@ -1790,40 +1794,64 @@ options_act(const or_options_t *old_options) return 0; } -/** Helper: Read a list of configuration options from the command line. - * If successful, put them in *<b>result</b> and return 0, and return - * -1 and leave *<b>result</b> alone. */ -static int -config_get_commandlines(int argc, char **argv, config_line_t **result) +static const struct { + const char *name; + int takes_argument; +} CMDLINE_ONLY_OPTIONS[] = { + { "-f", 1 }, + { "--defaults-torrc", 1 }, + { "--hash-password", 1 }, + { "--dump-config", 1 }, + { "--list-fingerprint", 0 }, + { "--verify-config", 0 }, + { "--ignore-missing-torrc", 0 }, + { "--quiet", 0 }, + { "--hush", 0 }, + { "--version", 0 }, + { "-h", 0 }, + { "--help", 0 }, + { "--list-torrc-options", 0 }, + { "--digests", 0 }, + { "--nt-service", 0 }, + { "-nt-service", 0 }, + { NULL, 0 }, +}; + +/** Helper: Read a list of configuration options from the command line. If + * successful, or if ignore_errors is set, put them in *<b>result</b>, put the + * commandline-only options in *<b>cmdline_result</b>, and return 0; + * otherwise, return -1 and leave *<b>result</b> and <b>cmdline_result</b> + * alone. */ +int +config_parse_commandline(int argc, char **argv, int ignore_errors, + config_line_t **result, + config_line_t **cmdline_result) { + config_line_t *param = NULL; + config_line_t *front = NULL; config_line_t **new = &front; - char *s; + + config_line_t *front_cmdline = NULL; + config_line_t **new_cmdline = &front_cmdline; + + char *s, *arg; int i = 1; while (i < argc) { unsigned command = CONFIG_LINE_NORMAL; int want_arg = 1; + int is_cmdline = 0; + int j; - if (!strcmp(argv[i],"-f") || - !strcmp(argv[i],"--defaults-torrc") || - !strcmp(argv[i],"--hash-password")) { - i += 2; /* command-line option with argument. ignore them. */ - continue; - } else if (!strcmp(argv[i],"--list-fingerprint") || - !strcmp(argv[i],"--verify-config") || - !strcmp(argv[i],"--ignore-missing-torrc") || - !strcmp(argv[i],"--quiet") || - !strcmp(argv[i],"--hush")) { - i += 1; /* command-line option. ignore it. */ - continue; - } else if (!strcmp(argv[i],"--nt-service") || - !strcmp(argv[i],"-nt-service")) { - i += 1; - continue; + for (j = 0; CMDLINE_ONLY_OPTIONS[j].name != NULL; ++j) { + if (!strcmp(argv[i], CMDLINE_ONLY_OPTIONS[j].name)) { + is_cmdline = 1; + want_arg = CMDLINE_ONLY_OPTIONS[j].takes_argument; + break; + } } - *new = tor_malloc_zero(sizeof(config_line_t)); s = argv[i]; /* Each keyword may be prefixed with one or two dashes. */ @@ -1843,22 +1871,38 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) } if (want_arg && i == argc-1) { - log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", - argv[i]); - config_free_lines(front); - return -1; + if (ignore_errors) { + arg = strdup(""); + } else { + log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", + argv[i]); + config_free_lines(front); + config_free_lines(front_cmdline); + return -1; + } + } else { + arg = want_arg ? tor_strdup(argv[i+1]) : strdup(""); } - (*new)->key = tor_strdup(config_expand_abbrev(&options_format, s, 1, 1)); - (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup(""); - (*new)->command = command; - (*new)->next = NULL; + param = tor_malloc_zero(sizeof(config_line_t)); + param->key = is_cmdline ? tor_strdup(argv[i]) : tor_strdup(s); + param->value = arg; + param->command = command; + param->next = NULL; log_debug(LD_CONFIG, "command line: parsed keyword '%s', value '%s'", - (*new)->key, (*new)->value); + param->key, param->value); + + if (is_cmdline) { + *new_cmdline = param; + new_cmdline = &((*new_cmdline)->next); + } else { + *new = param; + new = &((*new)->next); + } - new = &((*new)->next); i += want_arg ? 2 : 1; } + *cmdline_result = front_cmdline; *result = front; return 0; } @@ -2225,10 +2269,29 @@ options_init(or_options_t *options) * include options that are the same as Tor's defaults. */ char * -options_dump(const or_options_t *options, int minimal) +options_dump(const or_options_t *options, int how_to_dump) { - return config_dump(&options_format, global_default_options, - options, minimal, 0); + const or_options_t *use_defaults; + int minimal; + switch (how_to_dump) { + case OPTIONS_DUMP_MINIMAL: + use_defaults = global_default_options; + minimal = 1; + break; + case OPTIONS_DUMP_DEFAULTS: + use_defaults = NULL; + minimal = 1; + break; + case OPTIONS_DUMP_ALL: + use_defaults = NULL; + minimal = 0; + break; + default: + log_warn(LD_BUG, "Bogus value for how_to_dump==%d", how_to_dump); + return NULL; + } + + return config_dump(&options_format, use_defaults, options, minimal, 0); } /** Return 0 if every element of sl is a string holding a decimal @@ -2345,6 +2408,14 @@ compute_publishserverdescriptor(or_options_t *options) * */ #define RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT (10) +static int +options_validate_cb(void *old_options, void *options, void *default_options, + int from_setconf, char **msg) +{ + return options_validate(old_options, options, default_options, + from_setconf, msg); +} + /** Return 0 if every setting in <b>options</b> is reasonable, is a * permissible transition from <b>old_options</b>, and none of the * testing-only settings differ from <b>default_options</b> unless in @@ -3703,26 +3774,26 @@ check_nickname_list(char **lst, const char *name, char **msg) * filename if it doesn't exist. */ static char * -find_torrc_filename(int argc, char **argv, +find_torrc_filename(config_line_t *cmd_arg, int defaults_file, int *using_default_fname, int *ignore_missing_torrc) { char *fname=NULL; - int i; + config_line_t *p_index; const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f"; const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc"; if (defaults_file) *ignore_missing_torrc = 1; - for (i = 1; i < argc; ++i) { - if (i < argc-1 && !strcmp(argv[i],fname_opt)) { + for (p_index = cmd_arg; p_index; p_index = p_index->next) { + if (!strcmp(p_index->key, fname_opt)) { if (fname) { log_warn(LD_CONFIG, "Duplicate %s options on command line.", fname_opt); tor_free(fname); } - fname = expand_filename(argv[i+1]); + fname = expand_filename(p_index->value); { char *absfname; @@ -3732,8 +3803,7 @@ find_torrc_filename(int argc, char **argv, } *using_default_fname = 0; - ++i; - } else if (ignore_opt && !strcmp(argv[i],ignore_opt)) { + } else if (ignore_opt && !strcmp(p_index->key,ignore_opt)) { *ignore_missing_torrc = 1; } } @@ -3770,7 +3840,7 @@ find_torrc_filename(int argc, char **argv, * Return the contents of the file on success, and NULL on failure. */ static char * -load_torrc_from_disk(int argc, char **argv, int defaults_file) +load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file) { char *fname=NULL; char *cf = NULL; @@ -3778,7 +3848,7 @@ load_torrc_from_disk(int argc, char **argv, int defaults_file) int ignore_missing_torrc = 0; char **fname_var = defaults_file ? &torrc_defaults_fname : &torrc_fname; - fname = find_torrc_filename(argc, argv, defaults_file, + fname = find_torrc_filename(cmd_arg, defaults_file, &using_default_torrc, &ignore_missing_torrc); tor_assert(fname); log_debug(LD_CONFIG, "Opening config file \"%s\"", fname); @@ -3820,12 +3890,14 @@ int options_init_from_torrc(int argc, char **argv) { char *cf=NULL, *cf_defaults=NULL; - int i, command; + int command; int retval = -1; static char **backup_argv; static int backup_argc; char *command_arg = NULL; char *errmsg=NULL; + config_line_t *cmdline_only_options = NULL; + config_line_t *p_index = NULL; if (argv) { /* first time we're called. save command line args */ backup_argv = argv; @@ -3834,45 +3906,50 @@ options_init_from_torrc(int argc, char **argv) argv = backup_argv; argc = backup_argc; } - if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) { + + /* Go through command-line variables */ + if (!global_cmdline_options) { + /* 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, + &cmdline_only_options) < 0) { + goto err; + } + } + + if (config_line_find(cmdline_only_options, "-h") || + config_line_find(cmdline_only_options, "--help")) { print_usage(); exit(0); } - if (argc > 1 && !strcmp(argv[1], "--list-torrc-options")) { + if (config_line_find(cmdline_only_options, "--list-torrc-options")) { /* For documenting validating whether we've documented everything. */ list_torrc_options(); exit(0); } - if (argc > 1 && (!strcmp(argv[1],"--version"))) { + if (config_line_find(cmdline_only_options, "--version")) { printf("Tor version %s.\n",get_version()); exit(0); } - if (argc > 1 && (!strcmp(argv[1],"--digests"))) { + if (config_line_find(cmdline_only_options, "--digests")) { printf("Tor version %s.\n",get_version()); printf("%s", libor_get_digests()); printf("%s", tor_get_digests()); exit(0); } - /* Go through command-line variables */ - if (!global_cmdline_options) { - /* Or we could redo the list every time we pass this place. - * It does not really matter */ - if (config_get_commandlines(argc, argv, &global_cmdline_options) < 0) { - goto err; - } - } - command = CMD_RUN_TOR; - for (i = 1; i < argc; ++i) { - if (!strcmp(argv[i],"--list-fingerprint")) { + for (p_index = cmdline_only_options; p_index; p_index = p_index->next) { + if (!strcmp(p_index->key,"--list-fingerprint")) { command = CMD_LIST_FINGERPRINT; - } else if (!strcmp(argv[i],"--hash-password")) { + } else if (!strcmp(p_index->key, "--hash-password")) { command = CMD_HASH_PASSWORD; - command_arg = tor_strdup( (i < argc-1) ? argv[i+1] : ""); - ++i; - } else if (!strcmp(argv[i],"--verify-config")) { + 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; } } @@ -3881,8 +3958,8 @@ options_init_from_torrc(int argc, char **argv) cf_defaults = tor_strdup(""); cf = tor_strdup(""); } else { - cf_defaults = load_torrc_from_disk(argc, argv, 1); - cf = load_torrc_from_disk(argc, argv, 0); + cf_defaults = load_torrc_from_disk(cmdline_only_options, 1); + cf = load_torrc_from_disk(cmdline_only_options, 0); if (!cf) goto err; } @@ -3894,6 +3971,7 @@ options_init_from_torrc(int argc, char **argv) tor_free(cf); tor_free(cf_defaults); + config_free_lines(cmdline_only_options); if (errmsg) { log_warn(LD_CONFIG,"%s", errmsg); tor_free(errmsg); @@ -3928,7 +4006,7 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->magic_ = OR_OPTIONS_MAGIC; options_init(newoptions); newoptions->command = command; - newoptions->command_arg = command_arg; + newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; for (i = 0; i < 2; ++i) { const char *body = i==0 ? cf_defaults : cf; @@ -3992,7 +4070,7 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->magic_ = OR_OPTIONS_MAGIC; options_init(newoptions); newoptions->command = command; - newoptions->command_arg = command_arg; + newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; /* Assign all options a second time. */ for (i = 0; i < 2; ++i) { @@ -6096,7 +6174,7 @@ write_configuration_file(const char *fname, const or_options_t *options) return -1; } - if (!(new_conf = options_dump(options, 1))) { + if (!(new_conf = options_dump(options, OPTIONS_DUMP_MINIMAL))) { log_warn(LD_BUG, "Couldn't get configuration string"); goto err; } diff --git a/src/or/config.h b/src/or/config.h index eb16e74610..8ee2a45725 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -32,7 +32,11 @@ int resolve_my_address(int warn_severity, const or_options_t *options, const char **method_out, char **hostname_out); int is_local_addr(const tor_addr_t *addr); void options_init(or_options_t *options); -char *options_dump(const or_options_t *options, int minimal); + +#define OPTIONS_DUMP_MINIMAL 1 +#define OPTIONS_DUMP_DEFAULTS 2 +#define OPTIONS_DUMP_ALL 3 +char *options_dump(const or_options_t *options, int how_to_dump); int options_init_from_torrc(int argc, char **argv); setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf, int command, const char *command_arg, char **msg); @@ -96,6 +100,10 @@ int init_cookie_authentication(const char *fname, const char *header, or_options_t *options_new(void); +int config_parse_commandline(int argc, char **argv, int ignore_errors, + config_line_t **result, + config_line_t **cmdline_result); + void config_register_addressmaps(const or_options_t *options); /* XXXX024 move to connection_edge.h */ int addressmap_register_auto(const char *from, const char *to, diff --git a/src/or/confparse.c b/src/or/confparse.c index 41055791ef..f2ada4f420 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -79,6 +79,21 @@ config_line_append(config_line_t **lst, (*lst) = newline; } +/** Return the line in <b>lines</b> whose key is exactly <b>key</b>, or NULL + * if no such key exists. For handling commandline-only options only; other + * options should be looked up in the appropriate data structure. */ +const config_line_t * +config_line_find(const config_line_t *lines, + const char *key) +{ + const config_line_t *cl; + for (cl = lines; cl; cl = cl->next) { + if (!strcmp(cl->key, key)) + return cl; + } + return NULL; +} + /** Helper: parse the config string and strdup into key/value * strings. Set *result to the list, or NULL if parsing the string * failed. Return 0 on success, -1 on failure. Warn and ignore any @@ -1059,8 +1074,8 @@ config_dump(const config_format_t *fmt, const void *default_options, /* XXX use a 1 here so we don't add a new log line while dumping */ if (default_options == NULL) { - if (fmt->validate_fn(NULL, defaults_tmp, 1, &msg) < 0) { - log_err(LD_BUG, "Failed to validate default config."); + if (fmt->validate_fn(NULL, defaults_tmp, defaults_tmp, 1, &msg) < 0) { + log_err(LD_BUG, "Failed to validate default config: %s", msg); tor_free(msg); tor_assert(0); } diff --git a/src/or/confparse.h b/src/or/confparse.h index 924ee0d945..2cd6c49a2a 100644 --- a/src/or/confparse.h +++ b/src/or/confparse.h @@ -71,7 +71,7 @@ typedef struct config_var_description_t { /** Type of a callback to validate whether a given configuration is * well-formed and consistent. See options_trial_assign() for documentation * of arguments. */ -typedef int (*validate_fn_t)(void*,void*,int,char**); +typedef int (*validate_fn_t)(void*,void*,void*,int,char**); /** Information on the keys, value types, key-to-struct-member mappings, * variable descriptions, validation functions, and abbreviations for a @@ -103,6 +103,8 @@ void *config_new(const config_format_t *fmt); void config_line_append(config_line_t **lst, const char *key, const char *val); config_line_t *config_lines_dup(const config_line_t *inp); +const config_line_t *config_line_find(const config_line_t *lines, + const char *key); void config_free(const config_format_t *fmt, void *options); int config_lines_eq(config_line_t *a, config_line_t *b); int config_count_key(const config_line_t *a, const char *key); diff --git a/src/or/control.c b/src/or/control.c index 7034605c20..e97c18d892 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1406,7 +1406,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, } else if (!strcmp(question, "config-defaults-file")) { *answer = tor_strdup(get_torrc_fname(1)); } else if (!strcmp(question, "config-text")) { - *answer = options_dump(get_options(), 1); + *answer = options_dump(get_options(), OPTIONS_DUMP_MINIMAL); } else if (!strcmp(question, "info/names")) { *answer = list_getinfo_options(); } else if (!strcmp(question, "dormant")) { diff --git a/src/or/main.c b/src/or/main.c index fa112437fe..ceee29ce66 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -21,6 +21,7 @@ #include "circuituse.h" #include "command.h" #include "config.h" +#include "confparse.h" #include "connection.h" #include "connection_edge.h" #include "connection_or.h" @@ -2325,7 +2326,7 @@ int tor_init(int argc, char *argv[]) { char buf[256]; - int i, quiet = 0; + int quiet = 0; time_of_process_start = time(NULL); init_connection_lists(); /* Have the log set up with our application name. */ @@ -2338,17 +2339,28 @@ tor_init(int argc, char *argv[]) addressmap_init(); /* Init the client dns cache. Do it always, since it's * cheap. */ + { /* We search for the "quiet" option first, since it decides whether we * will log anything at all to the command line. */ - for (i=1;i<argc;++i) { - if (!strcmp(argv[i], "--hush")) - quiet = 1; - if (!strcmp(argv[i], "--quiet")) - quiet = 2; - /* --version implies --quiet */ - if (!strcmp(argv[i], "--version")) - quiet = 2; + 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) { + if (!strcmp(cl->key, "--hush")) + quiet = 1; + if (!strcmp(cl->key, "--quiet") || + !strcmp(cl->key, "--dump-config")) + quiet = 2; + /* --version, --digests, and --help imply --husth */ + if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") || + !strcmp(cl->key, "--list-torrc-options") || + !strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) + quiet = 1; + } + config_free_lines(opts); + config_free_lines(cmdline_opts); } + /* give it somewhere to log to initially */ switch (quiet) { case 2: @@ -2592,7 +2604,7 @@ do_list_fingerprint(void) const char *nickname = get_options()->Nickname; if (!server_mode(get_options())) { log_err(LD_GENERAL, - "Clients don't have long-term identity keys. Exiting.\n"); + "Clients don't have long-term identity keys. Exiting."); return -1; } tor_assert(nickname); @@ -2630,6 +2642,34 @@ do_hash_password(void) printf("16:%s\n",output); } +/** Entry point for configuration dumping: write the configuration to + * stdout. */ +static int +do_dump_config(void) +{ + const or_options_t *options = get_options(); + const char *arg = options->command_arg; + int how; + char *opts; + if (!strcmp(arg, "short")) { + how = OPTIONS_DUMP_MINIMAL; + } else if (!strcmp(arg, "non-builtin")) { + how = OPTIONS_DUMP_DEFAULTS; + } else if (!strcmp(arg, "full")) { + how = OPTIONS_DUMP_ALL; + } else { + printf("%s is not a recognized argument to --dump-config. " + "Please select 'short', 'non-builtin', or 'full'", arg); + return -1; + } + + opts = options_dump(options, how); + printf("%s", opts); + tor_free(opts); + + return 0; +} + #if defined (WINCE) int find_flashcard_path(PWCHAR path, size_t size) @@ -2844,6 +2884,9 @@ tor_main(int argc, char *argv[]) printf("Configuration was valid\n"); result = 0; break; + case CMD_DUMP_CONFIG: + result = do_dump_config(); + break; case CMD_RUN_UNITTESTS: /* only set by test.c */ default: log_warn(LD_BUG,"Illegal command number %d: internal error.", diff --git a/src/or/or.h b/src/or/or.h index 922ae4cb91..bd038f783c 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3398,9 +3398,9 @@ typedef struct { /** What should the tor process actually do? */ enum { CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD, - CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS + CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG } command; - const char *command_arg; /**< Argument for command-line option. */ + char *command_arg; /**< Argument for command-line option. */ config_line_t *Logs; /**< New-style list of configuration lines * for logs */ diff --git a/src/test/test_cmdline_args.py b/src/test/test_cmdline_args.py new file mode 100755 index 0000000000..5767a82d9b --- /dev/null +++ b/src/test/test_cmdline_args.py @@ -0,0 +1,242 @@ +#!/usr/bin/python + +import binascii +import hashlib +import os +import re +import shutil +import subprocess +import tempfile +import unittest + +TOR = "./src/or/tor-cov" +TOPDIR = "." + +class UnexpectedSuccess(Exception): + pass + +class UnexpectedFailure(Exception): + pass + +def contents(fn): + f = open(fn) + try: + return f.read() + finally: + f.close() + +def run_tor(args, failure=False): + p = subprocess.Popen([TOR] + args, stdout=subprocess.PIPE) + output, _ = p.communicate() + result = p.poll() + if result and not failure: + raise UnexpectedFailure() + elif not result and failure: + raise UnexpectedSuccess() + return output + +def spaceify_fp(fp): + for i in xrange(0, len(fp), 4): + yield fp[i:i+4] + +def lines(s): + out = s.split("\n") + if out and out[-1] == '': + del out[-1] + return out + +def strip_log_junk(line): + m = re.match(r'([^\[]+\[[a-z]*\] *)(.*)', line) + if not m: + return ""+line + return m.group(2).strip() + +class CmdlineTests(unittest.TestCase): + + def test_version(self): + out = run_tor(["--version"]) + self.failUnless(out.startswith("Tor version ")) + self.assertEquals(len(lines(out)), 1) + + def test_quiet(self): + out = run_tor(["--quiet", "--quumblebluffin", "1"], failure=True) + self.assertEquals(out, "") + + def test_help(self): + out = run_tor(["--help"], failure=False) + out2 = run_tor(["-h"], failure=False) + self.assert_(out.startswith("Copyright (c) 2001")) + self.assert_(out.endswith( + "tor -f <torrc> [args]\n" + "See man page for options, or https://www.torproject.org/ for documentation.\n")) + self.assert_(out == out2) + + def test_hush(self): + torrc = tempfile.NamedTemporaryFile(delete=False) + torrc.close() + try: + out = run_tor(["--hush", "-f", torrc.name, + "--quumblebluffin", "1"], failure=True) + finally: + os.unlink(torrc.name) + self.assertEquals(len(lines(out)), 2) + ln = [ strip_log_junk(l) for l in lines(out) ] + self.assertEquals(ln[0], "Failed to parse/validate config: Unknown option 'quumblebluffin'. Failing.") + self.assertEquals(ln[1], "Reading config failed--see warnings above.") + + def test_missing_argument(self): + out = run_tor(["--hush", "--hash-password"], failure=True) + self.assertEquals(len(lines(out)), 2) + ln = [ strip_log_junk(l) for l in lines(out) ] + self.assertEquals(ln[0], "Command-line option '--hash-password' with no value. Failing.") + + def test_hash_password(self): + out = run_tor(["--hash-password", "woodwose"]) + result = lines(out)[-1] + self.assertEquals(result[:3], "16:") + self.assertEquals(len(result), 61) + r = binascii.a2b_hex(result[3:]) + self.assertEquals(len(r), 29) + + salt, how, hashed = r[:8], r[8], r[9:] + self.assertEquals(len(hashed), 20) + + count = (16 + (ord(how) & 15)) << ((ord(how) >> 4) + 6) + stuff = salt + "woodwose" + repetitions = count // len(stuff) + 1 + inp = stuff * repetitions + inp = inp[:count] + + self.assertEquals(hashlib.sha1(inp).digest(), hashed) + + def test_digests(self): + main_c = os.path.join(TOPDIR, "src", "or", "main.c") + + if os.stat(TOR).st_mtime < os.stat(main_c).st_mtime: + self.skipTest(TOR+" not up to date") + out = run_tor(["--digests"]) + main_line = [ l for l in lines(out) if l.endswith("/main.c") ] + digest, name = main_line[0].split() + actual = hashlib.sha1(open(main_c).read()).hexdigest() + self.assertEquals(digest, actual) + + def test_dump_options(self): + default_torrc = tempfile.NamedTemporaryFile(delete=False) + torrc = tempfile.NamedTemporaryFile(delete=False) + torrc.write("SocksPort 9999") + torrc.close() + default_torrc.write("SafeLogging 0") + default_torrc.close() + out_sh = out_nb = out_fl = None + opts = [ "-f", torrc.name, + "--defaults-torrc", default_torrc.name ] + try: + out_sh = run_tor(["--dump-config", "short"]+opts) + out_nb = run_tor(["--dump-config", "non-builtin"]+opts) + out_fl = run_tor(["--dump-config", "full"]+opts) + out_nr = run_tor(["--dump-config", "bliznert"]+opts, + failure=True) + + out_verif = run_tor(["--verify-config"]+opts) + finally: + os.unlink(torrc.name) + os.unlink(default_torrc.name) + + self.assertEquals(len(lines(out_sh)), 2) + self.assert_(lines(out_sh)[0].startswith("DataDirectory ")) + self.assertEquals(lines(out_sh)[1:], + [ "SocksPort 9999" ]) + + self.assertEquals(len(lines(out_nb)), 2) + self.assertEquals(lines(out_nb), + [ "SafeLogging 0", + "SocksPort 9999" ]) + + out_fl = lines(out_fl) + self.assert_(len(out_fl) > 100) + self.assertIn("SocksPort 9999", out_fl) + self.assertIn("SafeLogging 0", out_fl) + self.assertIn("ClientOnly 0", out_fl) + + self.assert_(out_verif.endswith("Configuration was valid\n")) + + def test_list_fingerprint(self): + tmpdir = tempfile.mkdtemp(prefix='ttca_') + torrc = tempfile.NamedTemporaryFile(delete=False) + torrc.write("ORPort 9999\n") + torrc.write("DataDirectory %s\n"%tmpdir) + torrc.write("Nickname tippi") + torrc.close() + opts = ["-f", torrc.name] + try: + out = run_tor(["--list-fingerprint"]+opts) + fp = contents(os.path.join(tmpdir, "fingerprint")) + finally: + os.unlink(torrc.name) + shutil.rmtree(tmpdir) + + out = lines(out) + lastlog = strip_log_junk(out[-2]) + lastline = out[-1] + fp = fp.strip() + nn_fp = fp.split()[0] + space_fp = " ".join(spaceify_fp(fp.split()[1])) + self.assertEquals(lastlog, + "Your Tor server's identity key fingerprint is '%s'"%fp) + self.assertEquals(lastline, "tippi %s"%space_fp) + self.assertEquals(nn_fp, "tippi") + + def test_list_options(self): + out = lines(run_tor(["--list-torrc-options"])) + self.assert_(len(out)>100) + self.assert_(out[0] <= 'AccountingMax') + self.assert_("UseBridges" in out) + self.assert_("SocksPort" in out) + + def test_cmdline_args(self): + default_torrc = tempfile.NamedTemporaryFile(delete=False) + torrc = tempfile.NamedTemporaryFile(delete=False) + torrc.write("SocksPort 9999\n") + torrc.write("SocksPort 9998\n") + torrc.write("ORPort 9000\n") + torrc.write("ORPort 9001\n") + torrc.write("Nickname eleventeen\n") + torrc.write("ControlPort 9500\n") + torrc.close() + default_torrc.write("") + default_torrc.close() + out_sh = out_nb = out_fl = None + opts = [ "-f", torrc.name, + "--defaults-torrc", default_torrc.name, + "--dump-config", "short" ] + try: + out_1 = run_tor(opts) + out_2 = run_tor(opts+["+ORPort", "9003", + "SocksPort", "9090", + "/ControlPort", + "/TransPort", + "+ExtORPort", "9005"]) + finally: + os.unlink(torrc.name) + os.unlink(default_torrc.name) + + out_1 = [ l for l in lines(out_1) if not l.startswith("DataDir") ] + out_2 = [ l for l in lines(out_2) if not l.startswith("DataDir") ] + + self.assertEquals(out_1, + ["ControlPort 9500", + "Nickname eleventeen", + "ORPort 9000", + "ORPort 9001", + "SocksPort 9999", + "SocksPort 9998"]) + self.assertEquals(out_2, + ["ExtORPort 9005", + "Nickname eleventeen", + "ORPort 9000", + "ORPort 9001", + "ORPort 9003", + "SocksPort 9090"]) + +if __name__ == '__main__': + unittest.main() |