Merge branch 'confparse_refactor_squashed'

This commit is contained in:
Nick Mathewson 2012-09-13 12:25:45 -04:00
commit abbde7b184
16 changed files with 2016 additions and 1915 deletions

3
changes/refactor_config Normal file
View File

@ -0,0 +1,3 @@
o Code simplification and refactoring:
- Move the generic "config" code into a new file, and have "config.c"
hold only torrc- and state-related code.

View File

@ -16,6 +16,7 @@
#include "circuitlist.h" #include "circuitlist.h"
#include "circuituse.h" #include "circuituse.h"
#include "config.h" #include "config.h"
#include "confparse.h"
#include "connection.h" #include "connection.h"
#include "connection_edge.h" #include "connection_edge.h"
#include "connection_or.h" #include "connection_or.h"
@ -32,6 +33,7 @@
#include "router.h" #include "router.h"
#include "routerlist.h" #include "routerlist.h"
#include "routerparse.h" #include "routerparse.h"
#include "statefile.h"
#include "crypto.h" #include "crypto.h"
#undef log #undef log
#include <math.h> #include <math.h>

File diff suppressed because it is too large Load Diff

View File

@ -23,11 +23,9 @@ const char *escaped_safe_str_client(const char *address);
const char *escaped_safe_str(const char *address); const char *escaped_safe_str(const char *address);
const char *get_version(void); const char *get_version(void);
const char *get_short_version(void); const char *get_short_version(void);
int config_get_lines(const char *string, config_line_t **result, int extended);
void config_free_lines(config_line_t *front);
setopt_err_t options_trial_assign(config_line_t *list, int use_defaults, setopt_err_t options_trial_assign(config_line_t *list, int use_defaults,
int clear_first, char **msg); int clear_first, char **msg);
int resolve_my_address(int warn_severity, const or_options_t *options, int resolve_my_address(int warn_severity, const or_options_t *options,
uint32_t *addr, char **hostname_out); uint32_t *addr, char **hostname_out);
int is_local_addr(const tor_addr_t *addr); int is_local_addr(const tor_addr_t *addr);
@ -61,10 +59,6 @@ char *options_get_datadir_fname2_suffix(const or_options_t *options,
int get_num_cpus(const or_options_t *options); int get_num_cpus(const or_options_t *options);
or_state_t *get_or_state(void);
int did_last_state_file_write_fail(void);
int or_state_save(time_t now);
const smartlist_t *get_configured_ports(void); const smartlist_t *get_configured_ports(void);
int get_first_advertised_port_by_type_af(int listener_type, int get_first_advertised_port_by_type_af(int listener_type,
int address_family); int address_family);
@ -78,10 +72,6 @@ char *get_first_listener_addrport_string(int listener_type);
int options_need_geoip_info(const or_options_t *options, int options_need_geoip_info(const or_options_t *options,
const char **reason_out); const char **reason_out);
void save_transport_to_state(const char *transport_name,
const tor_addr_t *addr, uint16_t port);
char *get_stored_bindaddr_for_server_transport(const char *transport);
smartlist_t *get_list_of_ports_to_forward(void); smartlist_t *get_list_of_ports_to_forward(void);
int getinfo_helper_config(control_connection_t *conn, int getinfo_helper_config(control_connection_t *conn,

1226
src/or/confparse.c Normal file

File diff suppressed because it is too large Load Diff

132
src/or/confparse.h Normal file
View File

@ -0,0 +1,132 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2012, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_CONFPARSE_H
#define TOR_CONFPARSE_H
/** Enumeration of types which option values can take */
typedef enum config_type_t {
CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */
CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */
CONFIG_TYPE_UINT, /**< A non-negative integer less than MAX_INT */
CONFIG_TYPE_INT, /**< Any integer. */
CONFIG_TYPE_PORT, /**< A port from 1...65535, 0 for "not set", or
* "auto". */
CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/
CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional
* units */
CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/
CONFIG_TYPE_DOUBLE, /**< A floating-point value */
CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */
CONFIG_TYPE_AUTOBOOL, /**< A boolean+auto value, expressed 0 for false,
* 1 for true, and -1 for auto */
CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to GMT. */
CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and
* optional whitespace. */
CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */
CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines,
* mixed with other keywords. */
CONFIG_TYPE_LINELIST_V, /**< Catch-all "virtual" option to summarize
* context-sensitive config lines when fetching.
*/
CONFIG_TYPE_ROUTERSET, /**< A list of router names, addrs, and fps,
* parsed into a routerset_t. */
CONFIG_TYPE_OBSOLETE, /**< Obsolete (ignored) option. */
} config_type_t;
/** An abbreviation for a configuration option allowed on the command line. */
typedef struct config_abbrev_t {
const char *abbreviated;
const char *full;
int commandline_only;
int warn;
} config_abbrev_t;
/* Handy macro for declaring "In the config file or on the command line,
* you can abbreviate <b>tok</b>s as <b>tok</b>". */
#define PLURAL(tok) { #tok, #tok "s", 0, 0 }
/** A variable allowed in the configuration file or on the command line. */
typedef struct config_var_t {
const char *name; /**< The full keyword (case insensitive). */
config_type_t type; /**< How to interpret the type and turn it into a
* value. */
off_t var_offset; /**< Offset of the corresponding member of or_options_t. */
const char *initvalue; /**< String (or null) describing initial value. */
} config_var_t;
/** Represents an English description of a configuration variable; used when
* generating configuration file comments. */
typedef struct config_var_description_t {
const char *name;
const char *description;
} 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**);
/** Information on the keys, value types, key-to-struct-member mappings,
* variable descriptions, validation functions, and abbreviations for a
* configuration or storage format. */
typedef struct {
size_t size; /**< Size of the struct that everything gets parsed into. */
uint32_t magic; /**< Required 'magic value' to make sure we have a struct
* of the right type. */
off_t magic_offset; /**< Offset of the magic value within the struct. */
config_abbrev_t *abbrevs; /**< List of abbreviations that we expand when
* parsing this format. */
config_var_t *vars; /**< List of variables we recognize, their default
* values, and where we stick them in the structure. */
validate_fn_t validate_fn; /**< Function to validate config. */
/** If present, extra is a LINELIST variable for unrecognized
* lines. Otherwise, unrecognized lines are an error. */
config_var_t *extra;
} config_format_t;
/** Macro: assert that <b>cfg</b> has the right magic field for format
* <b>fmt</b>. */
#define CONFIG_CHECK(fmt, cfg) STMT_BEGIN \
tor_assert(fmt && cfg); \
tor_assert((fmt)->magic == \
*(uint32_t*)STRUCT_VAR_P(cfg,fmt->magic_offset)); \
STMT_END
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);
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);
config_line_t *config_get_assigned_option(const config_format_t *fmt,
const void *options, const char *key,
int escape_val);
int config_is_same(const config_format_t *fmt,
const void *o1, const void *o2,
const char *name);
void config_init(const config_format_t *fmt, void *options);
void *config_dup(const config_format_t *fmt, const void *old);
char *config_dump(const config_format_t *fmt, const void *default_options,
const void *options, int minimal,
int comment_defaults);
int config_assign(const config_format_t *fmt, void *options,
config_line_t *list,
int use_defaults, int clear_first, char **msg);
config_var_t *config_find_option_mutable(config_format_t *fmt,
const char *key);
const config_var_t *config_find_option(const config_format_t *fmt,
const char *key);
int config_get_lines(const char *string, config_line_t **result, int extended);
void config_free_lines(config_line_t *front);
const char *config_expand_abbrev(const config_format_t *fmt,
const char *option,
int command_line, int warn_obsolete);
#endif

