[or-cvs] [tor/master] Merge remote branch 'origin/maint-0.2.1' into maint-0.2.2

nickm at torproject.org nickm at torproject.org
Tue Feb 8 19:37:46 UTC 2011


commit d43470ad8a6b82e3d8a5aa0c1beb729fa65859b5
Merge: 9c7e2cf bcbcda3
Author: Nick Mathewson <nickm at torproject.org>
Date:   Tue Feb 8 14:37:02 2011 -0500

    Merge remote branch 'origin/maint-0.2.1' into maint-0.2.2

 changes/bug2470 |    5 +++++
 src/or/config.c |    8 +++++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --combined src/or/config.c
index 5aca225,a955b17..eae92b2
--- a/src/or/config.c
+++ b/src/or/config.c
@@@ -12,28 -12,6 +12,28 @@@
  #define CONFIG_PRIVATE
  
  #include "or.h"
 +#include "circuitbuild.h"
 +#include "circuitlist.h"
 +#include "config.h"
 +#include "connection.h"
 +#include "connection_edge.h"
 +#include "connection_or.h"
 +#include "control.h"
 +#include "cpuworker.h"
 +#include "dirserv.h"
 +#include "dirvote.h"
 +#include "dns.h"
 +#include "geoip.h"
 +#include "hibernate.h"
 +#include "main.h"
 +#include "networkstatus.h"
 +#include "policies.h"
 +#include "relay.h"
 +#include "rendclient.h"
 +#include "rendservice.h"
 +#include "rephist.h"
 +#include "router.h"
 +#include "routerlist.h"
  #ifdef MS_WINDOWS
  #include <shlobj.h>
  #endif
@@@ -83,12 -61,11 +83,12 @@@ static config_abbrev_t _option_abbrevs[
    PLURAL(LongLivedPort),
    PLURAL(HiddenServiceNode),
    PLURAL(HiddenServiceExcludeNode),
 -  PLURAL(NumCpu),
 +  PLURAL(NumCPU),
    PLURAL(RendNode),
    PLURAL(RendExcludeNode),
    PLURAL(StrictEntryNode),
    PLURAL(StrictExitNode),
 +  PLURAL(StrictNode),
    { "l", "Log", 1, 0},
    { "AllowUnverifiedNodes", "AllowInvalidNodes", 0, 0},
    { "AutomapHostSuffixes", "AutomapHostsSuffixes", 0, 0},
@@@ -106,12 -83,10 +106,12 @@@
    { "NumEntryNodes", "NumEntryGuards", 0, 0},
    { "ResolvConf", "ServerDNSResolvConfFile", 0, 1},
    { "SearchDomains", "ServerDNSSearchDomains", 0, 1},
 -  { "ServerDNSAllowBrokenResolvConf", "ServerDNSAllowBrokenConfig", 0, 0 },
 +  { "ServerDNSAllowBrokenResolvConf", "ServerDNSAllowBrokenConfig", 0, 0},
    { "PreferTunnelledDirConns", "PreferTunneledDirConns", 0, 0},
    { "BridgeAuthoritativeDirectory", "BridgeAuthoritativeDir", 0, 0},
    { "HashedControlPassword", "__HashedControlSessionPassword", 1, 0},
 +  { "StrictEntryNodes", "StrictNodes", 0, 1},
 +  { "StrictExitNodes", "StrictNodes", 0, 1},
    { NULL, NULL, 0, 0},
  };
  
@@@ -159,7 -134,6 +159,7 @@@ static config_var_t _option_vars[] = 
    V(AccountingMax,               MEMUNIT,  "0 bytes"),
    V(AccountingStart,             STRING,   NULL),
    V(Address,                     STRING,   NULL),
 +  V(AllowDotExit,                BOOL,     "0"),
    V(AllowInvalidNodes,           CSV,      "middle,rendezvous"),
    V(AllowNonRFC953Hostnames,     BOOL,     "0"),
    V(AllowSingleHopCircuits,      BOOL,     "0"),
@@@ -188,16 -162,10 +188,16 @@@
    V(BridgePassword,              STRING,   NULL),
    V(BridgeRecordUsageByCountry,  BOOL,     "1"),
    V(BridgeRelay,                 BOOL,     "0"),
 -  V(CircuitBuildTimeout,         INTERVAL, "1 minute"),
 +  V(CellStatistics,              BOOL,     "0"),
 +  V(LearnCircuitBuildTimeout,    BOOL,     "1"),
 +  V(CircuitBuildTimeout,         INTERVAL, "0"),
    V(CircuitIdleTimeout,          INTERVAL, "1 hour"),
 +  V(CircuitStreamTimeout,        INTERVAL, "0"),
 +  V(CircuitPriorityHalflife,     DOUBLE,  "-100.0"), /*negative:'Use default'*/
    V(ClientDNSRejectInternalAddresses, BOOL,"1"),
 +  V(ClientRejectInternalAddresses, BOOL,   "1"),
    V(ClientOnly,                  BOOL,     "0"),
 +  V(ConsensusParams,             STRING,   NULL),
    V(ConnLimit,                   UINT,     "1000"),
    V(ConstrainedSockets,          BOOL,     "0"),
    V(ConstrainedSockSize,         MEMUNIT,  "8192"),
@@@ -218,19 -186,18 +218,19 @@@
    V(DirPort,                     UINT,     "0"),
    V(DirPortFrontPage,            FILENAME, NULL),
    OBSOLETE("DirPostPeriod"),
 -#ifdef ENABLE_GEOIP_STATS
 -  V(DirRecordUsageByCountry,     BOOL,     "0"),
 -  V(DirRecordUsageGranularity,   UINT,     "4"),
 -  V(DirRecordUsageRetainIPs,     INTERVAL, "14 days"),
 -  V(DirRecordUsageSaveInterval,  INTERVAL, "6 hours"),
 -#endif
 +  OBSOLETE("DirRecordUsageByCountry"),
 +  OBSOLETE("DirRecordUsageGranularity"),
 +  OBSOLETE("DirRecordUsageRetainIPs"),
 +  OBSOLETE("DirRecordUsageSaveInterval"),
 +  V(DirReqStatistics,            BOOL,     "0"),
    VAR("DirServer",               LINELIST, DirServers, NULL),
 +  V(DisableAllSwap,              BOOL,     "0"),
    V(DNSPort,                     UINT,     "0"),
    V(DNSListenAddress,            LINELIST, NULL),
    V(DownloadExtraInfo,           BOOL,     "0"),
    V(EnforceDistinctSubnets,      BOOL,     "1"),
    V(EntryNodes,                  ROUTERSET,   NULL),
 +  V(EntryStatistics,             BOOL,     "0"),
    V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "10 minutes"),
    V(ExcludeNodes,                ROUTERSET, NULL),
    V(ExcludeExitNodes,            ROUTERSET, NULL),
@@@ -238,20 -205,12 +238,20 @@@
    V(ExitNodes,                   ROUTERSET, NULL),
    V(ExitPolicy,                  LINELIST, NULL),
    V(ExitPolicyRejectPrivate,     BOOL,     "1"),
 +  V(ExitPortStatistics,          BOOL,     "0"),
 +  V(ExtraInfoStatistics,         BOOL,     "0"),
 +
 +#if defined (WINCE)
 +  V(FallbackNetworkstatusFile,   FILENAME, "fallback-consensus"),
 +#else
    V(FallbackNetworkstatusFile,   FILENAME,
      SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "fallback-consensus"),
 +#endif
    V(FascistFirewall,             BOOL,     "0"),
    V(FirewallPorts,               CSV,      ""),
    V(FastFirstHopPK,              BOOL,     "1"),
    V(FetchDirInfoEarly,           BOOL,     "0"),
 +  V(FetchDirInfoExtraEarly,      BOOL,     "0"),
    V(FetchServerDescriptors,      BOOL,     "1"),
    V(FetchHidServDescriptors,     BOOL,     "1"),
    V(FetchUselessDescriptors,     BOOL,     "0"),
@@@ -263,8 -222,6 +263,8 @@@
  #endif
    OBSOLETE("Group"),
    V(HardwareAccel,               BOOL,     "0"),
 +  V(AccelName,                   STRING,   NULL),
 +  V(AccelDir,                    FILENAME, NULL),
    V(HashedControlPassword,       LINELIST, NULL),
    V(HidServDirectoryV2,          BOOL,     "1"),
    VAR("HiddenServiceDir",    LINELIST_S, RendConfigLines,    NULL),
@@@ -276,15 -233,11 +276,15 @@@
    VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
    V(HidServAuth,                 LINELIST, NULL),
    V(HSAuthoritativeDir,          BOOL,     "0"),
 -  V(HSAuthorityRecordStats,      BOOL,     "0"),
 -  V(HttpProxy,                   STRING,   NULL),
 -  V(HttpProxyAuthenticator,      STRING,   NULL),
 -  V(HttpsProxy,                  STRING,   NULL),
 -  V(HttpsProxyAuthenticator,     STRING,   NULL),
 +  OBSOLETE("HSAuthorityRecordStats"),
 +  V(HTTPProxy,                   STRING,   NULL),
 +  V(HTTPProxyAuthenticator,      STRING,   NULL),
 +  V(HTTPSProxy,                  STRING,   NULL),
 +  V(HTTPSProxyAuthenticator,     STRING,   NULL),
 +  V(Socks4Proxy,                 STRING,   NULL),
 +  V(Socks5Proxy,                 STRING,   NULL),
 +  V(Socks5ProxyUsername,         STRING,   NULL),
 +  V(Socks5ProxyPassword,         STRING,   NULL),
    OBSOLETE("IgnoreVersion"),
    V(KeepalivePeriod,             INTERVAL, "5 minutes"),
    VAR("Log",                     LINELIST, Logs,             NULL),
@@@ -301,20 -254,17 +301,20 @@@
    V(MyFamily,                    STRING,   NULL),
    V(NewCircuitPeriod,            INTERVAL, "30 seconds"),
    VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"),
 -  V(NatdListenAddress,           LINELIST, NULL),
 -  V(NatdPort,                    UINT,     "0"),
 +  V(NATDListenAddress,           LINELIST, NULL),
 +  V(NATDPort,                    UINT,     "0"),
    V(Nickname,                    STRING,   NULL),
 -  V(NoPublish,                   BOOL,     "0"),
 +  V(WarnUnsafeSocks,              BOOL,     "1"),
 +  OBSOLETE("NoPublish"),
    VAR("NodeFamily",              LINELIST, NodeFamilies,         NULL),
 -  V(NumCpus,                     UINT,     "1"),
 +  V(NumCPUs,                     UINT,     "1"),
    V(NumEntryGuards,              UINT,     "3"),
    V(ORListenAddress,             LINELIST, NULL),
    V(ORPort,                      UINT,     "0"),
    V(OutboundBindAddress,         STRING,   NULL),
    OBSOLETE("PathlenCoinWeight"),
 +  V(PerConnBWBurst,              MEMUNIT,  "0"),
 +  V(PerConnBWRate,               MEMUNIT,  "0"),
    V(PidFile,                     STRING,   NULL),
    V(TestingTorNetwork,           BOOL,     "0"),
    V(PreferTunneledDirConns,      BOOL,     "1"),
@@@ -328,7 -278,6 +328,7 @@@
    V(RecommendedClientVersions,   LINELIST, NULL),
    V(RecommendedServerVersions,   LINELIST, NULL),
    OBSOLETE("RedirectExit"),
 +  V(RefuseUnknownExits,          STRING,   "auto"),
    V(RejectPlaintextPorts,        CSV,      ""),
    V(RelayBandwidthBurst,         MEMUNIT,  "0"),
    V(RelayBandwidthRate,          MEMUNIT,  "0"),
@@@ -338,9 -287,8 +338,9 @@@
    V(RephistTrackTime,            INTERVAL, "24 hours"),
    OBSOLETE("RouterFile"),
    V(RunAsDaemon,                 BOOL,     "0"),
 -  V(RunTesting,                  BOOL,     "0"),
 -  V(SafeLogging,                 BOOL,     "1"),
 +//  V(RunTesting,                  BOOL,     "0"),
 +  OBSOLETE("RunTesting"), // currently unused
 +  V(SafeLogging,                 STRING,   "1"),
    V(SafeSocks,                   BOOL,     "0"),
    V(ServerDNSAllowBrokenConfig,  BOOL,     "1"),
    V(ServerDNSAllowNonRFC953Hostnames, BOOL,"0"),
@@@ -356,7 -304,8 +356,7 @@@
    V(SocksPort,                   UINT,     "9050"),
    V(SocksTimeout,                INTERVAL, "2 minutes"),
    OBSOLETE("StatusFetchPeriod"),
 -  V(StrictEntryNodes,            BOOL,     "0"),
 -  V(StrictExitNodes,             BOOL,     "0"),
 +  V(StrictNodes,                 BOOL,     "0"),
    OBSOLETE("SysLog"),
    V(TestSocks,                   BOOL,     "0"),
    OBSOLETE("TestVia"),
@@@ -381,7 -330,6 +381,7 @@@
    V(V3AuthDistDelay,             INTERVAL, "5 minutes"),
    V(V3AuthNIntervalsValid,       UINT,     "3"),
    V(V3AuthUseLegacyKey,          BOOL,     "0"),
 +  V(V3BandwidthsFile,            FILENAME, NULL),
    VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
    V(VirtualAddrNetwork,          STRING,   "127.192.0.0/10"),
    V(WarnPlaintextPorts,          CSV,      "23,109,110,143"),
@@@ -392,7 -340,6 +392,7 @@@
    VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword,
        NULL),
    V(MinUptimeHidServDirectoryV2, INTERVAL, "24 hours"),
 +
    { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
  };
  
