diff options
Diffstat (limited to 'src/or/config.c')
-rw-r--r-- | src/or/config.c | 544 |
1 files changed, 318 insertions, 226 deletions
diff --git a/src/or/config.c b/src/or/config.c index 3ab114ab51..e553dc9695 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -155,19 +155,16 @@ static config_var_t config_vars[] = { /** Largest allowed config line */ #define CONFIG_LINE_T_MAXLEN 4096 -static struct config_line_t *config_get_commandlines(int argc, char **argv); -static int config_get_lines(FILE *f, struct config_line_t **result); -static void config_free_lines(struct config_line_t *front); -static int config_assign_line(or_options_t *options, struct config_line_t *c, - int reset); -static int config_assign(or_options_t *options, struct config_line_t *list, - int reset); +static void option_reset(or_options_t *options, config_var_t *var); +static void options_free(or_options_t *options); +static or_options_t *options_dup(or_options_t *old); +static int options_validate(or_options_t *options); +static int options_transition_allowed(or_options_t *old, or_options_t *new); +static int check_nickname_list(const char *lst, const char *name); + static int parse_dir_server_line(const char *line); static int parse_redirect_line(or_options_t *options, struct config_line_t *line); -static const char *expand_abbrev(const char *option, int commandline_only); -static config_var_t *config_find_option(const char *key); -static void reset_option(or_options_t *options, config_var_t *var); static int parse_log_severity_range(const char *range, int *min_out, int *max_out); static int convert_log_option(or_options_t *options, @@ -178,6 +175,25 @@ static int add_single_log_option(or_options_t *options, int minSeverity, const char *type, const char *fname); static int normalize_log_options(or_options_t *options); +/* + * Functions to read and write the global options pointer. + */ + +/** Command-line and config-file options. */ +static or_options_t *global_options=NULL; + +or_options_t *get_options(void) { + tor_assert(global_options); + return global_options; +} +void set_options(or_options_t *new) { + global_options = new; +} + +/* + * Functions to parse config options + */ + /** If <b>option</b> is an official abbreviation for a longer option, * return the longer option. Otherwise return <b>option</b>. * If <b>command_line</b> is set, apply all abbreviations. Otherwise, only @@ -249,34 +265,34 @@ config_line_prepend(struct config_line_t *front, return newline; } -/** Helper: parse the config file and strdup into key/value - * strings. Set *result to the list, or NULL if parsing the file +/** 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 * misformatted lines. */ -static int -config_get_lines(FILE *f, struct config_line_t **result) +int +config_get_lines(char *string, struct config_line_t **result) { - struct config_line_t *front = NULL; - char line[CONFIG_LINE_T_MAXLEN]; - int r; - char *key, *value; + struct config_line_t *list = NULL; + char *k, *v; - while ((r = parse_line_from_file(line, sizeof(line), f, &key, &value)) > 0) { - front = config_line_prepend(front, key, value); - } + do { + string = parse_line_from_str(string, &k, &v); + if (!string) { + config_free_lines(list); + return -1; + } + if (k && v) + list = config_line_prepend(list, k, v); + } while (*string); - if (r < 0) { - *result = NULL; - return -1; - } - *result = front; + *result = list; return 0; } /** * Free all the configuration lines on the linked list <b>front</b>. */ -static void +void config_free_lines(struct config_line_t *front) { struct config_line_t *tmp; @@ -341,7 +357,7 @@ config_assign_line(or_options_t *options, struct config_line_t *c, int reset) } if (reset && !strlen(c->value)) { - reset_option(options, var); + option_reset(options, var); return 0; } @@ -351,9 +367,9 @@ config_assign_line(or_options_t *options, struct config_line_t *c, int reset) case CONFIG_TYPE_UINT: i = tor_parse_long(c->value, 10, 0, INT_MAX, &ok, NULL); if (!ok) { - log(LOG_WARN, "Int keyword '%s %s' is malformed or out of bounds. Skipping.", + log(LOG_WARN, "Int keyword '%s %s' is malformed or out of bounds.", c->key,c->value); - return 0; + return -1; } *(int *)lvalue = i; break; @@ -361,8 +377,8 @@ config_assign_line(or_options_t *options, struct config_line_t *c, int reset) case CONFIG_TYPE_BOOL: i = tor_parse_long(c->value, 10, 0, 1, &ok, NULL); if (!ok) { - log(LOG_WARN, "Boolean keyword '%s' expects 0 or 1. Skipping.", c->key); - return 0; + log(LOG_WARN, "Boolean keyword '%s' expects 0 or 1.", c->key); + return -1; } *(int *)lvalue = i; break; @@ -384,7 +400,6 @@ config_assign_line(or_options_t *options, struct config_line_t *c, int reset) SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); break; - case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST_S: /* Note: this reverses the order that the lines appear in. That's @@ -404,7 +419,6 @@ config_assign_line(or_options_t *options, struct config_line_t *c, int reset) tor_assert(0); break; } - return 0; } @@ -418,7 +432,7 @@ config_reset_line(or_options_t *options, const char *key) if (!var) return; /* give error on next pass. */ - reset_option(options, var); + option_reset(options, var); } /** Return a canonicalized list of the options assigned for key. @@ -465,6 +479,8 @@ config_get_assigned_option(or_options_t *options, const char *key) result->value = tor_strdup(value ? (char*)value : ""); break; case CONFIG_TYPE_UINT: + /* XXX This means every or_options_t uint or bool element + * needs to be an int. Not, say, a uint16_t or char. */ tor_snprintf(buf, sizeof(buf), "%d", *(int*)value); result->value = tor_strdup(buf); break; @@ -514,7 +530,7 @@ config_assign(or_options_t *options, struct config_line_t *list, int reset) } } - /* pass 2: if we're reading from a resetting souurce, clear all mentioned + /* pass 2: if we're reading from a resetting source, clear all mentioned * linelists. */ if (reset) { for (p = list; p; p = p->next) @@ -530,6 +546,89 @@ config_assign(or_options_t *options, struct config_line_t *list, int reset) return 0; } +/** Try assigning <b>list</b> to <b>options</b>. You do this by duping + * options, assigning list to the new one, then validating it. If it's + * ok, then through out the old one and stick with the new one. Else, + * revert to old and return failure. + */ +int +config_trial_assign(or_options_t **options, struct config_line_t *list, int reset) +{ + or_options_t *trial_options = options_dup(*options); + + if (config_assign(trial_options, list, reset) < 0) { + options_free(trial_options); + return -1; + } + + if (options_validate(trial_options) < 0) { + options_free(trial_options); + return -1; + } + + if (options_transition_allowed(*options, trial_options) < 0) { + options_free(trial_options); + return -1; + } + + /* XXX now act on options */ + + options_free(*options); + *options = trial_options; + return 0; +} + +/** Replace the option indexed by <b>var</b> in <b>options</b> with its + * default value. */ +static void +option_reset(or_options_t *options, config_var_t *var) +{ + struct config_line_t *c; + void *lvalue; + + lvalue = ((char*)options) + var->var_offset; + switch (var->type) { + case CONFIG_TYPE_STRING: + tor_free(*(char**)lvalue); + break; + case CONFIG_TYPE_DOUBLE: + *(double*)lvalue = 0.0; + break; + case CONFIG_TYPE_UINT: + case CONFIG_TYPE_BOOL: + *(int*)lvalue = 0; + break; + case CONFIG_TYPE_CSV: + if (*(smartlist_t**)lvalue) { + SMARTLIST_FOREACH(*(smartlist_t **)lvalue, char *, cp, tor_free(cp)); + smartlist_free(*(smartlist_t **)lvalue); + *(smartlist_t **)lvalue = NULL; + } + break; + case CONFIG_TYPE_LINELIST: + case CONFIG_TYPE_LINELIST_S: + config_free_lines(*(struct config_line_t **)lvalue); + *(struct config_line_t **)lvalue = NULL; + break; + case CONFIG_TYPE_LINELIST_V: + /* handled by linelist_s. */ + break; + case CONFIG_TYPE_OBSOLETE: + break; + } + if (var->initvalue) { + c = tor_malloc(sizeof(struct config_line_t)); + c->key = tor_strdup(var->name); + c->value = tor_strdup(var->initvalue); + config_assign_line(options,c,0); + config_free_lines(c); + } +} + + + + + static void add_default_trusted_dirservers(void) { @@ -657,7 +756,7 @@ get_default_nickname(void) /** Release storage held by <b>options</b> */ static void -free_options(or_options_t *options) +options_free(or_options_t *options) { int i; void *lvalue; @@ -699,149 +798,47 @@ free_options(or_options_t *options) } } -/** Replace the option indexed by <b>var</b> in <b>options</b> with its - * default value. */ -static void -reset_option(or_options_t *options, config_var_t *var) +/** Copy storage held by <b>old</b> into a new or_options_t and return it. */ +static or_options_t * +options_dup(or_options_t *old) { - struct config_line_t *c; - void *lvalue; + or_options_t *new; + int i; + struct config_line_t *line; - lvalue = ((char*)options) + var->var_offset; - switch (var->type) { - case CONFIG_TYPE_STRING: - tor_free(*(char**)lvalue); - break; - case CONFIG_TYPE_DOUBLE: - *(double*)lvalue = 0.0; - break; - case CONFIG_TYPE_UINT: - case CONFIG_TYPE_BOOL: - *(int*)lvalue = 0; - break; - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t **)lvalue, char *, cp, tor_free(cp)); - smartlist_free(*(smartlist_t **)lvalue); - *(smartlist_t **)lvalue = NULL; + new = tor_malloc_zero(sizeof(or_options_t)); + for (i=0; config_vars[i].name; ++i) { + line = config_get_assigned_option(old, config_vars[i].name); + if (line) { + if (config_assign(new, line, 0) < 0) { + log_fn(LOG_WARN,"Bug: config_get_assigned_option() generated " + "something we couldn't config_assign()."); + tor_assert(0); } - break; - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_S: - config_free_lines(*(struct config_line_t **)lvalue); - *(struct config_line_t **)lvalue = NULL; - break; - case CONFIG_TYPE_LINELIST_V: - /* handled by linelist_s. */ - break; - case CONFIG_TYPE_OBSOLETE: - break; - } - if (var->initvalue) { - c = tor_malloc(sizeof(struct config_line_t)); - c->key = tor_strdup(var->name); - c->value = tor_strdup(var->initvalue); - config_assign_line(options,c,0); - config_free_lines(c); + } + config_free_lines(line); } + return new; } /** Set <b>options</b> to hold reasonable defaults for most options. * Each option defaults to zero. */ -static void -init_options(or_options_t *options) +void +options_init(or_options_t *options) { int i; config_var_t *var; - memset(options,0,sizeof(or_options_t)); for (i=0; config_vars[i].name; ++i) { var = &config_vars[i]; if(!var->initvalue) continue; /* defaults to NULL or 0 */ - reset_option(options, var); + option_reset(options, var); } } -#ifdef MS_WINDOWS -static char *get_windows_conf_root(void) -{ - static int is_set = 0; - static char path[MAX_PATH+1]; - - LPITEMIDLIST idl; - IMalloc *m; - HRESULT result; - - if (is_set) - return path; - - /* Find X:\documents and settings\username\applicatation data\ . - * We would use SHGetSpecialFolder path, but that wasn't added until IE4. - */ - if (!SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, - &idl))) { - GetCurrentDirectory(MAX_PATH, path); - is_set = 1; - log_fn(LOG_WARN, "I couldn't find your application data folder: are you running an ancient version of Windows 95? Defaulting to '%s'", path); - return path; - } - /* Convert the path from an "ID List" (whatever that is!) to a path. */ - result = SHGetPathFromIDList(idl, path); - /* Now we need to free the */ - SHGetMalloc(&m); - if (m) { - m->lpVtbl->Free(m, idl); - m->lpVtbl->Release(m); - } - if (!SUCCEEDED(result)) { - return NULL; - } - strlcat(path,"\\tor",MAX_PATH); - is_set = 1; - return path; -} -#endif - -static char * -get_default_conf_file(void) -{ -#ifdef MS_WINDOWS - char *path = tor_malloc(MAX_PATH); - strlcpy(path, get_windows_conf_root(), MAX_PATH); - strlcat(path,"\\torrc",MAX_PATH); - return path; -#else - return tor_strdup(CONFDIR "/torrc"); -#endif -} - -/** Verify whether lst is a string containing valid-looking space-separated - * nicknames, or NULL. Return 0 on success. Warn and return -1 on failure. - */ -static int check_nickname_list(const char *lst, const char *name) -{ - int r = 0; - smartlist_t *sl; - - if (!lst) - return 0; - sl = smartlist_create(); - smartlist_split_string(sl, lst, ",", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH(sl, const char *, s, - { - if (!is_legal_nickname_or_hexdigest(s)) { - log_fn(LOG_WARN, "Invalid nickname '%s' in %s line", s, name); - r = -1; - } - }); - SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); - smartlist_free(sl); - return r; -} - static int -validate_options(or_options_t *options) +options_validate(or_options_t *options) { int i; int result = 0; @@ -853,7 +850,7 @@ validate_options(or_options_t *options) } if (options->Nickname == NULL) { - if (server_mode()) { + if (server_mode(options)) { if (!(options->Nickname = get_default_nickname())) return -1; log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname); @@ -875,7 +872,7 @@ validate_options(or_options_t *options) } } - if (server_mode()) { + if (server_mode(options)) { /* confirm that our address isn't broken, so we can complain now */ uint32_t tmp; if (resolve_my_address(options->Address, &tmp) < 0) @@ -1031,14 +1028,20 @@ validate_options(or_options_t *options) result = -1; } - if (!options->RedirectExitList) - options->RedirectExitList = smartlist_create(); -/* XXX need to free the old one if it's there, else they just keep piling up */ + /* free options->RedirectExitList */ + if (options->RedirectExitList) { + SMARTLIST_FOREACH(options->RedirectExitList, + exit_redirect_t *, p, tor_free(p)); + smartlist_free(options->RedirectExitList); + } + + options->RedirectExitList = smartlist_create(); for (cl = options->RedirectExit; cl; cl = cl->next) { if (parse_redirect_line(options, cl)<0) result = -1; } +/* XXX bug: this parsing shouldn't have side effects */ clear_trusted_dir_servers(); if (!options->DirServers) { add_default_trusted_dirservers(); @@ -1052,23 +1055,125 @@ validate_options(or_options_t *options) return result; } +/** Check if any of the previous options have changed but aren't allowed to. */ +static int +options_transition_allowed(or_options_t *old, or_options_t *new) { + + if(!old) + return 0; + + if (old->PidFile && + (!new->PidFile || strcmp(old->PidFile,new->PidFile))) { + log_fn(LOG_WARN,"PidFile is not allowed to change. Failing."); + return -1; + } + + if (old->RunAsDaemon && !new->RunAsDaemon) { + log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing."); + return -1; + } + + if (old->ORPort != new->ORPort) { + log_fn(LOG_WARN,"During reload, changing ORPort is not allowed. Failing."); + return -1; + } + + return 0; +} + +#ifdef MS_WINDOWS +static char *get_windows_conf_root(void) +{ + static int is_set = 0; + static char path[MAX_PATH+1]; + + LPITEMIDLIST idl; + IMalloc *m; + HRESULT result; + + if (is_set) + return path; + + /* Find X:\documents and settings\username\applicatation data\ . + * We would use SHGetSpecialFolder path, but that wasn't added until IE4. + */ + if (!SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, + &idl))) { + GetCurrentDirectory(MAX_PATH, path); + is_set = 1; + log_fn(LOG_WARN, "I couldn't find your application data folder: are you running an ancient version of Windows 95? Defaulting to '%s'", path); + return path; + } + /* Convert the path from an "ID List" (whatever that is!) to a path. */ + result = SHGetPathFromIDList(idl, path); + /* Now we need to free the */ + SHGetMalloc(&m); + if (m) { + m->lpVtbl->Free(m, idl); + m->lpVtbl->Release(m); + } + if (!SUCCEEDED(result)) { + return NULL; + } + strlcat(path,"\\tor",MAX_PATH); + is_set = 1; + return path; +} +#endif + +static char * +get_default_conf_file(void) +{ +#ifdef MS_WINDOWS + char *path = tor_malloc(MAX_PATH); + strlcpy(path, get_windows_conf_root(), MAX_PATH); + strlcat(path,"\\torrc",MAX_PATH); + return path; +#else + return tor_strdup(CONFDIR "/torrc"); +#endif +} + +/** Verify whether lst is a string containing valid-looking space-separated + * nicknames, or NULL. Return 0 on success. Warn and return -1 on failure. + */ +static int check_nickname_list(const char *lst, const char *name) +{ + int r = 0; + smartlist_t *sl; + + if (!lst) + return 0; + sl = smartlist_create(); + smartlist_split_string(sl, lst, ",", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + SMARTLIST_FOREACH(sl, const char *, s, + { + if (!is_legal_nickname_or_hexdigest(s)) { + log_fn(LOG_WARN, "Invalid nickname '%s' in %s line", s, name); + r = -1; + } + }); + SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); + smartlist_free(sl); + return r; +} + + /** Read a configuration file into <b>options</b>, finding the configuration * file location based on the command line. After loading the options, * validate them for consistency. Return 0 if success, <0 if failure. */ int -getconfig(int argc, char **argv, or_options_t *options) +getconfig(int argc, char **argv) { + or_options_t *oldoptions, *newoptions; struct config_line_t *cl; - FILE *cf; - char *fname; - int i; - int result = 0; + char *cf=NULL, *fname=NULL; + int i, retval; int using_default_torrc; static char **backup_argv; static int backup_argc; - char *previous_pidfile = NULL; - int previous_runasdaemon = 0; - int previous_orport = -1; + + oldoptions = global_options; if (argv) { /* first time we're called. save commandline args */ backup_argv = argv; @@ -1076,16 +1181,7 @@ getconfig(int argc, char **argv, or_options_t *options) } else { /* we're reloading. need to clean up old options first. */ argv = backup_argv; argc = backup_argc; - - /* record some previous values, so we can fail if they change */ - if (options->PidFile) - previous_pidfile = tor_strdup(options->PidFile); - previous_runasdaemon = options->RunAsDaemon; - previous_orport = options->ORPort; - free_options(options); } - init_options(options); - if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) { print_usage(); exit(0); @@ -1096,10 +1192,13 @@ getconfig(int argc, char **argv, or_options_t *options) exit(0); } + newoptions = tor_malloc_zero(sizeof(or_options_t)); + options_init(newoptions); + /* learn config file name, get config lines, assign them */ fname = NULL; using_default_torrc = 1; - options->command = CMD_RUN_TOR; + newoptions->command = CMD_RUN_TOR; for (i = 1; i < argc; ++i) { if (i < argc-1 && !strcmp(argv[i],"-f")) { if (fname) { @@ -1110,10 +1209,10 @@ getconfig(int argc, char **argv, or_options_t *options) using_default_torrc = 0; ++i; } else if (!strcmp(argv[i],"--list-fingerprint")) { - options->command = CMD_LIST_FINGERPRINT; + newoptions->command = CMD_LIST_FINGERPRINT; } else if (!strcmp(argv[i],"--hash-password")) { - options->command = CMD_HASH_PASSWORD; - options->command_arg = tor_strdup( (i < argc-1) ? argv[i+1] : ""); + newoptions->command = CMD_HASH_PASSWORD; + newoptions->command_arg = tor_strdup( (i < argc-1) ? argv[i+1] : ""); ++i; } } @@ -1138,7 +1237,7 @@ getconfig(int argc, char **argv, or_options_t *options) tor_assert(fname); log(LOG_DEBUG, "Opening config file '%s'", fname); - cf = fopen(fname, "r"); + cf = read_file_to_str(fname, 0); if (!cf) { if (using_default_torrc == 1) { log(LOG_NOTICE, "Configuration file '%s' not present, " @@ -1147,51 +1246,45 @@ getconfig(int argc, char **argv, or_options_t *options) } else { log(LOG_WARN, "Unable to open configuration file '%s'.", fname); tor_free(fname); - return -1; + goto err; } } else { /* it opened successfully. use it. */ tor_free(fname); - if (config_get_lines(cf, &cl)<0) - return -1; - if (config_assign(options,cl, 0) < 0) - return -1; + retval = config_get_lines(cf, &cl); + tor_free(cf); + if (retval < 0) + goto err; + retval = config_assign(newoptions, cl, 0); config_free_lines(cl); - fclose(cf); + if (retval < 0) + goto err; } /* go through command-line variables too */ cl = config_get_commandlines(argc,argv); - if (config_assign(options,cl,0) < 0) - return -1; + retval = config_assign(newoptions,cl,0); config_free_lines(cl); + if (retval < 0) + goto err; -/* Validate options */ - - /* first check if any of the previous options have changed but aren't allowed to */ - if (previous_pidfile && strcmp(previous_pidfile,options->PidFile)) { - log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.", - previous_pidfile, options->PidFile); - return -1; - } - tor_free(previous_pidfile); - - if (previous_runasdaemon && !options->RunAsDaemon) { - log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing."); - return -1; - } +/* Validate newoptions */ + if (options_validate(newoptions) < 0) + goto err; - if (previous_orport == 0 && options->ORPort > 0) { - log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing."); - return -1; - } + if (options_transition_allowed(oldoptions, newoptions) < 0) + goto err; - if (validate_options(options) < 0) - result = -1; + /* XXX now act on options */ + if (rend_config_services(newoptions) < 0) + goto err; - if (rend_config_services(options) < 0) { - result = -1; - } - return result; + if(oldoptions) + options_free(oldoptions); + set_options(newoptions); + return 0; + err: + options_free(newoptions); + return -1; } static int @@ -1282,6 +1375,7 @@ config_init_logs(or_options_t *options) { struct config_line_t *opt; int ok; + smartlist_t *elts; if (normalize_log_options(options)) return -1; @@ -1291,7 +1385,7 @@ config_init_logs(or_options_t *options) } ok = 1; - smartlist_t *elts = smartlist_create(); + elts = smartlist_create(); for (opt = options->Logs; opt; opt = opt->next) { int levelMin=LOG_DEBUG, levelMax=LOG_ERR; smartlist_split_string(elts, opt->value, " ", @@ -1496,7 +1590,7 @@ static int parse_redirect_line(or_options_t *options, } else { if (parse_addr_port(smartlist_get(elements,1),NULL,&r->addr_dest, &r->port_dest)) { - log_fn(LOG_WARN, "Error parseing dest address in RedirectExit line"); + log_fn(LOG_WARN, "Error parsing dest address in RedirectExit line"); goto err; } r->is_redirect = 1; @@ -1565,17 +1659,15 @@ static int parse_dir_server_line(const char *line) done: SMARTLIST_FOREACH(items, char*, s, tor_free(s)); smartlist_free(items); - - if (address) - tor_free(address); - + tor_free(address); return r; } const char * -get_data_directory(or_options_t *options) +get_data_directory(void) { const char *d; + or_options_t *options = get_options(); if (options->DataDirectory) { d = options->DataDirectory; |