View File

@ -16,6 +16,7 @@
#include "circuitlist.h" #include "circuitlist.h"
#include "circuituse.h" #include "circuituse.h"
#include "config.h" #include "config.h"
#include "confparse.h"
#include "connection.h" #include "connection.h"
#include "connection_edge.h" #include "connection_edge.h"
#include "connection_or.h" #include "connection_or.h"

View File

@ -7,6 +7,7 @@
#include "or.h" #include "or.h"
#include "buffers.h" #include "buffers.h"
#include "config.h" #include "config.h"
#include "confparse.h"
#include "connection.h" #include "connection.h"
#include "connection_or.h" #include "connection_or.h"
#include "control.h" #include "control.h"

View File

@ -29,6 +29,7 @@ hibernating, phase 2:
#include "hibernate.h" #include "hibernate.h"
#include "main.h" #include "main.h"
#include "router.h" #include "router.h"
#include "statefile.h"
extern long stats_n_seconds_working; /* published uptime */ extern long stats_n_seconds_working; /* published uptime */

View File

@ -21,7 +21,8 @@ src_or_libtor_a_SOURCES = \
src/or/circuitlist.c \ src/or/circuitlist.c \
src/or/circuituse.c \ src/or/circuituse.c \
src/or/command.c \ src/or/command.c \
src/or/config.c \ src/or/config.c \
src/or/confparse.c \
src/or/connection.c \ src/or/connection.c \
src/or/connection_edge.c \ src/or/connection_edge.c \
src/or/connection_or.c \ src/or/connection_or.c \
@ -52,6 +53,7 @@ src_or_libtor_a_SOURCES = \
src/or/router.c \ src/or/router.c \
src/or/routerlist.c \ src/or/routerlist.c \
src/or/routerparse.c \ src/or/routerparse.c \
src/or/statefile.c \
src/or/status.c \ src/or/status.c \
$(evdns_source) \ $(evdns_source) \
$(tor_platform_source) \ $(tor_platform_source) \
@ -88,6 +90,7 @@ ORHEADERS = \
src/or/circuituse.h \ src/or/circuituse.h \
src/or/command.h \ src/or/command.h \
src/or/config.h \ src/or/config.h \
src/or/confparse.h \
src/or/connection.h \ src/or/connection.h \
src/or/connection_edge.h \ src/or/connection_edge.h \
src/or/connection_or.h \ src/or/connection_or.h \
@ -122,6 +125,7 @@ ORHEADERS = \
src/or/router.h \ src/or/router.h \
src/or/routerlist.h \ src/or/routerlist.h \
src/or/routerparse.h \ src/or/routerparse.h \
src/or/statefile.h \
src/or/status.h src/or/status.h
noinst_HEADERS+= $(ORHEADERS) micro-revision.i noinst_HEADERS+= $(ORHEADERS) micro-revision.i