@@@ -406,7 -353,6 +406,7 @@@ static config_var_t testing_tor_network
    V(AuthDirMaxServersPerAddr,    UINT,     "0"),
    V(AuthDirMaxServersPerAuthAddr,UINT,     "0"),
    V(ClientDNSRejectInternalAddresses, BOOL,"0"),
 +  V(ClientRejectInternalAddresses, BOOL,   "0"),
    V(ExitPolicyRejectPrivate,     BOOL,     "0"),
    V(V3AuthVotingInterval,        INTERVAL, "5 minutes"),
    V(V3AuthVoteDelay,             INTERVAL, "20 seconds"),
@@@ -416,7 -362,6 +416,7 @@@
    V(TestingV3AuthInitialDistDelay, INTERVAL, "20 seconds"),
    V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"),
    V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"),
 +  V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
    { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
  };
  #undef VAR
@@@ -432,9 -377,6 +432,9 @@@ static config_var_t _state_vars[] = 
    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),
@@@ -448,23 -390,12 +448,23 @@@
    V(BWHistoryWriteEnds,               ISOTIME,  NULL),
    V(BWHistoryWriteInterval,           UINT,     "900"),
    V(BWHistoryWriteValues,             CSV,      ""),
 +  V(BWHistoryDirReadEnds,             ISOTIME,  NULL),
 +  V(BWHistoryDirReadInterval,         UINT,     "900"),
 +  V(BWHistoryDirReadValues,           CSV,      ""),
 +  V(BWHistoryDirWriteEnds,            ISOTIME,  NULL),
 +  V(BWHistoryDirWriteInterval,        UINT,     "900"),
 +  V(BWHistoryDirWriteValues,          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 }
  };
  
@@@ -479,6 -410,213 +479,6 @@@ typedef struct config_var_description_
    const char *description;
  } config_var_description_t;
  
 -/** Descriptions of the configuration options, to be displayed by online
 - * option browsers */
 -/* XXXX022 did anybody want this? at all? If not, kill it.*/
 -static config_var_description_t options_description[] = {
 -  /* ==== general options */
 -  { "AvoidDiskWrites", "If non-zero, try to write to disk less frequently than"
 -    " we would otherwise." },
 -  { "BandwidthRate", "A token bucket limits the average incoming bandwidth on "
 -    "this node to the specified number of bytes per second." },
 -  { "BandwidthBurst", "Limit the maximum token buffer size (also known as "
 -    "burst) to the given number of bytes." },
 -  { "ConnLimit", "Minimum number of simultaneous sockets we must have." },
 -  { "ConstrainedSockets", "Shrink tx and rx buffers for sockets to avoid "
 -    "system limits on vservers and related environments.  See man page for "
 -    "more information regarding this option." },
 -  { "ConstrainedSockSize", "Limit socket buffers to this size when "
 -    "ConstrainedSockets is enabled." },
 -  /*  ControlListenAddress */
 -  { "ControlPort", "If set, Tor will accept connections from the same machine "
 -    "(localhost only) on this port, and allow those connections to control "
 -    "the Tor process using the Tor Control Protocol (described in "
 -    "control-spec.txt).", },
 -  { "CookieAuthentication", "If this option is set to 1, don't allow any "
 -    "connections to the control port except when the connecting process "
 -    "can read a file that Tor creates in its data directory." },
 -  { "DataDirectory", "Store working data, state, keys, and caches here." },
 -  { "DirServer", "Tor only trusts directories signed with one of these "
 -    "servers' keys.  Used to override the standard list of directory "
 -    "authorities." },
 -  /* { "FastFirstHopPK", "" }, */
 -  /* FetchServerDescriptors, FetchHidServDescriptors,
 -   * FetchUselessDescriptors */
 -  { "HardwareAccel", "If set, Tor tries to use hardware crypto accelerators "
 -    "when it can." },
 -  /* HashedControlPassword */
 -  { "HTTPProxy", "Force Tor to make all HTTP directory requests through this "
 -    "host:port (or host:80 if port is not set)." },
 -  { "HTTPProxyAuthenticator", "A username:password pair to be used with "
 -    "HTTPProxy." },
 -  { "HTTPSProxy", "Force Tor to make all TLS (SSL) connections through this "
 -    "host:port (or host:80 if port is not set)." },
 -  { "HTTPSProxyAuthenticator", "A username:password pair to be used with "
 -    "HTTPSProxy." },
 -  { "KeepalivePeriod", "Send a padding cell every N seconds to keep firewalls "
 -    "from closing our connections while Tor is not in use." },
 -  { "Log", "Where to send logging messages.  Format is "
 -    "minSeverity[-maxSeverity] (stderr|stdout|syslog|file FILENAME)." },
 -  { "OutboundBindAddress", "Make all outbound connections originate from the "
 -    "provided IP address (only useful for multiple network interfaces)." },
 -  { "PIDFile", "On startup, write our PID to this file. On clean shutdown, "
 -    "remove the file." },
 -  { "PreferTunneledDirConns", "If non-zero, avoid directory servers that "
 -    "don't support tunneled connections." },
 -  /* PreferTunneledDirConns */
 -  /* ProtocolWarnings */
 -  /* RephistTrackTime */
 -  { "RunAsDaemon", "If set, Tor forks and daemonizes to the background when "
 -    "started.  Unix only." },
 -  { "SafeLogging", "If set to 0, Tor logs potentially sensitive strings "
 -    "rather than replacing them with the string [scrubbed]." },
 -  { "TunnelDirConns", "If non-zero, when a directory server we contact "
 -    "supports it, we will build a one-hop circuit and make an encrypted "
 -    "connection via its ORPort." },
 -  { "User", "On startup, setuid to this user." },
 -
 -  /* ==== client options */
 -  { "AllowInvalidNodes", "Where on our circuits should Tor allow servers "
 -    "that the directory authorities haven't called \"valid\"?" },
 -  { "AllowNonRFC953Hostnames", "If set to 1, we don't automatically reject "
 -    "hostnames for having invalid characters." },
 -  /*  CircuitBuildTimeout, CircuitIdleTimeout */
 -  { "ClientOnly", "If set to 1, Tor will under no circumstances run as a "
 -    "server, even if ORPort is enabled." },
 -  { "EntryNodes", "A list of preferred entry nodes to use for the first hop "
 -    "in circuits, when possible." },
 -  /* { "EnforceDistinctSubnets" , "" }, */
 -  { "ExitNodes", "A list of preferred nodes to use for the last hop in "
 -    "circuits, when possible." },
 -  { "ExcludeNodes", "A list of nodes never to use when building a circuit." },
 -  { "FascistFirewall", "If set, Tor will only create outgoing connections to "
 -    "servers running on the ports listed in FirewallPorts." },
 -  { "FirewallPorts", "A list of ports that we can connect to.  Only used "
 -    "when FascistFirewall is set." },
 -  { "LongLivedPorts", "A list of ports for services that tend to require "
 -    "high-uptime connections." },
 -  { "MapAddress", "Force Tor to treat all requests for one address as if "
 -    "they were for another." },
 -  { "NewCircuitPeriod", "Force Tor to consider whether to build a new circuit "
 -    "every NUM seconds." },
 -  { "MaxCircuitDirtiness", "Do not attach new streams to a circuit that has "
 -    "been used more than this many seconds ago." },
 -  /* NatdPort, NatdListenAddress */
 -  { "NodeFamily", "A list of servers that constitute a 'family' and should "
 -    "never be used in the same circuit." },
 -  { "NumEntryGuards", "How many entry guards should we keep at a time?" },
 -  /* PathlenCoinWeight */
 -  { "ReachableAddresses", "Addresses we can connect to, as IP/bits:port-port. "
 -    "By default, we assume all addresses are reachable." },
 -  /* reachablediraddresses, reachableoraddresses. */
 -  /* SafeSOCKS */
 -  { "SOCKSPort", "The port where we listen for SOCKS connections from "
 -    "applications." },
 -  { "SOCKSListenAddress", "Bind to this address to listen to connections from "
 -    "SOCKS-speaking applications." },
 -  { "SOCKSPolicy", "Set an entry policy to limit which addresses can connect "
 -    "to the SOCKSPort." },
 -  /* SocksTimeout */
 -  { "StrictExitNodes", "If set, Tor will fail to operate when none of the "
 -    "configured ExitNodes can be used." },
 -  { "StrictEntryNodes", "If set, Tor will fail to operate when none of the "
 -    "configured EntryNodes can be used." },
 -  /* TestSocks */
 -  { "TrackHostsExit", "Hosts and domains which should, if possible, be "
 -    "accessed from the same exit node each time we connect to them." },
 -  { "TrackHostsExitExpire", "Time after which we forget which exit we were "
 -    "using to connect to hosts in TrackHostsExit." },
 -  /* "TransPort", "TransListenAddress */
 -  { "UseEntryGuards", "Set to 0 if we want to pick from the whole set of "
 -    "servers for the first position in each circuit, rather than picking a "
 -    "set of 'Guards' to prevent profiling attacks." },
 -
 -  /* === server options */
 -  { "Address", "The advertised (external) address we should use." },
 -  /* Accounting* options. */
 -  /* AssumeReachable */
 -  { "ContactInfo", "Administrative contact information to advertise for this "
 -    "server." },
 -  { "ExitPolicy", "Address/port ranges for which to accept or reject outgoing "
 -    "connections on behalf of Tor users." },
 -  /*  { "ExitPolicyRejectPrivate, "" }, */
 -  { "MaxAdvertisedBandwidth", "If set, we will not advertise more than this "
 -    "amount of bandwidth for our bandwidth rate, regardless of how much "
 -    "bandwidth we actually detect." },
 -  { "MaxOnionsPending", "Reject new attempts to extend circuits when we "
 -    "already have this many pending." },
 -  { "MyFamily", "Declare a list of other servers as belonging to the same "
 -    "family as this one, so that clients will not use two from the same "
 -    "family in the same circuit." },
 -  { "Nickname", "Set the server nickname." },
 -  { "NoPublish", "{DEPRECATED}" },
 -  { "NumCPUs", "How many processes to use at once for public-key crypto." },
 -  { "ORPort", "Advertise this port to listen for connections from Tor clients "
 -    "and servers." },
 -  { "ORListenAddress", "Bind to this address to listen for connections from "
 -    "clients and servers, instead of the default 0.0.0.0:ORPort." },
 -  { "PublishServerDescriptor", "Set to 0 to keep the server from "
 -    "uploading info to the directory authorities." },
 -  /* ServerDNS: DetectHijacking, ResolvConfFile, SearchDomains */
 -  { "ShutdownWaitLength", "Wait this long for clients to finish when "
 -    "shutting down because of a SIGINT." },
 -
 -  /* === directory cache options */
 -  { "DirPort", "Serve directory information from this port, and act as a "
 -    "directory cache." },
 -  { "DirPortFrontPage", "Serve a static html disclaimer on DirPort." },
 -  { "DirListenAddress", "Bind to this address to listen for connections from "
 -    "clients and servers, instead of the default 0.0.0.0:DirPort." },
 -  { "DirPolicy", "Set a policy to limit who can connect to the directory "
 -    "port." },
 -
 -  /*  Authority options: AuthDirBadExit, AuthDirInvalid, AuthDirReject,
 -   * AuthDirRejectUnlisted, AuthDirListBadExits, AuthoritativeDirectory,
 -   * DirAllowPrivateAddresses, HSAuthoritativeDir,
 -   * NamingAuthoritativeDirectory, RecommendedVersions,
 -   * RecommendedClientVersions, RecommendedServerVersions, RendPostPeriod,
 -   * RunTesting, V1AuthoritativeDirectory, VersioningAuthoritativeDirectory, */
 -
 -  /* Hidden service options: HiddenService: dir,excludenodes, nodes,
 -   * options, port.  PublishHidServDescriptor */
 -
 -  /* Nonpersistent options: __LeaveStreamsUnattached, __AllDirActionsPrivate */
 -  { NULL, NULL },
 -};
 -
 -/** Online description of state variables. */
 -static config_var_description_t state_description[] = {
 -  { "AccountingBytesReadInInterval",
 -    "How many bytes have we read in this accounting period?" },
 -  { "AccountingBytesWrittenInInterval",
 -    "How many bytes have we written in this accounting period?" },
 -  { "AccountingExpectedUsage",
 -    "How many bytes did we expect to use per minute? (0 for no estimate.)" },
 -  { "AccountingIntervalStart", "When did this accounting period begin?" },
 -  { "AccountingSecondsActive", "How long have we been awake in this period?" },
 -
 -  { "BWHistoryReadEnds", "When does the last-recorded read-interval end?" },
 -  { "BWHistoryReadInterval", "How long is each read-interval (in seconds)?" },
 -  { "BWHistoryReadValues", "Number of bytes read in each interval." },
 -  { "BWHistoryWriteEnds", "When does the last-recorded write-interval end?" },
 -  { "BWHistoryWriteInterval", "How long is each write-interval (in seconds)?"},
 -  { "BWHistoryWriteValues", "Number of bytes written in each interval." },
 -
 -  { "EntryGuard", "One of the nodes we have chosen as a fixed entry" },
 -  { "EntryGuardDownSince",
 -    "The last entry guard has been unreachable since this time." },
 -  { "EntryGuardUnlistedSince",
 -    "The last entry guard has been unusable since this time." },
 -
 -  { "LastRotatedOnionKey",
 -    "The last time at which we changed the medium-term private key used for "
 -    "building circuits." },
 -  { "LastWritten", "When was this state file last regenerated?" },
 -
 -  { "TorVersion", "Which version of Tor generated this state file?" },
 -  { NULL, NULL },
 -};
 -
  /** Type of a callback to validate whether a given configuration is
   * well-formed and consistent. See options_trial_assign() for documentation
   * of arguments. */
