Browse Source

Split the or_state_t portions of config.c into their own file

Nick Mathewson 11 years ago
parent
commit
7d11952bf4
10 changed files with 638 additions and 582 deletions
  1. 1 0
      src/or/circuitbuild.c
  2. 2 574
      src/or/config.c
  3. 0 8
      src/or/config.h
  4. 1 0
      src/or/hibernate.c
  5. 2 0
      src/or/include.am
  6. 2 0
      src/or/main.c
  7. 1 0
      src/or/router.c
  8. 606 0
      src/or/statefile.c
  9. 22 0
      src/or/statefile.h
  10. 1 0
      src/or/transports.c

+ 1 - 0
src/or/circuitbuild.c

@@ -33,6 +33,7 @@
 #include "router.h"
 #include "routerlist.h"
 #include "routerparse.h"
+#include "statefile.h"
 #include "crypto.h"
 #undef log
 #include <math.h>

+ 2 - 574
src/or/config.c

@@ -36,6 +36,7 @@
 #include "router.h"
 #include "util.h"
 #include "routerlist.h"
+#include "statefile.h"
 #include "transports.h"
 #ifdef _WIN32
 #include <shlobj.h>
@@ -93,18 +94,6 @@ static config_abbrev_t _option_abbrevs[] = {
   { NULL, NULL, 0, 0},
 };
 
-/** 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},
-};
-
 /** An entry for config_vars: "The option <b>name</b> has type
  * CONFIG_TYPE_<b>conftype</b>, and corresponds to
  * or_options_t.<b>member</b>"
@@ -441,63 +430,6 @@ static const config_var_t testing_tor_network_defaults[] = {
 
   { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
 };
-#undef VAR
-
-#define VAR(name,conftype,member,initvalue)                             \
-  { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_state_t, 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
@@ -536,9 +468,6 @@ static int check_server_ports(const smartlist_t *ports,
 static int validate_data_directory(or_options_t *options);
 static int write_configuration_file(const char *fname,
                                     const or_options_t *options);
-static int or_state_validate(or_state_t *old_options, or_state_t *options,
-                             int from_setconf, char **msg);
-static int or_state_load(void);
 static int options_init_logs(or_options_t *options, int validate_only);
 
 static void init_libevent(const or_options_t *options);
@@ -558,26 +487,6 @@ static config_format_t options_format = {
   NULL
 };
 
-/** 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,
-};
-
 /*
  * Functions to read and write the global options pointer.
  */
@@ -591,8 +500,6 @@ static or_options_t *global_default_options = NULL;
 static char *torrc_fname = NULL;
 /** Name of the most recently read torrc-defaults file.*/
 static char *torrc_defaults_fname;
-/** Persistent serialized state. */
-static or_state_t *global_state = NULL;
 /** Configuration Options set by command line. */
 static config_line_t *global_cmdline_options = NULL;
 /** Contents of most recently read DirPortFrontPage file. */
@@ -748,9 +655,6 @@ config_free_all(void)
   or_options_free(global_default_options);
   global_default_options = NULL;
 
-  config_free(&state_format, global_state);
-  global_state = NULL;
-
   config_free_lines(global_cmdline_options);
   global_cmdline_options = NULL;
 
@@ -1336,7 +1240,7 @@ options_act(const or_options_t *old_options)
   }
 
   /* Load state */
-  if (! global_state && running_tor) {
+  if (! or_state_loaded() && running_tor) {
     if (or_state_load())
       return -1;
     rep_hist_load_mtbf_data(time(NULL));
@@ -5391,14 +5295,6 @@ init_libevent(const or_options_t *options)
   }
 }
 
-/** Return the persistent state struct for this Tor. */
-or_state_t *
-get_or_state(void)
-{
-  tor_assert(global_state);
-  return global_state;
-}
-
 /** Return a newly allocated string holding a filename relative to the data
  * directory.  If <b>sub1</b> is present, it is the first path component after
  * the data directory.  If <b>sub2</b> is also present, it is the second path
@@ -5449,474 +5345,6 @@ options_get_datadir_fname2_suffix(const or_options_t *options,
   return fname;
 }
 
-/** 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.
- */
-static 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);
-}
-
 /** Given a file name check to see whether the file exists but has not been
  * modified for a very long time.  If so, remove it. */
 void

+ 0 - 8
src/or/config.h

@@ -59,10 +59,6 @@ char *options_get_datadir_fname2_suffix(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);
 int get_first_advertised_port_by_type_af(int listener_type,
                                          int address_family);
@@ -76,10 +72,6 @@ char *get_first_listener_addrport_string(int listener_type);
 int options_need_geoip_info(const or_options_t *options,
                             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);
 
 int getinfo_helper_config(control_connection_t *conn,

+ 1 - 0
src/or/hibernate.c

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

+ 2 - 0
src/or/include.am

@@ -53,6 +53,7 @@ src_or_libtor_a_SOURCES = \
 	src/or/router.c					\
 	src/or/routerlist.c				\
 	src/or/routerparse.c				\
+	src/or/statefile.c				\
 	src/or/status.c					\
 	$(evdns_source)					\
 	$(tor_platform_source)				\
@@ -124,6 +125,7 @@ ORHEADERS = \
 	src/or/router.h					\
 	src/or/routerlist.h				\
 	src/or/routerparse.h				\
+	src/or/statefile.h				\
 	src/or/status.h
 
 noinst_HEADERS+= $(ORHEADERS) micro-revision.i

+ 2 - 0
src/or/main.c

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

+ 1 - 0
src/or/router.c

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

+ 606 - 0
src/or/statefile.c

@@ -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 - 0
src/or/statefile.h

@@ -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
+

+ 1 - 0
src/or/transports.c

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