View File

@ -46,6 +46,7 @@
#include "router.h" #include "router.h"
#include "routerlist.h" #include "routerlist.h"
#include "routerparse.h" #include "routerparse.h"
#include "statefile.h"
#include "status.h" #include "status.h"
#ifdef USE_DMALLOC #ifdef USE_DMALLOC
#include <dmalloc.h> #include <dmalloc.h>
@ -2458,6 +2459,7 @@ tor_free_all(int postfork)
microdesc_free_all(); microdesc_free_all();
if (!postfork) { if (!postfork) {
config_free_all(); config_free_all();
or_state_free_all();
router_free_all(); router_free_all();
policies_free_all(); policies_free_all();
} }

View File

@ -27,6 +27,7 @@
#include "router.h" #include "router.h"
#include "routerlist.h" #include "routerlist.h"
#include "routerparse.h" #include "routerparse.h"
#include "statefile.h"
#include "transports.h" #include "transports.h"
/** /**

606
src/or/statefile.c Normal file
View File

@ -0,0 +1,606 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2012, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "or.h"
#include "circuitbuild.h"
#include "config.h"
#include "confparse.h"
#include "hibernate.h"
#include "rephist.h"
#include "router.h"
#include "statefile.h"
/** A list of state-file "abbreviations," for compatibility. */
static config_abbrev_t _state_abbrevs[] = {
{ "AccountingBytesReadInterval", "AccountingBytesReadInInterval", 0, 0 },
{ "HelperNode", "EntryGuard", 0, 0 },
{ "HelperNodeDownSince", "EntryGuardDownSince", 0, 0 },
{ "HelperNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
{ "EntryNode", "EntryGuard", 0, 0 },
{ "EntryNodeDownSince", "EntryGuardDownSince", 0, 0 },
{ "EntryNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
{ NULL, NULL, 0, 0},
};
/*XXXX these next two are duplicates or near-duplicates from config.c */
#define VAR(name,conftype,member,initvalue) \
{ name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_state_t, member), \
initvalue }
/** As VAR, but the option name and member name are the same. */
#define V(member,conftype,initvalue) \
VAR(#member, conftype, member, initvalue)
/** Array of "state" variables saved to the ~/.tor/state file. */
static config_var_t _state_vars[] = {
/* Remember to document these in state-contents.txt ! */
V(AccountingBytesReadInInterval, MEMUNIT, NULL),
V(AccountingBytesWrittenInInterval, MEMUNIT, NULL),
V(AccountingExpectedUsage, MEMUNIT, NULL),
V(AccountingIntervalStart, ISOTIME, NULL),
V(AccountingSecondsActive, INTERVAL, NULL),
V(AccountingSecondsToReachSoftLimit,INTERVAL, NULL),
V(AccountingSoftLimitHitAt, ISOTIME, NULL),
V(AccountingBytesAtSoftLimit, MEMUNIT, NULL),
VAR("EntryGuard", LINELIST_S, EntryGuards, NULL),
VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL),
VAR("EntryGuardUnlistedSince", LINELIST_S, EntryGuards, NULL),
VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL),
VAR("EntryGuardPathBias", LINELIST_S, EntryGuards, NULL),
V(EntryGuards, LINELIST_V, NULL),
VAR("TransportProxy", LINELIST_S, TransportProxies, NULL),
V(TransportProxies, LINELIST_V, NULL),
V(BWHistoryReadEnds, ISOTIME, NULL),
V(BWHistoryReadInterval, UINT, "900"),
V(BWHistoryReadValues, CSV, ""),
V(BWHistoryReadMaxima, CSV, ""),
V(BWHistoryWriteEnds, ISOTIME, NULL),
V(BWHistoryWriteInterval, UINT, "900"),
V(BWHistoryWriteValues, CSV, ""),
V(BWHistoryWriteMaxima, CSV, ""),
V(BWHistoryDirReadEnds, ISOTIME, NULL),
V(BWHistoryDirReadInterval, UINT, "900"),
V(BWHistoryDirReadValues, CSV, ""),
V(BWHistoryDirReadMaxima, CSV, ""),
V(BWHistoryDirWriteEnds, ISOTIME, NULL),
V(BWHistoryDirWriteInterval, UINT, "900"),
V(BWHistoryDirWriteValues, CSV, ""),
V(BWHistoryDirWriteMaxima, CSV, ""),
V(TorVersion, STRING, NULL),
V(LastRotatedOnionKey, ISOTIME, NULL),
V(LastWritten, ISOTIME, NULL),
V(TotalBuildTimes, UINT, NULL),
V(CircuitBuildAbandonedCount, UINT, "0"),
VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL),
VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL),
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
};
#undef VAR
#undef V
static int or_state_validate(or_state_t *old_options, or_state_t *options,
int from_setconf, char **msg);
/** Magic value for or_state_t. */
#define OR_STATE_MAGIC 0x57A73f57
/** "Extra" variable in the state that receives lines we can't parse. This
* lets us preserve options from versions of Tor newer than us. */
static config_var_t state_extra_var = {
"__extra", CONFIG_TYPE_LINELIST, STRUCT_OFFSET(or_state_t, ExtraLines), NULL
};
/** Configuration format for or_state_t. */
static const config_format_t state_format = {
sizeof(or_state_t),
OR_STATE_MAGIC,
STRUCT_OFFSET(or_state_t, _magic),
_state_abbrevs,
_state_vars,
(validate_fn_t)or_state_validate,
&state_extra_var,
};
/** Persistent serialized state. */
static or_state_t *global_state = NULL;
/** Return the persistent state struct for this Tor. */
or_state_t *
get_or_state(void)
{
tor_assert(global_state);
return global_state;
}
/** Return true iff we have loaded the global state for this Tor */
int
or_state_loaded(void)
{
return global_state != NULL;
}
/** Return true if <b>line</b> is a valid state TransportProxy line.
* Return false otherwise. */
static int
state_transport_line_is_valid(const char *line)
{
smartlist_t *items = NULL;
char *addrport=NULL;
tor_addr_t addr;
uint16_t port = 0;
int r;
items = smartlist_new();
smartlist_split_string(items, line, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
if (smartlist_len(items) != 2) {
log_warn(LD_CONFIG, "state: Not enough arguments in TransportProxy line.");
goto err;
}
addrport = smartlist_get(items, 1);
if (tor_addr_port_lookup(addrport, &addr, &port) < 0) {
log_warn(LD_CONFIG, "state: Could not parse addrport.");
goto err;
}
if (!port) {
log_warn(LD_CONFIG, "state: Transport line did not contain port.");
goto err;
}
r = 1;
goto done;
err:
r = 0;
done:
SMARTLIST_FOREACH(items, char*, s, tor_free(s));
smartlist_free(items);
return r;
}
/** Return 0 if all TransportProxy lines in <b>state</b> are well
* formed. Otherwise, return -1. */
static int
validate_transports_in_state(or_state_t *state)
{
int broken = 0;
config_line_t *line;
for (line = state->TransportProxies ; line ; line = line->next) {
tor_assert(!strcmp(line->key, "TransportProxy"));
if (!state_transport_line_is_valid(line->value))
broken = 1;
}
if (broken)
log_warn(LD_CONFIG, "state: State file seems to be broken.");
return 0;
}
/** Return 0 if every setting in <b>state</b> is reasonable, and a
* permissible transition from <b>old_state</b>. Else warn and return -1.
* Should have no side effects, except for normalizing the contents of
* <b>state</b>.
*/
/* XXX from_setconf is here because of bug 238 */
static int
or_state_validate(or_state_t *old_state, or_state_t *state,
int from_setconf, char **msg)
{
/* We don't use these; only options do. Still, we need to match that
* signature. */
(void) from_setconf;
(void) old_state;
if (entry_guards_parse_state(state, 0, msg)<0)
return -1;
if (validate_transports_in_state(state)<0)
return -1;
return 0;
}
/** Replace the current persistent state with <b>new_state</b> */
static int
or_state_set(or_state_t *new_state)
{
char *err = NULL;
int ret = 0;
tor_assert(new_state);
config_free(&state_format, global_state);
global_state = new_state;
if (entry_guards_parse_state(global_state, 1, &err)<0) {
log_warn(LD_GENERAL,"%s",err);
tor_free(err);
ret = -1;
}
if (rep_hist_load_state(global_state, &err)<0) {
log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err);
tor_free(err);
ret = -1;
}
if (circuit_build_times_parse_state(&circ_times, global_state) < 0) {
ret = -1;
}
return ret;
}
/**
* Save a broken state file to a backup location.
*/
static void
or_state_save_broken(char *fname)
{
int i;
file_status_t status;
char *fname2 = NULL;
for (i = 0; i < 100; ++i) {
tor_asprintf(&fname2, "%s.%d", fname, i);
status = file_status(fname2);
if (status == FN_NOENT)
break;
tor_free(fname2);
}
if (i == 100) {
log_warn(LD_BUG, "Unable to parse state in \"%s\"; too many saved bad "
"state files to move aside. Discarding the old state file.",
fname);
unlink(fname);
} else {
log_warn(LD_BUG, "Unable to parse state in \"%s\". Moving it aside "
"to \"%s\". This could be a bug in Tor; please tell "
"the developers.", fname, fname2);
if (rename(fname, fname2) < 0) {
log_warn(LD_BUG, "Weirdly, I couldn't even move the state aside. The "
"OS gave an error of %s", strerror(errno));
}
}
tor_free(fname2);
}
/** Reload the persistent state from disk, generating a new state as needed.
* Return 0 on success, less than 0 on failure.
*/
int
or_state_load(void)
{
or_state_t *new_state = NULL;
char *contents = NULL, *fname;
char *errmsg = NULL;
int r = -1, badstate = 0;
fname = get_datadir_fname("state");
switch (file_status(fname)) {
case FN_FILE:
if (!(contents = read_file_to_str(fname, 0, NULL))) {
log_warn(LD_FS, "Unable to read state file \"%s\"", fname);
goto done;
}
break;
case FN_NOENT:
break;
case FN_ERROR:
case FN_DIR:
default:
log_warn(LD_GENERAL,"State file \"%s\" is not a file? Failing.", fname);
goto done;
}
new_state = tor_malloc_zero(sizeof(or_state_t));
new_state->_magic = OR_STATE_MAGIC;
config_init(&state_format, new_state);
if (contents) {
config_line_t *lines=NULL;
int assign_retval;
if (config_get_lines(contents, &lines, 0)<0)
goto done;
assign_retval = config_assign(&state_format, new_state,
lines, 0, 0, &errmsg);
config_free_lines(lines);
if (assign_retval<0)
badstate = 1;
if (errmsg) {
log_warn(LD_GENERAL, "%s", errmsg);
tor_free(errmsg);
}
}
if (!badstate && or_state_validate(NULL, new_state, 1, &errmsg) < 0)
badstate = 1;
if (errmsg) {
log_warn(LD_GENERAL, "%s", errmsg);
tor_free(errmsg);
}
if (badstate && !contents) {
log_warn(LD_BUG, "Uh oh. We couldn't even validate our own default state."
" This is a bug in Tor.");
goto done;
} else if (badstate && contents) {
or_state_save_broken(fname);
tor_free(contents);
config_free(&state_format, new_state);
new_state = tor_malloc_zero(sizeof(or_state_t));
new_state->_magic = OR_STATE_MAGIC;
config_init(&state_format, new_state);
} else if (contents) {
log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
} else {
log_info(LD_GENERAL, "Initialized state");
}
if (or_state_set(new_state) == -1) {
or_state_save_broken(fname);
}
new_state = NULL;
if (!contents) {
global_state->next_write = 0;
or_state_save(time(NULL));
}
r = 0;
done:
tor_free(fname);
tor_free(contents);
if (new_state)
config_free(&state_format, new_state);
return r;
}
/** Did the last time we tried to write the state file fail? If so, we
* should consider disabling such features as preemptive circuit generation
* to compute circuit-build-time. */
static int last_state_file_write_failed = 0;
/** Return whether the state file failed to write last time we tried. */
int
did_last_state_file_write_fail(void)
{
return last_state_file_write_failed;
}
/** If writing the state to disk fails, try again after this many seconds. */
#define STATE_WRITE_RETRY_INTERVAL 3600
/** If we're a relay, how often should we checkpoint our state file even
* if nothing else dirties it? This will checkpoint ongoing stats like
* bandwidth used, per-country user stats, etc. */
#define STATE_RELAY_CHECKPOINT_INTERVAL (12*60*60)
/** Write the persistent state to disk. Return 0 for success, <0 on failure. */
int
or_state_save(time_t now)
{
char *state, *contents;
char tbuf[ISO_TIME_LEN+1];
char *fname;
tor_assert(global_state);
if (global_state->next_write > now)
return 0;
/* Call everything else that might dirty the state even more, in order
* to avoid redundant writes. */
entry_guards_update_state(global_state);
rep_hist_update_state(global_state);
circuit_build_times_update_state(&circ_times, global_state);
if (accounting_is_enabled(get_options()))
accounting_run_housekeeping(now);
global_state->LastWritten = now;
tor_free(global_state->TorVersion);
tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
state = config_dump(&state_format, NULL, global_state, 1, 0);
format_local_iso_time(tbuf, now);
tor_asprintf(&contents,
"# Tor state file last generated on %s local time\n"
"# Other times below are in GMT\n"
"# You *do not* need to edit this file.\n\n%s",
tbuf, state);
tor_free(state);
fname = get_datadir_fname("state");
if (write_str_to_file(fname, contents, 0)<0) {
log_warn(LD_FS, "Unable to write state to file \"%s\"; "
"will try again later", fname);
last_state_file_write_failed = 1;
tor_free(fname);
tor_free(contents);
/* Try again after STATE_WRITE_RETRY_INTERVAL (or sooner, if the state
* changes sooner). */
global_state->next_write = now + STATE_WRITE_RETRY_INTERVAL;
return -1;
}
last_state_file_write_failed = 0;
log_info(LD_GENERAL, "Saved state to \"%s\"", fname);
tor_free(fname);
tor_free(contents);
if (server_mode(get_options()))
global_state->next_write = now + STATE_RELAY_CHECKPOINT_INTERVAL;
else
global_state->next_write = TIME_MAX;
return 0;
}
/** Return the config line for transport <b>transport</b> in the current state.
* Return NULL if there is no config line for <b>transport</b>. */
static config_line_t *
get_transport_in_state_by_name(const char *transport)
{
or_state_t *or_state = get_or_state();
config_line_t *line;
config_line_t *ret = NULL;
smartlist_t *items = NULL;
for (line = or_state->TransportProxies ; line ; line = line->next) {
tor_assert(!strcmp(line->key, "TransportProxy"));
items = smartlist_new();
smartlist_split_string(items, line->value, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
if (smartlist_len(items) != 2) /* broken state */
goto done;
if (!strcmp(smartlist_get(items, 0), transport)) {
ret = line;
goto done;
}
SMARTLIST_FOREACH(items, char*, s, tor_free(s));
smartlist_free(items);
items = NULL;
}
done:
if (items) {
SMARTLIST_FOREACH(items, char*, s, tor_free(s));
smartlist_free(items);
}
return ret;
}
/** Return string containing the address:port part of the
* TransportProxy <b>line</b> for transport <b>transport</b>.
* If the line is corrupted, return NULL. */
static const char *
get_transport_bindaddr(const char *line, const char *transport)
{
char *line_tmp = NULL;
if (strlen(line) < strlen(transport) + 2) {
goto broken_state;
} else {
/* line should start with the name of the transport and a space.
(for example, "obfs2 127.0.0.1:47245") */
tor_asprintf(&line_tmp, "%s ", transport);
if (strcmpstart(line, line_tmp))
goto broken_state;
tor_free(line_tmp);
return (line+strlen(transport)+1);
}
broken_state:
tor_free(line_tmp);
return NULL;
}
/** Return a string containing the address:port that a proxy transport
* should bind on. The string is stored on the heap and must be freed
* by the caller of this function. */
char *
get_stored_bindaddr_for_server_transport(const char *transport)
{
char *default_addrport = NULL;
const char *stored_bindaddr = NULL;
config_line_t *line = get_transport_in_state_by_name(transport);
if (!line) /* Found no references in state for this transport. */
goto no_bindaddr_found;
stored_bindaddr = get_transport_bindaddr(line->value, transport);
if (stored_bindaddr) /* found stored bindaddr in state file. */
return tor_strdup(stored_bindaddr);
no_bindaddr_found:
/** If we didn't find references for this pluggable transport in the
state file, we should instruct the pluggable transport proxy to
listen on INADDR_ANY on a random ephemeral port. */
tor_asprintf(&default_addrport, "%s:%s", fmt_addr32(INADDR_ANY), "0");
return default_addrport;
}
/** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to
state */
void
save_transport_to_state(const char *transport,
const tor_addr_t *addr, uint16_t port)
{
or_state_t *state = get_or_state();
char *transport_addrport=NULL;
/** find where to write on the state */
config_line_t **next, *line;
/* see if this transport is already stored in state */
config_line_t *transport_line =
get_transport_in_state_by_name(transport);
if (transport_line) { /* if transport already exists in state... */
const char *prev_bindaddr = /* get its addrport... */
get_transport_bindaddr(transport_line->value, transport);
tor_asprintf(&transport_addrport, "%s:%d", fmt_addr(addr), (int)port);
/* if transport in state has the same address as this one, life is good */
if (!strcmp(prev_bindaddr, transport_addrport)) {
log_info(LD_CONFIG, "Transport seems to have spawned on its usual "
"address:port.");
goto done;
} else { /* if addrport in state is different than the one we got */
log_info(LD_CONFIG, "Transport seems to have spawned on different "
"address:port. Let's update the state file with the new "
"address:port");
tor_free(transport_line->value); /* free the old line */
tor_asprintf(&transport_line->value, "%s %s:%d", transport,
fmt_addr(addr),
(int) port); /* replace old addrport line with new line */
}
} else { /* never seen this one before; save it in state for next time */
log_info(LD_CONFIG, "It's the first time we see this transport. "
"Let's save its address:port");
next = &state->TransportProxies;
/* find the last TransportProxy line in the state and point 'next'
right after it */
line = state->TransportProxies;
while (line) {
next = &(line->next);
line = line->next;
}
/* allocate space for the new line and fill it in */
*next = line = tor_malloc_zero(sizeof(config_line_t));
line->key = tor_strdup("TransportProxy");
tor_asprintf(&line->value, "%s %s:%d", transport,
fmt_addr(addr), (int) port);
next = &(line->next);
}
if (!get_options()->AvoidDiskWrites)
or_state_mark_dirty(state, 0);
done:
tor_free(transport_addrport);
}
void
or_state_free_all(void)
{
config_free(&state_format, global_state);
global_state = NULL;
}

22
src/or/statefile.h Normal file
View File

@ -0,0 +1,22 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2012, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_STATEFILE_H
#define TOR_STATEFILE_H
or_state_t *get_or_state(void);
int did_last_state_file_write_fail(void);
int or_state_save(time_t now);
void save_transport_to_state(const char *transport_name,
const tor_addr_t *addr, uint16_t port);
char *get_stored_bindaddr_for_server_transport(const char *transport);
int or_state_load(void);
int or_state_loaded(void);
void or_state_free_all(void);
#endif

View File

@ -94,6 +94,7 @@
#include "transports.h" #include "transports.h"
#include "util.h" #include "util.h"
#include "router.h" #include "router.h"
#include "statefile.h"
static process_environment_t * static process_environment_t *
create_managed_proxy_environment(const managed_proxy_t *mp); create_managed_proxy_environment(const managed_proxy_t *mp);

View File

@ -6,6 +6,7 @@
#include "orconfig.h" #include "orconfig.h"
#include "or.h" #include "or.h"
#include "config.h" #include "config.h"
#include "confparse.h"
#include "connection_edge.h" #include "connection_edge.h"
#include "test.h" #include "test.h"