@@@ -497,6 -635,8 +497,6 @@@ typedef struct 
    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. */
 -  /** Documentation for configuration variables. */
 -  config_var_description_t *descriptions;
    /** If present, extra is a LINELIST variable for unrecognized
     * lines.  Otherwise, unrecognized lines are an error. */
    config_var_t *extra;
@@@ -560,6 -700,20 +560,6 @@@ static uint64_t config_parse_memunit(co
  static int config_parse_interval(const char *s, int *ok);
  static void init_libevent(void);
  static int opt_streq(const char *s1, const char *s2);
 -/** Versions of libevent. */
 -typedef enum {
 -  /* Note: we compare these, so it's important that "old" precede everything,
 -   * and that "other" come last. */
 -  LE_OLD=0, LE_10C, LE_10D, LE_10E, LE_11, LE_11A, LE_11B, LE_12, LE_12A,
 -  LE_13, LE_13A, LE_13B, LE_13C, LE_13D, LE_13E,
 -  LE_140, LE_141, LE_142, LE_143, LE_144, LE_145, LE_146, LE_147, LE_148,
 -  LE_1499,
 -  LE_OTHER
 -} le_version_t;
 -static le_version_t decode_libevent_version(const char *v, int *bincompat_out);
 -#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
 -static void check_libevent_version(const char *m, int server);
 -#endif
  
  /** Magic value for or_options_t. */
  #define OR_OPTIONS_MAGIC 9090909
@@@ -572,6 -726,7 +572,6 @@@ static config_format_t options_format 
    _option_abbrevs,
    _option_vars,
    (validate_fn_t)options_validate,
 -  options_description,
    NULL
  };
  
@@@ -592,6 -747,7 +592,6 @@@ static config_format_t state_format = 
    _state_abbrevs,
    _state_vars,
    (validate_fn_t)or_state_validate,
 -  state_description,
    &state_extra_var,
  };
  
@@@ -656,13 -812,13 +656,13 @@@ set_options(or_options_t *new_val, cha
              "Acting on config options left us in a broken state. Dying.");
      exit(1);
    }
 -  if (old_options)
 -    config_free(&options_format, old_options);
 +
 +  config_free(&options_format, old_options);
  
    return 0;
  }
  
 -extern const char tor_svn_revision[]; /* from tor_main.c */
 +extern const char tor_git_revision[]; /* from tor_main.c */
  
  /** The version of this Tor process, as parsed. */
  static char *_version = NULL;
@@@ -672,10 -828,10 +672,10 @@@ const char 
  get_version(void)
  {
    if (_version == NULL) {
 -    if (strlen(tor_svn_revision)) {
 -      size_t len = strlen(VERSION)+strlen(tor_svn_revision)+8;
 +    if (strlen(tor_git_revision)) {
 +      size_t len = strlen(VERSION)+strlen(tor_git_revision)+16;
        _version = tor_malloc(len);
 -      tor_snprintf(_version, len, "%s (r%s)", VERSION, tor_svn_revision);
 +      tor_snprintf(_version, len, "%s (git-%s)", VERSION, tor_git_revision);
      } else {
        _version = tor_strdup(VERSION);
      }
@@@ -688,10 -844,8 +688,10 @@@
  static void
  or_options_free(or_options_t *options)
  {
 -  if (options->_ExcludeExitNodesUnion)
 -    routerset_free(options->_ExcludeExitNodesUnion);
 +  if (!options)
 +    return;
 +
 +  routerset_free(options->_ExcludeExitNodesUnion);
    config_free(&options_format, options);
  }
  
@@@ -700,72 -854,43 +700,72 @@@
  void
  config_free_all(void)
  {
 -  if (global_options) {
 -    or_options_free(global_options);
 -    global_options = NULL;
 -  }
 -  if (global_state) {
 -    config_free(&state_format, global_state);
 -    global_state = NULL;
 -  }
 -  if (global_cmdline_options) {
 -    config_free_lines(global_cmdline_options);
 -    global_cmdline_options = NULL;
 -  }
 +  or_options_free(global_options);
 +  global_options = NULL;
 +
 +  config_free(&state_format, global_state);
 +  global_state = NULL;
 +
 +  config_free_lines(global_cmdline_options);
 +  global_cmdline_options = NULL;
 +
    tor_free(torrc_fname);
    tor_free(_version);
    tor_free(global_dirfrontpagecontents);
  }
  
 -/** If options->SafeLogging is on, return a not very useful string,
 - * else return address.
 +/** Make <b>address</b> -- a piece of information related to our operation as
 + * a client -- safe to log according to the settings in options->SafeLogging,
 + * and return it.
 + *
 + * (We return "[scrubbed]" if SafeLogging is "1", and address otherwise.)
 + */
 +const char *
 +safe_str_client(const char *address)
 +{
 +  tor_assert(address);
 +  if (get_options()->_SafeLogging == SAFELOG_SCRUB_ALL)
 +    return "[scrubbed]";
 +  else
 +    return address;
 +}
 +
 +/** Make <b>address</b> -- a piece of information of unspecified sensitivity
 + * -- safe to log according to the settings in options->SafeLogging, and
 + * return it.
 + *
 + * (We return "[scrubbed]" if SafeLogging is anything besides "0", and address
 + * otherwise.)
   */
  const char *
  safe_str(const char *address)
  {
    tor_assert(address);
 -  if (get_options()->SafeLogging)
 +  if (get_options()->_SafeLogging != SAFELOG_SCRUB_NONE)
      return "[scrubbed]";
    else
      return address;
  }
  
 +/** Equivalent to escaped(safe_str_client(address)).  See reentrancy note on
 + * escaped(): don't use this outside the main thread, or twice in the same
 + * log statement. */
 +const char *
 +escaped_safe_str_client(const char *address)
 +{
 +  if (get_options()->_SafeLogging == SAFELOG_SCRUB_ALL)
 +    return "[scrubbed]";
 +  else
 +    return escaped(address);
 +}
 +
  /** Equivalent to escaped(safe_str(address)).  See reentrancy note on
   * escaped(): don't use this outside the main thread, or twice in the same
   * log statement. */
  const char *
  escaped_safe_str(const char *address)
  {
 -  if (get_options()->SafeLogging)
 +  if (get_options()->_SafeLogging != SAFELOG_SCRUB_NONE)
      return "[scrubbed]";
    else
      return escaped(address);
@@@ -964,12 -1089,10 +964,12 @@@ options_act_reversible(or_options_t *ol
      }
  
      /* Launch the listeners.  (We do this before we setuid, so we can bind to
 -     * ports under 1024.) */
 -    if (retry_all_listeners(replaced_listeners, new_listeners) < 0) {
 -      *msg = tor_strdup("Failed to bind one of the listener ports.");
 -      goto rollback;
 +     * ports under 1024.)  We don't want to rebind if we're hibernating. */
 +    if (!we_are_hibernating()) {
 +      if (retry_all_listeners(replaced_listeners, new_listeners) < 0) {
 +        *msg = tor_strdup("Failed to bind one of the listener ports.");
 +        goto rollback;
 +      }
      }
    }
  
@@@ -983,15 -1106,6 +983,15 @@@
    }
  #endif
  
 +  /* Attempt to lock all current and future memory with mlockall() only once */
 +  if (options->DisableAllSwap) {
 +    if (tor_mlockall() == -1) {
 +      *msg = tor_strdup("DisableAllSwap failure. Do you have proper "
 +                        "permissions?");
 +      goto done;
 +    }
 +  }
 +
    /* Setuid/setgid as appropriate */
    if (options->User) {
      if (switch_id(options->User) != 0) {
@@@ -1004,9 -1118,11 +1004,9 @@@
    /* Ensure data directory is private; create if possible. */
    if (check_private_dir(options->DataDirectory,
                          running_tor ? CPD_CREATE : CPD_CHECK)<0) {
 -    char buf[1024];
 -    int tmp = tor_snprintf(buf, sizeof(buf),
 +    tor_asprintf(msg,
                "Couldn't access/create private data directory \"%s\"",
                options->DataDirectory);
 -    *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
      goto done;
      /* No need to roll back, since you can't change the value. */
    }
@@@ -1017,8 -1133,10 +1017,8 @@@
      tor_snprintf(fn, len, "%s"PATH_SEPARATOR"cached-status",
                   options->DataDirectory);
      if (check_private_dir(fn, running_tor ? CPD_CREATE : CPD_CHECK) < 0) {
 -      char buf[1024];
 -      int tmp = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
                  "Couldn't access/create private data directory \"%s\"", fn);
 -      *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
        tor_free(fn);
        goto done;
      }
@@@ -1118,6 -1236,7 +1118,6 @@@ get_effective_bwrate(or_options_t *opti
      bw = options->MaxAdvertisedBandwidth;
    if (options->RelayBandwidthRate > 0 && bw > options->RelayBandwidthRate)
      bw = options->RelayBandwidthRate;
 -
    /* ensure_bandwidth_cap() makes sure that this cast can't overflow. */
    return (uint32_t)bw;
  }
@@@ -1195,14 -1314,14 +1195,14 @@@ options_act(or_options_t *old_options
      return 0;
  
    /* Finish backgrounding the process */
 -  if (running_tor && options->RunAsDaemon) {
 +  if (options->RunAsDaemon) {
      /* We may be calling this for the n'th time (on SIGHUP), but it's safe. */
      finish_daemon(options->DataDirectory);
    }
  
    /* Write our PID to the PID file. If we do not have write permissions we
     * will log a warning */
 -  if (running_tor && options->PidFile)
 +  if (options->PidFile)
      write_pidfile(options->PidFile);
  
    /* Register addressmap directives */
@@@ -1235,78 -1354,30 +1235,78 @@@
    if (accounting_is_enabled(options))
      configure_accounting(time(NULL));
  
 +  /* parse RefuseUnknownExits tristate */
 +  if (!strcmp(options->RefuseUnknownExits, "0"))
 +    options->RefuseUnknownExits_ = 0;
 +  else if (!strcmp(options->RefuseUnknownExits, "1"))
 +    options->RefuseUnknownExits_ = 1;
 +  else if (!strcmp(options->RefuseUnknownExits, "auto"))
 +    options->RefuseUnknownExits_ = -1;
 +  else {
 +    /* Should have caught this in options_validate */
 +    return -1;
 +  }
 +
 +  /* Change the cell EWMA settings */
 +  cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus());
 +
    /* Check for transitions that need action. */
    if (old_options) {
 -    if (options->UseEntryGuards && !old_options->UseEntryGuards) {
 +    if ((options->UseEntryGuards && !old_options->UseEntryGuards) ||
 +        (options->ExcludeNodes &&
 +         !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes)) ||
 +        (options->ExcludeExitNodes &&
 +         !routerset_equal(old_options->ExcludeExitNodes,
 +                          options->ExcludeExitNodes)) ||
 +        (options->EntryNodes &&
 +         !routerset_equal(old_options->EntryNodes, options->EntryNodes)) ||
 +        (options->ExitNodes &&
 +         !routerset_equal(old_options->ExitNodes, options->ExitNodes)) ||
 +        options->StrictNodes != old_options->StrictNodes) {
        log_info(LD_CIRC,
 -               "Switching to entry guards; abandoning previous circuits");
 +               "Changed to using entry guards, or changed preferred or "
 +               "excluded node lists. Abandoning previous circuits.");
        circuit_mark_all_unused_circs();
        circuit_expire_all_dirty_circs();
      }
  
 +/* How long should we delay counting bridge stats after becoming a bridge?
 + * We use this so we don't count people who used our bridge thinking it is
 + * a relay. If you change this, don't forget to change the log message
 + * below. It's 4 hours (the time it takes to stop being used by clients)
 + * plus some extra time for clock skew. */
 +#define RELAY_BRIDGE_STATS_DELAY (6 * 60 * 60)
 +
      if (! bool_eq(options->BridgeRelay, old_options->BridgeRelay)) {
 -      log_info(LD_GENERAL, "Bridge status changed.  Forgetting GeoIP stats.");
 -      geoip_remove_old_clients(time(NULL)+(2*60*60));
 +      int was_relay = 0;
 +      if (options->BridgeRelay) {
 +        time_t int_start = time(NULL);
 +        if (old_options->ORPort == options->ORPort) {
 +          int_start += RELAY_BRIDGE_STATS_DELAY;
 +          was_relay = 1;
 +        }
 +        geoip_bridge_stats_init(int_start);
 +        log_info(LD_CONFIG, "We are acting as a bridge now.  Starting new "
 +                 "GeoIP stats interval%s.", was_relay ? " in 6 "
 +                 "hours from now" : "");
 +      } else {
 +        geoip_bridge_stats_term();
 +        log_info(LD_GENERAL, "We are no longer acting as a bridge.  "
 +                 "Forgetting GeoIP stats.");
 +      }
      }
  
      if (options_transition_affects_workers(old_options, options)) {
        log_info(LD_GENERAL,
                 "Worker-related options changed. Rotating workers.");
 +
 +      if (init_keys() < 0) {
 +        log_warn(LD_BUG,"Error initializing keys; exiting");
 +        return -1;
 +      }
        if (server_mode(options) && !server_mode(old_options)) {
 -        if (init_keys() < 0) {
 -          log_warn(LD_BUG,"Error initializing keys; exiting");
 -          return -1;
 -        }
          ip_address_changed(0);
 -        if (has_completed_circuit || !any_predicted_circuits(time(NULL)))
 +        if (can_complete_circuit || !any_predicted_circuits(time(NULL)))
            inform_testing_reachability();
        }
        cpuworkers_rotate();
@@@ -1319,10 -1390,6 +1319,10 @@@
  
      if (options->V3AuthoritativeDir && !old_options->V3AuthoritativeDir)
        init_keys();
 +
 +    if (options->PerConnBWRate != old_options->PerConnBWRate ||
 +        options->PerConnBWBurst != old_options->PerConnBWBurst)
 +      connection_or_update_token_buckets(get_connection_array(), options);
    }
  
    /* Maybe load geoip file */
@@@ -1345,63 -1412,13 +1345,63 @@@
      geoip_load_file(actual_fname, options);
      tor_free(actual_fname);
    }
 -#ifdef ENABLE_GEOIP_STATS
 -  log_warn(LD_CONFIG, "We are configured to measure GeoIP statistics, but "
 -           "the way these statistics are measured has changed "
 -           "significantly in later versions of Tor. The results may not be "
 -           "as expected if you are used to later versions.  Be sure you "
 -           "know what you are doing.");
 -#endif
 +
 +  if (options->DirReqStatistics && !geoip_is_loaded()) {
 +    /* Check if GeoIP database could be loaded. */
 +    log_warn(LD_CONFIG, "Configured to measure directory request "
 +             "statistics, but no GeoIP database found!");
 +    return -1;
 +  }
 +
 +  if (options->EntryStatistics) {
 +    if (should_record_bridge_info(options)) {
 +      /* Don't allow measuring statistics on entry guards when configured
 +       * as bridge. */
 +      log_warn(LD_CONFIG, "Bridges cannot be configured to measure "
 +               "additional GeoIP statistics as entry guards.");
 +      return -1;
 +    } else if (!geoip_is_loaded()) {
 +      /* Check if GeoIP database could be loaded. */
 +      log_warn(LD_CONFIG, "Configured to measure entry node statistics, "
 +               "but no GeoIP database found!");
 +      return -1;
 +    }
 +  }
 +
 +  if (options->CellStatistics || options->DirReqStatistics ||
 +      options->EntryStatistics || options->ExitPortStatistics) {
 +    time_t now = time(NULL);
 +    if ((!old_options || !old_options->CellStatistics) &&
 +        options->CellStatistics)
 +      rep_hist_buffer_stats_init(now);
 +    if ((!old_options || !old_options->DirReqStatistics) &&
 +        options->DirReqStatistics)
 +      geoip_dirreq_stats_init(now);
 +    if ((!old_options || !old_options->EntryStatistics) &&
 +        options->EntryStatistics)
 +      geoip_entry_stats_init(now);
 +    if ((!old_options || !old_options->ExitPortStatistics) &&
 +        options->ExitPortStatistics)
 +      rep_hist_exit_stats_init(now);
 +    if (!old_options)
 +      log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
 +                 "the *-stats files that will first be written to the "
 +                 "data directory in 24 hours from now.");
 +  }
 +
 +  if (old_options && old_options->CellStatistics &&
 +      !options->CellStatistics)
 +    rep_hist_buffer_stats_term();
 +  if (old_options && old_options->DirReqStatistics &&
 +      !options->DirReqStatistics)
 +    geoip_dirreq_stats_term();
 +  if (old_options && old_options->EntryStatistics &&
 +      !options->EntryStatistics)
 +    geoip_entry_stats_term();
 +  if (old_options && old_options->ExitPortStatistics &&
 +      !options->ExitPortStatistics)
 +    rep_hist_exit_stats_term();
 +
    /* Check if we need to parse and add the EntryNodes config option. */
    if (options->EntryNodes &&
        (!old_options ||
@@@ -1474,10 -1491,7 +1474,10 @@@ expand_abbrev(config_format_t *fmt, con
                   fmt->abbrevs[i].abbreviated,
                   fmt->abbrevs[i].full);
        }
 -      return fmt->abbrevs[i].full;
 +      /* Keep going through the list in case we want to rewrite it more.
 +       * (We could imagine recursing here, but I don't want to get the
 +       * user into an infinite loop if we craft our list wrong.) */
 +      option = fmt->abbrevs[i].full;
      }
    }
    return option;
@@@ -1522,10 -1536,7 +1522,10 @@@ config_get_commandlines(int argc, char 
      *new = tor_malloc_zero(sizeof(config_line_t));
      s = argv[i];
  
 -    while (*s == '-')
 +    /* Each keyword may be prefixed with one or two dashes. */
 +    if (*s == '-')
 +      s++;
 +    if (*s == '-')
        s++;
  
      (*new)->key = tor_strdup(expand_abbrev(&options_format, s, 1, 1));
@@@ -1617,6 -1628,19 +1617,6 @@@ config_free_lines(config_line_t *front
    }
  }
  
 -/** Return the description for a given configuration variable, or NULL if no
 - * description exists. */
 -static const char *
 -config_find_description(config_format_t *fmt, const char *name)
 -{
 -  int i;
 -  for (i=0; fmt->descriptions[i].name; ++i) {
 -    if (!strcasecmp(name, fmt->descriptions[i].name))
 -      return fmt->descriptions[i].description;
 -  }
 -  return NULL;
 -}
 -
  /** If <b>key</b> is a configuration option, return the corresponding
   * config_var_t.  Otherwise, if <b>key</b> is a non-standard abbreviation,
   * warn, and return the corresponding config_var_t.  Otherwise return NULL.
@@@ -1647,16 -1671,6 +1647,16 @@@ config_find_option(config_format_t *fmt
    return NULL;
  }
  
 +/** Return the number of option entries in <b>fmt</b>. */
 +static int
 +config_count_options(config_format_t *fmt)
 +{
 +  int i;
 +  for (i=0; fmt->vars[i].name; ++i)
 +    ;
 +  return i;
 +}
 +
  /*
   * Functions to assign config options.
   */
@@@ -1670,7 -1684,8 +1670,7 @@@ static in
  config_assign_value(config_format_t *fmt, or_options_t *options,
                      config_line_t *c, char **msg)
  {
 -  int i, r, ok;
 -  char buf[1024];
 +  int i, ok;
    config_var_t *var;
    void *lvalue;
  
@@@ -1686,9 -1701,10 +1686,9 @@@
    case CONFIG_TYPE_UINT:
      i = (int)tor_parse_long(c->value, 10, 0, INT_MAX, &ok, NULL);
      if (!ok) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
            "Int keyword '%s %s' is malformed or out of bounds.",
            c->key, c->value);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
        return -1;
      }
      *(int *)lvalue = i;
@@@ -1697,9 -1713,10 +1697,9 @@@
    case CONFIG_TYPE_INTERVAL: {
      i = config_parse_interval(c->value, &ok);
      if (!ok) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
            "Interval '%s %s' is malformed or out of bounds.",
            c->key, c->value);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
        return -1;
      }
      *(int *)lvalue = i;
@@@ -1709,9 -1726,10 +1709,9 @@@
    case CONFIG_TYPE_MEMUNIT: {
      uint64_t u64 = config_parse_memunit(c->value, &ok);
      if (!ok) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
            "Value '%s %s' is malformed or out of bounds.",
            c->key, c->value);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
        return -1;
      }
      *(uint64_t *)lvalue = u64;
@@@ -1721,9 -1739,10 +1721,9 @@@
    case CONFIG_TYPE_BOOL:
      i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL);
      if (!ok) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
            "Boolean '%s %s' expects 0 or 1.",
            c->key, c->value);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
        return -1;
      }
      *(int *)lvalue = i;
@@@ -1741,8 -1760,9 +1741,8 @@@
  
    case CONFIG_TYPE_ISOTIME:
      if (parse_iso_time(c->value, (time_t *)lvalue)) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
            "Invalid time '%s' for keyword '%s'", c->value, c->key);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
        return -1;
      }
      break;
@@@ -1753,8 -1773,9 +1753,8 @@@
      }
      *(routerset_t**)lvalue = routerset_new();
      if (routerset_parse(*(routerset_t**)lvalue, c->value, c->key)<0) {
 -      tor_snprintf(buf, sizeof(buf), "Invalid exit list '%s' for option '%s'",
 +      tor_asprintf(msg, "Invalid exit list '%s' for option '%s'",
                     c->value, c->key);
 -      *msg = tor_strdup(buf);
        return -1;
      }
      break;
@@@ -1779,8 -1800,9 +1779,8 @@@
      log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key);
      break;
    case CONFIG_TYPE_LINELIST_V:
 -    r = tor_snprintf(buf, sizeof(buf),
 +    tor_asprintf(msg,
          "You may not provide a value for virtual option '%s'", c->key);
 -    *msg = tor_strdup(r >= 0 ? buf : "internal error");
      return -1;
    default:
      tor_assert(0);
@@@ -1801,7 -1823,7 +1801,7 @@@
  static int
  config_assign_line(config_format_t *fmt, or_options_t *options,
                     config_line_t *c, int use_defaults,
 -                   int clear_first, char **msg)
 +                   int clear_first, bitarray_t *options_seen, char **msg)
  {
    config_var_t *var;
  
@@@ -1816,12 -1838,13 +1816,12 @@@
        config_line_append((config_line_t**)lvalue, c->key, c->value);
        return 0;
      } else {
 -      char buf[1024];
 -      int tmp = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
                  "Unknown option '%s'.  Failing.", c->key);
        return -1;
      }
    }
 +
    /* Put keyword into canonical case. */
    if (strcmp(var->name, c->key)) {
      tor_free(c->key);
@@@ -1844,18 -1867,6 +1844,18 @@@
      return 0;
    }
  
 +  if (options_seen && (var->type != CONFIG_TYPE_LINELIST &&
 +                       var->type != CONFIG_TYPE_LINELIST_S)) {
 +    /* We're tracking which options we've seen, and this option is not
 +     * supposed to occur more than once. */
 +    int var_index = (int)(var - fmt->vars);
 +    if (bitarray_is_set(options_seen, var_index)) {
 +      log_warn(LD_CONFIG, "Option '%s' used more than once; all but the last "
 +               "value will be ignored.", var->name);
 +    }
 +    bitarray_set(options_seen, var_index);
 +  }
 +
    if (config_assign_value(fmt, options, c, msg) < 0)
      return -2;
    return 0;
@@@ -1956,6 -1967,7 +1956,6 @@@ get_assigned_option(config_format_t *fm
  {
    config_var_t *var;
    const void *value;
 -  char buf[32];
    config_line_t *result;
    tor_assert(options && key);
  
@@@ -1996,16 -2008,19 +1996,16 @@@
      case CONFIG_TYPE_UINT:
        /* 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);
 +      tor_asprintf(&result->value, "%d", *(int*)value);
        escape_val = 0; /* Can't need escape. */
        break;
      case CONFIG_TYPE_MEMUNIT:
 -      tor_snprintf(buf, sizeof(buf), U64_FORMAT,
 +      tor_asprintf(&result->value, U64_FORMAT,
                     U64_PRINTF_ARG(*(uint64_t*)value));
        escape_val = 0; /* Can't need escape. */
        break;
      case CONFIG_TYPE_DOUBLE:
 -      tor_snprintf(buf, sizeof(buf), "%f", *(double*)value);
 -      result->value = tor_strdup(buf);
 +      tor_asprintf(&result->value, "%f", *(double*)value);
        escape_val = 0; /* Can't need escape. */
        break;
      case CONFIG_TYPE_BOOL:
@@@ -2124,8 -2139,6 +2124,8 @@@ config_assign(config_format_t *fmt, voi
                int use_defaults, int clear_first, char **msg)
  {
    config_line_t *p;
 +  bitarray_t *options_seen;
 +  const int n_options = config_count_options(fmt);
  
    CHECK(fmt, options);
  
@@@ -2145,18 -2158,14 +2145,18 @@@
        config_reset_line(fmt, options, p->key, use_defaults);
    }
  
 +  options_seen = bitarray_init_zero(n_options);
    /* pass 3: assign. */
    while (list) {
      int r;
      if ((r=config_assign_line(fmt, options, list, use_defaults,
 -                              clear_first, msg)))
 +                              clear_first, options_seen, msg))) {
 +      bitarray_free(options_seen);
        return r;
 +    }
      list = list->next;
    }
 +  bitarray_free(options_seen);
    return 0;
  }
  
@@@ -2299,10 -2308,20 +2299,10 @@@ list_torrc_options(void
    smartlist_t *lines = smartlist_create();
    for (i = 0; _option_vars[i].name; ++i) {
      config_var_t *var = &_option_vars[i];
      if (var->type == CONFIG_TYPE_OBSOLETE ||
          var->type == CONFIG_TYPE_LINELIST_V)
        continue;
 -    desc = config_find_description(&options_format, var->name);
      printf("%s\n", var->name);
 -    if (desc) {
 -      wrap_string(lines, desc, 76, "    ", "    ");
 -      SMARTLIST_FOREACH(lines, char *, cp, {
 -          printf("%s", cp);
 -          tor_free(cp);
 -        });
 -      smartlist_clear(lines);
 -    }
    }
    smartlist_free(lines);
  }
@@@ -2321,7 -2340,7 +2321,7 @@@ resolve_my_address(int warn_severity, o
                     uint32_t *addr_out, char **hostname_out)
  {
    struct in_addr in;
 -  uint32_t addr;
 +  uint32_t addr; /* host order */
    char hostname[256];
    int explicit_ip=1;
    int explicit_hostname=1;
@@@ -2351,8 -2370,8 +2351,8 @@@
    if (tor_inet_aton(hostname, &in) == 0) {
      /* then we have to resolve it */
      explicit_ip = 0;
 -    if (tor_lookup_hostname(hostname, &addr)) {
 -      uint32_t interface_ip;
 +    if (tor_lookup_hostname(hostname, &addr)) { /* failed to resolve */
 +      uint32_t interface_ip; /* host order */
  
        if (explicit_hostname) {
          log_fn(warn_severity, LD_CONFIG,
@@@ -2373,7 -2392,7 +2373,7 @@@
        log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for "
               "local interface. Using that.", tmpbuf);
        strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
 -    } else {
 +    } else { /* resolved hostname into addr */
        in.s_addr = htonl(addr);
  
        if (!explicit_hostname &&
@@@ -2547,10 -2566,7 +2547,10 @@@ config_free(config_format_t *fmt, void 
  {
    int i;
  
 -  tor_assert(options);
 +  if (!options)
 +    return;
 +
 +  tor_assert(fmt);
  
    for (i=0; fmt->vars[i].name; ++i)
      option_clear(fmt, options, &(fmt->vars[i]));
@@@ -2652,8 -2668,6 +2652,8 @@@ is_listening_on_low_port(uint16_t port_
                           const config_line_t *listen_options)
  {
  #ifdef MS_WINDOWS
 +  (void) port_option;
 +  (void) listen_options;
    return 0; /* No port is too low for windows. */
  #else
    const config_line_t *l;
@@@ -2703,6 -2717,7 +2703,6 @@@ config_dump(config_format_t *fmt, void 
    config_line_t *line, *assigned;
    char *result;
    int i;
 -  const char *desc;
    char *msg = NULL;
  
    defaults = config_alloc(fmt);
@@@ -2730,13 -2745,24 +2730,13 @@@
               option_is_same(fmt, options, defaults, fmt->vars[i].name))
        comment_option = 1;
  
 -    desc = config_find_description(fmt, fmt->vars[i].name);
      line = assigned = get_assigned_option(fmt, options, fmt->vars[i].name, 1);
  
 -    if (line && desc) {
 -      /* Only dump the description if there's something to describe. */
 -      wrap_string(elements, desc, 78, "# ", "# ");
 -    }
 -
      for (; line; line = line->next) {
 -      size_t len = strlen(line->key) + strlen(line->value) + 5;
        char *tmp;
 -      tmp = tor_malloc(len);
 -      if (tor_snprintf(tmp, len, "%s%s %s\n",
 -                       comment_option ? "# " : "",
 -                       line->key, line->value)<0) {
 -        log_err(LD_BUG,"Internal error writing option value");
 -        tor_assert(0);
 -      }
 +      tor_asprintf(&tmp, "%s%s %s\n",
 +                   comment_option ? "# " : "",
 +                   line->key, line->value);
        smartlist_add(elements, tmp);
      }
      config_free_lines(assigned);
@@@ -2745,8 -2771,13 +2745,8 @@@
    if (fmt->extra) {
      line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->var_offset);
      for (; line; line = line->next) {
 -      size_t len = strlen(line->key) + strlen(line->value) + 3;
        char *tmp;
 -      tmp = tor_malloc(len);
 -      if (tor_snprintf(tmp, len, "%s %s\n", line->key, line->value)<0) {
 -        log_err(LD_BUG,"Internal error writing option value");
 -        tor_assert(0);
 -      }
 +      tor_asprintf(&tmp, "%s %s\n", line->key, line->value);
        smartlist_add(elements, tmp);
      }
    }
@@@ -2762,7 -2793,7 +2762,7 @@@
   * the configuration in <b>options</b>.  If <b>minimal</b> is true, do not
   * include options that are the same as Tor's defaults.
   */
 -static char *
 +char *
  options_dump(or_options_t *options, int minimal)
  {
    return config_dump(&options_format, options, minimal, 0);
@@@ -2775,6 -2806,7 +2775,6 @@@ static in
  validate_ports_csv(smartlist_t *sl, const char *name, char **msg)
  {
    int i;
 -  char buf[1024];
    tor_assert(name);
  
    if (!sl)
@@@ -2784,7 -2816,9 +2784,7 @@@
    {
      i = atoi(cp);
      if (i < 1 || i > 65535) {
 -      int r = tor_snprintf(buf, sizeof(buf),
 -                           "Port '%s' out of range in %s", cp, name);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
 +      tor_asprintf(msg, "Port '%s' out of range in %s", cp, name);
        return -1;
      }
    });
@@@ -2798,15 -2832,18 +2798,15 @@@
  static int
  ensure_bandwidth_cap(uint64_t *value, const char *desc, char **msg)
  {
 -  int r;
 -  char buf[1024];
    if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) {
      /* This handles an understandable special case where somebody says "2gb"
       * whereas our actual maximum is 2gb-1 (INT_MAX) */
      --*value;
    }
    if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) {
 -    r = tor_snprintf(buf, sizeof(buf), "%s ("U64_FORMAT") must be at most %d",
 -                     desc, U64_PRINTF_ARG(*value),
 -                     ROUTER_MAX_DECLARED_BANDWIDTH);
 -    *msg = tor_strdup(r >= 0 ? buf : "internal error");
 +    tor_asprintf(msg, "%s ("U64_FORMAT") must be at most %d",
 +                 desc, U64_PRINTF_ARG(*value),
 +                 ROUTER_MAX_DECLARED_BANDWIDTH);
      return -1;
    }
    return 0;
@@@ -2857,14 -2894,15 +2857,14 @@@ compute_publishserverdescriptor(or_opti
  /** Highest allowable value for RendPostPeriod. */
  #define MAX_DIR_PERIOD (MIN_ONION_KEY_LIFETIME/2)
  
 -/** Lowest allowable value for CircuitBuildTimeout; values too low will
 - * increase network load because of failing connections being retried, and
 - * might prevent users from connecting to the network at all. */
 -#define MIN_CIRCUIT_BUILD_TIMEOUT 30
 -
  /** Lowest allowable value for MaxCircuitDirtiness; if this is too low, Tor
   * will generate too many circuits and potentially overload the network. */
  #define MIN_MAX_CIRCUIT_DIRTINESS 10
  
 +/** Lowest allowable value for CircuitStreamTimeout; if this is too low, Tor
 + * will generate too many circuits and potentially overload the network. */
 +#define MIN_CIRCUIT_STREAM_TIMEOUT 10
 +
  /** Return 0 if every setting in <b>options</b> is reasonable, and a
   * permissible transition from <b>old_options</b>. Else return -1.
   * Should have no side effects, except for normalizing the contents of
@@@ -2881,9 -2919,10 +2881,9 @@@ static in
  options_validate(or_options_t *old_options, or_options_t *options,
                   int from_setconf, char **msg)
  {
 -  int i, r;
 +  int i;
    config_line_t *cl;
    const char *uname = get_uname();
 -  char buf[1024];
  #define REJECT(arg) \
    STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
  #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
@@@ -2900,7 -2939,7 +2900,7 @@@
         !strcmpstart(uname, "Windows Me"))) {
      log(LOG_WARN, LD_CONFIG, "Tor is running as a server, but you are "
          "running %s; this probably won't work. See "
 -        "http://wiki.noreply.org/noreply/TheOnionRouter/TorFAQ#ServerOS "
 +        "https://wiki.torproject.org/TheOnionRouter/TorFAQ#ServerOS "
          "for details.", uname);
    }
  
@@@ -2919,8 -2958,8 +2919,8 @@@
    if (options->TransPort == 0 && options->TransListenAddress != NULL)
      REJECT("TransPort must be defined if TransListenAddress is defined.");
  
 -  if (options->NatdPort == 0 && options->NatdListenAddress != NULL)
 -    REJECT("NatdPort must be defined if NatdListenAddress is defined.");
 +  if (options->NATDPort == 0 && options->NATDListenAddress != NULL)
 +    REJECT("NATDPort must be defined if NATDListenAddress is defined.");
  
    /* Don't gripe about SocksPort 0 with SocksListenAddress set; a standard
     * configuration does this. */
@@@ -2939,8 -2978,8 +2939,8 @@@
        old = old_options ? old_options->TransListenAddress : NULL;
        tp = "transparent proxy";
      } else {
 -      opt = options->NatdListenAddress;
 -      old = old_options ? old_options->NatdListenAddress : NULL;
 +      opt = options->NATDListenAddress;
 +      old = old_options ? old_options->NATDListenAddress : NULL;
        tp = "natd proxy";
      }
  
@@@ -2978,9 -3017,10 +2978,9 @@@
      }
    } else {
      if (!is_legal_nickname(options->Nickname)) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
            "Nickname '%s' is wrong length or contains illegal characters.",
            options->Nickname);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
        return -1;
      }
    }
@@@ -2997,6 -3037,14 +2997,6 @@@
    if (options_init_logs(options, 1)<0) /* Validate the log(s) */
      REJECT("Failed to validate Log options. See logs for details.");
  
 -  if (options->NoPublish) {
 -    log(LOG_WARN, LD_CONFIG,
 -        "NoPublish is obsolete. Use PublishServerDescriptor instead.");
 -    SMARTLIST_FOREACH(options->PublishServerDescriptor, char *, s,
 -                      tor_free(s));
 -    smartlist_clear(options->PublishServerDescriptor);
 -  }
 -
    if (authdir_mode(options)) {
      /* confirm that our address isn't broken, so we can complain now */
      uint32_t tmp;
@@@ -3004,12 -3052,6 +3004,12 @@@
        REJECT("Failed to resolve/guess local address. See logs for details.");
    }
  
 +  if (strcmp(options->RefuseUnknownExits, "0") &&
 +      strcmp(options->RefuseUnknownExits, "1") &&
 +      strcmp(options->RefuseUnknownExits, "auto")) {
 +    REJECT("RefuseUnknownExits must be 0, 1, or auto");
 +  }
 +
  #ifndef MS_WINDOWS
    if (options->RunAsDaemon && torrc_fname && path_is_relative(torrc_fname))
      REJECT("Can't use a relative path to torrc when RunAsDaemon is set.");
@@@ -3024,14 -3066,14 +3024,14 @@@
    if (options->TransPort < 0 || options->TransPort > 65535)
      REJECT("TransPort option out of bounds.");
  
 -  if (options->NatdPort < 0 || options->NatdPort > 65535)
 -    REJECT("NatdPort option out of bounds.");
 +  if (options->NATDPort < 0 || options->NATDPort > 65535)
 +    REJECT("NATDPort option out of bounds.");
  
    if (options->SocksPort == 0 && options->TransPort == 0 &&
 -      options->NatdPort == 0 && options->ORPort == 0 &&
 +      options->NATDPort == 0 && options->ORPort == 0 &&
        options->DNSPort == 0 && !options->RendConfigLines)
      log(LOG_WARN, LD_CONFIG,
 -        "SocksPort, TransPort, NatdPort, DNSPort, and ORPort are all "
 +        "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
          "undefined, and there aren't any hidden services configured.  "
          "Tor will still run, but probably won't do anything.");
  
@@@ -3065,11 -3107,19 +3065,11 @@@
      routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeNodes);
    }
  
 -  if (options->StrictExitNodes &&
 -      (!options->ExitNodes) &&
 -      (!old_options ||
 -       (old_options->StrictExitNodes != options->StrictExitNodes) ||
 -       (!routerset_equal(old_options->ExitNodes,options->ExitNodes))))
 -    COMPLAIN("StrictExitNodes set, but no ExitNodes listed.");
 -
 -  if (options->StrictEntryNodes &&
 -      (!options->EntryNodes) &&
 -      (!old_options ||
 -       (old_options->StrictEntryNodes != options->StrictEntryNodes) ||
 -       (!routerset_equal(old_options->EntryNodes,options->EntryNodes))))
 -    COMPLAIN("StrictEntryNodes set, but no EntryNodes listed.");
 +  if (options->ExcludeNodes && options->StrictNodes) {
 +    COMPLAIN("You have asked to exclude certain relays from all positions "
 +             "in your circuits. Expect hidden services and other Tor "
 +             "features to be broken in unpredictable ways.");
 +  }
  
    if (options->EntryNodes && !routerset_is_list(options->EntryNodes)) {
      /* XXXX fix this; see entry_guards_prepend_from_config(). */
@@@ -3107,10 -3157,6 +3107,10 @@@
            options->V3AuthoritativeDir))
        REJECT("AuthoritativeDir is set, but none of "
               "(Bridge/HS/V1/V2/V3)AuthoritativeDir is set.");
 +    /* If we have a v3bandwidthsfile and it's broken, complain on startup */
 +    if (options->V3BandwidthsFile && !old_options) {
 +      dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL);
 +    }
    }
  
    if (options->AuthoritativeDir && !options->DirPort)
@@@ -3122,14 -3168,15 +3122,14 @@@
    if (options->AuthoritativeDir && options->ClientOnly)
      REJECT("Running as authoritative directory, but ClientOnly also set.");
  
 -  if (options->HSAuthorityRecordStats && !options->HSAuthoritativeDir)
 -    REJECT("HSAuthorityRecordStats is set but we're not running as "
 -           "a hidden service authority.");
 +  if (options->FetchDirInfoExtraEarly && !options->FetchDirInfoEarly)
 +    REJECT("FetchDirInfoExtraEarly requires that you also set "
 +           "FetchDirInfoEarly");
  
    if (options->ConnLimit <= 0) {
 -    r = tor_snprintf(buf, sizeof(buf),
 +    tor_asprintf(msg,
          "ConnLimit must be greater than 0, but was set to %d",
          options->ConnLimit);
 -    *msg = tor_strdup(r >= 0 ? buf : "internal error");
      return -1;
    }
  
@@@ -3248,29 -3295,18 +3248,29 @@@
          else if (!strcasecmp(cp, "rendezvous"))
            options->_AllowInvalid |= ALLOW_INVALID_RENDEZVOUS;
          else {
 -          r = tor_snprintf(buf, sizeof(buf),
 +          tor_asprintf(msg,
                "Unrecognized value '%s' in AllowInvalidNodes", cp);
 -          *msg = tor_strdup(r >= 0 ? buf : "internal error");
            return -1;
          }
        });
    }
  
 +  if (!options->SafeLogging ||
 +      !strcasecmp(options->SafeLogging, "0")) {
 +    options->_SafeLogging = SAFELOG_SCRUB_NONE;
 +  } else if (!strcasecmp(options->SafeLogging, "relay")) {
 +    options->_SafeLogging = SAFELOG_SCRUB_RELAY;
 +  } else if (!strcasecmp(options->SafeLogging, "1")) {
 +    options->_SafeLogging = SAFELOG_SCRUB_ALL;
 +  } else {
 +    tor_asprintf(msg,
 +                     "Unrecognized value '%s' in SafeLogging",
 +                     escaped(options->SafeLogging));
 +    return -1;
 +  }
 +
    if (compute_publishserverdescriptor(options) < 0) {
 -    r = tor_snprintf(buf, sizeof(buf),
 -                     "Unrecognized value in PublishServerDescriptor");
 -    *msg = tor_strdup(r >= 0 ? buf : "internal error");
 +    tor_asprintf(msg, "Unrecognized value in PublishServerDescriptor");
      return -1;
    }
  
@@@ -3283,12 -3319,6 +3283,12 @@@
             "PublishServerDescriptor line.");
    }
  
 +  if (options->BridgeRelay && options->DirPort) {
 +    log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling "
 +             "DirPort");
 +    options->DirPort = 0;
 +  }
 +
    if (options->MinUptimeHidServDirectoryV2 < 0) {
      log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at "
                          "least 0 seconds. Changing to 0.");
@@@ -3296,30 -3326,29 +3296,30 @@@
    }
  
    if (options->RendPostPeriod < MIN_REND_POST_PERIOD) {
 -    log(LOG_WARN,LD_CONFIG,"RendPostPeriod option is too short; "
 -        "raising to %d seconds.", MIN_REND_POST_PERIOD);
 +    log_warn(LD_CONFIG, "RendPostPeriod option is too short; "
 +             "raising to %d seconds.", MIN_REND_POST_PERIOD);
      options->RendPostPeriod = MIN_REND_POST_PERIOD;
    }
  
    if (options->RendPostPeriod > MAX_DIR_PERIOD) {
 -    log(LOG_WARN, LD_CONFIG, "RendPostPeriod is too large; clipping to %ds.",
 -        MAX_DIR_PERIOD);
 +    log_warn(LD_CONFIG, "RendPostPeriod is too large; clipping to %ds.",
 +             MAX_DIR_PERIOD);
      options->RendPostPeriod = MAX_DIR_PERIOD;
    }
  
 -  if (options->CircuitBuildTimeout < MIN_CIRCUIT_BUILD_TIMEOUT) {
 -    log(LOG_WARN, LD_CONFIG, "CircuitBuildTimeout option is too short; "
 -      "raising to %d seconds.", MIN_CIRCUIT_BUILD_TIMEOUT);
 -    options->CircuitBuildTimeout = MIN_CIRCUIT_BUILD_TIMEOUT;
 -  }
 -
    if (options->MaxCircuitDirtiness < MIN_MAX_CIRCUIT_DIRTINESS) {
 -    log(LOG_WARN, LD_CONFIG, "MaxCircuitDirtiness option is too short; "
 -      "raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS);
 +    log_warn(LD_CONFIG, "MaxCircuitDirtiness option is too short; "
 +             "raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS);
      options->MaxCircuitDirtiness = MIN_MAX_CIRCUIT_DIRTINESS;
    }
  
 +  if (options->CircuitStreamTimeout &&
 +      options->CircuitStreamTimeout < MIN_CIRCUIT_STREAM_TIMEOUT) {
 +    log_warn(LD_CONFIG, "CircuitStreamTimeout option is too short; "
 +             "raising to %d seconds.", MIN_CIRCUIT_STREAM_TIMEOUT);
 +    options->CircuitStreamTimeout = MIN_CIRCUIT_STREAM_TIMEOUT;
 +  }
 +
    if (options->KeepalivePeriod < 1)
      REJECT("KeepalivePeriod option must be positive.");
  
@@@ -3338,44 -3367,43 +3338,46 @@@
    if (ensure_bandwidth_cap(&options->RelayBandwidthBurst,
                             "RelayBandwidthBurst", msg) < 0)
      return -1;
 +  if (ensure_bandwidth_cap(&options->PerConnBWRate,
 +                           "PerConnBWRate", msg) < 0)
 +    return -1;
 +  if (ensure_bandwidth_cap(&options->PerConnBWBurst,
 +                           "PerConnBWBurst", msg) < 0)
 +    return -1;
  
+   if (options->RelayBandwidthRate && !options->RelayBandwidthBurst)
+     options->RelayBandwidthBurst = options->RelayBandwidthRate;
+   if (options->RelayBandwidthBurst && !options->RelayBandwidthRate)
+     options->RelayBandwidthRate = options->RelayBandwidthBurst;
+ 
    if (server_mode(options)) {
      if (options->BandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
                         "BandwidthRate is set to %d bytes/second. "
                         "For servers, it must be at least %d.",
                         (int)options->BandwidthRate,
                         ROUTER_REQUIRED_MIN_BANDWIDTH);
        return -1;
      } else if (options->MaxAdvertisedBandwidth <
                 ROUTER_REQUIRED_MIN_BANDWIDTH/2) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
                         "MaxAdvertisedBandwidth is set to %d bytes/second. "
                         "For servers, it must be at least %d.",
                         (int)options->MaxAdvertisedBandwidth,
                         ROUTER_REQUIRED_MIN_BANDWIDTH/2);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
        return -1;
      }
      if (options->RelayBandwidthRate &&
        options->RelayBandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
                         "RelayBandwidthRate is set to %d bytes/second. "
                         "For servers, it must be at least %d.",
                         (int)options->RelayBandwidthRate,
                         ROUTER_REQUIRED_MIN_BANDWIDTH);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
        return -1;
      }
    }
  
-   if (options->RelayBandwidthRate && !options->RelayBandwidthBurst)
-     options->RelayBandwidthBurst = options->RelayBandwidthRate;
- 
    if (options->RelayBandwidthRate > options->RelayBandwidthBurst)
      REJECT("RelayBandwidthBurst must be at least equal "
             "to RelayBandwidthRate.");
@@@ -3393,73 -3421,34 +3395,73 @@@
    if (accounting_parse_options(options, 1)<0)
      REJECT("Failed to parse accounting options. See logs for details.");
  
 -  if (options->HttpProxy) { /* parse it now */
 -    if (parse_addr_port(LOG_WARN, options->HttpProxy, NULL,
 -                        &options->HttpProxyAddr, &options->HttpProxyPort) < 0)
 -      REJECT("HttpProxy failed to parse or resolve. Please fix.");
 -    if (options->HttpProxyPort == 0) { /* give it a default */
 -      options->HttpProxyPort = 80;
 +  if (options->HTTPProxy) { /* parse it now */
 +    if (tor_addr_port_parse(options->HTTPProxy,
 +                        &options->HTTPProxyAddr, &options->HTTPProxyPort) < 0)
 +      REJECT("HTTPProxy failed to parse or resolve. Please fix.");
 +    if (options->HTTPProxyPort == 0) { /* give it a default */
 +      options->HTTPProxyPort = 80;
 +    }
 +  }
 +
 +  if (options->HTTPProxyAuthenticator) {
 +    if (strlen(options->HTTPProxyAuthenticator) >= 48)
 +      REJECT("HTTPProxyAuthenticator is too long (>= 48 chars).");
 +  }
 +
 +  if (options->HTTPSProxy) { /* parse it now */
 +    if (tor_addr_port_parse(options->HTTPSProxy,
 +                        &options->HTTPSProxyAddr, &options->HTTPSProxyPort) <0)
 +      REJECT("HTTPSProxy failed to parse or resolve. Please fix.");
 +    if (options->HTTPSProxyPort == 0) { /* give it a default */
 +      options->HTTPSProxyPort = 443;
      }
    }
  
 -  if (options->HttpProxyAuthenticator) {
 -    if (strlen(options->HttpProxyAuthenticator) >= 48)
 -      REJECT("HttpProxyAuthenticator is too long (>= 48 chars).");
 +  if (options->HTTPSProxyAuthenticator) {
 +    if (strlen(options->HTTPSProxyAuthenticator) >= 48)
 +      REJECT("HTTPSProxyAuthenticator is too long (>= 48 chars).");
    }
  
 -  if (options->HttpsProxy) { /* parse it now */
 -    if (parse_addr_port(LOG_WARN, options->HttpsProxy, NULL,
 -                        &options->HttpsProxyAddr, &options->HttpsProxyPort) <0)
 -      REJECT("HttpsProxy failed to parse or resolve. Please fix.");
 -    if (options->HttpsProxyPort == 0) { /* give it a default */
 -      options->HttpsProxyPort = 443;
 +  if (options->Socks4Proxy) { /* parse it now */
 +    if (tor_addr_port_parse(options->Socks4Proxy,
 +                        &options->Socks4ProxyAddr,
 +                        &options->Socks4ProxyPort) <0)
 +      REJECT("Socks4Proxy failed to parse or resolve. Please fix.");
 +    if (options->Socks4ProxyPort == 0) { /* give it a default */
 +      options->Socks4ProxyPort = 1080;
      }
    }
  
 -  if (options->HttpsProxyAuthenticator) {
 -    if (strlen(options->HttpsProxyAuthenticator) >= 48)
 -      REJECT("HttpsProxyAuthenticator is too long (>= 48 chars).");
 +  if (options->Socks5Proxy) { /* parse it now */
 +    if (tor_addr_port_parse(options->Socks5Proxy,
 +                            &options->Socks5ProxyAddr,
 +                            &options->Socks5ProxyPort) <0)
 +      REJECT("Socks5Proxy failed to parse or resolve. Please fix.");
 +    if (options->Socks5ProxyPort == 0) { /* give it a default */
 +      options->Socks5ProxyPort = 1080;
 +    }
    }
  
 +  if (options->Socks4Proxy && options->Socks5Proxy)
 +    REJECT("You cannot specify both Socks4Proxy and SOCKS5Proxy");
 +
 +  if (options->Socks5ProxyUsername) {
 +    size_t len;
 +
 +    len = strlen(options->Socks5ProxyUsername);
 +    if (len < 1 || len > 255)
 +      REJECT("Socks5ProxyUsername must be between 1 and 255 characters.");
 +
 +    if (!options->Socks5ProxyPassword)
 +      REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
 +
 +    len = strlen(options->Socks5ProxyPassword);
 +    if (len < 1 || len > 255)
 +      REJECT("Socks5ProxyPassword must be between 1 and 255 characters.");
 +  } else if (options->Socks5ProxyPassword)
 +    REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
 +
    if (options->HashedControlPassword) {
      smartlist_t *sl = decode_hashed_passwords(options->HashedControlPassword);
      if (!sl) {
@@@ -3523,13 -3512,6 +3525,13 @@@
               "upgrade your Tor controller as soon as possible.");
    }
  
 +  if (options->CookieAuthFileGroupReadable && !options->CookieAuthFile) {
 +    log_warn(LD_CONFIG, "You set the CookieAuthFileGroupReadable but did "
 +             "not configure a the path for the cookie file via "
 +             "CookieAuthFile. This means your cookie will not be group "
 +             "readable.");
 +  }
 +
    if (options->UseEntryGuards && ! options->NumEntryGuards)
      REJECT("Cannot enable UseEntryGuards with NumEntryGuards set to 0");
  
@@@ -3563,10 -3545,11 +3565,10 @@@
      if (options->ConstrainedSockSize < MIN_CONSTRAINED_TCP_BUFFER ||
          options->ConstrainedSockSize > MAX_CONSTRAINED_TCP_BUFFER ||
          options->ConstrainedSockSize % 1024) {
 -      r = tor_snprintf(buf, sizeof(buf),
 +      tor_asprintf(msg,
            "ConstrainedSockSize is invalid.  Must be a value between %d and %d "
            "in 1024 byte increments.",
            MIN_CONSTRAINED_TCP_BUFFER, MAX_CONSTRAINED_TCP_BUFFER);
 -      *msg = tor_strdup(r >= 0 ? buf : "internal error");
        return -1;
      }
      if (options->DirPort) {
@@@ -3614,12 -3597,6 +3616,12 @@@
    if (options->PreferTunneledDirConns && !options->TunnelDirConns)
      REJECT("Must set TunnelDirConns if PreferTunneledDirConns is set.");
  
 +  if ((options->Socks4Proxy || options->Socks5Proxy) &&
 +      !options->HTTPProxy && !options->PreferTunneledDirConns)
 +    REJECT("When Socks4Proxy or Socks5Proxy is configured, "
 +           "PreferTunneledDirConns and TunnelDirConns must both be "
 +           "set to 1, or HTTPProxy must be configured.");
 +
    if (options->AutomapHostsSuffixes) {
      SMARTLIST_FOREACH(options->AutomapHostsSuffixes, char *, suf,
      {
@@@ -3634,13 -3611,6 +3636,13 @@@
             "a non-default set of DirServers.");
    }
  
 +  if (options->AllowSingleHopExits && !options->DirServers) {
 +    COMPLAIN("You have set AllowSingleHopExits; now your relay will allow "
 +             "others to make one-hop exits. However, since by default most "
 +             "clients avoid relays that set this option, most clients will "
 +             "ignore you.");
 +  }
 +
    /*XXXX022 checking for defaults manually like this is a bit fragile.*/
  
    /* Keep changes to hard-coded values synchronous to man page and default
@@@ -3706,26 -3676,6 +3708,26 @@@
                          "testing Tor network!");
    }
  
 +  if (options->AccelName && !options->HardwareAccel)
 +    options->HardwareAccel = 1;
 +  if (options->AccelDir && !options->AccelName)
 +    REJECT("Can't use hardware crypto accelerator dir without engine name.");
 +
 +  if (options->PublishServerDescriptor)
 +    SMARTLIST_FOREACH(options->PublishServerDescriptor, const char *, pubdes, {
 +      if (!strcmp(pubdes, "1") || !strcmp(pubdes, "0"))
 +        if (smartlist_len(options->PublishServerDescriptor) > 1) {
 +          COMPLAIN("You have passed a list of multiple arguments to the "
 +                   "PublishServerDescriptor option that includes 0 or 1. "
 +                   "0 or 1 should only be used as the sole argument. "
 +                   "This configuration will be rejected in a future release.");
 +          break;
 +        }
 +    });
 +
 +  if (options->BridgeRelay == 1 && options->ORPort == 0)
 +      REJECT("BridgeRelay is 1, ORPort is 0. This is an invalid combination.");
 +
    return 0;
  #undef REJECT
  #undef COMPLAIN
@@@ -3764,10 -3714,12 +3766,10 @@@ options_transition_allowed(or_options_
    }
  
    if (strcmp(old->DataDirectory,new_val->DataDirectory)!=0) {
 -    char buf[1024];
 -    int r = tor_snprintf(buf, sizeof(buf),
 +    tor_asprintf(msg,
                 "While Tor is running, changing DataDirectory "
                 "(\"%s\"->\"%s\") is not allowed.",
                 old->DataDirectory, new_val->DataDirectory);
 -    *msg = tor_strdup(r >= 0 ? buf : "internal error");
      return -1;
    }
  
@@@ -3776,22 -3728,19 +3778,22 @@@
      return -1;
    }
  
 -  if (!opt_streq(old->Group, new_val->Group)) {
 -    *msg = tor_strdup("While Tor is running, changing Group is not allowed.");
 +  if ((old->HardwareAccel != new_val->HardwareAccel)
 +      || !opt_streq(old->AccelName, new_val->AccelName)
 +      || !opt_streq(old->AccelDir, new_val->AccelDir)) {
 +    *msg = tor_strdup("While Tor is running, changing OpenSSL hardware "
 +                      "acceleration engine is not allowed.");
      return -1;
    }
  
 -  if (old->HardwareAccel != new_val->HardwareAccel) {
 -    *msg = tor_strdup("While Tor is running, changing HardwareAccel is "
 -                      "not allowed.");
 +  if (old->TestingTorNetwork != new_val->TestingTorNetwork) {
 +    *msg = tor_strdup("While Tor is running, changing TestingTorNetwork "
 +                      "is not allowed.");
      return -1;
    }
  
 -  if (old->TestingTorNetwork != new_val->TestingTorNetwork) {
 -    *msg = tor_strdup("While Tor is running, changing TestingTorNetwork "
 +  if (old->DisableAllSwap != new_val->DisableAllSwap) {
 +    *msg = tor_strdup("While Tor is running, changing DisableAllSwap "
                        "is not allowed.");
      return -1;
    }
@@@ -3806,13 -3755,12 +3808,13 @@@ options_transition_affects_workers(or_o
                                     or_options_t *new_options)
  {
    if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) ||
 -      old_options->NumCpus != new_options->NumCpus ||
 +      old_options->NumCPUs != new_options->NumCPUs ||
        old_options->ORPort != new_options->ORPort ||
        old_options->ServerDNSSearchDomains !=
                                         new_options->ServerDNSSearchDomains ||
        old_options->SafeLogging != new_options->SafeLogging ||
        old_options->ClientOnly != new_options->ClientOnly ||
 +      public_server_mode(old_options) != public_server_mode(new_options) ||
        !config_lines_eq(old_options->Logs, new_options->Logs))
      return 1;
  
@@@ -3839,6 -3787,7 +3841,6 @@@ options_transition_affects_descriptor(o
        old_options->ORPort != new_options->ORPort ||
        old_options->DirPort != new_options->DirPort ||
        old_options->ClientOnly != new_options->ClientOnly ||
 -      old_options->NoPublish != new_options->NoPublish ||
        old_options->_PublishServerDescriptor !=
          new_options->_PublishServerDescriptor ||
        get_effective_bwrate(old_options) != get_effective_bwrate(new_options) ||
@@@ -3861,7 -3810,6 +3863,7 @@@ get_windows_conf_root(void
  {
    static int is_set = 0;
    static char path[MAX_PATH+1];
 +  TCHAR tpath[MAX_PATH] = {0};
  
    LPITEMIDLIST idl;
    IMalloc *m;
@@@ -3879,7 -3827,7 +3881,7 @@@
  #define APPDATA_PATH CSIDL_APPDATA
  #endif
    if (!SUCCEEDED(SHGetSpecialFolderLocation(NULL, APPDATA_PATH, &idl))) {
 -    GetCurrentDirectory(MAX_PATH, path);
 +    getcwd(path,MAX_PATH);
      is_set = 1;
      log_warn(LD_CONFIG,
               "I couldn't find your application data folder: are you "
@@@ -3888,15 -3836,8 +3890,15 @@@
      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 */
 +  result = SHGetPathFromIDList(idl, tpath);
 +#ifdef UNICODE
 +  wcstombs(path,tpath,MAX_PATH);
 +#else
 +  strlcpy(path,tpath,sizeof(path));
 +#endif
 +
 +  /* Now we need to free the memory that the path-idl was stored in.  In
 +   * typical Windows fashion, we can't just call 'free()' on it. */
    SHGetMalloc(&m);
    if (m) {
      m->lpVtbl->Free(m, idl);
@@@ -3944,7 -3885,10 +3946,7 @@@ check_nickname_list(const char *lst, co
    SMARTLIST_FOREACH(sl, const char *, s,
      {
        if (!is_legal_nickname_or_hexdigest(s)) {
 -        char buf[1024];
 -        int tmp = tor_snprintf(buf, sizeof(buf),
 -                  "Invalid nickname '%s' in %s line", s, name);
 -        *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
 +        tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name);
          r = -1;
          break;
        }
@@@ -3968,7 -3912,13 +3970,7 @@@ find_torrc_filename(int argc, char **ar
          log(LOG_WARN, LD_CONFIG, "Duplicate -f options on command line.");
          tor_free(fname);
        }
 -#ifdef MS_WINDOWS
 -      /* XXX one day we might want to extend expand_filename to work
 -       * under Windows as well. */
 -      fname = tor_strdup(argv[i+1]);
 -#else
        fname = expand_filename(argv[i+1]);
 -#endif
        *using_default_torrc = 0;
        ++i;
      } else if (!strcmp(argv[i],"--ignore-missing-torrc")) {
@@@ -4074,12 -4024,6 +4076,12 @@@ options_init_from_torrc(int argc, char 
      printf("Tor version %s.\n",get_version());
      exit(0);
    }
 +  if (argc > 1 && (!strcmp(argv[1],"--digests"))) {
 +    printf("Tor version %s.\n",get_version());
 +    printf("%s", libor_get_digests());
 +    printf("%s", tor_get_digests());
 +    exit(0);
 +  }
  
    /* Go through command-line variables */
    if (!global_cmdline_options) {
@@@ -4247,9 -4191,12 +4249,9 @@@ options_init_from_string(const char *cf
   err:
    config_free(&options_format, newoptions);
    if (*msg) {
 -    int len = (int)strlen(*msg)+256;
 -    char *newmsg = tor_malloc(len);
 -
 -    tor_snprintf(newmsg, len, "Failed to parse/validate config: %s", *msg);
 -    tor_free(*msg);
 -    *msg = newmsg;
 +    char *old_msg = *msg;
 +    tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg);
 +    tor_free(old_msg);
    }
    return err;
  }
@@@ -4637,7 -4584,7 +4639,7 @@@ normalize_data_directory(or_options_t *
  }
  
  /** Check and normalize the value of options->DataDirectory; return 0 if it
 - * sane, -1 otherwise. */
 + * is sane, -1 otherwise. */
  static int
  validate_data_directory(or_options_t *options)
  {
@@@ -4669,6 -4616,7 +4671,6 @@@ write_configuration_file(const char *fn
  {
    char *old_val=NULL, *new_val=NULL, *new_conf=NULL;
    int rename_old = 0, r;
 -  size_t len;
  
    tor_assert(fname);
  
@@@ -4695,7 -4643,9 +4697,7 @@@
      goto err;
    }
  
 -  len = strlen(new_conf)+256;
 -  new_val = tor_malloc(len);
 -  tor_snprintf(new_val, len, "%s\n%s\n\n%s",
 +  tor_asprintf(&new_val, "%s\n%s\n\n%s",
                 GENERATED_FILE_PREFIX, GENERATED_FILE_COMMENT, new_conf);
  
    if (rename_old) {
@@@ -4745,12 -4695,15 +4747,12 @@@
  int
  options_save_current(void)
  {
 -  if (torrc_fname) {
 -    /* This fails if we can't write to our configuration file.
 -     *
 -     * If we try falling back to datadirectory or something, we have a better
 -     * chance of saving the configuration, but a better chance of doing
 -     * something the user never expected. Let's just warn instead. */
 -    return write_configuration_file(torrc_fname, get_options());
 -  }
 -  return write_configuration_file(get_default_conf_file(), get_options());
 +  /* This fails if we can't write to our configuration file.
 +   *
 +   * If we try falling back to datadirectory or something, we have a better
 +   * chance of saving the configuration, but a better chance of doing
 +   * something the user never expected. */
 +  return write_configuration_file(get_torrc_fname(), get_options());
  }
  
  /** Mapping from a unit name to a multiplier for converting that unit into a
@@@ -4815,47 -4768,30 +4817,47 @@@ static struct unit_table_t time_units[
  static uint64_t
  config_parse_units(const char *val, struct unit_table_t *u, int *ok)
  {
 -  uint64_t v;
 +  uint64_t v = 0;
 +  double d = 0;
 +  int use_float = 0;
    char *cp;
  
    tor_assert(ok);
  
    v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
 -  if (!*ok)
 -    return 0;
 +  if (!*ok || (cp && *cp == '.')) {
 +    d = tor_parse_double(val, 0, UINT64_MAX, ok, &cp);
 +    if (!*ok)
 +      goto done;
 +    use_float = 1;
 +  }
 +
    if (!cp) {
      *ok = 1;
 -    return v;
 +    v = use_float ? DBL_TO_U64(d) :  v;
 +    goto done;
    }
 -  while (TOR_ISSPACE(*cp))
 -    ++cp;
 +
 +  cp = (char*) eat_whitespace(cp);
 +
    for ( ;u->unit;++u) {
      if (!strcasecmp(u->unit, cp)) {
 -      v *= u->multiplier;
 +      if (use_float)
 +        v = u->multiplier * d;
 +      else
 +        v *= u->multiplier;
        *ok = 1;
 -      return v;
 +      goto done;
      }
    }
    log_warn(LD_CONFIG, "Unknown unit '%s'.", cp);
    *ok = 0;
 -  return 0;
 + done:
 +
 +  if (*ok)
 +    return v;
 +  else
 +    return 0;
  }
  
  /** Parse a string in the format "number unit", where unit is a unit of
@@@ -4865,8 -4801,7 +4867,8 @@@
  static uint64_t
  config_parse_memunit(const char *s, int *ok)
  {
 -  return config_parse_units(s, memory_units, ok);
 +  uint64_t u = config_parse_units(s, memory_units, ok);
 +  return u;
  }
  
  /** Parse a string in the format "number unit", where unit is a unit of time.
@@@ -4888,37 -4823,256 +4890,37 @@@ config_parse_interval(const char *s, in
    return (int)r;
  }
  
 -/* This is what passes for version detection on OSX.  We set
 - * MACOSX_KQUEUE_IS_BROKEN to true iff we're on a version of OSX before
 - * 10.4.0 (aka 1040). */
 -#ifdef __APPLE__
 -#ifdef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
 -#define MACOSX_KQUEUE_IS_BROKEN \
 -  (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1040)
 -#else
 -#define MACOSX_KQUEUE_IS_BROKEN 0
 -#endif
 -#endif
 -
  /**
   * Initialize the libevent library.
   */
  static void
  init_libevent(void)
  {
 +  const char *badness=NULL;
 +
    configure_libevent_logging();
    /* If the kernel complains that some method (say, epoll) doesn't
     * exist, we don't care about it, since libevent will cope.
     */
    suppress_libevent_log_msg("Function not implemented");
 -#ifdef __APPLE__
 -  if (MACOSX_KQUEUE_IS_BROKEN ||
 -      decode_libevent_version(event_get_version(), NULL) < LE_11B) {
 -    setenv("EVENT_NOKQUEUE","1",1);
 -  }
 -#endif
  
 -  /* In libevent versions before 2.0, it's hard to keep binary compatibility
 -   * between upgrades, and unpleasant to detect when the version we compiled
 -   * against is unlike the version we have linked against. Here's how. */
 -#if defined(_EVENT_VERSION) && defined(HAVE_EVENT_GET_VERSION)
 -  /* We have a header-file version and a function-call version. Easy. */
 -  if (strcmp(_EVENT_VERSION, event_get_version())) {
 -    int compat1 = -1, compat2 = -1;
 -    int verybad, prettybad ;
 -    decode_libevent_version(_EVENT_VERSION, &compat1);
 -    decode_libevent_version(event_get_version(), &compat2);
 -    verybad = compat1 != compat2;
 -    prettybad = (compat1 == -1 || compat2 == -1) && compat1 != compat2;
 -
 -    log(verybad ? LOG_WARN : (prettybad ? LOG_NOTICE : LOG_INFO),
 -        LD_GENERAL, "We were compiled with headers from version %s "
 -        "of Libevent, but we're using a Libevent library that says it's "
 -        "version %s.", _EVENT_VERSION, event_get_version());
 -    if (verybad)
 -      log_warn(LD_GENERAL, "This will almost certainly make Tor crash.");
 -    else if (prettybad)
 -      log_notice(LD_GENERAL, "If Tor crashes, this might be why.");
 -    else
 -      log_info(LD_GENERAL, "I think these versions are binary-compatible.");
 -  }
 -#elif defined(HAVE_EVENT_GET_VERSION)
 -  /* event_get_version but no _EVENT_VERSION.  We might be in 1.4.0-beta or
 -     earlier, where that's normal.  To see whether we were compiled with an
 -     earlier version, let's see whether the struct event defines MIN_HEAP_IDX.
 -  */
 -#ifdef HAVE_STRUCT_EVENT_MIN_HEAP_IDX
 -  /* The header files are 1.4.0-beta or later. If the version is not
 -   * 1.4.0-beta, we are incompatible. */
 -  {
 -    if (strcmp(event_get_version(), "1.4.0-beta")) {
 -      log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have "
 -               "Libevent 1.4.0-beta header files, whereas you have linked "
 -               "against Libevent %s.  This will probably make Tor crash.",
 -               event_get_version());
 -    }
 -  }
 -#else
 -  /* Our headers are 1.3e or earlier. If the library version is not 1.4.x or
 -     later, we're probably fine. */
 -  {
 -    const char *v = event_get_version();
 -    if ((v[0] == '1' && v[2] == '.' && v[3] > '3') || v[0] > '1') {
 -      log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have "
 -               "Libevent header file from 1.3e or earlier, whereas you have "
 -               "linked against Libevent %s.  This will probably make Tor "
 -               "crash.", event_get_version());
 -    }
 -  }
 -#endif
 +  tor_check_libevent_header_compatibility();
  
 -#elif defined(_EVENT_VERSION)
 -#warn "_EVENT_VERSION is defined but not get_event_version(): Libevent is odd."
 -#else
 -  /* Your libevent is ancient. */
 -#endif
 +  tor_libevent_initialize();
  
 -  event_init();
    suppress_libevent_log_msg(NULL);
 -#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
 -  /* Making this a NOTICE for now so we can link bugs to a libevent versions
 -   * or methods better. */
 -  log(LOG_NOTICE, LD_GENERAL,
 -      "Initialized libevent version %s using method %s. Good.",
 -      event_get_version(), event_get_method());
 -  check_libevent_version(event_get_method(), get_options()->ORPort != 0);
 -#else
 -  log(LOG_NOTICE, LD_GENERAL,
 -      "Initialized old libevent (version 1.0b or earlier).");
 -  log(LOG_WARN, LD_GENERAL,
 -      "You have a *VERY* old version of libevent.  It is likely to be buggy; "
 -      "please build Tor with a more recent version.");
 -#endif
 -}
 -
 -/** Table mapping return value of event_get_version() to le_version_t. */
 -static const struct {
 -  const char *name; le_version_t version; int bincompat;
 -} le_version_table[] = {
 -  /* earlier versions don't have get_version. */
 -  { "1.0c", LE_10C, 1},
 -  { "1.0d", LE_10D, 1},
 -  { "1.0e", LE_10E, 1},
 -  { "1.1",  LE_11,  1 },
 -  { "1.1a", LE_11A, 1 },
 -  { "1.1b", LE_11B, 1 },
 -  { "1.2",  LE_12,  1 },
 -  { "1.2a", LE_12A, 1 },
 -  { "1.3",  LE_13,  1 },
 -  { "1.3a", LE_13A, 1 },
 -  { "1.3b", LE_13B, 1 },
 -  { "1.3c", LE_13C, 1 },
 -  { "1.3d", LE_13D, 1 },
 -  { "1.3e", LE_13E, 1 },
 -  { "1.4.0-beta", LE_140, 2 },
 -  { "1.4.1-beta", LE_141, 2 },
 -  { "1.4.2-rc",   LE_142, 2 },
 -  { "1.4.3-stable", LE_143, 2 },
 -  { "1.4.4-stable", LE_144, 2 },
 -  { "1.4.5-stable", LE_145, 2 },
 -  { "1.4.6-stable", LE_146, 2 },
 -  { "1.4.7-stable", LE_147, 2 },
 -  { "1.4.8-stable", LE_148, 2 },
 -  { "1.4.99-trunk", LE_1499, 3 },
 -  { NULL, LE_OTHER, 0 }
 -};
  
 -/** Return the le_version_t for the current version of libevent.  If the
 - * version is very new, return LE_OTHER.  If the version is so old that it
 - * doesn't support event_get_version(), return LE_OLD. */
 -static le_version_t
 -decode_libevent_version(const char *v, int *bincompat_out)
 -{
 -  int i;
 -  for (i=0; le_version_table[i].name; ++i) {
 -    if (!strcmp(le_version_table[i].name, v)) {
 -      if (bincompat_out)
 -        *bincompat_out = le_version_table[i].bincompat;
 -      return le_version_table[i].version;
 -    }
 -  }
 -  if (v[0] != '1' && bincompat_out)
 -    *bincompat_out = 100;
 -  else if (!strcmpstart(v, "1.4") && bincompat_out)
 -    *bincompat_out = 2;
 -  return LE_OTHER;
 -}
 -
 -#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
 -/**
 - * Compare the given libevent method and version to a list of versions
 - * which are known not to work.  Warn the user as appropriate.
 - */
 -static void
 -check_libevent_version(const char *m, int server)
 -{
 -  int buggy = 0, iffy = 0, slow = 0, thread_unsafe = 0;
 -  le_version_t version;
 -  const char *v = event_get_version();
 -  const char *badness = NULL;
 -  const char *sad_os = "";
 -
 -  version = decode_libevent_version(v, NULL);
 -
 -  /* XXX Would it be worthwhile disabling the methods that we know
 -   * are buggy, rather than just warning about them and then proceeding
 -   * to use them? If so, we should probably not wrap this whole thing
 -   * in HAVE_EVENT_GET_VERSION and HAVE_EVENT_GET_METHOD. -RD */
 -  /* XXXX The problem is that it's not trivial to get libevent to change it's
 -   * method once it's initialized, and it's not trivial to tell what method it
 -   * will use without initializing it.  I guess we could preemptively disable
 -   * buggy libevent modes based on the version _before_ initializing it,
 -   * though, but then there's no good way (afaict) to warn "I would have used
 -   * kqueue, but instead I'm using select." -NM */
 -  if (!strcmp(m, "kqueue")) {
 -    if (version < LE_11B)
 -      buggy = 1;
 -  } else if (!strcmp(m, "epoll")) {
 -    if (version < LE_11)
 -      iffy = 1;
 -  } else if (!strcmp(m, "poll")) {
 -    if (version < LE_10E)
 -      buggy = 1;
 -    else if (version < LE_11)
 -      slow = 1;
 -  } else if (!strcmp(m, "select")) {
 -    if (version < LE_11)
 -      slow = 1;
 -  } else if (!strcmp(m, "win32")) {
 -    if (version < LE_11B)
 -      buggy = 1;
 -  }
 -
 -  /* Libevent versions before 1.3b do very badly on operating systems with
 -   * user-space threading implementations. */
 -#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
 -  if (server && version < LE_13B) {
 -    thread_unsafe = 1;
 -    sad_os = "BSD variants";
 -  }
 -#elif defined(__APPLE__) || defined(__darwin__)
 -  if (server && version < LE_13B) {
 -    thread_unsafe = 1;
 -    sad_os = "Mac OS X";
 -  }
 -#endif
 -
 -  if (thread_unsafe) {
 -    log(LOG_WARN, LD_GENERAL,
 -        "Libevent version %s often crashes when running a Tor server with %s. "
 -        "Please use the latest version of libevent (1.3b or later)",v,sad_os);
 -    badness = "BROKEN";
 -  } else if (buggy) {
 -    log(LOG_WARN, LD_GENERAL,
 -        "There are serious bugs in using %s with libevent %s. "
 -        "Please use the latest version of libevent.", m, v);
 -    badness = "BROKEN";
 -  } else if (iffy) {
 -    log(LOG_WARN, LD_GENERAL,
 -        "There are minor bugs in using %s with libevent %s. "
 -        "You may want to use the latest version of libevent.", m, v);
 -    badness = "BUGGY";
 -  } else if (slow && server) {
 -    log(LOG_WARN, LD_GENERAL,
 -        "libevent %s can be very slow with %s. "
 -        "When running a server, please use the latest version of libevent.",
 -        v,m);
 -    badness = "SLOW";
 -  }
 +  tor_check_libevent_version(tor_libevent_get_method(),
 +                             get_options()->ORPort != 0,
 +                             &badness);
    if (badness) {
 +    const char *v = tor_libevent_get_version_str();
 +    const char *m = tor_libevent_get_method();
      control_event_general_status(LOG_WARN,
          "BAD_LIBEVENT VERSION=%s METHOD=%s BADNESS=%s RECOVERED=NO",
                                   v, m, badness);
    }
 -
  }
 -#endif
  
  /** Return the persistent state struct for this Tor. */
  or_state_t *
@@@ -5000,61 -5154,22 +5002,61 @@@ or_state_validate(or_state_t *old_state
  }
  
  /** Replace the current persistent state with <b>new_state</b> */
 -static void
 +static int
  or_state_set(or_state_t *new_state)
  {
    char *err = NULL;
 +  int ret = 0;
    tor_assert(new_state);
 -  if (global_state)
 -    config_free(&state_format, global_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;
 +  size_t len = strlen(fname)+16;
 +  char *fname2 = tor_malloc(len);
 +  for (i = 0; i < 100; ++i) {
 +    tor_snprintf(fname2, len, "%s.%d", fname, i);
 +    status = file_status(fname2);
 +    if (status == FN_NOENT)
 +      break;
 +  }
 +  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.
@@@ -5116,8 -5231,31 +5118,8 @@@ or_state_load(void
               " This is a bug in Tor.");
      goto done;
    } else if (badstate && contents) {
 -    int i;
 -    file_status_t status;
 -    size_t len = strlen(fname)+16;
 -    char *fname2 = tor_malloc(len);
 -    for (i = 0; i < 100; ++i) {
 -      tor_snprintf(fname2, len, "%s.%d", fname, i);
 -      status = file_status(fname2);
 -      if (status == FN_NOENT)
 -        break;
 -    }
 -    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);
 +    or_state_save_broken(fname);
 +
      tor_free(contents);
      config_free(&state_format, new_state);
  
@@@ -5129,9 -5267,7 +5131,9 @@@
    } else {
      log_info(LD_GENERAL, "Initialized state");
    }
 -  or_state_set(new_state);
 +  if (or_state_set(new_state) == -1) {
 +    or_state_save_broken(fname);
 +  }
    new_state = NULL;
    if (!contents) {
      global_state->next_write = 0;
@@@ -5148,15 -5284,13 +5150,15 @@@
    return r;
  }
  
 +/** If writing the state to disk fails, try again after this many seconds. */
 +#define STATE_WRITE_RETRY_INTERVAL 3600
 +
  /** 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];
 -  size_t len;
    char *fname;
  
    tor_assert(global_state);
@@@ -5168,16 -5302,20 +5170,16 @@@
     * 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 = time(NULL);
    tor_free(global_state->TorVersion);
 -  len = strlen(get_version())+8;
 -  global_state->TorVersion = tor_malloc(len);
 -  tor_snprintf(global_state->TorVersion, len, "Tor %s", get_version());
 +  tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
  
    state = config_dump(&state_format, global_state, 1, 0);
 -  len = strlen(state)+256;
 -  contents = tor_malloc(len);
    format_local_iso_time(tbuf, time(NULL));
 -  tor_snprintf(contents, len,
 +  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",
@@@ -5187,16 -5325,10 +5189,16 @@@
    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);
 +    global_state->LastWritten = -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;
    }
 +
 +  global_state->LastWritten = time(NULL);
    log_info(LD_GENERAL, "Saved state to \"%s\"", fname);
    tor_free(fname);
    tor_free(contents);
@@@ -5228,18 -5360,18 +5230,18 @@@ remove_file_if_very_old(const char *fna
   * types. */
  int
  getinfo_helper_config(control_connection_t *conn,
 -                      const char *question, char **answer)
 +                      const char *question, char **answer,
 +                      const char **errmsg)
  {
    (void) conn;
 +  (void) errmsg;
    if (!strcmp(question, "config/names")) {
      smartlist_t *sl = smartlist_create();
      int i;
      for (i = 0; _option_vars[i].name; ++i) {
        config_var_t *var = &_option_vars[i];
 -      const char *type, *desc;
 +      const char *type;
        char *line;
 -      size_t len;
 -      desc = config_find_description(&options_format, var->name);
        switch (var->type) {
          case CONFIG_TYPE_STRING: type = "String"; break;
          case CONFIG_TYPE_FILENAME: type = "Filename"; break;
@@@ -5260,7 -5392,14 +5262,7 @@@
        }
        if (!type)
          continue;
 -      len = strlen(var->name)+strlen(type)+16;
 -      if (desc)
 -        len += strlen(desc);
 -      line = tor_malloc(len);
 -      if (desc)
 -        tor_snprintf(line, len, "%s %s %s\n",var->name,type,desc);
 -      else
 -        tor_snprintf(line, len, "%s %s\n",var->name,type);
 +      tor_asprintf(&line, "%s %s\n",var->name,type);
        smartlist_add(sl, line);
      }
      *answer = smartlist_join_strings(sl, "", 0, NULL);





More information about the tor-commits mailing list